wwf
2025-06-04 a430284aa21e3ae1f0d5654e55b2ad2852519cc2
初始化
1,826个文件已修改
86个文件已添加
74465 ■■■■ 已修改文件
.dockerignore 补丁 | 查看 | 原始文档 | blame | 历史
.editorconfig 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env.example 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.eslintignore 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.eslintrc.json 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.husky/pre-commit 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/.gitignore 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/modules.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/vcs.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.idea/web.iml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.storybook/main.ts 补丁 | 查看 | 原始文档 | blame | 历史
.storybook/preview.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.vscode/extensions.json 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.vscode/settings.example.json 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Dockerfile 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/annotations/page.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/cardView.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chartView.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx 86 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx 95 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/toggle-fold-btn.tsx 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/app/(appDetailLayout)/layout.tsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/apps/AppCard.tsx 69 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/apps/Apps.tsx 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/apps/NewAppCard.tsx 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/apps/hooks/useAppsQueryState.ts 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/apps/page.tsx 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/apps/style.module.css 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/api/page.tsx 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/create/page.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/page.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/hitTesting/page.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/style.module.css 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/ApiServer.tsx 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/Container.tsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/DatasetCard.tsx 52 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/DatasetFooter.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/Datasets.tsx 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/Doc.tsx 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/NewDatasetCard.tsx 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/page.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/template/template.en.mdx 1152 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/datasets/template/template.zh.mdx 1181 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/explore/installed/[appId]/page.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(commonLayout)/list.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/(shareLayout)/layout.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/(shareLayout)/webapp-signin/page.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/account/account-page/AvatarWithEdit.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/account/account-page/index.tsx 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/account/avatar.tsx 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/account/delete-account/components/check-email.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/account/delete-account/components/feed-back.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/account/delete-account/components/verify-email.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/account/header.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/account/layout.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/account/page.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/activate/activateForm.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/activate/page.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/activate/style.module.css 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/activate/team-28x28.png 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app-sidebar/app-info.tsx 338 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app-sidebar/basic.tsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app-sidebar/dataset-info.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app-sidebar/index.tsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app-sidebar/navLink.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app-sidebar/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/add-annotation-modal/edit-item/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/add-annotation-modal/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/batch-add-annotation-modal/csv-downloader.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/batch-add-annotation-modal/index.tsx 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/edit-annotation-modal/edit-item/index.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/edit-annotation-modal/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/empty-element.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/filter.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/header-opts/index.tsx 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/index.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/list.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/view-annotation-modal/hit-history-no-data.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/annotation/view-annotation-modal/index.tsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/app-publisher/index.tsx 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/app-publisher/publish-with-multiple-model.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/app-publisher/suggested-action.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/base/feature-panel/index.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/base/group-name/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/base/icons/remove-icon/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/base/operation-btn/index.tsx 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/base/var-highlight/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/base/var-highlight/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/base/warning-mask/cannot-query-dataset.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/base/warning-mask/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/base/warning-mask/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-prompt/advanced-prompt-input.tsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-prompt/confirm-add-var/index.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-prompt/conversation-history/edit-modal.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-prompt/index.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-prompt/message-type-selector.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-prompt/simple-prompt-input.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-prompt/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-var/config-modal/field.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-var/config-modal/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-var/config-select/index.tsx 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-var/config-select/style.module.css 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-var/config-string/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-var/index.tsx 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-var/select-type-item/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-var/select-var-type.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-var/style.module.css 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-vision/index.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-vision/param-config-content.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config-vision/param-config.tsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config/agent-setting-button.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config/agent/agent-setting/index.tsx 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config/agent/agent-setting/item-panel.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config/agent/agent-tools/index.tsx 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool.tsx 201 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config/agent/prompt-editor.tsx 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config/assistant-type-picker/index.tsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config/automatic/automatic-btn.tsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config/automatic/get-automatic-res.tsx 52 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config/automatic/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config/code-generator/get-code-generator-res.tsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config/config-document.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/config/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/ctrl-btn-group/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/ctrl-btn-group/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/card-item/index.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/card-item/item.tsx 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/card-item/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/context-var/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/context-var/style.module.css 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/context-var/var-picker.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/index.tsx 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/params-config/config-content.tsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/params-config/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/params-config/weighted-score.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/params-config/weighted-score.tsx 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/select-dataset/index.tsx 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/select-dataset/style.module.css 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/dataset-config/settings-modal/index.tsx 92 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/debug/chat-user-input.tsx 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/debug/debug-with-multiple-model/chat-item.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/debug/debug-with-multiple-model/context.tsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/debug/debug-with-multiple-model/debug-item.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/debug/debug-with-multiple-model/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/debug/debug-with-multiple-model/model-parameter-trigger.tsx 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/debug/debug-with-multiple-model/text-generation-item.tsx 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/debug/debug-with-single-model/index.tsx 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/debug/index.tsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/features/chat-group/opening-statement/index.tsx 300 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/features/experience-enhance-group/more-like-this/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/index.tsx 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/prompt-mode/advanced-mode-waring.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/prompt-value-panel/index.tsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/toolbox/annotation/config-param.tsx 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/toolbox/index.tsx 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/tools/external-data-tool-modal.tsx 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/configuration/tools/index.tsx 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/create-app-dialog/app-card/index.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/create-app-dialog/app-list/index.tsx 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/create-app-dialog/app-list/sidebar.tsx 71 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/create-app-dialog/index.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/create-app-dialog/newAppDialog.tsx 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/create-app-modal/index.tsx 124 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/create-from-dsl-modal/index.tsx 72 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/create-from-dsl-modal/uploader.tsx 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/duplicate-modal/index.tsx 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/duplicate-modal/style.module.css 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/log-annotation/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/log/filter.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/log/index.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/log/list.tsx 97 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/log/model-info.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/log/var-panel.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/overview/apikey-info-panel/index.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/overview/apikey-info-panel/progress/index.tsx 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/overview/apikey-info-panel/progress/style.module.css 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/overview/appCard.tsx 90 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/overview/appChart.tsx 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/overview/customize/index.tsx 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/overview/embedded/index.tsx 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/overview/embedded/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/overview/settings/index.tsx 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/switch-app-modal/index.tsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/switch-app-modal/style.module.css 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/text-generate/index.tsx 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/text-generate/item/index.tsx 407 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/text-generate/item/result-tab.tsx 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/text-generate/saved-items/index.tsx 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/text-generate/saved-items/no-data/index.tsx 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/type-selector/index.tsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/workflow-log/detail.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/workflow-log/filter.tsx 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/workflow-log/index.tsx 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/app/workflow-log/list.tsx 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/action-button/index.css 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/action-button/index.tsx 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/agent-log-modal/detail.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/agent-log-modal/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/agent-log-modal/iteration.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/agent-log-modal/result.tsx 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/agent-log-modal/tool-call.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/agent-log-modal/tracing.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/answer-icon/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/app-icon-picker/ImageInput.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/app-icon-picker/index.tsx 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/app-icon/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/app-unavailable.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/audio-btn/audio.player.manager.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/audio-btn/audio.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/audio-btn/index.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/audio-btn/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/audio-gallery/AudioPlayer.module.css 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/audio-gallery/AudioPlayer.tsx 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/auto-height-textarea/common.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/auto-height-textarea/index.tsx 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/avatar/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/badge.tsx 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/block-input/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/button/add-button.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/button/index.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/button/index.spec.tsx 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/button/index.stories.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/button/index.tsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/__tests__/utils.spec.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat-with-history/chat-wrapper.tsx 194 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat-with-history/config-panel/form-input.tsx 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat-with-history/config-panel/form.tsx 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat-with-history/config-panel/index.tsx 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat-with-history/context.tsx 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat-with-history/header-in-mobile.tsx 144 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat-with-history/header.tsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat-with-history/hooks.tsx 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat-with-history/index.tsx 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat-with-history/sidebar/index.tsx 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat-with-history/sidebar/item.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat-with-history/sidebar/list.tsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat-with-history/sidebar/rename-modal.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/answer/__mocks__/markdownContentSVG.ts 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/answer/__mocks__/workflowProcess.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/answer/agent-content.tsx 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/answer/index.tsx 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/answer/more.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/answer/operation.tsx 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/answer/suggested-questions.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/answer/tool-detail.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/answer/workflow-process.tsx 56 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/chat-input-area/hooks.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/chat-input-area/index.tsx 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/chat-input-area/operation.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/citation/index.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/citation/popup.tsx 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/citation/progress-tooltip.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/citation/tooltip.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/hooks.ts 61 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/index.tsx 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/loading-anim/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/log/index.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/question.stories.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/question.tsx 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/thought/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/thought/panel.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/thought/tool.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/try-to-ask.tsx 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/type.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/chat/utils.ts 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/embedded-chatbot/chat-wrapper.tsx 179 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/embedded-chatbot/config-panel/form-input.tsx 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/embedded-chatbot/config-panel/form.tsx 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/embedded-chatbot/config-panel/index.tsx 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/embedded-chatbot/context.tsx 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/embedded-chatbot/header.tsx 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/embedded-chatbot/hooks.tsx 145 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/embedded-chatbot/index.tsx 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/embedded-chatbot/theme/theme-context.ts 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/embedded-chatbot/theme/utils.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/types.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chat/utils.ts 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/checkbox/assets/mixed.svg 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/checkbox/index.module.css 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/checkbox/index.tsx 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/chip/index.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/confirm/index.tsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/copy-btn/index.tsx 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/copy-btn/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/copy-feedback/index.tsx 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/copy-icon/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/corner-label/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/dialog/index.tsx 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/divider/with-label.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/drawer-plus/index.tsx 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/drawer/index.tsx 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/dropdown/index.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/emoji-picker/Inner.tsx 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/emoji-picker/index.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/emoji-picker/style.module.css 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/context.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/annotation-reply/annotation-ctrl-btn/index.tsx 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/annotation-reply/config-param-modal.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/annotation-reply/config-param.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/annotation-reply/index.tsx 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/annotation-reply/score-slider/base-slider/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/annotation-reply/score-slider/base-slider/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/annotation-reply/score-slider/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/citation.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/conversation-opener/index.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/conversation-opener/modal.tsx 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/dialog-wrapper.tsx 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/feature-bar.tsx 52 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/feature-card.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/file-upload/index.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/file-upload/setting-content.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/file-upload/setting-modal.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/follow-up.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/image-upload/index.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/index.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/moderation/form-generation.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/moderation/index.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/moderation/moderation-content.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx 69 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/more-like-this.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/speech-to-text.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/text-to-speech/index.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/text-to-speech/param-config-content.tsx 74 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/features/new-feature-panel/text-to-speech/voice-settings.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/audio-preview.tsx 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/file-from-link-or-local/index.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/file-image-render.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/file-input.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/file-list-in-log.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/file-uploader-in-attachment/file-item.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/file-uploader-in-attachment/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/file-uploader-in-chat-input/file-image-item.tsx 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/file-uploader-in-chat-input/file-item.tsx 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/file-uploader-in-chat-input/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/hooks.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/pdf-preview.tsx 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/store.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/utils.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/file-uploader/video-preview.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/fullscreen-modal/index.tsx 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/ga/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/grid-mask/index.tsx 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/IconBase.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/script.js 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/image/llm/BaichuanTextCn.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/image/llm/Minimax.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/image/llm/MinimaxText.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/image/llm/Tongyi.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/image/llm/TongyiText.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/image/llm/TongyiTextCn.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/image/llm/Wxyy.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/image/llm/WxyyText.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/image/llm/WxyyTextCn.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/avatar/Robot.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/avatar/Robot.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/avatar/User.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/avatar/User.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/billing/Sparkles.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/billing/Sparkles.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/billing/index.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/D.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/D.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/DiagonalDividingLine.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/DiagonalDividingLine.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/Dify.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/Dify.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/Github.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/Github.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/Highlight.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/Highlight.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/Line3.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/Line3.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/Lock.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/Lock.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/MessageChatSquare.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/MessageChatSquare.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/MultiPathRetrieval.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/MultiPathRetrieval.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/NTo1Retrieval.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/NTo1Retrieval.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/Notion.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/Notion.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/SparklesSoft.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/SparklesSoft.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/common/index.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Csv.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Csv.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Doc.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Doc.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Docx.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Docx.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Html.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Html.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Json.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Json.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Md.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Md.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Pdf.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Pdf.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Txt.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Txt.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Unknown.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Unknown.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Xlsx.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Xlsx.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Yaml.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/files/Yaml.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/knowledge/Chunk.json 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/knowledge/Chunk.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/knowledge/Collapse.json 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/knowledge/Collapse.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/knowledge/GeneralType.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/knowledge/GeneralType.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/knowledge/LayoutRight2LineMod.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/knowledge/LayoutRight2LineMod.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/knowledge/ParentChildType.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/knowledge/ParentChildType.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/knowledge/SelectionMod.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/knowledge/SelectionMod.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Anthropic.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Anthropic.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/AnthropicText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/AnthropicText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/AzureOpenaiService.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/AzureOpenaiService.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/AzureOpenaiServiceText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/AzureOpenaiServiceText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Azureai.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Azureai.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/AzureaiText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/AzureaiText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Baichuan.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Baichuan.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/BaichuanText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/BaichuanText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Chatglm.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Chatglm.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/ChatglmText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/ChatglmText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Cohere.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Cohere.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/CohereText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/CohereText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Gpt3.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Gpt3.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Gpt4.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Gpt4.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Huggingface.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Huggingface.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/HuggingfaceText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/HuggingfaceText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/HuggingfaceTextHub.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/HuggingfaceTextHub.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/IflytekSpark.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/IflytekSpark.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/IflytekSparkText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/IflytekSparkText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/IflytekSparkTextCn.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/IflytekSparkTextCn.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Jina.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Jina.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/JinaText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/JinaText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Localai.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Localai.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/LocalaiText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/LocalaiText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Microsoft.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Microsoft.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenaiBlack.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenaiBlack.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenaiBlue.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenaiBlue.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenaiGreen.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenaiGreen.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenaiText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenaiText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenaiTransparent.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenaiTransparent.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenaiViolet.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenaiViolet.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Openllm.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Openllm.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenllmText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/OpenllmText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Replicate.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Replicate.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/ReplicateText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/ReplicateText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/XorbitsInference.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/XorbitsInference.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/XorbitsInferenceText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/XorbitsInferenceText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Zhipuai.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/Zhipuai.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/ZhipuaiText.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/ZhipuaiText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/ZhipuaiTextCn.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/ZhipuaiTextCn.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/llm/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/model/Checked.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/model/Checked.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/other/DefaultToolIcon.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/other/DefaultToolIcon.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/other/Icon3Dots.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/other/Icon3Dots.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/other/RowStruct.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/other/RowStruct.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/other/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/plugins/Google.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/plugins/Google.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/plugins/WebReader.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/plugins/WebReader.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/plugins/Wikipedia.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/plugins/Wikipedia.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/plugins/index.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/thought/DataSet.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/thought/DataSet.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/thought/Loading.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/thought/Loading.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/thought/Search.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/thought/Search.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/thought/ThoughtList.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/thought/ThoughtList.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/thought/WebReader.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/thought/WebReader.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/LangfuseIcon.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/LangfuseIcon.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/LangfuseIconBig.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/LangfuseIconBig.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/LangsmithIcon.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/LangsmithIcon.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/LangsmithIconBig.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/LangsmithIconBig.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/OpikIcon.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/OpikIcon.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/OpikIconBig.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/OpikIconBig.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/TracingIcon.json 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/TracingIcon.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/public/tracing/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/features/Citations.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/features/ContentModeration.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/features/Document.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/features/FolderUpload.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/features/LoveMessage.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/features/MessageFast.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/features/Microphone01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/features/TextToAudio.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/features/VirtualAssistant.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/features/Vision.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/alertsAndFeedback/AlertTriangle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/alertsAndFeedback/ThumbsDown.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/alertsAndFeedback/ThumbsUp.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/arrows/ArrowNarrowLeft.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/arrows/ArrowUpRight.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/arrows/ChevronDownDouble.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/arrows/ChevronRight.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/arrows/ChevronSelectorVertical.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/arrows/RefreshCcw01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/arrows/RefreshCw05.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/arrows/ReverseLeft.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/communication/AiText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/communication/ChatBot.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/communication/ChatBotSlim.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/communication/CuteRobot.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/communication/MessageCheckRemove.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/communication/MessageFastPlus.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/ArtificialBrain.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/BarChartSquare02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/BracketsX.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/CodeBrowser.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/Container.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/Database01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/Database03.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/FileHeart02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/GitBranch01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/PromptEngineering.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/PuzzlePiece01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/TerminalSquare.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/Variable.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/development/Webhooks.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/editor/AlignLeft.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/editor/BezierCurve03.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/editor/Colors.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/editor/ImageIndentLeft.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/editor/LeftIndent02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/editor/LetterSpacing01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/editor/TypeSquare.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/editor/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/education/BookOpen01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/files/Clipboard.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/files/ClipboardCheck.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/files/File02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/files/FileArrow01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/files/FileCheck02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/files/FileDownload02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/files/FilePlus01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/files/FilePlus02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/files/FileText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/files/FileUpload.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/files/Folder.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/financeAndECommerce/Balance.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/financeAndECommerce/CoinsStacked01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/financeAndECommerce/GoldCoin.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/financeAndECommerce/ReceiptList.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/financeAndECommerce/Tag01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/financeAndECommerce/Tag03.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/AtSign.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Bookmark.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Check.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/CheckDone01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/ChecklistSquare.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/DotsGrid.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Edit02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Edit04.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Edit05.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Hash02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/InfoCircle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Link03.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/LinkExternal02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/LogIn04.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/LogOut01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/LogOut04.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Menu01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Pin01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Pin02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Plus02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Refresh.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Settings01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Settings04.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Target04.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/Upload03.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/UploadCloud01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/general/X.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/images/ImagePlus.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/layout/AlignLeft01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/layout/AlignRight01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/layout/Grid01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/layout/LayoutGrid02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/mapsAndTravel/Globe01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/mapsAndTravel/Route.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/mediaAndDevices/Microphone01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/mediaAndDevices/PlayCircle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/mediaAndDevices/SlidersH.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/mediaAndDevices/Speaker.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/mediaAndDevices/Stop.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/mediaAndDevices/StopCircle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/others/Apps02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/others/BubbleX.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/others/Colors.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/others/DragHandle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/others/Env.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/others/Exchange02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/others/FileCode.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/others/GlobalVariable.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/others/Icon3Dots.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/others/LongArrowLeft.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/others/LongArrowRight.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/others/Tools.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/shapes/CubeOutline.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/time/ClockFastForward.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/time/ClockPlay.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/time/ClockPlaySlim.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/time/ClockRefresh.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/users/User01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/users/Users01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/line/weather/Stars02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/other/Generator.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/other/ReplayLine.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/other/index.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/FinanceAndECommerce/GoldCoin.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/FinanceAndECommerce/Scales02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/alertsAndFeedback/AlertTriangle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/arrows/ChevronDown.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/arrows/HighPriority.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/communication/AiText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/communication/BubbleTextMod.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/communication/ChatBot.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/communication/CuteRobot.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/communication/EditList.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/communication/ListSparkle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/communication/Logic.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/communication/MessageDotsCircle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/communication/MessageFast.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/communication/MessageHeartCircle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/communication/MessageSmileSquare.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/communication/Send03.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/development/ApiConnection.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/development/ApiConnectionMod.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/development/BarChartSquare02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/development/Container.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/development/Database02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/development/Database03.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/development/FileHeart02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/development/PatternRecognition.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/development/PromptEngineering.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/development/PuzzlePiece01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/development/Semantic.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/development/TerminalSquare.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/development/Variable02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/editor/Brush01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/editor/Citations.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/editor/Colors.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/editor/Paragraph.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/editor/TypeSquare.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/education/Beaker02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/education/BubbleText.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/education/Heart02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/education/Unblur.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/files/File05.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/files/FileSearch02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/files/Folder.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/files/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/AnswerTriangle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/CheckCircle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/CheckDone01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/Download02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/Edit03.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/Edit04.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/Eye.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/MessageClockCircle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/PlusCircle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/QuestionTriangle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/SearchMd.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/Target04.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/Tool03.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/XCircle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/ZapFast.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/ZapNarrow.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/general/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/layout/Grid01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/mapsAndTravel/Globe06.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/mapsAndTravel/Route.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/mediaAndDevices/MagicBox.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/mediaAndDevices/MagicEyes.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/mediaAndDevices/MagicWand.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/mediaAndDevices/Microphone01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/mediaAndDevices/Play.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/mediaAndDevices/Robot.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/mediaAndDevices/Sliders02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/mediaAndDevices/Speaker.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/mediaAndDevices/StopCircle.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/mediaAndDevices/index.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/security/Lock01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/shapes/Corner.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/shapes/Star04.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/shapes/Star06.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/users/User01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/users/UserEdit02.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/users/Users01.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/solid/users/UsersPlus.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/Answer.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/Assigner.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/Code.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/DocsExtractor.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/End.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/Home.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/Http.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/IfElse.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/Iteration.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/IterationStart.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/Jinja.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/KnowledgeRetrieval.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/ListFilter.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/Llm.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/ParameterExtractor.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/QuestionClassifier.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/TemplatingTransform.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/VariableX.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/icons/src/vender/workflow/index.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/image-gallery/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/image-gallery/style.module.css 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/image-uploader/audio-preview.tsx 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/image-uploader/chat-image-uploader.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/image-uploader/image-link-input.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/image-uploader/image-list.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/image-uploader/image-preview.tsx 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/image-uploader/text-generation-image-uploader.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/image-uploader/uploader.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/image-uploader/video-preview.tsx 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/input-number/index.tsx 56 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/input/index.tsx 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/linked-apps-panel/index.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/list-empty/index.tsx 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/loading/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/logo/logo-embedded-chat-avatar.tsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/logo/logo-embedded-chat-header.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/logo/logo-site.tsx 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/markdown-blocks/button.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/markdown-blocks/form.tsx 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/markdown.tsx 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/mermaid/index.tsx 609 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/message-log-modal/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/modal/index.tsx 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/notion-icon/index.module.css 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/notion-icon/index.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/notion-page-selector/base.module.css 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/notion-page-selector/base.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/notion-page-selector/notion-page-selector-modal/index.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/notion-page-selector/notion-page-selector-modal/index.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/notion-page-selector/page-selector/index.module.css 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/notion-page-selector/page-selector/index.tsx 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/notion-page-selector/search-input/index.module.css 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/notion-page-selector/search-input/index.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/notion-page-selector/workspace-selector/index.module.css 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/notion-page-selector/workspace-selector/index.tsx 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/pagination/hook.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/pagination/index.tsx 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/pagination/pagination.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/pagination/type.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/param-item/index.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/param-item/score-threshold-item.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/param-item/top-k-item.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/popover/index.tsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/portal-to-follow-elem/index.tsx 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/premium-badge/index.css 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/premium-badge/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/progress-bar/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/constants.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/component-picker-block/hooks.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/component-picker-block/index.tsx 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/component-picker-block/menu.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/component-picker-block/prompt-option.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/component-picker-block/variable-option.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/context-block/component.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/context-block/context-block-replacement-block.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/context-block/index.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/context-block/node.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/custom-text/node.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/history-block/component.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/history-block/history-block-replacement-block.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/history-block/index.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/history-block/node.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/on-blur-or-focus-block.tsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/placeholder.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/query-block/component.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/query-block/node.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/variable-value-block/node.tsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx 53 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/workflow-variable-block/index.tsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/plugins/workflow-variable-block/workflow-variable-block-replacement-block.tsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/types.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-editor/utils.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-log-modal/card.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/prompt-log-modal/index.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/qrcode/index.tsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/qrcode/style.module.css 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/radio-card/index.tsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/radio-card/simple/index.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/radio-card/simple/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/radio/component/group/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/radio/component/radio/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/radio/ui.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/regenerate-btn/index.tsx 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/search-input/index.tsx 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/select/index.tsx 93 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/select/locale.tsx 75 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/skeleton/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/slider/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/slider/style.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/sort/index.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/spinner/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/svg-gallery/index.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/svg/index.tsx 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/switch/index.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/tab-header/index.tsx 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/tab-header/style.module.css 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/tab-slider-new/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/tab-slider-plain/index.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/tab-slider/index.tsx 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/tag-input/index.tsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/tag-management/filter.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/tag-management/index.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/tag-management/selector.tsx 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/tag-management/style.module.css 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/tag-management/tag-item-editor.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/tag-management/tag-remove-modal.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/text-generation/types.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/textarea/index.tsx 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/toast/index.tsx 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/toast/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/tooltip/index.tsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/video-gallery/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/voice-input/index.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/base/voice-input/index.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/annotation-full/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/annotation-full/modal.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/annotation-full/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/apps-full-in-dialog/index.tsx 75 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/apps-full-in-dialog/style.module.css 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/apps-full/index.tsx 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/apps-full/style.module.css 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/billing-page/index.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/config.ts 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/header-billing-btn/index.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/plan/index.tsx 164 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/pricing/index.tsx 102 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/pricing/plan-item.tsx 290 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/pricing/select-plan-range.tsx 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/priority-label/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/progress-bar/index.tsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/type.ts 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/upgrade-btn/index.tsx 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/upgrade-btn/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/usage-info/apps-info.tsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/usage-info/index.tsx 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/usage-info/vector-space-info.tsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/vector-space-full/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/billing/vector-space-full/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/browser-initor.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/custom/custom-page/index.tsx 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/custom/custom-web-app-brand/index.tsx 212 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/custom/custom-web-app-brand/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/custom/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/api/index.tsx 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/chunk.tsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/common/chunking-mode-label.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/common/document-picker/document-list.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/common/document-picker/index.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/common/document-picker/preview-document-picker.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/common/document-status-with-action/index-failed.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/common/document-status-with-action/status-with-action.tsx 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/common/economical-retrieval-method-config/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/common/retrieval-method-config/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/common/retrieval-method-info/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/common/retrieval-param-config/index.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/embedding-process/index.tsx 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/empty-dataset-creation-modal/index.module.css 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/empty-dataset-creation-modal/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/file-preview/index.module.css 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/file-preview/index.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/file-uploader/index.tsx 99 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/notion-page-preview/index.module.css 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/notion-page-preview/index.tsx 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/step-one/index.module.css 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/step-one/index.tsx 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/step-three/index.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/step-two/index.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/step-two/index.tsx 117 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/step-two/inputs.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/step-two/language-select/index.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/step-two/option-card.tsx 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/step-two/preview-item/index.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/step-two/unescape.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/stepper/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/stepper/step.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/steps-nav-bar/index.module.css 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/steps-nav-bar/index.tsx 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/stop-embedding-modal/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/top-bar/index.tsx 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/base/checkbox-with-label.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/base/crawled-result-item.tsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/base/crawled-result.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/base/crawling.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/base/error-message.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/base/field.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/base/input.tsx 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/base/options-wrap.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/base/url-input.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/firecrawl/header.tsx 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/firecrawl/index.tsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/firecrawl/options.tsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/index.module.css 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/index.tsx 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/jina-reader/base/checkbox-with-label.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/jina-reader/base/error-message.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/jina-reader/base/field.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/jina-reader/base/input.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/jina-reader/base/options-wrap.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/jina-reader/crawled-result-item.tsx 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/jina-reader/crawled-result.tsx 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/jina-reader/crawling.tsx 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/jina-reader/header.tsx 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/jina-reader/index.tsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/jina-reader/mock-crawl-result.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/jina-reader/options.tsx 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/no-data.tsx 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/create/website/preview.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/batch-modal/csv-downloader.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/batch-modal/index.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/SegmentCard.tsx 259 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/child-segment-detail.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/child-segment-list.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/common/action-buttons.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/common/add-another.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/common/batch-action.tsx 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/common/chunk-content.tsx 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/common/dot.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/common/empty.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/common/full-screen-drawer.tsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/common/keywords.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/common/regeneration-modal.tsx 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/common/segment-index-tag.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/common/tag.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/display-toggle.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/index.tsx 73 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/new-child-segment.tsx 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/segment-card.tsx 280 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/segment-detail.tsx 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/segment-list.tsx 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/skeleton/full-doc-list-skeleton.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/skeleton/general-list-skeleton.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/skeleton/paragraph-list-skeleton.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/skeleton/parent-chunk-card-skeleton.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/completed/status-item.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/embedding/index.tsx 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/embedding/skeleton/index.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/index.tsx 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/metadata/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/new-segment.tsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/segment-add/index.tsx 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/detail/settings/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/index.tsx 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/list.tsx 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/rename-modal.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/documents/style.module.css 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/external-api/external-api-modal/Form.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/external-api/external-api-modal/index.tsx 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/external-api/external-api-panel/index.tsx 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/external-api/external-knowledge-api-card/index.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/external-knowledge-base/create/ExternalApiSelect.tsx 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/external-knowledge-base/create/ExternalApiSelection.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/external-knowledge-base/create/InfoPanel.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/external-knowledge-base/create/KnowledgeBaseInfo.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/external-knowledge-base/create/RetrievalSettings.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/external-knowledge-base/create/index.tsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/formatted-text/flavours/edit-slice.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/formatted-text/flavours/preview-slice.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/formatted-text/flavours/shared.tsx 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/hit-testing/components/child-chunks-item.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/hit-testing/components/chunk-detail-modal.tsx 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/hit-testing/components/result-item-external.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/hit-testing/components/result-item-footer.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/hit-testing/components/result-item-meta.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/hit-testing/components/result-item.tsx 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/hit-testing/components/score.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/hit-testing/index.tsx 56 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/hit-testing/modify-external-retrieval-modal.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/hit-testing/modify-retrieval-modal.tsx 41 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/hit-testing/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/hit-testing/textarea.tsx 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/preview/container.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/preview/header.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/rename-modal/index.tsx 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/settings/form/index.tsx 52 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/settings/index-method-radio/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/datasets/settings/permission-selector/index.tsx 105 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/code.tsx 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/doc.tsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/index.tsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/md.tsx 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/secret-key/input-copy.tsx 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/secret-key/secret-key-button.tsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/secret-key/secret-key-generate.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/secret-key/secret-key-modal.tsx 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/secret-key/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/template/template.en.mdx 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/template/template.ja.mdx 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/template/template.zh.mdx 421 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/template/template_advanced_chat.en.mdx 537 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/template/template_advanced_chat.ja.mdx 243 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/template/template_advanced_chat.zh.mdx 539 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/template/template_chat.en.mdx 551 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/template/template_chat.ja.mdx 253 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/template/template_chat.zh.mdx 245 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/template/template_workflow.en.mdx 101 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/template/template_workflow.ja.mdx 103 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/develop/template/template_workflow.zh.mdx 111 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/explore/app-card/index.tsx 52 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/explore/app-list/index.tsx 96 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/explore/app-list/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/explore/category.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/explore/create-app-modal/index.tsx 61 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/explore/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/explore/installed-app/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/explore/item-operation/index.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/explore/item-operation/style.module.css 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/explore/sidebar/app-nav-item/index.tsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/explore/sidebar/app-nav-item/style.module.css 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/explore/sidebar/index.tsx 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-about/index.module.css 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-about/index.tsx 45 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-dropdown/index.tsx 274 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-dropdown/workplace-selector/index.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-dropdown/workplace-selector/index.tsx 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/Integrations-page/index.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/Integrations-page/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/api-based-extension-page/empty.tsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/api-based-extension-page/index.tsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/api-based-extension-page/item.tsx 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/api-based-extension-page/modal.tsx 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/api-based-extension-page/selector.tsx 36 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/collapse/index.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/data-source-page/data-source-notion/index.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/data-source-page/data-source-notion/operate/index.tsx 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/data-source-page/data-source-website/config-firecrawl-modal.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/data-source-page/data-source-website/config-jina-reader-modal.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/data-source-page/data-source-website/index.tsx 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/data-source-page/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/data-source-page/panel/config-item.tsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/data-source-page/panel/index.tsx 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/data-source-page/panel/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/index.module.css 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/index.tsx 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/key-validator/KeyInput.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/key-validator/Operate.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/key-validator/ValidateStatus.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/key-validator/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/language-page/index.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/members-page/index.tsx 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/members-page/invite-modal/index.tsx 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/members-page/invite-modal/role-selector.tsx 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/members-page/invited-modal/index.module.css 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/members-page/invited-modal/index.tsx 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/members-page/operation/index.module.css 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/members-page/operation/index.tsx 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/declarations.ts 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/hooks.ts 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/index.tsx 163 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-icon/index.tsx 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-modal/Form.tsx 306 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-modal/Input.tsx 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-modal/index.tsx 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-modal/model-load-balancing-entry-modal.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-name/index.tsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-parameter-modal/stop-sequence.tsx 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-selector/empty-trigger.tsx 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-selector/feature-icon.tsx 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-selector/index.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx 108 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-selector/popup.tsx 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/model-selector/rerank-trigger.tsx 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-added-card/add-model-button.tsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-added-card/cooldown-timer.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-added-card/priority-use-tip.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-added-card/tab.tsx 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-card/index.module.css 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-card/index.tsx 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/provider-icon/index.tsx 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/model-provider-page/utils.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/account-setting/plugin-page/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/app-back/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/app-nav/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/app-selector/index.tsx 45 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/dataset-nav/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/env-nav/index.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/explore-nav/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/github-star/index.tsx 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/index.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/index.tsx 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/license-env/index.tsx 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/maintenance-notice.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/nav/index.tsx 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/nav/nav-selector/index.tsx 86 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/header/tools-nav/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/i18n-server.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/i18n.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/sentry-initor.tsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/share/text-generation/index.tsx 245 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/share/text-generation/no-data/index.tsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/share/text-generation/result/content.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/share/text-generation/result/header.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/share/text-generation/result/index.tsx 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/share/text-generation/run-batch/csv-download/index.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/share/text-generation/run-batch/csv-reader/index.tsx 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/share/text-generation/run-batch/csv-reader/style.module.css 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/share/text-generation/run-batch/index.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/share/text-generation/run-batch/res-download/index.tsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/share/text-generation/run-once/index.tsx 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/share/text-generation/style.module.css 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/share/utils.ts 50 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/signin/countdown.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/swr-initor.tsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/add-tool-modal/category.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/add-tool-modal/empty.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/add-tool-modal/index.tsx 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/add-tool-modal/tools.tsx 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/add-tool-modal/type.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/edit-custom-collection-modal/config-credentials.tsx 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/edit-custom-collection-modal/examples.ts 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/edit-custom-collection-modal/get-schema.tsx 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/edit-custom-collection-modal/index.tsx 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/edit-custom-collection-modal/test-api.tsx 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/labels/constant.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/labels/filter.tsx 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/labels/selector.tsx 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/labels/store.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/provider-list.tsx 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/provider/card.tsx 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/provider/contribute.tsx 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/provider/custom-create-card.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/provider/detail.tsx 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/provider/grid_bg.svg 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/provider/tool-item.tsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/setting/build-in/config-credentials.tsx 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/types.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/utils/to-form-schema.ts 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/workflow-tool/configure-button.tsx 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/workflow-tool/confirm-modal/index.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/workflow-tool/confirm-modal/style.module.css 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/workflow-tool/index.tsx 58 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/tools/workflow-tool/method-selector.tsx 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/block-icon.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/block-selector/all-tools.tsx 119 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/block-selector/blocks.tsx 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/block-selector/constants.tsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/block-selector/hooks.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/block-selector/index-bar.tsx 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/block-selector/index.tsx 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/block-selector/tabs.tsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/block-selector/tools.tsx 166 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/block-selector/types.ts 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/candidate-node.tsx 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/constants.ts 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/context.tsx 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/custom-edge.tsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/dsl-export-confirm-modal.tsx 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/chat-variable-button.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/checklist.tsx 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/editing-title.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/env-button.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/global-variable-button.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/index.tsx 234 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/restoring-title.tsx 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/run-and-history.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/running-title.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/undo-redo.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/version-history-item.tsx 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/version-history-modal.tsx 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/view-history.tsx 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/header/view-workflow-history.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/help-line/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/hooks/index.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/hooks/use-checklist.ts 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/hooks/use-edges-interactions.ts 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/hooks/use-helpline.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/hooks/use-nodes-data.ts 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/hooks/use-nodes-interactions.ts 307 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/hooks/use-nodes-sync-draft.ts 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/hooks/use-workflow-interactions.ts 155 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/hooks/use-workflow-run.ts 784 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/hooks/use-workflow-start-run.tsx 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/hooks/use-workflow-template.ts 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/hooks/use-workflow-variables.ts 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/hooks/use-workflow.ts 177 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/index.tsx 203 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/limit-tips.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/add-button.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/add-variable-popup-with-position.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/add-variable-popup.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/before-run-form/form.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/before-run-form/index.tsx 75 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/code-generator-button.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/collapse/field-collapse.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/collapse/index.tsx 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/editor/base.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/editor/code-editor/style.css 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/editor/text-editor.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/error-handle/default-value.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/error-handle/error-handle-on-node.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/error-handle/error-handle-tip.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/error-handle/error-handle-type-selector.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/error-handle/fail-branch-card.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/field.tsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/file-type-item.tsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/file-upload-setting.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/help-link.tsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/info-panel.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/input-number-with-slider.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/input-support-select-var.tsx 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/list-no-data-placeholder.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/memory-config.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/next-step/add.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/next-step/container.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/next-step/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/next-step/item.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/next-step/line.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/next-step/operator.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/node-control.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/node-handle.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/node-resizer.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/option-card.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/output-vars.tsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/panel-operator/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/panel-operator/panel-operator-popup.tsx 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/prompt/editor.tsx 67 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/readonly-input-with-select-var.tsx 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/remove-button.tsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/retry/retry-on-panel.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/retry/style.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/selector.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/support-var-input/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/title-description-input.tsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/toggle-expand-btn.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/variable-tag.tsx 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/variable/assigned-var-reference-popup.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/variable/constant-field.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/variable/output-var-list.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/variable/utils.ts 392 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/variable/var-list.tsx 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/components/variable/var-type-picker.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/hooks/use-available-var-list.ts 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/hooks/use-node-help-link.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/hooks/use-node-info.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/hooks/use-one-step-run.ts 193 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/hooks/use-toggle-expend.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/node.tsx 93 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/_base/panel.tsx 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/answer/panel.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/assigner/components/operation-selector.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/assigner/components/var-list/index.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/assigner/default.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/assigner/node.tsx 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/assigner/panel.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/assigner/types.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/assigner/use-config.ts 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/code/code-parser.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/code/dependency-picker.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/code/panel.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/code/use-config.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/code/utils.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/constants.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/document-extractor/default.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/document-extractor/node.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/document-extractor/panel.tsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/document-extractor/use-config.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/end/default.ts 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/end/node.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/end/panel.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/components/api-input.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/components/authorization/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/components/authorization/radio-group.tsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/components/curl-panel.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/components/edit-body/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/components/key-value/bulk-edit/index.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/components/key-value/key-value-edit/index.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/components/key-value/key-value-edit/input-item.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/components/timeout/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/hooks/use-key-value-list.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/node.tsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/panel.tsx 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/http/use-config.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/components/condition-add.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/components/condition-list/condition-var-selector.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/components/condition-list/index.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/components/condition-number-input.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/components/condition-value.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/components/condition-wrap.tsx 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/default.ts 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/node.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/panel.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/types.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/use-config.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/if-else/use-is-var-file-attribute.ts 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/iteration-start/index.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/iteration/add-block.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/iteration/node.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/iteration/panel.tsx 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/iteration/use-interactions.ts 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/knowledge-retrieval/node.tsx 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/knowledge-retrieval/panel.tsx 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/knowledge-retrieval/types.ts 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/knowledge-retrieval/use-config.ts 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/knowledge-retrieval/utils.ts 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/list-operator/components/extract-input.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/list-operator/components/filter-condition.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/list-operator/components/limit-config.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/list-operator/node.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/list-operator/panel.tsx 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/list-operator/use-config.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/llm/components/config-prompt-item.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/llm/components/config-prompt.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/llm/components/resolution-picker.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/llm/node.tsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/llm/panel.tsx 104 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/llm/types.ts 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/llm/use-config.ts 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/llm/utils.ts 333 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/parameter-extractor/components/extract-parameter/import-from-tool.tsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/parameter-extractor/components/extract-parameter/item.tsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/parameter-extractor/node.tsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/parameter-extractor/panel.tsx 63 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/parameter-extractor/use-config.ts 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/question-classifier/components/class-item.tsx 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/question-classifier/components/class-list.tsx 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/question-classifier/node.tsx 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/question-classifier/panel.tsx 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/question-classifier/use-config.ts 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/question-classifier/utils.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/start/components/var-item.tsx 33 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/start/components/var-list.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/start/node.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/start/panel.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/template-transform/panel.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/tool/components/input-var-list.tsx 92 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/tool/node.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/tool/panel.tsx 63 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/tool/types.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/tool/use-config.ts 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/variable-assigner/components/add-variable/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/variable-assigner/components/node-group-item.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx 87 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/variable-assigner/components/var-list/index.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/variable-assigner/default.ts 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/variable-assigner/node.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/variable-assigner/panel.tsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/nodes/variable-assigner/use-config.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/note-node/index.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/note-node/note-editor/context.tsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/note-node/note-editor/editor.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/component.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/note-node/note-editor/theme/theme.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/note-node/note-editor/toolbar/color-picker.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/note-node/note-editor/toolbar/command.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/note-node/note-editor/toolbar/divider.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/note-node/note-editor/toolbar/font-size-selector.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/note-node/note-editor/toolbar/index.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/note-node/note-editor/toolbar/operator.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/note-node/note-editor/utils.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/operator/add-block.tsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/operator/control.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/operator/index.tsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/operator/tip-popup.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/operator/zoom-in-out.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel-contextmenu.tsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/chat-record/index.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/chat-record/user-input.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/chat-variable-panel/components/object-value-list.tsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/chat-variable-panel/components/variable-item.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/chat-variable-panel/index.tsx 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/debug-and-preview/conversation-variable-modal.tsx 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/debug-and-preview/empty.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/debug-and-preview/hooks.ts 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/debug-and-preview/index.tsx 73 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/debug-and-preview/user-input.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/env-panel/env-item.tsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/env-panel/index.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/env-panel/variable-modal.tsx 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/env-panel/variable-trigger.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/global-variable-panel/index.tsx 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/global-variable-panel/item.tsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/index.tsx 78 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/inputs-panel.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/record.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/panel/workflow-preview.tsx 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/run/index.tsx 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/run/iteration-result-panel.tsx 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/run/meta.tsx 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/run/node.tsx 182 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/run/output-panel.tsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/run/result-panel.tsx 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/run/result-text.tsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/run/retry-result-panel.tsx 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/run/status-container.tsx 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/run/status.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/run/tracing-panel.tsx 247 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/shortcuts-name.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/store.ts 311 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/style.css 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/types.ts 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/update-dsl-modal.tsx 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/utils.ts 814 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/components/workflow/workflow-history-store.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/forgot-password/ChangePasswordForm.tsx 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/forgot-password/ForgotPasswordForm.tsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/forgot-password/page.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/init/InitPasswordPopup.tsx 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/init/page.tsx 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/install/installForm.tsx 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/install/page.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/layout.tsx 33 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/page.module.css 补丁 | 查看 | 原始文档 | blame | 历史
app/page.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/reset-password/check-code/page.tsx 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/reset-password/layout.tsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/reset-password/page.tsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/reset-password/set-password/page.tsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/signin/_header.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/signin/assets/background.png 补丁 | 查看 | 原始文档 | blame | 历史
app/signin/check-code/page.tsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/signin/components/mail-and-code-auth.tsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/signin/components/mail-and-password-auth.tsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/signin/components/sso-auth.tsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/signin/invite-settings/page.tsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/signin/layout.tsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/signin/normalForm.tsx 76 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/signin/oneMoreStep.tsx 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/signin/page.module.css 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/styles/globals.css 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/styles/markdown.scss 231 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/styles/preflight.css 补丁 | 查看 | 原始文档 | blame | 历史
config/index.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
context/app-context.tsx 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
context/datasets-context.tsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
context/debug-configuration.ts 62 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
context/explore-context.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
context/i18n.ts 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
context/modal-context.tsx 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
context/provider-context.tsx 87 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docker/entrypoint.sh 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hooks/use-metadata.ts 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hooks/use-pay.tsx 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hooks/use-tab-searchparams.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hooks/use-timestamp.ts 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/auto-gen-i18n.js 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/check-i18n.js 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/dataset-documents.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/dataset-hit-testing.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/de-DE/workflow.ts 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/app-debug.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/app.ts 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/billing.ts 134 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/common.ts 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/dataset-creation.ts 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/login.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/share-app.ts 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/tools.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/en-US/workflow.ts 215 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/app.ts 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/common.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/dataset.ts 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/es-ES/workflow.ts 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fa-IR/workflow.ts 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/app.ts 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/common.ts 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/dataset.ts 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/fr-FR/workflow.ts 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/dataset-creation.ts 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/share-app.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/hi-IN/workflow.ts 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/i18next-config.ts 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/index.ts 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/app.ts 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/dataset-creation.ts 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/dataset.ts 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/it-IT/workflow.ts 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/app-annotation.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/app.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/billing.ts 136 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/dataset-creation.ts 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/dataset-documents.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/dataset-settings.ts 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/dataset.ts 116 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/run-log.ts 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/share-app.ts 96 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/tools.ts 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ja-JP/workflow.ts 576 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ko-KR/workflow.ts 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/language.ts 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pl-PL/workflow.ts 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/pt-BR/workflow.ts 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ro-RO/workflow.ts 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/ru-RU/workflow.ts 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/server.ts 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/common.ts 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/sl-SI/workflow.ts 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/app-debug.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/th-TH/workflow.ts 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/tr-TR/workflow.ts 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/uk-UA/workflow.ts 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/app-overview.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/vi-VN/workflow.ts 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/app-debug.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/app-overview.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/billing.ts 132 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/common.ts 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/dataset-creation.ts 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/share-app.ts 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/tools.ts 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hans/workflow.ts 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/app-overview.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/app.ts 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/billing.ts 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/common.ts 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/custom.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/dataset-creation.ts 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/dataset-documents.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/dataset-settings.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/dataset.ts 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/explore.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/run-log.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/share-app.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/tools.ts 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
i18n/zh-Hant/workflow.ts 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
jest.config.ts 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
middleware.ts 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/app.ts 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/common.ts 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/datasets.ts 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
models/debug.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
next.config.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/embed.js 273 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/embed.min.js 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/favicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
public/logo/logo-embedded-chat-header.png 补丁 | 查看 | 原始文档 | blame | 历史
public/logo/logo-site-dark.png 补丁 | 查看 | 原始文档 | blame | 历史
public/logo/logo-site.png 补丁 | 查看 | 原始文档 | blame | 历史
public/vs/language/typescript/tsWorker.js 389 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/base.ts 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/common.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/datasets.ts 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/knowledge/use-create-dataset.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/knowledge/use-dateset.ts 补丁 | 查看 | 原始文档 | blame | 历史
service/knowledge/use-document.ts 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/log.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/refresh-token.ts 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/share.ts 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/tools.ts 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/use-base.ts 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/use-workflow.ts 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/workflow.ts 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
tailwind.config.js 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
themes/dark.css 1178 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
themes/light.css 1186 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
themes/manual-dark.css 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
themes/manual-light.css 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
themes/tailwind-theme-var-define.ts 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
types/app.ts 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
types/feature.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
types/workflow.ts 201 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
typography.js 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/app-redirection.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/classnames.spec.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/format.spec.ts 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/format.ts 52 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/index.ts 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/model-config.ts 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/timezone.json 补丁 | 查看 | 原始文档 | blame | 历史
utils/var.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yarn.lock 13310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.dockerignore
.editorconfig
New file
@@ -0,0 +1,22 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,tsx}]
charset = utf-8
indent_style = space
indent_size = 2
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2
.env.example
@@ -6,16 +6,10 @@
# different from api or web app domain.
# example: http://cloud.dify.ai/console/api
NEXT_PUBLIC_API_PREFIX=http://localhost:5001/console/api
NEXT_PUBLIC_WEB_PREFIX=http://localhost:3000
# The URL for Web APP, refers to the Web App base URL of WEB service if web app domain is different from
# console or api domain.
# example: http://udify.app/api
NEXT_PUBLIC_PUBLIC_API_PREFIX=http://localhost:5001/api
NEXT_PUBLIC_PUBLIC_WEB_PREFIX=http://localhost:3000
# The API PREFIX for MARKETPLACE
NEXT_PUBLIC_MARKETPLACE_API_PREFIX=https://marketplace.dify.ai/api/v1
# The URL for MARKETPLACE
NEXT_PUBLIC_MARKETPLACE_URL_PREFIX=https://marketplace.dify.ai
# SENTRY
NEXT_PUBLIC_SENTRY_DSN=
@@ -31,30 +25,9 @@
# CSP https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
NEXT_PUBLIC_CSP_WHITELIST=
# Default is not allow to embed into iframe to prevent Clickjacking: https://owasp.org/www-community/attacks/Clickjacking
NEXT_PUBLIC_ALLOW_EMBED=
# Github Access Token, used for invoking Github API
NEXT_PUBLIC_GITHUB_ACCESS_TOKEN=
# The maximum number of top-k value for RAG.
NEXT_PUBLIC_TOP_K_MAX_VALUE=10
# The maximum number of tokens for segmentation
NEXT_PUBLIC_INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH=4000
# Maximum loop count in the workflow
NEXT_PUBLIC_LOOP_NODE_MAX_COUNT=100
# Maximum number of tools in the agent/workflow
NEXT_PUBLIC_MAX_TOOLS_NUM=10
# Maximum number of Parallelism branches in the workflow
NEXT_PUBLIC_MAX_PARALLEL_LIMIT=10
# The maximum number of iterations for agent setting
NEXT_PUBLIC_MAX_ITERATIONS_NUM=5
NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER=true
NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL=true
NEXT_PUBLIC_ENABLE_WEBSITE_WATERCRAWL=true
.eslintignore
New file
@@ -0,0 +1,7 @@
/**/node_modules/*
node_modules/
dist/
build/
out/
.next/
.eslintrc.json
New file
@@ -0,0 +1,31 @@
{
  "extends": [
    "next",
    "@antfu",
    "plugin:storybook/recommended"
  ],
  "rules": {
    "@typescript-eslint/consistent-type-definitions": [
      "error",
      "type"
    ],
    "@typescript-eslint/no-var-requires": "off",
    "no-console": "off",
    "indent": "off",
    "@typescript-eslint/indent": [
      "error",
      2,
      {
        "SwitchCase": 1,
        "flatTernaryExpressions": false,
        "ignoredNodes": [
          "PropertyDefinition[decorators]",
          "TSUnionType",
          "FunctionExpression[params]:has(Identifier[decorators])"
        ]
      }
    ],
    "react-hooks/exhaustive-deps": "warn",
    "react/display-name": "warn"
  }
}
.gitignore
@@ -44,11 +44,12 @@
.pnp.cjs
.pnp.loader.mjs
.yarn/
.yarnrc.yml
# pmpm
pnpm-lock.yaml
.favorites.json
# storybook
/storybook-static
*storybook.log
# mise
.husky/pre-commit
@@ -1,4 +1,6 @@
#!/bin/sh
#!/usr/bin/env bash
. "$(dirname -- "$0")/_/husky.sh"
# get the list of modified files
files=$(git diff --cached --name-only)
@@ -9,17 +11,13 @@
for file in $files
do
    # Use POSIX compliant pattern matching
    case "$file" in
        api/*.py)
    if [[ $file == "api/"* && $file == *.py ]]; then
            # set api_modified flag to true
            api_modified=true
            ;;
        web/*)
    elif [[ $file == "web/"* ]]; then
            # set web_modified flag to true
            web_modified=true
            ;;
    esac
    fi
done
# run linters based on the modified modules
@@ -27,11 +25,17 @@
if $api_modified; then
    echo "Running Ruff linter on api module"
    # python style checks rely on `ruff` in path
    if ! command -v ruff &> /dev/null; then
        echo "Installing linting tools (Ruff, dotenv-linter ...) ..."
        poetry install -C api --only lint
    fi
    # run Ruff linter auto-fixing
    uv run --project api --dev ruff check --fix ./api
    ruff check --fix ./api
    # run Ruff linter checks
    uv run --project api --dev ruff check  ./api || status=$?
    ruff check --preview ./api || status=$?
    status=${status:-0}
@@ -46,7 +50,7 @@
if $web_modified; then
    echo "Running ESLint on web module"
    cd ./web || exit 1
    lint-staged
    npx lint-staged
    echo "Running unit tests check"
    modified_files=$(git diff --cached --name-only -- utils | grep -v '\.spec\.ts$' || true)
@@ -59,7 +63,7 @@
            # check if the test file exists
            if [ -f "../$test_file" ]; then
                echo "Detected changes in $file, running corresponding unit tests..."
                pnpm run test "../$test_file"
                npm run test "../$test_file"
                if [ $? -ne 0 ]; then
                    echo "Unit tests failed. Please fix the errors before committing."
.idea/.gitignore
New file
@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
.idea/modules.xml
New file
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="ProjectModuleManager">
    <modules>
      <module fileurl="file://$PROJECT_DIR$/.idea/web.iml" filepath="$PROJECT_DIR$/.idea/web.iml" />
    </modules>
  </component>
</project>
.idea/vcs.xml
New file
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="VcsDirectoryMappings">
    <mapping directory="$PROJECT_DIR$/.." vcs="Git" />
  </component>
</project>
.idea/web.iml
New file
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
  <component name="NewModuleRootManager">
    <content url="file://$MODULE_DIR$">
      <excludeFolder url="file://$MODULE_DIR$/.tmp" />
      <excludeFolder url="file://$MODULE_DIR$/temp" />
      <excludeFolder url="file://$MODULE_DIR$/tmp" />
    </content>
    <orderEntry type="inheritedJdk" />
    <orderEntry type="sourceFolder" forTests="false" />
  </component>
</module>
.storybook/main.ts
.storybook/preview.tsx
@@ -1,6 +1,6 @@
import React from 'react'
import type { Preview } from '@storybook/react'
import { withThemeByDataAttribute } from '@storybook/addon-themes'
import { withThemeByDataAttribute } from '@storybook/addon-themes';
import I18nServer from '../app/components/i18n-server'
import '../app/styles/globals.css'
@@ -16,12 +16,12 @@
    defaultTheme: 'light',
    attributeName: 'data-theme',
  }),
  (Story) => {
    Story => {
    return <I18nServer>
      <Story />
    </I18nServer>
  },
]
    }
  ];
const preview: Preview = {
  parameters: {
.vscode/extensions.json
@@ -1,7 +1,6 @@
{
  "recommendations": [
    "bradlc.vscode-tailwindcss",
    "firsttris.vscode-jest-runner",
    "kisstkondoros.vscode-codemetrics"
    "firsttris.vscode-jest-runner"
  ]
}
.vscode/settings.example.json
@@ -21,6 +21,5 @@
    "editor.defaultFormatter": "vscode.json-language-features"
  },
  "typescript.tsdk": "node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true,
  "npm.packageManager": "pnpm"
  "typescript.enablePromptUseWorkspaceTsdk": true
}
Dockerfile
@@ -1,14 +1,11 @@
# base image
FROM node:22-alpine3.21 AS base
FROM node:20-alpine3.20 AS base
LABEL maintainer="takatost@gmail.com"
# if you located in China, you can use aliyun mirror to speed up
# RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk add --no-cache tzdata
RUN npm install -g pnpm@10.8.0
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
# install packages
@@ -17,12 +14,12 @@
WORKDIR /app/web
COPY package.json .
COPY pnpm-lock.yaml .
COPY yarn.lock .
# if you located in China, you can use taobao registry to speed up
# RUN pnpm install --frozen-lockfile --registry https://registry.npmmirror.com/
# RUN yarn install --frozen-lockfile --registry https://registry.npmmirror.com/
RUN pnpm install --frozen-lockfile
RUN yarn install --frozen-lockfile
# build resources
FROM base AS builder
@@ -30,8 +27,7 @@
COPY --from=packages /app/web/ .
COPY . .
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN pnpm build
RUN yarn build
# production stage
@@ -42,11 +38,8 @@
ENV DEPLOY_ENV=PRODUCTION
ENV CONSOLE_API_URL=http://127.0.0.1:5001
ENV APP_API_URL=http://127.0.0.1:5001
ENV MARKETPLACE_API_URL=https://marketplace.dify.ai
ENV MARKETPLACE_URL=https://marketplace.dify.ai
ENV PORT=3000
ENV NEXT_TELEMETRY_DISABLED=1
ENV PM2_INSTANCES=2
# set timezone
ENV TZ=UTC
@@ -59,11 +52,13 @@
COPY --from=builder /app/web/.next/standalone ./
COPY --from=builder /app/web/.next/static ./.next/static
COPY docker/pm2.json ./pm2.json
COPY docker/entrypoint.sh ./entrypoint.sh
# global runtime packages
RUN pnpm add -g pm2 \
RUN yarn global add pm2 \
    && yarn cache clean \
    && mkdir /.pm2 \
    && chown -R 1001:0 /.pm2 /app/web \
    && chmod -R g=u /.pm2 /app/web
README.md
@@ -6,14 +6,14 @@
### Run by source code
Before starting the web frontend service, please make sure the following environment is ready.
- [Node.js](https://nodejs.org) >= v22.11.x
- [pnpm](https://pnpm.io) v10.x
To start the web frontend service, you will need [Node.js v18.x (LTS)](https://nodejs.org/en) and [NPM version 8.x.x](https://www.npmjs.com/) or [Yarn](https://yarnpkg.com/).
First, install the dependencies:
```bash
pnpm install
npm install
# or
yarn install --frozen-lockfile
```
Then, configure the environment variables. Create a file named `.env.local` in the current directory and copy the contents from `.env.example`. Modify the values of these environment variables according to your requirements:
@@ -31,12 +31,10 @@
# different from api or web app domain.
# example: http://cloud.dify.ai/console/api
NEXT_PUBLIC_API_PREFIX=http://localhost:5001/console/api
NEXT_PUBLIC_WEB_PREFIX=http://localhost:3000
# The URL for Web APP, refers to the Web App base URL of WEB service if web app domain is different from
# console or api domain.
# example: http://udify.app/api
NEXT_PUBLIC_PUBLIC_API_PREFIX=http://localhost:5001/api
NEXT_PUBLIC_PUBLIC_WEB_PREFIX=http://localhost:3000
# SENTRY
NEXT_PUBLIC_SENTRY_DSN=
@@ -45,7 +43,9 @@
Finally, run the development server:
```bash
pnpm run dev
npm run dev
# or
yarn dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
@@ -59,22 +59,20 @@
First, build the app for production:
```bash
pnpm run build
npm run build
```
Then, start the server:
```bash
pnpm run start
npm run start
```
If you want to customize the host and port:
```bash
pnpm run start --port=3001 --host=0.0.0.0
npm run start --port=3001 --host=0.0.0.0
```
If you want to customize the number of instances launched by PM2, you can configure `PM2_INSTANCES` in `docker-compose.yaml` or `Dockerfile`.
## Storybook
@@ -83,7 +81,7 @@
To start the storybook server, run:
```bash
pnpm storybook
yarn storybook
```
Open [http://localhost:6006](http://localhost:6006) with your browser to see the result.
@@ -101,7 +99,7 @@
Run test:
```bash
pnpm run test
npm run test
```
If you are not familiar with writing tests, here is some code to refer to:
app/(commonLayout)/app/(appDetailLayout)/[appId]/annotations/page.tsx
@@ -3,7 +3,7 @@
import { PageType } from '@/app/components/base/features/new-feature-panel/annotation-reply/type'
export type IProps = {
  params: Promise<{ appId: string }>
  params: { appId: string }
}
const Logs = async () => {
app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx
@@ -1,18 +1,14 @@
import React from 'react'
import type { Locale } from '@/i18n'
import { type Locale } from '@/i18n'
import DevelopMain from '@/app/components/develop'
export type IDevelopProps = {
  params: Promise<{ locale: Locale; appId: string }>
  params: { locale: Locale; appId: string }
}
const Develop = async (props: IDevelopProps) => {
  const params = await props.params
  const {
    appId,
  } = params
const Develop = async ({
  params: { appId },
}: IDevelopProps) => {
  return <DevelopMain appId={appId} />
}
app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx
@@ -1,14 +1,174 @@
import Main from './layout-main'
'use client'
import type { FC } from 'react'
import { useUnmount } from 'ahooks'
import React, { useCallback, useEffect, useState } from 'react'
import { usePathname, useRouter } from 'next/navigation'
import {
  RiDashboard2Fill,
  RiDashboard2Line,
  RiFileList3Fill,
  RiFileList3Line,
  RiTerminalBoxFill,
  RiTerminalBoxLine,
  RiTerminalWindowFill,
  RiTerminalWindowLine,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { useShallow } from 'zustand/react/shallow'
import { useContextSelector } from 'use-context-selector'
import s from './style.module.css'
import cn from '@/utils/classnames'
import { useStore } from '@/app/components/app/store'
import AppSideBar from '@/app/components/app-sidebar'
import type { NavIcon } from '@/app/components/app-sidebar/navLink'
import { fetchAppDetail, fetchAppSSO } from '@/service/apps'
import AppContext, { useAppContext } from '@/context/app-context'
import Loading from '@/app/components/base/loading'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import type { App } from '@/types/app'
const AppDetailLayout = async (props: {
export type IAppDetailLayoutProps = {
  children: React.ReactNode
  params: Promise<{ appId: string }>
}) => {
  params: { appId: string }
}
const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
  const {
    children,
    params,
    params: { appId }, // get appId in path
  } = props
  const { t } = useTranslation()
  const router = useRouter()
  const pathname = usePathname()
  const media = useBreakpoints()
  const isMobile = media === MediaType.mobile
  const { isCurrentWorkspaceEditor, isLoadingCurrentWorkspace } = useAppContext()
  const { appDetail, setAppDetail, setAppSiderbarExpand } = useStore(useShallow(state => ({
    appDetail: state.appDetail,
    setAppDetail: state.setAppDetail,
    setAppSiderbarExpand: state.setAppSiderbarExpand,
  })))
  const [isLoadingAppDetail, setIsLoadingAppDetail] = useState(false)
  const [appDetailRes, setAppDetailRes] = useState<App | null>(null)
  const [navigation, setNavigation] = useState<Array<{
    name: string
    href: string
    icon: NavIcon
    selectedIcon: NavIcon
  }>>([])
  const systemFeatures = useContextSelector(AppContext, state => state.systemFeatures)
  return <Main appId={(await params).appId}>{children}</Main>
  const getNavigations = useCallback((appId: string, isCurrentWorkspaceEditor: boolean, mode: string) => {
    const navs = [
      ...(isCurrentWorkspaceEditor
        ? [{
          name: t('common.appMenus.promptEng'),
          href: `/app/${appId}/${(mode === 'workflow' || mode === 'advanced-chat') ? 'workflow' : 'configuration'}`,
          icon: RiTerminalWindowLine,
          selectedIcon: RiTerminalWindowFill,
        }]
        : []
      ),
      {
        name: t('common.appMenus.apiAccess'),
        href: `/app/${appId}/develop`,
        icon: RiTerminalBoxLine,
        selectedIcon: RiTerminalBoxFill,
      },
      ...(isCurrentWorkspaceEditor
        ? [{
          name: mode !== 'workflow'
            ? t('common.appMenus.logAndAnn')
            : t('common.appMenus.logs'),
          href: `/app/${appId}/logs`,
          icon: RiFileList3Line,
          selectedIcon: RiFileList3Fill,
        }]
        : []
      ),
      {
        name: t('common.appMenus.overview'),
        href: `/app/${appId}/overview`,
        icon: RiDashboard2Line,
        selectedIcon: RiDashboard2Fill,
      },
    ]
    return navs
  }, [t])
  useEffect(() => {
    if (appDetail) {
      document.title = `${(appDetail.name || 'App')} - Dify`
      const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand'
      const mode = isMobile ? 'collapse' : 'expand'
      setAppSiderbarExpand(isMobile ? mode : localeMode)
      // TODO: consider screen size and mode
      // if ((appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow') && (pathname).endsWith('workflow'))
      //   setAppSiderbarExpand('collapse')
}
export default AppDetailLayout
  }, [appDetail, isMobile])
  useEffect(() => {
    setAppDetail()
    setIsLoadingAppDetail(true)
    fetchAppDetail({ url: '/apps', id: appId }).then((res) => {
      setAppDetailRes(res)
    }).catch((e: any) => {
      if (e.status === 404)
        router.replace('/apps')
    }).finally(() => {
      setIsLoadingAppDetail(false)
    })
  }, [appId, router, setAppDetail])
  useEffect(() => {
    if (!appDetailRes || isLoadingCurrentWorkspace || isLoadingAppDetail)
      return
    const res = appDetailRes
    // redirection
    const canIEditApp = isCurrentWorkspaceEditor
    if (!canIEditApp && (pathname.endsWith('configuration') || pathname.endsWith('workflow') || pathname.endsWith('logs'))) {
      router.replace(`/app/${appId}/overview`)
      return
    }
    if ((res.mode === 'workflow' || res.mode === 'advanced-chat') && (pathname).endsWith('configuration')) {
      router.replace(`/app/${appId}/workflow`)
    }
    else if ((res.mode !== 'workflow' && res.mode !== 'advanced-chat') && (pathname).endsWith('workflow')) {
      router.replace(`/app/${appId}/configuration`)
    }
    else {
      setAppDetail({ ...res, enable_sso: false })
      setNavigation(getNavigations(appId, isCurrentWorkspaceEditor, res.mode))
      if (systemFeatures.enable_web_sso_switch_component && canIEditApp) {
        fetchAppSSO({ appId }).then((ssoRes) => {
          setAppDetail({ ...res, enable_sso: ssoRes.enabled })
        })
      }
    }
  }, [appDetailRes, appId, getNavigations, isCurrentWorkspaceEditor, isLoadingAppDetail, isLoadingCurrentWorkspace, pathname, router, setAppDetail, systemFeatures.enable_web_sso_switch_component])
  useUnmount(() => {
    setAppDetail()
  })
  if (!appDetail) {
    return (
      <div className='flex h-full items-center justify-center bg-background-body'>
        <Loading />
      </div>
    )
  }
  return (
    <div className={cn(s.app, 'flex', 'overflow-hidden')}>
      {appDetail && (
        <AppSideBar title={appDetail.name} icon={appDetail.icon} icon_background={appDetail.icon_background} desc={appDetail.mode} navigation={navigation} />
      )}
      <div className="bg-components-panel-bg grow overflow-hidden">
        {children}
      </div>
    </div>
  )
}
export default React.memo(AppDetailLayout)
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/cardView.tsx
@@ -24,11 +24,9 @@
export type ICardViewProps = {
  appId: string
  isInPanel?: boolean
  className?: string
}
const CardView: FC<ICardViewProps> = ({ appId, isInPanel, className }) => {
const CardView: FC<ICardViewProps> = ({ appId }) => {
  const { t } = useTranslation()
  const { notify } = useContext(ToastContext)
  const appDetail = useAppStore(state => state.appDetail)
@@ -122,11 +120,10 @@
    return <Loading />
  return (
    <div className={className || 'mb-6 grid w-full grid-cols-1 gap-6 xl:grid-cols-2'}>
    <div className="grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6">
      <AppCard
        appInfo={appDetail}
        cardType="webapp"
        isInPanel={isInPanel}
        onChangeStatus={onChangeSiteStatus}
        onGenerateCode={onGenerateCode}
        onSaveSiteConfig={onSaveSiteConfig}
@@ -134,7 +131,6 @@
      <AppCard
        cardType="api"
        appInfo={appDetail}
        isInPanel={isInPanel}
        onChangeStatus={onChangeApiStatus}
      />
    </div>
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chartView.tsx
@@ -46,14 +46,14 @@
  return (
    <div>
      <div className='system-xl-semibold mb-4 mt-8 flex flex-row items-center text-text-primary'>
      <div className='flex flex-row items-center mt-8 mb-4 text-gray-900 text-base'>
        <span className='mr-3'>{t('appOverview.analysis.title')}</span>
        <SimpleSelect
          items={Object.entries(TIME_PERIOD_MAPPING).map(([k, v]) => ({ value: k, name: t(`appLog.filter.period.${v.name}`) }))}
          className='mt-0 !w-40'
          onSelect={(item) => {
            const id = item.value
            const value = TIME_PERIOD_MAPPING[id]?.value ?? '-1'
            const value = TIME_PERIOD_MAPPING[id]?.value || '-1'
            const name = item.name || t('appLog.filter.period.allTime')
            onSelect({ value, name })
          }}
@@ -61,13 +61,13 @@
        />
      </div>
      {!isWorkflow && (
        <div className='mb-6 grid w-full grid-cols-1 gap-6 xl:grid-cols-2'>
        <div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
          <ConversationsChart period={period} id={appId} />
          <EndUsersChart period={period} id={appId} />
        </div>
      )}
      {!isWorkflow && (
        <div className='mb-6 grid w-full grid-cols-1 gap-6 xl:grid-cols-2'>
        <div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
          {isChatApp
            ? (
              <AvgSessionInteractions period={period} id={appId} />
@@ -79,24 +79,24 @@
        </div>
      )}
      {!isWorkflow && (
        <div className='mb-6 grid w-full grid-cols-1 gap-6 xl:grid-cols-2'>
        <div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
          <UserSatisfactionRate period={period} id={appId} />
          <CostChart period={period} id={appId} />
        </div>
      )}
      {!isWorkflow && isChatApp && (
        <div className='mb-6 grid w-full grid-cols-1 gap-6 xl:grid-cols-2'>
        <div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
          <MessagesChart period={period} id={appId} />
        </div>
      )}
      {isWorkflow && (
        <div className='mb-6 grid w-full grid-cols-1 gap-6 xl:grid-cols-2'>
        <div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
          <WorkflowMessagesChart period={period} id={appId} />
          <WorkflowDailyTerminalsChart period={period} id={appId} />
        </div>
      )}
      {isWorkflow && (
        <div className='mb-6 grid w-full grid-cols-1 gap-6 xl:grid-cols-2'>
        <div className='grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'>
          <WorkflowCostChart period={period} id={appId} />
          <AvgUserInteractions period={period} id={appId} />
        </div>
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx
@@ -5,18 +5,14 @@
import ApikeyInfoPanel from '@/app/components/app/overview/apikey-info-panel'
export type IDevelopProps = {
  params: Promise<{ appId: string }>
  params: { appId: string }
}
const Overview = async (props: IDevelopProps) => {
  const params = await props.params
  const {
    appId,
  } = params
const Overview = async ({
  params: { appId },
}: IDevelopProps) => {
  return (
    <div className="h-full overflow-scroll bg-chatbot-bg px-4 py-6 sm:px-12">
    <div className="h-full px-4 sm:px-16 py-6 overflow-scroll">
      <ApikeyInfoPanel />
      <TracingPanel />
      <CardView appId={appId} />
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx
@@ -1,17 +1,19 @@
'use client'
import type { FC } from 'react'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import {
  RiEqualizer2Line,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import type { PopupProps } from './config-popup'
import ConfigPopup from './config-popup'
import cn from '@/utils/classnames'
import Button from '@/app/components/base/button'
import { Settings04 } from '@/app/components/base/icons/src/vender/line/general'
import {
  PortalToFollowElem,
  PortalToFollowElemContent,
  PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
const I18N_PREFIX = 'app.tracing'
type Props = {
  readOnly: boolean
@@ -26,6 +28,7 @@
  controlShowPopup,
  ...popupProps
}) => {
  const { t } = useTranslation()
  const [open, doSetOpen] = useState(false)
  const openRef = useRef(open)
  const setOpen = useCallback((v: boolean) => {
@@ -47,6 +50,21 @@
  if (popupProps.readOnly && !hasConfigured)
    return null
  const triggerContent = hasConfigured
    ? (
      <div className={cn(className, 'p-1 rounded-md hover:bg-black/5 cursor-pointer')}>
        <Settings04 className='w-4 h-4 text-gray-500' />
      </div>
    )
    : (
      <Button variant='primary'
        className={cn(className, '!h-8 !px-3 select-none')}
      >
        <Settings04 className='mr-1 w-4 h-4' />
        <span className='text-[13px]'>{t(`${I18N_PREFIX}.config`)}</span>
      </Button>
    )
  return (
    <PortalToFollowElem
      open={open}
@@ -54,13 +72,11 @@
      placement='bottom-end'
      offset={{
        mainAxis: 12,
        crossAxis: hasConfigured ? 8 : 49,
        crossAxis: hasConfigured ? 8 : 0,
      }}
    >
      <PortalToFollowElemTrigger onClick={handleTrigger}>
        <div className={cn(className, 'rounded-md p-1')}>
          <RiEqualizer2Line className='h-4 w-4 text-text-tertiary' />
        </div>
        {triggerContent}
      </PortalToFollowElemTrigger>
      <PortalToFollowElemContent className='z-[11]'>
        <ConfigPopup {...popupProps} />
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx
@@ -5,14 +5,12 @@
import { useBoolean } from 'ahooks'
import TracingIcon from './tracing-icon'
import ProviderPanel from './provider-panel'
import type { LangFuseConfig, LangSmithConfig, OpikConfig, WeaveConfig } from './type'
import type { LangFuseConfig, LangSmithConfig, OpikConfig } from './type'
import { TracingProvider } from './type'
import ProviderConfigModal from './provider-config-modal'
import Indicator from '@/app/components/header/indicator'
import Switch from '@/app/components/base/switch'
import Tooltip from '@/app/components/base/tooltip'
import Divider from '@/app/components/base/divider'
import cn from '@/utils/classnames'
const I18N_PREFIX = 'app.tracing'
@@ -26,8 +24,7 @@
  langSmithConfig: LangSmithConfig | null
  langFuseConfig: LangFuseConfig | null
  opikConfig: OpikConfig | null
  weaveConfig: WeaveConfig | null
  onConfigUpdated: (provider: TracingProvider, payload: LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig) => void
  onConfigUpdated: (provider: TracingProvider, payload: LangSmithConfig | LangFuseConfig | OpikConfig) => void
  onConfigRemoved: (provider: TracingProvider) => void
}
@@ -41,7 +38,6 @@
  langSmithConfig,
  langFuseConfig,
  opikConfig,
  weaveConfig,
  onConfigUpdated,
  onConfigRemoved,
}) => {
@@ -65,7 +61,7 @@
    }
  }, [onChooseProvider])
  const handleConfigUpdated = useCallback((payload: LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig) => {
  const handleConfigUpdated = useCallback((payload: LangSmithConfig | LangFuseConfig | OpikConfig) => {
    onConfigUpdated(currentProvider!, payload)
    hideConfigModal()
  }, [currentProvider, hideConfigModal, onConfigUpdated])
@@ -75,14 +71,15 @@
    hideConfigModal()
  }, [currentProvider, hideConfigModal, onConfigRemoved])
  const providerAllConfigured = langSmithConfig && langFuseConfig && opikConfig && weaveConfig
  const providerAllNotConfigured = !langSmithConfig && !langFuseConfig && !opikConfig && !weaveConfig
  const providerAllConfigured = langSmithConfig && langFuseConfig && opikConfig
  const providerAllNotConfigured = !langSmithConfig && !langFuseConfig && !opikConfig
  const switchContent = (
    <Switch
      className='ml-3'
      defaultValue={enabled}
      onChange={onStatusChange}
      size='l'
      disabled={providerAllNotConfigured}
    />
  )
@@ -125,50 +122,32 @@
    />
  )
  const weavePanel = (
    <ProviderPanel
      type={TracingProvider.weave}
      readOnly={readOnly}
      config={weaveConfig}
      hasConfigured={!!weaveConfig}
      onConfig={handleOnConfig(TracingProvider.weave)}
      isChosen={chosenProvider === TracingProvider.weave}
      onChoose={handleOnChoose(TracingProvider.weave)}
      key="weave-provider-panel"
    />
  )
  const configuredProviderPanel = () => {
    const configuredPanels: JSX.Element[] = []
    if (langFuseConfig)
      configuredPanels.push(langfusePanel)
    const configuredPanels: ProviderPanel[] = []
    if (langSmithConfig)
      configuredPanels.push(langSmithPanel)
    if (langFuseConfig)
      configuredPanels.push(langfusePanel)
    if (opikConfig)
      configuredPanels.push(opikPanel)
    if (weaveConfig)
      configuredPanels.push(weavePanel)
    return configuredPanels
  }
  const moreProviderPanel = () => {
    const notConfiguredPanels: JSX.Element[] = []
    if (!langFuseConfig)
      notConfiguredPanels.push(langfusePanel)
    const notConfiguredPanels: ProviderPanel[] = []
    if (!langSmithConfig)
      notConfiguredPanels.push(langSmithPanel)
    if (!langFuseConfig)
      notConfiguredPanels.push(langfusePanel)
    if (!opikConfig)
      notConfiguredPanels.push(opikPanel)
    if (!weaveConfig)
      notConfiguredPanels.push(weavePanel)
    return notConfiguredPanels
  }
@@ -178,21 +157,19 @@
      return langSmithConfig
    if (currentProvider === TracingProvider.langfuse)
      return langFuseConfig
    if (currentProvider === TracingProvider.opik)
      return opikConfig
    return weaveConfig
  }
  return (
    <div className='w-[420px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-4 shadow-xl'>
      <div className='flex items-center justify-between'>
    <div className='w-[420px] p-4 rounded-2xl bg-white border-[0.5px] border-black/5 shadow-lg'>
      <div className='flex justify-between items-center'>
        <div className='flex items-center'>
          <TracingIcon size='md' className='mr-2' />
          <div className='title-2xl-semi-bold text-text-primary'>{t(`${I18N_PREFIX}.tracing`)}</div>
          <div className='leading-[120%] text-[18px] font-semibold text-gray-900'>{t(`${I18N_PREFIX}.tracing`)}</div>
        </div>
        <div className='flex items-center'>
          <Indicator color={enabled ? 'green' : 'gray'} />
          <div className={cn('system-xs-semibold-uppercase ml-1 text-text-tertiary', enabled && 'text-util-colors-green-green-600')}>
          <div className='ml-1.5 text-xs font-semibold text-gray-500 uppercase'>
            {t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)}
          </div>
          {!readOnly && (
@@ -208,33 +185,33 @@
                : switchContent}
            </>
          )}
        </div>
      </div>
      <div className='system-xs-regular mt-2 text-text-tertiary'>
      <div className='mt-2 leading-4 text-xs font-normal text-gray-500'>
        {t(`${I18N_PREFIX}.tracingDescription`)}
      </div>
      <Divider className='my-3' />
      <div className='relative'>
      <div className='mt-3 h-px bg-gray-100'></div>
      <div className='mt-3'>
        {(providerAllConfigured || providerAllNotConfigured)
          ? (
            <>
              <div className='system-xs-medium-uppercase text-text-tertiary'>{t(`${I18N_PREFIX}.configProviderTitle.${providerAllConfigured ? 'configured' : 'notConfigured'}`)}</div>
              <div className='leading-4 text-xs font-medium text-gray-500 uppercase'>{t(`${I18N_PREFIX}.configProviderTitle.${providerAllConfigured ? 'configured' : 'notConfigured'}`)}</div>
              <div className='mt-2 space-y-2'>
                {langfusePanel}
                {langSmithPanel}
                {langfusePanel}
                {opikPanel}
                {weavePanel}
              </div>
            </>
          )
          : (
            <>
              <div className='system-xs-medium-uppercase text-text-tertiary'>{t(`${I18N_PREFIX}.configProviderTitle.configured`)}</div>
              <div className='leading-4 text-xs font-medium text-gray-500 uppercase'>{t(`${I18N_PREFIX}.configProviderTitle.configured`)}</div>
              <div className='mt-2 space-y-2'>
                {configuredProviderPanel()}
              </div>
              <div className='system-xs-medium-uppercase mt-3 text-text-tertiary'>{t(`${I18N_PREFIX}.configProviderTitle.moreProvider`)}</div>
              <div className='mt-3 leading-4 text-xs font-medium text-gray-500 uppercase'>{t(`${I18N_PREFIX}.configProviderTitle.moreProvider`)}</div>
              <div className='mt-2 space-y-2'>
                {moreProviderPanel()}
              </div>
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config.ts
@@ -4,5 +4,4 @@
  [TracingProvider.langSmith]: 'https://docs.smith.langchain.com/',
  [TracingProvider.langfuse]: 'https://docs.langfuse.com',
  [TracingProvider.opik]: 'https://www.comet.com/docs/opik/tracing/integrations/dify#setup-instructions',
  [TracingProvider.weave]: 'https://weave-docs.wandb.ai/',
}
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx
@@ -26,7 +26,7 @@
  return (
    <div className={cn(className)}>
      <div className='flex py-[7px]'>
        <div className={cn(labelClassName, 'flex h-[18px] items-center text-[13px] font-medium text-text-primary')}>{label} </div>
        <div className={cn(labelClassName, 'flex items-center h-[18px] text-[13px] font-medium text-gray-900')}>{label} </div>
        {isRequired && <span className='ml-0.5 text-xs font-semibold text-[#D92D20]'>*</span>}
      </div>
      <Input
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx
@@ -1,25 +1,21 @@
'use client'
import type { FC } from 'react'
import React, { useCallback, useEffect, useState } from 'react'
import {
  RiArrowDownDoubleLine,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { usePathname } from 'next/navigation'
import { useBoolean } from 'ahooks'
import type { LangFuseConfig, LangSmithConfig, OpikConfig, WeaveConfig } from './type'
import type { LangFuseConfig, LangSmithConfig } from './type'
import { TracingProvider } from './type'
import TracingIcon from './tracing-icon'
import ConfigButton from './config-button'
import cn from '@/utils/classnames'
import { LangfuseIcon, LangsmithIcon, OpikIcon, WeaveIcon } from '@/app/components/base/icons/src/public/tracing'
import { LangfuseIcon, LangsmithIcon, OpikIcon } from '@/app/components/base/icons/src/public/tracing'
import Indicator from '@/app/components/header/indicator'
import { fetchTracingConfig as doFetchTracingConfig, fetchTracingStatus, updateTracingStatus } from '@/service/apps'
import type { TracingStatus } from '@/models/app'
import Toast from '@/app/components/base/toast'
import { useAppContext } from '@/context/app-context'
import Loading from '@/app/components/base/loading'
import Divider from '@/app/components/base/divider'
const I18N_PREFIX = 'app.tracing'
@@ -31,7 +27,7 @@
  const { t } = useTranslation()
  return (
    <div className={cn('system-xl-semibold flex items-center text-text-primary', className)}>
    <div className={cn(className, 'flex items-center text-lg font-semibold text-gray-900')}>
      {t('common.appMenus.overview')}
    </div>
  )
@@ -82,15 +78,12 @@
        ? LangfuseIcon
        : inUseTracingProvider === TracingProvider.opik
          ? OpikIcon
          : inUseTracingProvider === TracingProvider.weave
            ? WeaveIcon
            : LangsmithIcon
          : null
  const [langSmithConfig, setLangSmithConfig] = useState<LangSmithConfig | null>(null)
  const [langFuseConfig, setLangFuseConfig] = useState<LangFuseConfig | null>(null)
  const [opikConfig, setOpikConfig] = useState<OpikConfig | null>(null)
  const [weaveConfig, setWeaveConfig] = useState<WeaveConfig | null>(null)
  const hasConfiguredTracing = !!(langSmithConfig || langFuseConfig || opikConfig || weaveConfig)
  const hasConfiguredTracing = !!(langSmithConfig || langFuseConfig || opikConfig)
  const fetchTracingConfig = async () => {
    const { tracing_config: langSmithConfig, has_not_configured: langSmithHasNotConfig } = await doFetchTracingConfig({ appId, provider: TracingProvider.langSmith })
@@ -102,9 +95,6 @@
    const { tracing_config: opikConfig, has_not_configured: OpikHasNotConfig } = await doFetchTracingConfig({ appId, provider: TracingProvider.opik })
    if (!OpikHasNotConfig)
      setOpikConfig(opikConfig as OpikConfig)
    const { tracing_config: weaveConfig, has_not_configured: weaveHasNotConfig } = await doFetchTracingConfig({ appId, provider: TracingProvider.weave })
    if (!weaveHasNotConfig)
      setWeaveConfig(weaveConfig as WeaveConfig)
  }
  const handleTracingConfigUpdated = async (provider: TracingProvider) => {
@@ -112,23 +102,19 @@
    const { tracing_config } = await doFetchTracingConfig({ appId, provider })
    if (provider === TracingProvider.langSmith)
      setLangSmithConfig(tracing_config as LangSmithConfig)
    else if (provider === TracingProvider.langfuse)
    else if (provider === TracingProvider.langSmith)
      setLangFuseConfig(tracing_config as LangFuseConfig)
    else if (provider === TracingProvider.opik)
      setOpikConfig(tracing_config as OpikConfig)
    else if (provider === TracingProvider.weave)
      setWeaveConfig(tracing_config as WeaveConfig)
  }
  const handleTracingConfigRemoved = (provider: TracingProvider) => {
    if (provider === TracingProvider.langSmith)
      setLangSmithConfig(null)
    else if (provider === TracingProvider.langfuse)
    else if (provider === TracingProvider.langSmith)
      setLangFuseConfig(null)
    else if (provider === TracingProvider.opik)
      setOpikConfig(null)
    else if (provider === TracingProvider.weave)
      setWeaveConfig(null)
    if (provider === inUseTracingProvider) {
      handleTracingStatusChange({
        enabled: false,
@@ -153,7 +139,7 @@
  }, [setControlShowPopup])
  if (!isLoaded) {
    return (
      <div className='mb-3 flex items-center justify-between'>
      <div className='flex items-center justify-between mb-3'>
        <Title className='h-[41px]' />
        <div className='w-[200px]'>
          <Loading />
@@ -163,53 +149,28 @@
  }
  return (
    <div className={cn('mb-3 flex items-center justify-between')}>
    <div className={cn('mb-3 flex justify-between items-center')}>
      <Title className='h-[41px]' />
      <div
        className={cn(
          'flex cursor-pointer items-center rounded-xl border-l-[0.5px] border-t border-effects-highlight bg-background-default-dodge p-2 shadow-xs hover:border-effects-highlight-lightmode-off hover:bg-background-default-lighter',
          controlShowPopup && 'border-effects-highlight-lightmode-off bg-background-default-lighter',
        )}
        onClick={showPopup}
      >
        {!inUseTracingProvider && (
          <>
            <TracingIcon size='md' />
            <div className='system-sm-semibold mx-2 text-text-secondary'>{t(`${I18N_PREFIX}.title`)}</div>
            <div className='flex items-center' onClick={e => e.stopPropagation()}>
              <ConfigButton
                appId={appId}
                readOnly={readOnly}
                hasConfigured={false}
                enabled={enabled}
                onStatusChange={handleTracingEnabledChange}
                chosenProvider={inUseTracingProvider}
                onChooseProvider={handleChooseProvider}
                langSmithConfig={langSmithConfig}
                langFuseConfig={langFuseConfig}
                opikConfig={opikConfig}
                weaveConfig={weaveConfig}
                onConfigUpdated={handleTracingConfigUpdated}
                onConfigRemoved={handleTracingConfigRemoved}
                controlShowPopup={controlShowPopup}
              />
            </div>
            <Divider type='vertical' className='h-3.5' />
            <div className='rounded-md p-1'>
              <RiArrowDownDoubleLine className='h-4 w-4 text-text-tertiary' />
            </div>
      <div className='flex items-center p-2 rounded-xl border-[0.5px] border-gray-200 shadow-xs cursor-pointer hover:bg-gray-100' onClick={showPopup}>
        {!inUseTracingProvider
          ? <>
            <TracingIcon size='md' className='mr-2' />
            <div className='leading-5 text-sm font-semibold text-gray-700'>{t(`${I18N_PREFIX}.title`)}</div>
          </>
        )}
          : <InUseProviderIcon className='ml-1 h-4' />}
        {hasConfiguredTracing && (
          <>
            <div className='ml-4 mr-1 flex items-center'>
              <Indicator color={enabled ? 'green' : 'gray'} />
              <div className='system-xs-semibold-uppercase ml-1.5 text-text-tertiary'>
            <div className='ml-1.5 text-xs font-semibold text-gray-500 uppercase'>
                {t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)}
              </div>
            </div>
            {InUseProviderIcon && <InUseProviderIcon className='ml-1 h-4' />}
            <Divider type='vertical' className='h-3.5' />
        )}
        {hasConfiguredTracing && (
          <div className='ml-2 w-px h-3.5 bg-gray-200'></div>
        )}
            <div className='flex items-center' onClick={e => e.stopPropagation()}>
              <ConfigButton
                appId={appId}
@@ -223,14 +184,11 @@
                langSmithConfig={langSmithConfig}
                langFuseConfig={langFuseConfig}
                opikConfig={opikConfig}
                weaveConfig={weaveConfig}
                onConfigUpdated={handleTracingConfigUpdated}
                onConfigRemoved={handleTracingConfigRemoved}
                controlShowPopup={controlShowPopup}
              />
            </div>
          </>
        )}
      </div >
    </div >
  )
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx
@@ -4,7 +4,7 @@
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import Field from './field'
import type { LangFuseConfig, LangSmithConfig, OpikConfig, WeaveConfig } from './type'
import type { LangFuseConfig, LangSmithConfig, OpikConfig } from './type'
import { TracingProvider } from './type'
import { docURL } from './config'
import {
@@ -17,15 +17,14 @@
import Confirm from '@/app/components/base/confirm'
import { addTracingConfig, removeTracingConfig, updateTracingConfig } from '@/service/apps'
import Toast from '@/app/components/base/toast'
import Divider from '@/app/components/base/divider'
type Props = {
  appId: string
  type: TracingProvider
  payload?: LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig | null
  payload?: LangSmithConfig | LangFuseConfig | OpikConfig | null
  onRemoved: () => void
  onCancel: () => void
  onSaved: (payload: LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig) => void
  onSaved: (payload: LangSmithConfig | LangFuseConfig | OpikConfig) => void
  onChosen: (provider: TracingProvider) => void
}
@@ -50,13 +49,6 @@
  workspace: '',
}
const weaveConfigTemplate = {
  api_key: '',
  entity: '',
  project: '',
  endpoint: '',
}
const ProviderConfigModal: FC<Props> = ({
  appId,
  type,
@@ -70,7 +62,7 @@
  const isEdit = !!payload
  const isAdd = !isEdit
  const [isSaving, setIsSaving] = useState(false)
  const [config, setConfig] = useState<LangSmithConfig | LangFuseConfig | OpikConfig | WeaveConfig>((() => {
  const [config, setConfig] = useState<LangSmithConfig | LangFuseConfig | OpikConfig>((() => {
    if (isEdit)
      return payload
@@ -80,10 +72,7 @@
    else if (type === TracingProvider.langfuse)
      return langFuseConfigTemplate
    else if (type === TracingProvider.opik)
      return opikConfigTemplate
    return weaveConfigTemplate
  })())
  const [isShowRemoveConfirm, {
    setTrue: showRemoveConfirm,
@@ -133,16 +122,7 @@
    }
    if (type === TracingProvider.opik) {
      // todo: check field validity
      // const postData = config as OpikConfig
    }
    if (type === TracingProvider.weave) {
      const postData = config as WeaveConfig
      if (!errorMessage && !postData.api_key)
        errorMessage = t('common.errorMsg.fieldRequired', { field: 'API Key' })
      if (!errorMessage && !postData.project)
        errorMessage = t('common.errorMsg.fieldRequired', { field: t(`${I18N_PREFIX}.project`) })
      const postData = config as OpikConfig
    }
    return errorMessage
@@ -185,49 +165,15 @@
      {!isShowRemoveConfirm
        ? (
          <PortalToFollowElem open>
            <PortalToFollowElemContent className='z-[60] h-full w-full'>
              <div className='fixed inset-0 flex items-center justify-center bg-background-overlay'>
                <div className='mx-2 max-h-[calc(100vh-120px)] w-[640px] overflow-y-auto rounded-2xl bg-components-panel-bg shadow-xl'>
            <PortalToFollowElemContent className='w-full h-full z-[60]'>
              <div className='fixed inset-0 flex items-center justify-center bg-black/[.25]'>
                <div className='mx-2 w-[640px] max-h-[calc(100vh-120px)] bg-white shadow-xl rounded-2xl overflow-y-auto'>
                  <div className='px-8 pt-8'>
                    <div className='mb-4 flex items-center justify-between'>
                      <div className='title-2xl-semi-bold text-text-primary'>{t(`${I18N_PREFIX}.title`)}{t(`app.tracing.${type}.title`)}</div>
                    <div className='flex justify-between items-center mb-4'>
                      <div className='text-xl font-semibold text-gray-900'>{t(`${I18N_PREFIX}.title`)}{t(`app.tracing.${type}.title`)}</div>
                    </div>
                    <div className='space-y-4'>
                      {type === TracingProvider.weave && (
                        <>
                          <Field
                            label='API Key'
                            labelClassName='!text-sm'
                            isRequired
                            value={(config as WeaveConfig).api_key}
                            onChange={handleConfigChange('api_key')}
                            placeholder={t(`${I18N_PREFIX}.placeholder`, { key: 'API Key' })!}
                          />
                          <Field
                            label={t(`${I18N_PREFIX}.project`)!}
                            labelClassName='!text-sm'
                            isRequired
                            value={(config as WeaveConfig).project}
                            onChange={handleConfigChange('project')}
                            placeholder={t(`${I18N_PREFIX}.placeholder`, { key: t(`${I18N_PREFIX}.project`) })!}
                          />
                          <Field
                            label='Entity'
                            labelClassName='!text-sm'
                            value={(config as WeaveConfig).entity}
                            onChange={handleConfigChange('entity')}
                            placeholder={t(`${I18N_PREFIX}.placeholder`, { key: 'Entity' })!}
                          />
                          <Field
                            label='Endpoint'
                            labelClassName='!text-sm'
                            value={(config as WeaveConfig).endpoint}
                            onChange={handleConfigChange('endpoint')}
                            placeholder={'https://trace.wandb.ai/'}
                          />
                        </>
                      )}
                      {type === TracingProvider.langSmith && (
                        <>
                          <Field
@@ -315,30 +261,31 @@
                          />
                        </>
                      )}
                    </div>
                    <div className='my-8 flex h-8 items-center justify-between'>
                    <div className='my-8 flex justify-between items-center h-8'>
                      <a
                        className='flex items-center space-x-1 text-xs font-normal leading-[18px] text-[#155EEF]'
                        className='flex items-center space-x-1 leading-[18px] text-xs font-normal text-[#155EEF]'
                        target='_blank'
                        href={docURL[type]}
                      >
                        <span>{t(`${I18N_PREFIX}.viewDocsLink`, { key: t(`app.tracing.${type}.title`) })}</span>
                        <LinkExternal02 className='h-3 w-3' />
                        <LinkExternal02 className='w-3 h-3' />
                      </a>
                      <div className='flex items-center'>
                        {isEdit && (
                          <>
                            <Button
                              className='h-9 text-sm font-medium text-text-secondary'
                              className='h-9 text-sm font-medium text-gray-700'
                              onClick={showRemoveConfirm}
                            >
                              <span className='text-[#D92D20]'>{t('common.operation.remove')}</span>
                            </Button>
                            <Divider className='mx-3 h-[18px]' />
                            <div className='mx-3 w-px h-[18px] bg-gray-200'></div>
                          </>
                        )}
                        <Button
                          className='mr-2 h-9 text-sm font-medium text-text-secondary'
                          className='mr-2 h-9 text-sm font-medium text-gray-700'
                          onClick={onCancel}
                        >
                          {t('common.operation.cancel')}
@@ -355,12 +302,12 @@
                    </div>
                  </div>
                  <div className='border-t-[0.5px] border-divider-regular'>
                    <div className='flex items-center justify-center bg-background-section-burn py-3 text-xs text-text-tertiary'>
                      <Lock01 className='mr-1 h-3 w-3 text-text-tertiary' />
                  <div className='border-t-[0.5px] border-t-black/5'>
                    <div className='flex justify-center items-center py-3 bg-gray-50 text-xs text-gray-500'>
                      <Lock01 className='mr-1 w-3 h-3 text-gray-500' />
                      {t('common.modelProvider.encrypted.front')}
                      <a
                        className='mx-1 text-primary-600'
                        className='text-primary-600 mx-1'
                        target='_blank' rel='noopener noreferrer'
                        href='https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html'
                      >
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx
@@ -1,13 +1,11 @@
'use client'
import type { FC } from 'react'
import React, { useCallback } from 'react'
import {
  RiEqualizer2Line,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { TracingProvider } from './type'
import cn from '@/utils/classnames'
import { LangfuseIconBig, LangsmithIconBig, OpikIconBig, WeaveIconBig } from '@/app/components/base/icons/src/public/tracing'
import { LangfuseIconBig, LangsmithIconBig, OpikIconBig } from '@/app/components/base/icons/src/public/tracing'
import { Settings04 } from '@/app/components/base/icons/src/vender/line/general'
import { Eye as View } from '@/app/components/base/icons/src/vender/solid/general'
const I18N_PREFIX = 'app.tracing'
@@ -27,7 +25,6 @@
    [TracingProvider.langSmith]: LangsmithIconBig,
    [TracingProvider.langfuse]: LangfuseIconBig,
    [TracingProvider.opik]: OpikIconBig,
    [TracingProvider.weave]: WeaveIconBig,
  })[type]
}
@@ -65,37 +62,34 @@
  }, [hasConfigured, isChosen, onChoose, readOnly])
  return (
    <div
      className={cn(
        'rounded-xl border-[1.5px] bg-background-section-burn px-4 py-3',
        isChosen ? 'border-components-option-card-option-selected-border bg-background-section' : 'border-transparent',
        !isChosen && hasConfigured && !readOnly && 'cursor-pointer',
      )}
      className={cn(isChosen ? 'border-primary-400' : 'border-transparent', !isChosen && hasConfigured && !readOnly && 'cursor-pointer', 'px-4 py-3 rounded-xl border-[1.5px]  bg-gray-100')}
      onClick={handleChosen}
    >
      <div className={'flex items-center justify-between space-x-1'}>
      <div className={'flex justify-between items-center space-x-1'}>
        <div className='flex items-center'>
          <Icon className='h-6' />
          {isChosen && <div className='system-2xs-medium-uppercase ml-1 flex h-4 items-center rounded-[4px] border border-text-accent-secondary px-1 text-text-accent-secondary'>{t(`${I18N_PREFIX}.inUse`)}</div>}
          {isChosen && <div className='ml-1 flex items-center h-4  px-1 rounded-[4px] border border-primary-500 leading-4 text-xs font-medium text-primary-500 uppercase '>{t(`${I18N_PREFIX}.inUse`)}</div>}
        </div>
        {!readOnly && (
          <div className={'flex items-center justify-between space-x-1'}>
          <div className={'flex justify-between items-center space-x-1'}>
            {hasConfigured && (
              <div className='flex h-6 cursor-pointer items-center space-x-1 rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-2 text-text-secondary shadow-xs' onClick={viewBtnClick} >
                <View className='h-3 w-3' />
              <div className='flex px-2 items-center h-6 bg-white rounded-md border-[0.5px] border-gray-200 shadow-xs cursor-pointer text-gray-700 space-x-1' onClick={viewBtnClick} >
                <View className='w-3 h-3'/>
                <div className='text-xs font-medium'>{t(`${I18N_PREFIX}.view`)}</div>
              </div>
            )}
            <div
              className='flex h-6 cursor-pointer items-center space-x-1 rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-2 text-text-secondary shadow-xs'
              className='flex px-2 items-center h-6 bg-white rounded-md border-[0.5px] border-gray-200 shadow-xs cursor-pointer text-gray-700 space-x-1'
              onClick={handleConfigBtnClick}
            >
              <RiEqualizer2Line className='h-3 w-3' />
              <Settings04 className='w-3 h-3' />
              <div className='text-xs font-medium'>{t(`${I18N_PREFIX}.config`)}</div>
            </div>
          </div>
        )}
      </div>
      <div className='system-xs-regular mt-2 text-text-tertiary'>
      <div className='mt-2 leading-4 text-xs font-normal text-gray-500'>
        {t(`${I18N_PREFIX}.${type}.description`)}
      </div>
    </div>
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/toggle-fold-btn.tsx
New file
@@ -0,0 +1,45 @@
'use client'
import { ChevronDoubleDownIcon } from '@heroicons/react/20/solid'
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
import React, { useCallback } from 'react'
import Tooltip from '@/app/components/base/tooltip'
const I18N_PREFIX = 'app.tracing'
type Props = {
  isFold: boolean
  onFoldChange: (isFold: boolean) => void
}
const ToggleFoldBtn: FC<Props> = ({
  isFold,
  onFoldChange,
}) => {
  const { t } = useTranslation()
  const handleFoldChange = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
    e.stopPropagation()
    onFoldChange(!isFold)
  }, [isFold, onFoldChange])
  return (
    // text-[0px] to hide spacing between tooltip elements
    <div className='shrink-0 cursor-pointer text-[0px]' onClick={handleFoldChange}>
      <Tooltip
        popupContent={t(`${I18N_PREFIX}.${isFold ? 'expand' : 'collapse'}`)}
      >
        {isFold && (
          <div className='p-1 rounded-md text-gray-500 hover:text-gray-800 hover:bg-black/5'>
            <ChevronDoubleDownIcon className='w-4 h-4' />
          </div>
        )}
        {!isFold && (
          <div className='p-2 rounded-lg text-gray-500 border-[0.5px] border-gray-200 hover:text-gray-800 hover:bg-black/5'>
            <ChevronDoubleDownIcon className='w-4 h-4 transform rotate-180' />
          </div>
        )}
      </Tooltip>
    </div>
  )
}
export default React.memo(ToggleFoldBtn)
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx
@@ -21,7 +21,7 @@
  const sizeClass = sizeClassMap[size]
  return (
    <div className={cn(className, sizeClass, 'bg-primary-500 shadow-md')}>
      <Icon className='h-full w-full' />
      <Icon className='w-full h-full' />
    </div>
  )
}
app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type.ts
@@ -2,7 +2,6 @@
  langSmith = 'langsmith',
  langfuse = 'langfuse',
  opik = 'opik',
  weave = 'weave',
}
export type LangSmithConfig = {
@@ -22,11 +21,4 @@
  project: string
  workspace: string
  url: string
}
export type WeaveConfig = {
  api_key: string
  entity: string
  project: string
  endpoint: string
}
app/(commonLayout)/app/(appDetailLayout)/[appId]/style.module.css
app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx
@@ -1,11 +1,11 @@
'use client'
import WorkflowApp from '@/app/components/workflow-app'
import Workflow from '@/app/components/workflow'
const Page = () => {
  return (
    <div className='h-full w-full overflow-x-auto'>
      <WorkflowApp />
    <div className='w-full h-full overflow-x-auto'>
      <Workflow />
    </div>
  )
}
app/(commonLayout)/app/(appDetailLayout)/layout.tsx
@@ -15,7 +15,6 @@
  useEffect(() => {
    if (isCurrentWorkspaceDatasetOperator)
      return router.replace('/datasets')
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isCurrentWorkspaceDatasetOperator])
  return (
app/(commonLayout)/apps/AppCard.tsx
@@ -5,6 +5,8 @@
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { RiMoreFill } from '@remixicon/react'
import s from './style.module.css'
import cn from '@/utils/classnames'
import type { App } from '@/types/app'
import Confirm from '@/app/components/base/confirm'
import Toast, { ToastContext } from '@/app/components/base/toast'
@@ -16,7 +18,6 @@
import type { HtmlContentProps } from '@/app/components/base/popover'
import CustomPopover from '@/app/components/base/popover'
import Divider from '@/app/components/base/divider'
import { WEB_PREFIX } from '@/config'
import { getRedirection } from '@/utils/app-redirection'
import { useProviderContext } from '@/context/provider-context'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
@@ -30,7 +31,6 @@
import { fetchWorkflowDraft } from '@/service/workflow'
import { fetchInstalledAppList } from '@/service/explore'
import { AppTypeIcon } from '@/app/components/app/type-selector'
import cn from '@/utils/classnames'
export type AppCardProps = {
  app: App
@@ -71,7 +71,6 @@
      })
    }
    setShowConfirmDelete(false)
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [app.id])
  const onEdit: CreateAppModalProps['onConfirm'] = useCallback(async ({
@@ -101,7 +100,7 @@
        onRefresh()
      mutateApps()
    }
    catch {
    catch (e) {
      notify({ type: 'error', message: t('app.editFailed') })
    }
  }, [app.id, mutateApps, notify, onRefresh, t])
@@ -128,7 +127,7 @@
      onPlanInfoChanged()
      getRedirection(isCurrentWorkspaceEditor, newApp, push)
    }
    catch {
    catch (e) {
      notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
    }
  }
@@ -145,7 +144,7 @@
      a.download = `${app.name}.yml`
      a.click()
    }
    catch {
    catch (e) {
      notify({ type: 'error', message: t('app.exportFailed') })
    }
  }
@@ -164,7 +163,7 @@
      }
      setSecretEnvList(list)
    }
    catch {
    catch (e) {
      notify({ type: 'error', message: t('app.exportFailed') })
    }
  }
@@ -217,7 +216,7 @@
      try {
        const { installed_apps }: any = await fetchInstalledAppList(app.id) || {}
        if (installed_apps?.length > 0)
          window.open(`${WEB_PREFIX}/explore/installed/${installed_apps[0].id}`, '_blank')
          window.open(`/explore/installed/${installed_apps[0].id}`, '_blank')
        else
          throw new Error('No app found in Explore')
      }
@@ -227,37 +226,37 @@
    }
    return (
      <div className="relative w-full py-1" onMouseLeave={onMouseLeave}>
        <button className='mx-1 flex h-8 w-[calc(100%_-_8px)] cursor-pointer items-center gap-2 rounded-lg px-3 py-[6px] hover:bg-state-base-hover' onClick={onClickSettings}>
          <span className='system-sm-regular text-text-secondary'>{t('app.editApp')}</span>
        <button className={s.actionItem} onClick={onClickSettings}>
          <span className={s.actionName}>{t('app.editApp')}</span>
        </button>
        <Divider className="!my-1" />
        <button className='mx-1 flex h-8 w-[calc(100%_-_8px)] cursor-pointer items-center gap-2 rounded-lg px-3 py-[6px] hover:bg-state-base-hover' onClick={onClickDuplicate}>
          <span className='system-sm-regular text-text-secondary'>{t('app.duplicate')}</span>
        <button className={s.actionItem} onClick={onClickDuplicate}>
          <span className={s.actionName}>{t('app.duplicate')}</span>
        </button>
        <button className='mx-1 flex h-8 w-[calc(100%_-_8px)] cursor-pointer items-center gap-2 rounded-lg px-3 py-[6px] hover:bg-state-base-hover' onClick={onClickExport}>
          <span className='system-sm-regular text-text-secondary'>{t('app.export')}</span>
        <button className={s.actionItem} onClick={onClickExport}>
          <span className={s.actionName}>{t('app.export')}</span>
        </button>
        {(app.mode === 'completion' || app.mode === 'chat') && (
          <>
            <Divider className="!my-1" />
            <div
              className='mx-1 flex h-9 cursor-pointer items-center rounded-lg px-3 py-2 hover:bg-state-base-hover'
              className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer'
              onClick={onClickSwitch}
            >
              <span className='text-sm leading-5 text-text-secondary'>{t('app.switch')}</span>
              <span className='text-gray-700 text-sm leading-5'>{t('app.switch')}</span>
            </div>
          </>
        )}
        <Divider className="!my-1" />
        <button className='mx-1 flex h-8 w-[calc(100%_-_8px)] cursor-pointer items-center gap-2 rounded-lg px-3 py-[6px] hover:bg-state-base-hover' onClick={onClickInstalledApp}>
          <span className='system-sm-regular text-text-secondary'>{t('app.openInExplore')}</span>
        <button className={s.actionItem} onClick={onClickInstalledApp}>
          <span className={s.actionName}>{t('app.openInExplore')}</span>
        </button>
        <Divider className="!my-1" />
        <div
          className='group mx-1 flex h-8 w-[calc(100%_-_8px)] cursor-pointer items-center gap-2 rounded-lg px-3 py-[6px] hover:bg-state-destructive-hover'
          className={cn(s.actionItem, s.deleteActionItem, 'group')}
          onClick={onClickDelete}
        >
          <span className='system-sm-regular text-text-secondary group-hover:text-text-destructive'>
          <span className={cn(s.actionName, 'group-hover:text-red-500')}>
            {t('common.operation.delete')}
          </span>
        </div>
@@ -277,9 +276,9 @@
          e.preventDefault()
          getRedirection(isCurrentWorkspaceEditor, app, push)
        }}
        className='group relative col-span-1 inline-flex h-[160px] cursor-pointer flex-col rounded-xl border-[1px] border-solid border-components-card-border bg-components-card-bg shadow-sm transition-all duration-200 ease-in-out hover:shadow-lg'
        className='relative h-[160px] group col-span-1 bg-components-card-bg border-[1px] border-solid border-components-card-border rounded-xl shadow-sm inline-flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg'
      >
        <div className='flex h-[66px] shrink-0 grow-0 items-center gap-3 px-[14px] pb-3 pt-[14px]'>
        <div className='flex pt-[14px] px-[14px] pb-3 h-[66px] items-center gap-3 grow-0 shrink-0'>
          <div className='relative shrink-0'>
            <AppIcon
              size="large"
@@ -288,13 +287,13 @@
              background={app.icon_background}
              imageUrl={app.icon_url}
            />
            <AppTypeIcon type={app.mode} wrapperClassName='absolute -bottom-0.5 -right-0.5 w-4 h-4 shadow-sm' className='h-3 w-3' />
            <AppTypeIcon type={app.mode} wrapperClassName='absolute -bottom-0.5 -right-0.5 w-4 h-4 shadow-sm' className='w-3 h-3' />
          </div>
          <div className='w-0 grow py-[1px]'>
            <div className='flex items-center text-sm font-semibold leading-5 text-text-secondary'>
          <div className='grow w-0 py-[1px]'>
            <div className='flex items-center text-sm leading-5 font-semibold text-text-secondary'>
              <div className='truncate' title={app.name}>{app.name}</div>
            </div>
            <div className='flex items-center text-[10px] font-medium leading-[18px] text-text-tertiary'>
            <div className='flex items-center text-[10px] leading-[18px] text-text-tertiary font-medium'>
              {app.mode === 'advanced-chat' && <div className='truncate'>{t('app.types.advanced').toUpperCase()}</div>}
              {app.mode === 'chat' && <div className='truncate'>{t('app.types.chatbot').toUpperCase()}</div>}
              {app.mode === 'agent-chat' && <div className='truncate'>{t('app.types.agent').toUpperCase()}</div>}
@@ -312,17 +311,17 @@
          </div>
        </div>
        <div className={cn(
          'absolute bottom-1 left-0 right-0 h-[42px] shrink-0 items-center pb-[6px] pl-[14px] pr-[6px] pt-1',
          'absolute bottom-1 left-0 right-0 items-center shrink-0 pt-1 pl-[14px] pr-[6px] pb-[6px] h-[42px]',
          tags.length ? 'flex' : '!hidden group-hover:!flex',
        )}>
          {isCurrentWorkspaceEditor && (
            <>
              <div className={cn('flex w-0 grow items-center gap-1')} onClick={(e) => {
              <div className={cn('grow flex items-center gap-1 w-0')} onClick={(e) => {
                e.stopPropagation()
                e.preventDefault()
              }}>
                <div className={cn(
                  'mr-[41px] w-full grow group-hover:!mr-0 group-hover:!block',
                  'group-hover:!block group-hover:!mr-0 mr-[41px] grow w-full',
                  tags.length ? '!block' : '!hidden',
                )}>
                  <TagSelector
@@ -336,23 +335,23 @@
                  />
                </div>
              </div>
              <div className='mx-1 !hidden h-[14px] w-[1px] shrink-0 group-hover:!flex' />
              <div className='!hidden shrink-0 group-hover:!flex'>
              <div className='!hidden group-hover:!flex shrink-0 mx-1 w-[1px] h-[14px]' />
              <div className='!hidden group-hover:!flex shrink-0'>
                <CustomPopover
                  htmlContent={<Operations />}
                  position="br"
                  trigger="click"
                  btnElement={
                    <div
                      className='flex h-8 w-8 cursor-pointer items-center justify-center rounded-md'
                      className='flex items-center justify-center w-8 h-8 cursor-pointer rounded-md'
                    >
                      <RiMoreFill className='h-4 w-4 text-text-tertiary' />
                      <RiMoreFill className='w-4 h-4 text-text-tertiary' />
                    </div>
                  }
                  btnClassName={open =>
                    cn(
                      open ? '!bg-black/5 !shadow-none' : '!bg-transparent',
                      'h-8 w-8 rounded-md border-none !p-2 hover:!bg-black/5',
                      'h-8 w-8 !p-2 rounded-md border-none hover:!bg-black/5',
                    )
                  }
                  popupClassName={
@@ -360,7 +359,7 @@
                      ? '!w-[256px] translate-x-[-224px]'
                      : '!w-[160px] translate-x-[-128px]'
                  }
                  className={'!z-20 h-fit'}
                  className={'h-fit !z-20'}
                />
              </div>
            </>
app/(commonLayout)/apps/Apps.tsx
@@ -1,16 +1,13 @@
'use client'
import { useCallback, useEffect, useRef, useState } from 'react'
import {
  useRouter,
} from 'next/navigation'
import { useRouter } from 'next/navigation'
import useSWRInfinite from 'swr/infinite'
import { useTranslation } from 'react-i18next'
import { useDebounceFn } from 'ahooks'
import {
  RiApps2Line,
  RiExchange2Line,
  RiFile4Line,
  RiMessage3Line,
  RiRobot3Line,
} from '@remixicon/react'
@@ -62,11 +59,10 @@
  const [activeTab, setActiveTab] = useTabSearchParams({
    defaultTab: 'all',
  })
  const { query: { tagIDs = [], keywords = '', isCreatedByMe: queryIsCreatedByMe = false }, setQuery } = useAppsQueryState()
  const [isCreatedByMe, setIsCreatedByMe] = useState(queryIsCreatedByMe)
  const { query: { tagIDs = [], keywords = '' }, setQuery } = useAppsQueryState()
  const [isCreatedByMe, setIsCreatedByMe] = useState(false)
  const [tagFilterValue, setTagFilterValue] = useState<string[]>(tagIDs)
  const [searchKeywords, setSearchKeywords] = useState(keywords)
  const newAppCardRef = useRef<HTMLDivElement>(null)
  const setKeywords = useCallback((keywords: string) => {
    setQuery(prev => ({ ...prev, keywords }))
  }, [setQuery])
@@ -74,25 +70,18 @@
    setQuery(prev => ({ ...prev, tagIDs }))
  }, [setQuery])
  const { data, isLoading, error, setSize, mutate } = useSWRInfinite(
  const { data, isLoading, setSize, mutate } = useSWRInfinite(
    (pageIndex: number, previousPageData: AppListResponse) => getKey(pageIndex, previousPageData, activeTab, isCreatedByMe, tagIDs, searchKeywords),
    fetchAppList,
    {
      revalidateFirstPage: true,
      shouldRetryOnError: false,
      dedupingInterval: 500,
      errorRetryCount: 3,
    },
    { revalidateFirstPage: true },
  )
  const anchorRef = useRef<HTMLDivElement>(null)
  const options = [
    { value: 'all', text: t('app.types.all'), icon: <RiApps2Line className='mr-1 h-[14px] w-[14px]' /> },
    { value: 'chat', text: t('app.types.chatbot'), icon: <RiMessage3Line className='mr-1 h-[14px] w-[14px]' /> },
    { value: 'agent-chat', text: t('app.types.agent'), icon: <RiRobot3Line className='mr-1 h-[14px] w-[14px]' /> },
    { value: 'completion', text: t('app.types.completion'), icon: <RiFile4Line className='mr-1 h-[14px] w-[14px]' /> },
    { value: 'advanced-chat', text: t('app.types.advanced'), icon: <RiMessage3Line className='mr-1 h-[14px] w-[14px]' /> },
    { value: 'workflow', text: t('app.types.workflow'), icon: <RiExchange2Line className='mr-1 h-[14px] w-[14px]' /> },
    { value: 'all', text: t('app.types.all'), icon: <RiApps2Line className='w-[14px] h-[14px] mr-1' /> },
    { value: 'chat', text: t('app.types.chatbot'), icon: <RiMessage3Line className='w-[14px] h-[14px] mr-1' /> },
    { value: 'agent-chat', text: t('app.types.agent'), icon: <RiRobot3Line className='w-[14px] h-[14px] mr-1' /> },
    { value: 'workflow', text: t('app.types.workflow'), icon: <RiExchange2Line className='w-[14px] h-[14px] mr-1' /> },
  ]
  useEffect(() => {
@@ -111,22 +100,15 @@
  useEffect(() => {
    const hasMore = data?.at(-1)?.has_more ?? true
    let observer: IntersectionObserver | undefined
    if (error) {
      if (observer)
        observer.disconnect()
      return
    }
    if (anchorRef.current) {
      observer = new IntersectionObserver((entries) => {
        if (entries[0].isIntersecting && !isLoading && !error && hasMore)
        if (entries[0].isIntersecting && !isLoading && hasMore)
          setSize((size: number) => size + 1)
      }, { rootMargin: '100px' })
      observer.observe(anchorRef.current)
    }
    return () => observer?.disconnect()
  }, [isLoading, setSize, anchorRef, mutate, data, error])
  }, [isLoading, setSize, anchorRef, mutate, data])
  const { run: handleSearch } = useDebounceFn(() => {
    setSearchKeywords(keywords)
@@ -144,15 +126,9 @@
    handleTagsUpdate()
  }
  const handleCreatedByMeChange = useCallback(() => {
    const newValue = !isCreatedByMe
    setIsCreatedByMe(newValue)
    setQuery(prev => ({ ...prev, isCreatedByMe: newValue }))
  }, [isCreatedByMe, setQuery])
  return (
    <>
      <div className='sticky top-0 z-10 flex flex-wrap items-center justify-between gap-y-2 bg-background-body px-12 pb-2 pt-4 leading-[56px]'>
      <div className='sticky top-0 flex justify-between items-center pt-4 px-12 pb-2 leading-[56px] bg-background-body z-10 flex-wrap gap-y-2'>
        <TabSliderNew
          value={activeTab}
          onChange={setActiveTab}
@@ -163,7 +139,7 @@
            className='mr-2'
            label={t('app.showMyCreatedAppsOnly')}
            isChecked={isCreatedByMe}
            onChange={handleCreatedByMeChange}
            onChange={() => setIsCreatedByMe(!isCreatedByMe)}
          />
          <TagFilter type='app' value={tagFilterValue} onChange={handleTagsChange} />
          <Input
@@ -177,16 +153,16 @@
        </div>
      </div>
      {(data && data[0].total > 0)
        ? <div className='relative grid grow grid-cols-1 content-start gap-4 px-12 pt-2 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6'>
        ? <div className='grid content-start grid-cols-1 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6 gap-4 px-12 pt-2 grow relative'>
          {isCurrentWorkspaceEditor
            && <NewAppCard ref={newAppCardRef} onSuccess={mutate} />}
            && <NewAppCard onSuccess={mutate} />}
          {data.map(({ data: apps }) => apps.map(app => (
            <AppCard key={app.id} app={app} onRefresh={mutate} />
          )))}
        </div>
        : <div className='relative grid grow grid-cols-1 content-start gap-4 overflow-hidden px-12 pt-2 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6'>
        : <div className='grid content-start grid-cols-1 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6 gap-4 px-12 pt-2 grow relative overflow-hidden'>
          {isCurrentWorkspaceEditor
            && <NewAppCard ref={newAppCardRef} className='z-10' onSuccess={mutate} />}
            && <NewAppCard className='z-10' onSuccess={mutate} />}
          <NoAppsFound />
        </div>}
      <CheckModal />
@@ -204,14 +180,14 @@
  const { t } = useTranslation()
  function renderDefaultCard() {
    const defaultCards = Array.from({ length: 36 }, (_, index) => (
      <div key={index} className='inline-flex h-[160px] rounded-xl bg-background-default-lighter'></div>
      <div key={index} className='h-[160px] inline-flex rounded-xl bg-background-default-lighter'></div>
    ))
    return defaultCards
  }
  return (
    <>
      {renderDefaultCard()}
      <div className='absolute bottom-0 left-0 right-0 top-0 flex items-center justify-center bg-gradient-to-t from-background-body to-transparent'>
      <div className='absolute top-0 left-0 right-0 bottom-0 flex items-center justify-center bg-gradient-to-t from-background-body to-transparent'>
        <span className='system-md-medium text-text-tertiary'>{t('app.newApp.noAppsFound')}</span>
      </div>
    </>
app/(commonLayout)/apps/NewAppCard.tsx
@@ -1,6 +1,6 @@
'use client'
import { useMemo, useState } from 'react'
import { forwardRef, useMemo, useState } from 'react'
import {
  useRouter,
  useSearchParams,
@@ -18,15 +18,7 @@
  onSuccess?: () => void
}
const CreateAppCard = (
  {
    ref,
    className,
    onSuccess,
  }: CreateAppCardProps & {
    ref: React.RefObject<HTMLDivElement>;
  },
) => {
const CreateAppCard = forwardRef<HTMLDivElement, CreateAppCardProps>(({ className, onSuccess }, ref) => {
  const { t } = useTranslation()
  const { onPlanInfoChanged } = useProviderContext()
  const searchParams = useSearchParams()
@@ -47,22 +39,22 @@
  return (
    <div
      ref={ref}
      className={cn('relative col-span-1 inline-flex h-[160px] flex-col justify-between rounded-xl border-[0.5px] border-components-card-border bg-components-card-bg', className)}
      className={cn('relative col-span-1 inline-flex flex-col justify-between h-[160px] bg-components-card-bg rounded-xl border-[0.5px] border-components-card-border', className)}
    >
      <div className='grow rounded-t-xl p-2'>
        <div className='px-6 pb-1 pt-2 text-xs font-medium leading-[18px] text-text-tertiary'>{t('app.createApp')}</div>
        <button className='mb-1 flex w-full cursor-pointer items-center rounded-lg px-6 py-[7px] text-[13px] font-medium leading-[18px] text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary' onClick={() => setShowNewAppModal(true)}>
          <FilePlus01 className='mr-2 h-4 w-4 shrink-0' />
      <div className='grow p-2 rounded-t-xl'>
        <div className='px-6 pt-2 pb-1 text-xs font-medium leading-[18px] text-text-tertiary'>{t('app.createApp')}</div>
        <button className='w-full flex items-center mb-1 px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover' onClick={() => setShowNewAppModal(true)}>
          <FilePlus01 className='shrink-0 mr-2 w-4 h-4' />
          {t('app.newApp.startFromBlank')}
        </button>
        <button className='flex w-full cursor-pointer items-center rounded-lg px-6 py-[7px] text-[13px] font-medium leading-[18px] text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary' onClick={() => setShowNewAppTemplateDialog(true)}>
          <FilePlus02 className='mr-2 h-4 w-4 shrink-0' />
        <button className='w-full flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover' onClick={() => setShowNewAppTemplateDialog(true)}>
          <FilePlus02 className='shrink-0 mr-2 w-4 h-4' />
          {t('app.newApp.startFromTemplate')}
        </button>
        <button
          onClick={() => setShowCreateFromDSLModal(true)}
          className='flex w-full cursor-pointer items-center rounded-lg px-6 py-[7px] text-[13px] font-medium leading-[18px] text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary'>
          <FileArrow01 className='mr-2 h-4 w-4 shrink-0' />
          className='w-full flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover'>
          <FileArrow01 className='shrink-0 mr-2 w-4 h-4' />
          {t('app.importDSL')}
        </button>
      </div>
@@ -111,7 +103,7 @@
      />
    </div>
  )
}
})
CreateAppCard.displayName = 'CreateAppCard'
export default CreateAppCard
app/(commonLayout)/apps/hooks/useAppsQueryState.ts
@@ -4,20 +4,18 @@
type AppsQuery = {
  tagIDs?: string[]
  keywords?: string
  isCreatedByMe?: boolean
}
// Parse the query parameters from the URL search string.
function parseParams(params: ReadonlyURLSearchParams): AppsQuery {
  const tagIDs = params.get('tagIDs')?.split(';')
  const keywords = params.get('keywords') || undefined
  const isCreatedByMe = params.get('isCreatedByMe') === 'true'
  return { tagIDs, keywords, isCreatedByMe }
  return { tagIDs, keywords }
}
// Update the URL search string with the given query parameters.
function updateSearchParams(query: AppsQuery, current: URLSearchParams) {
  const { tagIDs, keywords, isCreatedByMe } = query || {}
  const { tagIDs, keywords } = query || {}
  if (tagIDs && tagIDs.length > 0)
    current.set('tagIDs', tagIDs.join(';'))
@@ -28,11 +26,6 @@
    current.set('keywords', keywords)
  else
    current.delete('keywords')
  if (isCreatedByMe)
    current.set('isCreatedByMe', 'true')
  else
    current.delete('isCreatedByMe')
}
function useAppsQueryState() {
app/(commonLayout)/apps/page.tsx
@@ -7,26 +7,23 @@
import Apps from './Apps'
import AppContext from '@/context/app-context'
import { LicenseStatus } from '@/types/feature'
import { useEducationInit } from '@/app/education-apply/hooks'
const AppList = () => {
  const { t } = useTranslation()
  useEducationInit()
  const systemFeatures = useContextSelector(AppContext, v => v.systemFeatures)
  return (
    <div className='relative flex h-0 shrink-0 grow flex-col overflow-y-auto bg-background-body'>
    <div className='relative flex flex-col overflow-y-auto bg-background-body shrink-0 h-0 grow'>
      <Apps />
      {systemFeatures.license.status === LicenseStatus.NONE && <footer className='shrink-0 grow-0 px-12 py-6'>
        <h3 className='text-gradient text-xl font-semibold leading-tight'>{t('app.join')}</h3>
        <p className='system-sm-regular mt-1 text-text-tertiary'>{t('app.communityIntro')}</p>
        <div className='mt-3 flex items-center gap-2'>
      {systemFeatures.license.status === LicenseStatus.NONE && <footer className='px-12 py-6 grow-0 shrink-0'>
        <h3 className='text-xl font-semibold leading-tight text-gradient'>{t('app.join')}</h3>
        <p className='mt-1 system-sm-regular text-text-tertiary'>{t('app.communityIntro')}</p>
        <div className='flex items-center gap-2 mt-3'>
          <Link className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://github.com/langgenius/dify'>
            <RiGithubFill className='h-5 w-5 text-text-tertiary' />
            <RiGithubFill className='w-5 h-5 text-text-tertiary' />
          </Link>
          <Link className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://discord.gg/FngNHpbcY7'>
            <RiDiscordFill className='h-5 w-5 text-text-tertiary' />
            <RiDiscordFill className='w-5 h-5 text-text-tertiary' />
          </Link>
        </div>
      </footer>}
app/(commonLayout)/apps/style.module.css
New file
@@ -0,0 +1,29 @@
.commonIcon {
  @apply w-4 h-4 inline-block align-middle;
  background-repeat: no-repeat;
  background-position: center center;
  background-size: contain;
}
.actionIcon {
  @apply bg-gray-500;
  mask-image: url(~@/assets/action.svg);
}
.actionItem {
  @apply h-8 py-[6px] px-3 mx-1 flex items-center gap-2 hover:bg-gray-100 rounded-lg cursor-pointer;
  width: calc(100% - 0.5rem);
}
.deleteActionItem {
  @apply hover:bg-red-50 !important;
}
.actionName {
  @apply text-gray-700 text-sm;
}
/* .completionPic {
  background-image: url(~@/app/components/app-sidebar/completion.png)
}
.expertPic {
  background-image: url(~@/app/components/app-sidebar/expert.png)
} */
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/api/page.tsx
@@ -1,6 +1,8 @@
import React from 'react'
const page = () => {
type Props = {}
const page = (props: Props) => {
  return (
    <div>dataset detail api</div>
  )
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx
@@ -2,17 +2,12 @@
import MainDetail from '@/app/components/datasets/documents/detail'
export type IDocumentDetailProps = {
  params: Promise<{ datasetId: string; documentId: string }>
  params: { datasetId: string; documentId: string }
}
const DocumentDetail = async (props: IDocumentDetailProps) => {
  const params = await props.params
  const {
    datasetId,
    documentId,
  } = params
const DocumentDetail = async ({
  params: { datasetId, documentId },
}: IDocumentDetailProps) => {
  return (
    <MainDetail datasetId={datasetId} documentId={documentId} />
  )
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx
@@ -2,17 +2,12 @@
import Settings from '@/app/components/datasets/documents/detail/settings'
export type IProps = {
  params: Promise<{ datasetId: string; documentId: string }>
  params: { datasetId: string; documentId: string }
}
const DocumentSettings = async (props: IProps) => {
  const params = await props.params
  const {
    datasetId,
    documentId,
  } = params
const DocumentSettings = async ({
  params: { datasetId, documentId },
}: IProps) => {
  return (
    <Settings datasetId={datasetId} documentId={documentId} />
  )
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/create/page.tsx
@@ -2,16 +2,12 @@
import DatasetUpdateForm from '@/app/components/datasets/create'
export type IProps = {
  params: Promise<{ datasetId: string }>
  params: { datasetId: string }
}
const Create = async (props: IProps) => {
  const params = await props.params
  const {
    datasetId,
  } = params
const Create = async ({
  params: { datasetId },
}: IProps) => {
  return (
    <DatasetUpdateForm datasetId={datasetId} />
  )
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/page.tsx
@@ -2,16 +2,12 @@
import Main from '@/app/components/datasets/documents'
export type IProps = {
  params: Promise<{ datasetId: string }>
  params: { datasetId: string }
}
const Documents = async (props: IProps) => {
  const params = await props.params
  const {
    datasetId,
  } = params
const Documents = async ({
  params: { datasetId },
}: IProps) => {
  return (
    <Main datasetId={datasetId} />
  )
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/hitTesting/page.tsx
@@ -2,16 +2,12 @@
import Main from '@/app/components/datasets/hit-testing'
type Props = {
  params: Promise<{ datasetId: string }>
  params: { datasetId: string }
}
const HitTesting = async (props: Props) => {
  const params = await props.params
  const {
    datasetId,
  } = params
const HitTesting = ({
  params: { datasetId },
}: Props) => {
  return (
    <Main datasetId={datasetId} />
  )
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx
@@ -1,17 +1,228 @@
import Main from './layout-main'
'use client'
import type { FC, SVGProps } from 'react'
import React, { useEffect, useMemo } from 'react'
import { usePathname } from 'next/navigation'
import useSWR from 'swr'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import {
  Cog8ToothIcon,
  DocumentTextIcon,
  PaperClipIcon,
} from '@heroicons/react/24/outline'
import {
  Cog8ToothIcon as Cog8ToothSolidIcon,
  // CommandLineIcon as CommandLineSolidIcon,
  DocumentTextIcon as DocumentTextSolidIcon,
} from '@heroicons/react/24/solid'
import { RiApps2AddLine, RiInformation2Line } from '@remixicon/react'
import s from './style.module.css'
import classNames from '@/utils/classnames'
import { fetchDatasetDetail, fetchDatasetRelatedApps } from '@/service/datasets'
import type { RelatedAppResponse } from '@/models/datasets'
import AppSideBar from '@/app/components/app-sidebar'
import Loading from '@/app/components/base/loading'
import DatasetDetailContext from '@/context/dataset-detail'
import { DataSourceType } from '@/models/datasets'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { LanguagesSupported } from '@/i18n/language'
import { useStore } from '@/app/components/app/store'
import { getLocaleOnClient } from '@/i18n'
import { useAppContext } from '@/context/app-context'
import Tooltip from '@/app/components/base/tooltip'
import LinkedAppsPanel from '@/app/components/base/linked-apps-panel'
const DatasetDetailLayout = async (
  props: {
export type IAppDetailLayoutProps = {
    children: React.ReactNode
    params: Promise<{ datasetId: string }>
  },
) => {
  const params = await props.params
  params: { datasetId: string }
}
const TargetIcon = ({ className }: SVGProps<SVGElement>) => {
  return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
    <g clipPath="url(#clip0_4610_6951)">
      <path d="M10.6666 5.33325V3.33325L12.6666 1.33325L13.3332 2.66659L14.6666 3.33325L12.6666 5.33325H10.6666ZM10.6666 5.33325L7.9999 7.99988M14.6666 7.99992C14.6666 11.6818 11.6818 14.6666 7.99992 14.6666C4.31802 14.6666 1.33325 11.6818 1.33325 7.99992C1.33325 4.31802 4.31802 1.33325 7.99992 1.33325M11.3333 7.99992C11.3333 9.84087 9.84087 11.3333 7.99992 11.3333C6.15897 11.3333 4.66659 9.84087 4.66659 7.99992C4.66659 6.15897 6.15897 4.66659 7.99992 4.66659" stroke="#344054" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
    </g>
    <defs>
      <clipPath id="clip0_4610_6951">
        <rect width="16" height="16" fill="white" />
      </clipPath>
    </defs>
  </svg>
}
const TargetSolidIcon = ({ className }: SVGProps<SVGElement>) => {
  return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
    <path fillRule="evenodd" clipRule="evenodd" d="M12.7733 0.67512C12.9848 0.709447 13.1669 0.843364 13.2627 1.03504L13.83 2.16961L14.9646 2.73689C15.1563 2.83273 15.2902 3.01486 15.3245 3.22639C15.3588 3.43792 15.2894 3.65305 15.1379 3.80458L13.1379 5.80458C13.0128 5.92961 12.8433 5.99985 12.6665 5.99985H10.9426L8.47124 8.47124C8.21089 8.73159 7.78878 8.73159 7.52843 8.47124C7.26808 8.21089 7.26808 7.78878 7.52843 7.52843L9.9998 5.05707V3.33318C9.9998 3.15637 10.07 2.9868 10.1951 2.86177L12.1951 0.861774C12.3466 0.710244 12.5617 0.640794 12.7733 0.67512Z" fill="#155EEF" />
    <path d="M1.99984 7.99984C1.99984 4.68613 4.68613 1.99984 7.99984 1.99984C8.36803 1.99984 8.6665 1.70136 8.6665 1.33317C8.6665 0.964981 8.36803 0.666504 7.99984 0.666504C3.94975 0.666504 0.666504 3.94975 0.666504 7.99984C0.666504 12.0499 3.94975 15.3332 7.99984 15.3332C12.0499 15.3332 15.3332 12.0499 15.3332 7.99984C15.3332 7.63165 15.0347 7.33317 14.6665 7.33317C14.2983 7.33317 13.9998 7.63165 13.9998 7.99984C13.9998 11.3135 11.3135 13.9998 7.99984 13.9998C4.68613 13.9998 1.99984 11.3135 1.99984 7.99984Z" fill="#155EEF" />
    <path d="M5.33317 7.99984C5.33317 6.52708 6.52708 5.33317 7.99984 5.33317C8.36803 5.33317 8.6665 5.03469 8.6665 4.6665C8.6665 4.29831 8.36803 3.99984 7.99984 3.99984C5.7907 3.99984 3.99984 5.7907 3.99984 7.99984C3.99984 10.209 5.7907 11.9998 7.99984 11.9998C10.209 11.9998 11.9998 10.209 11.9998 7.99984C11.9998 7.63165 11.7014 7.33317 11.3332 7.33317C10.965 7.33317 10.6665 7.63165 10.6665 7.99984C10.6665 9.4726 9.4726 10.6665 7.99984 10.6665C6.52708 10.6665 5.33317 9.4726 5.33317 7.99984Z" fill="#155EEF" />
  </svg>
}
const BookOpenIcon = ({ className }: SVGProps<SVGElement>) => {
  return <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
    <path opacity="0.12" d="M1 3.1C1 2.53995 1 2.25992 1.10899 2.04601C1.20487 1.85785 1.35785 1.70487 1.54601 1.60899C1.75992 1.5 2.03995 1.5 2.6 1.5H2.8C3.9201 1.5 4.48016 1.5 4.90798 1.71799C5.28431 1.90973 5.59027 2.21569 5.78201 2.59202C6 3.01984 6 3.5799 6 4.7V10.5L5.94997 10.425C5.60265 9.90398 5.42899 9.64349 5.19955 9.45491C4.99643 9.28796 4.76238 9.1627 4.5108 9.0863C4.22663 9 3.91355 9 3.28741 9H2.6C2.03995 9 1.75992 9 1.54601 8.89101C1.35785 8.79513 1.20487 8.64215 1.10899 8.45399C1 8.24008 1 7.96005 1 7.4V3.1Z" fill="#155EEF" />
    <path d="M6 10.5L5.94997 10.425C5.60265 9.90398 5.42899 9.64349 5.19955 9.45491C4.99643 9.28796 4.76238 9.1627 4.5108 9.0863C4.22663 9 3.91355 9 3.28741 9H2.6C2.03995 9 1.75992 9 1.54601 8.89101C1.35785 8.79513 1.20487 8.64215 1.10899 8.45399C1 8.24008 1 7.96005 1 7.4V3.1C1 2.53995 1 2.25992 1.10899 2.04601C1.20487 1.85785 1.35785 1.70487 1.54601 1.60899C1.75992 1.5 2.03995 1.5 2.6 1.5H2.8C3.9201 1.5 4.48016 1.5 4.90798 1.71799C5.28431 1.90973 5.59027 2.21569 5.78201 2.59202C6 3.01984 6 3.5799 6 4.7M6 10.5V4.7M6 10.5L6.05003 10.425C6.39735 9.90398 6.57101 9.64349 6.80045 9.45491C7.00357 9.28796 7.23762 9.1627 7.4892 9.0863C7.77337 9 8.08645 9 8.71259 9H9.4C9.96005 9 10.2401 9 10.454 8.89101C10.6422 8.79513 10.7951 8.64215 10.891 8.45399C11 8.24008 11 7.96005 11 7.4V3.1C11 2.53995 11 2.25992 10.891 2.04601C10.7951 1.85785 10.6422 1.70487 10.454 1.60899C10.2401 1.5 9.96005 1.5 9.4 1.5H9.2C8.07989 1.5 7.51984 1.5 7.09202 1.71799C6.71569 1.90973 6.40973 2.21569 6.21799 2.59202C6 3.01984 6 3.5799 6 4.7" stroke="#155EEF" strokeLinecap="round" strokeLinejoin="round" />
  </svg>
}
type IExtraInfoProps = {
  isMobile: boolean
  relatedApps?: RelatedAppResponse
  expand: boolean
}
const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => {
  const locale = getLocaleOnClient()
  const [isShowTips, { toggle: toggleTips, set: setShowTips }] = useBoolean(!isMobile)
  const { t } = useTranslation()
  const hasRelatedApps = relatedApps?.data && relatedApps?.data?.length > 0
  const relatedAppsTotal = relatedApps?.data?.length || 0
  useEffect(() => {
    setShowTips(!isMobile)
  }, [isMobile, setShowTips])
  return <div>
    {hasRelatedApps && (
      <>
        {!isMobile && (
          <Tooltip
            position='right'
            noDecoration
            needsDelay
            popupContent={
              <LinkedAppsPanel
                relatedApps={relatedApps.data}
                isMobile={isMobile}
              />
            }
          >
            <div className='inline-flex items-center system-xs-medium-uppercase text-text-secondary space-x-1 cursor-pointer'>
              <span>{relatedAppsTotal || '--'} {t('common.datasetMenus.relatedApp')}</span>
              <RiInformation2Line className='w-4 h-4' />
            </div>
          </Tooltip>
        )}
        {isMobile && <div className={classNames(s.subTitle, 'flex items-center justify-center !px-0 gap-1')}>
          {relatedAppsTotal || '--'}
          <PaperClipIcon className='h-4 w-4 text-gray-700' />
        </div>}
      </>
    )}
    {!hasRelatedApps && !expand && (
      <Tooltip
        position='right'
        noDecoration
        needsDelay
        popupContent={
          <div className='p-4 w-[240px] bg-components-panel-bg-blur border-[0.5px] border-components-panel-border rounded-xl'>
            <div className='inline-flex p-2 rounded-lg border-[0.5px] border-components-panel-border-subtle bg-background-default-subtle'>
              <RiApps2AddLine className='h-4 w-4 text-text-tertiary' />
            </div>
            <div className='text-xs text-text-tertiary my-2'>{t('common.datasetMenus.emptyTip')}</div>
            <a
              className='inline-flex items-center text-xs text-text-accent mt-2 cursor-pointer'
              href={
                locale === LanguagesSupported[1]
                  ? 'https://docs.dify.ai/v/zh-hans/guides/knowledge-base/integrate-knowledge-within-application'
                  : 'https://docs.dify.ai/guides/knowledge-base/integrate-knowledge-within-application'
              }
              target='_blank' rel='noopener noreferrer'
            >
              <BookOpenIcon className='mr-1' />
              {t('common.datasetMenus.viewDoc')}
            </a>
          </div>
        }
      >
        <div className='inline-flex items-center system-xs-medium-uppercase text-text-secondary space-x-1 cursor-pointer'>
          <span>{t('common.datasetMenus.noRelatedApp')}</span>
          <RiInformation2Line className='w-4 h-4' />
        </div>
      </Tooltip>
    )}
  </div>
}
const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
  const {
    children,
    params: { datasetId },
  } = props
  const pathname = usePathname()
  const hideSideBar = /documents\/create$/.test(pathname)
  const { t } = useTranslation()
  const { isCurrentWorkspaceDatasetOperator } = useAppContext()
  return <Main params={(await params)}>{children}</Main>
  const media = useBreakpoints()
  const isMobile = media === MediaType.mobile
  const { data: datasetRes, error, mutate: mutateDatasetRes } = useSWR({
    url: 'fetchDatasetDetail',
    datasetId,
  }, apiParams => fetchDatasetDetail(apiParams.datasetId))
  const { data: relatedApps } = useSWR({
    action: 'fetchDatasetRelatedApps',
    datasetId,
  }, apiParams => fetchDatasetRelatedApps(apiParams.datasetId))
  const navigation = useMemo(() => {
    const baseNavigation = [
      { name: t('common.datasetMenus.hitTesting'), href: `/datasets/${datasetId}/hitTesting`, icon: TargetIcon, selectedIcon: TargetSolidIcon },
      // { name: 'api & webhook', href: `/datasets/${datasetId}/api`, icon: CommandLineIcon, selectedIcon: CommandLineSolidIcon },
      { name: t('common.datasetMenus.settings'), href: `/datasets/${datasetId}/settings`, icon: Cog8ToothIcon, selectedIcon: Cog8ToothSolidIcon },
    ]
    if (datasetRes?.provider !== 'external') {
      baseNavigation.unshift({
        name: t('common.datasetMenus.documents'),
        href: `/datasets/${datasetId}/documents`,
        icon: DocumentTextIcon,
        selectedIcon: DocumentTextSolidIcon,
      })
}
export default DatasetDetailLayout
    return baseNavigation
  }, [datasetRes?.provider, datasetId, t])
  useEffect(() => {
    if (datasetRes)
      document.title = `${datasetRes.name || 'Dataset'} - Dify`
  }, [datasetRes])
  const setAppSiderbarExpand = useStore(state => state.setAppSiderbarExpand)
  useEffect(() => {
    const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand'
    const mode = isMobile ? 'collapse' : 'expand'
    setAppSiderbarExpand(isMobile ? mode : localeMode)
  }, [isMobile, setAppSiderbarExpand])
  if (!datasetRes && !error)
    return <Loading type='app' />
  return (
    <div className='grow flex overflow-hidden'>
      {!hideSideBar && <AppSideBar
        title={datasetRes?.name || '--'}
        icon={datasetRes?.icon || 'https://static.dify.ai/images/dataset-default-icon.png'}
        icon_background={datasetRes?.icon_background || '#F5F5F5'}
        desc={datasetRes?.description || '--'}
        isExternal={datasetRes?.provider === 'external'}
        navigation={navigation}
        extraInfo={!isCurrentWorkspaceDatasetOperator ? mode => <ExtraInfo isMobile={mode === 'collapse'} relatedApps={relatedApps} expand={mode === 'collapse'} /> : undefined}
        iconType={datasetRes?.data_source_type === DataSourceType.NOTION ? 'notion' : 'dataset'}
      />}
      <DatasetDetailContext.Provider value={{
        indexingTechnique: datasetRes?.indexing_technique,
        dataset: datasetRes,
        mutateDatasetRes: () => mutateDatasetRes(),
      }}>
        <div className="bg-background-default-subtle grow overflow-hidden">{children}</div>
      </DatasetDetailContext.Provider>
    </div>
  )
}
export default React.memo(DatasetDetailLayout)
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx
@@ -3,13 +3,13 @@
import Form from '@/app/components/datasets/settings/form'
const Settings = async () => {
  const locale = await getLocaleOnServer()
  const locale = getLocaleOnServer()
  const { t } = await translate(locale, 'dataset-settings')
  return (
    <div className='h-full overflow-y-auto'>
      <div className='px-6 py-3'>
        <div className='system-xl-semibold mb-1 text-text-primary'>{t('title')}</div>
        <div className='mb-1 system-xl-semibold text-text-primary'>{t('title')}</div>
        <div className='system-sm-regular text-text-tertiary'>{t('desc')}</div>
      </div>
      <Form />
app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/style.module.css
New file
@@ -0,0 +1,9 @@
.statusPoint {
  @apply flex justify-center items-center absolute -right-0.5 -bottom-0.5 w-2.5 h-2.5 bg-white rounded;
}
.subTitle {
  @apply uppercase text-xs text-gray-500 font-medium px-3 pb-2 pt-4;
}
.emptyIconDiv {
  @apply h-7 w-7 bg-gray-50 border border-[#EAECF5] inline-flex justify-center items-center rounded-lg;
}
app/(commonLayout)/datasets/ApiServer.tsx
New file
@@ -0,0 +1,41 @@
'use client'
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
import CopyFeedback from '@/app/components/base/copy-feedback'
import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-button'
import { randomString } from '@/utils'
type ApiServerProps = {
  apiBaseUrl: string
}
const ApiServer: FC<ApiServerProps> = ({
  apiBaseUrl,
}) => {
  const { t } = useTranslation()
  return (
    <div className='flex items-center flex-wrap gap-y-2'>
      <div className='flex items-center mr-2 pl-1.5 pr-1 h-8 bg-white/80 border-[0.5px] border-white rounded-lg leading-5'>
        <div className='mr-0.5 px-1.5 h-5 border border-gray-200 text-[11px] text-gray-500 rounded-md shrink-0'>{t('appApi.apiServer')}</div>
        <div className='px-1 truncate w-fit sm:w-[248px] text-[13px] font-medium text-gray-800'>{apiBaseUrl}</div>
        <div className='mx-1 w-[1px] h-[14px] bg-gray-200'></div>
        <CopyFeedback
          content={apiBaseUrl}
          selectorId={randomString(8)}
          className={'!w-6 !h-6 hover:bg-gray-200'}
        />
      </div>
      <div className='flex items-center mr-2 px-3 h-8 bg-[#ECFDF3] text-xs font-semibold text-[#039855] rounded-lg border-[0.5px] border-[#D1FADF]'>
        {t('appApi.ok')}
      </div>
      <SecretKeyButton
        className='flex-shrink-0 !h-8 bg-white'
        textCls='!text-gray-700 font-medium'
        iconCls='stroke-[1.2px]'
      />
    </div>
  )
}
export default ApiServer
app/(commonLayout)/datasets/Container.tsx
@@ -11,7 +11,7 @@
import ExternalAPIPanel from '../../components/datasets/external-api/external-api-panel'
import Datasets from './Datasets'
import DatasetFooter from './DatasetFooter'
import ApiServer from '../../components/develop/ApiServer'
import ApiServer from './ApiServer'
import Doc from './Doc'
import TabSliderNew from '@/app/components/base/tab-slider-new'
import TagManagementModal from '@/app/components/base/tag-management'
@@ -37,8 +37,6 @@
  const showTagManagementModal = useTagStore(s => s.showTagManagementModal)
  const { showExternalApiPanel, setShowExternalApiPanel } = useExternalApiPanel()
  const [includeAll, { toggle: toggleIncludeAll }] = useBoolean(false)
  document.title = `${t('dataset.knowledge')} - Dify`
  const options = useMemo(() => {
    return [
@@ -84,8 +82,8 @@
  }, [currentWorkspace, router])
  return (
    <div ref={containerRef} className='scroll-container relative flex grow flex-col overflow-y-auto bg-background-body'>
      <div className='sticky top-0 z-10 flex flex-wrap items-center justify-between gap-y-2 bg-background-body px-12 pb-2 pt-4 leading-[56px]'>
    <div ref={containerRef} className='grow relative flex flex-col bg-background-body overflow-y-auto scroll-container'>
      <div className='sticky top-0 flex justify-between pt-4 px-12 pb-2 leading-[56px] bg-background-body z-10 flex-wrap gap-y-2'>
        <TabSliderNew
          value={activeTab}
          onChange={newActiveTab => setActiveTab(newActiveTab)}
@@ -110,13 +108,13 @@
              onChange={e => handleKeywordsChange(e.target.value)}
              onClear={() => handleKeywordsChange('')}
            />
            <div className="h-4 w-[1px] bg-divider-regular" />
            <div className="w-[1px] h-4 bg-divider-regular" />
            <Button
              className='shadows-shadow-xs gap-0.5'
              className='gap-0.5 shadows-shadow-xs'
              onClick={() => setShowExternalApiPanel(true)}
            >
              <ApiConnectionMod className='h-4 w-4 text-components-button-secondary-text' />
              <div className='system-sm-medium flex items-center justify-center gap-1 px-0.5 text-components-button-secondary-text'>{t('dataset.externalAPIPanelTitle')}</div>
              <ApiConnectionMod className='w-4 h-4 text-components-button-secondary-text' />
              <div className='flex px-0.5 justify-center items-center gap-1 text-components-button-secondary-text system-sm-medium'>{t('dataset.externalAPIPanelTitle')}</div>
            </Button>
          </div>
        )}
app/(commonLayout)/datasets/DatasetCard.tsx
@@ -61,7 +61,7 @@
      if (onSuccess)
        onSuccess()
    }
    catch {
    catch (e: any) {
    }
    setShowConfirmDelete(false)
  }, [dataset.id, notify, onSuccess, t])
@@ -84,17 +84,17 @@
    }
    return (
      <div className="relative w-full py-1" onMouseLeave={onMouseLeave}>
        <div className='mx-1 flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 py-[6px] hover:bg-state-base-hover' onClick={onClickRename}>
          <span className='text-sm text-text-secondary'>{t('common.operation.settings')}</span>
        <div className='h-8 py-[6px] px-3 mx-1 flex items-center gap-2 hover:bg-gray-100 rounded-lg cursor-pointer' onClick={onClickRename}>
          <span className='text-gray-700 text-sm'>{t('common.operation.settings')}</span>
        </div>
        {props.showDelete && (
          <>
            <Divider className="!my-1" />
            <div
              className='group mx-1 flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 py-[6px] hover:bg-state-destructive-hover'
              className='group h-8 py-[6px] px-3 mx-1 flex items-center gap-2 hover:bg-red-50 rounded-lg cursor-pointer'
              onClick={onClickDelete}
            >
              <span className={cn('text-sm text-text-secondary', 'group-hover:text-text-destructive')}>
              <span className={cn('text-gray-700 text-sm', 'group-hover:text-red-500')}>
                {t('common.operation.delete')}
              </span>
            </div>
@@ -111,7 +111,7 @@
  return (
    <>
      <div
        className='group relative col-span-1 flex min-h-[160px] cursor-pointer flex-col rounded-xl border-[0.5px] border-solid border-components-card-border bg-components-card-bg shadow-sm transition-all duration-200 ease-in-out hover:shadow-lg'
        className='group relative col-span-1 bg-components-card-bg border-[0.5px] border-solid border-components-card-border rounded-xl shadow-sm min-h-[160px] flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg'
        data-disable-nprogress={true}
        onClick={(e) => {
          e.preventDefault()
@@ -121,25 +121,25 @@
        }}
      >
        {isExternalProvider(dataset.provider) && <CornerLabel label='External' className='absolute right-0' labelClassName='rounded-tr-xl' />}
        <div className='flex h-[66px] shrink-0 grow-0 items-center gap-3 px-[14px] pb-3 pt-[14px]'>
        <div className='flex pt-[14px] px-[14px] pb-3 h-[66px] items-center gap-3 grow-0 shrink-0'>
          <div className={cn(
            'flex shrink-0 items-center justify-center rounded-md border-[0.5px] border-[#E0EAFF] bg-[#F5F8FF] p-2.5',
            'shrink-0 flex items-center justify-center p-2.5 bg-[#F5F8FF] rounded-md border-[0.5px] border-[#E0EAFF]',
            !dataset.embedding_available && 'opacity-50 hover:opacity-100',
          )}>
            <Folder className='h-5 w-5 text-[#444CE7]' />
            <Folder className='w-5 h-5 text-[#444CE7]' />
          </div>
          <div className='w-0 grow py-[1px]'>
            <div className='flex items-center text-sm font-semibold leading-5 text-text-secondary'>
              <div className={cn('truncate', !dataset.embedding_available && 'text-text-tertiary opacity-50 hover:opacity-100')} title={dataset.name}>{dataset.name}</div>
          <div className='grow w-0 py-[1px]'>
            <div className='flex items-center text-sm leading-5 font-semibold text-text-secondary'>
              <div className={cn('truncate', !dataset.embedding_available && 'opacity-50 hover:opacity-100 text-text-tertiary')} title={dataset.name}>{dataset.name}</div>
              {!dataset.embedding_available && (
                <Tooltip
                  popupContent={t('dataset.unavailableTip')}
                >
                  <span className='ml-1 inline-flex w-max shrink-0 rounded-md border border-divider-regular px-1 text-xs font-normal leading-[18px] text-text-tertiary'>{t('dataset.unavailable')}</span>
                  <span className='shrink-0 inline-flex w-max ml-1 px-1 border border-gray-200 rounded-md text-gray-500 text-xs font-normal leading-[18px]'>{t('dataset.unavailable')}</span>
                </Tooltip>
              )}
            </div>
            <div className='mt-[1px] flex items-center text-xs leading-[18px] text-text-tertiary'>
            <div className='flex items-center mt-[1px] text-xs leading-[18px] text-text-tertiary'>
              <div
                className={cn('truncate', (!dataset.embedding_available || !dataset.document_count) && 'opacity-50')}
                title={dataset.provider === 'external' ? `${dataset.app_count}${t('dataset.appCount')}` : `${dataset.document_count}${t('dataset.documentCount')} · ${Math.round(dataset.word_count / 1000)}${t('dataset.wordCount')} · ${dataset.app_count}${t('dataset.appCount')}`}
@@ -150,9 +150,9 @@
                  </>
                  : <>
                    <span>{dataset.document_count}{t('dataset.documentCount')}</span>
                    <span className='mx-0.5 w-1 shrink-0 text-text-tertiary'>·</span>
                    <span className='shrink-0 mx-0.5 w-1 text-gray-400'>·</span>
                    <span>{Math.round(dataset.word_count / 1000)}{t('dataset.wordCount')}</span>
                    <span className='mx-0.5 w-1 shrink-0 text-text-tertiary'>·</span>
                    <span className='shrink-0 mx-0.5 w-1 text-gray-400'>·</span>
                    <span>{dataset.app_count}{t('dataset.appCount')}</span>
                  </>
                }
@@ -162,7 +162,7 @@
        </div>
        <div
          className={cn(
            'mb-2 max-h-[72px] grow px-[14px] text-xs leading-normal text-text-tertiary group-hover:line-clamp-2 group-hover:max-h-[36px]',
            'grow mb-2 px-[14px] max-h-[72px] text-xs leading-normal text-text-tertiary group-hover:line-clamp-2 group-hover:max-h-[36px]',
            tags.length ? 'line-clamp-2' : 'line-clamp-4',
            !dataset.embedding_available && 'opacity-50 hover:opacity-100',
          )}
@@ -170,15 +170,15 @@
          {dataset.description}
        </div>
        <div className={cn(
          'mt-4 h-[42px] shrink-0 items-center pb-[6px] pl-[14px] pr-[6px] pt-1',
          'items-center shrink-0 mt-1 pt-1 pl-[14px] pr-[6px] pb-[6px] h-[42px]',
          tags.length ? 'flex' : '!hidden group-hover:!flex',
        )}>
          <div className={cn('flex w-0 grow items-center gap-1', !dataset.embedding_available && 'opacity-50 hover:opacity-100')} onClick={(e) => {
          <div className={cn('grow flex items-center gap-1 w-0', !dataset.embedding_available && 'opacity-50 hover:opacity-100')} onClick={(e) => {
            e.stopPropagation()
            e.preventDefault()
          }}>
            <div className={cn(
              'mr-[41px] w-full grow group-hover:!mr-0 group-hover:!block',
              'group-hover:!block group-hover:!mr-0 mr-[41px] grow w-full',
              tags.length ? '!block' : '!hidden',
            )}>
              <TagSelector
@@ -192,26 +192,26 @@
              />
            </div>
          </div>
          <div className='mx-1 !hidden h-[14px] w-[1px] shrink-0 bg-divider-regular group-hover:!flex' />
          <div className='!hidden shrink-0 group-hover:!flex'>
          <div className='!hidden group-hover:!flex shrink-0 mx-1 w-[1px] h-[14px] bg-gray-200' />
          <div className='!hidden group-hover:!flex shrink-0'>
            <CustomPopover
              htmlContent={<Operations showDelete={!isCurrentWorkspaceDatasetOperator} />}
              position="br"
              trigger="click"
              btnElement={
                <div
                  className='flex h-8 w-8 cursor-pointer items-center justify-center rounded-md'
                  className='flex items-center justify-center w-8 h-8 cursor-pointer rounded-md'
                >
                  <RiMoreFill className='h-4 w-4 text-text-secondary' />
                  <RiMoreFill className='w-4 h-4 text-gray-700' />
                </div>
              }
              btnClassName={open =>
                cn(
                  open ? '!bg-black/5 !shadow-none' : '!bg-transparent',
                  'h-8 w-8 rounded-md border-none !p-2 hover:!bg-black/5',
                  'h-8 w-8 !p-2 rounded-md border-none hover:!bg-black/5',
                )
              }
              className={'!z-20 h-fit !w-[128px]'}
              className={'!w-[128px] h-fit !z-20'}
            />
          </div>
        </div>
app/(commonLayout)/datasets/DatasetFooter.tsx
@@ -6,11 +6,11 @@
  const { t } = useTranslation()
  return (
    <footer className='shrink-0 grow-0 px-12 py-6'>
      <h3 className='text-gradient text-xl font-semibold leading-tight'>{t('dataset.didYouKnow')}</h3>
      <p className='mt-1 text-sm font-normal leading-tight text-text-secondary'>
        {t('dataset.intro1')}<span className='inline-flex items-center gap-1 text-text-accent'>{t('dataset.intro2')}</span>{t('dataset.intro3')}<br />
        {t('dataset.intro4')}<span className='inline-flex items-center gap-1 text-text-accent'>{t('dataset.intro5')}</span>{t('dataset.intro6')}
    <footer className='px-12 py-6 grow-0 shrink-0'>
      <h3 className='text-xl font-semibold leading-tight text-gradient'>{t('dataset.didYouKnow')}</h3>
      <p className='mt-1 text-sm font-normal leading-tight text-gray-700'>
        {t('dataset.intro1')}<span className='inline-flex items-center gap-1 text-blue-600'>{t('dataset.intro2')}</span>{t('dataset.intro3')}<br />
        {t('dataset.intro4')}<span className='inline-flex items-center gap-1 text-blue-600'>{t('dataset.intro5')}</span>{t('dataset.intro6')}
      </p>
    </footer>
  )
app/(commonLayout)/datasets/Datasets.tsx
@@ -1,6 +1,6 @@
'use client'
import { useCallback, useEffect, useRef } from 'react'
import { useEffect, useRef } from 'react'
import useSWRInfinite from 'swr/infinite'
import { debounce } from 'lodash-es'
import { useTranslation } from 'react-i18next'
@@ -62,31 +62,24 @@
  useEffect(() => {
    loadingStateRef.current = isLoading
    document.title = `${t('dataset.knowledge')} - Dify`
  }, [isLoading, t])
  }, [isLoading])
  const onScroll = useCallback(
    debounce(() => {
      if (!loadingStateRef.current && containerRef.current && anchorRef.current) {
        const { scrollTop, clientHeight } = containerRef.current
        const anchorOffset = anchorRef.current.offsetTop
  useEffect(() => {
    const onScroll = debounce(() => {
      if (!loadingStateRef.current) {
        const { scrollTop, clientHeight } = containerRef.current!
        const anchorOffset = anchorRef.current!.offsetTop
        if (anchorOffset - scrollTop - clientHeight < 100)
          setSize(size => size + 1)
      }
    }, 50),
    [setSize],
  )
    }, 50)
  useEffect(() => {
    const currentContainer = containerRef.current
    currentContainer?.addEventListener('scroll', onScroll)
    return () => {
      currentContainer?.removeEventListener('scroll', onScroll)
      onScroll.cancel()
    }
  }, [onScroll])
    containerRef.current?.addEventListener('scroll', onScroll)
    return () => containerRef.current?.removeEventListener('scroll', onScroll)
  }, [])
  return (
    <nav className='grid shrink-0 grow grid-cols-1 content-start gap-4 px-12 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4'>
    <nav className='grid content-start grid-cols-1 gap-4 px-12 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 grow shrink-0'>
      { isCurrentWorkspaceEditor && <NewDatasetCard ref={anchorRef} /> }
      {data?.map(({ data: datasets }) => datasets.map(dataset => (
        <DatasetCard key={dataset.id} dataset={dataset} onSuccess={mutate} />),
app/(commonLayout)/datasets/Doc.tsx
@@ -1,17 +1,13 @@
'use client'
import { useEffect, useMemo, useState } from 'react'
import { useEffect, useState } from 'react'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import { RiListUnordered } from '@remixicon/react'
import TemplateEn from './template/template.en.mdx'
import TemplateZh from './template/template.zh.mdx'
import TemplateJa from './template/template.ja.mdx'
import I18n from '@/context/i18n'
import { LanguagesSupported } from '@/i18n/language'
import useTheme from '@/hooks/use-theme'
import { Theme } from '@/types/app'
import cn from '@/utils/classnames'
type DocProps = {
  apiBaseUrl: string
@@ -22,7 +18,6 @@
  const { t } = useTranslation()
  const [toc, setToc] = useState<Array<{ href: string; text: string }>>([])
  const [isTocExpanded, setIsTocExpanded] = useState(false)
  const { theme } = useTheme()
  // Set initial TOC expanded state based on screen width
  useEffect(() => {
@@ -71,28 +66,17 @@
    }
  }
  const Template = useMemo(() => {
    switch (locale) {
      case LanguagesSupported[1]:
        return <TemplateZh apiBaseUrl={apiBaseUrl} />
      case LanguagesSupported[7]:
        return <TemplateJa apiBaseUrl={apiBaseUrl} />
      default:
        return <TemplateEn apiBaseUrl={apiBaseUrl} />
    }
  }, [apiBaseUrl, locale])
  return (
    <div className="flex">
      <div className={`fixed right-20 top-32 z-10 transition-all ${isTocExpanded ? 'w-64' : 'w-10'}`}>
      <div className={`fixed right-16 top-32 z-10 transition-all ${isTocExpanded ? 'w-64' : 'w-10'}`}>
        {isTocExpanded
          ? (
            <nav className="toc max-h-[calc(100vh-150px)] w-full overflow-y-auto rounded-lg bg-components-panel-bg p-4 shadow-md">
              <div className="mb-4 flex items-center justify-between">
                <h3 className="text-lg font-semibold text-text-primary">{t('appApi.develop.toc')}</h3>
            <nav className="toc w-full bg-gray-50 p-4 rounded-lg shadow-md max-h-[calc(100vh-150px)] overflow-y-auto">
              <div className="flex justify-between items-center mb-4">
                <h3 className="text-lg font-semibold">{t('appApi.develop.toc')}</h3>
                <button
                  onClick={() => setIsTocExpanded(false)}
                  className="text-text-tertiary hover:text-text-secondary"
                  className="text-gray-500 hover:text-gray-700"
                >
                  ✕
                </button>
@@ -102,7 +86,7 @@
                  <li key={index}>
                    <a
                      href={item.href}
                      className="text-text-secondary transition-colors duration-200 hover:text-text-primary hover:underline"
                      className="text-gray-600 hover:text-gray-900 hover:underline transition-colors duration-200"
                      onClick={e => handleTocClick(e, item)}
                    >
                      {item.text}
@@ -115,14 +99,17 @@
          : (
            <button
              onClick={() => setIsTocExpanded(true)}
              className="flex h-10 w-10 items-center justify-center rounded-full bg-components-button-secondary-bg shadow-md transition-colors duration-200 hover:bg-components-button-secondary-bg-hover"
              className="w-10 h-10 bg-gray-50 rounded-full shadow-md flex items-center justify-center hover:bg-gray-100 transition-colors duration-200"
            >
              <RiListUnordered className="h-6 w-6 text-components-button-secondary-text" />
              <RiListUnordered className="w-6 h-6" />
            </button>
          )}
      </div>
      <article className={cn('prose-xl prose mx-1 rounded-t-xl bg-background-default px-4 pt-16 sm:mx-12', theme === Theme.dark && 'prose-invert')}>
        {Template}
      <article className='mx-1 px-4 sm:mx-12 pt-16 bg-white rounded-t-xl prose prose-xl'>
        {locale !== LanguagesSupported[1]
          ? <TemplateEn apiBaseUrl={apiBaseUrl} />
          : <TemplateZh apiBaseUrl={apiBaseUrl} />
        }
      </article>
    </div>
  )
app/(commonLayout)/datasets/NewDatasetCard.tsx
@@ -1,41 +1,37 @@
'use client'
import { forwardRef } from 'react'
import { useTranslation } from 'react-i18next'
import Link from 'next/link'
import {
  RiAddLine,
  RiArrowRightLine,
} from '@remixicon/react'
const CreateAppCard = (
  {
    ref,
    ..._
  },
) => {
const CreateAppCard = forwardRef<HTMLAnchorElement>((_, ref) => {
  const { t } = useTranslation()
  return (
    <div className='bg-background-default-dimm flex min-h-[160px] flex-col rounded-xl border-[0.5px]
      border-components-panel-border transition-all duration-200 ease-in-out'
    <div className='flex flex-col bg-background-default-dimm border-[0.5px] border-components-panel-border rounded-xl
      min-h-[160px] transition-all duration-200 ease-in-out'
    >
      <Link ref={ref} className='group flex grow cursor-pointer items-start p-4' href={'/datasets/create'}>
      <a ref={ref} className='group flex flex-grow items-start p-4 cursor-pointer' href='/datasets/create'>
        <div className='flex items-center gap-3'>
          <div className='flex h-10 w-10 items-center justify-center rounded-lg border border-dashed border-divider-regular bg-background-default-lighter
            p-2 group-hover:border-solid group-hover:border-effects-highlight group-hover:bg-background-default-dodge'
          <div className='w-10 h-10 p-2 flex items-center justify-center border border-dashed border-divider-regular rounded-lg
            bg-background-default-lighter group-hover:border-solid group-hover:border-effects-highlight group-hover:bg-background-default-dodge'
          >
            <RiAddLine className='h-4 w-4 text-text-tertiary group-hover:text-text-accent'/>
            <RiAddLine className='w-4 h-4 text-text-tertiary group-hover:text-text-accent'/>
          </div>
          <div className='system-md-semibold text-text-secondary group-hover:text-text-accent'>{t('dataset.createDataset')}</div>
        </div>
      </Link>
      <div className='system-xs-regular p-4 pt-0 text-text-tertiary'>{t('dataset.createDatasetIntro')}</div>
      <Link className='group flex cursor-pointer items-center gap-1 rounded-b-xl border-t-[0.5px] border-divider-subtle p-4' href={'datasets/connect'}>
      </a>
      <div className='p-4 pt-0 text-text-tertiary system-xs-regular'>{t('dataset.createDatasetIntro')}</div>
      <a className='group flex p-4 items-center gap-1 border-t-[0.5px] border-divider-subtle rounded-b-xl cursor-pointer' href='/datasets/connect'>
        <div className='system-xs-medium text-text-tertiary group-hover:text-text-accent'>{t('dataset.connectDataset')}</div>
        <RiArrowRightLine className='h-3.5 w-3.5 text-text-tertiary group-hover:text-text-accent' />
      </Link>
        <RiArrowRightLine className='w-3.5 h-3.5 text-text-tertiary group-hover:text-text-accent' />
      </a>
    </div>
  )
}
})
CreateAppCard.displayName = 'CreateAppCard'
app/(commonLayout)/datasets/page.tsx
@@ -4,4 +4,8 @@
  return <Container />
}
export const metadata = {
  title: 'Datasets - Dify',
}
export default AppList
app/(commonLayout)/datasets/template/template.en.mdx
@@ -1,8 +1,3 @@
{/**
  * @typedef Props
  * @property {string} apiBaseUrl
  */}
import { CodeGroup } from '@/app/components/develop/code.tsx'
import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstruction, Paragraph } from '@/app/components/develop/md.tsx'
@@ -37,7 +32,7 @@
  <Col>
    This API is based on an existing knowledge and creates a new document through text based on this knowledge.
    ### Path
    ### Params
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
@@ -51,6 +46,44 @@
      </Property>
      <Property name='text' type='string' key='text'>
        Document content
      </Property>
      <Property name='doc_type' type='string' key='doc_type'>
        Type of document (optional):
          - <code>book</code> Book
          - <code>web_page</code> Web page
          - <code>paper</code> Academic paper/article
          - <code>social_media_post</code> Social media post
          - <code>wikipedia_entry</code> Wikipedia entry
          - <code>personal_document</code> Personal document
          - <code>business_document</code> Business document
          - <code>im_chat_log</code> Chat log
          - <code>synced_from_notion</code> Notion document
          - <code>synced_from_github</code> GitHub document
          - <code>others</code> Other document types
      </Property>
      <Property name='doc_metadata' type='object' key='doc_metadata'>
        Document metadata (required if doc_type is provided). Fields vary by doc_type:
          For <code>book</code>:
          - <code>title</code> Book title
          - <code>language</code> Book language
          - <code>author</code> Book author
          - <code>publisher</code> Publisher name
          - <code>publication_date</code> Publication date
          - <code>isbn</code> ISBN number
          - <code>category</code> Book category
          For <code>web_page</code>:
          - <code>title</code> Page title
          - <code>url</code> Page URL
          - <code>language</code> Page language
          - <code>publish_date</code> Publish date
          - <code>author/publisher</code> Author or publisher
          - <code>topic/keywords</code> Topic or keywords
          - <code>description</code> Page description
          Please check [api/services/dataset_service.py](https://github.com/langgenius/dify/blob/main/api/services/dataset_service.py#L475) for more details on the fields required for each doc_type.
          For doc_type "others", any valid JSON object is accepted
      </Property>
      <Property name='indexing_technique' type='string' key='indexing_technique'>
        Index mode
@@ -175,7 +208,7 @@
  <Col>
    This API is based on an existing knowledge and creates a new document through a file based on this knowledge.
    ### Path
    ### Params
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
@@ -199,6 +232,68 @@
          - <code>text_model</code> Text documents are directly embedded; `economy` mode defaults to using this form
          - <code>hierarchical_model</code> Parent-child mode
          - <code>qa_model</code> Q&A Mode: Generates Q&A pairs for segmented documents and then embeds the questions
        - <code>doc_type</code> Type of document (optional)
          - <code>book</code> Book
            Document records a book or publication
          - <code>web_page</code> Web page
            Document records web page content
          - <code>paper</code> Academic paper/article
            Document records academic paper or research article
          - <code>social_media_post</code> Social media post
            Content from social media posts
          - <code>wikipedia_entry</code> Wikipedia entry
            Content from Wikipedia entries
          - <code>personal_document</code> Personal document
            Documents related to personal content
          - <code>business_document</code> Business document
            Documents related to business content
          - <code>im_chat_log</code> Chat log
            Records of instant messaging chats
          - <code>synced_from_notion</code> Notion document
            Documents synchronized from Notion
          - <code>synced_from_github</code> GitHub document
            Documents synchronized from GitHub
          - <code>others</code> Other document types
            Other document types not listed above
        - <code>doc_metadata</code> Document metadata (required if doc_type is provided)
          Fields vary by doc_type:
          For <code>book</code>:
          - <code>title</code> Book title
            Title of the book
          - <code>language</code> Book language
            Language of the book
          - <code>author</code> Book author
            Author of the book
          - <code>publisher</code> Publisher name
            Name of the publishing house
          - <code>publication_date</code> Publication date
            Date when the book was published
          - <code>isbn</code> ISBN number
            International Standard Book Number
          - <code>category</code> Book category
            Category or genre of the book
          For <code>web_page</code>:
          - <code>title</code> Page title
            Title of the web page
          - <code>url</code> Page URL
            URL address of the web page
          - <code>language</code> Page language
            Language of the web page
          - <code>publish_date</code> Publish date
            Date when the web page was published
          - <code>author/publisher</code> Author or publisher
            Author or publisher of the web page
          - <code>topic/keywords</code> Topic or keywords
            Topics or keywords of the web page
          - <code>description</code> Page description
            Description of the web page content
          Please check [api/services/dataset_service.py](https://github.com/langgenius/dify/blob/main/api/services/dataset_service.py#L475) for more details on the fields required for each doc_type.
          For doc_type "others", any valid JSON object is accepted
        - <code>doc_language</code> In Q&A mode, specify the language of the document, for example: <code>English</code>, <code>Chinese</code>
@@ -312,9 +407,46 @@
      <Property name='description' type='string' key='description'>
        Knowledge description (optional)
      </Property>
      <Property name='doc_type' type='string' key='doc_type'>
        Type of document (optional):
          - <code>book</code> Book
          - <code>web_page</code> Web page
          - <code>paper</code> Academic paper/article
          - <code>social_media_post</code> Social media post
          - <code>wikipedia_entry</code> Wikipedia entry
          - <code>personal_document</code> Personal document
          - <code>business_document</code> Business document
          - <code>im_chat_log</code> Chat log
          - <code>synced_from_notion</code> Notion document
          - <code>synced_from_github</code> GitHub document
          - <code>others</code> Other document types
      </Property>
      <Property name='doc_metadata' type='object' key='doc_metadata'>
        Document metadata (required if doc_type is provided). Fields vary by doc_type:
          For <code>book</code>:
          - <code>title</code> Book title
          - <code>language</code> Book language
          - <code>author</code> Book author
          - <code>publisher</code> Publisher name
          - <code>publication_date</code> Publication date
          - <code>isbn</code> ISBN number
          - <code>category</code> Book category
          For <code>web_page</code>:
          - <code>title</code> Page title
          - <code>url</code> Page URL
          - <code>language</code> Page language
          - <code>publish_date</code> Publish date
          - <code>author/publisher</code> Author or publisher
          - <code>topic/keywords</code> Topic or keywords
          - <code>description</code> Page description
          Please check [api/services/dataset_service.py](https://github.com/langgenius/dify/blob/main/api/services/dataset_service.py#L475) for more details on the fields required for each doc_type.
          For doc_type "others", any valid JSON object is accepted
      </Property>
      <Property name='indexing_technique' type='string' key='indexing_technique'>
        Index technique (optional)
        If this is not set, embedding_model, embedding_model_provider and retrieval_model will be set to null
          - <code>high_quality</code> High quality
          - <code>economy</code> Economy
      </Property>
@@ -334,26 +466,6 @@
      </Property>
      <Property name='external_knowledge_id' type='str' key='external_knowledge_id'>
        External knowledge ID (optional)
      </Property>
      <Property name='embedding_model' type='str' key='embedding_model'>
        Embedding model name (optional)
      </Property>
      <Property name='embedding_model_provider' type='str' key='embedding_model_provider'>
        Embedding model provider name (optional)
      </Property>
      <Property name='retrieval_model' type='object' key='retrieval_model'>
        Retrieval model (optional)
          - <code>search_method</code> (string) Search method
            - <code>hybrid_search</code> Hybrid search
            - <code>semantic_search</code> Semantic search
            - <code>full_text_search</code> Full-text search
          - <code>reranking_enable</code> (bool) Whether to enable reranking
          - <code>reranking_model</code> (object) Rerank model configuration
              - <code>reranking_provider_name</code> (string) Rerank model provider
              - <code>reranking_model_name</code> (string) Rerank model name
          - <code>top_k</code> (int) Number of results to return
          - <code>score_threshold_enabled</code> (bool) Whether to enable score threshold
          - <code>score_threshold</code> (float) Score threshold
      </Property>
    </Properties>
  </Col>
@@ -412,20 +524,11 @@
  <Col>
    ### Query
    <Properties>
      <Property name='keyword' type='string' key='keyword'>
        Search keyword, optional
      </Property>
      <Property name='tag_ids' type='array[string]' key='tag_ids'>
        Tag ID list, optional
      </Property>
      <Property name='page' type='string' key='page'>
        Page number, optional, default 1
        Page number
      </Property>
      <Property name='limit' type='string' key='limit'>
        Number of items returned, optional, default 20, range 1-100
      </Property>
      <Property name='include_all' type='boolean' key='include_all'>
        Whether to include all datasets (only effective for owners), optional, defaults to false
        Number of items returned, default 20, range 1-100
      </Property>
    </Properties>
  </Col>
@@ -476,255 +579,13 @@
<Heading
  url='/datasets/{dataset_id}'
  method='GET'
  title='Get knowledge base details by knowledge base ID'
  name='#view_dataset'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge Base ID
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="GET"
      label="/datasets/{dataset_id}"
      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
    >
    ```bash {{ title: 'cURL' }}
    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}' \
    --header 'Authorization: Bearer {api_key}'
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "id": "eaedb485-95ac-4ffd-ab1e-18da6d676a2f",
      "name": "Test Knowledge Base",
      "description": "",
      "provider": "vendor",
      "permission": "only_me",
      "data_source_type": null,
      "indexing_technique": null,
      "app_count": 0,
      "document_count": 0,
      "word_count": 0,
      "created_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6",
      "created_at": 1735620612,
      "updated_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6",
      "updated_at": 1735620612,
      "embedding_model": null,
      "embedding_model_provider": null,
      "embedding_available": true,
      "retrieval_model_dict": {
        "search_method": "semantic_search",
        "reranking_enable": false,
        "reranking_mode": null,
        "reranking_model": {
          "reranking_provider_name": "",
          "reranking_model_name": ""
        },
        "weights": null,
        "top_k": 2,
        "score_threshold_enabled": false,
        "score_threshold": null
      },
      "tags": [],
      "doc_form": null,
      "external_knowledge_info": {
        "external_knowledge_id": null,
        "external_knowledge_api_id": null,
        "external_knowledge_api_name": null,
        "external_knowledge_api_endpoint": null
      },
      "external_retrieval_model": {
        "top_k": 2,
        "score_threshold": 0.0,
        "score_threshold_enabled": null
      }
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}'
  method='PATCH'
  title='Update knowledge base'
  name='#update_dataset'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge Base ID
      </Property>
      <Property name='indexing_technique' type='string' key='indexing_technique'>
        Index technique (optional)
          - <code>high_quality</code> High quality
          - <code>economy</code> Economy
      </Property>
      <Property name='permission' type='string' key='permission'>
        Permission
          - <code>only_me</code> Only me
          - <code>all_team_members</code> All team members
          - <code>partial_members</code> Partial members
      </Property>
      <Property name='embedding_model_provider' type='string' key='embedding_model_provider'>
        Specified embedding model provider, must be set up in the system first, corresponding to the provider field(Optional)
      </Property>
      <Property name='embedding_model' type='string' key='embedding_model'>
        Specified embedding model, corresponding to the model field(Optional)
      </Property>
      <Property name='retrieval_model' type='object' key='retrieval_model'>
        Retrieval model (optional, if not filled, it will be recalled according to the default method)
        - <code>search_method</code> (text) Search method: One of the following four keywords is required
          - <code>keyword_search</code> Keyword search
          - <code>semantic_search</code> Semantic search
          - <code>full_text_search</code> Full-text search
          - <code>hybrid_search</code> Hybrid search
        - <code>reranking_enable</code> (bool) Whether to enable reranking, required if the search mode is semantic_search or hybrid_search (optional)
        - <code>reranking_mode</code> (object) Rerank model configuration, required if reranking is enabled
            - <code>reranking_provider_name</code> (string) Rerank model provider
            - <code>reranking_model_name</code> (string) Rerank model name
        - <code>weights</code> (float) Semantic search weight setting in hybrid search mode
        - <code>top_k</code> (integer) Number of results to return (optional)
        - <code>score_threshold_enabled</code> (bool) Whether to enable score threshold
        - <code>score_threshold</code> (float) Score threshold
      </Property>
      <Property name='partial_member_list' type='array' key='partial_member_list'>
        Partial member list(Optional)
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="PATCH"
      label="/datasets/{dataset_id}"
      targetCode={`curl --location --request PATCH '${props.apiBaseUrl}/datasets/{dataset_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{
        "name": "Test Knowledge Base",
        "indexing_technique": "high_quality",
        "permission": "only_me",
        "embedding_model_provider": "zhipuai",
        "embedding_model": "embedding-3",
        "retrieval_model": {
          "search_method": "keyword_search",
          "reranking_enable": false,
          "reranking_mode": null,
          "reranking_model": {
              "reranking_provider_name": "",
              "reranking_model_name": ""
          },
          "weights": null,
          "top_k": 1,
          "score_threshold_enabled": false,
          "score_threshold": null
        },
        "partial_member_list": []
      }'
    `}
    >
    ```bash {{ title: 'cURL' }}
    curl --location --request PATCH '${props.apiBaseUrl}/datasets/{dataset_id}' \
    --header 'Authorization: Bearer {api_key}' \
    --header 'Content-Type: application/json' \
    --data-raw '{
      "name": "Test Knowledge Base",
      "indexing_technique": "high_quality",
      "permission": "only_me",
      "embedding_model_provider": "zhipuai",
      "embedding_model": "embedding-3",
      "retrieval_model": {
        "search_method": "keyword_search",
        "reranking_enable": false,
        "reranking_mode": null,
        "reranking_model": {
            "reranking_provider_name": "",
            "reranking_model_name": ""
        },
        "weights": null,
        "top_k": 1,
        "score_threshold_enabled": false,
        "score_threshold": null
      },
      "partial_member_list": []
    }'
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "id": "eaedb485-95ac-4ffd-ab1e-18da6d676a2f",
      "name": "Test Knowledge Base",
      "description": "",
      "provider": "vendor",
      "permission": "only_me",
      "data_source_type": null,
      "indexing_technique": "high_quality",
      "app_count": 0,
      "document_count": 0,
      "word_count": 0,
      "created_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6",
      "created_at": 1735620612,
      "updated_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6",
      "updated_at": 1735622679,
      "embedding_model": "embedding-3",
      "embedding_model_provider": "zhipuai",
      "embedding_available": null,
      "retrieval_model_dict": {
          "search_method": "semantic_search",
          "reranking_enable": false,
          "reranking_mode": null,
          "reranking_model": {
              "reranking_provider_name": "",
              "reranking_model_name": ""
          },
          "weights": null,
          "top_k": 2,
          "score_threshold_enabled": false,
          "score_threshold": null
      },
      "tags": [],
      "doc_form": null,
      "external_knowledge_info": {
          "external_knowledge_id": null,
          "external_knowledge_api_id": null,
          "external_knowledge_api_name": null,
          "external_knowledge_api_endpoint": null
      },
      "external_retrieval_model": {
          "top_k": 2,
          "score_threshold": 0.0,
          "score_threshold_enabled": null
      },
      "partial_member_list": []
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}'
  method='DELETE'
  title='Delete a Knowledge Base'
  name='#delete_dataset'
/>
<Row>
  <Col>
    ### Path
    ### Params
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
@@ -763,7 +624,7 @@
  <Col>
    This API is based on an existing knowledge and updates the document through text based on this knowledge.
    ### Path
    ### Params
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
@@ -865,7 +726,7 @@
  <Col>
    This API is based on an existing knowledge, and updates documents through files based on this knowledge
    ### Path
    ### Params
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
@@ -901,6 +762,67 @@
              - <code>separator</code> Segmentation identifier. Currently, only one delimiter is allowed. The default is <code>***</code>
              - <code>max_tokens</code> The maximum length (tokens) must be validated to be shorter than the length of the parent chunk
              - <code>chunk_overlap</code> Define the overlap between adjacent chunks (optional)
            - <code>doc_type</code> Type of document (optional)
              - <code>book</code> Book
                Document records a book or publication
              - <code>web_page</code> Web page
                Document records web page content
              - <code>paper</code> Academic paper/article
                Document records academic paper or research article
              - <code>social_media_post</code> Social media post
                Content from social media posts
              - <code>wikipedia_entry</code> Wikipedia entry
                Content from Wikipedia entries
              - <code>personal_document</code> Personal document
                Documents related to personal content
              - <code>business_document</code> Business document
                Documents related to business content
              - <code>im_chat_log</code> Chat log
                Records of instant messaging chats
              - <code>synced_from_notion</code> Notion document
                Documents synchronized from Notion
              - <code>synced_from_github</code> GitHub document
                Documents synchronized from GitHub
              - <code>others</code> Other document types
                Other document types not listed above
            - <code>doc_metadata</code> Document metadata (required if doc_type is provided)
              Fields vary by doc_type:
              For <code>book</code>:
              - <code>title</code> Book title
                Title of the book
              - <code>language</code> Book language
                Language of the book
              - <code>author</code> Book author
                Author of the book
              - <code>publisher</code> Publisher name
                Name of the publishing house
              - <code>publication_date</code> Publication date
                Date when the book was published
              - <code>isbn</code> ISBN number
                International Standard Book Number
              - <code>category</code> Book category
                Category or genre of the book
              For <code>web_page</code>:
              - <code>title</code> Page title
                Title of the web page
              - <code>url</code> Page URL
                URL address of the web page
              - <code>language</code> Page language
                Language of the web page
              - <code>publish_date</code> Publish date
                Date when the web page was published
              - <code>author/publisher</code> Author or publisher
                Author or publisher of the web page
              - <code>topic/keywords</code> Topic or keywords
                Topics or keywords of the web page
              - <code>description</code> Page description
                Description of the web page content
              Please check [api/services/dataset_service.py](https://github.com/langgenius/dify/blob/main/api/services/dataset_service.py#L475) for more details on the fields required for each doc_type.
              For doc_type "others", any valid JSON object is accepted
      </Property>
    </Properties>
  </Col>
@@ -962,7 +884,7 @@
/>
<Row>
  <Col>
    ### Path
    ### Params
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
@@ -1017,7 +939,7 @@
/>
<Row>
  <Col>
    ### Path
    ### Params
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
@@ -1040,8 +962,10 @@
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```text {{ title: 'Response' }}
    204 No Content
    ```json {{ title: 'Response' }}
    {
      "result": "success"
    }
    ```
    </CodeGroup>
  </Col>
@@ -1057,7 +981,7 @@
/>
<Row>
  <Col>
    ### Path
    ### Params
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
@@ -1132,7 +1056,7 @@
/>
<Row>
  <Col>
    ### Path
    ### Params
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
@@ -1236,12 +1160,6 @@
      <Property name='status' type='string' key='status'>
        Search status, completed
      </Property>
      <Property name='page' type='string' key='page'>
        Page number (optional)
      </Property>
      <Property name='limit' type='string' key='limit'>
        Number of items returned, default 20, range 1-100 (optional)
      </Property>
    </Properties>
  </Col>
  <Col sticky>
@@ -1285,11 +1203,7 @@
        "error": null,
        "stopped_at": null
      }],
      "doc_form": "text_model",
      "has_more": false,
      "limit": 20,
      "total": 9,
      "page": 1
      "doc_form": "text_model"
    }
    ```
    </CodeGroup>
@@ -1324,17 +1238,19 @@
      title="Request"
      tag="DELETE"
      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}"
      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'`}
      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/segments/{segment_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'`}
    >
    ```bash {{ title: 'cURL' }}
    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}' \
    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/segments/{segment_id}' \
    --header 'Authorization: Bearer {api_key}' \
    --header 'Content-Type: application/json'
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```text {{ title: 'Response' }}
    204 No Content
    ```json {{ title: 'Response' }}
    {
      "result": "success"
    }
    ```
    </CodeGroup>
  </Col>
@@ -1397,7 +1313,7 @@
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "data": {
      "data": [{
        "id": "",
        "position": 1,
        "document_id": "",
@@ -1421,276 +1337,8 @@
        "completed_at": 1695312007,
        "error": null,
        "stopped_at": null
      },
      "doc_form": "text_model"
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks'
  method='POST'
  title='Create Child Chunk'
  name='#create_child_chunk'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
      </Property>
      <Property name='document_id' type='string' key='document_id'>
        Document ID
      </Property>
      <Property name='segment_id' type='string' key='segment_id'>
        Segment ID
      </Property>
    </Properties>
    ### Request Body
    <Properties>
      <Property name='content' type='string' key='content'>
        Child chunk content
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="POST"
      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks"
      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"content": "Child chunk content"}'`}
    >
    ```bash {{ title: 'cURL' }}
    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks' \
    --header 'Authorization: Bearer {api_key}' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "content": "Child chunk content"
    }'
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "data": {
        "id": "",
        "segment_id": "",
        "content": "Child chunk content",
        "word_count": 25,
        "tokens": 0,
        "index_node_id": "",
        "index_node_hash": "",
        "status": "completed",
        "created_by": "",
        "created_at": 1695312007,
        "indexing_at": 1695312007,
        "completed_at": 1695312007,
        "error": null,
        "stopped_at": null
      }
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks'
  method='GET'
  title='Get Child Chunks'
  name='#get_child_chunks'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
      </Property>
      <Property name='document_id' type='string' key='document_id'>
        Document ID
      </Property>
      <Property name='segment_id' type='string' key='segment_id'>
        Segment ID
      </Property>
    </Properties>
    ### Query
    <Properties>
      <Property name='keyword' type='string' key='keyword'>
        Search keyword (optional)
      </Property>
      <Property name='page' type='integer' key='page'>
        Page number (optional, default: 1)
      </Property>
      <Property name='limit' type='integer' key='limit'>
        Items per page (optional, default: 20, max: 100)
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="GET"
      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks"
      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks?page=1&limit=20' \\\n--header 'Authorization: Bearer {api_key}'`}
    >
    ```bash {{ title: 'cURL' }}
    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks?page=1&limit=20' \
    --header 'Authorization: Bearer {api_key}'
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "data": [{
        "id": "",
        "segment_id": "",
        "content": "Child chunk content",
        "word_count": 25,
        "tokens": 0,
        "index_node_id": "",
        "index_node_hash": "",
        "status": "completed",
        "created_by": "",
        "created_at": 1695312007,
        "indexing_at": 1695312007,
        "completed_at": 1695312007,
        "error": null,
        "stopped_at": null
      }],
      "total": 1,
      "total_pages": 1,
      "page": 1,
      "limit": 20
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}'
  method='DELETE'
  title='Delete Child Chunk'
  name='#delete_child_chunk'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
      </Property>
      <Property name='document_id' type='string' key='document_id'>
        Document ID
      </Property>
      <Property name='segment_id' type='string' key='segment_id'>
        Segment ID
      </Property>
      <Property name='child_chunk_id' type='string' key='child_chunk_id'>
        Child Chunk ID
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="DELETE"
      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}"
      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/segments/{segment_id}/child_chunks/{child_chunk_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
    >
    ```bash {{ title: 'cURL' }}
    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/segments/{segment_id}/child_chunks/{child_chunk_id}' \
    --header 'Authorization: Bearer {api_key}'
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```text {{ title: 'Response' }}
    204 No Content
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}'
  method='PATCH'
  title='Update Child Chunk'
  name='#update_child_chunk'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
      </Property>
      <Property name='document_id' type='string' key='document_id'>
        Document ID
      </Property>
      <Property name='segment_id' type='string' key='segment_id'>
        Segment ID
      </Property>
      <Property name='child_chunk_id' type='string' key='child_chunk_id'>
        Child Chunk ID
      </Property>
    </Properties>
    ### Request Body
    <Properties>
      <Property name='content' type='string' key='content'>
        Child chunk content
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="PATCH"
      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}"
      targetCode={`curl --location --request PATCH '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"content": "Updated child chunk content"}'`}
    >
    ```bash {{ title: 'cURL' }}
    curl --location --request PATCH '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}' \
    --header 'Authorization: Bearer {api_key}' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "content": "Updated child chunk content"
    }'
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "data": {
        "id": "",
        "segment_id": "",
        "content": "Updated child chunk content",
        "word_count": 25,
        "tokens": 0,
        "index_node_id": "",
        "index_node_hash": "",
        "status": "completed",
        "created_by": "",
        "created_at": 1695312007,
        "indexing_at": 1695312007,
        "completed_at": 1695312007,
        "error": null,
        "stopped_at": null
      }
      "doc_form": "text_model"
    }
    ```
    </CodeGroup>
@@ -1880,363 +1528,11 @@
              "id": "a8c6c36f-9f5d-4d7a-8472-f5d7b75d71d2",
              "data_source_type": "upload_file",
              "name": "readme.txt",
              "doc_type": null
            }
          },
          "score": 3.730463140527718e-05,
          "tsne_position": null
        }
      ]
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/metadata'
  method='POST'
  title='Create a Knowledge Metadata'
  name='#create_metadata'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
      </Property>
    </Properties>
    ### Request Body
    <Properties>
      <Property name='segment' type='object' key='segment'>
        - <code>type</code> (string) Metadata type, required
        - <code>name</code> (string) Metadata name, required
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="POST"
      label="/datasets/{dataset_id}/metadata"
      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/metadata' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'\\\n--data-raw '{"type": "string", "name": "test"}'`}
    >
    ```bash {{ title: 'cURL' }}
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "id": "abc",
      "type": "string",
      "name": "test",
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/metadata/{metadata_id}'
  method='PATCH'
  title='Update a Knowledge Metadata'
  name='#update_metadata'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
      </Property>
      <Property name='metadata_id' type='string' key='metadata_id'>
        Metadata ID
      </Property>
    </Properties>
    ### Request Body
    <Properties>
      <Property name='segment' type='object' key='segment'>
        - <code>name</code> (string) Metadata name, required
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="PATCH"
      label="/datasets/{dataset_id}/metadata/{metadata_id}"
      targetCode={`curl --location --request PATCH '${props.apiBaseUrl}/datasets/{dataset_id}/metadata/{metadata_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'\\\n--data-raw '{"name": "test"}'`}
    >
    ```bash {{ title: 'cURL' }}
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "id": "abc",
      "type": "string",
      "name": "test",
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/metadata/{metadata_id}'
  method='DELETE'
  title='Delete a Knowledge Metadata'
  name='#delete_metadata'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
      </Property>
      <Property name='metadata_id' type='string' key='metadata_id'>
        Metadata ID
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="DELETE"
      label="/datasets/{dataset_id}/metadata/{metadata_id}"
      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/metadata/{metadata_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
    >
    ```bash {{ title: 'cURL' }}
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/metadata/built-in/{action}'
  method='POST'
  title='Disable Or Enable Built-in Metadata'
  name='#toggle_metadata'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
      </Property>
      <Property name='action' type='string' key='action'>
        disable/enable
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="POST"
      label="/datasets/{dataset_id}/metadata/built-in/{action}"
      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/metadata/built-in/{action}' \\\n--header 'Authorization: Bearer {api_key}'`}
    >
    ```bash {{ title: 'cURL' }}
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/documents/metadata'
  method='POST'
  title='Update Documents Metadata'
  name='#update_documents_metadata'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
      </Property>
    </Properties>
    ### Request Body
    <Properties>
      <Property name='operation_data' type='object list' key='segments'>
        - <code>document_id</code> (string) Document ID
        - <code>metadata_list</code> (list) Metadata list
          - <code>id</code> (string) Metadata ID
          - <code>value</code> (string) Metadata value
          - <code>name</code> (string) Metadata name
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="POST"
      label="/datasets/{dataset_id}/documents/metadata"
      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/metadata' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'\\\n--data-raw '{"operation_data": [{"document_id": "document_id", "metadata_list": [{"id": "id", "value": "value", "name": "name"}]}]}'`}
    >
    ```bash {{ title: 'cURL' }}
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/metadata'
  method='GET'
  title='Get Knowledge Metadata List'
  name='#dataset_metadata_list'
/>
<Row>
  <Col>
    ### Params
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        Knowledge ID
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="GET"
      label="/datasets/{dataset_id}/metadata"
      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/metadata' \\\n--header 'Authorization: Bearer {api_key}'`}
    >
    ```bash {{ title: 'cURL' }}
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "doc_metadata": [
        {
          "id": "",
          "name": "name",
          "type": "string",
          "use_count": 0,
        },
        ...
      ],
      "built_in_field_enabled": true
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
 url='/workspaces/current/models/model-types/text-embedding'
 method='GET'
 title='Get available embedding models'
 name='#model_type_list'
/>
<Row>
   <Col>
     ### Query
     <Properties>
     </Properties>
   </Col>
   <Col sticky>
     <CodeGroup
       title="Request"
       tag="GET"
       label="/datasets/{dataset_id}"
       targetCode={`curl --location --location --request GET '${props.apiBaseUrl}/workspaces/current/models/model-types/text-embedding' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' `}
     >
     ```bash {{ title: 'cURL' }}
     curl --location --request GET '${props.apiBaseUrl}/workspaces/current/models/model-types/text-embedding' \
     --header 'Authorization: Bearer {api_key}' \
     --header 'Content-Type: application/json' \
     ```
     </CodeGroup>
     <CodeGroup title="Response">
     ```json {{ title: 'Response' }}
     {
       "data": [
           {
               "provider": "zhipuai",
               "label": {
                   "zh_Hans": "智谱 AI",
                   "en_US": "ZHIPU AI"
               },
               "icon_small": {
                   "zh_Hans": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_small/zh_Hans",
                   "en_US": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_small/en_US"
               },
               "icon_large": {
                   "zh_Hans": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_large/zh_Hans",
                   "en_US": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_large/en_US"
               },
               "status": "active",
               "models": [
                   {
                       "model": "embedding-3",
                       "label": {
                           "zh_Hans": "embedding-3",
                           "en_US": "embedding-3"
                       },
                       "model_type": "text-embedding",
                       "features": null,
                       "fetch_from": "predefined-model",
                       "model_properties": {
                           "context_size": 8192
                       },
                       "deprecated": false,
                       "status": "active",
                       "load_balancing_enabled": false
                   },
                   {
                       "model": "embedding-2",
                       "label": {
                           "zh_Hans": "embedding-2",
                           "en_US": "embedding-2"
                       },
                       "model_type": "text-embedding",
                       "features": null,
                       "fetch_from": "predefined-model",
                       "model_properties": {
                           "context_size": 8192
                       },
                       "deprecated": false,
                       "status": "active",
                       "load_balancing_enabled": false
                   },
                   {
                       "model": "text_embedding",
                       "label": {
                           "zh_Hans": "text_embedding",
                           "en_US": "text_embedding"
                       },
                       "model_type": "text-embedding",
                       "features": null,
                       "fetch_from": "predefined-model",
                       "model_properties": {
                           "context_size": 512
                       },
                       "deprecated": false,
                       "status": "active",
                       "load_balancing_enabled": false
                   }
               ]
           }
       ]
     }
app/(commonLayout)/datasets/template/template.zh.mdx
@@ -1,8 +1,3 @@
{/**
  * @typedef Props
  * @property {string} apiBaseUrl
  */}
import { CodeGroup } from '@/app/components/develop/code.tsx'
import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstruction, Paragraph } from '@/app/components/develop/md.tsx'
@@ -52,10 +47,49 @@
      <Property name='text' type='string' key='text'>
        文档内容
      </Property>
      <Property name='doc_type' type='string' key='doc_type'>
        文档类型(选填)
          - <code>book</code> 图书 Book
          - <code>web_page</code> 网页 Web page
          - <code>paper</code> 学术论文/文章 Academic paper/article
          - <code>social_media_post</code> 社交媒体帖子 Social media post
          - <code>wikipedia_entry</code> 维基百科条目 Wikipedia entry
          - <code>personal_document</code> 个人文档 Personal document
          - <code>business_document</code> 商业文档 Business document
          - <code>im_chat_log</code> 即时通讯记录 Chat log
          - <code>synced_from_notion</code> Notion同步文档 Notion document
          - <code>synced_from_github</code> GitHub同步文档 GitHub document
          - <code>others</code> 其他文档类型 Other document types
      </Property>
      <Property name='doc_metadata' type='object' key='doc_metadata'>
        文档元数据(如提供文档类型则必填)。字段因文档类型而异:
          针对图书 For <code>book</code>:
          - <code>title</code> 书名 Book title
          - <code>language</code> 图书语言 Book language
          - <code>author</code> 作者 Book author
          - <code>publisher</code> 出版社 Publisher name
          - <code>publication_date</code> 出版日期 Publication date
          - <code>isbn</code> ISBN号码 ISBN number
          - <code>category</code> 图书分类 Book category
          针对网页 For <code>web_page</code>:
          - <code>title</code> 页面标题 Page title
          - <code>url</code> 页面网址 Page URL
          - <code>language</code> 页面语言 Page language
          - <code>publish_date</code> 发布日期 Publish date
          - <code>author/publisher</code> 作者/发布者 Author or publisher
          - <code>topic/keywords</code> 主题/关键词 Topic or keywords
          - <code>description</code> 页面描述 Page description
          请查看 [api/services/dataset_service.py](https://github.com/langgenius/dify/blob/main/api/services/dataset_service.py#L475) 了解各文档类型所需字段的详细信息。
          针对"其他"类型文档,接受任何有效的JSON对象
      </Property>
      <Property name='indexing_technique' type='string' key='indexing_technique'>
        索引方式
          - <code>high_quality</code> 高质量:使用
        ding 模型进行嵌入,构建为向量数据库索引
          - <code>high_quality</code> 高质量:使用  embedding 模型进行嵌入,构建为向量数据库索引
          - <code>economy</code> 经济:使用 keyword table index 的倒排索引进行构建
      </Property>
      <Property name='doc_form' type='string' key='doc_form'>
@@ -94,9 +128,6 @@
            - <code>semantic_search</code> 语义检索
            - <code>full_text_search</code> 全文检索
          - <code>reranking_enable</code> (bool) 是否开启rerank
          - <code>reranking_mode</code> (String) 混合检索
            - <code>weighted_score</code> 权重设置
            - <code>reranking_model</code> Rerank 模型
          - <code>reranking_model</code> (object) Rerank 模型配置
            - <code>reranking_provider_name</code> (string) Rerank 模型的提供商
            - <code>reranking_model_name</code> (string) Rerank 模型的名称
@@ -203,6 +234,68 @@
          - <code>text_model</code> text 文档直接 embedding,经济模式默认为该模式
          - <code>hierarchical_model</code> parent-child 模式
          - <code>qa_model</code> Q&A 模式:为分片文档生成 Q&A 对,然后对问题进行 embedding
        - <code>doc_type</code> 文档类型(选填)Type of document (optional)
          - <code>book</code> 图书
            文档记录一本书籍或出版物
          - <code>web_page</code> 网页
            网页内容的文档记录
          - <code>paper</code> 学术论文/文章
            学术论文或研究文章的记录
          - <code>social_media_post</code> 社交媒体帖子
            社交媒体上的帖子内容
          - <code>wikipedia_entry</code> 维基百科条目
            维基百科的词条内容
          - <code>personal_document</code> 个人文档
            个人相关的文档记录
          - <code>business_document</code> 商业文档
            商业相关的文档记录
          - <code>im_chat_log</code> 即时通讯记录
            即时通讯的聊天记录
          - <code>synced_from_notion</code> Notion同步文档
            从Notion同步的文档内容
          - <code>synced_from_github</code> GitHub同步文档
            从GitHub同步的文档内容
          - <code>others</code> 其他文档类型
            其他未列出的文档类型
        - <code>doc_metadata</code> 文档元数据(如提供文档类型则必填
          字段因文档类型而异
          针对图书类型 For <code>book</code>:
          - <code>title</code> 书名
            书籍的标题
          - <code>language</code> 图书语言
            书籍的语言
          - <code>author</code> 作者
            书籍的作者
          - <code>publisher</code> 出版社
            出版社的名称
          - <code>publication_date</code> 出版日期
            书籍的出版日期
          - <code>isbn</code> ISBN号码
            书籍的ISBN编号
          - <code>category</code> 图书分类
            书籍的分类类别
          针对网页类型 For <code>web_page</code>:
          - <code>title</code> 页面标题
            网页的标题
          - <code>url</code> 页面网址
            网页的URL地址
          - <code>language</code> 页面语言
            网页的语言
          - <code>publish_date</code> 发布日期
            网页的发布日期
          - <code>author/publisher</code> 作者/发布者
            网页的作者或发布者
          - <code>topic/keywords</code> 主题/关键词
            网页的主题或关键词
          - <code>description</code> 页面描述
            网页的描述信息
          请查看 [api/services/dataset_service.py](https://github.com/langgenius/dify/blob/main/api/services/dataset_service.py#L475) 了解各文档类型所需字段的详细信息。
          针对"其他"类型文档,接受任何有效的JSON对象
        - <code>doc_language</code> 在 Q&A 模式下,指定文档的语言,例如:<code>English</code>、<code>Chinese</code>
@@ -338,26 +431,6 @@
      <Property name='external_knowledge_id' type='str' key='external_knowledge_id'>
        外部知识库 ID(选填)
      </Property>
      <Property name='embedding_model' type='str' key='embedding_model'>
        Embedding 模型名称
      </Property>
      <Property name='embedding_model_provider' type='str' key='embedding_model_provider'>
        Embedding 模型供应商
      </Property>
      <Property name='retrieval_model' type='object' key='retrieval_model'>
        检索模式
          - <code>search_method</code> (string) 检索方法
            - <code>hybrid_search</code> 混合检索
            - <code>semantic_search</code> 语义检索
            - <code>full_text_search</code> 全文检索
          - <code>reranking_enable</code> (bool) 是否开启rerank
          - <code>reranking_model</code> (object) Rerank 模型配置
            - <code>reranking_provider_name</code> (string) Rerank 模型的提供商
            - <code>reranking_model_name</code> (string) Rerank 模型的名称
          - <code>top_k</code> (int) 召回条数
          - <code>score_threshold_enabled</code> (bool)是否开启召回分数限制
          - <code>score_threshold</code> (float) 召回分数限制
      </Property>
    </Properties>
  </Col>
  <Col sticky>
@@ -415,20 +488,11 @@
  <Col>
    ### Query
    <Properties>
      <Property name='keyword' type='string' key='keyword'>
        搜索关键词,可选
      </Property>
      <Property name='tag_ids' type='array[string]' key='tag_ids'>
        标签 ID 列表,可选
      </Property>
      <Property name='page' type='integer' key='page'>
        页码,可选,默认为 1
      <Property name='page' type='string' key='page'>
        页码
      </Property>
      <Property name='limit' type='string' key='limit'>
        返回条数,可选,默认 20,范围 1-100
      </Property>
      <Property name='include_all' type='boolean' key='include_all'>
        是否包含所有数据集(仅对所有者生效),可选,默认为 false
        返回条数,默认 20,范围 1-100
      </Property>
    </Properties>
  </Col>
@@ -469,252 +533,6 @@
      "limit": 20,
      "total": 50,
      "page": 1
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}'
  method='GET'
  title='查看知识库详情'
  name='#view_dataset'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        知识库 ID
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="GET"
      label="/datasets/{dataset_id}"
      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
    >
    ```bash {{ title: 'cURL' }}
    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}' \
    --header 'Authorization: Bearer {api_key}'
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "id": "eaedb485-95ac-4ffd-ab1e-18da6d676a2f",
      "name": "Test Knowledge Base",
      "description": "",
      "provider": "vendor",
      "permission": "only_me",
      "data_source_type": null,
      "indexing_technique": null,
      "app_count": 0,
      "document_count": 0,
      "word_count": 0,
      "created_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6",
      "created_at": 1735620612,
      "updated_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6",
      "updated_at": 1735620612,
      "embedding_model": null,
      "embedding_model_provider": null,
      "embedding_available": true,
      "retrieval_model_dict": {
        "search_method": "semantic_search",
        "reranking_enable": false,
        "reranking_mode": null,
        "reranking_model": {
          "reranking_provider_name": "",
          "reranking_model_name": ""
        },
        "weights": null,
        "top_k": 2,
        "score_threshold_enabled": false,
        "score_threshold": null
      },
      "tags": [],
      "doc_form": null,
      "external_knowledge_info": {
        "external_knowledge_id": null,
        "external_knowledge_api_id": null,
        "external_knowledge_api_name": null,
        "external_knowledge_api_endpoint": null
      },
      "external_retrieval_model": {
        "top_k": 2,
        "score_threshold": 0.0,
        "score_threshold_enabled": null
      }
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}'
  method='PATCH'
  title='修改知识库详情'
  name='#update_dataset'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        知识库 ID
      </Property>
    </Properties>
    ### Request Body
    <Properties>
      <Property name='indexing_technique' type='string' key='indexing_technique'>
        索引模式(选填,建议填写)
          - <code>high_quality</code> 高质量
          - <code>economy</code> 经济
      </Property>
      <Property name='permission' type='string' key='permission'>
        权限(选填,默认 only_me)
          - <code>only_me</code> 仅自己
          - <code>all_team_members</code> 所有团队成员
          - <code>partial_members</code> 部分团队成员
      </Property>
      <Property name='embedding_model_provider' type='string' key='embedding_model_provider'>
        嵌入模型提供商(选填), 必须先在系统内设定好接入的模型,对应的是provider字段
      </Property>
      <Property name='embedding_model' type='string' key='embedding_model'>
        嵌入模型(选填)
      </Property>
      <Property name='retrieval_model' type='object' key='retrieval_model'>
        检索参数(选填,如不填,按照默认方式召回)
        - <code>search_method</code> (text) 检索方法:以下四个关键字之一,必填
          - <code>keyword_search</code> 关键字检索
          - <code>semantic_search</code> 语义检索
          - <code>full_text_search</code> 全文检索
          - <code>hybrid_search</code> 混合检索
        - <code>reranking_enable</code> (bool) 是否启用 Reranking,非必填,如果检索模式为 semantic_search 模式或者 hybrid_search 则传值
        - <code>reranking_mode</code> (object) Rerank 模型配置,非必填,如果启用了 reranking 则传值
            - <code>reranking_provider_name</code> (string) Rerank 模型提供商
            - <code>reranking_model_name</code> (string) Rerank 模型名称
        - <code>weights</code> (float) 混合检索模式下语意检索的权重设置
        - <code>top_k</code> (integer) 返回结果数量,非必填
        - <code>score_threshold_enabled</code> (bool) 是否开启 score 阈值
        - <code>score_threshold</code> (float) Score 阈值
      </Property>
      <Property name='partial_member_list' type='array' key='partial_member_list'>
        部分团队成员 ID 列表(选填)
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="PATCH"
      label="/datasets/{dataset_id}"
      targetCode={`curl --location --request PATCH '${props.apiBaseUrl}/datasets/{dataset_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{
        "name": "Test Knowledge Base",
        "indexing_technique": "high_quality",
        "permission": "only_me",
        "embedding_model_provider": "zhipuai",
        "embedding_model": "embedding-3",
        "retrieval_model": {
          "search_method": "keyword_search",
          "reranking_enable": false,
          "reranking_mode": null,
          "reranking_model": {
              "reranking_provider_name": "",
              "reranking_model_name": ""
          },
          "weights": null,
          "top_k": 1,
          "score_threshold_enabled": false,
          "score_threshold": null
        },
        "partial_member_list": []
      }'
    `}
    >
    ```bash {{ title: 'cURL' }}
    curl --location --request PATCH '${props.apiBaseUrl}/datasets/{dataset_id}' \
    --header 'Authorization: Bearer {api_key}' \
    --header 'Content-Type: application/json' \
    --data-raw '{
      "name": "Test Knowledge Base",
      "indexing_technique": "high_quality",
      "permission": "only_me",
      "embedding_model_provider": "zhipuai",
      "embedding_model": "embedding-3",
      "retrieval_model": {
        "search_method": "keyword_search",
        "reranking_enable": false,
        "reranking_mode": null,
        "reranking_model": {
            "reranking_provider_name": "",
            "reranking_model_name": ""
        },
        "weights": null,
        "top_k": 1,
        "score_threshold_enabled": false,
        "score_threshold": null
      },
      "partial_member_list": []
    }'
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "id": "eaedb485-95ac-4ffd-ab1e-18da6d676a2f",
      "name": "Test Knowledge Base",
      "description": "",
      "provider": "vendor",
      "permission": "only_me",
      "data_source_type": null,
      "indexing_technique": "high_quality",
      "app_count": 0,
      "document_count": 0,
      "word_count": 0,
      "created_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6",
      "created_at": 1735620612,
      "updated_by": "e99a1635-f725-4951-a99a-1daaaa76cfc6",
      "updated_at": 1735622679,
      "embedding_model": "embedding-3",
      "embedding_model_provider": "zhipuai",
      "embedding_available": null,
      "retrieval_model_dict": {
          "search_method": "semantic_search",
          "reranking_enable": false,
          "reranking_mode": null,
          "reranking_model": {
              "reranking_provider_name": "",
              "reranking_model_name": ""
          },
          "weights": null,
          "top_k": 2,
          "score_threshold_enabled": false,
          "score_threshold": null
      },
      "tags": [],
      "doc_form": null,
      "external_knowledge_info": {
          "external_knowledge_id": null,
          "external_knowledge_api_id": null,
          "external_knowledge_api_name": null,
          "external_knowledge_api_endpoint": null
      },
      "external_retrieval_model": {
          "top_k": 2,
          "score_threshold": 0.0,
          "score_threshold_enabled": null
      },
      "partial_member_list": []
    }
    ```
    </CodeGroup>
@@ -787,6 +605,46 @@
      </Property>
      <Property name='text' type='string' key='text'>
        文档内容(选填)
      </Property>
      <Property name='doc_type' type='string' key='doc_type'>
        文档类型(选填)
          - <code>book</code> 图书 Book
          - <code>web_page</code> 网页 Web page
          - <code>paper</code> 学术论文/文章 Academic paper/article
          - <code>social_media_post</code> 社交媒体帖子 Social media post
          - <code>wikipedia_entry</code> 维基百科条目 Wikipedia entry
          - <code>personal_document</code> 个人文档 Personal document
          - <code>business_document</code> 商业文档 Business document
          - <code>im_chat_log</code> 即时通讯记录 Chat log
          - <code>synced_from_notion</code> Notion同步文档 Notion document
          - <code>synced_from_github</code> GitHub同步文档 GitHub document
          - <code>others</code> 其他文档类型 Other document types
      </Property>
      <Property name='doc_metadata' type='object' key='doc_metadata'>
        文档元数据(如提供文档类型则必填)。字段因文档类型而异:
          针对图书 For <code>book</code>:
          - <code>title</code> 书名 Book title
          - <code>language</code> 图书语言 Book language
          - <code>author</code> 作者 Book author
          - <code>publisher</code> 出版社 Publisher name
          - <code>publication_date</code> 出版日期 Publication date
          - <code>isbn</code> ISBN号码 ISBN number
          - <code>category</code> 图书分类 Book category
          针对网页 For <code>web_page</code>:
          - <code>title</code> 页面标题 Page title
          - <code>url</code> 页面网址 Page URL
          - <code>language</code> 页面语言 Page language
          - <code>publish_date</code> 发布日期 Publish date
          - <code>author/publisher</code> 作者/发布者 Author or publisher
          - <code>topic/keywords</code> 主题/关键词 Topic or keywords
          - <code>description</code> 页面描述 Page description
          请查看 [api/services/dataset_service.py](https://github.com/langgenius/dify/blob/main/api/services/dataset_service.py#L475) 了解各文档类型所需字段的详细信息。
          针对"其他"类型文档,接受任何有效的JSON对象
      </Property>
      <Property name='process_rule' type='object' key='process_rule'>
        处理规则(选填)
@@ -908,6 +766,68 @@
              - <code>separator</code> 分段标识符,目前仅允许设置一个分隔符。默认为 <code>***</code>
              - <code>max_tokens</code> 最大长度 (token) 需要校验小于父级的长度
              - <code>chunk_overlap</code> 分段重叠指的是在对数据进行分段时,段与段之间存在一定的重叠部分(选填)
            - <code>doc_type</code> 文档类型(选填)Type of document (optional)
              - <code>book</code> 图书
                文档记录一本书籍或出版物
              - <code>web_page</code> 网页
                网页内容的文档记录
              - <code>paper</code> 学术论文/文章
                学术论文或研究文章的记录
              - <code>social_media_post</code> 社交媒体帖子
                社交媒体上的帖子内容
              - <code>wikipedia_entry</code> 维基百科条目
                维基百科的词条内容
              - <code>personal_document</code> 个人文档
                个人相关的文档记录
              - <code>business_document</code> 商业文档
                商业相关的文档记录
              - <code>im_chat_log</code> 即时通讯记录
                即时通讯的聊天记录
              - <code>synced_from_notion</code> Notion同步文档
                从Notion同步的文档内容
              - <code>synced_from_github</code> GitHub同步文档
                从GitHub同步的文档内容
              - <code>others</code> 其他文档类型
                其他未列出的文档类型
            - <code>doc_metadata</code> 文档元数据(如提供文档类型则必填
              字段因文档类型而异
              针对图书类型 For <code>book</code>:
              - <code>title</code> 书名
                书籍的标题
              - <code>language</code> 图书语言
                书籍的语言
              - <code>author</code> 作者
                书籍的作者
              - <code>publisher</code> 出版社
                出版社的名称
              - <code>publication_date</code> 出版日期
                书籍的出版日期
              - <code>isbn</code> ISBN号码
                书籍的ISBN编号
              - <code>category</code> 图书分类
                书籍的分类类别
              针对网页类型 For <code>web_page</code>:
              - <code>title</code> 页面标题
                网页的标题
              - <code>url</code> 页面网址
                网页的URL地址
              - <code>language</code> 页面语言
                网页的语言
              - <code>publish_date</code> 发布日期
                网页的发布日期
              - <code>author/publisher</code> 作者/发布者
                网页的作者或发布者
              - <code>topic/keywords</code> 主题/关键词
                网页的主题或关键词
              - <code>description</code> 页面描述
                网页的描述信息
              请查看 [api/services/dataset_service.py](https://github.com/langgenius/dify/blob/main/api/services/dataset_service.py#L475) 了解各文档类型所需字段的详细信息。
              针对"其他"类型文档,接受任何有效的JSON对象
      </Property>
    </Properties>
  </Col>
@@ -1047,8 +967,10 @@
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```text {{ title: 'Response' }}
    204 No Content
    ```json {{ title: 'Response' }}
    {
      "result": "success"
    }
    ```
    </CodeGroup>
  </Col>
@@ -1243,12 +1165,6 @@
      <Property name='status' type='string' key='status'>
        搜索状态,completed
      </Property>
      <Property name='page' type='string' key='page'>
        页码,可选
      </Property>
      <Property name='limit' type='string' key='limit'>
        返回条数,可选,默认 20,范围 1-100
      </Property>
    </Properties>
  </Col>
  <Col sticky>
@@ -1292,11 +1208,7 @@
        "error": null,
        "stopped_at": null
      }],
      "doc_form": "text_model",
      "has_more": false,
      "limit": 20,
      "total": 9,
      "page": 1
      "doc_form": "text_model"
    }
    ```
    </CodeGroup>
@@ -1340,8 +1252,10 @@
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```text {{ title: 'Response' }}
    204 No Content
    ```json {{ title: 'Response' }}
    {
      "result": "success"
    }
    ```
    </CodeGroup>
  </Col>
@@ -1405,7 +1319,7 @@
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "data": {
      "data": [{
        "id": "",
        "position": 1,
        "document_id": "",
@@ -1429,310 +1343,8 @@
        "completed_at": 1695312007,
        "error": null,
        "stopped_at": null
      },
      "doc_form": "text_model"
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks'
  method='POST'
  title='新增文档子分段'
  name='#create_child_chunk'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        知识库 ID
      </Property>
      <Property name='document_id' type='string' key='document_id'>
        文档 ID
      </Property>
      <Property name='segment_id' type='string' key='segment_id'>
        分段 ID
      </Property>
    </Properties>
    ### Request Body
    <Properties>
      <Property name='content' type='string' key='content'>
        子分段内容
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="POST"
      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks"
      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"content": "子分段内容"}'`}
    >
    ```bash {{ title: 'cURL' }}
    curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks' \
    --header 'Authorization: Bearer {api_key}' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "content": "子分段内容"
    }'
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "data": {
        "id": "",
        "segment_id": "",
        "content": "子分段内容",
        "word_count": 25,
        "tokens": 0,
        "index_node_id": "",
        "index_node_hash": "",
        "status": "completed",
        "created_by": "",
        "created_at": 1695312007,
        "indexing_at": 1695312007,
        "completed_at": 1695312007,
        "error": null,
        "stopped_at": null
      }
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks'
  method='GET'
  title='查询文档子分段'
  name='#get_child_chunks'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        知识库 ID
      </Property>
      <Property name='document_id' type='string' key='document_id'>
        文档 ID
      </Property>
      <Property name='segment_id' type='string' key='segment_id'>
        分段 ID
      </Property>
    </Properties>
    ### Query
    <Properties>
      <Property name='keyword' type='string' key='keyword'>
        搜索关键词(选填)
      </Property>
      <Property name='page' type='integer' key='page'>
        页码(选填,默认1)
      </Property>
      <Property name='limit' type='integer' key='limit'>
        每页数量(选填,默认20,最大100)
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="GET"
      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks"
      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks?page=1&limit=20' \\\n--header 'Authorization: Bearer {api_key}'`}
    >
    ```bash {{ title: 'cURL' }}
    curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks?page=1&limit=20' \
    --header 'Authorization: Bearer {api_key}'
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "data": [{
        "id": "",
        "segment_id": "",
        "content": "子分段内容",
        "word_count": 25,
        "tokens": 0,
        "index_node_id": "",
        "index_node_hash": "",
        "status": "completed",
        "created_by": "",
        "created_at": 1695312007,
        "indexing_at": 1695312007,
        "completed_at": 1695312007,
        "error": null,
        "stopped_at": null
      }],
      "total": 1,
      "total_pages": 1,
      "page": 1,
      "limit": 20
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}'
  method='DELETE'
  title='删除文档子分段'
  name='#delete_child_chunk'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        知识库 ID
      </Property>
      <Property name='document_id' type='string' key='document_id'>
        文档 ID
      </Property>
      <Property name='segment_id' type='string' key='segment_id'>
        分段 ID
      </Property>
      <Property name='child_chunk_id' type='string' key='child_chunk_id'>
        子分段 ID
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="DELETE"
      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}"
      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
    >
    ```bash {{ title: 'cURL' }}
    curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}' \
    --header 'Authorization: Bearer {api_key}'
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```text {{ title: 'Response' }}
    204 No Content
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Row>
  <Col>
    ### 错误信息
    <Properties>
      <Property name='code' type='string' key='code'>
        返回的错误代码
      </Property>
    </Properties>
    <Properties>
      <Property name='status' type='number' key='status'>
        返回的错误状态
      </Property>
    </Properties>
    <Properties>
      <Property name='message' type='string' key='message'>
        返回的错误信息
      </Property>
    </Properties>
  </Col>
  <Col>
    <CodeGroup title="Example">
    ```json {{ title: 'Response' }}
      {
        "code": "no_file_uploaded",
        "message": "Please upload your file.",
        "status": 400
      }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}'
  method='PATCH'
  title='更新文档子分段'
  name='#update_child_chunk'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        知识库 ID
      </Property>
      <Property name='document_id' type='string' key='document_id'>
        文档 ID
      </Property>
      <Property name='segment_id' type='string' key='segment_id'>
        分段 ID
      </Property>
      <Property name='child_chunk_id' type='string' key='child_chunk_id'>
        子分段 ID
      </Property>
    </Properties>
    ### Request Body
    <Properties>
      <Property name='content' type='string' key='content'>
        子分段内容
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="PATCH"
      label="/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}"
      targetCode={`curl --location --request PATCH '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{"content": "更新的子分段内容"}'`}
    >
    ```bash {{ title: 'cURL' }}
    curl --location --request PATCH '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}/segments/{segment_id}/child_chunks/{child_chunk_id}' \
    --header 'Authorization: Bearer {api_key}' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "content": "更新的子分段内容"
    }'
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "data": {
        "id": "",
        "segment_id": "",
        "content": "更新的子分段内容",
        "word_count": 25,
        "tokens": 0,
        "index_node_id": "",
        "index_node_hash": "",
        "status": "completed",
        "created_by": "",
        "created_at": 1695312007,
        "indexing_at": 1695312007,
        "completed_at": 1695312007,
        "error": null,
        "stopped_at": null
      }
      "doc_form": "text_model"
    }
    ```
    </CodeGroup>
@@ -1814,7 +1426,7 @@
      </Property>
      <Property name='retrieval_model' type='object' key='retrieval_model'>
        检索参数(选填,如不填,按照默认方式召回)
        - <code>search_method</code> (text) 检索方法:以下四个关键字之一,必填
        - <code>search_method</code> (text) 检索方法:以下三个关键字之一,必填
          - <code>keyword_search</code> 关键字检索
          - <code>semantic_search</code> 语义检索
          - <code>full_text_search</code> 全文检索
@@ -1922,6 +1534,7 @@
              "id": "a8c6c36f-9f5d-4d7a-8472-f5d7b75d71d2",
              "data_source_type": "upload_file",
              "name": "readme.txt",
              "doc_type": null
            }
          },
          "score": 3.730463140527718e-05,
@@ -1934,358 +1547,6 @@
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/metadata'
  method='POST'
  title='新增元数据'
  name='#create_metadata'
/>
<Row>
  <Col>
    ### Params
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        知识库 ID
      </Property>
    </Properties>
    ### Request Body
    <Properties>
      <Property name='segment' type='object' key='segment'>
        - <code>type</code> (string) 元数据类型,必填
        - <code>name</code> (string) 元数据名称,必填
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="POST"
      label="/datasets/{dataset_id}/metadata"
      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/metadata' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'\\\n--data-raw '{"type": "string", "name": "test"}'`}
    >
    ```bash {{ title: 'cURL' }}
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "id": "abc",
      "type": "string",
      "name": "test",
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/metadata/{metadata_id}'
  method='PATCH'
  title='更新元数据'
  name='#update_metadata'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        知识库 ID
      </Property>
      <Property name='metadata_id' type='string' key='metadata_id'>
        元数据 ID
      </Property>
    </Properties>
    ### Request Body
    <Properties>
      <Property name='segment' type='object' key='segment'>
        - <code>name</code> (string) 元数据名称,必填
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="PATCH"
      label="/datasets/{dataset_id}/metadata/{metadata_id}"
      targetCode={`curl --location --request PATCH '${props.apiBaseUrl}/datasets/{dataset_id}/metadata/{metadata_id}' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'\\\n--data-raw '{"name": "test"}'`}
    >
    ```bash {{ title: 'cURL' }}
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "id": "abc",
      "type": "string",
      "name": "test",
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/metadata/{metadata_id}'
  method='DELETE'
  title='删除元数据'
  name='#delete_metadata'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        知识库 ID
      </Property>
      <Property name='metadata_id' type='string' key='metadata_id'>
        元数据 ID
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="DELETE"
      label="/datasets/{dataset_id}/metadata/{metadata_id}"
      targetCode={`curl --location --request DELETE '${props.apiBaseUrl}/datasets/{dataset_id}/metadata/{metadata_id}' \\\n--header 'Authorization: Bearer {api_key}'`}
    >
    ```bash {{ title: 'cURL' }}
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/metadata/built-in/{action}'
  method='POST'
  title='启用/禁用内置元数据'
  name='#toggle_metadata'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        知识库 ID
      </Property>
      <Property name='action' type='string' key='action'>
        disable/enable
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="POST"
      label="/datasets/{dataset_id}/metadata/built-in/{action}"
      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/metadata/built-in/{action}' \\\n--header 'Authorization: Bearer {api_key}'`}
    >
    ```bash {{ title: 'cURL' }}
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/documents/metadata'
  method='POST'
  title='更新文档元数据'
  name='#update_documents_metadata'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        知识库 ID
      </Property>
    </Properties>
    ### Request Body
    <Properties>
      <Property name='operation_data' type='object list' key='segments'>
        - <code>document_id</code> (string) 文档 ID
        - <code>metadata_list</code> (list) 元数据列表
          - <code>id</code> (string) 元数据 ID
          - <code>type</code> (string) 元数据类型
          - <code>name</code> (string) 元数据名称
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="POST"
      label="/datasets/{dataset_id}/documents/metadata"
      targetCode={`curl --location --request POST '${props.apiBaseUrl}/datasets/{dataset_id}/documents/metadata' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json'\\\n--data-raw '{"operation_data": [{"document_id": "document_id", "metadata_list": [{"id": "id", "value": "value", "name": "name"}]}]}'`}
    >
    ```bash {{ title: 'cURL' }}
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
  url='/datasets/{dataset_id}/metadata'
  method='GET'
  title='查询知识库元数据列表'
  name='#dataset_metadata_list'
/>
<Row>
  <Col>
    ### Path
    <Properties>
      <Property name='dataset_id' type='string' key='dataset_id'>
        知识库 ID
      </Property>
    </Properties>
  </Col>
  <Col sticky>
    <CodeGroup
      title="Request"
      tag="GET"
      label="/datasets/{dataset_id}/metadata"
      targetCode={`curl --location --request GET '${props.apiBaseUrl}/datasets/{dataset_id}/metadata' \\\n--header 'Authorization: Bearer {api_key}'`}
    >
    ```bash {{ title: 'cURL' }}
    ```
    </CodeGroup>
    <CodeGroup title="Response">
    ```json {{ title: 'Response' }}
    {
      "doc_metadata": [
        {
          "id": "",
          "name": "name",
          "type": "string",
          "use_count": 0,
        },
        ...
      ],
      "built_in_field_enabled": true
    }
    ```
    </CodeGroup>
  </Col>
</Row>
<hr className='ml-0 mr-0' />
<Heading
 url='/workspaces/current/models/model-types/text-embedding'
 method='GET'
 title='获取嵌入模型列表'
 name='#model_type_list'
/>
<Row>
   <Col>
     ### Query
     <Properties>
     </Properties>
   </Col>
   <Col sticky>
     <CodeGroup
       title="Request"
       tag="GET"
       label="/datasets/{dataset_id}"
       targetCode={`curl --location --location --request GET '${props.apiBaseUrl}/workspaces/current/models/model-types/text-embedding' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' `}
     >
     ```bash {{ title: 'cURL' }}
     curl --location --request GET '${props.apiBaseUrl}/workspaces/current/models/model-types/text-embedding' \
     --header 'Authorization: Bearer {api_key}' \
     --header 'Content-Type: application/json' \
     ```
     </CodeGroup>
     <CodeGroup title="Response">
     ```json {{ title: 'Response' }}
     {
       "data": [
           {
               "provider": "zhipuai",
               "label": {
                   "zh_Hans": "智谱 AI",
                   "en_US": "ZHIPU AI"
               },
               "icon_small": {
                   "zh_Hans": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_small/zh_Hans",
                   "en_US": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_small/en_US"
               },
               "icon_large": {
                   "zh_Hans": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_large/zh_Hans",
                   "en_US": "http://127.0.0.1:5001/console/api/workspaces/current/model-providers/zhipuai/icon_large/en_US"
               },
               "status": "active",
               "models": [
                   {
                       "model": "embedding-3",
                       "label": {
                           "zh_Hans": "embedding-3",
                           "en_US": "embedding-3"
                       },
                       "model_type": "text-embedding",
                       "features": null,
                       "fetch_from": "predefined-model",
                       "model_properties": {
                           "context_size": 8192
                       },
                       "deprecated": false,
                       "status": "active",
                       "load_balancing_enabled": false
                   },
                   {
                       "model": "embedding-2",
                       "label": {
                           "zh_Hans": "embedding-2",
                           "en_US": "embedding-2"
                       },
                       "model_type": "text-embedding",
                       "features": null,
                       "fetch_from": "predefined-model",
                       "model_properties": {
                           "context_size": 8192
                       },
                       "deprecated": false,
                       "status": "active",
                       "load_balancing_enabled": false
                   },
                   {
                       "model": "text_embedding",
                       "label": {
                           "zh_Hans": "text_embedding",
                           "en_US": "text_embedding"
                       },
                       "model_type": "text-embedding",
                       "features": null,
                       "fetch_from": "predefined-model",
                       "model_properties": {
                           "context_size": 512
                       },
                       "deprecated": false,
                       "status": "active",
                       "load_balancing_enabled": false
                   }
               ]
           }
       ]
     }
     ```
     </CodeGroup>
   </Col>
</Row>
<hr className='ml-0 mr-0' />
app/(commonLayout)/explore/installed/[appId]/page.tsx
@@ -3,14 +3,14 @@
import Main from '@/app/components/explore/installed-app'
export type IInstalledAppProps = {
  params: Promise<{
  params: {
    appId: string
  }>
  }
}
const InstalledApp: FC<IInstalledAppProps> = async ({ params }) => {
const InstalledApp: FC<IInstalledAppProps> = ({ params: { appId } }) => {
  return (
    <Main id={(await params).appId} />
    <Main id={appId} />
  )
}
export default React.memo(InstalledApp)
app/(commonLayout)/list.module.css
app/(shareLayout)/layout.tsx
@@ -10,7 +10,7 @@
  children: React.ReactNode
}> = ({ children }) => {
  return (
    <div className="h-full min-w-[300px] pb-[env(safe-area-inset-bottom)]">
    <div className="min-w-[300px] h-full pb-[env(safe-area-inset-bottom)]">
      {children}
    </div>
  )
app/(shareLayout)/webapp-signin/page.tsx
@@ -92,8 +92,8 @@
  }, [message, tokenFromUrl]) // Added dependencies to useEffect
  return (
    <div className="flex h-full items-center justify-center">
      <div className={cn('flex w-full grow flex-col items-center justify-center', 'px-6', 'md:px-[108px]')}>
    <div className="flex items-center justify-center h-full">
      <div className={cn('flex flex-col items-center w-full grow justify-center', 'px-6', 'md:px-[108px]')}>
        <Loading type='area' />
      </div>
    </div>
app/account/account-page/AvatarWithEdit.tsx
@@ -83,13 +83,13 @@
  return (
    <>
      <div>
        <div className="group relative">
        <div className="relative group">
          <Avatar {...props} />
          <div
            onClick={() => { setIsShowAvatarPicker(true) }}
            className="absolute inset-0 flex cursor-pointer items-center justify-center rounded-full bg-black bg-opacity-50 opacity-0 transition-opacity group-hover:opacity-100"
            className="absolute inset-0 bg-black bg-opacity-50 rounded-full opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer flex items-center justify-center"
          >
            <span className="text-xs text-white">
            <span className="text-white text-xs">
              <RiPencilLine />
            </span>
          </div>
@@ -105,7 +105,7 @@
        <ImageInput onImageInput={handleImageInput} cropShape='round' />
        <Divider className='m-0' />
        <div className='flex w-full items-center justify-center gap-2 p-3'>
        <div className='w-full flex items-center justify-center p-3 gap-2'>
          <Button className='w-full' onClick={() => setIsShowAvatarPicker(false)}>
            {t('app.iconPicker.cancel')}
          </Button>
app/account/account-page/index.tsx
@@ -1,9 +1,7 @@
'use client'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
  RiGraduationCapFill,
} from '@remixicon/react'
import { useContext } from 'use-context-selector'
import DeleteAccount from '../delete-account'
import s from './index.module.css'
@@ -14,12 +12,10 @@
import Button from '@/app/components/base/button'
import { updateUserProfile } from '@/service/common'
import { useAppContext } from '@/context/app-context'
import { useProviderContext } from '@/context/provider-context'
import { ToastContext } from '@/app/components/base/toast'
import AppIcon from '@/app/components/base/app-icon'
import { IS_CE_EDITION } from '@/config'
import Input from '@/app/components/base/input'
import PremiumBadge from '@/app/components/base/premium-badge'
const titleClassName = `
  system-sm-semibold text-text-secondary
@@ -34,7 +30,6 @@
  const { t } = useTranslation()
  const { systemFeatures } = useAppContext()
  const { mutateUserProfile, userProfile, apps } = useAppContext()
  const { isEducationAccount } = useProviderContext()
  const { notify } = useContext(ToastContext)
  const [editNameModalVisible, setEditNameModalVisible] = useState(false)
  const [editName, setEditName] = useState('')
@@ -127,46 +122,38 @@
        <div className='mr-3'>
          <AppIcon size='tiny' />
        </div>
        <div className='system-sm-medium mt-[3px] text-text-secondary'>{item.name}</div>
        <div className='mt-[3px] system-sm-medium text-text-secondary'>{item.name}</div>
      </div>
    )
  }
  return (
    <>
      <div className='pb-3 pt-2'>
      <div className='pt-2 pb-3'>
        <h4 className='title-2xl-semi-bold text-text-primary'>{t('common.account.myAccount')}</h4>
      </div>
      <div className='mb-8 flex items-center rounded-xl bg-gradient-to-r from-background-gradient-bg-fill-chat-bg-2 to-background-gradient-bg-fill-chat-bg-1 p-6'>
      <div className='mb-8 p-6 rounded-xl flex items-center bg-gradient-to-r from-background-gradient-bg-fill-chat-bg-2 to-background-gradient-bg-fill-chat-bg-1'>
        <AvatarWithEdit avatar={userProfile.avatar_url} name={userProfile.name} onSave={ mutateUserProfile } size={64} />
        <div className='ml-4'>
          <p className='system-xl-semibold text-text-primary'>
            {userProfile.name}
            {isEducationAccount && (
              <PremiumBadge size='s' color='blue' className='ml-1 !px-2'>
                <RiGraduationCapFill className='mr-1 h-3 w-3' />
                <span className='system-2xs-medium'>EDU</span>
              </PremiumBadge>
            )}
          </p>
          <p className='system-xl-semibold text-text-primary'>{userProfile.name}</p>
          <p className='system-xs-regular text-text-tertiary'>{userProfile.email}</p>
        </div>
      </div>
      <div className='mb-8'>
        <div className={titleClassName}>{t('common.account.name')}</div>
        <div className='mt-2 flex w-full items-center justify-between gap-2'>
          <div className='system-sm-regular flex-1 rounded-lg bg-components-input-bg-normal p-2 text-components-input-text-filled '>
        <div className='flex items-center justify-between gap-2 w-full mt-2'>
          <div className='flex-1 bg-components-input-bg-normal rounded-lg p-2 system-sm-regular text-components-input-text-filled '>
            <span className='pl-1'>{userProfile.name}</span>
          </div>
          <div className='system-sm-medium cursor-pointer rounded-lg bg-components-button-tertiary-bg px-3 py-2 text-components-button-tertiary-text' onClick={handleEditName}>
          <div className='bg-components-button-tertiary-bg rounded-lg py-2 px-3 cursor-pointer system-sm-medium text-components-button-tertiary-text' onClick={handleEditName}>
            {t('common.operation.edit')}
          </div>
        </div>
      </div>
      <div className='mb-8'>
        <div className={titleClassName}>{t('common.account.email')}</div>
        <div className='mt-2 flex w-full items-center justify-between gap-2'>
          <div className='system-sm-regular flex-1 rounded-lg bg-components-input-bg-normal p-2 text-components-input-text-filled '>
        <div className='flex items-center justify-between gap-2 w-full mt-2'>
          <div className='flex-1 bg-components-input-bg-normal rounded-lg p-2 system-sm-regular text-components-input-text-filled '>
            <span className='pl-1'>{userProfile.email}</span>
          </div>
        </div>
@@ -175,8 +162,8 @@
        systemFeatures.enable_email_password_login && (
          <div className='mb-8 flex justify-between gap-2'>
            <div>
              <div className='system-sm-semibold mb-1 text-text-secondary'>{t('common.account.password')}</div>
              <div className='body-xs-regular mb-2 text-text-tertiary'>{t('common.account.passwordTip')}</div>
              <div className='mb-1 system-sm-semibold text-text-secondary'>{t('common.account.password')}</div>
              <div className='mb-2 body-xs-regular text-text-tertiary'>{t('common.account.passwordTip')}</div>
            </div>
            <Button onClick={() => setEditPasswordModalVisible(true)}>{userProfile.is_password_set ? t('common.account.resetPassword') : t('common.account.setPassword')}</Button>
          </div>
@@ -203,13 +190,13 @@
            onClose={() => setEditNameModalVisible(false)}
            className={s.modal}
          >
            <div className='title-2xl-semi-bold mb-6 text-text-primary'>{t('common.account.editName')}</div>
            <div className='mb-6 title-2xl-semi-bold text-text-primary'>{t('common.account.editName')}</div>
            <div className={titleClassName}>{t('common.account.name')}</div>
            <Input className='mt-2'
              value={editName}
              onChange={e => setEditName(e.target.value)}
            />
            <div className='mt-10 flex justify-end'>
            <div className='flex justify-end mt-10'>
              <Button className='mr-2' onClick={() => setEditNameModalVisible(false)}>{t('common.operation.cancel')}</Button>
              <Button
                disabled={editing || !editName}
@@ -232,7 +219,7 @@
            }}
            className={s.modal}
          >
            <div className='title-2xl-semi-bold mb-6 text-text-primary'>{userProfile.is_password_set ? t('common.account.resetPassword') : t('common.account.setPassword')}</div>
            <div className='mb-6 title-2xl-semi-bold text-text-primary'>{userProfile.is_password_set ? t('common.account.resetPassword') : t('common.account.setPassword')}</div>
            {userProfile.is_password_set && (
              <>
                <div className={titleClassName}>{t('common.account.currentPassword')}</div>
@@ -255,7 +242,7 @@
                </div>
              </>
            )}
            <div className='system-sm-semibold mt-8 text-text-secondary'>
            <div className='mt-8 system-sm-semibold text-text-secondary'>
              {userProfile.is_password_set ? t('common.account.newPassword') : t('common.account.password')}
            </div>
            <div className='relative mt-2'>
@@ -274,7 +261,7 @@
                </Button>
              </div>
            </div>
            <div className='system-sm-semibold mt-8 text-text-secondary'>{t('common.account.confirmPassword')}</div>
            <div className='mt-8 system-sm-semibold text-text-secondary'>{t('common.account.confirmPassword')}</div>
            <div className='relative mt-2'>
              <Input
                type={showConfirmPassword ? 'text' : 'password'}
@@ -291,7 +278,7 @@
                </Button>
              </div>
            </div>
            <div className='mt-10 flex justify-end'>
            <div className='flex justify-end mt-10'>
              <Button className='mr-2' onClick={() => {
                setEditPasswordModalVisible(false)
                resetPasswordForm()
app/account/avatar.tsx
@@ -2,16 +2,11 @@
import { useTranslation } from 'react-i18next'
import { Fragment } from 'react'
import { useRouter } from 'next/navigation'
import {
  RiGraduationCapFill,
} from '@remixicon/react'
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
import { Menu, Transition } from '@headlessui/react'
import Avatar from '@/app/components/base/avatar'
import { logout } from '@/service/common'
import { useAppContext } from '@/context/app-context'
import { useProviderContext } from '@/context/provider-context'
import { LogOut01 } from '@/app/components/base/icons/src/vender/line/general'
import PremiumBadge from '@/app/components/base/premium-badge'
export type IAppSelector = {
  isMobile: boolean
@@ -21,7 +16,6 @@
  const router = useRouter()
  const { t } = useTranslation()
  const { userProfile } = useAppContext()
  const { isEducationAccount } = useProviderContext()
  const handleLogout = async () => {
    await logout({
@@ -42,17 +36,17 @@
        ({ open }) => (
          <>
            <div>
              <MenuButton
              <Menu.Button
                className={`
                    p-1x inline-flex
                    items-center rounded-[20px] text-sm
                    inline-flex items-center
                    rounded-[20px] p-1x text-sm
                    text-text-primary
                    mobile:px-1
                    ${open && 'bg-components-panel-bg-blur'}
                  `}
              >
                <Avatar avatar={userProfile.avatar_url} name={userProfile.name} size={32} />
              </MenuButton>
              </Menu.Button>
            </div>
            <Transition
              as={Fragment}
@@ -63,43 +57,35 @@
              leaveFrom="transform opacity-100 scale-100"
              leaveTo="transform opacity-0 scale-95"
            >
              <MenuItems
              <Menu.Items
                className="
                    absolute -right-2 -top-1 w-60 max-w-80
                    origin-top-right divide-y divide-divider-subtle rounded-lg bg-components-panel-bg-blur
                    divide-y divide-divider-subtle origin-top-right rounded-lg bg-components-panel-bg-blur
                    shadow-lg
                  "
              >
                <MenuItem>
                <Menu.Item>
                  <div className='p-1'>
                    <div className='flex flex-nowrap items-center px-3 py-2'>
                      <div className='grow'>
                        <div className='system-md-medium break-all text-text-primary'>
                          {userProfile.name}
                          {isEducationAccount && (
                            <PremiumBadge size='s' color='blue' className='ml-1 !px-2'>
                              <RiGraduationCapFill className='mr-1 h-3 w-3' />
                              <span className='system-2xs-medium'>EDU</span>
                            </PremiumBadge>
                          )}
                        </div>
                        <div className='system-xs-regular break-all text-text-tertiary'>{userProfile.email}</div>
                        <div className='system-md-medium text-text-primary break-all'>{userProfile.name}</div>
                        <div className='system-xs-regular text-text-tertiary break-all'>{userProfile.email}</div>
                      </div>
                      <Avatar avatar={userProfile.avatar_url} name={userProfile.name} size={32} />
                    </div>
                  </div>
                </MenuItem>
                <MenuItem>
                </Menu.Item>
                <Menu.Item>
                  <div className='p-1' onClick={() => handleLogout()}>
                    <div
                      className='group flex h-9 cursor-pointer items-center justify-start rounded-lg px-3 hover:bg-state-base-hover'
                      className='flex items-center justify-start h-9 px-3 rounded-lg cursor-pointer group hover:bg-state-base-hover'
                    >
                      <LogOut01 className='mr-1 flex h-4 w-4 text-text-tertiary' />
                      <div className='text-[14px] font-normal text-text-secondary'>{t('common.userProfile.logout')}</div>
                      <LogOut01 className='w-4 h-4 text-text-tertiary flex mr-1' />
                      <div className='font-normal text-[14px] text-text-secondary'>{t('common.userProfile.logout')}</div>
                    </div>
                  </div>
                </MenuItem>
              </MenuItems>
                </Menu.Item>
              </Menu.Items>
            </Transition>
          </>
        )
app/account/delete-account/components/check-email.tsx
@@ -29,18 +29,18 @@
  }, [getDeleteEmailVerifyCode, props])
  return <>
    <div className='body-md-medium py-1 text-text-destructive'>
    <div className='py-1 text-text-destructive body-md-medium'>
      {t('common.account.deleteTip')}
    </div>
    <div className='body-md-regular pb-2 pt-1 text-text-secondary'>
    <div className='pt-1 pb-2 text-text-secondary body-md-regular'>
      {t('common.account.deletePrivacyLinkTip')}
      <Link href='https://dify.ai/privacy' className='text-text-accent'>{t('common.account.deletePrivacyLink')}</Link>
    </div>
    <label className='system-sm-semibold mb-1 mt-3 flex h-6 items-center text-text-secondary'>{t('common.account.deleteLabel')}</label>
    <label className='mt-3 mb-1 h-6 flex items-center system-sm-semibold text-text-secondary'>{t('common.account.deleteLabel')}</label>
    <Input placeholder={t('common.account.deletePlaceholder') as string} onChange={(e) => {
      setUserInputEmail(e.target.value)
    }} />
    <div className='mt-3 flex w-full flex-col gap-2'>
    <div className='w-full flex flex-col mt-3 gap-2'>
      <Button className='w-full' disabled={userInputEmail !== userProfile.email || isSendingEmail} loading={isSendingEmail} variant='primary' onClick={handleConfirm}>{t('common.account.sendVerificationButton')}</Button>
      <Button className='w-full' onClick={props.onCancel}>{t('common.operation.cancel')}</Button>
    </div>
app/account/delete-account/components/feed-back.tsx
@@ -56,11 +56,11 @@
    className="max-w-[480px]"
    footer={false}
  >
    <label className='system-sm-semibold mb-1 mt-3 flex items-center text-text-secondary'>{t('common.account.feedbackLabel')}</label>
    <label className='mt-3 mb-1 flex items-center system-sm-semibold text-text-secondary'>{t('common.account.feedbackLabel')}</label>
    <Textarea rows={6} value={userFeedback} placeholder={t('common.account.feedbackPlaceholder') as string} onChange={(e) => {
      setUserFeedback(e.target.value)
    }} />
    <div className='mt-3 flex w-full flex-col gap-2'>
    <div className='w-full flex flex-col mt-3 gap-2'>
      <Button className='w-full' loading={isPending} variant='primary' onClick={handleSubmit}>{t('common.operation.submit')}</Button>
      <Button className='w-full' onClick={handleSkip}>{t('common.operation.skip')}</Button>
    </div>
app/account/delete-account/components/verify-email.tsx
@@ -35,18 +35,18 @@
    catch (error) { console.error(error) }
  }, [emailToken, verificationCode, confirmDeleteAccount, props])
  return <>
    <div className='body-md-medium pt-1 text-text-destructive'>
    <div className='pt-1 text-text-destructive body-md-medium'>
      {t('common.account.deleteTip')}
    </div>
    <div className='body-md-regular pb-2 pt-1 text-text-secondary'>
    <div className='pt-1 pb-2 text-text-secondary body-md-regular'>
      {t('common.account.deletePrivacyLinkTip')}
      <Link href='https://dify.ai/privacy' className='text-text-accent'>{t('common.account.deletePrivacyLink')}</Link>
    </div>
    <label className='system-sm-semibold mb-1 mt-3 flex h-6 items-center text-text-secondary'>{t('common.account.verificationLabel')}</label>
    <label className='mt-3 mb-1 h-6 flex items-center system-sm-semibold text-text-secondary'>{t('common.account.verificationLabel')}</label>
    <Input minLength={6} maxLength={6} placeholder={t('common.account.verificationPlaceholder') as string} onChange={(e) => {
      setVerificationCode(e.target.value)
    }} />
    <div className='mt-3 flex w-full flex-col gap-2'>
    <div className='w-full flex flex-col mt-3 gap-2'>
      <Button className='w-full' disabled={shouldButtonDisabled} loading={isDeleting} variant='warning' onClick={handleConfirm}>{t('common.account.permanentlyDeleteButton')}</Button>
      <Button className='w-full' onClick={props.onCancel}>{t('common.operation.cancel')}</Button>
      <Countdown onResend={sendEmail} />
app/account/header.tsx
@@ -4,33 +4,31 @@
import { useRouter } from 'next/navigation'
import Button from '../components/base/button'
import Avatar from './avatar'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import { useCallback } from 'react'
import LogoSite from '@/app/components/base/logo/logo-site'
const Header = () => {
  const { t } = useTranslation()
  const router = useRouter()
  const back = useCallback(() => {
  const back = () => {
    router.back()
  }, [router])
  }
  return (
    <div className='flex flex-1 items-center justify-between px-4'>
      <div className='flex items-center gap-3'>
        <div className='flex cursor-pointer items-center' onClick={back}>
          <DifyLogo />
        <div className='flex items-center cursor-pointer' onClick={back}>
          <LogoSite className='object-contain' />
        </div>
        <div className='h-4 w-[1px] origin-center rotate-[11.31deg] bg-divider-regular' />
        <p className='title-3xl-semi-bold relative mt-[-2px] text-text-primary'>{t('common.account.account')}</p>
        <div className='w-[1px] h-4 bg-divider-regular' />
        <p className='text-text-primary title-3xl-semi-bold'>{t('common.account.account')}</p>
      </div>
      <div className='flex shrink-0 items-center gap-3'>
        <Button className='system-sm-medium gap-2 px-3 py-2' onClick={back}>
          <RiRobot2Line className='h-4 w-4' />
      <div className='flex items-center flex-shrink-0 gap-3'>
        <Button className='gap-2 py-2 px-3 system-sm-medium' onClick={back}>
          <RiRobot2Line className='w-4 h-4' />
          <p>{t('common.account.studio')}</p>
          <RiArrowRightUpLine className='h-4 w-4' />
          <RiArrowRightUpLine className='w-4 h-4' />
        </Button>
        <div className='h-4 w-[1px] bg-divider-regular' />
        <div className='w-[1px] h-4 bg-divider-regular' />
        <Avatar />
      </div>
    </div>
app/account/layout.tsx
@@ -21,7 +21,7 @@
                <HeaderWrapper>
                  <Header />
                </HeaderWrapper>
                <div className='relative flex h-0 shrink-0 grow flex-col overflow-y-auto bg-components-panel-bg'>
                <div className='relative flex flex-col overflow-y-auto bg-components-panel-bg shrink-0 h-0 grow'>
                  {children}
                </div>
              </ModalContextProvider>
app/account/page.tsx
@@ -1,7 +1,7 @@
import AccountPage from './account-page'
export default function Account() {
  return <div className='mx-auto w-full max-w-[640px] px-6 pt-12'>
  return <div className='max-w-[640px] w-full mx-auto pt-12 px-6'>
    <AccountPage />
  </div>
}
app/activate/activateForm.tsx
@@ -41,7 +41,7 @@
  return (
    <div className={
      cn(
        'flex w-full grow flex-col items-center justify-center',
        'flex flex-col items-center w-full grow justify-center',
        'px-6',
        'md:px-[108px]',
      )
@@ -49,11 +49,11 @@
      {!checkRes && <Loading />}
      {checkRes && !checkRes.is_valid && (
        <div className="flex flex-col md:w-[400px]">
          <div className="mx-auto w-full">
            <div className="mb-3 flex h-20 w-20 items-center justify-center rounded-[20px] border border-divider-regular bg-components-option-card-option-bg p-5 text-[40px] font-bold shadow-lg">🤷‍♂️</div>
            <h2 className="text-[32px] font-bold text-text-primary">{t('login.invalid')}</h2>
          <div className="w-full mx-auto">
            <div className="mb-3 flex justify-center items-center w-20 h-20 p-5 rounded-[20px] border border-gray-100 shadow-lg text-[40px] font-bold">🤷‍♂️</div>
            <h2 className="text-[32px] font-bold text-gray-900">{t('login.invalid')}</h2>
          </div>
          <div className="mx-auto mt-6 w-full">
          <div className="w-full mx-auto mt-6">
            <Button variant='primary' className='w-full !text-sm'>
              <a href="https://dify.ai">{t('login.explore')}</a>
            </Button>
app/activate/page.tsx
@@ -1,15 +1,27 @@
import React from 'react'
import Header from '../signin/_header'
import style from '../signin/page.module.css'
import ActivateForm from './activateForm'
import cn from '@/utils/classnames'
const Activate = () => {
  return (
    <div className={cn('flex min-h-screen w-full justify-center bg-background-default-burn p-6')}>
      <div className={cn('flex w-full shrink-0 flex-col rounded-2xl border border-effects-highlight bg-background-default-subtle')}>
    <div className={cn(
      style.background,
      'flex w-full min-h-screen',
      'sm:p-4 lg:p-8',
      'gap-x-20',
      'justify-center lg:justify-start',
    )}>
      <div className={
        cn(
          'flex w-full flex-col bg-white shadow rounded-2xl shrink-0',
          'space-between',
        )
      }>
        <Header />
        <ActivateForm />
        <div className='px-8 py-6 text-sm font-normal text-text-tertiary'>
        <div className='px-8 py-6 text-sm font-normal text-gray-500'>
          © {new Date().getFullYear()} LangGenius, Inc. All rights reserved.
        </div>
      </div>
app/activate/style.module.css
New file
@@ -0,0 +1,4 @@
.logo {
  background: #fff center no-repeat url(./team-28x28.png);
  background-size: 56px;
}
app/activate/team-28x28.png
app/components/app-sidebar/app-info.tsx
@@ -1,20 +1,18 @@
import { useTranslation } from 'react-i18next'
import { useRouter } from 'next/navigation'
import { useContext, useContextSelector } from 'use-context-selector'
import { RiArrowDownSLine } from '@remixicon/react'
import React, { useCallback, useState } from 'react'
import {
  RiDeleteBinLine,
  RiEditLine,
  RiEqualizer2Line,
  RiExchange2Line,
  RiFileCopy2Line,
  RiFileDownloadLine,
  RiFileUploadLine,
  RiMoreLine,
} from '@remixicon/react'
import AppIcon from '../base/app-icon'
import SwitchAppModal from '../app/switch-app-modal'
import s from './style.module.css'
import cn from '@/utils/classnames'
import {
  PortalToFollowElem,
  PortalToFollowElemContent,
  PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import Divider from '@/app/components/base/divider'
import Confirm from '@/app/components/base/confirm'
import { useStore as useAppStore } from '@/app/components/app/store'
import { ToastContext } from '@/app/components/base/toast'
@@ -24,6 +22,8 @@
import DuplicateAppModal from '@/app/components/app/duplicate-modal'
import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal'
import CreateAppModal from '@/app/components/explore/create-app-modal'
import { AiText, ChatBot, CuteRobot } from '@/app/components/base/icons/src/vender/solid/communication'
import { Route } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import { getRedirection } from '@/utils/app-redirection'
@@ -31,10 +31,6 @@
import type { EnvironmentVariable } from '@/app/components/workflow/types'
import DSLExportConfirmModal from '@/app/components/workflow/dsl-export-confirm-modal'
import { fetchWorkflowDraft } from '@/service/workflow'
import ContentDialog from '@/app/components/base/content-dialog'
import Button from '@/app/components/base/button'
import CardView from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/cardView'
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '../base/portal-to-follow-elem'
export type IAppInfoProps = {
  expand: boolean
@@ -51,6 +47,7 @@
  const [showEditModal, setShowEditModal] = useState(false)
  const [showDuplicateModal, setShowDuplicateModal] = useState(false)
  const [showConfirmDelete, setShowConfirmDelete] = useState(false)
  const [showSwitchTip, setShowSwitchTip] = useState<string>('')
  const [showSwitchModal, setShowSwitchModal] = useState<boolean>(false)
  const [showImportDSLModal, setShowImportDSLModal] = useState<boolean>(false)
  const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([])
@@ -88,7 +85,7 @@
      setAppDetail(app)
      mutateApps()
    }
    catch {
    catch (e) {
      notify({ type: 'error', message: t('app.editFailed') })
    }
  }, [appDetail, mutateApps, notify, setAppDetail, t])
@@ -115,7 +112,7 @@
      onPlanInfoChanged()
      getRedirection(true, newApp, replace)
    }
    catch {
    catch (e) {
      notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
    }
  }
@@ -134,7 +131,7 @@
      a.download = `${appDetail.name}.yml`
      a.click()
    }
    catch {
    catch (e) {
      notify({ type: 'error', message: t('app.exportFailed') })
    }
  }
@@ -155,7 +152,7 @@
      }
      setSecretEnvList(list)
    }
    catch {
    catch (e) {
      notify({ type: 'error', message: t('app.exportFailed') })
    }
  }
@@ -178,29 +175,30 @@
      })
    }
    setShowConfirmDelete(false)
  }, [appDetail, mutateApps, notify, onPlanInfoChanged, replace, setAppDetail, t])
  }, [appDetail, mutateApps, notify, onPlanInfoChanged, replace, t])
  const { isCurrentWorkspaceEditor } = useAppContext()
  const [showMore, setShowMore] = useState(false)
  const handleTriggerMore = useCallback(() => {
    setShowMore(true)
  }, [setShowMore])
  if (!appDetail)
    return null
  return (
    <div>
      <button
    <PortalToFollowElem
      open={open}
      onOpenChange={setOpen}
      placement='bottom-start'
      offset={4}
    >
      <div className='relative'>
        <PortalToFollowElemTrigger
        onClick={() => {
          if (isCurrentWorkspaceEditor)
            setOpen(v => !v)
        }}
        className='block w-full'
          className='block'
      >
        <div className={cn('flex rounded-lg', expand ? 'flex-col gap-2 p-2 pb-2.5' : 'items-start justify-center gap-1 p-1', open && 'bg-state-base-hover', isCurrentWorkspaceEditor && 'cursor-pointer hover:bg-state-base-hover')}>
          <div className={`flex items-center self-stretch ${expand ? 'justify-between' : 'flex-col gap-1'}`}>
          <div className={cn('flex p-1 rounded-lg', open && 'bg-gray-100', isCurrentWorkspaceEditor && 'hover:bg-gray-100 cursor-pointer')}>
            <div className='relative shrink-0 mr-2'>
            <AppIcon
              size={expand ? 'large' : 'small'}
              iconType={appDetail.icon_type}
@@ -208,31 +206,68 @@
              background={appDetail.icon_background}
              imageUrl={appDetail.icon_url}
            />
            <div className='flex items-center justify-center rounded-md p-0.5'>
              <div className='flex h-5 w-5 items-center justify-center'>
                <RiEqualizer2Line className='h-4 w-4 text-text-tertiary' />
              <span className={cn(
                'absolute bottom-[-3px] right-[-3px] w-4 h-4 p-0.5 bg-white rounded border-[0.5px] border-[rgba(0,0,0,0.02)] shadow-sm',
                !expand && '!w-3.5 !h-3.5 !bottom-[-2px] !right-[-2px]',
              )}>
                {appDetail.mode === 'advanced-chat' && (
                  <ChatBot className={cn('w-3 h-3 text-[#1570EF]', !expand && '!w-2.5 !h-2.5')} />
                )}
                {appDetail.mode === 'agent-chat' && (
                  <CuteRobot className={cn('w-3 h-3 text-indigo-600', !expand && '!w-2.5 !h-2.5')} />
                )}
                {appDetail.mode === 'chat' && (
                  <ChatBot className={cn('w-3 h-3 text-[#1570EF]', !expand && '!w-2.5 !h-2.5')} />
                )}
                {appDetail.mode === 'completion' && (
                  <AiText className={cn('w-3 h-3 text-[#0E9384]', !expand && '!w-2.5 !h-2.5')} />
                )}
                {appDetail.mode === 'workflow' && (
                  <Route className={cn('w-3 h-3 text-[#f79009]', !expand && '!w-2.5 !h-2.5')} />
                )}
              </span>
            </div>
            {expand && (
              <div className="grow w-0">
                <div className='flex justify-between items-center text-sm leading-5 font-medium text-text-secondary'>
                  <div className='truncate' title={appDetail.name}>{appDetail.name}</div>
                  {isCurrentWorkspaceEditor && <RiArrowDownSLine className='shrink-0 ml-[2px] w-3 h-3 text-gray-500' />}
                </div>
                <div className='flex items-center text-[10px] leading-[18px] font-medium text-gray-500 gap-1'>
                  {appDetail.mode === 'advanced-chat' && (
                    <>
                      <div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
                      <div title={t('app.types.advanced') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.advanced').toUpperCase()}</div>
                    </>
                  )}
                  {appDetail.mode === 'agent-chat' && (
                    <div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.agent').toUpperCase()}</div>
                  )}
                  {appDetail.mode === 'chat' && (
                    <>
                      <div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
                      <div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
                    </>
                  )}
                  {appDetail.mode === 'completion' && (
                    <>
                      <div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.completion').toUpperCase()}</div>
                      <div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
                    </>
                  )}
                  {appDetail.mode === 'workflow' && (
                    <div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.workflow').toUpperCase()}</div>
                  )}
              </div>
            </div>
            )}
          </div>
          {
            expand && (
              <div className='flex flex-col items-start gap-1'>
                <div className='flex w-full'>
                  <div className='system-md-semibold truncate text-text-secondary'>{appDetail.name}</div>
                </div>
                <div className='system-2xs-medium-uppercase text-text-tertiary'>{appDetail.mode === 'advanced-chat' ? t('app.types.advanced') : appDetail.mode === 'agent-chat' ? t('app.types.agent') : appDetail.mode === 'chat' ? t('app.types.chatbot') : appDetail.mode === 'completion' ? t('app.types.completion') : t('app.types.workflow')}</div>
              </div>
            )
          }
        </div>
      </button>
      <ContentDialog
        show={open}
        onClose={() => setOpen(false)}
        className='absolute bottom-2 left-2 top-2 flex w-[420px] flex-col rounded-2xl !p-0'
      >
        <div className='flex shrink-0 flex-col items-start justify-center gap-3 self-stretch p-4'>
          <div className='flex items-center gap-3 self-stretch'>
        </PortalToFollowElemTrigger>
        <PortalToFollowElemContent className='z-[1002]'>
          <div className='relative w-[320px] bg-white rounded-2xl shadow-xl'>
            {/* header */}
            <div className={cn('flex pl-4 pt-3 pr-3', !appDetail.description && 'pb-2')}>
              <div className='relative shrink-0 mr-2'>
            <AppIcon
              size="large"
              iconType={appDetail.icon_type}
@@ -240,118 +275,138 @@
              background={appDetail.icon_background}
              imageUrl={appDetail.icon_url}
            />
            <div className='flex w-full grow flex-col items-start justify-center'>
              <div className='system-md-semibold w-full truncate text-text-secondary'>{appDetail.name}</div>
              <div className='system-2xs-medium-uppercase text-text-tertiary'>{appDetail.mode === 'advanced-chat' ? t('app.types.advanced') : appDetail.mode === 'agent-chat' ? t('app.types.agent') : appDetail.mode === 'chat' ? t('app.types.chatbot') : appDetail.mode === 'completion' ? t('app.types.completion') : t('app.types.workflow')}</div>
                <span className='absolute bottom-[-3px] right-[-3px] w-4 h-4 p-0.5 bg-white rounded border-[0.5px] border-[rgba(0,0,0,0.02)] shadow-sm'>
                  {appDetail.mode === 'advanced-chat' && (
                    <ChatBot className='w-3 h-3 text-[#1570EF]' />
                  )}
                  {appDetail.mode === 'agent-chat' && (
                    <CuteRobot className='w-3 h-3 text-indigo-600' />
                  )}
                  {appDetail.mode === 'chat' && (
                    <ChatBot className='w-3 h-3 text-[#1570EF]' />
                  )}
                  {appDetail.mode === 'completion' && (
                    <AiText className='w-3 h-3 text-[#0E9384]' />
                  )}
                  {appDetail.mode === 'workflow' && (
                    <Route className='w-3 h-3 text-[#f79009]' />
                  )}
                </span>
              </div>
              <div className='grow w-0'>
                <div title={appDetail.name} className='flex justify-between items-center text-sm leading-5 font-medium text-gray-900 truncate'>{appDetail.name}</div>
                <div className='flex items-center text-[10px] leading-[18px] font-medium text-gray-500 gap-1'>
                  {appDetail.mode === 'advanced-chat' && (
                    <>
                      <div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
                      <div title={t('app.types.advanced') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.advanced').toUpperCase()}</div>
                    </>
                  )}
                  {appDetail.mode === 'agent-chat' && (
                    <div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.agent').toUpperCase()}</div>
                  )}
                  {appDetail.mode === 'chat' && (
                    <>
                      <div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
                      <div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
                    </>
                  )}
                  {appDetail.mode === 'completion' && (
                    <>
                      <div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.completion').toUpperCase()}</div>
                      <div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
                    </>
                  )}
                  {appDetail.mode === 'workflow' && (
                    <div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.workflow').toUpperCase()}</div>
                  )}
                </div>
            </div>
          </div>
          {/* description */}
          {appDetail.description && (
            <div className='system-xs-regular text-text-tertiary'>{appDetail.description}</div>
              <div className='px-4 py-2 text-gray-500 text-xs leading-[18px]'>{appDetail.description}</div>
          )}
          {/* operations */}
          <div className='flex flex-wrap items-center gap-1 self-stretch'>
            <Button
              size={'small'}
              variant={'secondary'}
              className='gap-[1px]'
              onClick={() => {
            <Divider className="!my-1" />
            <div className="w-full py-1">
              <div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={() => {
                setOpen(false)
                setShowEditModal(true)
              }}
            >
              <RiEditLine className='h-3.5 w-3.5 text-components-button-secondary-text' />
              <span className='system-xs-medium text-components-button-secondary-text'>{t('app.editApp')}</span>
            </Button>
            <Button
              size={'small'}
              variant={'secondary'}
              className='gap-[1px]'
              onClick={() => {
              }}>
                <span className='text-gray-700 text-sm leading-5'>{t('app.editApp')}</span>
              </div>
              <div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={() => {
                setOpen(false)
                setShowDuplicateModal(true)
              }}>
                <span className='text-gray-700 text-sm leading-5'>{t('app.duplicate')}</span>
              </div>
              {(appDetail.mode === 'completion' || appDetail.mode === 'chat') && (
                <>
                  <Divider className="!my-1" />
                  <div
                    className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer'
                    onMouseEnter={() => setShowSwitchTip(appDetail.mode)}
                    onMouseLeave={() => setShowSwitchTip('')}
                    onClick={() => {
                      setOpen(false)
                      setShowSwitchModal(true)
              }}
            >
              <RiFileCopy2Line className='h-3.5 w-3.5 text-components-button-secondary-text' />
              <span className='system-xs-medium text-components-button-secondary-text'>{t('app.duplicate')}</span>
            </Button>
            <Button
              size={'small'}
              variant={'secondary'}
              className='gap-[1px]'
              onClick={exportCheck}
            >
              <RiFileDownloadLine className='h-3.5 w-3.5 text-components-button-secondary-text' />
              <span className='system-xs-medium text-components-button-secondary-text'>{t('app.export')}</span>
            </Button>
            {appDetail.mode !== 'agent-chat' && <PortalToFollowElem
              open={showMore}
              onOpenChange={setShowMore}
              placement='bottom-end'
              offset={{
                mainAxis: 4,
              }}>
              <PortalToFollowElemTrigger onClick={handleTriggerMore}>
                <Button
                  size={'small'}
                  variant={'secondary'}
                  className='gap-[1px]'
                >
                  <RiMoreLine className='h-3.5 w-3.5 text-components-button-secondary-text' />
                  <span className='system-xs-medium text-components-button-secondary-text'>{t('common.operation.more')}</span>
                </Button>
              </PortalToFollowElemTrigger>
              <PortalToFollowElemContent className='z-[21]'>
                <div className='flex w-[264px] flex-col rounded-[12px] border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg backdrop-blur-[5px]'>
                    <span className='text-gray-700 text-sm leading-5'>{t('app.switch')}</span>
                  </div>
                </>
              )}
              <Divider className="!my-1" />
              <div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={exportCheck}>
                <span className='text-gray-700 text-sm leading-5'>{t('app.export')}</span>
              </div>
                  {
                    (appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow')
                    && <div className='flex h-8 cursor-pointer items-center gap-x-1 rounded-lg p-1.5 hover:bg-state-base-hover'
                (appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow') && (
                  <div
                    className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer'
                      onClick={() => {
                        setOpen(false)
                        setShowImportDSLModal(true)
                      }}>
                      <RiFileUploadLine className='h-4 w-4 text-text-tertiary' />
                      <span className='system-md-regular text-text-secondary'>{t('workflow.common.importDSL')}</span>
                    <span className='text-gray-700 text-sm leading-5'>{t('workflow.common.importDSL')}</span>
                    </div>
                )
                  }
                  {
                    (appDetail.mode === 'completion' || appDetail.mode === 'chat')
                    && <div className='flex h-8 cursor-pointer items-center gap-x-1 rounded-lg p-1.5 hover:bg-state-base-hover'
                      onClick={() => {
                        setOpen(false)
                        setShowSwitchModal(true)
                      }}>
                      <RiExchange2Line className='h-4 w-4 text-text-tertiary' />
                      <span className='system-md-regular text-text-secondary'>{t('app.switch')}</span>
                    </div>
                  }
                </div>
              </PortalToFollowElemContent>
            </PortalToFollowElem>}
          </div>
        </div>
        <div className='flex flex-1'>
          <CardView
            appId={appDetail.id}
            isInPanel={true}
            className='flex grow flex-col gap-2 overflow-auto px-2 py-1'
          />
        </div>
        <div className='flex min-h-fit shrink-0 flex-col items-start justify-center gap-3 self-stretch border-t-[0.5px] border-divider-subtle p-2'>
          <Button
            size={'medium'}
            variant={'ghost'}
            className='gap-0.5'
            onClick={() => {
              <Divider className="!my-1" />
              <div className='group h-9 py-2 px-3 mx-1 flex items-center hover:bg-red-50 rounded-lg cursor-pointer' onClick={() => {
              setOpen(false)
              setShowConfirmDelete(true)
            }}
          >
            <RiDeleteBinLine className='h-4 w-4 text-text-tertiary' />
            <span className='system-sm-medium text-text-tertiary'>{t('common.operation.deleteApp')}</span>
          </Button>
              }}>
                <span className='text-gray-700 text-sm leading-5 group-hover:text-red-500'>
                  {t('common.operation.delete')}
                </span>
        </div>
      </ContentDialog>
            </div>
            {/* switch tip */}
            <div
              className={cn(
                'hidden absolute left-[324px] top-0 w-[376px] rounded-xl bg-white border-[0.5px] border-[rgba(0,0,0,0.05)] shadow-lg',
                showSwitchTip && '!block',
              )}
            >
              <div className={cn(
                'w-full h-[256px] bg-center bg-no-repeat bg-contain rounded-xl',
                showSwitchTip === 'chat' && s.expertPic,
                showSwitchTip === 'completion' && s.completionPic,
              )} />
              <div className='px-4 pb-2'>
                <div className='flex items-center gap-1 text-gray-700 text-md leading-6 font-semibold'>
                  {showSwitchTip === 'chat' ? t('app.types.advanced') : t('app.types.workflow')}
                  <span className='px-1 rounded-[5px] bg-white border border-black/8 text-gray-500 text-[10px] leading-[18px] font-medium'>BETA</span>
                </div>
                <div className='text-orange-500 text-xs leading-[18px] font-medium'>{t('app.newApp.advancedFor').toLocaleUpperCase()}</div>
                <div className='mt-1 text-gray-500 text-sm leading-5'>{t('app.newApp.advancedDescription')}</div>
              </div>
            </div>
          </div>
        </PortalToFollowElemContent>
      {showSwitchModal && (
        <SwitchAppModal
          inAppDetail
@@ -412,6 +467,7 @@
        />
      )}
    </div>
    </PortalToFollowElem>
  )
}
app/components/app-sidebar/basic.tsx
@@ -48,33 +48,31 @@
const ICON_MAP = {
  app: <AppIcon className='border !border-[rgba(0,0,0,0.05)]' />,
  api: <AppIcon innerIcon={ApiSvg} className='border !border-purple-200 !bg-purple-50' />,
  api: <AppIcon innerIcon={ApiSvg} className='border !bg-purple-50 !border-purple-200' />,
  dataset: <AppIcon innerIcon={DatasetSvg} className='!border-[0.5px] !border-indigo-100 !bg-indigo-25' />,
  webapp: <AppIcon innerIcon={WebappSvg} className='border !border-primary-200 !bg-primary-100' />,
  webapp: <AppIcon innerIcon={WebappSvg} className='border !bg-primary-100 !border-primary-200' />,
  notion: <AppIcon innerIcon={NotionSvg} className='!border-[0.5px] !border-indigo-100 !bg-white' />,
}
export default function AppBasic({ icon, icon_background, name, isExternal, type, hoverTip, textStyle, isExtraInLine, mode = 'expand', iconType = 'app' }: IAppBasicProps) {
export default function AppBasic({ icon, icon_background, name, isExternal, type, hoverTip, textStyle, mode = 'expand', iconType = 'app' }: IAppBasicProps) {
  const { t } = useTranslation()
  return (
    <div className="flex grow items-center">
    <div className="flex items-start p-1">
      {icon && icon_background && iconType === 'app' && (
        <div className='mr-3 shrink-0'>
        <div className='flex-shrink-0 mr-3'>
          <AppIcon icon={icon} background={icon_background} />
        </div>
      )}
      {iconType !== 'app'
        && <div className='mr-3 shrink-0'>
        && <div className='flex-shrink-0 mr-3'>
          {ICON_MAP[iconType]}
        </div>
      }
      {mode === 'expand' && <div className="group w-full">
        <div className={`system-md-semibold flex flex-row items-center text-text-secondary group-hover:text-text-primary ${textStyle?.main ?? ''}`}>
          <div className="min-w-0 overflow-hidden text-ellipsis break-normal">
      {mode === 'expand' && <div className="group">
        <div className={`flex flex-row items-center text-sm font-semibold text-gray-700 group-hover:text-gray-900 break-all ${textStyle?.main ?? ''}`}>
            {name}
          </div>
          {hoverTip
            && <Tooltip
              popupContent={
@@ -88,11 +86,8 @@
            />
          }
        </div>
        {isExtraInLine ? (
          <div className="system-2xs-medium-uppercase flex text-text-tertiary">{type}</div>
        ) : (
          <div className='system-2xs-medium-uppercase text-text-tertiary'>{isExternal ? t('dataset.externalTag') : type}</div>
        )}
        <div className={`text-xs font-normal text-gray-500 group-hover:text-gray-700 break-all ${textStyle?.extra ?? ''}`}>{type}</div>
        <div className='text-text-tertiary system-2xs-medium-uppercase'>{isExternal ? t('dataset.externalTag') : ''}</div>
      </div>}
    </div>
  )
app/components/app-sidebar/dataset-info.tsx
@@ -26,7 +26,7 @@
  const { t } = useTranslation()
  return (
    <div className='pl-1 pt-1'>
      <div className='mr-3 shrink-0'>
      <div className='flex-shrink-0 mr-3'>
        <AppIcon innerIcon={DatasetSvg} className='!border-[0.5px] !border-indigo-100 !bg-indigo-25' />
      </div>
      {expand && (
@@ -34,8 +34,8 @@
          <div className='system-md-semibold text-text-secondary'>
            {name}
          </div>
          <div className='system-2xs-medium-uppercase mt-1 text-text-tertiary'>{isExternal ? t('dataset.externalTag') : t('dataset.localDocs')}</div>
          <div className='system-xs-regular  my-3 text-text-tertiary first-letter:capitalize'>{description}</div>
          <div className='mt-1 text-text-tertiary system-2xs-medium-uppercase'>{isExternal ? t('dataset.externalTag') : t('dataset.localDocs')}</div>
          <div className='my-3  system-xs-regular text-text-tertiary first-letter:capitalize'>{description}</div>
        </div>
      )}
      {extraInfo}
app/components/app-sidebar/index.tsx
@@ -1,6 +1,7 @@
import React, { useEffect } from 'react'
import { useShallow } from 'zustand/react/shallow'
import { RiLayoutLeft2Line, RiLayoutRight2Line } from '@remixicon/react'
import { RiLayoutRight2Line } from '@remixicon/react'
import { LayoutRight2LineMod } from '../base/icons/src/public/knowledge'
import NavLink from './navLink'
import type { NavIcon } from './navLink'
import AppBasic from './basic'
@@ -49,14 +50,14 @@
  return (
    <div
      className={`
        flex shrink-0 flex-col border-r border-divider-burn bg-background-default-subtle transition-all
        shrink-0 flex flex-col bg-background-default-subtle border-r border-divider-burn transition-all
        ${expand ? 'w-[216px]' : 'w-14'}
      `}
    >
      <div
        className={`
          shrink-0
          ${expand ? 'p-2' : 'p-1'}
          ${expand ? 'p-3' : 'p-2'}
        `}
      >
        {iconType === 'app' && (
@@ -84,7 +85,7 @@
        )}
      </div>
      <div className='px-4'>
        <div className={cn('mx-auto mt-1 h-[1px] bg-divider-subtle', !expand && 'w-6')} />
        <div className={cn('mt-1 mx-auto h-[1px] bg-divider-subtle', !expand && 'w-6')} />
      </div>
      <nav
        className={`
@@ -107,13 +108,13 @@
            `}
          >
            <div
              className='flex h-6 w-6 cursor-pointer items-center justify-center'
              className='flex items-center justify-center w-6 h-6 text-gray-500 cursor-pointer'
              onClick={() => handleToggle(appSidebarExpand)}
            >
              {
                expand
                  ? <RiLayoutRight2Line className='h-5 w-5 text-components-menu-item-text' />
                  : <RiLayoutLeft2Line className='h-5 w-5 text-components-menu-item-text' />
                  ? <RiLayoutRight2Line className='w-5 h-5 text-components-menu-item-text' />
                  : <LayoutRight2LineMod className='w-5 h-5 text-components-menu-item-text' />
              }
            </div>
          </div>
app/components/app-sidebar/navLink.tsx
@@ -3,13 +3,13 @@
import { useSelectedLayoutSegment } from 'next/navigation'
import Link from 'next/link'
import classNames from '@/utils/classnames'
import type { RemixiconComponentType } from '@remixicon/react'
export type NavIcon = React.ComponentType<
React.PropsWithoutRef<React.ComponentProps<'svg'>> & {
  title?: string | undefined
  titleId?: string | undefined
}> | RemixiconComponentType
}
>
export type NavLinkProps = {
  name: string
@@ -44,7 +44,7 @@
      key={name}
      href={href}
      className={classNames(
        isActive ? 'bg-state-accent-active text-text-accent font-semibold' : 'text-components-menu-item-text hover:bg-state-base-hover hover:text-components-menu-item-text-hover',
        isActive ? 'bg-state-accent-active text-text-accent font-semibold' : 'text-components-menu-item-text hover:bg-gray-100 hover:text-components-menu-item-text-hover',
        'group flex items-center h-9 rounded-md py-2 text-sm font-normal',
        mode === 'expand' ? 'px-3' : 'px-2.5',
      )}
app/components/app-sidebar/style.module.css
app/components/app/annotation/add-annotation-modal/edit-item/index.tsx
@@ -21,17 +21,17 @@
  onChange,
}) => {
  const { t } = useTranslation()
  const avatar = type === EditItemType.Query ? <User className='h-6 w-6' /> : <Robot className='h-6 w-6' />
  const avatar = type === EditItemType.Query ? <User className='w-6 h-6' /> : <Robot className='w-6 h-6' />
  const name = type === EditItemType.Query ? t('appAnnotation.addModal.queryName') : t('appAnnotation.addModal.answerName')
  const placeholder = type === EditItemType.Query ? t('appAnnotation.addModal.queryPlaceholder') : t('appAnnotation.addModal.answerPlaceholder')
  return (
    <div className='flex' onClick={e => e.stopPropagation()}>
      <div className='mr-3 shrink-0'>
      <div className='shrink-0 mr-3'>
        {avatar}
      </div>
      <div className='grow'>
        <div className='system-xs-semibold mb-1 text-text-primary'>{name}</div>
        <div className='mb-1 system-xs-semibold text-text-primary'>{name}</div>
        <Textarea
          value={content}
          onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => onChange(e.target.value)}
app/components/app/annotation/add-annotation-modal/index.tsx
@@ -56,7 +56,7 @@
    try {
      await onAdd(payload)
    }
    catch {
    catch (e) {
    }
    setIsSaving(false)
@@ -76,7 +76,7 @@
        maxWidthClassName='!max-w-[480px]'
        title={t('appAnnotation.addModal.title') as string}
        body={(
          <div className='space-y-6 p-6 pb-4'>
          <div className='p-6 pb-4 space-y-6'>
            <EditItem
              type={EditItemType.Query}
              content={question}
@@ -93,11 +93,11 @@
          (
            <div>
              {isAnnotationFull && (
                <div className='mb-4 mt-6 px-6'>
                <div className='mt-6 mb-4 px-6'>
                  <AnnotationFull />
                </div>
              )}
              <div className='system-sm-medium flex h-16 items-center justify-between rounded-bl-xl rounded-br-xl border-t border-divider-subtle bg-background-section-burn px-4 text-text-tertiary'>
              <div className='px-4 flex h-16 items-center justify-between border-t border-divider-subtle bg-background-section-burn rounded-bl-xl rounded-br-xl system-sm-medium text-text-tertiary'>
                <div
                  className='flex items-center space-x-2'
                >
app/components/app/annotation/batch-add-annotation-modal/csv-downloader.tsx
@@ -35,17 +35,17 @@
    <div className='mt-6'>
      <div className='system-sm-medium text-text-primary'>{t('share.generation.csvStructureTitle')}</div>
      <div className='mt-2 max-h-[500px] overflow-auto'>
        <table className='w-full table-fixed border-separate border-spacing-0 rounded-lg border border-divider-regular text-xs'>
        <table className='table-fixed w-full border-separate border-spacing-0 border border-divider-regular rounded-lg text-xs'>
          <thead className='text-text-tertiary'>
            <tr>
              <td className='h-9 border-b border-divider-regular pl-3 pr-2'>{t('appAnnotation.batchModal.question')}</td>
              <td className='h-9 border-b border-divider-regular pl-3 pr-2'>{t('appAnnotation.batchModal.answer')}</td>
              <td className='h-9 pl-3 pr-2 border-b border-divider-regular'>{t('appAnnotation.batchModal.question')}</td>
              <td className='h-9 pl-3 pr-2 border-b border-divider-regular'>{t('appAnnotation.batchModal.answer')}</td>
            </tr>
          </thead>
          <tbody className='text-text-secondary'>
          <tbody className='text-gray-700'>
            <tr>
              <td className='h-9 border-b border-divider-subtle pl-3 pr-2 text-[13px]'>{t('appAnnotation.batchModal.question')} 1</td>
              <td className='h-9 border-b border-divider-subtle pl-3 pr-2 text-[13px]'>{t('appAnnotation.batchModal.answer')} 1</td>
              <td className='h-9 pl-3 pr-2 border-b border-divider-subtle text-[13px]'>{t('appAnnotation.batchModal.question')} 1</td>
              <td className='h-9 pl-3 pr-2 border-b border-divider-subtle text-[13px]'>{t('appAnnotation.batchModal.answer')} 1</td>
            </tr>
            <tr>
              <td className='h-9 pl-3 pr-2 text-[13px]'>{t('appAnnotation.batchModal.question')} 2</td>
@@ -55,14 +55,14 @@
        </table>
      </div>
      <CSVDownloader
        className="mt-2 block cursor-pointer"
        className="block mt-2 cursor-pointer"
        type={Type.Link}
        filename={`template-${locale}`}
        bom={true}
        data={getTemplate()}
      >
        <div className='system-xs-medium flex h-[18px] items-center space-x-1 text-text-accent'>
          <DownloadIcon className='mr-1 h-3 w-3' />
        <div className='flex items-center h-[18px] space-x-1 text-text-accent system-xs-medium'>
          <DownloadIcon className='w-3 h-3 mr-1' />
          {t('appAnnotation.batchModal.template')}
        </div>
      </CSVDownloader>
app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx
@@ -91,29 +91,29 @@
      />
      <div ref={dropRef}>
        {!file && (
          <div className={cn('system-sm-regular flex h-20 items-center rounded-xl border border-dashed border-components-dropzone-border bg-components-dropzone-bg', dragging && 'border border-components-dropzone-border-accent bg-components-dropzone-bg-accent')}>
            <div className='flex w-full items-center justify-center space-x-2'>
          <div className={cn('flex items-center h-20 rounded-xl bg-components-dropzone-bg border border-dashed border-components-dropzone-border system-sm-regular', dragging && 'bg-components-dropzone-bg-accent border border-components-dropzone-border-accent')}>
            <div className='w-full flex items-center justify-center space-x-2'>
              <CSVIcon className="shrink-0" />
              <div className='text-text-tertiary'>
                {t('appAnnotation.batchModal.csvUploadTitle')}
                <span className='cursor-pointer text-text-accent' onClick={selectHandle}>{t('appAnnotation.batchModal.browse')}</span>
                <span className='text-text-accent cursor-pointer' onClick={selectHandle}>{t('appAnnotation.batchModal.browse')}</span>
              </div>
            </div>
            {dragging && <div ref={dragRef} className='absolute left-0 top-0 h-full w-full' />}
            {dragging && <div ref={dragRef} className='absolute w-full h-full top-0 left-0' />}
          </div>
        )}
        {file && (
          <div className={cn('group flex h-20 items-center rounded-xl border border-components-panel-border bg-components-panel-bg px-6 text-sm font-normal', 'hover:border-components-panel-bg-blur hover:bg-components-panel-bg-blur')}>
          <div className={cn('flex items-center h-20 px-6 rounded-xl bg-components-panel-bg border border-components-panel-border text-sm font-normal group', 'hover:bg-components-panel-bg-blur hover:border-components-panel-bg-blur')}>
            <CSVIcon className="shrink-0" />
            <div className='ml-2 flex w-0 grow'>
              <span className='max-w-[calc(100%_-_30px)] overflow-hidden text-ellipsis whitespace-nowrap text-text-primary'>{file.name.replace(/.csv$/, '')}</span>
            <div className='flex ml-2 w-0 grow'>
              <span className='max-w-[calc(100%_-_30px)] text-ellipsis whitespace-nowrap overflow-hidden text-text-primary'>{file.name.replace(/.csv$/, '')}</span>
              <span className='shrink-0 text-text-tertiary'>.csv</span>
            </div>
            <div className='hidden items-center group-hover:flex'>
            <div className='hidden group-hover:flex items-center'>
              <Button variant='secondary' onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.change')}</Button>
              <div className='mx-2 h-4 w-px bg-divider-regular' />
              <div className='cursor-pointer p-2' onClick={removeFile}>
                <RiDeleteBinLine className='h-4 w-4 text-text-tertiary' />
              <div className='mx-2 w-px h-4 bg-divider-regular' />
              <div className='p-2 cursor-pointer' onClick={removeFile}>
                <RiDeleteBinLine className='w-4 h-4 text-text-tertiary' />
              </div>
            </div>
          </div>
app/components/app/annotation/batch-add-annotation-modal/index.tsx
@@ -11,7 +11,6 @@
import { annotationBatchImport, checkAnnotationBatchImportProgress } from '@/service/annotation'
import { useProviderContext } from '@/context/provider-context'
import AnnotationFull from '@/app/components/billing/annotation-full'
import { noop } from 'lodash-es'
export enum ProcessStatus {
  WAITING = 'waiting',
@@ -88,10 +87,10 @@
  }
  return (
    <Modal isShow={isShow} onClose={noop} className='!max-w-[520px] !rounded-xl px-8 py-6'>
      <div className='system-xl-medium relative pb-1 text-text-primary'>{t('appAnnotation.batchModal.title')}</div>
      <div className='absolute right-4 top-4 cursor-pointer p-2' onClick={onCancel}>
        <RiCloseLine className='h-4 w-4 text-text-tertiary' />
    <Modal isShow={isShow} onClose={() => { }} className='px-8 py-6 !max-w-[520px] !rounded-xl'>
      <div className='relative pb-1 system-xl-medium text-text-primary'>{t('appAnnotation.batchModal.title')}</div>
      <div className='absolute right-4 top-4 p-2 cursor-pointer' onClick={onCancel}>
        <RiCloseLine className='w-4 h-4 text-text-tertiary' />
      </div>
      <CSVUploader
        file={currentCSV}
@@ -105,8 +104,8 @@
        </div>
      )}
      <div className='mt-[28px] flex justify-end pt-6'>
        <Button className='system-sm-medium mr-2 text-text-tertiary' onClick={onCancel}>
      <div className='mt-[28px] pt-6 flex justify-end'>
        <Button className='mr-2 text-text-tertiary system-sm-medium' onClick={onCancel}>
          {t('appAnnotation.batchModal.cancel')}
        </Button>
        <Button
app/components/app/annotation/edit-annotation-modal/edit-item/index.tsx
@@ -20,11 +20,11 @@
}
export const EditTitle: FC<{ className?: string; title: string }> = ({ className, title }) => (
  <div className={cn(className, 'system-xs-medium flex h-[18px] items-center text-text-tertiary')}>
    <RiEditFill className='mr-1 h-3.5 w-3.5' />
  <div className={cn(className, 'flex items-center h-[18px] system-xs-medium text-text-tertiary')}>
    <RiEditFill className='mr-1 w-3.5 h-3.5' />
    <div>{title}</div>
    <div
      className='ml-2 h-[1px] grow'
      className='ml-2 grow h-[1px]'
      style={{
        background: 'linear-gradient(90deg, rgba(0, 0, 0, 0.05) -1.65%, rgba(0, 0, 0, 0.00) 100%)',
      }}
@@ -40,7 +40,7 @@
  const { t } = useTranslation()
  const [newContent, setNewContent] = useState('')
  const showNewContent = newContent && newContent !== content
  const avatar = type === EditItemType.Query ? <User className='h-6 w-6' /> : <Robot className='h-6 w-6' />
  const avatar = type === EditItemType.Query ? <User className='w-6 h-6' /> : <Robot className='w-6 h-6' />
  const name = type === EditItemType.Query ? t('appAnnotation.editModal.queryName') : t('appAnnotation.editModal.answerName')
  const editTitle = type === EditItemType.Query ? t('appAnnotation.editModal.yourQuery') : t('appAnnotation.editModal.yourAnswer')
  const placeholder = type === EditItemType.Query ? t('appAnnotation.editModal.queryPlaceholder') : t('appAnnotation.editModal.answerPlaceholder')
@@ -58,11 +58,11 @@
  return (
    <div className='flex' onClick={e => e.stopPropagation()}>
      <div className='mr-3 shrink-0'>
      <div className='shrink-0 mr-3'>
        {avatar}
      </div>
      <div className='grow'>
        <div className='system-xs-semibold mb-1 text-text-primary'>{name}</div>
        <div className='mb-1 system-xs-semibold text-text-primary'>{name}</div>
        <div className='system-sm-regular text-text-primary'>{content}</div>
        {!isEdit
          ? (
@@ -70,34 +70,34 @@
              {showNewContent && (
                <div className='mt-3'>
                  <EditTitle title={editTitle} />
                  <div className='system-sm-regular mt-1 text-text-primary'>{newContent}</div>
                  <div className='mt-1 system-sm-regular text-text-primary'>{newContent}</div>
                </div>
              )}
              <div className='mt-2 flex items-center'>
                {!readonly && (
                  <div
                    className='system-xs-medium flex cursor-pointer items-center space-x-1 text-text-accent'
                    className='flex items-center space-x-1 system-xs-medium text-text-accent cursor-pointer'
                    onClick={() => {
                      setIsEdit(true)
                    }}
                  >
                    <RiEditLine className='mr-1 h-3.5 w-3.5' />
                    <RiEditLine className='mr-1 w-3.5 h-3.5' />
                    <div>{t('common.operation.edit')}</div>
                  </div>
                )}
                {showNewContent && (
                  <div className='system-xs-medium ml-2 flex items-center text-text-tertiary'>
                  <div className='ml-2 flex items-center system-xs-medium text-text-tertiary'>
                    <div className='mr-2'>·</div>
                    <div
                      className='flex cursor-pointer items-center space-x-1'
                      className='flex items-center space-x-1 cursor-pointer'
                      onClick={() => {
                        setNewContent(content)
                        onSave(content)
                      }}
                    >
                      <div className='h-3.5 w-3.5'>
                        <RiDeleteBinLine className='h-3.5 w-3.5' />
                      <div className='w-3.5 h-3.5'>
                        <RiDeleteBinLine className='w-3.5 h-3.5' />
                      </div>
                      <div>{t('common.operation.delete')}</div>
                    </div>
app/components/app/annotation/edit-annotation-modal/index.tsx
@@ -86,7 +86,7 @@
        title={t('appAnnotation.editModal.title') as string}
        body={(
          <div>
            <div className='space-y-6 p-6 pb-4'>
            <div className='p-6 pb-4 space-y-6'>
              <EditItem
                type={EditItemType.Query}
                content={query}
@@ -115,7 +115,7 @@
        foot={
          <div>
            {isAnnotationFull && (
              <div className='mb-4 mt-6 px-6'>
              <div className='mt-6 mb-4 px-6'>
                <AnnotationFull />
              </div>
            )}
@@ -123,9 +123,9 @@
            {
              annotationId
                ? (
                  <div className='system-sm-medium flex h-16 items-center justify-between rounded-bl-xl rounded-br-xl border-t border-divider-subtle bg-background-section-burn px-4 text-text-tertiary'>
                  <div className='px-4 flex h-16 items-center justify-between border-t border-divider-subtle bg-background-section-burn rounded-bl-xl rounded-br-xl system-sm-medium text-text-tertiary'>
                    <div
                      className='flex cursor-pointer items-center space-x-2 pl-3'
                      className='flex items-center pl-3 space-x-2 cursor-pointer'
                      onClick={() => setShowModal(true)}
                    >
                      <MessageCheckRemove />
app/components/app/annotation/empty-element.tsx
@@ -13,10 +13,10 @@
  const { t } = useTranslation()
  return (
    <div className='flex h-full items-center justify-center'>
      <div className='box-border h-fit w-[560px] rounded-2xl bg-background-section-burn px-5 py-4'>
        <span className='system-md-semibold text-text-secondary'>{t('appAnnotation.noData.title')}<ThreeDotsIcon className='relative -left-1.5 -top-3 inline' /></span>
        <div className='system-sm-regular mt-2 text-text-tertiary'>
    <div className='flex items-center justify-center h-full'>
      <div className='bg-background-section-burn w-[560px] h-fit box-border px-5 py-4 rounded-2xl'>
        <span className='text-text-secondary system-md-semibold'>{t('appAnnotation.noData.title')}<ThreeDotsIcon className='inline relative -top-3 -left-1.5' /></span>
        <div className='mt-2 text-text-tertiary system-sm-regular'>
          {t('appAnnotation.noData.description')}
        </div>
      </div>
app/components/app/annotation/filter.tsx
@@ -14,7 +14,7 @@
  appId: string
  queryParams: QueryParam
  setQueryParams: (v: QueryParam) => void
  children: React.JSX.Element
  children: JSX.Element
}
const Filter: FC<IFilterProps> = ({
@@ -29,7 +29,7 @@
  if (!data)
    return null
  return (
    <div className='mb-2 flex flex-row flex-wrap items-center justify-between gap-2'>
    <div className='flex justify-between flex-row flex-wrap gap-2 items-center mb-2'>
      <Input
        wrapperClassName='w-[200px]'
        showLeftIcon
app/components/app/annotation/header-opts/index.tsx
@@ -10,7 +10,7 @@
import {
  useCSVDownloader,
} from 'react-papaparse'
import { Menu, MenuButton, MenuItems, Transition } from '@headlessui/react'
import { Menu, Transition } from '@headlessui/react'
import Button from '../../../base/button'
import AddAnnotationModal from '../add-annotation-modal'
import type { AnnotationItemBasic } from '../type'
@@ -80,18 +80,18 @@
  const Operations = () => {
    return (
      <div className="w-full py-1">
        <button className='mx-1 flex h-9 w-[calc(100%_-_8px)] cursor-pointer items-center space-x-2 rounded-lg px-3 py-2 hover:bg-components-panel-on-panel-item-bg-hover disabled:opacity-50' onClick={() => {
        <button className='h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-components-panel-on-panel-item-bg-hover rounded-lg cursor-pointer disabled:opacity-50 w-[calc(100%_-_8px)]' onClick={() => {
          setShowBulkImportModal(true)
        }}>
          <FilePlus02 className='h-4 w-4 text-text-tertiary' />
          <span className='system-sm-regular grow text-left text-text-secondary'>{t('appAnnotation.table.header.bulkImport')}</span>
          <FilePlus02 className='w-4 h-4 text-text-tertiary' />
          <span className='grow text-text-secondary system-sm-regular text-left'>{t('appAnnotation.table.header.bulkImport')}</span>
        </button>
        <Menu as="div" className="relative h-full w-full">
          <MenuButton className='mx-1 flex h-9 w-[calc(100%_-_8px)] cursor-pointer items-center space-x-2 rounded-lg px-3 py-2 hover:bg-components-panel-on-panel-item-bg-hover disabled:opacity-50'>
            <FileDownload02 className='h-4 w-4 text-text-tertiary' />
            <span className='system-sm-regular grow text-left text-text-secondary'>{t('appAnnotation.table.header.bulkExport')}</span>
            <ChevronRight className='h-[14px] w-[14px] shrink-0 text-text-tertiary' />
          </MenuButton>
        <Menu as="div" className="relative w-full h-full">
          <Menu.Button className='h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-components-panel-on-panel-item-bg-hover rounded-lg cursor-pointer disabled:opacity-50 w-[calc(100%_-_8px)]'>
            <FileDownload02 className='w-4 h-4 text-text-tertiary' />
            <span className='grow text-text-secondary system-sm-regular text-left'>{t('appAnnotation.table.header.bulkExport')}</span>
            <ChevronRight className='shrink-0 w-[14px] h-[14px] text-text-tertiary' />
          </Menu.Button>
          <Transition
            as={Fragment}
            enter="transition ease-out duration-100"
@@ -101,9 +101,9 @@
            leaveFrom="transform opacity-100 scale-100"
            leaveTo="transform opacity-0 scale-95"
          >
            <MenuItems
            <Menu.Items
              className={cn(
                'absolute left-1 top-[1px] z-10 min-w-[100px] origin-top-right -translate-x-full rounded-xl border-[0.5px] border-components-panel-on-panel-item-bg bg-components-panel-bg py-1 shadow-xs',
                'absolute top-[1px] left-1 -translate-x-full py-1 min-w-[100px] z-10 bg-components-panel-bg border-[0.5px] border-components-panel-on-panel-item-bg origin-top-right rounded-xl shadow-xs',
              )}
            >
              <CSVDownloader
@@ -115,14 +115,14 @@
                  ...list.map(item => [item.question, item.answer]),
                ]}
              >
                <button disabled={annotationUnavailable} className='mx-1 flex h-9 w-[calc(100%_-_8px)] cursor-pointer items-center space-x-2 rounded-lg px-3 py-2 hover:bg-components-panel-on-panel-item-bg-hover disabled:opacity-50'>
                  <span className='system-sm-regular grow text-left text-text-secondary'>CSV</span>
                <button disabled={annotationUnavailable} className='h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-components-panel-on-panel-item-bg-hover rounded-lg cursor-pointer disabled:opacity-50 w-[calc(100%_-_8px)]'>
                  <span className='grow text-text-secondary system-sm-regular text-left'>CSV</span>
                </button>
              </CSVDownloader>
              <button disabled={annotationUnavailable} className={cn('mx-1 flex h-9 w-[calc(100%_-_8px)] cursor-pointer items-center space-x-2 rounded-lg px-3 py-2 hover:bg-components-panel-on-panel-item-bg-hover disabled:opacity-50', '!border-0')} onClick={JSONLOutput}>
                <span className='system-sm-regular grow text-left text-text-secondary'>JSONL</span>
              <button disabled={annotationUnavailable} className={cn('h-9 py-2 px-3 mx-1 flex items-center space-x-2 hover:bg-components-panel-on-panel-item-bg-hover rounded-lg cursor-pointer disabled:opacity-50 w-[calc(100%_-_8px)]', '!border-0')} onClick={JSONLOutput}>
                <span className='grow text-text-secondary system-sm-regular text-left'>JSONL</span>
              </button>
            </MenuItems>
            </Menu.Items>
          </Transition>
        </Menu>
      </div>
@@ -134,7 +134,7 @@
  return (
    <div className='flex space-x-2'>
      <Button variant='primary' onClick={() => setShowAddModal(true)}>
        <RiAddLine className='mr-0.5 h-4 w-4' />
        <RiAddLine className='w-4 h-4 mr-0.5' />
        <div>{t('appAnnotation.table.header.addAnnotation')}</div>
      </Button>
      <CustomPopover
@@ -143,11 +143,11 @@
        trigger="click"
        btnElement={
          <Button variant='secondary' className='w-8 p-0'>
            <RiMoreFill className='h-4 w-4' />
            <RiMoreFill className='w-4 h-4' />
          </Button>
        }
        btnClassName='p-0 border-0'
        className={'!z-20 h-fit !w-[155px]'}
        className={'!w-[155px] h-fit !z-20'}
        popupClassName='!w-full !overflow-visible'
        manualClose
      />
app/components/app/annotation/index.tsx
@@ -90,7 +90,7 @@
      setList(data as AnnotationItem[])
      setTotal(total)
    }
    catch {
    catch (e) {
    }
    setIsLoading(false)
@@ -152,15 +152,15 @@
  }
  return (
    <div className='flex h-full flex-col'>
      <p className='system-sm-regular text-text-tertiary'>{t('appLog.description')}</p>
      <div className='flex flex-1 flex-col py-4'>
    <div className='flex flex-col h-full'>
      <p className='text-text-tertiary system-sm-regular'>{t('appLog.description')}</p>
      <div className='flex flex-col py-4 flex-1'>
        <Filter appId={appDetail.id} queryParams={queryParams} setQueryParams={setQueryParams}>
          <div className='flex items-center space-x-2'>
            {isChatApp && (
              <>
                <div className={cn(!annotationConfig?.enabled && 'pr-2', 'flex h-7 items-center space-x-1 rounded-lg border border-components-panel-border bg-components-panel-bg-blur pl-2')}>
                  <MessageFast className='h-4 w-4 text-util-colors-indigo-indigo-600' />
                <div className={cn(!annotationConfig?.enabled && 'pr-2', 'flex items-center h-7 rounded-lg bg-components-panel-bg-blur border border-components-panel-border pl-2 space-x-1')}>
                  <MessageFast className='w-4 h-4 text-util-colors-indigo-indigo-600' />
                  <div className='system-sm-medium text-text-primary'>{t('appAnnotation.name')}</div>
                  <Switch
                    key={controlRefreshSwitch}
@@ -188,14 +188,14 @@
                  ></Switch>
                  {annotationConfig?.enabled && (
                    <div className='flex items-center pl-1.5'>
                      <div className='mr-1 h-3.5 w-[1px] shrink-0 bg-divider-subtle'></div>
                      <div className='shrink-0 mr-1 w-[1px] h-3.5 bg-divider-subtle'></div>
                      <ActionButton onClick={() => setIsShowEdit(true)}>
                        <RiEqualizer2Line className='h-4 w-4 text-text-tertiary' />
                        <RiEqualizer2Line className='w-4 h-4 text-text-tertiary' />
                      </ActionButton>
                    </div>
                  )}
                </div>
                <div className='mx-3 h-3.5 w-[1px] shrink-0 bg-divider-regular'></div>
                <div className='shrink-0 mx-3 w-[1px] h-3.5 bg-divider-regular'></div>
              </>
            )}
@@ -217,7 +217,7 @@
              onRemove={handleRemove}
              onView={handleView}
            />
            : <div className='flex h-full grow items-center justify-center'><EmptyElement /></div>
            : <div className='grow flex h-full items-center justify-center'><EmptyElement /></div>
        }
        {/* Show Pagination only if the total is more than the limit */}
        {(total && total > APP_PAGE_LIMIT)
app/components/app/annotation/list.tsx
@@ -29,18 +29,18 @@
      <table className={cn('mt-2 w-full min-w-[440px] border-collapse border-0')}>
        <thead className='system-xs-medium-uppercase text-text-tertiary'>
          <tr>
            <td className='w-5 whitespace-nowrap rounded-l-lg bg-background-section-burn pl-2 pr-1'>{t('appAnnotation.table.header.question')}</td>
            <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appAnnotation.table.header.answer')}</td>
            <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appAnnotation.table.header.createdAt')}</td>
            <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appAnnotation.table.header.hits')}</td>
            <td className='w-[96px] whitespace-nowrap rounded-r-lg bg-background-section-burn py-1.5 pl-3'>{t('appAnnotation.table.header.actions')}</td>
            <td className='pl-2 pr-1 w-5 rounded-l-lg bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.table.header.question')}</td>
            <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.table.header.answer')}</td>
            <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.table.header.createdAt')}</td>
            <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.table.header.hits')}</td>
            <td className='pl-3 py-1.5 rounded-r-lg bg-background-section-burn whitespace-nowrap w-[96px]'>{t('appAnnotation.table.header.actions')}</td>
          </tr>
        </thead>
        <tbody className="system-sm-regular text-text-secondary">
        <tbody className="text-text-secondary system-sm-regular">
          {list.map(item => (
            <tr
              key={item.id}
              className='cursor-pointer border-b border-divider-subtle hover:bg-background-default-hover'
              className='border-b border-divider-subtle hover:bg-background-default-hover cursor-pointer'
              onClick={
                () => {
                  onView(item)
@@ -48,11 +48,11 @@
              }
            >
              <td
                className='max-w-[250px] overflow-hidden text-ellipsis whitespace-nowrap p-3 pr-2'
                className='p-3 pr-2 whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
                title={item.question}
              >{item.question}</td>
              <td
                className='max-w-[250px] overflow-hidden text-ellipsis whitespace-nowrap p-3 pr-2'
                className='p-3 pr-2 whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
                title={item.answer}
              >{item.answer}</td>
              <td className='p-3 pr-2'>{formatTime(item.created_at, t('appLog.dateTimeFormat') as string)}</td>
@@ -61,7 +61,7 @@
                {/* Actions */}
                <div className='flex space-x-1 text-text-tertiary'>
                  <ActionButton onClick={() => onView(item)}>
                    <RiEditLine className='h-4 w-4' />
                    <RiEditLine className='w-4 h-4' />
                  </ActionButton>
                  <ActionButton
                    onClick={() => {
@@ -69,7 +69,7 @@
                      setShowConfirmDelete(true)
                    }}
                  >
                    <RiDeleteBinLine className='h-4 w-4' />
                    <RiDeleteBinLine className='w-4 h-4' />
                  </ActionButton>
                </div>
              </td>
app/components/app/annotation/view-annotation-modal/hit-history-no-data.tsx
@@ -7,9 +7,9 @@
const HitHistoryNoData: FC = () => {
  const { t } = useTranslation()
  return (
    <div className='mx-auto mt-20 w-[480px] space-y-2 rounded-2xl bg-background-section-burn p-5'>
      <div className='inline-block rounded-lg border border-divider-subtle p-3'>
        <ClockFastForward className='h-5 w-5 text-text-tertiary' />
    <div className='mx-auto mt-20 w-[480px] p-5 rounded-2xl bg-background-section-burn space-y-2'>
      <div className='inline-block p-3 rounded-lg border border-divider-subtle'>
        <ClockFastForward className='w-5 h-5 text-text-tertiary' />
      </div>
      <div className='system-sm-regular text-text-tertiary'>{t('appAnnotation.viewModal.noHitHistory')}</div>
    </div>
app/components/app/annotation/view-annotation-modal/index.tsx
@@ -55,7 +55,7 @@
      setHitHistoryList(data as HitHistoryItem[])
      setTotal(total)
    }
    catch {
    catch (e) {
    }
  }
@@ -116,30 +116,30 @@
        <table className={cn('w-full min-w-[440px] border-collapse border-0')} >
          <thead className="system-xs-medium-uppercase text-text-tertiary">
            <tr>
              <td className='w-5 whitespace-nowrap rounded-l-lg bg-background-section-burn pl-2 pr-1'>{t('appAnnotation.hitHistoryTable.query')}</td>
              <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appAnnotation.hitHistoryTable.match')}</td>
              <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appAnnotation.hitHistoryTable.response')}</td>
              <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appAnnotation.hitHistoryTable.source')}</td>
              <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appAnnotation.hitHistoryTable.score')}</td>
              <td className='w-[160px] whitespace-nowrap rounded-r-lg bg-background-section-burn py-1.5 pl-3'>{t('appAnnotation.hitHistoryTable.time')}</td>
              <td className='pl-2 pr-1 w-5 rounded-l-lg bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.query')}</td>
              <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.match')}</td>
              <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.response')}</td>
              <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.source')}</td>
              <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appAnnotation.hitHistoryTable.score')}</td>
              <td className='pl-3 py-1.5 rounded-r-lg bg-background-section-burn whitespace-nowrap w-[160px]'>{t('appAnnotation.hitHistoryTable.time')}</td>
            </tr>
          </thead>
          <tbody className="system-sm-regular text-text-secondary">
          <tbody className="text-text-secondary system-sm-regular">
            {hitHistoryList.map(item => (
              <tr
                key={item.id}
                className={'cursor-pointer border-b border-divider-subtle hover:bg-background-default-hover'}
                className={'border-b border-divider-subtle hover:bg-background-default-hover cursor-pointer'}
              >
                <td
                  className='max-w-[250px] overflow-hidden text-ellipsis whitespace-nowrap p-3 pr-2'
                  className='p-3 pr-2 whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
                  title={item.question}
                >{item.question}</td>
                <td
                  className='max-w-[250px] overflow-hidden text-ellipsis whitespace-nowrap p-3 pr-2'
                  className='p-3 pr-2 whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
                  title={item.match}
                >{item.match}</td>
                <td
                  className='max-w-[250px] overflow-hidden text-ellipsis whitespace-nowrap p-3 pr-2'
                  className='p-3 pr-2 whitespace-nowrap overflow-hidden text-ellipsis max-w-[250px]'
                  title={item.response}
                >{item.response}</td>
                <td className='p-3 pr-2'>{item.source}</td>
@@ -168,7 +168,7 @@
        maxWidthClassName='!max-w-[800px]'
        title={
          <TabSlider
            className='relative top-[9px] shrink-0'
            className='shrink-0 relative top-[9px]'
            value={activeTab}
            onChange={v => setActiveTab(v as TabType)}
            options={tabs}
@@ -178,7 +178,7 @@
        }
        body={(
          <div>
            <div className='space-y-6 p-6 pb-4'>
            <div className='p-6 pb-4 space-y-6'>
              {activeTab === TabType.annotation ? annotationTab : hitHistoryTab}
            </div>
            <Confirm
@@ -195,9 +195,9 @@
        )}
        foot={id
          ? (
            <div className='system-sm-medium flex h-16 items-center justify-between rounded-bl-xl rounded-br-xl border-t border-divider-subtle bg-background-section-burn px-4 text-text-tertiary'>
            <div className='px-4 flex h-16 items-center justify-between border-t border-divider-subtle bg-background-section-burn rounded-bl-xl rounded-br-xl system-sm-medium text-text-tertiary'>
              <div
                className='flex cursor-pointer items-center space-x-2 pl-3'
                className='flex items-center pl-3 space-x-2 cursor-pointer'
                onClick={() => setShowModal(true)}
              >
                <MessageCheckRemove />
app/components/app/app-publisher/index.tsx
@@ -5,17 +5,9 @@
} from 'react'
import { useTranslation } from 'react-i18next'
import dayjs from 'dayjs'
import {
  RiArrowDownSLine,
  RiPlanetLine,
  RiPlayCircleLine,
  RiPlayList2Line,
  RiTerminalBoxLine,
} from '@remixicon/react'
import { useKeyPress } from 'ahooks'
import { RiArrowDownSLine, RiPlanetLine } from '@remixicon/react'
import Toast from '../../base/toast'
import type { ModelAndParameter } from '../configuration/debug/types'
import { getKeyboardKeyCodeBySystem } from '../../workflow/utils'
import SuggestedAction from './suggested-action'
import PublishWithMultipleModel from './publish-with-multiple-model'
import Button from '@/app/components/base/button'
@@ -24,16 +16,17 @@
  PortalToFollowElemContent,
  PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import { WEB_PREFIX } from '@/config'
import { fetchInstalledAppList } from '@/service/explore'
import EmbeddedModal from '@/app/components/app/overview/embedded'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useGetLanguage } from '@/context/i18n'
import { PlayCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
import { CodeBrowser } from '@/app/components/base/icons/src/vender/line/development'
import { LeftIndent02 } from '@/app/components/base/icons/src/vender/line/editor'
import { FileText } from '@/app/components/base/icons/src/vender/line/files'
import WorkflowToolConfigureButton from '@/app/components/tools/workflow-tool/configure-button'
import type { InputVar } from '@/app/components/workflow/types'
import { appDefaultIconBackground } from '@/config'
import type { PublishWorkflowParams } from '@/types/workflow'
export type AppPublisherProps = {
  disabled?: boolean
@@ -44,7 +37,7 @@
  debugWithMultipleModel?: boolean
  multipleModelConfigs?: ModelAndParameter[]
  /** modelAndParameter is passed when debugWithMultipleModel is true */
  onPublish?: (params?: any) => Promise<any> | any
  onPublish?: (modelAndParameter?: ModelAndParameter) => Promise<any> | any
  onRestore?: () => Promise<any> | any
  onToggle?: (state: boolean) => void
  crossAxisOffset?: number
@@ -52,8 +45,6 @@
  inputs?: InputVar[]
  onRefreshData?: () => void
}
const PUBLISH_SHORTCUT = ['⌘', '⇧', 'P']
const AppPublisher = ({
  disabled = false,
@@ -77,29 +68,28 @@
  const { app_base_url: appBaseURL = '', access_token: accessToken = '' } = appDetail?.site ?? {}
  const appMode = (appDetail?.mode !== 'completion' && appDetail?.mode !== 'workflow') ? 'chat' : appDetail.mode
  const appURL = `${appBaseURL}/${appMode}/${accessToken}`
  const isChatApp = ['chat', 'agent-chat', 'completion'].includes(appDetail?.mode || '')
  const language = useGetLanguage()
  const formatTimeFromNow = useCallback((time: number) => {
    return dayjs(time).locale(language === 'zh_Hans' ? 'zh-cn' : language.replace('_', '-')).fromNow()
  }, [language])
  const handlePublish = useCallback(async (params?: ModelAndParameter | PublishWorkflowParams) => {
  const handlePublish = async (modelAndParameter?: ModelAndParameter) => {
    try {
      await onPublish?.(params)
      await onPublish?.(modelAndParameter)
      setPublished(true)
    }
    catch {
    catch (e) {
      setPublished(false)
    }
  }, [onPublish])
  }
  const handleRestore = useCallback(async () => {
    try {
      await onRestore?.()
      setOpen(false)
    }
    catch {}
    catch (e) { }
  }, [onRestore])
  const handleTrigger = useCallback(() => {
@@ -121,7 +111,7 @@
    try {
      const { installed_apps }: any = await fetchInstalledAppList(appDetail?.id) || {}
      if (installed_apps?.length > 0)
        window.open(`${WEB_PREFIX}/explore/installed/${installed_apps[0].id}`, '_blank')
        window.open(`/explore/installed/${installed_apps[0].id}`, '_blank')
      else
        throw new Error('No app found in Explore')
    }
@@ -132,16 +122,7 @@
  const [embeddingModalOpen, setEmbeddingModalOpen] = useState(false)
  useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.shift.p`, (e) => {
    e.preventDefault()
    if (publishDisabled || published)
      return
    handlePublish()
  },
  { exactMatch: true, useCapture: true })
  return (
    <>
      <PortalToFollowElem
        open={open}
        onOpenChange={setOpen}
@@ -154,37 +135,40 @@
        <PortalToFollowElemTrigger onClick={handleTrigger}>
          <Button
            variant='primary'
            className='p-2'
          className='pl-3 pr-2'
            disabled={disabled}
          >
            {t('workflow.common.publish')}
            <RiArrowDownSLine className='h-4 w-4 text-components-button-primary-text' />
          <RiArrowDownSLine className='w-4 h-4 ml-0.5' />
          </Button>
        </PortalToFollowElemTrigger>
        <PortalToFollowElemContent className='z-[11]'>
          <div className='w-[320px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl shadow-shadow-shadow-5'>
        <div className='w-[336px] bg-white rounded-2xl border-[0.5px] border-gray-200 shadow-xl'>
            <div className='p-4 pt-3'>
              <div className='system-xs-medium-uppercase flex h-6 items-center text-text-tertiary'>
            <div className='flex items-center h-6 text-xs font-medium text-gray-500 uppercase'>
                {publishedAt ? t('workflow.common.latestPublished') : t('workflow.common.currentDraftUnpublished')}
              </div>
              {publishedAt
                ? (
                  <div className='flex items-center justify-between'>
                    <div className='system-sm-medium flex items-center text-text-secondary'>
                <div className='flex justify-between items-center h-[18px]'>
                  <div className='flex items-center mt-[3px] mb-[3px] leading-[18px] text-[13px] font-medium text-gray-700'>
                      {t('workflow.common.publishedAt')} {formatTimeFromNow(publishedAt)}
                    </div>
                    {isChatApp && <Button
                      variant='secondary-accent'
                  <Button
                    className={`
                      ml-2 px-2 text-primary-600
                      ${published && 'text-primary-300 border-gray-100'}
                    `}
                      size='small'
                      onClick={handleRestore}
                      disabled={published}
                    >
                      {t('workflow.common.restore')}
                    </Button>}
                  </Button>
                  </div>
                )
                : (
                  <div className='system-sm-medium flex items-center text-text-secondary'>
                <div className='flex items-center h-[18px] leading-[18px] text-[13px] font-medium text-gray-700'>
                    {t('workflow.common.autoSaved')} · {Boolean(draftUpdatedAt) && formatTimeFromNow(draftUpdatedAt!)}
                  </div>
                )}
@@ -199,44 +183,27 @@
                : (
                  <Button
                    variant='primary'
                    className='mt-3 w-full'
                  className='w-full mt-3'
                    onClick={() => handlePublish()}
                    disabled={publishDisabled || published}
                  >
                    {
                      published
                        ? t('workflow.common.published')
                        : (
                          <div className='flex gap-1'>
                            <span>{t('workflow.common.publishUpdate')}</span>
                            <div className='flex gap-0.5'>
                              {PUBLISH_SHORTCUT.map(key => (
                                <span key={key} className='system-kbd h-4 w-4 rounded-[4px] bg-components-kbd-bg-white text-text-primary-on-surface'>
                                  {key}
                                </span>
                              ))}
                            </div>
                          </div>
                        )
                      : publishedAt ? t('workflow.common.update') : t('workflow.common.publish')
                    }
                  </Button>
                )
              }
            </div>
            <div className='border-t-[0.5px] border-t-divider-regular p-4 pt-3'>
              <SuggestedAction
                disabled={!publishedAt}
                link={appURL}
                icon={<RiPlayCircleLine className='h-4 w-4' />}
              >
                {t('workflow.common.runApp')}
              </SuggestedAction>
              {appDetail?.mode === 'workflow' || appDetail?.mode === 'completion'
          <div className='p-4 pt-3 border-t-[0.5px] border-t-black/5'>
            <SuggestedAction disabled={!publishedAt} link={appURL} icon={<PlayCircle />}>{t('workflow.common.runApp')}</SuggestedAction>
            {appDetail?.mode === 'workflow'
                ? (
                  <SuggestedAction
                    disabled={!publishedAt}
                    link={`${appURL}${appURL.includes('?') ? '&' : '?'}mode=batch`}
                    icon={<RiPlayList2Line className='h-4 w-4' />}
                  icon={<LeftIndent02 className='w-4 h-4' />}
                  >
                    {t('workflow.common.batchRunApp')}
                  </SuggestedAction>
@@ -248,27 +215,21 @@
                      handleTrigger()
                    }}
                    disabled={!publishedAt}
                    icon={<CodeBrowser className='h-4 w-4' />}
                  icon={<CodeBrowser className='w-4 h-4' />}
                  >
                    {t('workflow.common.embedIntoSite')}
                  </SuggestedAction>
                )}
              <SuggestedAction
                onClick={() => {
                  publishedAt && handleOpenInExplore()
                handleOpenInExplore()
                }}
                disabled={!publishedAt}
                icon={<RiPlanetLine className='h-4 w-4' />}
              icon={<RiPlanetLine className='w-4 h-4' />}
              >
                {t('workflow.common.openInExplore')}
              </SuggestedAction>
              <SuggestedAction
                disabled={!publishedAt}
                link='./develop'
                icon={<RiTerminalBoxLine className='h-4 w-4' />}
              >
                {t('workflow.common.accessAPIReference')}
              </SuggestedAction>
            <SuggestedAction disabled={!publishedAt} link='./develop' icon={<FileText className='w-4 h-4' />}>{t('workflow.common.accessAPIReference')}</SuggestedAction>
              {appDetail?.mode === 'workflow' && (
                <WorkflowToolConfigureButton
                  disabled={!publishedAt}
@@ -297,7 +258,6 @@
          accessToken={accessToken}
        />
      </PortalToFollowElem >
    </>
  )
}
app/components/app/app-publisher/publish-with-multiple-model.tsx
@@ -73,25 +73,25 @@
          className='mt-3 w-full'
        >
          {t('appDebug.operation.applyConfig')}
          <RiArrowDownSLine className='ml-0.5 h-3 w-3' />
          <RiArrowDownSLine className='ml-0.5 w-3 h-3' />
        </Button>
      </PortalToFollowElemTrigger>
      <PortalToFollowElemContent className='z-50 mt-1 w-[288px]'>
        <div className='rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg p-1 shadow-lg'>
          <div className='flex h-[22px] items-center px-3 text-xs font-medium text-text-tertiary'>
      <PortalToFollowElemContent className='mt-1 w-[288px] z-50'>
        <div className='p-1 rounded-lg border-[0.5px] border-gray-200 shadow-lg bg-white'>
          <div className='flex items-center px-3 h-[22px] text-xs font-medium text-gray-500'>
            {t('appDebug.publishAs')}
          </div>
          {
            validModelConfigs.map((item, index) => (
              <div
                key={item.id}
                className='flex h-8 cursor-pointer items-center rounded-lg px-3 text-sm text-text-tertiary hover:bg-state-base-hover'
                className='flex items-center h-8 px-3 text-sm text-gray-500 rounded-lg cursor-pointer hover:bg-gray-100'
                onClick={() => handleSelect(item)}
              >
                <span className='min-w-[18px] italic'>#{index + 1}</span>
                <span className='italic min-w-[18px]'>#{index + 1}</span>
                <ModelIcon modelName={item.model} provider={item.providerItem} className='ml-2' />
                <div
                  className='ml-1 truncate text-text-secondary'
                  className='ml-1 text-gray-700 truncate'
                  title={item.modelItem.label[language]}
                >
                  {item.modelItem.label[language]}
app/components/app/app-publisher/suggested-action.tsx
@@ -1,6 +1,6 @@
import type { HTMLProps, PropsWithChildren } from 'react'
import { RiArrowRightUpLine } from '@remixicon/react'
import classNames from '@/utils/classnames'
import { ArrowUpRight } from '@/app/components/base/icons/src/vender/line/arrows'
export type SuggestedActionProps = PropsWithChildren<HTMLProps<HTMLAnchorElement> & {
  icon?: React.ReactNode
@@ -14,15 +14,15 @@
    target='_blank'
    rel='noreferrer'
    className={classNames(
      'flex justify-start items-center gap-2 py-2 px-2.5 bg-background-section-burn rounded-lg transition-colors [&:not(:first-child)]:mt-1',
      disabled ? 'shadow-xs opacity-30 cursor-not-allowed' : 'text-text-secondary hover:bg-state-accent-hover hover:text-text-accent cursor-pointer',
      'flex justify-start items-center gap-2 h-[34px] px-2.5 bg-gray-100 rounded-lg transition-colors [&:not(:first-child)]:mt-1',
      disabled ? 'shadow-xs opacity-30 cursor-not-allowed' : 'hover:bg-primary-50 hover:text-primary-600 cursor-pointer',
      className,
    )}
    {...props}
  >
    <div className='relative h-4 w-4'>{icon}</div>
    <div className='system-sm-medium shrink grow basis-0'>{children}</div>
    <RiArrowRightUpLine className='h-3.5 w-3.5' />
    <div className='relative w-4 h-4'>{icon}</div>
    <div className='grow shrink basis-0 text-[13px] font-medium leading-[18px]'>{children}</div>
    <ArrowUpRight />
  </a>
)
app/components/app/configuration/base/feature-panel/index.tsx
@@ -23,15 +23,15 @@
  children,
}) => {
  return (
    <div className={cn('rounded-xl border-l-[0.5px] border-t-[0.5px] border-effects-highlight bg-background-section-burn pb-3', noBodySpacing && 'pb-0', className)}>
    <div className={cn('rounded-xl border-t-[0.5px] border-l-[0.5px] bg-background-section-burn pb-3', noBodySpacing && '!pb-0', className)}>
      {/* Header */}
      <div className={cn('px-3 pt-2', hasHeaderBottomBorder && 'border-b border-divider-subtle')}>
        <div className='flex h-8 items-center justify-between'>
          <div className='flex shrink-0 items-center space-x-1'>
            {headerIcon && <div className='flex h-6 w-6 items-center justify-center'>{headerIcon}</div>}
            <div className='system-sm-semibold text-text-secondary'>{title}</div>
        <div className='flex justify-between items-center h-8'>
          <div className='flex items-center space-x-1 shrink-0'>
            {headerIcon && <div className='flex items-center justify-center w-6 h-6'>{headerIcon}</div>}
            <div className='text-text-secondary system-sm-semibold'>{title}</div>
          </div>
          <div className='flex items-center gap-2'>
          <div className='flex gap-2 items-center'>
            {headerRight && <div>{headerRight}</div>}
          </div>
        </div>
app/components/app/configuration/base/group-name/index.tsx
@@ -10,9 +10,9 @@
  name,
}) => {
  return (
    <div className='mb-1 flex items-center'>
      <div className='mr-3 text-xs font-semibold uppercase leading-[18px] text-text-tertiary'>{name}</div>
      <div className='h-[1px] grow'
    <div className='flex items-center mb-1'>
      <div className='mr-3 leading-[18px] text-xs font-semibold text-gray-500 uppercase'>{name}</div>
      <div className='grow h-[1px]'
        style={{
          background: 'linear-gradient(270deg, rgba(243, 244, 246, 0) 0%, #F3F4F6 100%)',
app/components/app/configuration/base/icons/remove-icon/index.tsx
@@ -17,7 +17,7 @@
  const computedIsHovered = isHoverStatus || isHovered
  return (
    <div
      className={cn(className, computedIsHovered && 'bg-[#FEE4E2]', 'flex h-6 w-6 cursor-pointer items-center justify-center rounded-md hover:bg-[#FEE4E2]')}
      className={cn(className, computedIsHovered && 'bg-[#FEE4E2]', 'flex w-6 h-6 items-center justify-center rounded-md cursor-pointer hover:bg-[#FEE4E2]')}
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
      onClick={onClick}
app/components/app/configuration/base/operation-btn/index.tsx
@@ -2,12 +2,8 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import {
  RiAddLine,
  RiEditLine,
} from '@remixicon/react'
import { PlusIcon } from '@heroicons/react/20/solid'
import cn from '@/utils/classnames'
import { noop } from 'lodash-es'
export type IOperationBtnProps = {
  className?: string
@@ -17,20 +13,23 @@
}
const iconMap = {
  add: <RiAddLine className='h-3.5 w-3.5' />,
  edit: <RiEditLine className='h-3.5 w-3.5' />,
  add: <PlusIcon className='w-3.5 h-3.5' />,
  edit: (<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M6.99998 11.6666H12.25M1.75 11.6666H2.72682C3.01217 11.6666 3.15485 11.6666 3.28912 11.6344C3.40816 11.6058 3.52196 11.5587 3.62635 11.4947C3.74408 11.4226 3.84497 11.3217 4.04675 11.1199L11.375 3.79164C11.8583 3.30839 11.8583 2.52488 11.375 2.04164C10.8918 1.55839 10.1083 1.55839 9.62501 2.04164L2.29674 9.3699C2.09496 9.57168 1.99407 9.67257 1.92192 9.7903C1.85795 9.89469 1.81081 10.0085 1.78224 10.1275C1.75 10.2618 1.75 10.4045 1.75 10.6898V11.6666Z" stroke="#344054" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
  </svg>
  ),
}
const OperationBtn: FC<IOperationBtnProps> = ({
  className,
  type,
  actionName,
  onClick = noop,
  onClick = () => { },
}) => {
  const { t } = useTranslation()
  return (
    <div
      className={cn('flex h-7 cursor-pointer select-none items-center space-x-1 rounded-md px-3 text-text-secondary hover:bg-state-base-hover', className)}
      className={cn(className, 'flex items-center rounded-md h-7 px-3 space-x-1 text-gray-700 cursor-pointer hover:bg-gray-200 select-none')}
      onClick={onClick}>
      <div>
        {iconMap[type]}
app/components/app/configuration/base/var-highlight/index.tsx
@@ -16,7 +16,7 @@
  return (
    <div
      key={name}
      className={`${s.item} ${className} mb-2 flex h-5 items-center justify-center rounded-md px-1 text-xs font-medium text-primary-600`}
      className={`${s.item} ${className} flex mb-2 items-center justify-center rounded-md px-1 h-5 text-xs font-medium text-primary-600`}
    >
      <span className='opacity-60'>{'{{'}</span>
      <span>{name}</span>
app/components/app/configuration/base/var-highlight/style.module.css
app/components/app/configuration/base/warning-mask/cannot-query-dataset.tsx
@@ -20,7 +20,7 @@
      description={t('appDebug.feature.dataSet.queryVariable.unableToQueryDataSetTip')}
      footer={
        <div className='flex space-x-2'>
          <Button variant='primary' className='flex !w-[96px] justify-start' onClick={onConfirm}>
          <Button variant='primary' className='flex justify-start !w-[96px]' onClick={onConfirm}>
            <span className='text-[13px] font-medium'>{t('appDebug.feature.dataSet.queryVariable.ok')}</span>
          </Button>
        </div>
app/components/app/configuration/base/warning-mask/index.tsx
@@ -22,11 +22,11 @@
  footer,
}) => {
  return (
    <div className={`${s.mask} absolute inset-0 z-10 pt-16`}
    <div className={`${s.mask} absolute z-10 inset-0 pt-16`}
    >
      <div className='mx-auto px-10'>
        <div className={`${s.icon} flex h-11 w-11 items-center justify-center rounded-xl bg-white`}>{warningIcon}</div>
        <div className='mt-4 text-[24px] font-semibold leading-normal text-gray-800'>
        <div className={`${s.icon} flex items-center justify-center w-11 h-11 rounded-xl bg-white`}>{warningIcon}</div>
        <div className='mt-4 text-[24px] leading-normal font-semibold text-gray-800'>
          {title}
        </div>
        <div className='mt-3 text-base text-gray-500'>
app/components/app/configuration/base/warning-mask/style.module.css
app/components/app/configuration/config-prompt/advanced-prompt-input.tsx
@@ -20,7 +20,6 @@
  Clipboard,
  ClipboardCheck,
} from '@/app/components/base/icons/src/vender/line/files'
import Button from '@/app/components/base/button'
import Tooltip from '@/app/components/base/tooltip'
import PromptEditor from '@/app/components/base/prompt-editor'
import ConfigContext from '@/context/debug-configuration'
@@ -142,29 +141,28 @@
  const [editorHeight, setEditorHeight] = React.useState(isChatMode ? 200 : 508)
  const contextMissing = (
    <div
      className='flex h-11 items-center justify-between rounded-tl-xl rounded-tr-xl pb-1 pl-4 pr-3 pt-2'
      className='flex justify-between items-center h-11 pt-2 pr-3 pb-1 pl-4 rounded-tl-xl rounded-tr-xl'
      style={{
        background: 'linear-gradient(180deg, #FEF0C7 0%, rgba(254, 240, 199, 0) 100%)',
      }}
    >
      <div className='flex items-center pr-2' >
        <RiErrorWarningFill className='mr-1 h-4 w-4 text-[#F79009]' />
        <div className='text-[13px] font-medium leading-[18px] text-[#DC6803]'>{t('appDebug.promptMode.contextMissing')}</div>
        <RiErrorWarningFill className='mr-1 w-4 h-4 text-[#F79009]' />
        <div className='leading-[18px] text-[13px] font-medium text-[#DC6803]'>{t('appDebug.promptMode.contextMissing')}</div>
      </div>
      <Button
        size='small'
        variant='secondary-accent'
      <div
        className='flex items-center h-6 px-2 rounded-md bg-[#fff] border border-gray-200 shadow-xs text-xs font-medium text-primary-600 cursor-pointer'
        onClick={onHideContextMissingTip}
      >{t('common.operation.ok')}</Button>
      >{t('common.operation.ok')}</div>
    </div>
  )
  return (
    <div className={`rounded-xl bg-gradient-to-r from-components-input-border-active-prompt-1 to-components-input-border-active-prompt-2 p-0.5 shadow-xs ${!isContextMissing ? '' : s.warningBorder}`}>
      <div className='rounded-xl bg-background-default'>
    <div className={`relative ${!isContextMissing ? s.gradientBorder : s.warningBorder}`}>
      <div className='rounded-xl bg-white'>
        {isContextMissing
          ? contextMissing
          : (
            <div className={cn(s.boxHeader, 'flex h-11 items-center justify-between rounded-tl-xl rounded-tr-xl bg-background-default pb-1 pl-4 pr-3 pt-2 hover:shadow-xs')}>
            <div className={cn(s.boxHeader, 'flex justify-between items-center h-11 pt-2 pr-3 pb-1 pl-4 rounded-tl-xl rounded-tr-xl bg-white hover:shadow-xs')}>
              {isChatMode
                ? (
                  <MessageTypeSelector value={type} onChange={onTypeChange} />
@@ -184,30 +182,30 @@
                  </div>)}
              <div className={cn(s.optionWrap, 'items-center space-x-1')}>
                {canDelete && (
                  <RiDeleteBinLine onClick={onDelete} className='h-6 w-6 cursor-pointer p-1 text-text-tertiary' />
                  <RiDeleteBinLine onClick={onDelete} className='h-6 w-6 p-1 text-gray-500 cursor-pointer' />
                )}
                {!isCopied
                  ? (
                    <Clipboard className='h-6 w-6 cursor-pointer p-1 text-text-tertiary' onClick={() => {
                    <Clipboard className='h-6 w-6 p-1 text-gray-500 cursor-pointer' onClick={() => {
                      copy(value)
                      setIsCopied(true)
                    }} />
                  )
                  : (
                    <ClipboardCheck className='h-6 w-6 p-1 text-text-tertiary' />
                    <ClipboardCheck className='h-6 w-6 p-1 text-gray-500' />
                  )}
              </div>
            </div>
          )}
        <PromptEditorHeightResizeWrap
          className='min-h-[102px] overflow-y-auto px-4 text-sm text-text-secondary'
          className='px-4 min-h-[102px] overflow-y-auto text-sm text-gray-700'
          height={editorHeight}
          minHeight={minHeight}
          onHeightChange={setEditorHeight}
          footer={(
            <div className='flex pb-2 pl-4'>
              <div className="h-[18px] rounded-md bg-divider-regular px-1 text-xs leading-[18px] text-text-tertiary">{value.length}</div>
            <div className='pl-4 pb-2 flex'>
              <div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{value.length}</div>
            </div>
          )}
          hideResize={noResize}
app/components/app/configuration/config-prompt/confirm-add-var/index.tsx
@@ -39,18 +39,22 @@
      }}>
      <div
        ref={mainContentRef}
        className='w-[420px] rounded-xl bg-components-panel-bg p-6'
        className='w-[420px] rounded-xl bg-gray-50 p-6'
        style={{
          boxShadow: '0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03)',
        }}
      >
        <div className='flex items-start space-x-3'>
          <div
            className='flex h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-components-card-border bg-components-card-bg-alt shadow-lg'
            className='shrink-0 flex items-center justify-center h-10 w-10 rounded-xl border border-gray-100'
            style={{
              backgroundColor: 'rgba(255, 255, 255, 0.9)',
              boxShadow: '0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03)',
            }}
          >{VarIcon}</div>
          <div className='grow-1'>
            <div className='text-sm font-medium text-text-primary'>{t('appDebug.autoAddVar')}</div>
            <div className='mt-[15px] flex max-h-[66px] flex-wrap space-x-1 overflow-y-auto px-1'>
            <div className='text-sm font-medium text-gray-900'>{t('appDebug.autoAddVar')}</div>
            <div className='flex flex-wrap mt-[15px] max-h-[66px] overflow-y-auto px-1 space-x-1'>
              {varNameArr.map(name => (
                <VarHighlight key={name} name={name} />
              ))}
app/components/app/configuration/config-prompt/conversation-history/edit-modal.tsx
@@ -28,8 +28,8 @@
      isShow={isShow}
      onClose={onClose}
    >
      <div className={'mt-6 text-sm font-medium leading-[21px] text-text-primary'}>{t('appDebug.feature.conversationHistory.editModal.userPrefix')}</div>
      <input className={'mt-2 box-border h-10 w-full rounded-lg bg-components-input-bg-normal px-3 text-sm leading-10'}
      <div className={'mt-6 font-medium text-sm leading-[21px] text-gray-900'}>{t('appDebug.feature.conversationHistory.editModal.userPrefix')}</div>
      <input className={'mt-2 w-full rounded-lg h-10 box-border px-3 text-sm leading-10 bg-gray-100'}
        value={tempData.user_prefix}
        onChange={e => setTempData({
          ...tempData,
@@ -37,8 +37,8 @@
        })}
      />
      <div className={'mt-6 text-sm font-medium leading-[21px] text-text-primary'}>{t('appDebug.feature.conversationHistory.editModal.assistantPrefix')}</div>
      <input className={'mt-2 box-border h-10 w-full rounded-lg bg-components-input-bg-normal px-3 text-sm leading-10'}
      <div className={'mt-6 font-medium text-sm leading-[21px] text-gray-900'}>{t('appDebug.feature.conversationHistory.editModal.assistantPrefix')}</div>
      <input className={'mt-2 w-full rounded-lg h-10 box-border px-3 text-sm leading-10 bg-gray-100'}
        value={tempData.assistant_prefix}
        onChange={e => setTempData({
          ...tempData,
@@ -48,8 +48,8 @@
      />
      <div className='mt-10 flex justify-end'>
        <Button className='mr-2 shrink-0' onClick={onClose}>{t('common.operation.cancel')}</Button>
        <Button variant='primary' className='shrink-0' onClick={() => onSave(tempData)} loading={saveLoading}>{t('common.operation.save')}</Button>
        <Button className='mr-2 flex-shrink-0' onClick={onClose}>{t('common.operation.cancel')}</Button>
        <Button variant='primary' className='flex-shrink-0' onClick={() => onSave(tempData)} loading={saveLoading}>{t('common.operation.save')}</Button>
      </div>
    </Modal>
  )
app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx
@@ -30,24 +30,24 @@
        </div>
      }
      headerIcon={
        <div className='rounded-md p-1 shadow-xs'>
          <MessageClockCircle className='h-4 w-4 text-[#DD2590]' />
        <div className='p-1 rounded-md bg-white shadow-xs'>
          <MessageClockCircle className='w-4 h-4 text-[#DD2590]' />
        </div>}
      headerRight={
        <div className='flex items-center'>
          <div className='text-xs text-text-tertiary'>{t('appDebug.feature.conversationHistory.description')}</div>
          <div className='ml-3 h-[14px] w-[1px] bg-divider-regular'></div>
          <div className='text-xs text-gray-500'>{t('appDebug.feature.conversationHistory.description')}</div>
          <div className='ml-3 w-[1px] h-[14px] bg-gray-200'></div>
          <OperationBtn type="edit" onClick={onShowEditModal} />
        </div>
      }
      noBodySpacing
    >
      {showWarning && (
        <div className='flex justify-between rounded-b-xl bg-background-section-burn px-3 py-2 text-xs text-text-secondary'>
        <div className='flex justify-between py-2 px-3 rounded-b-xl bg-[#FFFAEB] text-xs text-gray-700'>
          <div>{t('appDebug.feature.conversationHistory.tip')}
            <a href={`${locale === LanguagesSupported[1]
              ? 'https://docs.dify.ai/zh-hans/learn-more/extended-reading/prompt-engineering/README'
              : 'https://docs.dify.ai/en/features/prompt-engineering'}`}
              ? 'https://docs.dify.ai/v/zh-hans/guides/application-design/prompt-engineering'
              : 'https://docs.dify.ai/features/prompt-engineering'}`}
            target='_blank' rel='noopener noreferrer'
            className='text-[#155EEF]'>{t('appDebug.feature.conversationHistory.learnMore')}
            </a>
app/components/app/configuration/config-prompt/index.tsx
@@ -8,7 +8,6 @@
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import SimplePromptInput from './simple-prompt-input'
import Button from '@/app/components/base/button'
import AdvancedMessageInput from '@/app/components/app/configuration/config-prompt/advanced-prompt-input'
import { PromptRole } from '@/models/debug'
import type { PromptItem, PromptVariable } from '@/models/debug'
@@ -156,12 +155,12 @@
        }
      </div>
      {(modelModeType === ModelModeType.chat && (currentAdvancedPrompt as PromptItem[]).length < MAX_PROMPT_MESSAGE_LENGTH) && (
        <Button
        <div
          onClick={handleAddMessage}
          className='mt-3 w-full'>
          <RiAddLine className='mr-2 h-4 w-4' />
          className='mt-3 flex items-center h-8 justify-center bg-gray-50 rounded-lg cursor-pointer text-[13px] font-medium text-gray-700 space-x-2'>
          <RiAddLine className='w-4 h-4' />
          <div>{t('appDebug.promptMode.operation.addMessage')}</div>
        </Button>
        </div>
      )}
    </div>
  )
app/components/app/configuration/config-prompt/message-type-selector.tsx
@@ -24,12 +24,12 @@
    <div className='relative left-[-8px]' ref={ref}>
      <div
        onClick={toggleShow}
        className={cn(showOption && 'bg-indigo-100', 'flex h-7 cursor-pointer items-center space-x-0.5 rounded-lg pl-1.5 pr-1 text-indigo-800')}>
        className={cn(showOption && 'bg-indigo-100', 'flex items-center h-7 pl-1.5 pr-1 space-x-0.5 rounded-lg cursor-pointer text-indigo-800')}>
        <div className='text-sm font-semibold uppercase'>{value}</div>
        <ChevronSelectorVertical className='h-3 w-3 ' />
        <ChevronSelectorVertical className='w-3 h-3 ' />
      </div>
      {showOption && (
        <div className='absolute top-[30px] z-10 rounded-lg border border-components-panel-border bg-components-panel-bg p-1 shadow-lg'>
        <div className='absolute z-10 top-[30px] p-1 border border-gray-200 shadow-lg rounded-lg bg-white'>
          {allTypes.map(type => (
            <div
              key={type}
@@ -37,7 +37,7 @@
                setHide()
                onChange(type)
              }}
              className='flex h-9 min-w-[44px] cursor-pointer items-center rounded-lg px-3 text-sm font-medium uppercase text-text-secondary hover:bg-state-base-hover'
              className='flex items-center h-9 min-w-[44px] px-3 rounded-lg cursor-pointer text-sm font-medium text-gray-700 uppercase hover:bg-gray-50'
            >{type}</div>
          ))
          }
app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.tsx
@@ -9,8 +9,8 @@
  height: number
  minHeight: number
  onHeightChange: (height: number) => void
  children: React.JSX.Element
  footer?: React.JSX.Element
  children: JSX.Element
  footer?: JSX.Element
  hideResize?: boolean
}
@@ -85,9 +85,9 @@
      {footer}
      {!hideResize && (
        <div
          className='absolute bottom-0 left-0 flex h-2 w-full cursor-row-resize justify-center'
          className='absolute bottom-0 left-0 w-full flex justify-center h-2 cursor-row-resize'
          onMouseDown={handleStartResize}>
          <div className='h-[3px] w-5 rounded-sm bg-gray-300'></div>
          <div className='w-5 h-[3px] rounded-sm bg-gray-300'></div>
        </div>
      )}
    </div>
app/components/app/configuration/config-prompt/simple-prompt-input.tsx
@@ -6,9 +6,10 @@
import produce from 'immer'
import { useContext } from 'use-context-selector'
import ConfirmAddVar from './confirm-add-var'
import s from './style.module.css'
import PromptEditorHeightResizeWrap from './prompt-editor-height-resize-wrap'
import cn from '@/utils/classnames'
import type { PromptVariable } from '@/models/debug'
import { type PromptVariable } from '@/models/debug'
import Tooltip from '@/app/components/base/tooltip'
import type { CompletionParams } from '@/types/app'
import { AppType } from '@/types/app'
@@ -27,7 +28,6 @@
import { PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER } from '@/app/components/base/prompt-editor/plugins/update-block'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { useFeaturesStore } from '@/app/components/base/features/hooks'
import { noop } from 'lodash-es'
export type ISimplePromptInput = {
  mode: AppType
@@ -48,6 +48,7 @@
  readonly = false,
  onChange,
  noTitle,
  gradientBorder,
  editorHeight: initEditorHeight,
  noResize,
}) => {
@@ -160,12 +161,12 @@
  const [editorHeight, setEditorHeight] = useState(minHeight)
  return (
    <div className={cn('relative rounded-xl bg-gradient-to-r from-components-input-border-active-prompt-1 to-components-input-border-active-prompt-2 p-0.5 shadow-xs')}>
      <div className='rounded-xl bg-background-section-burn'>
    <div className={cn((!readonly || gradientBorder) ? `${s.gradientBorder}` : 'bg-gray-50', ' relative shadow-md')}>
      <div className='rounded-xl bg-[#EEF4FF]'>
        {!noTitle && (
          <div className="flex h-11 items-center justify-between pl-3 pr-2.5">
          <div className="flex justify-between items-center h-11 pl-3 pr-6">
            <div className="flex items-center space-x-1">
              <div className='h2 system-sm-semibold-uppercase text-text-secondary'>{mode !== AppType.completion ? t('appDebug.chatSubTitle') : t('appDebug.completionSubTitle')}</div>
              <div className='h2'>{mode !== AppType.completion ? t('appDebug.chatSubTitle') : t('appDebug.completionSubTitle')}</div>
              {!readonly && (
                <Tooltip
                  popupContent={
@@ -185,14 +186,14 @@
        )}
        <PromptEditorHeightResizeWrap
          className='min-h-[228px] rounded-t-xl bg-background-default px-4 pt-2 text-sm text-text-secondary'
          className='px-4 pt-2 min-h-[228px] bg-white rounded-t-xl text-sm text-gray-700'
          height={editorHeight}
          minHeight={minHeight}
          onHeightChange={setEditorHeight}
          hideResize={noResize}
          footer={(
            <div className='flex rounded-b-xl bg-background-default pb-2 pl-4'>
              <div className="h-[18px] rounded-md bg-components-badge-bg-gray-soft px-1 text-xs leading-[18px] text-text-tertiary">{promptTemplate.length}</div>
            <div className='pl-4 pb-2 flex bg-white rounded-b-xl'>
              <div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{promptTemplate.length}</div>
            </div>
          )}
        >
@@ -234,15 +235,14 @@
                user: '',
                assistant: '',
              },
              onEditRole: noop,
              onEditRole: () => { },
            }}
            queryBlock={{
              show: false,
              selectable: !hasSetBlockStatus.query,
            }}
            onChange={(value) => {
              if (handleChange)
                handleChange(value, [])
              handleChange?.(value, [])
            }}
            onBlur={() => {
              handleChange(promptTemplate, getVars(promptTemplate))
app/components/app/configuration/config-prompt/style.module.css
app/components/app/configuration/config-var/config-modal/field.tsx
@@ -6,7 +6,7 @@
type Props = {
  className?: string
  title: string
  children: React.JSX.Element
  children: JSX.Element
}
const Field: FC<Props> = ({
@@ -16,7 +16,7 @@
}) => {
  return (
    <div className={cn(className)}>
      <div className='system-sm-semibold leading-8 text-text-secondary'>{title}</div>
      <div className='text-text-secondary system-sm-semibold leading-8'>{title}</div>
      <div>{children}</div>
    </div>
  )
app/components/app/configuration/config-var/config-modal/index.tsx
@@ -233,9 +233,9 @@
            />
          )}
          <div className='!mt-5 flex h-6 items-center space-x-2'>
          <div className='!mt-5 flex items-center h-6 space-x-2'>
            <Checkbox checked={tempPayload.required} onCheck={() => handlePayloadChange('required')(!tempPayload.required)} />
            <span className='system-sm-semibold text-text-secondary'>{t('appDebug.variableConfig.required')}</span>
            <span className='text-text-secondary system-sm-semibold'>{t('appDebug.variableConfig.required')}</span>
          </div>
        </div>
      </div>
app/components/app/configuration/config-var/config-select/index.tsx
@@ -1,10 +1,12 @@
'use client'
import type { FC } from 'react'
import React, { useState } from 'react'
import { RiAddLine, RiDeleteBinLine, RiDraggable } from '@remixicon/react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { PlusIcon } from '@heroicons/react/24/outline'
import { ReactSortable } from 'react-sortablejs'
import cn from '@/utils/classnames'
import RemoveIcon from '../../base/icons/remove-icon'
import s from './style.module.css'
export type Options = string[]
export type IConfigSelectProps = {
@@ -17,8 +19,6 @@
  onChange,
}) => {
  const { t } = useTranslation()
  const [focusID, setFocusID] = useState<number | null>(null)
  const [deletingID, setDeletingID] = useState<number | null>(null)
  const optionList = options.map((content, index) => {
    return ({
@@ -40,18 +40,15 @@
            animation={150}
          >
            {options.map((o, index) => (
              <div
                className={cn(
                  'group relative flex items-center rounded-lg border border-components-panel-border-subtle bg-components-panel-on-panel-item-bg pl-2.5 hover:bg-components-panel-on-panel-item-bg-hover',
                  focusID === index && 'border-components-input-border-active bg-components-input-bg-active hover:border-components-input-border-active hover:bg-components-input-bg-active',
                  deletingID === index && 'border-components-input-border-destructive bg-state-destructive-hover hover:border-components-input-border-destructive hover:bg-state-destructive-hover',
                )}
                key={index}
              >
                <RiDraggable className='handle h-4 w-4 cursor-grab text-text-quaternary' />
              <div className={`${s.inputWrap} relative`} key={index}>
                <div className='handle flex items-center justify-center w-4 h-4 cursor-grab'>
                  <svg width="6" height="10" viewBox="0 0 6 10" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <path fillRule="evenodd" clipRule="evenodd" d="M1 2C1.55228 2 2 1.55228 2 1C2 0.447715 1.55228 0 1 0C0.447715 0 0 0.447715 0 1C0 1.55228 0.447715 2 1 2ZM1 6C1.55228 6 2 5.55228 2 5C2 4.44772 1.55228 4 1 4C0.447715 4 0 4.44772 0 5C0 5.55228 0.447715 6 1 6ZM6 1C6 1.55228 5.55228 2 5 2C4.44772 2 4 1.55228 4 1C4 0.447715 4.44772 0 5 0C5.55228 0 6 0.447715 6 1ZM5 6C5.55228 6 6 5.55228 6 5C6 4.44772 5.55228 4 5 4C4.44772 4 4 4.44772 4 5C4 5.55228 4.44772 6 5 6ZM2 9C2 9.55229 1.55228 10 1 10C0.447715 10 0 9.55229 0 9C0 8.44771 0.447715 8 1 8C1.55228 8 2 8.44771 2 9ZM5 10C5.55228 10 6 9.55229 6 9C6 8.44771 5.55228 8 5 8C4.44772 8 4 8.44771 4 9C4 9.55229 4.44772 10 5 10Z" fill="#98A2B3" />
                  </svg>
                </div>
                <input
                  key={index}
                  type='input'
                  type="input"
                  value={o || ''}
                  onChange={(e) => {
                    const value = e.target.value
@@ -62,21 +59,14 @@
                      return item
                    }))
                  }}
                  className={'h-9 w-full grow cursor-pointer overflow-x-auto rounded-lg border-0 bg-transparent pl-1.5 pr-8 text-sm leading-9 text-text-secondary focus:outline-none'}
                  onFocus={() => setFocusID(index)}
                  onBlur={() => setFocusID(null)}
                  className={'w-full pl-1.5 pr-8 text-sm leading-9 text-gray-900 border-0 grow h-9 bg-transparent focus:outline-none cursor-pointer'}
                />
                <div
                  role='button'
                  className='absolute right-1.5 top-1/2 block translate-y-[-50%] cursor-pointer rounded-md p-1 text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive'
                <RemoveIcon
                  className={`${s.deleteBtn} absolute top-1/2 translate-y-[-50%] right-1.5 items-center justify-center w-6 h-6 rounded-md cursor-pointer hover:bg-[#FEE4E2]`}
                  onClick={() => {
                    onChange(options.filter((_, i) => index !== i))
                  }}
                  onMouseEnter={() => setDeletingID(index)}
                  onMouseLeave={() => setDeletingID(null)}
                >
                  <RiDeleteBinLine className='h-3.5 w-3.5' />
                </div>
                />
              </div>
            ))}
          </ReactSortable>
@@ -85,9 +75,9 @@
      <div
        onClick={() => { onChange([...options, '']) }}
        className='mt-1 flex h-9 cursor-pointer items-center gap-2 rounded-lg bg-components-button-tertiary-bg px-3  text-components-button-tertiary-text hover:bg-components-button-tertiary-bg-hover'>
        <RiAddLine className='h-4 w-4' />
        <div className='system-sm-medium text-[13px]'>{t('appDebug.variableConfig.addOption')}</div>
        className='flex items-center h-9 px-3 gap-2 rounded-lg cursor-pointer text-gray-400  bg-gray-100'>
        <PlusIcon width={16} height={16}></PlusIcon>
        <div className='text-gray-500 text-[13px]'>{t('appDebug.variableConfig.addOption')}</div>
      </div>
    </div>
  )
app/components/app/configuration/config-var/config-select/style.module.css
New file
@@ -0,0 +1,21 @@
.inputWrap {
  display: flex;
  align-items: center;
  border-radius: 8px;
  border: 1px solid #EAECF0;
  padding-left: 10px;
  cursor: pointer;
}
.deleteBtn {
  display: none;
  display: flex;
}
.inputWrap:hover {
  box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
}
.inputWrap:hover .deleteBtn {
  display: flex;
}
app/components/app/configuration/config-var/config-string/index.tsx
@@ -28,7 +28,7 @@
        min={1}
        value={value || ''}
        onChange={(e) => {
          let value = Number.parseInt(e.target.value, 10)
          let value = parseInt(e.target.value, 10)
          if (value > maxLength)
            value = maxLength
app/components/app/configuration/config-var/index.tsx
@@ -3,17 +3,26 @@
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import type { Timeout } from 'ahooks/lib/useRequest/src/types'
import { useContext } from 'use-context-selector'
import produce from 'immer'
import {
  RiDeleteBinLine,
} from '@remixicon/react'
import Panel from '../base/feature-panel'
import EditModal from './config-modal'
import VarItem from './var-item'
import IconTypeIcon from './input-type-icon'
import type { IInputTypeIconProps } from './input-type-icon'
import s from './style.module.css'
import SelectVarType from './select-var-type'
import { BracketsX as VarIcon } from '@/app/components/base/icons/src/vender/line/development'
import Tooltip from '@/app/components/base/tooltip'
import type { PromptVariable } from '@/models/debug'
import { DEFAULT_VALUE_MAX_LEN } from '@/config'
import { getNewVar } from '@/utils/var'
import { DEFAULT_VALUE_MAX_LEN, getMaxVarNameLength } from '@/config'
import { checkKeys, getNewVar } from '@/utils/var'
import Switch from '@/app/components/base/switch'
import Toast from '@/app/components/base/toast'
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
import Confirm from '@/app/components/base/confirm'
import ConfigContext from '@/context/debug-configuration'
import { AppType } from '@/types/app'
@@ -41,6 +50,8 @@
  onPromptVariablesChange?: (promptVariables: PromptVariable[]) => void
}
let conflictTimer: Timeout
const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVariablesChange }) => {
  const { t } = useTranslation()
  const {
@@ -50,6 +61,19 @@
  const { eventEmitter } = useEventEmitterContextContext()
  const hasVar = promptVariables.length > 0
  const updatePromptVariable = (key: string, updateKey: string, newValue: string | boolean) => {
    const newPromptVariables = promptVariables.map((item) => {
      if (item.key === key) {
        return {
          ...item,
          [updateKey]: newValue,
        }
      }
      return item
    })
    onPromptVariablesChange?.(newPromptVariables)
  }
  const [currIndex, setCurrIndex] = useState<number>(-1)
  const currItem = currIndex !== -1 ? promptVariables[currIndex] : null
  const currItemToEdit: InputVar | null = (() => {
@@ -78,6 +102,55 @@
      if (payload.type !== InputVarType.select)
        delete draft[currIndex].options
    })
    onPromptVariablesChange?.(newPromptVariables)
  }
  const updatePromptKey = (index: number, newKey: string) => {
    clearTimeout(conflictTimer)
    const { isValid, errorKey, errorMessageKey } = checkKeys([newKey], true)
    if (!isValid) {
      Toast.notify({
        type: 'error',
        message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }),
      })
      return
    }
    const newPromptVariables = promptVariables.map((item, i) => {
      if (i === index) {
        return {
          ...item,
          key: newKey,
        }
      }
      return item
    })
    conflictTimer = setTimeout(() => {
      const isKeyExists = promptVariables.some(item => item.key?.trim() === newKey.trim())
      if (isKeyExists) {
        Toast.notify({
          type: 'error',
          message: t('appDebug.varKeyError.keyAlreadyExists', { key: newKey }),
        })
      }
    }, 1000)
    onPromptVariablesChange?.(newPromptVariables)
  }
  const updatePromptNameIfNameEmpty = (index: number, newKey: string) => {
    if (!newKey)
      return
    const newPromptVariables = promptVariables.map((item, i) => {
      if (i === index && !item.name) {
        return {
          ...item,
          name: newKey,
        }
      }
      return item
    })
    onPromptVariablesChange?.(newPromptVariables)
@@ -200,6 +273,9 @@
  return (
    <Panel
      className="mt-2"
      headerIcon={
        <VarIcon className='w-4 h-4 text-primary-500' />
      }
      title={
        <div className='flex items-center'>
          <div className='mr-1'>{t('appDebug.variableTitle')}</div>
@@ -215,27 +291,87 @@
        </div>
      }
      headerRight={!readonly ? <SelectVarType onChange={handleAddVar} /> : null}
      noBodySpacing
    >
      {!hasVar && (
        <div className='mt-1 px-3 pb-3'>
          <div className='pb-1 pt-2 text-xs text-text-tertiary'>{t('appDebug.notSetVar')}</div>
        </div>
        <div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.notSetVar')}</div>
      )}
      {hasVar && (
        <div className='mt-1 px-3 pb-3'>
        <div className='rounded-lg border border-gray-200 bg-white overflow-x-auto'>
          <table className={`${s.table} min-w-[440px] w-full max-w-full border-collapse border-0 rounded-lg text-sm`}>
            <thead className="border-b  border-gray-200 text-gray-500 text-xs font-medium">
              <tr className='uppercase'>
                <td>{t('appDebug.variableTable.key')}</td>
                <td>{t('appDebug.variableTable.name')}</td>
                {!readonly && (
                  <>
                    <td>{t('appDebug.variableTable.optional')}</td>
                    <td>{t('appDebug.variableTable.action')}</td>
                  </>
                )}
              </tr>
            </thead>
            <tbody className="text-gray-700">
          {promptVariables.map(({ key, name, type, required, config, icon, icon_background }, index) => (
            <VarItem
              key={index}
              readonly={readonly}
              name={key}
              label={name}
              required={!!required}
              type={type}
              onEdit={() => handleConfig({ type, key, index, name, config, icon, icon_background })}
              onRemove={() => handleRemoveVar(index)}
                <tr key={index} className="h-9 leading-9">
                  <td className="w-[160px] border-b border-gray-100 pl-3">
                    <div className='flex items-center space-x-1'>
                      <IconTypeIcon type={type as IInputTypeIconProps['type']} className='text-gray-400' />
                      {!readonly
                        ? (
                          <input
                            type="text"
                            placeholder="key"
                            value={key}
                            onChange={e => updatePromptKey(index, e.target.value)}
                            onBlur={e => updatePromptNameIfNameEmpty(index, e.target.value)}
                            maxLength={getMaxVarNameLength(name)}
                            className="h-6 leading-6 block w-full rounded-md border-0 py-1.5 text-gray-900  placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
            />
                        )
                        : (
                          <div className='h-6 leading-6 text-[13px] text-gray-700'>{key}</div>
                        )}
                    </div>
                  </td>
                  <td className="py-1 border-b border-gray-100">
                    {!readonly
                      ? (
                        <input
                          type="text"
                          placeholder={key}
                          value={name}
                          onChange={e => updatePromptVariable(key, 'name', e.target.value)}
                          maxLength={getMaxVarNameLength(name)}
                          className="h-6 leading-6 block w-full rounded-md border-0 py-1.5 text-gray-900  placeholder:text-gray-400 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
                        />)
                      : (
                        <div className='h-6 leading-6 text-[13px] text-gray-700'>{name}</div>
                      )}
                  </td>
                  {!readonly && (
                    <>
                      <td className='w-[84px] border-b border-gray-100'>
                        <div className='flex items-center h-full'>
                          <Switch defaultValue={!required} size='md' onChange={value => updatePromptVariable(key, 'required', !value)} />
                        </div>
                      </td>
                      <td className='w-20  border-b border-gray-100'>
                        <div className='flex h-full items-center space-x-1'>
                          <div className=' p-1 rounded-md hover:bg-black/5 w-6 h-6 cursor-pointer' onClick={() => handleConfig({ type, key, index, name, config, icon, icon_background })}>
                            <Settings01 className='w-4 h-4 text-gray-500' />
                          </div>
                          <div className=' p-1 rounded-md hover:bg-black/5 w-6 h-6 cursor-pointer' onClick={() => handleRemoveVar(index)} >
                            <RiDeleteBinLine className='w-4 h-4 text-gray-500' />
                          </div>
                        </div>
                      </td>
                    </>
                  )}
                </tr>
          ))}
            </tbody>
          </table>
        </div>
      )}
app/components/app/configuration/config-var/select-type-item/index.tsx
@@ -27,12 +27,12 @@
  return (
    <div
      className={cn(
        'flex h-[58px] flex-col items-center justify-center space-y-1 rounded-lg border border-components-option-card-option-border bg-components-option-card-option-bg text-text-secondary',
        selected ? 'system-xs-medium border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg shadow-xs' : ' system-xs-regular cursor-pointer hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs')}
        'flex flex-col justify-center items-center h-[58px] rounded-lg border border-components-option-card-option-border bg-components-option-card-option-bg space-y-1',
        selected ? 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg shadow-xs system-xs-medium' : ' hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs cursor-pointer system-xs-regular')}
      onClick={onClick}
    >
      <div className='shrink-0'>
        <InputVarTypeIcon type={type} className='h-5 w-5' />
        <InputVarTypeIcon type={type} className='w-5 h-5' />
      </div>
      <span>{typeName}</span>
    </div>
app/components/app/configuration/config-var/select-var-type.tsx
@@ -28,11 +28,11 @@
const SelectItem: FC<ItemProps> = ({ text, type, value, Icon, onClick }) => {
  return (
    <div
      className='flex h-8 cursor-pointer items-center rounded-lg px-3 hover:bg-gray-50'
      className='flex items-center px-3 h-8 rounded-lg hover:bg-gray-50 cursor-pointer'
      onClick={() => onClick(value)}
    >
      {Icon ? <Icon className='h-4 w-4 text-gray-500' /> : <InputVarTypeIcon type={type!} className='h-4 w-4 text-gray-500' />}
      <div className='ml-2 truncate text-xs text-gray-600'>{text}</div>
      {Icon ? <Icon className='w-4 h-4 text-gray-500' /> : <InputVarTypeIcon type={type!} className='w-4 h-4 text-gray-500' />}
      <div className='ml-2 text-xs text-gray-600 truncate'>{text}</div>
    </div>
  )
}
@@ -60,7 +60,7 @@
        <OperationBtn type='add' className={cn(open && 'bg-gray-200')} />
      </PortalToFollowElemTrigger>
      <PortalToFollowElemContent style={{ zIndex: 1000 }}>
        <div className='min-w-[192px] rounded-lg border border-gray-200 bg-white shadow-lg'>
        <div className='bg-white border border-gray-200 shadow-lg rounded-lg min-w-[192px]'>
          <div className='p-1'>
            <SelectItem type={InputVarType.textInput} value='string' text={t('appDebug.variableConfig.string')} onClick={handleChange}></SelectItem>
            <SelectItem type={InputVarType.paragraph} value='paragraph' text={t('appDebug.variableConfig.paragraph')} onClick={handleChange}></SelectItem>
app/components/app/configuration/config-var/style.module.css
New file
@@ -0,0 +1,12 @@
.table td {
  padding-left: 12px;
}
.table thead td {
  height: 33px;
  line-height: 33px;
}
.table tbody tr:last-child td {
  border-bottom: none;
}
app/components/app/configuration/config-vision/index.tsx
@@ -57,14 +57,14 @@
    return null
  return (
    <div className='mt-2 flex items-center gap-2 rounded-xl border-l-[0.5px] border-t-[0.5px] border-effects-highlight bg-background-section-burn p-2'>
    <div className='mt-2 flex items-center gap-2 p-2 rounded-xl border-t-[0.5px] border-l-[0.5px] bg-background-section-burn'>
      <div className='shrink-0 p-1'>
        <div className='rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-indigo-indigo-600 p-1 shadow-xs'>
          <Vision className='h-4 w-4 text-text-primary-on-surface' />
        <div className='p-1 rounded-lg border-[0.5px] border-divider-subtle shadow-xs bg-util-colors-indigo-indigo-600'>
          <Vision className='w-4 h-4 text-text-primary-on-surface' />
        </div>
      </div>
      <div className='flex grow items-center'>
        <div className='system-sm-semibold mr-1 text-text-secondary'>{t('appDebug.vision.name')}</div>
      <div className='grow flex items-center'>
        <div className='mr-1 text-text-secondary system-sm-semibold'>{t('appDebug.vision.name')}</div>
        <Tooltip
          popupContent={
            <div className='w-[180px]' >
@@ -73,7 +73,7 @@
          }
        />
      </div>
      <div className='flex shrink-0 items-center'>
      <div className='shrink-0 flex items-center'>
        {/* <div className='mr-2 flex items-center gap-0.5'>
          <div className='text-text-tertiary system-xs-medium-uppercase'>{t('appDebug.vision.visionSettings.resolution')}</div>
          <Tooltip
@@ -99,7 +99,7 @@
          />
        </div> */}
        <ParamConfig />
        <div className='ml-1 mr-3 h-3.5 w-[1px] bg-divider-regular'></div>
        <div className='ml-1 mr-3 w-[1px] h-3.5 bg-divider-subtle'></div>
        <Switch
          defaultValue={isImageEnabled}
          onChange={handleChange}
app/components/app/configuration/config-vision/param-config-content.tsx
@@ -41,11 +41,11 @@
  return (
    <div>
      <div className='text-base font-semibold leading-6 text-text-primary'>{t('appDebug.vision.visionSettings.title')}</div>
      <div className='space-y-6 pt-3'>
      <div className='leading-6 text-base font-semibold text-gray-800'>{t('appDebug.vision.visionSettings.title')}</div>
      <div className='pt-3 space-y-6'>
        <div>
          <div className='mb-2 flex items-center  space-x-1'>
            <div className='text-[13px] font-semibold leading-[18px] text-text-secondary'>{t('appDebug.vision.visionSettings.resolution')}</div>
            <div className='leading-[18px] text-[13px] font-semibold text-gray-800'>{t('appDebug.vision.visionSettings.resolution')}</div>
            <Tooltip
              popupContent={
                <div className='w-[180px]' >
@@ -78,7 +78,7 @@
          </div>
        </div>
        <div>
          <div className='mb-2 text-[13px] font-semibold leading-[18px] text-text-secondary'>{t('appDebug.vision.visionSettings.uploadMethod')}</div>
          <div className='mb-2 leading-[18px] text-[13px] font-semibold text-gray-800'>{t('appDebug.vision.visionSettings.uploadMethod')}</div>
          <div className='flex items-center gap-1'>
            <OptionCard
              className='grow'
app/components/app/configuration/config-vision/param-config.tsx
@@ -2,15 +2,14 @@
import type { FC } from 'react'
import { memo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { RiSettings2Line } from '@remixicon/react'
import ParamConfigContent from './param-config-content'
import Button from '@/app/components/base/button'
import cn from '@/utils/classnames'
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
import {
  PortalToFollowElem,
  PortalToFollowElemContent,
  PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import cn from '@/utils/classnames'
const ParamsConfig: FC = () => {
  const { t } = useTranslation()
@@ -26,13 +25,13 @@
      }}
    >
      <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
        <Button variant='ghost' size='small' className={cn('')}>
          <RiSettings2Line className='h-3.5 w-3.5' />
          <div className='ml-1'>{t('appDebug.voice.settings')}</div>
        </Button>
        <div className={cn('flex items-center rounded-md h-7 px-3 space-x-1 text-text-tertiary cursor-pointer hover:bg-gray-200', open && 'bg-gray-200')}>
          <Settings01 className='w-3.5 h-3.5 ' />
          <div className='ml-1 leading-[18px] text-xs font-medium '>{t('appDebug.voice.settings')}</div>
        </div>
      </PortalToFollowElemTrigger>
      <PortalToFollowElemContent style={{ zIndex: 50 }}>
        <div className='w-80 space-y-3 rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg p-4 shadow-lg sm:w-[412px]'>
        <div className='w-80 sm:w-[412px] p-4 bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg space-y-3'>
          <ParamConfigContent />
        </div>
      </PortalToFollowElemContent>
app/components/app/configuration/config/agent-setting-button.tsx
@@ -2,9 +2,9 @@
import type { FC } from 'react'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { RiSettings2Line } from '@remixicon/react'
import AgentSetting from './agent/agent-setting'
import Button from '@/app/components/base/button'
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
import type { AgentConfig } from '@/models/debug'
type Props = {
@@ -25,8 +25,8 @@
  return (
    <>
      <Button onClick={() => setIsShowAgentSetting(true)} className='mr-2 shrink-0'>
        <RiSettings2Line className='mr-1 h-4 w-4 text-text-tertiary' />
      <Button onClick={() => setIsShowAgentSetting(true)} className='shrink-0 mr-2'>
        <Settings01 className='mr-1 w-4 h-4 text-gray-500' />
        {t('appDebug.agent.setting.name')}
      </Button>
      {isShowAgentSetting && (
app/components/app/configuration/config/agent/agent-setting/index.tsx
@@ -1,16 +1,15 @@
'use client'
import type { FC } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { RiCloseLine } from '@remixicon/react'
import { useClickAway } from 'ahooks'
import ItemPanel from './item-panel'
import Button from '@/app/components/base/button'
import { CuteRobot } from '@/app/components/base/icons/src/vender/solid/communication'
import { Unblur } from '@/app/components/base/icons/src/vender/solid/education'
import Slider from '@/app/components/base/slider'
import type { AgentConfig } from '@/models/debug'
import { DEFAULT_AGENT_PROMPT, MAX_ITERATIONS_NUM } from '@/config'
import { DEFAULT_AGENT_PROMPT } from '@/config'
type Props = {
  isChatModel: boolean
@@ -21,6 +20,7 @@
}
const maxIterationsMin = 1
const maxIterationsMax = 5
const AgentSetting: FC<Props> = ({
  isChatModel,
@@ -31,65 +31,52 @@
}) => {
  const { t } = useTranslation()
  const [tempPayload, setTempPayload] = useState(payload)
  const ref = useRef(null)
  const [mounted, setMounted] = useState(false)
  useClickAway(() => {
    if (mounted)
      onCancel()
  }, ref)
  useEffect(() => {
    setMounted(true)
  }, [])
  const handleSave = () => {
    onSave(tempPayload)
  }
  return (
    <div className='fixed inset-0 z-[100] flex justify-end overflow-hidden p-2'
    <div className='fixed z-[100] inset-0 overflow-hidden flex justify-end p-2'
      style={{
        backgroundColor: 'rgba(16, 24, 40, 0.20)',
      }}
    >
      <div
        ref={ref}
        className='flex h-full w-[640px] flex-col overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl'
        className='w-[640px] flex flex-col h-full overflow-hidden bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl'
      >
        <div className='flex h-14 shrink-0 items-center justify-between border-b border-divider-regular pl-6 pr-5'>
          <div className='flex flex-col text-base font-semibold text-text-primary'>
        <div className='shrink-0 flex justify-between items-center pl-6 pr-5 h-14 border-b border-b-gray-100'>
          <div className='flex flex-col text-base font-semibold text-gray-900'>
            <div className='leading-6'>{t('appDebug.agent.setting.name')}</div>
          </div>
          <div className='flex items-center'>
            <div
              onClick={onCancel}
              className='flex h-6 w-6 cursor-pointer items-center justify-center'
              className='flex justify-center items-center w-6 h-6 cursor-pointer'
            >
              <RiCloseLine className='h-4 w-4 text-text-tertiary' />
              <RiCloseLine className='w-4 h-4 text-gray-500' />
            </div>
          </div>
        </div>
        {/* Body */}
        <div className='grow overflow-y-auto border-b p-6 pb-[68px] pt-5' style={{
        <div className='grow p-6 pt-5 border-b overflow-y-auto pb-[68px]' style={{
          borderBottom: 'rgba(0, 0, 0, 0.05)',
        }}>
          {/* Agent Mode */}
          <ItemPanel
            className='mb-4'
            icon={
              <CuteRobot className='h-4 w-4 text-indigo-600' />
              <CuteRobot className='w-4 h-4 text-indigo-600' />
            }
            name={t('appDebug.agent.agentMode')}
            description={t('appDebug.agent.agentModeDes')}
          >
            <div className='text-[13px] font-medium leading-[18px] text-text-primary'>{isFunctionCall ? t('appDebug.agent.agentModeType.functionCall') : t('appDebug.agent.agentModeType.ReACT')}</div>
            <div className='leading-[18px] text-[13px] font-medium text-gray-900'>{isFunctionCall ? t('appDebug.agent.agentModeType.functionCall') : t('appDebug.agent.agentModeType.ReACT')}</div>
          </ItemPanel>
          <ItemPanel
            className='mb-4'
            icon={
              <Unblur className='h-4 w-4 text-[#FB6514]' />
              <Unblur className='w-4 h-4 text-[#FB6514]' />
            }
            name={t('appDebug.agent.setting.maximumIterations.name')}
            description={t('appDebug.agent.setting.maximumIterations.description')}
@@ -98,7 +85,7 @@
              <Slider
                className='mr-3 w-[156px]'
                min={maxIterationsMin}
                max={MAX_ITERATIONS_NUM}
                max={maxIterationsMax}
                value={tempPayload.max_iteration}
                onChange={(value) => {
                  setTempPayload({
@@ -111,16 +98,16 @@
              <input
                type="number"
                min={maxIterationsMin}
                max={MAX_ITERATIONS_NUM} step={1}
                className="block h-7 w-11 rounded-lg border-0 bg-components-input-bg-normal px-1.5 pl-1 leading-7 text-text-primary placeholder:text-text-tertiary focus:ring-1 focus:ring-inset focus:ring-primary-600"
                max={maxIterationsMax} step={1}
                className="block w-11 h-7 leading-7 rounded-lg border-0 pl-1 px-1.5 bg-gray-100 text-gray-900  placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-primary-600"
                value={tempPayload.max_iteration}
                onChange={(e) => {
                  let value = Number.parseInt(e.target.value, 10)
                  let value = parseInt(e.target.value, 10)
                  if (value < maxIterationsMin)
                    value = maxIterationsMin
                  if (value > MAX_ITERATIONS_NUM)
                    value = MAX_ITERATIONS_NUM
                  if (value > maxIterationsMax)
                    value = maxIterationsMax
                  setTempPayload({
                    ...tempPayload,
                    max_iteration: value,
@@ -130,20 +117,23 @@
          </ItemPanel>
          {!isFunctionCall && (
            <div className='rounded-xl bg-background-section-burn py-2 shadow-xs'>
              <div className='flex h-8 items-center px-4 text-sm font-semibold leading-6 text-text-secondary'>{t('tools.builtInPromptTitle')}</div>
              <div className='h-[396px] overflow-y-auto whitespace-pre-line px-4 text-sm font-normal leading-5 text-text-secondary'>
            <div className='py-2 bg-gray-50 rounded-xl shadow-xs'>
              <div className='flex items-center h-8 px-4 leading-6 text-sm font-semibold text-gray-700'>{t('tools.builtInPromptTitle')}</div>
              <div className='h-[396px] px-4 overflow-y-auto leading-5 text-sm font-normal text-gray-700 whitespace-pre-line'>
                {isChatModel ? DEFAULT_AGENT_PROMPT.chat : DEFAULT_AGENT_PROMPT.completion}
              </div>
              <div className='px-4'>
                <div className='inline-flex h-5 items-center rounded-md bg-components-input-bg-normal px-1 text-xs font-medium leading-[18px] text-text-tertiary'>{(isChatModel ? DEFAULT_AGENT_PROMPT.chat : DEFAULT_AGENT_PROMPT.completion).length}</div>
                <div className='inline-flex items-center h-5 px-1 rounded-md bg-gray-100 leading-[18px] text-xs font-medium text-gray-500'>{(isChatModel ? DEFAULT_AGENT_PROMPT.chat : DEFAULT_AGENT_PROMPT.completion).length}</div>
              </div>
            </div>
          )}
        </div>
        <div
          className='sticky bottom-0 z-[5] flex w-full justify-end border-t border-divider-regular bg-background-section-burn px-6 py-4'
          className='sticky z-[5] bottom-0 w-full flex justify-end py-4 px-6 border-t bg-white '
          style={{
            borderColor: 'rgba(0, 0, 0, 0.05)',
          }}
        >
          <Button
            onClick={onCancel}
app/components/app/configuration/config/agent/agent-setting/item-panel.tsx
@@ -5,10 +5,10 @@
import Tooltip from '@/app/components/base/tooltip'
type Props = {
  className?: string
  icon: React.JSX.Element
  icon: JSX.Element
  name: string
  description: string
  children: React.JSX.Element
  children: JSX.Element
}
const ItemPanel: FC<Props> = ({
@@ -19,10 +19,10 @@
  children,
}) => {
  return (
    <div className={cn(className, 'flex h-12 items-center justify-between rounded-lg bg-background-section-burn px-3')}>
    <div className={cn(className, 'flex justify-between items-center h-12 px-3 rounded-lg bg-gray-50')}>
      <div className='flex items-center'>
        {icon}
        <div className='ml-3 mr-1 text-sm font-semibold leading-6 text-text-secondary'>{name}</div>
        <div className='ml-3 mr-1 leading-6 text-sm font-semibold text-gray-800'>{name}</div>
        <Tooltip
          popupContent={
            <div className='w-[180px]'>
app/components/app/configuration/config/agent/agent-tools/index.tsx
@@ -1,24 +1,21 @@
'use client'
import type { FC } from 'react'
import React, { useMemo, useState } from 'react'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import copy from 'copy-to-clipboard'
import produce from 'immer'
import {
  RiDeleteBinLine,
  RiEqualizer2Line,
  RiInformation2Line,
  RiHammerFill,
} from '@remixicon/react'
import { useFormattingChangedDispatcher } from '../../../debug/hooks'
import SettingBuiltInTool from './setting-built-in-tool'
import cn from '@/utils/classnames'
import Panel from '@/app/components/app/configuration/base/feature-panel'
import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general'
import OperationBtn from '@/app/components/app/configuration/base/operation-btn'
import AppIcon from '@/app/components/base/app-icon'
import Button from '@/app/components/base/button'
import Indicator from '@/app/components/header/indicator'
import Switch from '@/app/components/base/switch'
import Toast from '@/app/components/base/toast'
import ConfigContext from '@/context/debug-configuration'
import type { AgentTool } from '@/types/app'
import { type Collection, CollectionType } from '@/app/components/tools/types'
@@ -26,12 +23,7 @@
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
import Tooltip from '@/app/components/base/tooltip'
import { DefaultToolIcon } from '@/app/components/base/icons/src/public/other'
import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials'
import { updateBuiltInToolCredential } from '@/service/tools'
import cn from '@/utils/classnames'
import ToolPicker from '@/app/components/workflow/block-selector/tool-picker'
import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types'
import { canFindTool } from '@/utils'
import AddToolModal from '@/app/components/tools/add-tool-modal'
type AgentToolWithMoreInfo = AgentTool & { icon: any; collection?: Collection } | null
const AgentTools: FC = () => {
@@ -41,19 +33,9 @@
  const formattingChangedDispatcher = useFormattingChangedDispatcher()
  const [currentTool, setCurrentTool] = useState<AgentToolWithMoreInfo>(null)
  const currentCollection = useMemo(() => {
    if (!currentTool) return null
    const collection = collectionList.find(collection => canFindTool(collection.id, currentTool?.provider_id) && collection.type === currentTool?.provider_type)
    return collection
  }, [currentTool, collectionList])
  const [isShowSettingTool, setIsShowSettingTool] = useState(false)
  const [isShowSettingAuth, setShowSettingAuth] = useState(false)
  const tools = (modelConfig?.agentConfig?.tools as AgentTool[] || []).map((item) => {
    const collection = collectionList.find(
      collection =>
        canFindTool(collection.id, item.provider_id)
        && collection.type === item.provider_type,
    )
    const collection = collectionList.find(collection => collection.id === item.provider_id && collection.type === item.provider_type)
    const icon = collection?.icon
    return {
      ...item,
@@ -73,40 +55,14 @@
    formattingChangedDispatcher()
  }
  const handleToolAuthSetting = (value: AgentToolWithMoreInfo) => {
    const newModelConfig = produce(modelConfig, (draft) => {
      const tool = (draft.agentConfig.tools).find((item: any) => item.provider_id === value?.collection?.id && item.tool_name === value?.tool_name)
      if (tool)
        (tool as AgentTool).notAuthor = false
    })
    setModelConfig(newModelConfig)
    setIsShowSettingTool(false)
    formattingChangedDispatcher()
  }
  const [isDeleting, setIsDeleting] = useState<number>(-1)
  const handleSelectTool = (tool: ToolDefaultValue) => {
    const newModelConfig = produce(modelConfig, (draft) => {
      draft.agentConfig.tools.push({
        provider_id: tool.provider_id,
        provider_type: tool.provider_type as CollectionType,
        provider_name: tool.provider_name,
        tool_name: tool.tool_name,
        tool_label: tool.tool_label,
        tool_parameters: tool.params,
        notAuthor: !tool.is_team_authorization,
        enabled: true,
      })
    })
    setModelConfig(newModelConfig)
  }
  return (
    <>
      <Panel
        className={cn('mt-2', tools.length === 0 && 'pb-2')}
        className="mt-2"
        noBodySpacing={tools.length === 0}
        headerIcon={
          <RiHammerFill className='w-4 h-4 text-primary-500' />
        }
        title={
          <div className='flex items-center'>
            <div className='mr-1'>{t('appDebug.agent.tools.name')}</div>
@@ -121,131 +77,113 @@
        }
        headerRight={
          <div className='flex items-center'>
            <div className='text-xs font-normal leading-[18px] text-text-tertiary'>{tools.filter(item => !!item.enabled).length}/{tools.length}&nbsp;{t('appDebug.agent.tools.enabled')}</div>
            <div className='leading-[18px] text-xs font-normal text-gray-500'>{tools.filter((item: any) => !!item.enabled).length}/{tools.length}&nbsp;{t('appDebug.agent.tools.enabled')}</div>
            {tools.length < MAX_TOOLS_NUM && (
              <>
                <div className='ml-3 mr-1 h-3.5 w-px bg-divider-regular'></div>
                <ToolPicker
                  trigger={<OperationBtn type="add" />}
                  isShow={isShowChooseTool}
                  onShowChange={setIsShowChooseTool}
                  disabled={false}
                  supportAddCustomTool
                  onSelect={handleSelectTool}
                  selectedTools={tools}
                />
                <div className='ml-3 mr-1 h-3.5 w-px bg-gray-200'></div>
                <OperationBtn type="add" onClick={() => setIsShowChooseTool(true)} />
              </>
            )}
          </div>
        }
      >
        <div className='grid grid-cols-1 flex-wrap items-center justify-between gap-1 2xl:grid-cols-2'>
        <div className='grid gap-1 grid-cols-1 2xl:grid-cols-2 items-center flex-wrap justify-between'>
          {tools.map((item: AgentTool & { icon: any; collection?: Collection }, index) => (
            <div key={index}
              className={cn(
                'cursor group relative flex w-full items-center justify-between rounded-lg border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg p-1.5 pr-2 shadow-xs last-of-type:mb-0 hover:bg-components-panel-on-panel-item-bg-hover hover:shadow-sm',
                isDeleting === index && 'border-state-destructive-border hover:bg-state-destructive-hover',
              )}
              className={cn((item.isDeleted || item.notAuthor) ? 'bg-white/50' : 'bg-white', (item.enabled && !item.isDeleted && !item.notAuthor) && 'shadow-xs', index > 1 && 'mt-1', 'group relative flex justify-between items-center last-of-type:mb-0  pl-2.5 py-2 pr-3 w-full  rounded-lg border-[0.5px] border-gray-200 ')}
            >
              <div className='flex w-0 grow items-center'>
                {item.isDeleted && <DefaultToolIcon className='h-5 w-5' />}
                {!item.isDeleted && (
                  <div className={cn((item.notAuthor || !item.enabled) && 'opacity-50')}>
                    {typeof item.icon === 'string' && <div className='h-5 w-5 rounded-md bg-cover bg-center' style={{ backgroundImage: `url(${item.icon})` }} />}
                    {typeof item.icon !== 'string' && <AppIcon className='rounded-md' size='xs' icon={item.icon?.content} background={item.icon?.background} />}
                  </div>
                )}
              <div className='grow w-0 flex items-center'>
                {(item.isDeleted || item.notAuthor)
                  ? (
                    <DefaultToolIcon className='w-6 h-6' />
                  )
                  : (
                    typeof item.icon === 'string'
                      ? (
                <div
                  className={cn(
                    'system-xs-regular ml-1.5 flex w-0 grow items-center truncate',
                    (item.isDeleted || item.notAuthor || !item.enabled) ? 'opacity-50' : '',
                  )}
                          className='w-6 h-6 bg-cover bg-center rounded-md'
                          style={{
                            backgroundImage: `url(${item.icon})`,
                          }}
                        ></div>
                      )
                      : (
                        <AppIcon
                          className='rounded-md'
                          size='tiny'
                          icon={item.icon?.content}
                          background={item.icon?.background}
                        />
                      ))}
                <div
                  className={cn((item.isDeleted || item.notAuthor) ? 'line-through opacity-50' : '', 'grow w-0 ml-2 leading-[18px] text-[13px] font-medium text-gray-800  truncate')}
                >
                  <span className='system-xs-medium pr-1.5 text-text-secondary'>{item.provider_type === CollectionType.builtIn ? item.provider_name.split('/').pop() : item.tool_label}</span>
                  <span className='text-text-tertiary'>{item.tool_label}</span>
                  {!item.isDeleted && (
                  <span className='text-gray-800 pr-2'>{item.provider_type === CollectionType.builtIn ? item.provider_name : item.tool_label}</span>
                    <Tooltip
                      needsDelay
                      popupContent={
                        <div className='w-[180px]'>
                          <div className='mb-1.5 text-text-secondary'>{item.tool_name}</div>
                          <div className='mb-1.5 text-text-tertiary'>{t('tools.toolNameUsageTip')}</div>
                          <div className='cursor-pointer text-text-accent' onClick={() => copy(item.tool_name)}>{t('tools.copyToolName')}</div>
                        </div>
                      }
                    popupContent={t('tools.toolNameUsageTip')}
                    >
                      <div className='h-4 w-4'>
                        <div className='ml-0.5 hidden group-hover:inline-block'>
                          <RiInformation2Line className='h-4 w-4 text-text-tertiary' />
                        </div>
                      </div>
                    <span className='text-gray-500'>{item.tool_name}</span>
                    </Tooltip>
                  )}
                </div>
              </div>
              <div className='ml-1 flex shrink-0 items-center'>
                {item.isDeleted && (
                  <div className='mr-2 flex items-center'>
              <div className='shrink-0 ml-1 flex items-center'>
                {(item.isDeleted || item.notAuthor)
                  ? (
                    <div className='flex items-center'>
                    <Tooltip
                      popupContent={t('tools.toolRemoved')}
                        popupContent={t(`tools.${item.isDeleted ? 'toolRemoved' : 'notAuthorized'}`)}
                      needsDelay
                    >
                      <div className='mr-1 cursor-pointer rounded-md p-1 hover:bg-black/5'>
                        <AlertTriangle className='h-4 w-4 text-[#F79009]' />
                        <div className='mr-1 p-1 rounded-md hover:bg-black/5  cursor-pointer' onClick={() => {
                          if (item.notAuthor)
                            setIsShowChooseTool(true)
                        }}>
                          <AlertTriangle className='w-4 h-4 text-[#F79009]' />
                      </div>
                    </Tooltip>
                    <div
                      className='cursor-pointer rounded-md p-1 text-text-tertiary hover:text-text-destructive'
                      onClick={() => {
                      <div className='p-1 rounded-md hover:bg-black/5 cursor-pointer' onClick={() => {
                        const newModelConfig = produce(modelConfig, (draft) => {
                          draft.agentConfig.tools.splice(index, 1)
                        })
                        setModelConfig(newModelConfig)
                        formattingChangedDispatcher()
                      }}
                      onMouseOver={() => setIsDeleting(index)}
                      onMouseLeave={() => setIsDeleting(-1)}
                    >
                      <RiDeleteBinLine className='h-4 w-4' />
                      }}>
                        <RiDeleteBinLine className='w-4 h-4 text-gray-500' />
                    </div>
                      <div className='ml-2 mr-3 w-px h-3.5 bg-gray-200'></div>
                  </div>
                )}
                {!item.isDeleted && (
                  <div className='mr-2 hidden items-center gap-1 group-hover:flex'>
                    {!item.notAuthor && (
                  )
                  : (
                    <div className='hidden group-hover:flex items-center'>
                      <Tooltip
                        popupContent={t('tools.setBuiltInTools.infoAndSetting')}
                        needsDelay
                      >
                        <div className='cursor-pointer rounded-md p-1  hover:bg-black/5' onClick={() => {
                        <div className='p-1 rounded-md hover:bg-black/5  cursor-pointer' onClick={() => {
                          setCurrentTool(item)
                          setIsShowSettingTool(true)
                        }}>
                          <RiEqualizer2Line className='h-4 w-4 text-text-tertiary' />
                          <InfoCircle className='w-4 h-4 text-gray-500' />
                        </div>
                      </Tooltip>
                    )}
                    <div
                      className='cursor-pointer rounded-md p-1 text-text-tertiary hover:text-text-destructive'
                      onClick={() => {
                      <div className='p-1 rounded-md hover:bg-black/5 cursor-pointer' onClick={() => {
                        const newModelConfig = produce(modelConfig, (draft) => {
                          draft.agentConfig.tools.splice(index, 1)
                        })
                        setModelConfig(newModelConfig)
                        formattingChangedDispatcher()
                      }}
                      onMouseOver={() => setIsDeleting(index)}
                      onMouseLeave={() => setIsDeleting(-1)}
                    >
                      <RiDeleteBinLine className='h-4 w-4' />
                      }}>
                        <RiDeleteBinLine className='w-4 h-4 text-gray-500' />
                    </div>
                      <div className='ml-2 mr-3 w-px h-3.5 bg-gray-200'></div>
                  </div>
                )}
                <div className={cn(item.isDeleted && 'opacity-50')}>
                  {!item.notAuthor && (
                <div className={cn((item.isDeleted || item.notAuthor) && 'opacity-50')}>
                    <Switch
                      defaultValue={item.isDeleted ? false : item.enabled}
                      disabled={item.isDeleted}
                    defaultValue={(item.isDeleted || item.notAuthor) ? false : item.enabled}
                    disabled={(item.isDeleted || item.notAuthor)}
                      size='md'
                      onChange={(enabled) => {
                        const newModelConfig = produce(modelConfig, (draft) => {
@@ -254,48 +192,27 @@
                        setModelConfig(newModelConfig)
                        formattingChangedDispatcher()
                      }} />
                  )}
                  {item.notAuthor && (
                    <Button variant='secondary' size='small' onClick={() => {
                      setCurrentTool(item)
                      setShowSettingAuth(true)
                    }}>
                      {t('tools.notAuthorized')}
                      <Indicator className='ml-2' color='orange' />
                    </Button>
                  )}
                </div>
              </div>
            </div>
          ))}
        </div >
      </Panel >
      {isShowSettingTool && (
      {isShowChooseTool && (
        <AddToolModal onHide={() => setIsShowChooseTool(false)} />
      )}
      {
        isShowSettingTool && (
        <SettingBuiltInTool
          toolName={currentTool?.tool_name as string}
          setting={currentTool?.tool_parameters}
            setting={currentTool?.tool_parameters as any}
          collection={currentTool?.collection as Collection}
          isBuiltIn={currentTool?.collection?.type === CollectionType.builtIn}
          isModel={currentTool?.collection?.type === CollectionType.model}
          onSave={handleToolSettingChange}
          onHide={() => setIsShowSettingTool(false)}
        />
      )}
      {isShowSettingAuth && (
        <ConfigCredential
          collection={currentCollection as any}
          onCancel={() => setShowSettingAuth(false)}
          onSaved={async (value) => {
            await updateBuiltInToolCredential((currentCollection as any).name, value)
            Toast.notify({
              type: 'success',
              message: t('common.api.actionSuccess'),
            })
            handleToolAuthSetting(currentTool)
            setShowSettingAuth(false)
          }}
        />
      )}
          />)
      }
    </>
  )
}
app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool.tsx
@@ -3,30 +3,21 @@
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import {
  RiArrowLeftLine,
  RiCloseLine,
} from '@remixicon/react'
import Drawer from '@/app/components/base/drawer'
import Loading from '@/app/components/base/loading'
import ActionButton from '@/app/components/base/action-button'
import Icon from '@/app/components/plugins/card/base/card-icon'
import OrgInfo from '@/app/components/plugins/card/base/org-info'
import Description from '@/app/components/plugins/card/base/description'
import TabSlider from '@/app/components/base/tab-slider-plain'
import Button from '@/app/components/base/button'
import cn from '@/utils/classnames'
import Drawer from '@/app/components/base/drawer-plus'
import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
import { addDefaultValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
import type { Collection, Tool } from '@/app/components/tools/types'
import { CollectionType } from '@/app/components/tools/types'
import { fetchBuiltInToolList, fetchCustomToolList, fetchModelToolList, fetchWorkflowToolList } from '@/service/tools'
import I18n from '@/context/i18n'
import Button from '@/app/components/base/button'
import Loading from '@/app/components/base/loading'
import { DiagonalDividingLine } from '@/app/components/base/icons/src/public/common'
import { getLanguage } from '@/i18n/language'
import cn from '@/utils/classnames'
import AppIcon from '@/app/components/base/app-icon'
type Props = {
  showBackButton?: boolean
  collection: Collection
  isBuiltIn?: boolean
  isModel?: boolean
@@ -38,7 +29,6 @@
}
const SettingBuiltInTool: FC<Props> = ({
  showBackButton = false,
  collection,
  isBuiltIn = true,
  isModel = true,
@@ -56,8 +46,8 @@
  const [tools, setTools] = useState<Tool[]>([])
  const currTool = tools.find(tool => tool.name === toolName)
  const formSchemas = currTool ? toolParametersToFormSchemas(currTool.parameters) : []
  const infoSchemas = formSchemas.filter(item => item.form === 'llm')
  const settingSchemas = formSchemas.filter(item => item.form !== 'llm')
  const infoSchemas = formSchemas.filter((item: any) => item.form === 'llm')
  const settingSchemas = formSchemas.filter((item: any) => item.form !== 'llm')
  const hasSetting = settingSchemas.length > 0
  const [tempSetting, setTempSetting] = useState(setting)
  const [currType, setCurrType] = useState('info')
@@ -88,7 +78,7 @@
          setTempSetting(addDefaultValue(setting, formSchemas))
        }
      }
      catch { }
      catch (e) { }
      setIsLoading(false)
    })()
  }, [collection?.name, collection?.id, collection?.type])
@@ -99,45 +89,46 @@
  const isValid = (() => {
    let valid = true
    settingSchemas.forEach((item) => {
    settingSchemas.forEach((item: any) => {
      if (item.required && !tempSetting[item.name])
        valid = false
    })
    return valid
  })()
  const getType = (type: string) => {
    if (type === 'number-input')
      return t('tools.setBuiltInTools.number')
    if (type === 'text-input')
      return t('tools.setBuiltInTools.string')
    if (type === 'file')
      return t('tools.setBuiltInTools.file')
    return type
  }
  const infoUI = (
    <div className=''>
      {infoSchemas.length > 0 && (
        <div className='space-y-1 py-2'>
          {infoSchemas.map((item, index) => (
            <div key={index} className='py-1'>
              <div className='flex items-center gap-2'>
                <div className='code-sm-semibold text-text-secondary'>{item.label[language]}</div>
                <div className='system-xs-regular text-text-tertiary'>
                  {getType(item.type)}
    <div className='pt-2'>
      <div className='leading-5 text-sm font-medium text-gray-900'>
        {t('tools.setBuiltInTools.toolDescription')}
                </div>
      <div className='mt-1 leading-[18px] text-xs font-normal text-gray-600'>
        {currTool?.description[language]}
      </div>
      {infoSchemas.length > 0 && (
        <div className='mt-6'>
          <div className='flex items-center mb-4 leading-[18px] text-xs font-semibold text-gray-500 uppercase'>
            <div className='mr-3'>{t('tools.setBuiltInTools.parameters')}</div>
            <div className='grow w-0 h-px bg-[#f3f4f6]'></div>
          </div>
          <div className='space-y-4'>
            {infoSchemas.map((item: any, index) => (
              <div key={index}>
                <div className='flex items-center space-x-2 leading-[18px]'>
                  <div className='text-[13px] font-semibold text-gray-900'>{item.label[language]}</div>
                  <div className='text-xs font-medium text-gray-500'>{item.type === 'number-input' ? t('tools.setBuiltInTools.number') : t('tools.setBuiltInTools.string')}</div>
                {item.required && (
                  <div className='system-xs-medium text-text-warning-secondary'>{t('tools.setBuiltInTools.required')}</div>
                    <div className='text-xs font-medium text-[#EC4A0A]'>{t('tools.setBuiltInTools.required')}</div>
                )}
              </div>
              {item.human_description && (
                <div className='system-xs-regular mt-0.5 text-text-tertiary'>
                  <div className='mt-1 leading-[18px] text-xs font-normal text-gray-600'>
                  {item.human_description?.[language]}
                </div>
              )}
            </div>
          ))}
          </div>
        </div>
      )}
    </div>
@@ -147,92 +138,86 @@
    <Form
      value={tempSetting}
      onChange={setTempSetting}
      formSchemas={settingSchemas}
      formSchemas={settingSchemas as any}
      isEditMode={false}
      showOnVariableMap={{}}
      validating={false}
      inputClassName='!bg-gray-50'
      readonly={readonly}
    />
  )
  return (
    <Drawer
      isOpen
      clickOutsideNotOpen={false}
      onClose={onHide}
      footer={null}
      mask={false}
      positionCenter={false}
      panelClassName={cn('mb-2 mr-2 mt-[64px] !w-[420px] !max-w-[420px] justify-start rounded-2xl border-[0.5px] border-components-panel-border !bg-components-panel-bg !p-0 shadow-xl')}
    >
      <>
        {isLoading && <Loading type='app' />}
        {!isLoading && (
          <>
            {/* header */}
            <div className='relative border-b border-divider-subtle p-4 pb-3'>
              <div className='absolute right-3 top-3'>
                <ActionButton onClick={onHide}>
                  <RiCloseLine className='h-4 w-4' />
                </ActionButton>
              </div>
              {showBackButton && (
      isShow
      onHide={onHide}
      title={(
        <div className='flex items-center'>
          {typeof collection.icon === 'string'
            ? (
                <div
                  className='system-xs-semibold-uppercase mb-2 flex cursor-pointer items-center gap-1 text-text-accent-secondary'
                  onClick={onHide}
                >
                  <RiArrowLeftLine className='h-4 w-4' />
                  BACK
                </div>
              )}
              <div className='flex items-center gap-1'>
                <Icon size='tiny' className='h-6 w-6' src={collection.icon} />
                <OrgInfo
                  packageNameClassName='w-auto'
                  orgName={collection.author}
                  packageName={collection.name.split('/').pop() || ''}
                />
              </div>
              <div className='system-md-semibold mt-1 text-text-primary'>{currTool?.label[language]}</div>
              {!!currTool?.description[language] && (
                <Description className='mt-3' text={currTool.description[language]} descriptionLineRows={2}></Description>
              )}
            </div>
            {/* form */}
            <div className='h-full'>
              <div className='flex h-full flex-col'>
                {(hasSetting && !readonly) ? (
                  <TabSlider
                    className='mt-1 shrink-0 px-4'
                    itemClassName='py-3'
                    noBorderBottom
                    value={currType}
                    onChange={(value) => {
                      setCurrType(value)
                className='w-6 h-6 bg-cover bg-center rounded-md flex-shrink-0'
                style={{
                  backgroundImage: `url(${collection.icon})`,
                    }}
                    options={[
                      { value: 'info', text: t('tools.setBuiltInTools.parameters')! },
                      { value: 'setting', text: t('tools.setBuiltInTools.setting')! },
                    ]}
              ></div>
            )
            : (
              <AppIcon
                className='rounded-md'
                size='tiny'
                icon={(collection.icon as any)?.content}
                background={(collection.icon as any)?.background}
                  />
                ) : (
                  <div className='system-sm-semibold-uppercase p-4 pb-1 text-text-primary'>{t('tools.setBuiltInTools.parameters')}</div>
                )}
                <div className='h-0 grow overflow-y-auto px-4'>
          <div className='ml-2 leading-6 text-base font-semibold text-gray-900'>{currTool?.label[language]}</div>
          {(hasSetting && !readonly) && (<>
            <DiagonalDividingLine className='mx-4' />
            <div className='flex space-x-6'>
              <div
                className={cn(isInfoActive ? 'text-gray-900 font-semibold' : 'font-normal text-gray-600 cursor-pointer', 'relative text-base')}
                onClick={() => setCurrType('info')}
              >
                {t('tools.setBuiltInTools.info')}
                {isInfoActive && <div className='absolute left-0 bottom-[-16px] w-full h-0.5 bg-primary-600'></div>}
              </div>
              <div className={cn(!isInfoActive ? 'text-gray-900 font-semibold' : 'font-normal text-gray-600 cursor-pointer', 'relative text-base ')}
                onClick={() => setCurrType('setting')}
              >
                {t('tools.setBuiltInTools.setting')}
                {!isInfoActive && <div className='absolute left-0 bottom-[-16px] w-full h-0.5 bg-primary-600'></div>}
              </div>
            </div>
          </>)}
        </div>
      )}
      panelClassName='mt-[65px] !w-[405px]'
      maxWidthClassName='!max-w-[405px]'
      height='calc(100vh - 65px)'
      headerClassName='!border-b-black/5'
      body={
        <div className='h-full pt-3'>
          {isLoading
            ? <div className='flex h-full items-center'>
              <Loading type='app' />
            </div>
            : (<div className='flex flex-col h-full'>
              <div className='grow h-0 overflow-y-auto  px-6'>
                  {isInfoActive ? infoUI : settingUI}
                </div>
                {!readonly && !isInfoActive && (
                  <div className='mt-2 flex shrink-0 justify-end space-x-2 rounded-b-[10px]  border-t border-divider-regular bg-components-panel-bg px-6 py-4'>
                    <Button className='flex h-8 items-center !px-3 !text-[13px] font-medium ' onClick={onHide}>{t('common.operation.cancel')}</Button>
                    <Button className='flex h-8 items-center !px-3 !text-[13px] font-medium' variant='primary' disabled={!isValid} onClick={() => onSave?.(addDefaultValue(tempSetting, formSchemas))}>{t('common.operation.save')}</Button>
                <div className='mt-2 shrink-0 flex justify-end py-4 px-6  space-x-2 rounded-b-[10px] bg-gray-50 border-t border-black/5'>
                  <Button className='flex items-center h-8 !px-3 !text-[13px] font-medium !text-gray-700' onClick={onHide}>{t('common.operation.cancel')}</Button>
                  <Button className='flex items-center h-8 !px-3 !text-[13px] font-medium' variant='primary' disabled={!isValid} onClick={() => onSave?.(addDefaultValue(tempSetting, formSchemas))}>{t('common.operation.save')}</Button>
                  </div>
                )}
            </div>)}
              </div>
            </div>
          </>
        )}
      </>
    </Drawer>
      }
      isShowMask={false}
      clickOutsideNotOpen={false}
    />
  )
}
export default React.memo(SettingBuiltInTool)
app/components/app/configuration/config/agent/prompt-editor.tsx
@@ -14,9 +14,8 @@
import ConfigContext from '@/context/debug-configuration'
import { useModalContext } from '@/context/modal-context'
import { useToastContext } from '@/app/components/base/toast'
import s from '@/app/components/app/configuration/config-prompt/style.module.css'
import { noop } from 'lodash-es'
import s from '@/app/components/app/configuration/config-prompt/style.module.css'
type Props = {
  className?: string
  type: 'first-prompt' | 'next-iteration'
@@ -76,12 +75,12 @@
  return (
    <div className={cn(className, s.gradientBorder, 'relative')}>
      <div className='rounded-xl bg-white'>
        <div className={cn(s.boxHeader, 'flex h-11 items-center justify-between rounded-tl-xl rounded-tr-xl bg-white pb-1 pl-4 pr-3 pt-2 hover:shadow-xs')}>
        <div className={cn(s.boxHeader, 'flex justify-between items-center h-11 pt-2 pr-3 pb-1 pl-4 rounded-tl-xl rounded-tr-xl bg-white hover:shadow-xs')}>
          <div className='text-sm font-semibold uppercase text-indigo-800'>{t(`appDebug.agent.${isFirstPrompt ? 'firstPrompt' : 'nextIteration'}`)}</div>
          <div className={cn(s.optionWrap, 'items-center space-x-1')}>
            {!isCopied
              ? (
                <Clipboard className='h-6 w-6 cursor-pointer p-1 text-gray-500' onClick={() => {
                <Clipboard className='h-6 w-6 p-1 text-gray-500 cursor-pointer' onClick={() => {
                  copy(value)
                  setIsCopied(true)
                }} />
@@ -91,7 +90,7 @@
              )}
          </div>
        </div>
        <div className={cn(editorHeight, ' min-h-[102px] overflow-y-auto px-4 text-sm text-gray-700')}>
        <div className={cn(editorHeight, ' px-4 min-h-[102px] overflow-y-auto text-sm text-gray-700')}>
          <PromptEditor
            className={editorHeight}
            value={value}
@@ -129,18 +128,18 @@
                user: '',
                assistant: '',
              },
              onEditRole: noop,
              onEditRole: () => { },
            }}
            queryBlock={{
              show: false,
              selectable: false,
            }}
            onChange={onChange}
            onBlur={noop}
            onBlur={() => { }}
          />
        </div>
        <div className='flex pb-2 pl-4'>
          <div className="h-[18px] rounded-md bg-gray-100 px-1 text-xs leading-[18px] text-gray-500">{value.length}</div>
        <div className='pl-4 pb-2 flex'>
          <div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{value.length}</div>
        </div>
      </div>
    </div>
app/components/app/configuration/config/assistant-type-picker/index.tsx
@@ -40,19 +40,19 @@
const SelectItem: FC<ItemProps> = ({ text, value, Icon, isChecked, description, onClick, disabled }) => {
  return (
    <div
      className={cn(disabled ? 'opacity-50' : 'cursor-pointer', isChecked ? 'border-[2px] border-indigo-600 shadow-sm' : 'border border-gray-100', 'mb-2 rounded-xl bg-gray-25 p-3 pr-4 hover:bg-gray-50')}
      className={cn(disabled ? 'opacity-50' : 'cursor-pointer', isChecked ? 'border-[2px] border-indigo-600 shadow-sm' : 'border border-gray-100', 'mb-2 p-3 pr-4 rounded-xl bg-gray-25 hover:bg-gray-50')}
      onClick={() => !disabled && onClick(value)}
    >
      <div className='flex items-center justify-between'>
        <div className='flex items-center '>
          <div className='mr-3 rounded-lg bg-indigo-50 p-1'>
            <Icon className='h-4 w-4 text-indigo-600' />
          <div className='mr-3 p-1 bg-indigo-50 rounded-lg'>
            <Icon className='w-4 h-4 text-indigo-600' />
          </div>
          <div className='text-sm font-medium leading-5 text-gray-900'>{text}</div>
          <div className='leading-5 text-sm font-medium text-gray-900'>{text}</div>
        </div>
        <Radio isChecked={isChecked} />
      </div>
      <div className='ml-9 text-xs font-normal leading-[18px] text-gray-500'>{description}</div>
      <div className='ml-9 leading-[18px] text-xs font-normal text-gray-500'>{description}</div>
    </div>
  )
}
@@ -83,7 +83,7 @@
    <>
      <div className='my-4 h-[1px] bg-gray-100'></div>
      <div
        className={cn(isAgent ? 'group cursor-pointer hover:bg-primary-50' : 'opacity-30', 'rounded-xl bg-gray-50 p-3 pr-4 ')}
        className={cn(isAgent ? 'group cursor-pointer hover:bg-primary-50' : 'opacity-30', 'p-3 pr-4 rounded-xl bg-gray-50 ')}
        onClick={() => {
          if (isAgent) {
            setOpen(false)
@@ -93,14 +93,14 @@
      >
        <div className='flex items-center justify-between'>
          <div className='flex items-center '>
            <div className='mr-3 rounded-lg bg-gray-200 p-1 group-hover:bg-white'>
              <Settings04 className='h-4 w-4 text-gray-600 group-hover:text-[#155EEF]' />
            <div className='mr-3 p-1 bg-gray-200 group-hover:bg-white rounded-lg'>
              <Settings04 className='w-4 h-4 text-gray-600 group-hover:text-[#155EEF]' />
            </div>
            <div className='text-sm font-medium leading-5 text-gray-900 group-hover:text-[#155EEF]'>{t('appDebug.agent.setting.name')}</div>
            <div className='leading-5 text-sm font-medium text-gray-900 group-hover:text-[#155EEF]'>{t('appDebug.agent.setting.name')}</div>
          </div>
          <ArrowUpRight className='h-4 w-4 text-gray-500 group-hover:text-[#155EEF]' />
          <ArrowUpRight className='w-4 h-4 text-gray-500 group-hover:text-[#155EEF]' />
        </div>
        <div className='ml-9 text-xs font-normal leading-[18px] text-gray-500'>{t('appDebug.agent.setting.description')}</div>
        <div className='ml-9 leading-[18px] text-xs font-normal text-gray-500'>{t('appDebug.agent.setting.description')}</div>
      </div>
    </>
  )
@@ -116,15 +116,15 @@
        }}
      >
        <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
          <div className={cn(open && 'bg-gray-50', 'flex h-8 cursor-pointer select-none items-center space-x-1 rounded-lg border border-black/5 px-3 text-indigo-600')}>
            {isAgent ? <BubbleText className='h-3 w-3' /> : <CuteRobot className='h-3 w-3' />}
          <div className={cn(open && 'bg-gray-50', 'flex items-center h-8 px-3 border border-black/5 rounded-lg cursor-pointer select-none space-x-1 text-indigo-600')}>
            {isAgent ? <BubbleText className='w-3 h-3' /> : <CuteRobot className='w-3 h-3' />}
            <div className='text-xs font-medium'>{t(`appDebug.assistantType.${isAgent ? 'agentAssistant' : 'chatAssistant'}.name`)}</div>
            <RiArrowDownSLine className='h-3 w-3' />
            <RiArrowDownSLine className='w-3 h-3' />
          </div>
        </PortalToFollowElemTrigger>
        <PortalToFollowElemContent style={{ zIndex: 1000 }}>
          <div className='relative left-0.5 w-[480px] rounded-xl border border-black/8 bg-white p-6 shadow-lg'>
            <div className='mb-2 text-sm font-semibold leading-5 text-gray-900'>{t('appDebug.assistantType.name')}</div>
          <div className='relative left-0.5 p-6 bg-white border border-black/8 shadow-lg rounded-xl w-[480px]'>
            <div className='mb-2 leading-5 text-sm font-semibold text-gray-900'>{t('appDebug.assistantType.name')}</div>
            <SelectItem
              Icon={BubbleText}
              value='chat'
app/components/app/configuration/config/automatic/automatic-btn.tsx
@@ -2,10 +2,7 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import {
  RiSparklingFill,
} from '@remixicon/react'
import Button from '@/app/components/base/button'
import { Generator } from '@/app/components/base/icons/src/vender/other'
export type IAutomaticBtnProps = {
  onClick: () => void
@@ -16,10 +13,12 @@
  const { t } = useTranslation()
  return (
    <Button variant='secondary-accent' size='small' onClick={onClick}>
      <RiSparklingFill className='mr-1 h-3.5 w-3.5' />
      <span className=''>{t('appDebug.operation.automatic')}</span>
    </Button>
    <div className='flex space-x-1 items-center !h-8 cursor-pointer'
      onClick={onClick}
    >
      <Generator className='w-3.5 h-3.5 text-indigo-600' />
      <span className='text-xs font-semibold text-indigo-600'>{t('appDebug.operation.automatic')}</span>
    </div>
  )
}
export default React.memo(AutomaticBtn)
app/components/app/configuration/config/automatic/get-automatic-res.tsx
@@ -54,11 +54,11 @@
}> = ({ Icon, text, onClick }) => {
  return (
    <div
      className='mr-1 mt-2 flex h-7 shrink-0 cursor-pointer items-center rounded-lg bg-components-button-secondary-bg px-2'
      className='mt-2 mr-1 shrink-0 flex h-7 items-center px-2 bg-gray-100 rounded-lg cursor-pointer'
      onClick={onClick}
    >
      <Icon className='h-4 w-4 text-text-tertiary'></Icon>
      <div className='ml-1 text-xs font-medium text-text-secondary'>{text}</div>
      <Icon className='w-4 h-4 text-gray-500'></Icon>
      <div className='ml-1 text-xs font-medium text-gray-700'>{text}</div>
    </div>
  )
}
@@ -138,16 +138,16 @@
  const [res, setRes] = React.useState<AutomaticRes | null>(null)
  const renderLoading = (
    <div className='flex h-full w-0 grow flex-col items-center justify-center space-y-3'>
    <div className='w-0 grow flex flex-col items-center justify-center h-full space-y-3'>
      <Loading />
      <div className='text-[13px] text-text-tertiary'>{t('appDebug.generate.loading')}</div>
      <div className='text-[13px] text-gray-400'>{t('appDebug.generate.loading')}</div>
    </div>
  )
  const renderNoData = (
    <div className='flex h-full w-0 grow flex-col items-center justify-center space-y-3 px-8'>
      <Generator className='h-14 w-14 text-text-tertiary' />
      <div className='text-center text-[13px] font-normal leading-5 text-text-tertiary'>
    <div className='w-0 grow flex flex-col items-center px-8 justify-center h-full space-y-3'>
      <Generator className='w-14 h-14 text-gray-300' />
      <div className='leading-5 text-center text-[13px] font-normal text-gray-400'>
        <div>{t('appDebug.generate.noDataLine1')}</div>
        <div>{t('appDebug.generate.noDataLine2')}</div>
      </div>
@@ -189,18 +189,18 @@
    <Modal
      isShow={isShow}
      onClose={onClose}
      className='min-w-[1140px] !p-0'
      className='!p-0 min-w-[1140px]'
      closable
    >
      <div className='flex h-[680px] flex-wrap'>
        <div className='h-full w-[570px] shrink-0 overflow-y-auto border-r border-divider-regular p-6'>
        <div className='w-[570px] shrink-0 p-6 h-full overflow-y-auto border-r border-gray-100'>
          <div className='mb-8'>
            <div className={`text-lg font-bold leading-[28px] ${s.textGradient}`}>{t('appDebug.generate.title')}</div>
            <div className='mt-1 text-[13px] font-normal text-text-tertiary'>{t('appDebug.generate.description')}</div>
            <div className={`leading-[28px] text-lg font-bold ${s.textGradient}`}>{t('appDebug.generate.title')}</div>
            <div className='mt-1 text-[13px] font-normal text-gray-500'>{t('appDebug.generate.description')}</div>
          </div>
          <div className='mb-8 flex items-center'>
          <div className='flex items-center mb-8'>
            <ModelIcon
              className='mr-1.5 shrink-0 '
              className='shrink-0 mr-1.5 '
              provider={currentProvider}
              modelName={currentModel?.model}
            />
@@ -213,8 +213,8 @@
          </div>
          <div >
            <div className='flex items-center'>
              <div className='mr-3 shrink-0 text-xs font-semibold uppercase leading-[18px] text-text-tertiary'>{t('appDebug.generate.tryIt')}</div>
              <div className='h-px grow' style={{
              <div className='mr-3 shrink-0 leading-[18px] text-xs font-semibold text-gray-500 uppercase'>{t('appDebug.generate.tryIt')}</div>
              <div className='grow h-px' style={{
                background: 'linear-gradient(to right, rgba(243, 244, 246, 1), rgba(243, 244, 246, 0))',
              }}></div>
            </div>
@@ -232,7 +232,7 @@
          {/* inputs */}
          <div className='mt-6'>
            <div className='text-[0px]'>
              <div className='mb-2 text-sm font-medium leading-5 text-text-primary'>{t('appDebug.generate.instruction')}</div>
              <div className='mb-2 leading-5 text-sm font-medium text-gray-900'>{t('appDebug.generate.instruction')}</div>
              <Textarea
                className="h-[200px] resize-none"
                placeholder={t('appDebug.generate.instructionPlaceHolder') as string}
@@ -247,7 +247,7 @@
                onClick={onGenerate}
                disabled={isLoading}
              >
                <Generator className='h-4 w-4 text-white' />
                <Generator className='w-4 h-4 text-white' />
                <span className='text-xs font-semibold text-white'>{t('appDebug.generate.generate')}</span>
              </Button>
            </div>
@@ -255,8 +255,8 @@
        </div>
        {(!isLoading && res) && (
          <div className='h-full w-0 grow p-6 pb-0'>
            <div className='mb-3 shrink-0 text-base font-semibold leading-[160%] text-text-secondary'>{t('appDebug.generate.resTitle')}</div>
          <div className='w-0 grow p-6 pb-0 h-full'>
            <div className='shrink-0 mb-3 leading-[160%] text-base font-semibold text-gray-800'>{t('appDebug.generate.resTitle')}</div>
            <div className={cn('max-h-[555px] overflow-y-auto', !isInLLMNode && 'pb-2')}>
              <ConfigPrompt
                mode={mode}
@@ -283,17 +283,17 @@
                    <div className='mt-7'>
                      <GroupName name={t('appDebug.feature.groupChat.title')} />
                      <div
                        className='mb-1 rounded-xl border-l-[0.5px] border-t-[0.5px] border-effects-highlight bg-background-section-burn p-3'
                        className='mb-1 p-3 border-t-[0.5px] border-l-[0.5px] border-effects-highlight rounded-xl bg-background-section-burn'
                      >
                        <div className='mb-2 flex items-center gap-2'>
                          <div className='shrink-0 rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-blue-light-blue-light-500 p-1 shadow-xs'>
                            <LoveMessage className='h-4 w-4 text-text-primary-on-surface' />
                          <div className='shrink-0 p-1 rounded-lg border-[0.5px] border-divider-subtle shadow-xs bg-util-colors-blue-light-blue-light-500'>
                            <LoveMessage className='w-4 h-4 text-text-primary-on-surface' />
                          </div>
                          <div className='system-sm-semibold flex grow items-center text-text-secondary'>
                          <div className='grow flex items-center text-text-secondary system-sm-semibold'>
                            {t('appDebug.feature.conversationOpener.title')}
                          </div>
                        </div>
                        <div className='system-xs-regular min-h-8 text-text-tertiary'>{res.opening_statement}</div>
                        <div className='min-h-8 text-text-tertiary system-xs-regular'>{res.opening_statement}</div>
                      </div>
                    </div>
                  )}
@@ -301,7 +301,7 @@
              )}
            </div>
            <div className='flex justify-end bg-background-default py-4'>
            <div className='flex justify-end py-4 bg-white'>
              <Button onClick={onClose}>{t('common.operation.cancel')}</Button>
              <Button variant='primary' className='ml-2' onClick={() => {
                setShowConfirmOverwrite(true)
app/components/app/configuration/config/automatic/style.module.css
app/components/app/configuration/config/code-generator/get-code-generator-res.tsx
@@ -100,15 +100,15 @@
  const [showConfirmOverwrite, setShowConfirmOverwrite] = React.useState(false)
  const renderLoading = (
    <div className='flex h-full w-0 grow flex-col items-center justify-center space-y-3'>
    <div className='w-0 grow flex flex-col items-center justify-center h-full space-y-3'>
      <Loading />
      <div className='text-[13px] text-gray-400'>{t('appDebug.codegen.loading')}</div>
    </div>
  )
  const renderNoData = (
    <div className='flex h-full w-0 grow flex-col items-center justify-center space-y-3 px-8'>
      <Generator className='h-14 w-14 text-gray-300' />
      <div className='text-center text-[13px] font-normal leading-5 text-gray-400'>
    <div className='w-0 grow flex flex-col items-center px-8 justify-center h-full space-y-3'>
      <Generator className='w-14 h-14 text-gray-300' />
      <div className='leading-5 text-center text-[13px] font-normal text-gray-400'>
        <div>{t('appDebug.codegen.noDataLine1')}</div>
        <div>{t('appDebug.codegen.noDataLine2')}</div>
      </div>
@@ -119,18 +119,18 @@
    <Modal
      isShow={isShow}
      onClose={onClose}
      className='min-w-[1140px] !p-0'
      className='!p-0 min-w-[1140px]'
      closable
    >
      <div className='relative flex h-[680px] flex-wrap'>
        <div className='h-full w-[570px] shrink-0 overflow-y-auto border-r border-gray-100 p-8'>
        <div className='w-[570px] shrink-0 p-8 h-full overflow-y-auto border-r border-gray-100'>
          <div className='mb-8'>
            <div className={'text-lg font-bold leading-[28px]'}>{t('appDebug.codegen.title')}</div>
            <div className={'leading-[28px] text-lg font-bold'}>{t('appDebug.codegen.title')}</div>
            <div className='mt-1 text-[13px] font-normal text-gray-500'>{t('appDebug.codegen.description')}</div>
          </div>
          <div className='flex items-center'>
            <ModelIcon
              className='mr-1.5 shrink-0'
              className='shrink-0 mr-1.5'
              provider={currentProvider}
              modelName={currentModel?.model}
            />
@@ -143,9 +143,9 @@
          </div>
          <div className='mt-6'>
            <div className='text-[0px]'>
              <div className='mb-2 text-sm font-medium leading-5 text-gray-900'>{t('appDebug.codegen.instruction')}</div>
              <div className='mb-2 leading-5 text-sm font-medium text-gray-900'>{t('appDebug.codegen.instruction')}</div>
              <textarea
                className="h-[200px] w-full overflow-y-auto rounded-lg bg-gray-50 px-3 py-2 text-sm"
                className="w-full h-[200px] overflow-y-auto px-3 py-2 text-sm bg-gray-50 rounded-lg"
                placeholder={t('appDebug.codegen.instructionPlaceholder') || ''}
                value={instruction}
                onChange={e => setInstruction(e.target.value)}
@@ -159,7 +159,7 @@
                onClick={onGenerate}
                disabled={isLoading}
              >
                <Generator className='h-4 w-4 text-white' />
                <Generator className='w-4 h-4 text-white' />
                <span className='text-xs font-semibold text-white'>{t('appDebug.codegen.generate')}</span>
              </Button>
            </div>
@@ -168,8 +168,8 @@
        {isLoading && renderLoading}
        {!isLoading && !res && renderNoData}
        {(!isLoading && res) && (
          <div className='h-full w-0 grow p-6 pb-0'>
            <div className='mb-3 shrink-0 text-base font-semibold leading-[160%] text-gray-800'>{t('appDebug.codegen.resTitle')}</div>
          <div className='w-0 grow p-6 pb-0 h-full'>
            <div className='shrink-0 mb-3 leading-[160%] text-base font-semibold text-gray-800'>{t('appDebug.codegen.resTitle')}</div>
            <div className={cn('max-h-[555px] overflow-y-auto', !isInLLMNode && 'pb-2')}>
              <ConfigPrompt
                mode={mode}
@@ -186,7 +186,7 @@
                  {res?.code && (
                    <div className='mt-4'>
                      <h3 className='mb-2 text-sm font-medium text-gray-900'>{t('appDebug.codegen.generatedCode')}</h3>
                      <pre className='overflow-x-auto rounded-lg bg-gray-50 p-4'>
                      <pre className='p-4 bg-gray-50 rounded-lg overflow-x-auto'>
                        <code className={`language-${res.language}`}>
                          {res.code}
                        </code>
@@ -194,7 +194,7 @@
                    </div>
                  )}
                  {res?.error && (
                    <div className='mt-4 rounded-lg bg-red-50 p-4'>
                    <div className='mt-4 p-4 bg-red-50 rounded-lg'>
                      <p className='text-sm text-red-600'>{res.error}</p>
                    </div>
                  )}
@@ -202,7 +202,7 @@
              )}
            </div>
            <div className='flex justify-end bg-white py-4'>
            <div className='flex justify-end py-4 bg-white'>
              <Button onClick={onClose}>{t('common.operation.cancel')}</Button>
              <Button variant='primary' className='ml-2' onClick={() => {
                setShowConfirmOverwrite(true)
app/components/app/configuration/config/config-document.tsx
@@ -48,14 +48,14 @@
    return null
  return (
    <div className='mt-2 flex items-center gap-2 rounded-xl border-l-[0.5px] border-t-[0.5px] bg-background-section-burn p-2'>
    <div className='mt-2 flex items-center gap-2 p-2 rounded-xl border-t-[0.5px] border-l-[0.5px] bg-background-section-burn'>
      <div className='shrink-0 p-1'>
        <div className='rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-indigo-indigo-600 p-1 shadow-xs'>
          <Document className='h-4 w-4 text-text-primary-on-surface' />
        <div className='p-1 rounded-lg border-[0.5px] border-divider-subtle shadow-xs bg-util-colors-indigo-indigo-600'>
          <Document className='w-4 h-4 text-text-primary-on-surface' />
        </div>
      </div>
      <div className='flex grow items-center'>
        <div className='system-sm-semibold mr-1 text-text-secondary'>{t('appDebug.feature.documentUpload.title')}</div>
      <div className='grow flex items-center'>
        <div className='mr-1 text-text-secondary system-sm-semibold'>{t('appDebug.feature.documentUpload.title')}</div>
        <Tooltip
          popupContent={
            <div className='w-[180px]' >
@@ -64,8 +64,8 @@
          }
        />
      </div>
      <div className='flex shrink-0 items-center'>
        <div className='ml-1 mr-3 h-3.5 w-[1px] bg-divider-subtle'></div>
      <div className='shrink-0 flex items-center'>
        <div className='ml-1 mr-3 w-[1px] h-3.5 bg-divider-subtle'></div>
        <Switch
          defaultValue={isDocumentEnabled}
          onChange={handleChange}
app/components/app/configuration/config/index.tsx
@@ -12,7 +12,7 @@
import ConfigContext from '@/context/debug-configuration'
import ConfigPrompt from '@/app/components/app/configuration/config-prompt'
import ConfigVar from '@/app/components/app/configuration/config-var'
import type { ModelConfig, PromptVariable } from '@/models/debug'
import { type ModelConfig, type PromptVariable } from '@/models/debug'
import type { AppType } from '@/types/app'
import { ModelModeType } from '@/types/app'
@@ -57,7 +57,7 @@
  return (
    <>
      <div
        className="relative h-0 grow overflow-y-auto px-6 pb-[50px]"
        className="grow h-0 relative px-6 pb-[50px] overflow-y-auto"
      >
        {/* Template */}
        <ConfigPrompt
app/components/app/configuration/ctrl-btn-group/index.tsx
@@ -13,8 +13,8 @@
const ContrlBtnGroup: FC<IContrlBtnGroupProps> = ({ onSave, onReset }) => {
  const { t } = useTranslation()
  return (
    <div className="fixed bottom-0 left-[224px] h-[64px] w-[519px]">
      <div className={`${s.ctrlBtn} flex h-full items-center gap-2  bg-white pl-4`}>
    <div className="fixed left-[224px] bottom-0 w-[519px] h-[64px]">
      <div className={`${s.ctrlBtn} flex items-center h-full pl-4  gap-2 bg-white`}>
        <Button variant='primary' onClick={onSave}>{t('appDebug.operation.applyConfig')}</Button>
        <Button onClick={onReset}>{t('appDebug.operation.resetConfig')}</Button>
      </div>
app/components/app/configuration/ctrl-btn-group/style.module.css
app/components/app/configuration/dataset-config/card-item/index.tsx
@@ -28,24 +28,24 @@
    <div
      className={
        cn(className, s.card,
          'relative flex cursor-pointer items-center  rounded-xl border border-gray-200 bg-white px-3  py-2.5')
          'relative flex items-center rounded-xl  px-3 py-2.5 bg-white border border-gray-200  cursor-pointer')
      }>
      <div className='flex items-center space-x-2'>
        <div className={cn(!config.embedding_available && 'opacity-50')}>
          <TypeIcon type="upload_file" />
        </div>
        <div>
          <div className='mr-1 flex w-[160px] items-center'>
            <div className={cn('overflow-hidden text-ellipsis whitespace-nowrap text-[13px] font-medium leading-[18px] text-gray-800', !config.embedding_available && 'opacity-50')}>{config.name}</div>
          <div className='flex items-center w-[160px] mr-1'>
            <div className={cn('text-[13px] leading-[18px] font-medium text-gray-800 overflow-hidden text-ellipsis whitespace-nowrap', !config.embedding_available && 'opacity-50')}>{config.name}</div>
            {!config.embedding_available && (
              <Tooltip
                popupContent={t('dataset.unavailableTip')}
              >
                <span className='inline-flex shrink-0 whitespace-nowrap rounded-md border border-gray-200 px-1 text-xs font-normal leading-[18px] text-gray-500'>{t('dataset.unavailable')}</span>
                <span className='shrink-0 inline-flex whitespace-nowrap px-1 border border-gray-200 rounded-md text-gray-500 text-xs font-normal leading-[18px]'>{t('dataset.unavailable')}</span>
              </Tooltip>
            )}
          </div>
          <div className={cn('flex max-w-[150px] text-xs text-gray-500', !config.embedding_available && 'opacity-50')}>
          <div className={cn('max-w-[150px] flex text-xs text-gray-500', !config.embedding_available && 'opacity-50')}>
            {formatNumber(config.word_count)} {t('appDebug.feature.dataSet.words')} · {formatNumber(config.document_count)} {t('appDebug.feature.dataSet.textBlocks')}
          </div>
        </div>
app/components/app/configuration/dataset-config/card-item/item.tsx
@@ -12,12 +12,10 @@
import FileIcon from '@/app/components/base/file-icon'
import { Folder } from '@/app/components/base/icons/src/vender/solid/files'
import { Globe06 } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
import Drawer from '@/app/components/base/drawer'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import Badge from '@/app/components/base/badge'
import { useKnowledge } from '@/hooks/use-knowledge'
import cn from '@/utils/classnames'
type ItemProps = {
  className?: string
@@ -45,70 +43,56 @@
    setShowSettingsModal(false)
  }
  const [isDeleting, setIsDeleting] = useState(false)
  return (
    <div className={cn(
      'group relative mb-1 flex h-10 w-full cursor-pointer items-center justify-between rounded-lg border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg px-2 last-of-type:mb-0 hover:bg-components-panel-on-panel-item-bg-hover',
      isDeleting && 'border-state-destructive-border hover:bg-state-destructive-hover',
    )}>
      <div className='flex w-0 grow items-center space-x-1.5'>
    <div className='group relative flex items-center mb-1 last-of-type:mb-0  pl-2.5 py-2 pr-3 w-full bg-white rounded-lg border-[0.5px] border-gray-200 shadow-xs'>
        {
          config.data_source_type === DataSourceType.FILE && (
            <div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-[#E0EAFF] bg-[#F5F8FF]'>
              <Folder className='h-4 w-4 text-[#444CE7]' />
          <div className='shrink-0 flex items-center justify-center mr-2 w-6 h-6 bg-[#F5F8FF] rounded-md border-[0.5px] border-[#E0EAFF]'>
            <Folder className='w-4 h-4 text-[#444CE7]' />
            </div>
          )
        }
        {
          config.data_source_type === DataSourceType.NOTION && (
            <div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-[#EAECF5]'>
              <FileIcon type='notion' className='h-4 w-4' />
          <div className='shrink-0 flex items-center justify-center mr-2 w-6 h-6 rounded-md border-[0.5px] border-[#EAECF5]'>
            <FileIcon type='notion' className='w-4 h-4' />
            </div>
          )
        }
        {
          config.data_source_type === DataSourceType.WEB && (
            <div className='mr-2 flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-[0.5px] border-blue-100 bg-[#F5FAFF]'>
              <Globe06 className='h-4 w-4 text-blue-600' />
          <div className='shrink-0 flex items-center justify-center mr-2 w-6 h-6 bg-[#F5FAFF] border-[0.5px] border-blue-100 rounded-md'>
            <Globe06 className='w-4 h-4 text-blue-600' />
            </div>
          )
        }
        <div className='system-sm-medium w-0 grow truncate text-text-secondary' title={config.name}>{config.name}</div>
      </div>
      <div className='ml-2 hidden shrink-0 items-center space-x-1 group-hover:flex'>
        {
          editable && <ActionButton
            onClick={(e) => {
              e.stopPropagation()
              setShowSettingsModal(true)
            }}
          >
            <RiEditLine className='h-4 w-4 shrink-0 text-text-tertiary' />
          </ActionButton>
        }
        <ActionButton
          onClick={() => onRemove(config.id)}
          state={isDeleting ? ActionButtonState.Destructive : ActionButtonState.Default}
          onMouseEnter={() => setIsDeleting(true)}
          onMouseLeave={() => setIsDeleting(false)}
        >
          <RiDeleteBinLine className={cn('h-4 w-4 shrink-0 text-text-tertiary', isDeleting && 'text-text-destructive')} />
        </ActionButton>
      </div>
      {
        config.indexing_technique && <Badge
          className='shrink-0 group-hover:hidden'
      <div className='grow'>
        <div className='flex items-center h-[18px]'>
          <div className='grow text-[13px] font-medium text-gray-800 truncate' title={config.name}>{config.name}</div>
          {config.provider === 'external'
            ? <Badge text={t('dataset.externalTag') as string} />
            : <Badge
          text={formatIndexingTechniqueAndMethod(config.indexing_technique, config.retrieval_model_dict?.search_method)}
        />
      }
            />}
        </div>
      </div>
      <div className='hidden rounded-lg group-hover:flex items-center justify-end absolute right-0 top-0 bottom-0 pr-2 w-[124px] bg-gradient-to-r from-white/50 to-white to-50%'>
      {
        config.provider === 'external' && <Badge
          className='shrink-0 group-hover:hidden'
          text={t('dataset.externalTag') as string}
        />
          editable && <div
            className='flex items-center justify-center mr-1 w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
            onClick={() => setShowSettingsModal(true)}
          >
            <RiEditLine className='w-4 h-4 text-gray-500' />
          </div>
      }
      <Drawer isOpen={showSettingsModal} onClose={() => setShowSettingsModal(false)} footer={null} mask={isMobile} panelClassName='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
        <div
          className='group/action flex items-center justify-center w-6 h-6 hover:bg-[#FEE4E2] rounded-md cursor-pointer'
          onClick={() => onRemove(config.id)}
        >
          <RiDeleteBinLine className='w-4 h-4 text-gray-500 group-hover/action:text-[#D92D20]' />
        </div>
      </div>
      <Drawer isOpen={showSettingsModal} onClose={() => setShowSettingsModal(false)} footer={null} mask={isMobile} panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'>
        <SettingsModal
          currentDataset={config}
          onCancel={() => setShowSettingsModal(false)}
app/components/app/configuration/dataset-config/card-item/style.module.css
app/components/app/configuration/dataset-config/context-var/index.tsx
@@ -14,12 +14,12 @@
  const currItem = options.find(item => item.value === value)
  const notSetVar = !currItem
  return (
    <div className={cn(notSetVar ? 'rounded-bl-xl rounded-br-xl border-[#FEF0C7] bg-[#FEF0C7]' : 'border-components-panel-border-subtle', 'flex h-12 items-center justify-between border-t px-3 ')}>
      <div className='flex shrink-0 items-center space-x-1'>
    <div className={cn(notSetVar ? 'rounded-bl-xl rounded-br-xl bg-[#FEF0C7] border-[#FEF0C7]' : 'border-gray-200', 'flex justify-between items-center h-12 px-3 border-t ')}>
      <div className='flex items-center space-x-1 shrink-0'>
        <div className='p-1'>
          <BracketsX className='h-4 w-4 text-text-accent' />
          <BracketsX className='w-4 h-4 text-primary-500' />
        </div>
        <div className='mr-1 text-sm font-medium text-text-secondary'>{t('appDebug.feature.dataSet.queryVariable.title')}</div>
        <div className='mr-1 text-sm font-medium text-gray-800'>{t('appDebug.feature.dataSet.queryVariable.title')}</div>
        <Tooltip
          popupContent={
            <div className='w-[180px]'>
app/components/app/configuration/dataset-config/context-var/style.module.css
New file
@@ -0,0 +1,3 @@
.trigger:hover .dropdownIcon {
  color: #98A2B3;
}
app/components/app/configuration/dataset-config/context-var/var-picker.tsx
@@ -3,6 +3,7 @@
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ChevronDownIcon } from '@heroicons/react/24/outline'
import s from './style.module.css'
import cn from '@/utils/classnames'
import {
  PortalToFollowElem,
@@ -23,7 +24,7 @@
}
const VarItem: FC<{ item: Option }> = ({ item }) => (
  <div className='flex h-[18px] items-center space-x-1 rounded bg-[#EFF8FF] px-1'>
  <div className='flex items-center h-[18px] px-1 bg-[#EFF8FF] rounded space-x-1'>
    <IconTypeIcon type={item.type as IInputTypeIconProps['type']} className='text-[#1570EF]' />
    <div className='flex text-xs font-medium text-[#1570EF]'>
      <span className='opacity-60'>{'{{'}</span>
@@ -55,12 +56,13 @@
    >
      <PortalToFollowElemTrigger className={cn(triggerClassName)} onClick={() => setOpen(v => !v)}>
        <div className={cn(
          s.trigger,
          className,
          notSetVar ? 'border-[#FEDF89] bg-[#FFFCF5] text-[#DC6803]' : ' border-components-button-secondary-border text-text-accent hover:bg-components-button-secondary-bg',
          open ? 'bg-components-button-secondary-bg' : 'bg-transparent',
          notSetVar ? 'bg-[#FFFCF5] border-[#FEDF89] text-[#DC6803]' : ' hover:bg-gray-50 border-gray-200 text-primary-600',
          open ? 'bg-gray-50' : 'bg-white',
          `
          flex h-8 cursor-pointer items-center justify-center space-x-1 rounded-lg border  px-2 text-[13px]
          font-medium  shadow-xs
          flex items-center h-8 justify-center px-2 space-x-1 rounded-lg border  shadow-xs cursor-pointer
          text-[13px]  font-medium
          `)}>
          <div>
            {value
@@ -71,16 +73,16 @@
                {notSelectedVarTip || t('appDebug.feature.dataSet.queryVariable.choosePlaceholder')}
              </div>)}
          </div>
          <ChevronDownIcon className={cn(open && 'rotate-180 text-text-tertiary', 'h-3.5 w-3.5')} />
          <ChevronDownIcon className={cn(s.dropdownIcon, open && 'rotate-180 text-[#98A2B3]', 'w-3.5 h-3.5')} />
        </div>
      </PortalToFollowElemTrigger>
      <PortalToFollowElemContent style={{ zIndex: 1000 }}>
        {options.length > 0
          ? (<div className='max-h-[50vh] w-[240px] overflow-y-auto rounded-lg  border border-components-panel-border bg-components-panel-bg p-1 shadow-lg'>
          ? (<div className='w-[240px] max-h-[50vh] overflow-y-auto p-1  border bg-white border-gray-200 rounded-lg shadow-lg'>
            {options.map(({ name, value, type }, index) => (
              <div
                key={index}
                className='flex cursor-pointer rounded-lg px-3 py-1 hover:bg-state-base-hover'
                className='px-3 py-1 flex rounded-lg hover:bg-gray-50 cursor-pointer'
                onClick={() => {
                  onChange(value)
                  setOpen(false)
@@ -91,9 +93,9 @@
            ))}
          </div>)
          : (
            <div className='w-[240px] rounded-lg border border-components-panel-border bg-components-panel-bg p-6 shadow-lg'>
              <div className='mb-1 text-sm font-medium text-text-secondary'>{t('appDebug.feature.dataSet.queryVariable.noVar')}</div>
              <div className='text-xs leading-normal text-text-tertiary'>{t('appDebug.feature.dataSet.queryVariable.noVarTip')}</div>
            <div className='w-[240px] p-6 bg-white border border-gray-200 rounded-lg shadow-lg'>
              <div className='mb-1 text-sm font-medium text-gray-700'>{t('appDebug.feature.dataSet.queryVariable.noVar')}</div>
              <div className='text-xs leading-normal text-gray-500'>{t('appDebug.feature.dataSet.queryVariable.noVarTip')}</div>
            </div>
          )}
app/components/app/configuration/dataset-config/index.tsx
@@ -1,11 +1,9 @@
'use client'
import type { FC } from 'react'
import React, { useCallback, useMemo } from 'react'
import React, { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { intersectionBy } from 'lodash-es'
import { useContext } from 'use-context-selector'
import produce from 'immer'
import { v4 as uuid4 } from 'uuid'
import { useFormattingChangedDispatcher } from '../debug/hooks'
import FeaturePanel from '../base/feature-panel'
import OperationBtn from '../base/operation-btn'
@@ -23,19 +21,13 @@
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useSelector as useAppContextSelector } from '@/context/app-context'
import { hasEditPermissionForDataset } from '@/utils/permission'
import MetadataFilter from '@/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter'
import type {
  HandleAddCondition,
  HandleRemoveCondition,
  HandleToggleConditionLogicalOperator,
  HandleUpdateCondition,
  MetadataFilteringModeEnum,
} from '@/app/components/workflow/nodes/knowledge-retrieval/types'
import {
  ComparisonOperator,
  LogicalOperator,
  MetadataFilteringVariableType,
} from '@/app/components/workflow/nodes/knowledge-retrieval/types'
const Icon = (
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path fillRule="evenodd" clipRule="evenodd" d="M12.6667 5.34368C12.6667 5.32614 12.6667 5.31738 12.6659 5.30147C12.6502 4.97229 12.3607 4.68295 12.0315 4.66737C12.0156 4.66662 12.0104 4.66662 12 4.66663H9.8391C9.30248 4.66662 8.85957 4.66661 8.49878 4.69609C8.12405 4.72671 7.77958 4.79242 7.45603 4.95728C6.95426 5.21294 6.54631 5.62089 6.29065 6.12265C6.12579 6.44621 6.06008 6.79068 6.02946 7.16541C5.99999 7.5262 5.99999 7.96911 6 8.50574V15.4942C5.99999 16.0308 5.99999 16.4737 6.02946 16.8345C6.06008 17.2092 6.12579 17.5537 6.29065 17.8773C6.54631 18.379 6.95426 18.787 7.45603 19.0426C7.77958 19.2075 8.12405 19.2732 8.49878 19.3038C8.85958 19.3333 9.30248 19.3333 9.83912 19.3333H14.1609C14.6975 19.3333 15.1404 19.3333 15.5012 19.3038C15.8759 19.2732 16.2204 19.2075 16.544 19.0426C17.0457 18.787 17.4537 18.379 17.7093 17.8773C17.8742 17.5537 17.9399 17.2092 17.9705 16.8345C18 16.4737 18 16.0308 18 15.4942V10.6666C18 10.6562 18 10.6511 17.9993 10.6352C17.9837 10.306 17.6943 10.0164 17.3651 10.0007C17.3492 9.99997 17.3405 9.99997 17.323 9.99997L14.3787 9.99997C14.2105 9.99999 14.0466 10 13.9078 9.98867C13.7555 9.97622 13.5756 9.94684 13.3947 9.85464C13.1438 9.72681 12.9398 9.52284 12.812 9.27195C12.7198 9.09101 12.6904 8.91118 12.678 8.75879C12.6666 8.62001 12.6666 8.45615 12.6667 8.2879L12.6667 5.34368ZM9.33333 12.6666C8.96514 12.6666 8.66667 12.9651 8.66667 13.3333C8.66667 13.7015 8.96514 14 9.33333 14H14.6667C15.0349 14 15.3333 13.7015 15.3333 13.3333C15.3333 12.9651 15.0349 12.6666 14.6667 12.6666H9.33333ZM9.33333 15.3333C8.96514 15.3333 8.66667 15.6318 8.66667 16C8.66667 16.3681 8.96514 16.6666 9.33333 16.6666H13.3333C13.7015 16.6666 14 16.3681 14 16C14 15.6318 13.7015 15.3333 13.3333 15.3333H9.33333Z" fill="#6938EF" />
    <path d="M16.6053 8.66662C16.8011 8.66662 16.8989 8.66663 16.9791 8.61747C17.0923 8.54806 17.16 8.38452 17.129 8.25538C17.107 8.16394 17.0432 8.10018 16.9155 7.97265L14.694 5.75111C14.5664 5.62345 14.5027 5.55962 14.4112 5.53764C14.2821 5.5066 14.1186 5.57429 14.0492 5.68752C14 5.7677 14 5.86557 14 6.06132L14 8.13327C14 8.31994 14 8.41328 14.0363 8.48459C14.0683 8.54731 14.1193 8.5983 14.182 8.63026C14.2533 8.66659 14.3466 8.66659 14.5333 8.66659L16.6053 8.66662Z" fill="#6938EF" />
  </svg>
)
const DatasetConfig: FC = () => {
  const { t } = useTranslation()
@@ -49,7 +41,6 @@
    showSelectDataSet,
    isAgent,
    datasetConfigs,
    datasetConfigsRef,
    setDatasetConfigs,
    setRerankSettingModalOpen,
  } = useContext(ConfigContext)
@@ -131,100 +122,10 @@
    })
  }, [dataSet, userProfile?.id])
  const metadataList = useMemo(() => {
    return intersectionBy(...formattedDataset.filter((dataset) => {
      return !!dataset.doc_metadata
    }).map((dataset) => {
      return dataset.doc_metadata!
    }), 'name')
  }, [formattedDataset])
  const handleMetadataFilterModeChange = useCallback((newMode: MetadataFilteringModeEnum) => {
    setDatasetConfigs(produce(datasetConfigsRef.current!, (draft) => {
      draft.metadata_filtering_mode = newMode
    }))
  }, [setDatasetConfigs, datasetConfigsRef])
  const handleAddCondition = useCallback<HandleAddCondition>(({ name, type }) => {
    let operator: ComparisonOperator = ComparisonOperator.is
    if (type === MetadataFilteringVariableType.number)
      operator = ComparisonOperator.equal
    const newCondition = {
      id: uuid4(),
      name,
      comparison_operator: operator,
    }
    const newInputs = produce(datasetConfigsRef.current!, (draft) => {
      if (draft.metadata_filtering_conditions) {
        draft.metadata_filtering_conditions.conditions.push(newCondition)
      }
      else {
        draft.metadata_filtering_conditions = {
          logical_operator: LogicalOperator.and,
          conditions: [newCondition],
        }
      }
    })
    setDatasetConfigs(newInputs)
  }, [setDatasetConfigs, datasetConfigsRef])
  const handleRemoveCondition = useCallback<HandleRemoveCondition>((id) => {
    const conditions = datasetConfigsRef.current!.metadata_filtering_conditions?.conditions || []
    const index = conditions.findIndex(c => c.id === id)
    const newInputs = produce(datasetConfigsRef.current!, (draft) => {
      if (index > -1)
        draft.metadata_filtering_conditions?.conditions.splice(index, 1)
    })
    setDatasetConfigs(newInputs)
  }, [setDatasetConfigs, datasetConfigsRef])
  const handleUpdateCondition = useCallback<HandleUpdateCondition>((id, newCondition) => {
    const conditions = datasetConfigsRef.current!.metadata_filtering_conditions?.conditions || []
    const index = conditions.findIndex(c => c.id === id)
    const newInputs = produce(datasetConfigsRef.current!, (draft) => {
      if (index > -1)
        draft.metadata_filtering_conditions!.conditions[index] = newCondition
    })
    setDatasetConfigs(newInputs)
  }, [setDatasetConfigs, datasetConfigsRef])
  const handleToggleConditionLogicalOperator = useCallback<HandleToggleConditionLogicalOperator>(() => {
    const oldLogicalOperator = datasetConfigsRef.current!.metadata_filtering_conditions?.logical_operator
    const newLogicalOperator = oldLogicalOperator === LogicalOperator.and ? LogicalOperator.or : LogicalOperator.and
    const newInputs = produce(datasetConfigsRef.current!, (draft) => {
      draft.metadata_filtering_conditions!.logical_operator = newLogicalOperator
    })
    setDatasetConfigs(newInputs)
  }, [setDatasetConfigs, datasetConfigsRef])
  const handleMetadataModelChange = useCallback((model: { provider: string; modelId: string; mode?: string }) => {
    const newInputs = produce(datasetConfigsRef.current!, (draft) => {
      draft.metadata_model_config = {
        provider: model.provider,
        name: model.modelId,
        mode: model.mode || 'chat',
        completion_params: draft.metadata_model_config?.completion_params || { temperature: 0.7 },
      }
    })
    setDatasetConfigs(newInputs)
  }, [setDatasetConfigs, datasetConfigsRef])
  const handleMetadataCompletionParamsChange = useCallback((newParams: Record<string, any>) => {
    const newInputs = produce(datasetConfigsRef.current!, (draft) => {
      draft.metadata_model_config = {
        ...draft.metadata_model_config!,
        completion_params: newParams,
      }
    })
    setDatasetConfigs(newInputs)
  }, [setDatasetConfigs, datasetConfigsRef])
  return (
    <FeaturePanel
      className='mt-2'
      headerIcon={Icon}
      title={t('appDebug.feature.dataSet.title')}
      headerRight={
        <div className='flex items-center gap-1'>
@@ -237,7 +138,7 @@
    >
      {hasData
        ? (
          <div className='mt-1 flex flex-wrap justify-between px-3 pb-3'>
          <div className='flex flex-wrap mt-1 px-3 pb-3 justify-between'>
            {formattedDataset.map(item => (
              <CardItem
                key={item.id}
@@ -251,29 +152,9 @@
        )
        : (
          <div className='mt-1 px-3 pb-3'>
            <div className='pb-1 pt-2 text-xs text-text-tertiary'>{t('appDebug.feature.dataSet.noData')}</div>
            <div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.feature.dataSet.noData')}</div>
          </div>
        )}
      <div className='border-t border-t-divider-subtle py-2'>
        <MetadataFilter
          metadataList={metadataList}
          selectedDatasetsLoaded
          metadataFilterMode={datasetConfigs.metadata_filtering_mode}
          metadataFilteringConditions={datasetConfigs.metadata_filtering_conditions}
          handleAddCondition={handleAddCondition}
          handleMetadataFilterModeChange={handleMetadataFilterModeChange}
          handleRemoveCondition={handleRemoveCondition}
          handleToggleConditionLogicalOperator={handleToggleConditionLogicalOperator}
          handleUpdateCondition={handleUpdateCondition}
          metadataModelConfig={datasetConfigs.metadata_model_config}
          handleMetadataModelChange={handleMetadataModelChange}
          handleMetadataCompletionParamsChange={handleMetadataCompletionParamsChange}
          isCommonVariable
          availableCommonStringVars={promptVariablesToSelect.filter(item => item.type === MetadataFilteringVariableType.string || item.type === MetadataFilteringVariableType.select)}
          availableCommonNumberVars={promptVariablesToSelect.filter(item => item.type === MetadataFilteringVariableType.number)}
        />
      </div>
      {mode === AppType.completion && dataSet.length > 0 && (
        <ContextVar
app/components/app/configuration/dataset-config/params-config/config-content.tsx
@@ -24,8 +24,6 @@
import { useSelectedDatasetsMode } from '@/app/components/workflow/nodes/knowledge-retrieval/hooks'
import Switch from '@/app/components/base/switch'
import Toast from '@/app/components/base/toast'
import Divider from '@/app/components/base/divider'
import { noop } from 'lodash-es'
type Props = {
  datasetConfigs: DatasetConfigs
@@ -42,8 +40,8 @@
  onChange,
  isInWorkflow,
  singleRetrievalModelConfig: singleRetrievalConfig = {} as ModelConfig,
  onSingleRetrievalModelChange = noop,
  onSingleRetrievalModelParamsChange = noop,
  onSingleRetrievalModelChange = () => { },
  onSingleRetrievalModelParamsChange = () => { },
  selectedDatasets = [],
}) => {
  const { t } = useTranslation()
@@ -186,30 +184,30 @@
      </div>
      {type === RETRIEVE_TYPE.multiWay && (
        <>
          <div className='my-2 flex h-6 items-center py-1'>
            <div className='system-xs-semibold-uppercase mr-2 shrink-0 text-text-secondary'>
          <div className='flex items-center my-2 py-1 h-6'>
            <div className='shrink-0 mr-2 system-xs-semibold-uppercase text-text-secondary'>
              {t('dataset.rerankSettings')}
            </div>
            <Divider bgStyle='gradient' className='mx-0 !h-px' />
            <div className='grow h-[1px] bg-gradient-to-l from-white to-[rgba(16,24,40,0.08)]'></div>
          </div>
          {
            selectedDatasetsMode.inconsistentEmbeddingModel
            && (
              <div className='system-xs-medium mt-4 text-text-warning'>
              <div className='mt-4 system-xs-medium text-text-warning'>
                {t('dataset.inconsistentEmbeddingModelTip')}
              </div>
            )
          }
          {
            selectedDatasetsMode.mixtureInternalAndExternal && (
              <div className='system-xs-medium mt-4 text-text-warning'>
              <div className='mt-4 system-xs-medium text-text-warning'>
                {t('dataset.mixtureInternalAndExternalTip')}
              </div>
            )
          }
          {
            selectedDatasetsMode.allExternal && (
              <div className='system-xs-medium mt-4 text-text-warning'>
              <div className='mt-4 system-xs-medium text-text-warning'>
                {t('dataset.allExternalTip')}
              </div>
            )
@@ -217,7 +215,7 @@
          {
            selectedDatasetsMode.mixtureHighQualityAndEconomic
            && (
              <div className='system-xs-medium mt-4 text-text-warning'>
              <div className='mt-4 system-xs-medium text-text-warning'>
                {t('dataset.mixtureHighQualityAndEconomicTip')}
              </div>
            )
@@ -230,7 +228,7 @@
                    <div
                      key={option.value}
                      className={cn(
                        'system-sm-medium flex h-8 w-[calc((100%-8px)/2)] cursor-pointer items-center justify-center rounded-lg border border-components-option-card-option-border bg-components-option-card-option-bg text-text-secondary',
                        'flex items-center justify-center w-[calc((100%-8px)/2)] h-8 rounded-lg border border-components-option-card-option-border bg-components-option-card-option-bg cursor-pointer system-sm-medium text-text-secondary',
                        selectedRerankMode === option.value && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary',
                      )}
                      onClick={() => handleRerankModeChange(option.value)}
@@ -265,7 +263,7 @@
                      />
                    )
                  }
                  <div className='system-sm-semibold ml-1 leading-[32px] text-text-secondary'>{t('common.modelProvider.rerankModel.key')}</div>
                  <div className='leading-[32px] ml-1 text-text-secondary system-sm-semibold'>{t('common.modelProvider.rerankModel.key')}</div>
                  <Tooltip
                    popupContent={
                      <div className="w-[200px]">
@@ -354,7 +352,7 @@
      {isInWorkflow && type === RETRIEVE_TYPE.oneWay && (
        <div className='mt-4'>
          <div className='flex items-center space-x-0.5'>
            <div className='text-[13px] font-medium leading-[32px] text-text-primary'>{t('common.modelProvider.systemReasoningModel.key')}</div>
            <div className='leading-[32px] text-[13px] font-medium text-gray-900'>{t('common.modelProvider.systemReasoningModel.key')}</div>
            <Tooltip
              popupContent={t('common.modelProvider.systemReasoningModel.tip')}
            />
@@ -368,8 +366,8 @@
            provider={model?.provider}
            completionParams={model?.completion_params}
            modelId={model?.name}
            setModel={onSingleRetrievalModelChange}
            onCompletionParamsChange={onSingleRetrievalModelParamsChange}
            setModel={onSingleRetrievalModelChange as any}
            onCompletionParamsChange={onSingleRetrievalModelParamsChange as any}
            hideDebugWithMultipleModel
            debugWithMultipleModel={false}
          />
app/components/app/configuration/dataset-config/params-config/index.tsx
@@ -121,7 +121,7 @@
        }}
        disabled={disabled}
      >
        <RiEqualizer2Line className='mr-1 h-3.5 w-3.5' />
        <RiEqualizer2Line className='mr-1 w-3.5 h-3.5' />
        {t('dataset.retrievalSettings')}
      </Button>
      {
@@ -140,11 +140,11 @@
            />
            <div className='mt-6 flex justify-end'>
              <Button className='mr-2 shrink-0' onClick={() => {
              <Button className='mr-2 flex-shrink-0' onClick={() => {
                setTempDataSetConfigs(datasetConfigs)
                setRerankSettingModalOpen(false)
              }}>{t('common.operation.cancel')}</Button>
              <Button variant='primary' className='shrink-0' onClick={handleSave} >{t('common.operation.save')}</Button>
              <Button variant='primary' className='flex-shrink-0' onClick={handleSave} >{t('common.operation.save')}</Button>
            </div>
          </Modal>
        )
app/components/app/configuration/dataset-config/params-config/weighted-score.css
app/components/app/configuration/dataset-config/params-config/weighted-score.tsx
@@ -3,7 +3,6 @@
import './weighted-score.css'
import Slider from '@/app/components/base/slider'
import cn from '@/utils/classnames'
import { noop } from 'lodash-es'
const formatNumber = (value: number) => {
  if (value > 0 && value < 1)
@@ -24,15 +23,15 @@
}
const WeightedScore = ({
  value,
  onChange = noop,
  onChange = () => {},
}: WeightedScoreProps) => {
  const { t } = useTranslation()
  return (
    <div>
      <div className='space-x-3 rounded-lg border border-components-panel-border px-3 pb-2 pt-5'>
      <div className='px-3 pt-5 pb-2 space-x-3 rounded-lg border border-components-panel-border'>
        <Slider
          className={cn('h-0.5 grow rounded-full !bg-util-colors-teal-teal-500')}
          className={cn('grow h-0.5 !bg-util-colors-teal-teal-500 rounded-full')}
          max={1.0}
          min={0}
          step={0.1}
@@ -40,14 +39,14 @@
          onChange={v => onChange({ value: [v, (10 - v * 10) / 10] })}
          trackClassName='weightedScoreSliderTrack'
        />
        <div className='mt-3 flex justify-between'>
          <div className='system-xs-semibold-uppercase flex w-[90px] shrink-0 items-center text-util-colors-blue-light-blue-light-500'>
        <div className='flex justify-between mt-3'>
          <div className='shrink-0 flex items-center w-[90px] system-xs-semibold-uppercase text-util-colors-blue-light-blue-light-500'>
            <div className='mr-1 truncate uppercase' title={t('dataset.weightedScore.semantic') || ''}>
              {t('dataset.weightedScore.semantic')}
            </div>
            {formatNumber(value.value[0])}
          </div>
          <div className='system-xs-semibold-uppercase flex w-[90px] shrink-0 items-center justify-end text-util-colors-teal-teal-500'>
          <div className='shrink-0 flex items-center justify-end w-[90px] system-xs-semibold-uppercase text-util-colors-teal-teal-500'>
            {formatNumber(value.value[1])}
            <div className='ml-1 truncate uppercase' title={t('dataset.weightedScore.keyword') || ''}>
              {t('dataset.weightedScore.keyword')}
app/components/app/configuration/dataset-config/select-dataset/index.tsx
@@ -6,6 +6,8 @@
import Link from 'next/link'
import produce from 'immer'
import TypeIcon from '../type-icon'
import s from './style.module.css'
import cn from '@/utils/classnames'
import Modal from '@/app/components/base/modal'
import type { DataSet } from '@/models/datasets'
import Button from '@/app/components/base/button'
@@ -13,7 +15,6 @@
import Loading from '@/app/components/base/loading'
import Badge from '@/app/components/base/badge'
import { useKnowledge } from '@/hooks/use-knowledge'
import cn from '@/utils/classnames'
export type ISelectDataSetProps = {
  isShow: boolean
@@ -104,54 +105,49 @@
      )}
      {(loaded && hasNoData) && (
        <div className='mt-6 flex h-[128px] items-center justify-center space-x-1  rounded-lg border text-[13px]'
        <div className='flex items-center justify-center mt-6 rounded-lg space-x-1  h-[128px] text-[13px] border'
          style={{
            background: 'rgba(0, 0, 0, 0.02)',
            borderColor: 'rgba(0, 0, 0, 0.02',
          }}
        >
          <span className='text-text-tertiary'>{t('appDebug.feature.dataSet.noDataSet')}</span>
          <Link href={'/datasets/create'} className='font-normal text-text-accent'>{t('appDebug.feature.dataSet.toCreate')}</Link>
          <span className='text-gray-500'>{t('appDebug.feature.dataSet.noDataSet')}</span>
          <Link href="/datasets/create" className='font-normal text-[#155EEF]'>{t('appDebug.feature.dataSet.toCreate')}</Link>
        </div>
      )}
      {datasets && datasets?.length > 0 && (
        <>
          <div ref={listRef} className='mt-7 max-h-[286px] space-y-1 overflow-y-auto'>
          <div ref={listRef} className='mt-7 space-y-1 max-h-[286px] overflow-y-auto'>
            {datasets.map(item => (
              <div
                key={item.id}
                className={cn(
                  'flex h-10 cursor-pointer items-center justify-between rounded-lg border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg px-2 shadow-xs hover:border-components-panel-border hover:bg-components-panel-on-panel-item-bg-hover hover:shadow-sm',
                  selected.some(i => i.id === item.id) && 'border-[1.5px] border-components-option-card-option-selected-border bg-state-accent-hover shadow-xs hover:border-components-option-card-option-selected-border hover:bg-state-accent-hover hover:shadow-xs',
                  !item.embedding_available && 'hover:border-components-panel-border-subtle hover:bg-components-panel-on-panel-item-bg hover:shadow-xs',
                )}
                className={cn(s.item, selected.some(i => i.id === item.id) && s.selected, 'flex justify-between items-center h-10 px-2 rounded-lg bg-white border border-gray-200  cursor-pointer', !item.embedding_available && s.disabled)}
                onClick={() => {
                  if (!item.embedding_available)
                    return
                  toggleSelect(item)
                }}
              >
                <div className='mr-1 flex items-center overflow-hidden'>
                  <div className={cn('mr-2', !item.embedding_available && 'opacity-30')}>
                <div className='mr-1 flex items-center'>
                  <div className={cn('mr-2', !item.embedding_available && 'opacity-50')}>
                    <TypeIcon type="upload_file" size='md' />
                  </div>
                  <div className={cn('max-w-[200px] truncate text-[13px] font-medium text-text-secondary', !item.embedding_available && '!max-w-[120px] opacity-30')}>{item.name}</div>
                  <div className={cn('max-w-[200px] text-[13px] font-medium text-gray-800 overflow-hidden text-ellipsis whitespace-nowrap', !item.embedding_available && 'opacity-50 !max-w-[120px]')}>{item.name}</div>
                  {!item.embedding_available && (
                    <span className='ml-1 shrink-0 rounded-md border border-divider-deep px-1 text-xs font-normal leading-[18px] text-text-tertiary'>{t('dataset.unavailable')}</span>
                    <span className='ml-1 shrink-0 px-1 border border-gray-200 rounded-md text-gray-500 text-xs font-normal leading-[18px]'>{t('dataset.unavailable')}</span>
                  )}
                </div>
                {
                  item.indexing_technique && (
                    <Badge
                      className='shrink-0'
                      text={formatIndexingTechniqueAndMethod(item.indexing_technique, item.retrieval_model_dict?.search_method)}
                    />
                  )
                }
                {
                  item.provider === 'external' && (
                    <Badge className='shrink-0' text={t('dataset.externalTag')} />
                    <Badge text={t('dataset.externalTag')} />
                  )
                }
              </div>
@@ -160,8 +156,8 @@
        </>
      )}
      {loaded && (
        <div className='mt-8 flex items-center justify-between'>
          <div className='text-sm  font-medium text-text-secondary'>
        <div className='flex justify-between items-center mt-8'>
          <div className='text-sm  font-medium text-gray-700'>
            {selected.length > 0 && `${selected.length} ${t('appDebug.feature.dataSet.selected')}`}
          </div>
          <div className='flex space-x-2'>
app/components/app/configuration/dataset-config/select-dataset/style.module.css
New file
@@ -0,0 +1,13 @@
.item {
  box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
}
.item:hover,
.item.selected {
  background: #F5F8FF;
  border-color: #528BFF;
}
.item.disabled {
  @apply bg-white border-gray-200 cursor-default;
}
app/components/app/configuration/dataset-config/settings-modal/index.tsx
@@ -4,6 +4,7 @@
import { useTranslation } from 'react-i18next'
import { isEqual } from 'lodash-es'
import { RiCloseLine } from '@remixicon/react'
import { BookOpenIcon } from '@heroicons/react/24/outline'
import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/development'
import cn from '@/utils/classnames'
import IndexMethodRadio from '@/app/components/datasets/settings/index-method-radio'
@@ -61,13 +62,13 @@
  const { notify } = useToastContext()
  const ref = useRef(null)
  const isExternal = currentDataset.provider === 'external'
  const [topK, setTopK] = useState(currentDataset?.external_retrieval_model.top_k ?? 2)
  const [scoreThreshold, setScoreThreshold] = useState(currentDataset?.external_retrieval_model.score_threshold ?? 0.5)
  const [scoreThresholdEnabled, setScoreThresholdEnabled] = useState(currentDataset?.external_retrieval_model.score_threshold_enabled ?? false)
  const { setShowAccountSettingModal } = useModalContext()
  const [loading, setLoading] = useState(false)
  const { isCurrentWorkspaceDatasetOperator } = useAppContext()
  const [localeCurrentDataset, setLocaleCurrentDataset] = useState({ ...currentDataset })
  const [topK, setTopK] = useState(localeCurrentDataset?.external_retrieval_model.top_k ?? 2)
  const [scoreThreshold, setScoreThreshold] = useState(localeCurrentDataset?.external_retrieval_model.score_threshold ?? 0.5)
  const [scoreThresholdEnabled, setScoreThresholdEnabled] = useState(localeCurrentDataset?.external_retrieval_model.score_threshold_enabled ?? false)
  const [selectedMemberIDs, setSelectedMemberIDs] = useState<string[]>(currentDataset.partial_member_list || [])
  const [memberList, setMemberList] = useState<Member[]>([])
@@ -87,14 +88,6 @@
      setScoreThreshold(data.score_threshold)
    if (data.score_threshold_enabled !== undefined)
      setScoreThresholdEnabled(data.score_threshold_enabled)
    setLocaleCurrentDataset({
      ...localeCurrentDataset,
      external_retrieval_model: {
        ...localeCurrentDataset?.external_retrieval_model,
        ...data,
      },
    })
  }
  const handleSave = async () => {
@@ -157,7 +150,7 @@
        retrieval_model_dict: retrievalConfig,
      })
    }
    catch {
    catch (e) {
      notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
    }
    finally {
@@ -179,30 +172,32 @@
  return (
    <div
      className='flex w-full flex-col overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl'
      className='overflow-hidden w-full flex flex-col bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl'
      style={{
        height: 'calc(100vh - 72px)',
      }}
      ref={ref}
    >
      <div className='flex h-14 shrink-0 items-center justify-between border-b border-divider-regular pl-6 pr-5'>
        <div className='flex flex-col text-base font-semibold text-text-primary'>
      <div className='shrink-0 flex justify-between items-center pl-6 pr-5 h-14 border-b border-b-gray-100'>
        <div className='flex flex-col text-base font-semibold text-gray-900'>
          <div className='leading-6'>{t('datasetSettings.title')}</div>
        </div>
        <div className='flex items-center'>
          <div
            onClick={onCancel}
            className='flex h-6 w-6 cursor-pointer items-center justify-center'
            className='flex justify-center items-center w-6 h-6 cursor-pointer'
          >
            <RiCloseLine className='h-4 w-4 text-text-tertiary' />
            <RiCloseLine className='w-4 h-4 text-gray-500' />
          </div>
        </div>
      </div>
      {/* Body */}
      <div className='overflow-y-auto border-b border-divider-regular p-6 pb-[68px] pt-5'>
      <div className='p-6 pt-5 border-b overflow-y-auto pb-[68px]' style={{
        borderBottom: 'rgba(0, 0, 0, 0.05)',
      }}>
        <div className={cn(rowClass, 'items-center')}>
          <div className={labelClass}>
            <div className='system-sm-semibold text-text-secondary'>{t('datasetSettings.form.name')}</div>
            <div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.name')}</div>
          </div>
          <Input
            value={localeCurrentDataset.name}
@@ -213,7 +208,7 @@
        </div>
        <div className={cn(rowClass)}>
          <div className={labelClass}>
            <div className='system-sm-semibold text-text-secondary'>{t('datasetSettings.form.desc')}</div>
            <div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.desc')}</div>
          </div>
          <div className='w-full'>
            <Textarea
@@ -222,11 +217,15 @@
              className='resize-none'
              placeholder={t('datasetSettings.form.descPlaceholder') || ''}
            />
            <a className='mt-2 flex items-center h-[18px] px-3 text-xs text-gray-500' href="https://docs.dify.ai/features/datasets#how-to-write-a-good-dataset-description" target='_blank' rel='noopener noreferrer'>
              <BookOpenIcon className='w-3 h-[18px] mr-1' />
              {t('datasetSettings.form.descWrite')}
            </a>
          </div>
        </div>
        <div className={rowClass}>
          <div className={labelClass}>
            <div className='system-sm-semibold text-text-secondary'>{t('datasetSettings.form.permissions')}</div>
            <div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.permissions')}</div>
          </div>
          <div className='w-full'>
            <PermissionSelector
@@ -242,7 +241,7 @@
        {currentDataset && currentDataset.indexing_technique && (
          <div className={cn(rowClass)}>
            <div className={labelClass}>
              <div className='system-sm-semibold text-text-secondary'>{t('datasetSettings.form.indexMethod')}</div>
              <div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.indexMethod')}</div>
            </div>
            <div className='grow'>
              <IndexMethodRadio
@@ -258,10 +257,10 @@
        {indexMethod === 'high_quality' && (
          <div className={cn(rowClass)}>
            <div className={labelClass}>
              <div className='system-sm-semibold text-text-secondary'>{t('datasetSettings.form.embeddingModel')}</div>
              <div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.embeddingModel')}</div>
            </div>
            <div className='w-full'>
              <div className='h-8 w-full rounded-lg bg-components-input-bg-normal opacity-60'>
              <div className='w-full h-9 rounded-lg bg-gray-100 opacity-60'>
                <ModelSelector
                  readonly
                  defaultModel={{
@@ -271,9 +270,9 @@
                  modelList={embeddingsModelList}
                />
              </div>
              <div className='mt-2 w-full text-xs leading-6 text-text-tertiary'>
              <div className='mt-2 w-full text-xs leading-6 text-gray-500'>
                {t('datasetSettings.form.embeddingModelTip')}
                <span className='cursor-pointer text-text-accent' onClick={() => setShowAccountSettingModal({ payload: 'provider' })}>{t('datasetSettings.form.embeddingModelTipLink')}</span>
                <span className='text-[#155eef] cursor-pointer' onClick={() => setShowAccountSettingModal({ payload: 'provider' })}>{t('datasetSettings.form.embeddingModelTipLink')}</span>
              </div>
            </div>
          </div>
@@ -285,7 +284,7 @@
            <div className={rowClass}><Divider /></div>
            <div className={rowClass}>
              <div className={labelClass}>
                <div className='system-sm-semibold text-text-secondary'>{t('datasetSettings.form.retrievalSetting.title')}</div>
                <div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.retrievalSetting.title')}</div>
              </div>
              <RetrievalSettings
                topK={topK}
@@ -298,26 +297,26 @@
            <div className={rowClass}><Divider /></div>
            <div className={rowClass}>
              <div className={labelClass}>
                <div className='system-sm-semibold text-text-secondary'>{t('datasetSettings.form.externalKnowledgeAPI')}</div>
                <div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.externalKnowledgeAPI')}</div>
              </div>
              <div className='w-full max-w-[480px]'>
                <div className='flex h-full items-center gap-1 rounded-lg bg-components-input-bg-normal px-3 py-2'>
                  <ApiConnectionMod className='h-4 w-4 text-text-secondary' />
                  <div className='system-sm-medium overflow-hidden text-ellipsis text-text-secondary'>
                <div className='flex h-full px-3 py-2 items-center gap-1 rounded-lg bg-components-input-bg-normal'>
                  <ApiConnectionMod className='w-4 h-4 text-text-secondary' />
                  <div className='overflow-hidden text-text-secondary text-ellipsis system-sm-medium'>
                    {currentDataset?.external_knowledge_info.external_knowledge_api_name}
                  </div>
                  <div className='system-xs-regular text-text-tertiary'>·</div>
                  <div className='system-xs-regular text-text-tertiary'>{currentDataset?.external_knowledge_info.external_knowledge_api_endpoint}</div>
                  <div className='text-text-tertiary system-xs-regular'>·</div>
                  <div className='text-text-tertiary system-xs-regular'>{currentDataset?.external_knowledge_info.external_knowledge_api_endpoint}</div>
                </div>
              </div>
            </div>
            <div className={rowClass}>
              <div className={labelClass}>
                <div className='system-sm-semibold text-text-secondary'>{t('datasetSettings.form.externalKnowledgeID')}</div>
                <div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.externalKnowledgeID')}</div>
              </div>
              <div className='w-full max-w-[480px]'>
                <div className='flex h-full items-center gap-1 rounded-lg bg-components-input-bg-normal px-3 py-2'>
                  <div className='system-xs-regular text-text-tertiary'>{currentDataset?.external_knowledge_info.external_knowledge_id}</div>
                <div className='flex h-full px-3 py-2 items-center gap-1 rounded-lg bg-components-input-bg-normal'>
                  <div className='text-text-tertiary system-xs-regular'>{currentDataset?.external_knowledge_info.external_knowledge_id}</div>
                </div>
              </div>
            </div>
@@ -326,9 +325,9 @@
          : <div className={rowClass}>
            <div className={cn(labelClass, 'w-auto min-w-[168px]')}>
              <div>
                <div className='system-sm-semibold text-text-secondary'>{t('datasetSettings.form.retrievalSetting.title')}</div>
                <div className='text-xs font-normal leading-[18px] text-text-tertiary'>
                  <a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
                <div className='text-text-secondary system-sm-semibold'>{t('datasetSettings.form.retrievalSetting.title')}</div>
                <div className='leading-[18px] text-xs font-normal text-gray-500'>
                  <a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-[#155eef]'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a>
                  {t('datasetSettings.form.retrievalSetting.description')}
                </div>
              </div>
@@ -351,23 +350,26 @@
          </div>}
      </div>
      {isRetrievalChanged && !isHideChangedTip && (
        <div className='absolute bottom-[76px] left-[30px] right-[30px] z-10 flex h-10 items-center justify-between rounded-lg border border-[#FEF0C7] bg-[#FFFAEB] px-3 shadow-lg'>
        <div className='absolute z-10 left-[30px] right-[30px] bottom-[76px] flex h-10 items-center px-3 rounded-lg border border-[#FEF0C7] bg-[#FFFAEB] shadow-lg justify-between'>
          <div className='flex items-center'>
            <AlertTriangle className='mr-1 h-3 w-3 text-[#F79009]' />
            <div className='text-xs font-medium leading-[18px] text-gray-700'>{t('appDebug.datasetConfig.retrieveChangeTip')}</div>
            <AlertTriangle className='mr-1 w-3 h-3 text-[#F79009]' />
            <div className='leading-[18px] text-xs font-medium text-gray-700'>{t('appDebug.datasetConfig.retrieveChangeTip')}</div>
          </div>
          <div className='cursor-pointer p-1' onClick={(e) => {
          <div className='p-1 cursor-pointer' onClick={(e) => {
            setIsHideChangedTip(true)
            e.stopPropagation()
            e.nativeEvent.stopImmediatePropagation()
          }}>
            <RiCloseLine className='h-4 w-4 text-gray-500' />
            <RiCloseLine className='w-4 h-4 text-gray-500 ' />
          </div>
        </div>
      )}
      <div
        className='sticky bottom-0 z-[5] flex w-full justify-end border-t border-divider-regular bg-background-section px-6 py-4'
        className='sticky z-[5] bottom-0 w-full flex justify-end py-4 px-6 border-t bg-white '
        style={{
          borderColor: 'rgba(0, 0, 0, 0.05)',
        }}
      >
        <Button
          onClick={onCancel}
app/components/app/configuration/debug/chat-user-input.tsx
@@ -47,17 +47,17 @@
    return null
  return (
    <div className={cn('z-[1] rounded-xl border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg shadow-xs')}>
      <div className='px-4 pb-4 pt-3'>
    <div className={cn('bg-components-panel-on-panel-item-bg rounded-xl border-[0.5px] border-components-panel-border-subtle shadow-xs z-[1]')}>
      <div className='px-4 pt-3 pb-4'>
        {promptVariables.map(({ key, name, type, options, max_length, required }, index) => (
          <div
            key={key}
            className='mb-4 last-of-type:mb-0'
          >
            <div>
              <div className='system-sm-semibold mb-1 flex h-6 items-center gap-1 text-text-secondary'>
              <div className='h-6 mb-1 flex items-center gap-1 text-text-secondary system-sm-semibold'>
                <div className='truncate'>{name || key}</div>
                {!required && <span className='system-xs-regular text-text-tertiary'>{t('workflow.panel.optional')}</span>}
                {!required && <span className='text-text-tertiary system-xs-regular'>{t('workflow.panel.optional')}</span>}
              </div>
              <div className='grow'>
                {type === 'string' && (
@@ -71,7 +71,7 @@
                )}
                {type === 'paragraph' && (
                  <Textarea
                    className='h-[120px] grow'
                    className='grow h-[120px]'
                    placeholder={name}
                    value={inputs[key] ? `${inputs[key]}` : ''}
                    onChange={(e) => { handleInputValueChange(key, e.target.value) }}
@@ -84,6 +84,7 @@
                    onSelect={(i) => { handleInputValueChange(key, i.value as string) }}
                    items={(options || []).map(i => ({ name: i, value: i }))}
                    allowSearch={false}
                    bgClassName='bg-gray-50'
                  />
                )}
                {type === 'number' && (
app/components/app/configuration/debug/debug-with-multiple-model/chat-item.tsx
@@ -30,7 +30,6 @@
import { useFeatures } from '@/app/components/base/features/hooks'
import type { InputForm } from '@/app/components/base/chat/chat/type'
import { getLastAnswer } from '@/app/components/base/chat/utils'
import { canFindTool } from '@/utils'
type ChatItemProps = {
  modelAndParameter: ModelAndParameter
@@ -129,7 +128,7 @@
  const allToolIcons = useMemo(() => {
    const icons: Record<string, any> = {}
    modelConfig.agentConfig.tools?.forEach((item: any) => {
      icons[item.tool_name] = collectionList.find((collection: any) => canFindTool(collection.id, item.provider_id))?.icon
      icons[item.tool_name] = collectionList.find((collection: any) => collection.id === item.provider_id)?.icon
    })
    return icons
  }, [collectionList, modelConfig.agentConfig.tools])
app/components/app/configuration/debug/debug-with-multiple-model/context.tsx
@@ -2,7 +2,6 @@
import { createContext, useContext } from 'use-context-selector'
import type { ModelAndParameter } from '../types'
import { noop } from 'lodash-es'
export type DebugWithMultipleModelContextType = {
  multipleModelConfigs: ModelAndParameter[]
@@ -12,8 +11,8 @@
}
const DebugWithMultipleModelContext = createContext<DebugWithMultipleModelContextType>({
  multipleModelConfigs: [],
  onMultipleModelConfigsChange: noop,
  onDebugWithMultipleModelChange: noop,
  onMultipleModelConfigsChange: () => {},
  onDebugWithMultipleModelChange: () => {},
})
export const useDebugWithMultipleModelContext = () => useContext(DebugWithMultipleModelContext)
app/components/app/configuration/debug/debug-with-multiple-model/debug-item.tsx
@@ -64,11 +64,11 @@
  return (
    <div
      className={`flex min-w-[320px] flex-col rounded-xl bg-background-section-burn ${className}`}
      className={`flex flex-col min-w-[320px] rounded-xl bg-white border-[0.5px] border-black/5 ${className}`}
      style={style}
    >
      <div className='flex h-10 shrink-0 items-center justify-between border-b-[0.5px] border-divider-regular px-3'>
        <div className='flex h-5 w-6 items-center justify-center font-medium italic text-text-tertiary'>
      <div className='shrink-0 flex items-center justify-between h-10 px-3 border-b-[0.5px] border-b-black/5'>
        <div className='flex items-center justify-center w-6 h-5 font-medium italic text-gray-500'>
          #{index + 1}
        </div>
        <ModelParameterTrigger
app/components/app/configuration/debug/debug-with-multiple-model/index.tsx
@@ -102,10 +102,10 @@
  const inputsForm = modelConfig.configs.prompt_variables.filter(item => item.type !== 'api').map(item => ({ ...item, label: item.name, variable: item.key })) as InputForm[]
  return (
    <div className='flex h-full flex-col'>
    <div className='flex flex-col h-full'>
      <div
        className={`
          relative mb-3 grow overflow-auto px-6
          grow mb-3 relative px-6 overflow-auto
        `}
        style={{ height: isChatMode ? 'calc(100% - 60px)' : '100%' }}
      >
@@ -131,7 +131,7 @@
        }
      </div>
      {isChatMode && (
        <div className='shrink-0 px-6 pb-0'>
        <div className='shrink-0 pb-0 px-6'>
          <ChatInputArea
            showFeatureBar
            showFileUpload={false}
app/components/app/configuration/debug/debug-with-multiple-model/model-parameter-trigger.tsx
@@ -8,7 +8,6 @@
import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon'
import ModelName from '@/app/components/header/account-setting/model-provider-page/model-name'
import {
  type FormValue,
  MODEL_STATUS_TEXT,
  ModelStatusEnum,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
@@ -46,7 +45,7 @@
    }
    onMultipleModelConfigsChange(true, newModelConfigs)
  }
  const handleParamsChange = (params: FormValue) => {
  const handleParamsChange = (params: any) => {
    const newModelConfigs = [...multipleModelConfigs]
    newModelConfigs[index] = {
      ...newModelConfigs[index],
@@ -73,15 +72,15 @@
      }) => (
        <div
          className={`
            flex h-8 max-w-[200px] cursor-pointer items-center rounded-lg px-2
            ${open && 'bg-state-base-hover'}
            flex items-center max-w-[200px] h-8 px-2 rounded-lg cursor-pointer
            ${open && 'bg-gray-100'}
            ${currentModel && currentModel.status !== ModelStatusEnum.active && '!bg-[#FFFAEB]'}
          `}
        >
          {
            currentProvider && (
              <ModelIcon
                className='mr-1 !h-4 !w-4'
                className='mr-1 !w-4 !h-4'
                provider={currentProvider}
                modelName={currentModel?.model}
              />
@@ -89,31 +88,31 @@
          }
          {
            !currentProvider && (
              <div className='mr-1 flex h-4 w-4 items-center justify-center rounded'>
                <CubeOutline className='h-4 w-4 text-text-accent' />
              <div className='flex items-center justify-center mr-1 w-4 h-4 rounded border border-dashed border-primary-100'>
                <CubeOutline className='w-[11px] h-[11px] text-primary-600' />
              </div>
            )
          }
          {
            currentModel && (
              <ModelName
                className='mr-0.5 text-text-secondary'
                className='mr-0.5 text-gray-800'
                modelItem={currentModel}
              />
            )
          }
          {
            !currentModel && (
              <div className='mr-0.5 truncate text-[13px] font-medium text-text-accent'>
              <div className='mr-0.5 text-[13px] font-medium text-primary-600 truncate'>
                {t('common.modelProvider.selectModel')}
              </div>
            )
          }
          <RiArrowDownSLine className={`h-3 w-3 ${(currentModel && currentProvider) ? 'text-text-tertiary' : 'text-text-accent'}`} />
          <RiArrowDownSLine className={`w-3 h-3 ${(currentModel && currentProvider) ? 'text-gray-800' : 'text-primary-600'}`} />
          {
            currentModel && currentModel.status !== ModelStatusEnum.active && (
              <Tooltip popupContent={MODEL_STATUS_TEXT[currentModel.status][language]}>
                <AlertTriangle className='h-4 w-4 text-[#F79009]' />
                <AlertTriangle className='w-4 h-4 text-[#F79009]' />
              </Tooltip>
            )
          }
app/components/app/configuration/debug/debug-with-multiple-model/text-generation-item.tsx
@@ -14,7 +14,6 @@
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { useProviderContext } from '@/context/provider-context'
import { useFeatures } from '@/app/components/base/features/hooks'
import { noop } from 'lodash-es'
type TextGenerationItemProps = {
  modelAndParameter: ModelAndParameter
@@ -125,9 +124,18 @@
      doSend(v.payload.message, v.payload.files)
  })
  const varList = modelConfig.configs.prompt_variables.map((item: any) => {
    return {
      label: item.key,
      value: inputs[item.key],
    }
  })
  return (
    <TextGeneration
      className='flex h-full flex-col overflow-y-auto border-none'
      className='flex flex-col h-full overflow-y-auto border-none'
      innerClassName='grow flex flex-col'
      contentClassName='grow'
      content={completion}
      isLoading={!completion && isResponding}
      isResponding={isResponding}
@@ -135,8 +143,9 @@
      siteInfo={null}
      messageId={messageId}
      isError={false}
      onRetry={noop}
      inSidePanel
      onRetry={() => { }}
      appId={appId}
      varList={varList}
    />
  )
}
app/components/app/configuration/debug/debug-with-single-model/index.tsx
@@ -1,4 +1,10 @@
import { memo, useCallback, useImperativeHandle, useMemo } from 'react'
import {
  forwardRef,
  memo,
  useCallback,
  useImperativeHandle,
  useMemo,
} from 'react'
import {
  useConfigFromDebugContext,
  useFormattingChangedSubscription,
@@ -20,8 +26,6 @@
import { useFeatures } from '@/app/components/base/features/hooks'
import { getLastAnswer, isValidGeneratedAnswer } from '@/app/components/base/chat/utils'
import type { InputForm } from '@/app/components/base/chat/chat/type'
import { canFindTool } from '@/utils'
import type { FileEntity } from '@/app/components/base/file-uploader/types'
type DebugWithSingleModelProps = {
  checkCanSend?: () => boolean
@@ -29,14 +33,9 @@
export type DebugWithSingleModelRefType = {
  handleRestart: () => void
}
const DebugWithSingleModel = (
  {
    ref,
const DebugWithSingleModel = forwardRef<DebugWithSingleModelRefType, DebugWithSingleModelProps>(({
    checkCanSend,
  }: DebugWithSingleModelProps & {
    ref: React.RefObject<DebugWithSingleModelRefType>;
  },
) => {
}, ref) => {
  const { userProfile } = useAppContext()
  const {
    modelConfig,
@@ -126,20 +125,16 @@
    )
  }, [appId, chatList, checkCanSend, completionParams, config, handleSend, inputs, modelConfig.mode, modelConfig.model_id, modelConfig.provider, textGenerationModelList])
  const doRegenerate = useCallback((chatItem: ChatItemInTree, editedQuestion?: { message: string, files?: FileEntity[] }) => {
    const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)!
  const doRegenerate = useCallback((chatItem: ChatItemInTree) => {
    const question = chatList.find(item => item.id === chatItem.parentMessageId)!
    const parentAnswer = chatList.find(item => item.id === question.parentMessageId)
    doSend(editedQuestion ? editedQuestion.message : question.content,
      editedQuestion ? editedQuestion.files : question.message_files,
      true,
      isValidGeneratedAnswer(parentAnswer) ? parentAnswer : null,
    )
    doSend(question.content, question.message_files, true, isValidGeneratedAnswer(parentAnswer) ? parentAnswer : null)
  }, [chatList, doSend])
  const allToolIcons = useMemo(() => {
    const icons: Record<string, any> = {}
    modelConfig.agentConfig.tools?.forEach((item: any) => {
      icons[item.tool_name] = collectionList.find((collection: any) => canFindTool(collection.id, item.provider_id))?.icon
      icons[item.tool_name] = collectionList.find((collection: any) => collection.id === item.provider_id)?.icon
    })
    return icons
  }, [collectionList, modelConfig.agentConfig.tools])
@@ -178,7 +173,7 @@
      noSpacing
    />
  )
}
})
DebugWithSingleModel.displayName = 'DebugWithSingleModel'
app/components/app/configuration/debug/index.tsx
@@ -47,7 +47,6 @@
import PromptLogModal from '@/app/components/base/prompt-log-modal'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
import { noop } from 'lodash-es'
type IDebug = {
  isAPIKeySet: boolean
@@ -392,8 +391,8 @@
  return (
    <>
      <div className="shrink-0">
        <div className='flex items-center justify-between px-4 pb-2 pt-3'>
          <div className='system-xl-semibold text-text-primary'>{t('appDebug.inputs.title')}</div>
        <div className='flex items-center justify-between px-4 pt-3 pb-2'>
          <div className='text-text-primary system-xl-semibold'>{t('appDebug.inputs.title')}</div>
          <div className='flex items-center'>
            {
              debugWithMultipleModel
@@ -404,10 +403,10 @@
                      onClick={() => onMultipleModelConfigsChange(true, [...multipleModelConfigs, { id: `${Date.now()}`, model: '', provider: '', parameters: {} }])}
                      disabled={multipleModelConfigs.length >= 4}
                    >
                      <RiAddLine className='mr-1 h-3.5 w-3.5' />
                      <RiAddLine className='mr-1 w-3.5 h-3.5' />
                      {t('common.modelProvider.addModel')}({multipleModelConfigs.length}/4)
                    </Button>
                    <div className='mx-2 h-[14px] w-[1px] bg-divider-regular' />
                    <div className='mx-2 w-[1px] h-[14px] bg-divider-regular' />
                  </>
                )
                : null
@@ -418,7 +417,7 @@
                  popupContent={t('common.operation.refresh')}
                >
                  <ActionButton onClick={clearConversation}>
                    <RefreshCcw01 className='h-4 w-4' />
                    <RefreshCcw01 className='w-4 h-4' />
                  </ActionButton>
                </TooltipPlus>
                {varList.length > 0 && (
@@ -427,10 +426,10 @@
                      popupContent={t('workflow.panel.userInputField')}
                    >
                      <ActionButton state={expanded ? ActionButtonState.Active : undefined} onClick={() => setExpanded(!expanded)}>
                        <RiEqualizer2Line className='h-4 w-4' />
                        <RiEqualizer2Line className='w-4 h-4' />
                      </ActionButton>
                    </TooltipPlus>
                    {expanded && <div className='absolute bottom-[-14px] right-[5px] z-10 h-3 w-3 rotate-45 border-l-[0.5px] border-t-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg' />}
                    {expanded && <div className='absolute z-10 bottom-[-14px] right-[5px] w-3 h-3 bg-components-panel-on-panel-item-bg border-l-[0.5px] border-t-[0.5px] border-components-panel-border-subtle rotate-45' />}
                  </div>
                )}
              </>
@@ -458,7 +457,7 @@
      </div>
      {
        debugWithMultipleModel && (
          <div className='mt-3 grow overflow-hidden' ref={ref}>
          <div className='grow mt-3 overflow-hidden' ref={ref}>
            <DebugWithMultipleModel
              multipleModelConfigs={multipleModelConfigs}
              onMultipleModelConfigsChange={onMultipleModelConfigsChange}
@@ -490,10 +489,10 @@
      }
      {
        !debugWithMultipleModel && (
          <div className="flex grow flex-col" ref={ref}>
          <div className="flex flex-col grow" ref={ref}>
            {/* Chat */}
            {mode !== AppType.completion && (
              <div className='h-0 grow overflow-hidden'>
              <div className='grow h-0 overflow-hidden'>
                <DebugWithSingleModel
                  ref={debugWithSingleModelRef}
                  checkCanSend={checkCanSend}
@@ -516,16 +515,19 @@
                        isInstalledApp={false}
                        messageId={messageId}
                        isError={false}
                        onRetry={noop}
                        onRetry={() => { }}
                        supportAnnotation
                        appId={appId}
                        varList={varList}
                        siteInfo={null}
                      />
                    </div>
                  </>
                )}
                {!completionRes && !isResponding && (
                  <div className='flex grow flex-col items-center justify-center gap-2'>
                    <RiSparklingFill className='h-12 w-12 text-text-empty-state-icon' />
                    <div className='system-sm-regular text-text-quaternary'>{t('appDebug.noResult')}</div>
                  <div className='grow flex flex-col items-center justify-center gap-2'>
                    <RiSparklingFill className='w-12 h-12 text-text-empty-state-icon' />
                    <div className='text-text-quaternary system-sm-regular'>{t('appDebug.noResult')}</div>
                  </div>
                )}
              </>
app/components/app/configuration/features/chat-group/opening-statement/index.tsx
New file
@@ -0,0 +1,300 @@
/* eslint-disable multiline-ternary */
'use client'
import type { FC } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import {
  RiAddLine,
  RiDeleteBinLine,
} from '@remixicon/react'
import { useContext } from 'use-context-selector'
import produce from 'immer'
import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import { ReactSortable } from 'react-sortablejs'
import cn from '@/utils/classnames'
import ConfigContext from '@/context/debug-configuration'
import Panel from '@/app/components/app/configuration/base/feature-panel'
import Button from '@/app/components/base/button'
import OperationBtn from '@/app/components/app/configuration/base/operation-btn'
import { getInputKeys } from '@/app/components/base/block-input'
import ConfirmAddVar from '@/app/components/app/configuration/config-prompt/confirm-add-var'
import { getNewVar } from '@/utils/var'
import { varHighlightHTML } from '@/app/components/app/configuration/base/var-highlight'
import Toast from '@/app/components/base/toast'
const MAX_QUESTION_NUM = 10
export type IOpeningStatementProps = {
  value: string
  readonly?: boolean
  onChange?: (value: string) => void
  suggestedQuestions?: string[]
  onSuggestedQuestionsChange?: (value: string[]) => void
}
// regex to match the {{}} and replace it with a span
const regex = /\{\{([^}]+)\}\}/g
const OpeningStatement: FC<IOpeningStatementProps> = ({
  value = '',
  readonly,
  onChange,
  suggestedQuestions = [],
  onSuggestedQuestionsChange = () => { },
}) => {
  const { t } = useTranslation()
  const {
    modelConfig,
    setModelConfig,
  } = useContext(ConfigContext)
  const promptVariables = modelConfig.configs.prompt_variables
  const [notIncludeKeys, setNotIncludeKeys] = useState<string[]>([])
  const hasValue = !!(value || '').trim()
  const inputRef = useRef<HTMLTextAreaElement>(null)
  const [isFocus, { setTrue: didSetFocus, setFalse: setBlur }] = useBoolean(false)
  const setFocus = () => {
    didSetFocus()
    setTimeout(() => {
      const input = inputRef.current
      if (input) {
        input.focus()
        input.setSelectionRange(input.value.length, input.value.length)
      }
    }, 0)
  }
  const [tempValue, setTempValue] = useState(value)
  useEffect(() => {
    setTempValue(value || '')
  }, [value])
  const [tempSuggestedQuestions, setTempSuggestedQuestions] = useState(suggestedQuestions || [])
  const notEmptyQuestions = tempSuggestedQuestions.filter(question => !!question && question.trim())
  const coloredContent = (tempValue || '')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(regex, varHighlightHTML({ name: '$1' })) // `<span class="${highLightClassName}">{{$1}}</span>`
    .replace(/\n/g, '<br />')
  const handleEdit = () => {
    if (readonly)
      return
    setFocus()
  }
  const [isShowConfirmAddVar, { setTrue: showConfirmAddVar, setFalse: hideConfirmAddVar }] = useBoolean(false)
  const handleCancel = () => {
    setBlur()
    setTempValue(value)
    setTempSuggestedQuestions(suggestedQuestions)
  }
  const handleConfirm = () => {
    if (!(tempValue || '').trim()) {
      Toast.notify({
        type: 'error',
        message: t('common.errorMsg.fieldRequired', {
          field: t('appDebug.openingStatement.title'),
        }),
      })
      return
    }
    const keys = getInputKeys(tempValue)
    const promptKeys = promptVariables.map(item => item.key)
    let notIncludeKeys: string[] = []
    if (promptKeys.length === 0) {
      if (keys.length > 0)
        notIncludeKeys = keys
    }
    else {
      notIncludeKeys = keys.filter(key => !promptKeys.includes(key))
    }
    if (notIncludeKeys.length > 0) {
      setNotIncludeKeys(notIncludeKeys)
      showConfirmAddVar()
      return
    }
    setBlur()
    onChange?.(tempValue)
    onSuggestedQuestionsChange(tempSuggestedQuestions)
  }
  const cancelAutoAddVar = () => {
    onChange?.(tempValue)
    hideConfirmAddVar()
    setBlur()
  }
  const autoAddVar = () => {
    const newModelConfig = produce(modelConfig, (draft) => {
      draft.configs.prompt_variables = [...draft.configs.prompt_variables, ...notIncludeKeys.map(key => getNewVar(key, 'string'))]
    })
    onChange?.(tempValue)
    setModelConfig(newModelConfig)
    hideConfirmAddVar()
    setBlur()
  }
  const headerRight = !readonly ? (
    isFocus ? (
      <div className='flex items-center space-x-1'>
        <Button
          variant='ghost'
          size='small'
          onClick={handleCancel}
        >
          {t('common.operation.cancel')}
        </Button>
        <Button
          onClick={handleConfirm}
          variant="primary"
          size='small'
        >
          {t('common.operation.save')}
        </Button>
      </div>
    ) : (
      <OperationBtn type='edit' actionName={hasValue ? '' : t('appDebug.openingStatement.writeOpener') as string} onClick={handleEdit} />
    )
  ) : null
  const renderQuestions = () => {
    return isFocus ? (
      <div>
        <div className='flex items-center py-2'>
          <div className='shrink-0 flex space-x-0.5 leading-[18px] text-xs font-medium text-gray-500'>
            <div className='uppercase'>{t('appDebug.openingStatement.openingQuestion')}</div>
            <div>·</div>
            <div>{tempSuggestedQuestions.length}/{MAX_QUESTION_NUM}</div>
          </div>
          <div className='ml-3 grow w-0 h-px bg-[#243, 244, 246]'></div>
        </div>
        <ReactSortable
          className="space-y-1"
          list={tempSuggestedQuestions.map((name, index) => {
            return {
              id: index,
              name,
            }
          })}
          setList={list => setTempSuggestedQuestions(list.map(item => item.name))}
          handle='.handle'
          ghostClass="opacity-50"
          animation={150}
        >
          {tempSuggestedQuestions.map((question, index) => {
            return (
              <div className='group relative rounded-lg border border-gray-200 flex items-center pl-2.5 hover:border-gray-300 hover:bg-white' key={index}>
                <div className='handle flex items-center justify-center w-4 h-4 cursor-grab'>
                  <svg width="6" height="10" viewBox="0 0 6 10" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <path fillRule="evenodd" clipRule="evenodd" d="M1 2C1.55228 2 2 1.55228 2 1C2 0.447715 1.55228 0 1 0C0.447715 0 0 0.447715 0 1C0 1.55228 0.447715 2 1 2ZM1 6C1.55228 6 2 5.55228 2 5C2 4.44772 1.55228 4 1 4C0.447715 4 0 4.44772 0 5C0 5.55228 0.447715 6 1 6ZM6 1C6 1.55228 5.55228 2 5 2C4.44772 2 4 1.55228 4 1C4 0.447715 4.44772 0 5 0C5.55228 0 6 0.447715 6 1ZM5 6C5.55228 6 6 5.55228 6 5C6 4.44772 5.55228 4 5 4C4.44772 4 4 4.44772 4 5C4 5.55228 4.44772 6 5 6ZM2 9C2 9.55229 1.55228 10 1 10C0.447715 10 0 9.55229 0 9C0 8.44771 0.447715 8 1 8C1.55228 8 2 8.44771 2 9ZM5 10C5.55228 10 6 9.55229 6 9C6 8.44771 5.55228 8 5 8C4.44772 8 4 8.44771 4 9C4 9.55229 4.44772 10 5 10Z" fill="#98A2B3" />
                  </svg>
                </div>
                <input
                  type="input"
                  value={question || ''}
                  onChange={(e) => {
                    const value = e.target.value
                    setTempSuggestedQuestions(tempSuggestedQuestions.map((item, i) => {
                      if (index === i)
                        return value
                      return item
                    }))
                  }}
                  className={'w-full overflow-x-auto pl-1.5 pr-8 text-sm leading-9 text-gray-900 border-0 grow h-9 bg-transparent focus:outline-none cursor-pointer rounded-lg'}
                />
                <div
                  className='block absolute top-1/2 translate-y-[-50%] right-1.5 p-1 rounded-md cursor-pointer hover:bg-[#FEE4E2] hover:text-[#D92D20]'
                  onClick={() => {
                    setTempSuggestedQuestions(tempSuggestedQuestions.filter((_, i) => index !== i))
                  }}
                >
                  <RiDeleteBinLine className='w-3.5 h-3.5' />
                </div>
              </div>
            )
          })}</ReactSortable>
        {tempSuggestedQuestions.length < MAX_QUESTION_NUM && (
          <div
            onClick={() => { setTempSuggestedQuestions([...tempSuggestedQuestions, '']) }}
            className='mt-1 flex items-center h-9 px-3 gap-2 rounded-lg cursor-pointer text-gray-400  bg-gray-100 hover:bg-gray-200'>
            <RiAddLine className='w-4 h-4' />
            <div className='text-gray-500 text-[13px]'>{t('appDebug.variableConfig.addOption')}</div>
          </div>
        )}
      </div>
    ) : (
      <div className='mt-1.5 flex flex-wrap'>
        {notEmptyQuestions.map((question, index) => {
          return (
            <div key={index} className='mt-1 mr-1 max-w-full truncate last:mr-0 shrink-0 leading-8 items-center px-2.5 rounded-lg border border-gray-200 shadow-xs bg-white text-[13px] font-normal text-gray-900 cursor-pointer'>
              {question}
            </div>
          )
        })}
      </div>
    )
  }
  return (
    <Panel
      className={cn(isShowConfirmAddVar && 'h-[220px]', 'relative mt-4 !bg-gray-25')}
      title={t('appDebug.openingStatement.title')}
      headerIcon={
        <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
          <path fillRule="evenodd" clipRule="evenodd" d="M8.33353 1.33301C4.83572 1.33301 2.00019 4.16854 2.00019 7.66634C2.00019 8.37301 2.11619 9.05395 2.3307 9.69036C2.36843 9.80229 2.39063 9.86853 2.40507 9.91738L2.40979 9.93383L2.40729 9.93903C2.39015 9.97437 2.36469 10.0218 2.31705 10.11L1.2158 12.1484C1.14755 12.2746 1.07633 12.4064 1.02735 12.5209C0.978668 12.6348 0.899813 12.8437 0.938613 13.0914C0.984094 13.3817 1.15495 13.6373 1.40581 13.7903C1.61981 13.9208 1.843 13.9279 1.96683 13.9264C2.09141 13.925 2.24036 13.9095 2.38314 13.8947L5.81978 13.5395C5.87482 13.5338 5.9036 13.5309 5.92468 13.5292L5.92739 13.529L5.93564 13.532C5.96154 13.5413 5.99666 13.5548 6.0573 13.5781C6.76459 13.8506 7.53244 13.9997 8.33353 13.9997C11.8313 13.9997 14.6669 11.1641 14.6669 7.66634C14.6669 4.16854 11.8313 1.33301 8.33353 1.33301ZM5.9799 5.72116C6.73142 5.08698 7.73164 5.27327 8.33144 5.96584C8.93125 5.27327 9.91854 5.09365 10.683 5.72116C11.4474 6.34867 11.5403 7.41567 10.9501 8.16572C10.5845 8.6304 9.6668 9.47911 9.02142 10.0576C8.78435 10.2702 8.66582 10.3764 8.52357 10.4192C8.40154 10.456 8.26134 10.456 8.13931 10.4192C7.99706 10.3764 7.87853 10.2702 7.64147 10.0576C6.99609 9.47911 6.07839 8.6304 5.71276 8.16572C5.12259 7.41567 5.22839 6.35534 5.9799 5.72116Z" fill="#E74694" />
        </svg>
      }
      headerRight={headerRight}
      hasHeaderBottomBorder={!hasValue}
      isFocus={isFocus}
    >
      <div className='text-gray-700 text-sm'>
        {(hasValue || (!hasValue && isFocus)) ? (
          <>
            {isFocus
              ? (
                <div>
                  <textarea
                    ref={inputRef}
                    value={tempValue}
                    rows={3}
                    onChange={e => setTempValue(e.target.value)}
                    className="w-full px-0 text-sm  border-0 bg-transparent focus:outline-none "
                    placeholder={t('appDebug.openingStatement.placeholder') as string}
                  >
                  </textarea>
                </div>
              )
              : (
                <div dangerouslySetInnerHTML={{
                  __html: coloredContent,
                }}></div>
              )}
            {renderQuestions()}
          </>) : (
          <div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.openingStatement.noDataPlaceHolder')}</div>
        )}
        {isShowConfirmAddVar && (
          <ConfirmAddVar
            varNameArr={notIncludeKeys}
            onConfirm={autoAddVar}
            onCancel={cancelAutoAddVar}
            onHide={hideConfirmAddVar}
          />
        )}
      </div>
    </Panel>
  )
}
export default React.memo(OpeningStatement)
app/components/app/configuration/features/experience-enhance-group/more-like-this/index.tsx
@@ -34,13 +34,13 @@
      noBodySpacing
    >
      {!isHideTip && (
        <div className='flex h-9 items-center justify-between rounded-b-xl bg-[#FFFAEB] px-3 text-xs text-gray-700'>
        <div className='flex justify-between items-center h-9 px-3 rounded-b-xl bg-[#FFFAEB] text-xs text-gray-700'>
          <div className='flex  items-center space-x-2'>
            <div>{warningIcon}</div>
            <div>{t('appDebug.feature.moreLikeThis.tip')}</div>
          </div>
          <div className='flex h-4 w-4 cursor-pointer items-center justify-center' onClick={() => setIsHideTip(true)}>
            <XMarkIcon className="h-3 w-3" />
          <div className='flex items-center justify-center w-4 h-4 cursor-pointer' onClick={() => setIsHideTip(true)}>
            <XMarkIcon className="w-3 h-3" />
          </div>
        </div>
      )}
app/components/app/configuration/index.tsx
@@ -2,7 +2,6 @@
import type { FC } from 'react'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import useSWR from 'swr'
import { basePath } from '@/utils/var'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { usePathname } from 'next/navigation'
@@ -20,7 +19,6 @@
} from '@/app/components/app/configuration/debug/hooks'
import type { ModelAndParameter } from '@/app/components/app/configuration/debug/types'
import Button from '@/app/components/base/button'
import Divider from '@/app/components/base/divider'
import Loading from '@/app/components/base/loading'
import AppPublisher from '@/app/components/app/app-publisher/features-wrapper'
import type {
@@ -61,7 +59,7 @@
  useTextGenerationCurrentProviderAndModelAndModelList,
} from '@/app/components/header/account-setting/model-provider-page/hooks'
import { fetchCollectionList } from '@/service/tools'
import type { Collection } from '@/app/components/tools/types'
import { type Collection } from '@/app/components/tools/types'
import { useStore as useAppStore } from '@/app/components/app/store'
import {
  getMultipleRetrievalConfig,
@@ -73,12 +71,6 @@
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
import NewFeaturePanel from '@/app/components/base/features/new-feature-panel'
import { fetchFileUploadConfig } from '@/service/common'
import {
  correctModelProvider,
  correctToolProvider,
} from '@/utils'
import PluginDependency from '@/app/components/workflow/plugin-dependency'
import { supportFunctionCall } from '@/utils/tool-call'
type PublishConfig = {
  modelConfig: ModelConfig
@@ -96,7 +88,7 @@
  })))
  const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig)
  const latestPublishedAt = useMemo(() => appDetail?.model_config?.updated_at, [appDetail])
  const latestPublishedAt = useMemo(() => appDetail?.model_config.updated_at, [appDetail])
  const [formattingChanged, setFormattingChanged] = useState(false)
  const { setShowAccountSettingModal } = useModalContext()
  const [hasFetchedDetail, setHasFetchedDetail] = useState(false)
@@ -164,7 +156,7 @@
  const setCompletionParams = (value: FormValue) => {
    const params = { ...value }
    // eslint-disable-next-line ts/no-use-before-define
    // eslint-disable-next-line @typescript-eslint/no-use-before-define
    if ((!params.stop || params.stop.length === 0) && (modeModeTypeRef.current === ModelModeType.completion)) {
      params.stop = getTempStop()
      setTempStop([])
@@ -173,7 +165,7 @@
  }
  const [modelConfig, doSetModelConfig] = useState<ModelConfig>({
    provider: 'langgenius/openai/openai',
    provider: 'openai',
    model_id: 'gpt-3.5-turbo',
    mode: ModelModeType.unset,
    configs: {
@@ -193,12 +185,16 @@
    dataSets: [],
    agentConfig: DEFAULT_AGENT_SETTING,
  })
  const isAgent = mode === 'agent-chat'
  const isOpenAI = modelConfig.provider === 'langgenius/openai/openai'
  const isOpenAI = modelConfig.provider === 'openai'
  const [collectionList, setCollectionList] = useState<Collection[]>([])
  const [datasetConfigs, doSetDatasetConfigs] = useState<DatasetConfigs>({
  useEffect(() => {
  }, [])
  const [datasetConfigs, setDatasetConfigs] = useState<DatasetConfigs>({
    retrieval_model: RETRIEVE_TYPE.multiWay,
    reranking_model: {
      reranking_provider_name: '',
@@ -211,11 +207,6 @@
      datasets: [],
    },
  })
  const datasetConfigsRef = useRef(datasetConfigs)
  const setDatasetConfigs = useCallback((newDatasetConfigs: DatasetConfigs) => {
    doSetDatasetConfigs(newDatasetConfigs)
    datasetConfigsRef.current = newDatasetConfigs
  }, [])
  const setModelConfig = (newModelConfig: ModelConfig) => {
    doSetModelConfig(newModelConfig)
@@ -228,7 +219,7 @@
  }, [modelModeType])
  const [dataSets, setDataSets] = useState<DataSet[]>([])
  const contextVar = modelConfig.configs.prompt_variables.find(item => item.is_context_var)?.key
  const contextVar = modelConfig.configs.prompt_variables.find((item: any) => item.is_context_var)?.key
  const hasSetContextVar = !!contextVar
  const [isShowSelectDataSet, { setTrue: showSelectDataSet, setFalse: hideSelectDataSet }] = useBoolean(false)
  const selectedIds = dataSets.map(item => item.id)
@@ -246,7 +237,7 @@
    formattingChangedDispatcher()
    let newDatasets = data
    if (data.find(item => !item.name)) { // has not loaded selected dataset
      const newSelected = produce(data, (draft) => {
      const newSelected = produce(data, (draft: any) => {
        data.forEach((item, index) => {
          if (!item.name) { // not fetched database
            const newItem = dataSets.find(i => i.id === item.id)
@@ -295,7 +286,6 @@
    })
    setDatasetConfigs({
      ...datasetConfigsRef.current,
      ...retrievalConfig,
      reranking_model: {
        reranking_provider_name: retrievalConfig?.reranking_model?.provider || '',
@@ -346,7 +336,12 @@
    },
  )
  const isFunctionCall = supportFunctionCall(currModel?.features)
  const isFunctionCall = (() => {
    const features = currModel?.features
    if (!features)
      return false
    return features.includes(ModelFeatureEnum.toolCall) || features.includes(ModelFeatureEnum.multiToolCall)
  })()
  // Fill old app data missing model mode.
  useEffect(() => {
@@ -366,7 +361,7 @@
  const [canReturnToSimpleMode, setCanReturnToSimpleMode] = useState(true)
  const setPromptMode = async (mode: PromptMode) => {
    if (mode === PromptMode.advanced) {
      // eslint-disable-next-line ts/no-use-before-define
      // eslint-disable-next-line @typescript-eslint/no-use-before-define
      await migrateToDefaultPrompt()
      setCanReturnToSimpleMode(true)
    }
@@ -504,12 +499,6 @@
  useEffect(() => {
    (async () => {
      const collectionList = await fetchCollectionList()
      if (basePath) {
        collectionList.forEach((item) => {
          if (typeof item.icon == 'string' && !item.icon.includes(basePath))
            item.icon = `${basePath}${item.icon}`
        })
      }
      setCollectionList(collectionList)
      fetchAppDetail({ url: '/apps', id: appId }).then(async (res: any) => {
        setMode(res.mode)
@@ -520,7 +509,7 @@
          if (modelConfig.chat_prompt_config && modelConfig.chat_prompt_config.prompt.length > 0)
            setChatPromptConfig(modelConfig.chat_prompt_config)
          else
            setChatPromptConfig(clone(DEFAULT_CHAT_PROMPT_CONFIG))
            setChatPromptConfig(clone(DEFAULT_CHAT_PROMPT_CONFIG) as any)
          setCompletionPromptConfig(modelConfig.completion_prompt_config || clone(DEFAULT_COMPLETION_PROMPT_CONFIG) as any)
          setCanReturnToSimpleMode(false)
        }
@@ -558,19 +547,8 @@
        if (modelConfig.retriever_resource)
          setCitationConfig(modelConfig.retriever_resource)
        if (modelConfig.annotation_reply) {
          let annotationConfig = modelConfig.annotation_reply
          if (modelConfig.annotation_reply.enabled) {
            annotationConfig = {
              ...modelConfig.annotation_reply,
              embedding_model: {
                ...modelConfig.annotation_reply.embedding_model,
                embedding_provider_name: correctModelProvider(modelConfig.annotation_reply.embedding_model.embedding_provider_name),
              },
            }
          }
          setAnnotationConfig(annotationConfig, true)
        }
        if (modelConfig.annotation_reply)
          setAnnotationConfig(modelConfig.annotation_reply, true)
        if (modelConfig.sensitive_word_avoidance)
          setModerationConfig(modelConfig.sensitive_word_avoidance)
@@ -580,7 +558,7 @@
        const config = {
          modelConfig: {
            provider: correctModelProvider(model.provider),
            provider: model.provider,
            model_id: model.name,
            mode: model.mode,
            configs: {
@@ -622,6 +600,7 @@
            annotation_reply: modelConfig.annotation_reply,
            external_data_tools: modelConfig.external_data_tools,
            dataSets: datasets || [],
            // eslint-disable-next-line multiline-ternary
            agentConfig: res.mode === 'agent-chat' ? {
              max_iteration: DEFAULT_AGENT_SETTING.max_iteration,
              ...modelConfig.agent_mode,
@@ -630,15 +609,10 @@
              tools: modelConfig.agent_mode?.tools.filter((tool: any) => {
                return !tool.dataset
              }).map((tool: any) => {
                const toolInCollectionList = collectionList.find(c => tool.provider_id === c.id)
                return {
                  ...tool,
                  isDeleted: res.deleted_tools?.some((deletedTool: any) => deletedTool.id === tool.id && deletedTool.tool_name === tool.tool_name),
                  notAuthor: toolInCollectionList?.is_team_authorization === false,
                  ...(tool.provider_type === 'builtin' ? {
                    provider_id: correctToolProvider(tool.provider_name, !!toolInCollectionList),
                    provider_name: correctToolProvider(tool.provider_name, !!toolInCollectionList),
                  } : {}),
                  isDeleted: res.deleted_tools?.includes(tool.tool_name),
                  notAuthor: collectionList.find(c => tool.provider_id === c.id)?.is_team_authorization === false,
                }
              }),
            } : DEFAULT_AGENT_SETTING,
@@ -651,13 +625,7 @@
        syncToPublishedConfig(config)
        setPublishedConfig(config)
        const retrievalConfig = getMultipleRetrievalConfig({
          ...modelConfig.dataset_configs,
          reranking_model: modelConfig.dataset_configs.reranking_model && {
            provider: modelConfig.dataset_configs.reranking_model.reranking_provider_name,
            model: modelConfig.dataset_configs.reranking_model.reranking_model_name,
          },
        }, datasets, datasets, {
        const retrievalConfig = getMultipleRetrievalConfig(modelConfig.dataset_configs, datasets, datasets, {
          provider: currentRerankProvider?.provider,
          model: currentRerankModel?.model,
        })
@@ -665,12 +633,6 @@
          retrieval_model: RETRIEVE_TYPE.multiWay,
          ...modelConfig.dataset_configs,
          ...retrievalConfig,
          ...(retrievalConfig.reranking_model ? {
            reranking_model: {
              reranking_model_name: retrievalConfig.reranking_model.model,
              reranking_provider_name: correctModelProvider(retrievalConfig.reranking_model.provider),
            },
          } : {}),
        })
        setHasFetchedDetail(true)
      })
@@ -826,7 +788,7 @@
  }
  if (isLoading) {
    return <div className='flex h-full items-center justify-center'>
    return <div className='flex items-center justify-center h-full'>
      <Loading type='area' />
    </div>
  }
@@ -895,7 +857,6 @@
      dataSets,
      setDataSets,
      datasetConfigs,
      datasetConfigsRef,
      setDatasetConfigs,
      hasSetContextVar,
      isShowVisionConfig,
@@ -909,16 +870,16 @@
    >
      <FeaturesProvider features={featuresData}>
        <>
          <div className="flex h-full flex-col">
            <div className='relative flex h-[200px] grow pt-14'>
          <div className="flex flex-col h-full">
            <div className='relative flex grow h-[200px] pt-14'>
              {/* Header */}
              <div className='bg-default-subtle absolute left-0 top-0 h-14 w-full'>
                <div className='flex h-14 items-center justify-between px-6'>
              <div className='absolute top-0 left-0 w-full bg-white h-14'>
                <div className='flex items-center justify-between px-6 h-14'>
                  <div className='flex items-center'>
                    <div className='system-xl-semibold text-text-primary'>{t('appDebug.orchestrate')}</div>
                    <div className='flex h-[14px] items-center space-x-1 text-xs'>
                    <div className='text-base font-semibold leading-6 text-gray-900'>{t('appDebug.orchestrate')}</div>
                    <div className='flex items-center h-[14px] space-x-1 text-xs'>
                      {isAdvancedMode && (
                        <div className='system-xs-medium-uppercase ml-1 flex h-5 items-center rounded-md border border-components-button-secondary-border px-1.5 uppercase text-text-tertiary'>{t('appDebug.promptMode.advanced')}</div>
                        <div className='ml-1 flex items-center h-5 px-1.5 border border-gray-100 rounded-md text-[11px] font-medium text-gray-500 uppercase'>{t('appDebug.promptMode.advanced')}</div>
                      )}
                    </div>
                  </div>
@@ -954,13 +915,13 @@
                          debugWithMultipleModel={debugWithMultipleModel}
                          onDebugWithMultipleModelChange={handleDebugWithMultipleModelChange}
                        />
                        <Divider type='vertical' className='mx-2 h-[14px]' />
                        <div className='mx-2 w-[1px] h-[14px] bg-gray-200'></div>
                      </>
                    )}
                    {isMobile && (
                      <Button className='mr-2 !h-8 !text-[13px] font-medium' onClick={showDebugPanel}>
                      <Button className='!h-8 !text-[13px] font-medium' onClick={showDebugPanel}>
                        <span className='mr-1'>{t('appDebug.operation.debugConfig')}</span>
                        <CodeBracketIcon className="h-4 w-4 text-text-tertiary" />
                        <CodeBracketIcon className="w-4 h-4 text-gray-500" />
                      </Button>
                    )}
                    <AppPublisher {...{
@@ -975,11 +936,11 @@
                  </div>
                </div>
              </div>
              <div className={`flex h-full w-full shrink-0 flex-col sm:w-1/2 ${debugWithMultipleModel && 'max-w-[560px]'}`}>
              <div className={`w-full sm:w-1/2 shrink-0 flex flex-col h-full ${debugWithMultipleModel && 'max-w-[560px]'}`}>
                <Config />
              </div>
              {!isMobile && <div className="relative flex h-full w-1/2 grow flex-col overflow-y-auto " style={{ borderColor: 'rgba(0, 0, 0, 0.02)' }}>
                <div className='flex grow flex-col rounded-tl-2xl border-l-[0.5px] border-t-[0.5px] border-components-panel-border bg-chatbot-bg '>
              {!isMobile && <div className="relative flex flex-col w-1/2 h-full overflow-y-auto grow " style={{ borderColor: 'rgba(0, 0, 0, 0.02)' }}>
                <div className='grow flex flex-col border-t-[0.5px] border-l-[0.5px] rounded-tl-2xl border-components-panel-border bg-chatbot-bg '>
                  <Debug
                    isAPIKeySet={isAPIKeySet}
                    onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
@@ -1031,7 +992,7 @@
            />
          )}
          {isMobile && (
            <Drawer showClose isOpen={isShowDebugPanel} onClose={hideDebugPanel} mask footer={null}>
            <Drawer showClose isOpen={isShowDebugPanel} onClose={hideDebugPanel} mask footer={null} panelClassname='!bg-gray-50'>
              <Debug
                isAPIKeySet={isAPIKeySet}
                onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
@@ -1059,7 +1020,6 @@
              onAutoAddPromptVariable={handleAddPromptVariable}
            />
          )}
          <PluginDependency />
        </>
      </FeaturesProvider>
    </ConfigContext.Provider>
app/components/app/configuration/prompt-mode/advanced-mode-waring.tsx
@@ -18,14 +18,14 @@
  if (!show)
    return null
  return (
    <div className='mb-3 rounded-xl border border-[#FEF0C7] bg-[#FFFAEB] px-4 py-3' >
      <div className='mb-2 text-xs font-bold leading-[18px] text-[#DC6803]'>{t('appDebug.promptMode.advancedWarning.title')}</div>
      <div className='flex items-center justify-between'>
    <div className='mb-3 py-3 px-4 border border-[#FEF0C7] rounded-xl bg-[#FFFAEB]' >
      <div className='mb-2 text-xs leading-[18px] font-bold text-[#DC6803]'>{t('appDebug.promptMode.advancedWarning.title')}</div>
      <div className='flex justify-between items-center'>
        <div className='text-xs leading-[18px] '>
          <span className='text-gray-700'>{t('appDebug.promptMode.advancedWarning.description')}</span>
          <a
            className='font-medium text-[#155EEF]'
            href={`https://docs.dify.ai/${locale === LanguagesSupported[1] ? '/guides/features/prompt-engineering' : 'features/prompt-engineering'}`}
            href={`https://docs.dify.ai/${locale === LanguagesSupported[1] ? 'v/zh-hans/guides/application-design/prompt-engineering' : 'features/prompt-engineering'}`}
            target='_blank' rel='noopener noreferrer'
          >
            {t('appDebug.promptMode.advancedWarning.learnMore')}
@@ -35,12 +35,12 @@
        <div className='flex items-center space-x-1'>
          <div
            onClick={onReturnToSimpleMode}
            className='flex h-6 shrink-0 cursor-pointer items-center space-x-1 rounded-lg border border-gray-200 bg-indigo-600 px-2 text-xs font-semibold text-white shadow-xs'
            className='shrink-0 flex items-center h-6 px-2 bg-indigo-600 shadow-xs border border-gray-200 rounded-lg text-white text-xs font-semibold cursor-pointer space-x-1'
          >
            <div className='text-xs font-semibold uppercase'>{t('appDebug.promptMode.switchBack')}</div>
          </div>
          <div
            className='flex h-6 cursor-pointer items-center rounded-md border border-gray-200 bg-[#fff] px-2 text-xs font-medium text-primary-600 shadow-xs'
            className='flex items-center h-6 px-2 rounded-md bg-[#fff] border border-gray-200 shadow-xs text-xs font-medium text-primary-600 cursor-pointer'
            onClick={() => setShow(false)}
          >{t('appDebug.promptMode.advancedWarning.ok')}</div>
        </div>
app/components/app/configuration/prompt-value-panel/index.tsx
@@ -79,7 +79,7 @@
  }
  const onClear = () => {
    const newInputs: Inputs = {}
    const newInputs: Record<string, any> = {}
    promptVariables.forEach((item) => {
      newInputs[item.key] = ''
    })
@@ -90,28 +90,28 @@
  return (
    <>
      <div className='relative z-[1] mx-3 rounded-xl border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg shadow-md'>
      <div className='relative z-[1] mx-3 border-[0.5px] bg-components-panel-on-panel-item-bg border-components-panel-border-subtle rounded-xl shadow-md'>
        <div className={cn('px-4 pt-3', userInputFieldCollapse ? 'pb-3' : 'pb-1')}>
          <div className='flex cursor-pointer items-center gap-0.5 py-0.5' onClick={() => setUserInputFieldCollapse(!userInputFieldCollapse)}>
            <div className='system-md-semibold-uppercase text-text-secondary'>{t('appDebug.inputs.userInputField')}</div>
            {userInputFieldCollapse && <RiArrowRightSLine className='h-4 w-4 text-text-secondary'/>}
            {!userInputFieldCollapse && <RiArrowDownSLine className='h-4 w-4 text-text-secondary'/>}
          <div className='flex items-center gap-0.5 py-0.5 cursor-pointer' onClick={() => setUserInputFieldCollapse(!userInputFieldCollapse)}>
            <div className='text-text-secondary system-md-semibold-uppercase'>{t('appDebug.inputs.userInputField')}</div>
            {userInputFieldCollapse && <RiArrowRightSLine className='w-4 h-4 text-text-secondary'/>}
            {!userInputFieldCollapse && <RiArrowDownSLine className='w-4 h-4 text-text-secondary'/>}
          </div>
          {!userInputFieldCollapse && (
            <div className='system-xs-regular mt-1 text-text-tertiary'>{t('appDebug.inputs.completionVarTip')}</div>
            <div className='mt-1 text-text-tertiary system-xs-regular'>{t('appDebug.inputs.completionVarTip')}</div>
          )}
        </div>
        {!userInputFieldCollapse && promptVariables.length > 0 && (
          <div className='px-4 pb-4 pt-3'>
          <div className='px-4 pt-3 pb-4'>
            {promptVariables.map(({ key, name, type, options, max_length, required }, index) => (
              <div
                key={key}
                className='mb-4 last-of-type:mb-0'
              >
                <div>
                  <div className='system-sm-semibold mb-1 flex h-6 items-center gap-1 text-text-secondary'>
                  <div className='h-6 mb-1 flex items-center gap-1 text-text-secondary system-sm-semibold'>
                    <div className='truncate'>{name || key}</div>
                    {!required && <span className='system-xs-regular text-text-tertiary'>{t('workflow.panel.optional')}</span>}
                    {!required && <span className='text-text-tertiary system-xs-regular'>{t('workflow.panel.optional')}</span>}
                  </div>
                  <div className='grow'>
                    {type === 'string' && (
@@ -125,7 +125,7 @@
                    )}
                    {type === 'paragraph' && (
                      <Textarea
                        className='h-[120px] grow'
                        className='grow h-[120px]'
                        placeholder={name}
                        value={inputs[key] ? `${inputs[key]}` : ''}
                        onChange={(e) => { handleInputValueChange(key, e.target.value) }}
@@ -156,8 +156,8 @@
              </div>
            ))}
            {visionConfig?.enabled && (
              <div className="mt-3 justify-between xl:flex">
                <div className="mr-1 w-[120px] shrink-0 py-2 text-sm text-text-primary">{t('common.imageUploader.imageUpload')}</div>
              <div className="mt-3 xl:flex justify-between">
                <div className="mr-1 py-2 shrink-0 w-[120px] text-sm text-gray-900">{t('common.imageUploader.imageUpload')}</div>
                <div className='grow'>
                  <TextGenerationImageUploader
                    settings={visionConfig}
@@ -174,7 +174,7 @@
          </div>
        )}
        {!userInputFieldCollapse && (
          <div className='flex justify-between border-t border-divider-subtle p-4 pt-3'>
          <div className='flex justify-between p-4 pt-3 border-t border-divider-subtle'>
            <Button className='w-[72px]' onClick={onClear}>{t('common.operation.clear')}</Button>
            {canNotRun && (
              <Tooltip popupContent={t('appDebug.otherError.promptNoBeEmpty')} needsDelay>
@@ -183,7 +183,7 @@
                  disabled={canNotRun}
                  onClick={() => onSend && onSend()}
                  className="w-[96px]">
                  <RiPlayLargeFill className="mr-0.5 h-4 w-4 shrink-0" aria-hidden="true" />
                  <RiPlayLargeFill className="shrink-0 w-4 h-4 mr-0.5" aria-hidden="true" />
                  {t('appDebug.inputs.run')}
                </Button>
              </Tooltip>
@@ -194,7 +194,7 @@
                disabled={canNotRun}
                onClick={() => onSend && onSend()}
                className="w-[96px]">
                <RiPlayLargeFill className="mr-0.5 h-4 w-4 shrink-0" aria-hidden="true" />
                <RiPlayLargeFill className="shrink-0 w-4 h-4 mr-0.5" aria-hidden="true" />
                {t('appDebug.inputs.run')}
              </Button>
            )}
app/components/app/configuration/style.module.css
app/components/app/configuration/toolbox/annotation/config-param.tsx
New file
@@ -0,0 +1,124 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { usePathname, useRouter } from 'next/navigation'
import ConfigParamModal from './config-param-modal'
import Panel from '@/app/components/app/configuration/base/feature-panel'
import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
import Tooltip from '@/app/components/base/tooltip'
import { LinkExternal02, Settings04 } from '@/app/components/base/icons/src/vender/line/general'
import ConfigContext from '@/context/debug-configuration'
import type { EmbeddingModelConfig } from '@/app/components/app/annotation/type'
import { fetchAnnotationConfig, updateAnnotationScore } from '@/service/annotation'
import type { AnnotationReplyConfig as AnnotationReplyConfigType } from '@/models/debug'
type Props = {
  onEmbeddingChange: (embeddingModel: EmbeddingModelConfig) => void
  onScoreChange: (score: number, embeddingModel?: EmbeddingModelConfig) => void
}
export const Item: FC<{ title: string; tooltip: string; children: JSX.Element }> = ({
  title,
  tooltip,
  children,
}) => {
  return (
    <div>
      <div className='flex items-center space-x-1'>
        <div>{title}</div>
        <Tooltip
          popupContent={
            <div className='max-w-[200px] leading-[18px] text-[13px] font-medium text-gray-800'>{tooltip}</div>
          }
        />
      </div>
      <div>{children}</div>
    </div>
  )
}
const AnnotationReplyConfig: FC<Props> = ({
  onEmbeddingChange,
  onScoreChange,
}) => {
  const { t } = useTranslation()
  const router = useRouter()
  const pathname = usePathname()
  const matched = pathname.match(/\/app\/([^/]+)/)
  const appId = (matched?.length && matched[1]) ? matched[1] : ''
  const {
    annotationConfig,
  } = useContext(ConfigContext)
  const [isShowEdit, setIsShowEdit] = React.useState(false)
  return (
    <>
      <Panel
        className="mt-4"
        headerIcon={
          <MessageFast className='w-4 h-4 text-[#444CE7]' />
        }
        title={t('appDebug.feature.annotation.title')}
        headerRight={
          <div className='flex items-center'>
            <div
              className='flex items-center rounded-md h-7 px-3 space-x-1 text-gray-700 cursor-pointer hover:bg-gray-200'
              onClick={() => { setIsShowEdit(true) }}
            >
              <Settings04 className="w-[14px] h-[14px]" />
              <div className='text-xs font-medium'>
                {t('common.operation.params')}
              </div>
            </div>
            <div
              className='ml-1 flex items-center h-7 px-3 space-x-1 leading-[18px] text-xs font-medium text-gray-700 rounded-md cursor-pointer hover:bg-gray-200'
              onClick={() => {
                router.push(`/app/${appId}/annotations`)
              }}>
              <div>{t('appDebug.feature.annotation.cacheManagement')}</div>
              <LinkExternal02 className='w-3.5 h-3.5' />
            </div>
          </div>
        }
        noBodySpacing
      />
      {isShowEdit && (
        <ConfigParamModal
          appId={appId}
          isShow
          onHide={() => {
            setIsShowEdit(false)
          }}
          onSave={async (embeddingModel, score) => {
            const annotationConfig = await fetchAnnotationConfig(appId) as AnnotationReplyConfigType
            let isEmbeddingModelChanged = false
            if (
              embeddingModel.embedding_model_name !== annotationConfig.embedding_model.embedding_model_name
              || embeddingModel.embedding_provider_name !== annotationConfig.embedding_model.embedding_provider_name
            ) {
              await onEmbeddingChange(embeddingModel)
              isEmbeddingModelChanged = true
            }
            if (score !== annotationConfig.score_threshold) {
              await updateAnnotationScore(appId, annotationConfig.id, score)
              if (isEmbeddingModelChanged)
                onScoreChange(score, embeddingModel)
              else
                onScoreChange(score)
            }
            setIsShowEdit(false)
          }}
          annotationConfig={annotationConfig}
        />
      )}
    </>
  )
}
export default React.memo(AnnotationReplyConfig)
app/components/app/configuration/toolbox/index.tsx
New file
@@ -0,0 +1,45 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import GroupName from '../base/group-name'
import Moderation from './moderation'
import Annotation from './annotation/config-param'
import type { EmbeddingModelConfig } from '@/app/components/app/annotation/type'
export type ToolboxProps = {
  showModerationSettings: boolean
  showAnnotation: boolean
  onEmbeddingChange: (embeddingModel: EmbeddingModelConfig) => void
  onScoreChange: (score: number, embeddingModel?: EmbeddingModelConfig) => void
}
const Toolbox: FC<ToolboxProps> = ({
  showModerationSettings,
  showAnnotation,
  onEmbeddingChange,
  onScoreChange,
}) => {
  const { t } = useTranslation()
  return (
    <div className='mt-7'>
      <GroupName name={t('appDebug.feature.toolbox.title')} />
      {
        showModerationSettings && (
          <Moderation />
        )
      }
      {
        showAnnotation && (
          <Annotation
            onEmbeddingChange={onEmbeddingChange}
            onScoreChange={onScoreChange}
          />
        )
      }
    </div>
  )
}
export default React.memo(Toolbox)
app/components/app/configuration/tools/external-data-tool-modal.tsx
@@ -19,7 +19,6 @@
} from '@/models/common'
import { useToastContext } from '@/app/components/base/toast'
import AppIcon from '@/app/components/base/app-icon'
import { noop } from 'lodash-es'
const systemTypes = ['api']
type ExternalDataToolModalProps = {
@@ -151,7 +150,7 @@
      return
    }
    if (localeData.variable && !/[a-zA-Z_]\w{0,29}/g.test(localeData.variable)) {
    if (localeData.variable && !/[a-zA-Z_][a-zA-Z0-9_]{0,29}/g.test(localeData.variable)) {
      notify({ type: 'error', message: t('appDebug.varKeyError.notValid', { key: t('appDebug.feature.tools.modal.variableName.title') }) })
      return
    }
@@ -186,14 +185,14 @@
  return (
    <Modal
      isShow
      onClose={noop}
      className='!w-[640px] !max-w-none !p-8 !pb-6'
      onClose={() => { }}
      className='!p-8 !pb-6 !max-w-none !w-[640px]'
    >
      <div className='mb-2 text-xl font-semibold text-gray-900'>
        {`${action} ${t('appDebug.variableConfig.apiBasedVar')}`}
      </div>
      <div className='py-2'>
        <div className='text-sm font-medium leading-9 text-gray-900'>
        <div className='leading-9 text-sm font-medium text-gray-900'>
          {t('common.apiBasedExtension.type')}
        </div>
        <SimpleSelect
@@ -208,46 +207,46 @@
        />
      </div>
      <div className='py-2'>
        <div className='text-sm font-medium leading-9 text-gray-900'>
        <div className='leading-9 text-sm font-medium text-gray-900'>
          {t('appDebug.feature.tools.modal.name.title')}
        </div>
        <div className='flex items-center'>
          <input
            value={localeData.label || ''}
            onChange={e => handleValueChange({ label: e.target.value })}
            className='mr-2 block h-9 grow appearance-none rounded-lg bg-gray-100 px-3 text-sm text-gray-900 outline-none'
            className='grow block mr-2 px-3 h-9 bg-gray-100 rounded-lg text-sm text-gray-900 outline-none appearance-none'
            placeholder={t('appDebug.feature.tools.modal.name.placeholder') || ''}
          />
          <AppIcon size='large'
            onClick={() => { setShowEmojiPicker(true) }}
            className='!h-9 !w-9 cursor-pointer rounded-lg border-[0.5px] border-black/5 '
            className='!w-9 !h-9 rounded-lg border-[0.5px] border-black/5 cursor-pointer '
            icon={localeData.icon}
            background={localeData.icon_background}
          />
        </div>
      </div>
      <div className='py-2'>
        <div className='text-sm font-medium leading-9 text-gray-900'>
        <div className='leading-9 text-sm font-medium text-gray-900'>
          {t('appDebug.feature.tools.modal.variableName.title')}
        </div>
        <input
          value={localeData.variable || ''}
          onChange={e => handleValueChange({ variable: e.target.value })}
          className='block h-9 w-full appearance-none rounded-lg bg-gray-100 px-3 text-sm text-gray-900 outline-none'
          className='block px-3 w-full h-9 bg-gray-100 rounded-lg text-sm text-gray-900 outline-none appearance-none'
          placeholder={t('appDebug.feature.tools.modal.variableName.placeholder') || ''}
        />
      </div>
      {
        localeData.type === 'api' && (
          <div className='py-2'>
            <div className='flex h-9 items-center justify-between text-sm font-medium text-gray-900'>
            <div className='flex justify-between items-center h-9 text-sm font-medium text-gray-900'>
              {t('common.apiBasedExtension.selector.title')}
              <a
                href={t('common.apiBasedExtension.linkUrl') || '/'}
                target='_blank' rel='noopener noreferrer'
                className='group flex items-center text-xs font-normal text-gray-500 hover:text-primary-600'
              >
                <BookOpen01 className='mr-1 h-3 w-3 text-gray-500 group-hover:text-primary-600' />
                <BookOpen01 className='mr-1 w-3 h-3 text-gray-500 group-hover:text-primary-600' />
                {t('common.apiBasedExtension.link')}
              </a>
            </div>
@@ -269,7 +268,7 @@
          />
        )
      }
      <div className='mt-6 flex items-center justify-end'>
      <div className='flex items-center justify-end mt-6'>
        <Button
          onClick={onCancel}
          className='mr-2'
app/components/app/configuration/tools/index.tsx
@@ -82,24 +82,24 @@
  }
  return (
    <div className='mt-3 rounded-xl bg-gray-50 px-3'>
      <div className='flex h-12 items-center'>
        <div className='flex grow items-center'>
    <div className='mt-3 px-3 rounded-xl bg-gray-50'>
      <div className='flex items-center h-12'>
        <div className='grow flex items-center'>
          <div
            className={`
              group mr-1 flex h-6 w-6 items-center justify-center rounded-md
              ${externalDataToolsConfig.length && 'hover:bg-white hover:shadow-xs'}
              group flex items-center justify-center mr-1 w-6 h-6 rounded-md
              ${externalDataToolsConfig.length && 'hover:shadow-xs hover:bg-white'}
            `}
            onClick={() => setExpanded(v => !v)}
          >
            {
              externalDataToolsConfig.length
                ? <Tool03 className='h-4 w-4 text-[#444CE7] group-hover:hidden' />
                : <Tool03 className='h-4 w-4 text-[#444CE7]' />
                ? <Tool03 className='group-hover:hidden w-4 h-4 text-[#444CE7]' />
                : <Tool03 className='w-4 h-4 text-[#444CE7]' />
            }
            {
              !!externalDataToolsConfig.length && (
                <RiArrowDownSLine className={`hidden h-4 w-4 cursor-pointer text-primary-600 group-hover:block ${expanded ? 'rotate-180' : 'rotate-0'}`} />
                <RiArrowDownSLine className={`hidden group-hover:block w-4 h-4 text-primary-600 cursor-pointer ${expanded ? 'rotate-180' : 'rotate-0'}`} />
              )
            }
          </div>
@@ -118,15 +118,15 @@
          !expanded && !!externalDataToolsConfig.length && (
            <>
              <div className='mr-3 text-xs text-gray-500'>{t('appDebug.feature.tools.toolsInUse', { count: externalDataToolsConfig.length })}</div>
              <div className='mr-1 h-3.5 w-[1px] bg-gray-200' />
              <div className='mr-1 w-[1px] h-3.5 bg-gray-200' />
            </>
          )
        }
        <div
          className='flex h-7 cursor-pointer items-center px-3 text-xs font-medium text-gray-700'
          className='flex items-center h-7 px-3 text-xs font-medium text-gray-700 cursor-pointer'
          onClick={() => handleOpenExternalDataToolModal({}, -1)}
        >
          <RiAddLine className='mr-[5px] h-3.5 w-3.5 ' />
          <RiAddLine className='mr-[5px] w-3.5 h-3.5 ' />
          {t('common.operation.add')}
        </div>
      </div>
@@ -137,11 +137,11 @@
              externalDataToolsConfig.map((item, index: number) => (
                <div
                  key={`${index}-${item.type}-${item.label}-${item.variable}`}
                  className='group mb-1 flex items-center rounded-lg border-[0.5px] border-gray-200 bg-white px-2.5 py-2 shadow-xs last-of-type:mb-0'
                  className='group flex items-center mb-1 last-of-type:mb-0 px-2.5 py-2 rounded-lg border-[0.5px] border-gray-200 bg-white shadow-xs'
                >
                  <div className='flex grow items-center'>
                  <div className='grow flex items-center'>
                    <AppIcon size='large'
                      className='mr-2 !h-6 !w-6 rounded-md border-[0.5px] border-black/5'
                      className='mr-2 !w-6 !h-6 rounded-md border-[0.5px] border-black/5'
                      icon={item.icon}
                      background={item.icon_background}
                    />
@@ -161,18 +161,18 @@
                    </Tooltip>
                  </div>
                  <div
                    className='mr-1 hidden h-6 w-6 cursor-pointer items-center justify-center rounded-md hover:bg-black/5 group-hover:flex'
                    className='hidden group-hover:flex items-center justify-center mr-1 w-6 h-6 hover:bg-black/5 rounded-md cursor-pointer'
                    onClick={() => handleOpenExternalDataToolModal(item, index)}
                  >
                    <Settings01 className='h-4 w-4 text-gray-500' />
                    <Settings01 className='w-4 h-4 text-gray-500' />
                  </div>
                  <div
                    className='group/action hidden h-6 w-6 cursor-pointer items-center justify-center rounded-md hover:bg-[#FEE4E2] group-hover:flex'
                    className='hidden group/action group-hover:flex items-center justify-center w-6 h-6 hover:bg-[#FEE4E2] rounded-md cursor-pointer'
                    onClick={() => setExternalDataToolsConfig([...externalDataToolsConfig.slice(0, index), ...externalDataToolsConfig.slice(index + 1)])}
                  >
                    <RiDeleteBinLine className='h-4 w-4 text-gray-500 group-hover/action:text-[#D92D20]' />
                    <RiDeleteBinLine className='w-4 h-4 text-gray-500 group-hover/action:text-[#D92D20]' />
                  </div>
                  <div className='ml-2 mr-3 hidden h-3.5 w-[1px] bg-gray-200 group-hover:block' />
                  <div className='hidden group-hover:block ml-2 mr-3 w-[1px] h-3.5 bg-gray-200' />
                  <Switch
                    size='l'
                    defaultValue={item.enabled}
app/components/app/create-app-dialog/app-card/index.tsx
@@ -20,8 +20,8 @@
  const { t } = useTranslation()
  const { app: appBasicInfo } = app
  return (
    <div className={cn('group relative flex h-[132px] cursor-pointer flex-col overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg p-4  shadow-xs hover:shadow-lg')}>
      <div className='flex shrink-0 grow-0 items-center gap-3 pb-2'>
    <div className={cn('p-4 h-[132px] relative overflow-hidden flex flex-col group bg-components-panel-on-panel-item-bg border-[0.5px] border-components-panel-border rounded-xl shadow-xs  hover:shadow-lg cursor-pointer')}>
      <div className='flex items-center gap-3 pb-2 grow-0 shrink-0'>
        <div className='relative shrink-0'>
          <AppIcon
            size='large'
@@ -31,24 +31,24 @@
            imageUrl={appBasicInfo.icon_url}
          />
          <AppTypeIcon wrapperClassName='absolute -bottom-0.5 -right-0.5 w-4 h-4 rounded-[4px] border border-divider-regular outline outline-components-panel-on-panel-item-bg'
            className='h-3 w-3' type={appBasicInfo.mode} />
            className='w-3 h-3' type={appBasicInfo.mode} />
        </div>
        <div className='flex grow flex-col gap-1'>
        <div className='grow flex flex-col gap-1'>
          <div className='line-clamp-1'>
            <span className='system-md-semibold text-text-secondary' title={appBasicInfo.name}>{appBasicInfo.name}</span>
          </div>
          <AppTypeLabel className='system-2xs-medium-uppercase text-text-tertiary' type={app.app.mode} />
        </div>
      </div>
      <div className="system-xs-regular py-1 text-text-tertiary">
      <div className="py-1 system-xs-regular text-text-tertiary">
        <div className='line-clamp-3'>
          {app.description}
        </div>
      </div>
      <div className={cn('absolute bottom-0 left-0 right-0 hidden bg-gradient-to-t from-components-panel-gradient-2 from-[60.27%] to-transparent p-4 pt-8 group-hover:flex')}>
        <div className={cn('flex h-8 w-full items-center space-x-2')}>
      <div className={cn('hidden absolute bottom-0 left-0 right-0 p-4 pt-8 group-hover:flex bg-gradient-to-t from-[60.27%] from-components-panel-gradient-2 to-transparent')}>
        <div className={cn('flex items-center w-full space-x-2 h-8')}>
          <Button variant='primary' className='grow' onClick={() => onCreate()}>
            <PlusIcon className='mr-1 h-4 w-4' />
            <PlusIcon className='w-4 h-4 mr-1' />
            <span className='text-xs'>{t('app.newApp.useTemplate')}</span>
          </Button>
        </div>
app/components/app/create-app-dialog/app-list/index.tsx
@@ -27,7 +27,6 @@
import Input from '@/app/components/base/input'
import type { AppMode } from '@/types/app'
import { DSLImportMode } from '@/models/app'
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
type AppsProps = {
  onSuccess?: () => void
@@ -120,7 +119,6 @@
  const [currApp, setCurrApp] = React.useState<App | null>(null)
  const [isShowCreateModal, setIsShowCreateModal] = React.useState(false)
  const { handleCheckPluginDependencies } = usePluginDependencies()
  const onCreate: CreateAppModalProps['onConfirm'] = async ({
    name,
    icon_type,
@@ -128,7 +126,7 @@
    icon_background,
    description,
  }) => {
    const { export_data, mode } = await fetchAppDetail(
    const { export_data } = await fetchAppDetail(
      currApp?.app.id as string,
    )
    try {
@@ -148,12 +146,10 @@
      })
      if (onSuccess)
        onSuccess()
      if (app.app_id)
        await handleCheckPluginDependencies(app.app_id)
      localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
      getRedirection(isCurrentWorkspaceEditor, { id: app.app_id!, mode }, push)
      getRedirection(isCurrentWorkspaceEditor, { id: app.app_id }, push)
    }
    catch {
    catch (e) {
      Toast.notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
    }
  }
@@ -167,12 +163,12 @@
  }
  return (
    <div className='flex h-full flex-col'>
      <div className='flex items-center justify-between border-b border-divider-burn py-3'>
    <div className='h-full flex flex-col'>
      <div className='flex justify-between items-center py-3 border-b border-divider-burn'>
        <div className='min-w-[180px] pl-5'>
          <span className='title-xl-semi-bold text-text-primary'>{t('app.newApp.startFromTemplate')}</span>
        </div>
        <div className='flex max-w-[548px] flex-1 items-center rounded-xl border border-components-panel-border bg-components-panel-bg-blur p-1.5 shadow-md'>
        <div className='flex-1 max-w-[548px] p-1.5 flex items-center rounded-xl shadow-md bg-components-panel-bg-blur border border-components-panel-border'>
          <AppTypeSelector value={currentType} onChange={setCurrentType} />
          <div className='h-[14px]'>
            <Divider type='vertical' />
@@ -180,31 +176,29 @@
          <Input
            showClearIcon
            wrapperClassName='w-full flex-1'
            className='bg-transparent hover:border-transparent hover:bg-transparent focus:border-transparent focus:bg-transparent focus:shadow-none'
            className='bg-transparent hover:bg-transparent focus:bg-transparent hover:border-transparent focus:border-transparent focus:shadow-none'
            placeholder={t('app.newAppFromTemplate.searchAllTemplate') as string}
            value={keywords}
            onChange={e => handleKeywordsChange(e.target.value)}
            onClear={() => handleKeywordsChange('')}
          />
        </div>
        <div className='h-8 w-[180px]'></div>
        <div className='w-[180px] h-8'></div>
      </div>
      <div className='relative flex flex-1 overflow-y-auto'>
        {!searchKeywords && <div className='h-full w-[200px] p-4'>
          <Sidebar current={currCategory as AppCategories} categories={categories} onClick={(category) => { setCurrCategory(category) }} onCreateFromBlank={onCreateFromBlank} />
        {!searchKeywords && <div className='w-[200px] h-full p-4'>
          <Sidebar current={currCategory as AppCategories} onClick={(category) => { setCurrCategory(category) }} onCreateFromBlank={onCreateFromBlank} />
        </div>}
        <div className='h-full flex-1 shrink-0 grow overflow-auto border-l border-divider-burn p-6 pt-2'>
        <div className='flex-1 h-full overflow-auto shrink-0 grow p-6 pt-2 border-l border-divider-burn'>
          {searchFilteredList && searchFilteredList.length > 0 && <>
            <div className='pb-1 pt-4'>
            <div className='pt-4 pb-1'>
              {searchKeywords
                ? <p className='title-md-semi-bold text-text-tertiary'>{searchFilteredList.length > 1 ? t('app.newApp.foundResults', { count: searchFilteredList.length }) : t('app.newApp.foundResult', { count: searchFilteredList.length })}</p>
                : <div className='flex h-[22px] items-center'>
                  <AppCategoryLabel category={currCategory as AppCategories} className='title-md-semi-bold text-text-primary' />
                </div>}
                : <AppCategoryLabel category={currCategory as AppCategories} className='title-md-semi-bold text-text-primary' />}
            </div>
            <div
              className={cn(
                'grid shrink-0 grid-cols-1 content-start gap-3 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6',
                'grid content-start shrink-0 gap-3 grid-cols-1 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6',
              )}>
              {searchFilteredList.map(app => (
                <AppCard
@@ -243,9 +237,9 @@
function NoTemplateFound() {
  const { t } = useTranslation()
  return <div className='w-full rounded-lg bg-workflow-process-bg p-4'>
    <div className='mb-2 inline-flex h-8 w-8 items-center justify-center rounded-lg bg-components-card-bg shadow-lg'>
      <RiRobot2Line className='h-5 w-5 text-text-tertiary' />
  return <div className='p-4 rounded-lg w-full bg-workflow-process-bg'>
    <div className='w-8 h-8 rounded-lg inline-flex items-center justify-center mb-2 shadow-lg bg-components-card-bg'>
      <RiRobot2Line className='w-5 h-5 text-text-tertiary' />
    </div>
    <p className='title-md-semi-bold text-text-primary'>{t('app.newApp.noTemplateFound')}</p>
    <p className='system-sm-regular text-text-tertiary'>{t('app.newApp.noTemplateFoundTip')}</p>
app/components/app/create-app-dialog/app-list/sidebar.tsx
@@ -1,33 +1,43 @@
'use client'
import { RiStickyNoteAddLine, RiThumbUpLine } from '@remixicon/react'
import { RiAppsFill, RiChatSmileAiFill, RiExchange2Fill, RiPassPendingFill, RiQuillPenAiFill, RiSpeakAiFill, RiStickyNoteAddLine, RiTerminalBoxFill, RiThumbUpFill } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import classNames from '@/utils/classnames'
import Divider from '@/app/components/base/divider'
export enum AppCategories {
  RECOMMENDED = 'Recommended',
  ASSISTANT = 'Assistant',
  AGENT = 'Agent',
  HR = 'HR',
  PROGRAMMING = 'Programming',
  WORKFLOW = 'Workflow',
  WRITING = 'Writing',
}
type SidebarProps = {
  current: AppCategories | string
  categories: string[]
  onClick?: (category: AppCategories | string) => void
  current: AppCategories
  onClick?: (category: AppCategories) => void
  onCreateFromBlank?: () => void
}
export default function Sidebar({ current, categories, onClick, onCreateFromBlank }: SidebarProps) {
export default function Sidebar({ current, onClick, onCreateFromBlank }: SidebarProps) {
  const { t } = useTranslation()
  return <div className="flex h-full w-full flex-col">
    <ul className='pt-0.5'>
  return <div className="w-full h-full flex flex-col">
    <ul>
      <CategoryItem category={AppCategories.RECOMMENDED} active={current === AppCategories.RECOMMENDED} onClick={onClick} />
    </ul>
    <div className='system-xs-medium-uppercase mb-0.5 mt-3 px-3 pb-1 pt-2 text-text-tertiary'>{t('app.newAppFromTemplate.byCategories')}</div>
    <ul className='flex grow flex-col gap-0.5'>
      {categories.map(category => (<CategoryItem key={category} category={category} active={current === category} onClick={onClick} />))}
    <div className='px-3 pt-2 pb-1 system-xs-medium-uppercase text-text-tertiary'>{t('app.newAppFromTemplate.byCategories')}</div>
    <ul className='flex-grow flex flex-col gap-0.5'>
      <CategoryItem category={AppCategories.ASSISTANT} active={current === AppCategories.ASSISTANT} onClick={onClick} />
      <CategoryItem category={AppCategories.AGENT} active={current === AppCategories.AGENT} onClick={onClick} />
      <CategoryItem category={AppCategories.HR} active={current === AppCategories.HR} onClick={onClick} />
      <CategoryItem category={AppCategories.PROGRAMMING} active={current === AppCategories.PROGRAMMING} onClick={onClick} />
      <CategoryItem category={AppCategories.WORKFLOW} active={current === AppCategories.WORKFLOW} onClick={onClick} />
      <CategoryItem category={AppCategories.WRITING} active={current === AppCategories.WRITING} onClick={onClick} />
    </ul>
    <Divider bgStyle='gradient' />
    <div className='flex cursor-pointer items-center gap-1 px-3 py-1 text-text-tertiary' onClick={onCreateFromBlank}>
      <RiStickyNoteAddLine className='h-3.5 w-3.5' />
    <div className='px-3 py-1 flex items-center gap-1 text-text-tertiary cursor-pointer' onClick={onCreateFromBlank}>
      <RiStickyNoteAddLine className='w-3.5 h-3.5' />
      <span className='system-xs-regular'>{t('app.newApp.startFromBlank')}</span>
    </div>
  </div>
@@ -35,26 +45,47 @@
type CategoryItemProps = {
  active: boolean
  category: AppCategories | string
  onClick?: (category: AppCategories | string) => void
  category: AppCategories
  onClick?: (category: AppCategories) => void
}
function CategoryItem({ category, active, onClick }: CategoryItemProps) {
  return <li
    className={classNames('p-1 pl-3 h-8 rounded-lg flex items-center gap-2 group cursor-pointer hover:bg-state-base-hover [&.active]:bg-state-base-active', active && 'active')}
    className={classNames('p-1 pl-3 rounded-lg flex items-center gap-2 group cursor-pointer hover:bg-state-base-hover [&.active]:bg-state-base-active', active && 'active')}
    onClick={() => { onClick?.(category) }}>
    {category === AppCategories.RECOMMENDED && <div className='inline-flex h-5 w-5 items-center justify-center rounded-md'>
      <RiThumbUpLine className='h-4 w-4 text-components-menu-item-text group-[.active]:text-components-menu-item-text-active' />
    </div>}
    <div className='w-5 h-5 inline-flex items-center justify-center rounded-md border border-divider-regular bg-components-icon-bg-midnight-solid group-[.active]:bg-components-icon-bg-blue-solid'>
      <AppCategoryIcon category={category} />
    </div>
    <AppCategoryLabel category={category}
      className={classNames('system-sm-medium text-components-menu-item-text group-[.active]:text-components-menu-item-text-active group-hover:text-components-menu-item-text-hover', active && 'system-sm-semibold')} />
  </li >
}
type AppCategoryLabelProps = {
  category: AppCategories | string
  category: AppCategories
  className?: string
}
export function AppCategoryLabel({ category, className }: AppCategoryLabelProps) {
  const { t } = useTranslation()
  return <span className={className}>{category === AppCategories.RECOMMENDED ? t('app.newAppFromTemplate.sidebar.Recommended') : category}</span>
  return <span className={className}>{t(`app.newAppFromTemplate.sidebar.${category}`)}</span>
}
type AppCategoryIconProps = {
  category: AppCategories
}
function AppCategoryIcon({ category }: AppCategoryIconProps) {
  if (category === AppCategories.AGENT)
    return <RiSpeakAiFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
  if (category === AppCategories.ASSISTANT)
    return <RiChatSmileAiFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
  if (category === AppCategories.HR)
    return <RiPassPendingFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
  if (category === AppCategories.PROGRAMMING)
    return <RiTerminalBoxFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
  if (category === AppCategories.RECOMMENDED)
    return <RiThumbUpFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
  if (category === AppCategories.WRITING)
    return <RiQuillPenAiFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
  if (category === AppCategories.WORKFLOW)
    return <RiExchange2Fill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
  return <RiAppsFill className='w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100' />
}
app/components/app/create-app-dialog/index.tsx
@@ -1,6 +1,4 @@
'use client'
import { useCallback } from 'react'
import { useKeyPress } from 'ahooks'
import AppList from './app-list'
import FullScreenModal from '@/app/components/base/fullscreen-modal'
@@ -12,13 +10,6 @@
}
const CreateAppTemplateDialog = ({ show, onSuccess, onClose, onCreateFromBlank }: CreateAppDialogProps) => {
  const handleEscKeyPress = useCallback(() => {
    if (show)
      onClose()
  }, [show, onClose])
  useKeyPress('esc', handleEscKeyPress)
  return (
    <FullScreenModal
      open={show}
app/components/app/create-app-dialog/newAppDialog.tsx
New file
@@ -0,0 +1,57 @@
import { Fragment, useCallback } from 'react'
import type { ReactNode } from 'react'
import { Dialog, Transition } from '@headlessui/react'
import cn from '@/utils/classnames'
type DialogProps = {
  className?: string
  children: ReactNode
  show: boolean
  onClose?: () => void
}
const NewAppDialog = ({
  className,
  children,
  show,
  onClose,
}: DialogProps) => {
  const close = useCallback(() => onClose?.(), [onClose])
  return (
    <Transition appear show={show} as={Fragment}>
      <Dialog as="div" className="relative z-40" onClose={close}>
        <Transition.Child
          as={Fragment}
          enter="ease-out duration-300"
          enterFrom="opacity-0"
          enterTo="opacity-100"
          leave="ease-in duration-200"
          leaveFrom="opacity-100"
          leaveTo="opacity-0"
        >
          <div className="fixed inset-0 bg-black bg-opacity-25" />
        </Transition.Child>
        <div className="fixed inset-0">
          <div className="flex flex-col items-center justify-center min-h-full pt-[56px]">
            <Transition.Child
              as={Fragment}
              enter="ease-out duration-300"
              enterFrom="opacity-0 scale-95"
              enterTo="opacity-100 scale-100"
              leave="ease-in duration-200"
              leaveFrom="opacity-100 scale-100"
              leaveTo="opacity-0 scale-95"
            >
              <Dialog.Panel className={cn('grow relative w-full h-[calc(100vh-56px)] p-0 overflow-hidden text-left align-middle transition-all transform bg-white shadow-xl rounded-t-xl', className)}>
                {children}
              </Dialog.Panel>
            </Transition.Child>
          </div>
        </div>
      </Dialog>
    </Transition >
  )
}
export default NewAppDialog
app/components/app/create-app-modal/index.tsx
@@ -1,9 +1,9 @@
'use client'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useRouter, useSearchParams } from 'next/navigation'
import { useRouter } from 'next/navigation'
import { useContext, useContextSelector } from 'use-context-selector'
import { RiArrowRightLine, RiCommandLine, RiCornerDownLeftLine, RiExchange2Fill } from '@remixicon/react'
import Link from 'next/link'
@@ -14,12 +14,10 @@
import Button from '@/app/components/base/button'
import Divider from '@/app/components/base/divider'
import cn from '@/utils/classnames'
import { WEB_PREFIX } from '@/config'
import AppsContext, { useAppContext } from '@/context/app-context'
import { useProviderContext } from '@/context/provider-context'
import { ToastContext } from '@/app/components/base/toast'
import type { AppMode } from '@/types/app'
import { AppModes } from '@/types/app'
import { createApp } from '@/service/apps'
import Input from '@/app/components/base/input'
import Textarea from '@/app/components/base/textarea'
@@ -29,7 +27,6 @@
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import { getRedirection } from '@/utils/app-redirection'
import FullScreenModal from '@/app/components/base/fullscreen-modal'
import useTheme from '@/hooks/use-theme'
type CreateAppProps = {
  onSuccess: () => void
@@ -54,14 +51,6 @@
  const { isCurrentWorkspaceEditor } = useAppContext()
  const isCreatingRef = useRef(false)
  const searchParams = useSearchParams()
  useEffect(() => {
    const category = searchParams.get('category')
    if (category && AppModes.includes(category as AppMode))
      setAppMode(category as AppMode)
  }, [searchParams])
  const onCreate = useCallback(async () => {
    if (!appMode) {
@@ -91,7 +80,7 @@
      localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
      getRedirection(isCurrentWorkspaceEditor, app, push)
    }
    catch {
    catch (e) {
      notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
    }
    isCreatingRef.current = false
@@ -104,17 +93,17 @@
    handleCreateApp()
  })
  return <>
    <div className='flex h-full justify-center overflow-y-auto overflow-x-hidden'>
      <div className='flex flex-1 shrink-0 justify-end'>
    <div className='flex justify-center h-full overflow-y-auto overflow-x-hidden'>
      <div className='flex-1 shrink-0 flex justify-end'>
        <div className='px-10'>
          <div className='h-6 w-full 2xl:h-[139px]' />
          <div className='pb-6 pt-1'>
          <div className='w-full h-6 2xl:h-[139px]' />
          <div className='pt-1 pb-6'>
            <span className='title-2xl-semi-bold text-text-primary'>{t('app.newApp.startFromBlank')}</span>
          </div>
          <div className='mb-2 leading-6'>
          <div className='leading-6 mb-2'>
            <span className='system-sm-semibold text-text-secondary'>{t('app.newApp.chooseAppType')}</span>
          </div>
          <div className='flex w-[660px] flex-col gap-4'>
          <div className='flex flex-col w-[660px] gap-4'>
            <div>
              <div className='mb-2'>
                <span className='system-2xs-medium-uppercase text-text-tertiary'>{t('app.newApp.forBeginners')}</span>
@@ -124,8 +113,8 @@
                  active={appMode === 'chat'}
                  title={t('app.types.chatbot')}
                  description={t('app.newApp.chatbotShortDescription')}
                  icon={<div className='flex h-6 w-6 items-center justify-center rounded-md bg-components-icon-bg-blue-solid'>
                    <ChatBot className='h-4 w-4 text-components-avatar-shape-fill-stop-100' />
                  icon={<div className='w-6 h-6 bg-components-icon-bg-blue-solid rounded-md flex items-center justify-center'>
                    <ChatBot className='w-4 h-4 text-components-avatar-shape-fill-stop-100' />
                  </div>}
                  onClick={() => {
                    setAppMode('chat')
@@ -134,8 +123,8 @@
                  active={appMode === 'agent-chat'}
                  title={t('app.types.agent')}
                  description={t('app.newApp.agentShortDescription')}
                  icon={<div className='flex h-6 w-6 items-center justify-center rounded-md bg-components-icon-bg-violet-solid'>
                    <Logic className='h-4 w-4 text-components-avatar-shape-fill-stop-100' />
                  icon={<div className='w-6 h-6 bg-components-icon-bg-violet-solid rounded-md flex items-center justify-center'>
                    <Logic className='w-4 h-4 text-components-avatar-shape-fill-stop-100' />
                  </div>}
                  onClick={() => {
                    setAppMode('agent-chat')
@@ -144,8 +133,8 @@
                  active={appMode === 'completion'}
                  title={t('app.newApp.completeApp')}
                  description={t('app.newApp.completionShortDescription')}
                  icon={<div className='flex h-6 w-6 items-center justify-center rounded-md bg-components-icon-bg-teal-solid'>
                    <ListSparkle className='h-4 w-4 text-components-avatar-shape-fill-stop-100' />
                  icon={<div className='w-6 h-6 bg-components-icon-bg-teal-solid rounded-md flex items-center justify-center'>
                    <ListSparkle className='w-4 h-4 text-components-avatar-shape-fill-stop-100' />
                  </div>}
                  onClick={() => {
                    setAppMode('completion')
@@ -158,21 +147,23 @@
              </div>
              <div className='flex flex-row gap-2'>
                <AppTypeCard
                  beta
                  active={appMode === 'advanced-chat'}
                  title={t('app.types.advanced')}
                  description={t('app.newApp.advancedShortDescription')}
                  icon={<div className='flex h-6 w-6 items-center justify-center rounded-md bg-components-icon-bg-blue-light-solid'>
                    <BubbleTextMod className='h-4 w-4 text-components-avatar-shape-fill-stop-100' />
                  icon={<div className='w-6 h-6 bg-components-icon-bg-blue-light-solid rounded-md flex items-center justify-center'>
                    <BubbleTextMod className='w-4 h-4 text-components-avatar-shape-fill-stop-100' />
                  </div>}
                  onClick={() => {
                    setAppMode('advanced-chat')
                  }} />
                <AppTypeCard
                  beta
                  active={appMode === 'workflow'}
                  title={t('app.types.workflow')}
                  description={t('app.newApp.workflowShortDescription')}
                  icon={<div className='flex h-6 w-6 items-center justify-center rounded-md bg-components-icon-bg-indigo-solid'>
                    <RiExchange2Fill className='h-4 w-4 text-components-avatar-shape-fill-stop-100' />
                  icon={<div className='w-6 h-6 bg-components-icon-bg-indigo-solid rounded-md flex items-center justify-center'>
                    <RiExchange2Fill className='w-4 h-4 text-components-avatar-shape-fill-stop-100' />
                  </div>}
                  onClick={() => {
                    setAppMode('workflow')
@@ -180,9 +171,9 @@
              </div>
            </div>
            <Divider style={{ margin: 0 }} />
            <div className='flex items-center space-x-3'>
            <div className='flex space-x-3 items-center'>
              <div className='flex-1'>
                <div className='mb-1 flex h-6 items-center'>
                <div className='h-6 flex items-center mb-1'>
                  <label className='system-sm-semibold text-text-secondary'>{t('app.newApp.captionName')}</label>
                </div>
                <Input
@@ -210,9 +201,9 @@
              />}
            </div>
            <div>
              <div className='mb-1 flex h-6 items-center'>
              <div className='h-6 flex items-center mb-1'>
                <label className='system-sm-semibold text-text-secondary'>{t('app.newApp.captionDescription')}</label>
                <span className='system-xs-regular ml-1 text-text-tertiary'>({t('app.newApp.optional')})</span>
                <span className='system-xs-regular text-text-tertiary ml-1'>({t('app.newApp.optional')})</span>
              </div>
              <Textarea
                className='resize-none'
@@ -222,12 +213,11 @@
              />
            </div>
          </div>
          {isAppsFull && <AppsFull className='mt-4' loc='app-create' />}
          <div className='flex items-center justify-between pb-10 pt-5'>
            <div className='system-xs-regular flex cursor-pointer items-center gap-1 text-text-tertiary' onClick={onCreateFromTemplate}>
          <div className='pt-5 pb-10 flex justify-between items-center'>
            <div className='flex gap-1 items-center system-xs-regular text-text-tertiary cursor-pointer' onClick={onCreateFromTemplate}>
              <span>{t('app.newApp.noIdeaTip')}</span>
              <div className='p-[1px]'>
                <RiArrowRightLine className='h-3.5 w-3.5' />
                <RiArrowRightLine className='w-3.5 h-3.5' />
              </div>
            </div>
            <div className='flex gap-2'>
@@ -235,21 +225,21 @@
              <Button disabled={isAppsFull || !name} className='gap-1' variant="primary" onClick={handleCreateApp}>
                <span>{t('app.newApp.Create')}</span>
                <div className='flex gap-0.5'>
                  <RiCommandLine size={14} className='system-kbd rounded-sm bg-components-kbd-bg-white p-0.5' />
                  <RiCornerDownLeftLine size={14} className='system-kbd rounded-sm bg-components-kbd-bg-white p-0.5' />
                  <RiCommandLine size={14} className='p-0.5 system-kbd bg-components-kbd-bg-white rounded-sm' />
                  <RiCornerDownLeftLine size={14} className='p-0.5 system-kbd bg-components-kbd-bg-white rounded-sm' />
                </div>
              </Button>
            </div>
          </div>
        </div>
      </div>
      <div className='relative flex h-full flex-1 shrink justify-start overflow-hidden'>
        <div className='absolute left-0 right-0 top-0 h-6 border-b border-b-divider-subtle 2xl:h-[139px]'></div>
      <div className='flex-1 shrink h-full flex justify-start relative overflow-hidden'>
        <div className='h-6 2xl:h-[139px] absolute left-0 top-0 right-0 border-b border-b-divider-subtle'></div>
        <div className='max-w-[760px] border-x border-x-divider-subtle'>
          <div className='h-6 2xl:h-[139px]' />
          <AppPreview mode={appMode} />
          <div className='absolute left-0 right-0 border-b border-b-divider-subtle'></div>
          <div className='flex h-[448px] w-[664px] items-center justify-center' style={{ background: 'repeating-linear-gradient(135deg, transparent, transparent 2px, rgba(16,24,40,0.04) 4px,transparent 3px, transparent 6px)' }}>
          <div className='w-[664px] h-[448px] flex items-center justify-center' style={{ background: 'repeating-linear-gradient(135deg, transparent, transparent 2px, rgba(16,24,40,0.04) 4px,transparent 3px, transparent 6px)' }}>
            <AppScreenShot show={appMode === 'chat'} mode='chat' />
            <AppScreenShot show={appMode === 'advanced-chat'} mode='advanced-chat' />
            <AppScreenShot show={appMode === 'agent-chat'} mode='agent-chat' />
@@ -260,6 +250,13 @@
        </div>
      </div>
    </div>
    {
      isAppsFull && (
        <div className='px-8 py-2'>
          <AppsFull loc='app-create' />
        </div>
      )
    }
  </>
}
type CreateAppDialogProps = CreateAppProps & {
@@ -281,25 +278,30 @@
export default CreateAppModal
type AppTypeCardProps = {
  icon: React.JSX.Element
  icon: JSX.Element
  beta?: boolean
  title: string
  description: string
  active: boolean
  onClick: () => void
}
function AppTypeCard({ icon, title, description, active, onClick }: AppTypeCardProps) {
function AppTypeCard({ icon, title, beta = false, description, active, onClick }: AppTypeCardProps) {
  const { t } = useTranslation()
  return <div
    className={
      cn(`relative box-content h-[84px] w-[191px] cursor-pointer rounded-xl
      border-[0.5px] border-components-option-card-option-border
      bg-components-panel-on-panel-item-bg p-3 shadow-xs hover:shadow-md`, active
        ? 'shadow-md outline outline-[1.5px] outline-components-option-card-option-selected-border'
      cn(`w-[191px] h-[84px] p-3 border-[0.5px] relative box-content
      rounded-xl border-components-option-card-option-border
      bg-components-panel-on-panel-item-bg shadow-xs cursor-pointer hover:shadow-md`, active
        ? 'outline outline-[1.5px] outline-components-option-card-option-selected-border shadow-md'
        : '')
    }
    onClick={onClick}
  >
    {beta && <div className='px-[5px] py-[3px]
      rounded-[5px] min-w-[18px] absolute top-3 right-3
      border border-divider-deep system-2xs-medium-uppercase text-text-tertiary'>{t('common.menus.status')}</div>}
    {icon}
    <div className='system-sm-semibold mb-0.5 mt-2 text-text-secondary'>{title}</div>
    <div className='system-sm-semibold text-text-secondary mt-2 mb-0.5'>{title}</div>
    <div className='system-xs-regular text-text-tertiary'>{description}</div>
  </div>
}
@@ -310,17 +312,17 @@
    'chat': {
      title: t('app.types.chatbot'),
      description: t('app.newApp.chatbotUserDescription'),
      link: 'https://docs.dify.ai/guides/application-orchestrate/readme',
      link: 'https://docs.dify.ai/guides/application-orchestrate/conversation-application?fallback=true',
    },
    'advanced-chat': {
      title: t('app.types.advanced'),
      description: t('app.newApp.advancedUserDescription'),
      link: 'https://docs.dify.ai/en/guides/workflow/README',
      link: 'https://docs.dify.ai/guides/workflow',
    },
    'agent-chat': {
      title: t('app.types.agent'),
      description: t('app.newApp.agentUserDescription'),
      link: 'https://docs.dify.ai/en/guides/application-orchestrate/agent',
      link: 'https://docs.dify.ai/guides/application-orchestrate/agent',
    },
    'completion': {
      title: t('app.newApp.completeApp'),
@@ -330,21 +332,21 @@
    'workflow': {
      title: t('app.types.workflow'),
      description: t('app.newApp.workflowUserDescription'),
      link: 'https://docs.dify.ai/en/guides/workflow/README',
      link: 'https://docs.dify.ai/guides/workflow',
    },
  }
  const previewInfo = modeToPreviewInfoMap[mode]
  return <div className='px-8 py-4'>
    <h4 className='system-sm-semibold-uppercase text-text-secondary'>{previewInfo.title}</h4>
    <div className='system-xs-regular mt-1 min-h-8 max-w-96 text-text-tertiary'>
    <div className='mt-1 system-xs-regular text-text-tertiary max-w-96 min-h-8'>
      <span>{previewInfo.description}</span>
      {previewInfo.link && <Link target='_blank' href={previewInfo.link} className='ml-1 text-text-accent'>{t('app.newApp.learnMore')}</Link>}
      {previewInfo.link && <Link target='_blank' href={previewInfo.link} className='text-text-accent ml-1'>{t('app.newApp.learnMore')}</Link>}
    </div>
  </div>
}
function AppScreenShot({ mode, show }: { mode: AppMode; show: boolean }) {
  const { theme } = useTheme()
  const theme = useContextSelector(AppsContext, state => state.theme)
  const modeToImageMap = {
    'chat': 'Chatbot',
    'advanced-chat': 'Chatflow',
@@ -353,11 +355,11 @@
    'workflow': 'Workflow',
  }
  return <picture>
    <source media="(resolution: 1x)" srcSet={`${WEB_PREFIX}/screenshots/${theme}/${modeToImageMap[mode]}.png`} />
    <source media="(resolution: 2x)" srcSet={`${WEB_PREFIX}/screenshots/${theme}/${modeToImageMap[mode]}@2x.png`} />
    <source media="(resolution: 3x)" srcSet={`${WEB_PREFIX}/screenshots/${theme}/${modeToImageMap[mode]}@3x.png`} />
    <source media="(resolution: 1x)" srcSet={`/screenshots/${theme}/${modeToImageMap[mode]}.png`} />
    <source media="(resolution: 2x)" srcSet={`/screenshots/${theme}/${modeToImageMap[mode]}@2x.png`} />
    <source media="(resolution: 3x)" srcSet={`/screenshots/${theme}/${modeToImageMap[mode]}@3x.png`} />
    <Image className={show ? '' : 'hidden'}
      src={`${WEB_PREFIX}/screenshots/${theme}/${modeToImageMap[mode]}.png`}
      src={`/screenshots/${theme}/${modeToImageMap[mode]}.png`}
      alt='App Screen Shot'
      width={664} height={448} />
  </picture>
app/components/app/create-from-dsl-modal/index.tsx
@@ -5,8 +5,7 @@
import { useRouter } from 'next/navigation'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import { RiCloseLine, RiCommandLine, RiCornerDownLeftLine } from '@remixicon/react'
import { useDebounceFn, useKeyPress } from 'ahooks'
import { RiCloseLine } from '@remixicon/react'
import Uploader from './uploader'
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
@@ -26,8 +25,6 @@
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import { getRedirection } from '@/utils/app-redirection'
import cn from '@/utils/classnames'
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
import { noop } from 'lodash-es'
type CreateFromDSLModalProps = {
  show: boolean
@@ -53,7 +50,6 @@
  const [showErrorModal, setShowErrorModal] = useState(false)
  const [versions, setVersions] = useState<{ importedVersion: string; systemVersion: string }>()
  const [importId, setImportId] = useState<string>()
  const { handleCheckPluginDependencies } = usePluginDependencies()
  const readFile = (file: File) => {
    const reader = new FileReader()
@@ -104,7 +100,8 @@
      if (!response)
        return
      const { id, status, app_id, app_mode, imported_dsl_version, current_dsl_version } = response
      const { id, status, app_id, imported_dsl_version, current_dsl_version } = response
      if (status === DSLImportStatus.COMPLETED || status === DSLImportStatus.COMPLETED_WITH_WARNINGS) {
        if (onSuccess)
          onSuccess()
@@ -117,9 +114,7 @@
          children: status === DSLImportStatus.COMPLETED_WITH_WARNINGS && t('app.newApp.appCreateDSLWarning'),
        })
        localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
        if (app_id)
          await handleCheckPluginDependencies(app_id)
        getRedirection(isCurrentWorkspaceEditor, { id: app_id!, mode: app_mode }, push)
        getRedirection(isCurrentWorkspaceEditor, { id: app_id }, push)
      }
      else if (status === DSLImportStatus.PENDING) {
        setVersions({
@@ -137,24 +132,11 @@
        notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
      }
    }
    // eslint-disable-next-line unused-imports/no-unused-vars
    catch (e) {
      notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
    }
    isCreatingRef.current = false
  }
  const { run: handleCreateApp } = useDebounceFn(onCreate, { wait: 300 })
  useKeyPress(['meta.enter', 'ctrl.enter'], () => {
    if (show && !isAppsFull && ((currentTab === CreateFromDSLModalTab.FROM_FILE && currentFile) || (currentTab === CreateFromDSLModalTab.FROM_URL && dslUrlValue)))
      handleCreateApp()
  })
  useKeyPress('esc', () => {
    if (show && !showErrorModal)
      onClose()
  })
  const onDSLConfirm: MouseEventHandler = async () => {
    try {
@@ -164,7 +146,7 @@
        import_id: importId,
      })
      const { status, app_id, app_mode } = response
      const { status, app_id } = response
      if (status === DSLImportStatus.COMPLETED) {
        if (onSuccess)
@@ -176,16 +158,13 @@
          type: 'success',
          message: t('app.newApp.appCreated'),
        })
        if (app_id)
          await handleCheckPluginDependencies(app_id)
        localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
        getRedirection(isCurrentWorkspaceEditor, { id: app_id!, mode: app_mode }, push)
        getRedirection(isCurrentWorkspaceEditor, { id: app_id }, push)
      }
      else if (status === DSLImportStatus.FAILED) {
        notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
      }
    }
    // eslint-disable-next-line unused-imports/no-unused-vars
    catch (e) {
      notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
    }
@@ -215,26 +194,26 @@
  return (
    <>
      <Modal
        className='w-[520px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-0 shadow-xl'
        className='p-0 w-[520px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl'
        isShow={show}
        onClose={noop}
        onClose={() => { }}
      >
        <div className='title-2xl-semi-bold flex items-center justify-between pb-3 pl-6 pr-5 pt-6 text-text-primary'>
        <div className='flex items-center justify-between pt-6 pl-6 pr-5 pb-3 text-text-primary title-2xl-semi-bold'>
          {t('app.importFromDSL')}
          <div
            className='flex h-8 w-8 cursor-pointer items-center'
            className='flex items-center w-8 h-8 cursor-pointer'
            onClick={() => onClose()}
          >
            <RiCloseLine className='h-5 w-5 text-text-tertiary' />
            <RiCloseLine className='w-5 h-5 text-text-tertiary' />
          </div>
        </div>
        <div className='system-md-semibold flex h-9 items-center space-x-6 border-b border-divider-subtle px-6 text-text-tertiary'>
        <div className='flex items-center px-6 h-9 space-x-6 system-md-semibold text-text-tertiary border-b border-divider-subtle'>
          {
            tabs.map(tab => (
              <div
                key={tab.key}
                className={cn(
                  'relative flex h-full cursor-pointer items-center',
                  'relative flex items-center h-full cursor-pointer',
                  currentTab === tab.key && 'text-text-primary',
                )}
                onClick={() => setCurrentTab(tab.key)}
@@ -242,7 +221,7 @@
                {tab.label}
                {
                  currentTab === tab.key && (
                    <div className='absolute bottom-0 h-[2px] w-full bg-util-colors-blue-brand-blue-brand-600'></div>
                    <div className='absolute bottom-0 w-full h-[2px] bg-util-colors-blue-brand-blue-brand-600'></div>
                  )
                }
              </div>
@@ -262,7 +241,7 @@
          {
            currentTab === CreateFromDSLModalTab.FROM_URL && (
              <div>
                <div className='system-md-semibold mb-1 text-text-secondary'>DSL URL</div>
                <div className='mb-1 system-md-semibold leading6'>DSL URL</div>
                <Input
                  placeholder={t('app.importFromDSLUrlPlaceholder') || ''}
                  value={dslUrlValue}
@@ -279,18 +258,7 @@
        )}
        <div className='flex justify-end px-6 py-5'>
          <Button className='mr-2' onClick={onClose}>{t('app.newApp.Cancel')}</Button>
          <Button
            disabled={buttonDisabled}
            variant="primary"
            onClick={handleCreateApp}
            className="gap-1"
          >
            <span>{t('app.newApp.Create')}</span>
            <div className='flex gap-0.5'>
              <RiCommandLine size={14} className='system-kbd rounded-sm bg-components-kbd-bg-white p-0.5' />
              <RiCornerDownLeftLine size={14} className='system-kbd rounded-sm bg-components-kbd-bg-white p-0.5' />
            </div>
          </Button>
          <Button disabled={buttonDisabled} variant="primary" onClick={onCreate}>{t('app.newApp.Create')}</Button>
        </div>
      </Modal>
      <Modal
@@ -298,9 +266,9 @@
        onClose={() => setShowErrorModal(false)}
        className='w-[480px]'
      >
        <div className='flex flex-col items-start gap-2 self-stretch pb-4'>
          <div className='title-2xl-semi-bold text-text-primary'>{t('app.newApp.appCreateDSLErrorTitle')}</div>
          <div className='system-md-regular flex grow flex-col text-text-secondary'>
        <div className='flex pb-4 flex-col items-start gap-2 self-stretch'>
          <div className='text-text-primary title-2xl-semi-bold'>{t('app.newApp.appCreateDSLErrorTitle')}</div>
          <div className='flex flex-grow flex-col text-text-secondary system-md-regular'>
            <div>{t('app.newApp.appCreateDSLErrorPart1')}</div>
            <div>{t('app.newApp.appCreateDSLErrorPart2')}</div>
            <br />
@@ -308,7 +276,7 @@
            <div>{t('app.newApp.appCreateDSLErrorPart4')}<span className='system-md-medium'>{versions?.systemVersion}</span></div>
          </div>
        </div>
        <div className='flex items-start justify-end gap-2 self-stretch pt-6'>
        <div className='flex pt-6 justify-end items-start gap-2 self-stretch'>
          <Button variant='secondary' onClick={() => setShowErrorModal(false)}>{t('app.newApp.Cancel')}</Button>
          <Button variant='primary' destructive onClick={onDSLConfirm}>{t('app.newApp.Confirm')}</Button>
        </div>
app/components/app/create-from-dsl-modal/uploader.tsx
@@ -3,7 +3,6 @@
import React, { useEffect, useRef, useState } from 'react'
import {
  RiDeleteBinLine,
  RiUploadCloud2Line,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
@@ -11,7 +10,8 @@
import cn from '@/utils/classnames'
import { Yaml as YamlIcon } from '@/app/components/base/icons/src/public/files'
import { ToastContext } from '@/app/components/base/toast'
import ActionButton from '@/app/components/base/action-button'
import { UploadCloud01 } from '@/app/components/base/icons/src/vender/line/general'
import Button from '@/app/components/base/button'
export type Props = {
  file: File | undefined
@@ -97,39 +97,41 @@
        style={{ display: 'none' }}
        type="file"
        id="fileUploader"
        accept='.yaml,.yml'
        accept='.yml'
        onChange={fileChangeHandle}
      />
      <div ref={dropRef}>
        {!file && (
          <div className={cn('flex h-12 items-center rounded-[10px] border border-dashed border-components-dropzone-border bg-components-dropzone-bg text-sm font-normal', dragging && 'border-components-dropzone-border-accent bg-components-dropzone-bg-accent')}>
            <div className='flex w-full items-center justify-center space-x-2'>
              <RiUploadCloud2Line className='h-6 w-6 text-text-tertiary' />
              <div className='text-text-tertiary'>
          <div className={cn('flex items-center h-12 rounded-xl bg-gray-50 border border-dashed border-gray-200 text-sm font-normal', dragging && 'bg-[#F5F8FF] border border-[#B2CCFF]')}>
            <div className='w-full flex items-center justify-center space-x-2'>
              <UploadCloud01 className='w-6 h-6 mr-2' />
              <div className='text-gray-500'>
                {t('datasetCreation.stepOne.uploader.button')}
                <span className='cursor-pointer pl-1 text-text-accent' onClick={selectHandle}>{t('datasetDocuments.list.batchModal.browse')}</span>
                <span className='pl-1 text-[#155eef] cursor-pointer' onClick={selectHandle}>{t('datasetDocuments.list.batchModal.browse')}</span>
              </div>
            </div>
            {dragging && <div ref={dragRef} className='absolute left-0 top-0 h-full w-full' />}
            {dragging && <div ref={dragRef} className='absolute w-full h-full top-0 left-0' />}
          </div>
        )}
        {file && (
          <div className={cn('group flex items-center rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg shadow-xs', ' hover:bg-components-panel-on-panel-item-bg-hover')}>
            <div className='flex items-center justify-center p-3'>
              <YamlIcon className="h-6 w-6 shrink-0" />
          <div className={cn('flex items-center rounded-lg bg-components-panel-on-panel-item-bg border-[0.5px] border-components-panel-border shadow-xs group', 'hover:bg-[#F5F8FF] hover:border-[#B2CCFF]')}>
            <div className='flex p-3 justify-center items-center'>
              <YamlIcon className="w-6 h-6 shrink-0" />
            </div>
            <div className='flex grow flex-col items-start gap-0.5 py-1 pr-2'>
              <span className='font-inter max-w-[calc(100%_-_30px)] overflow-hidden text-ellipsis whitespace-nowrap text-[12px] font-medium leading-4 text-text-secondary'>{file.name}</span>
              <div className='font-inter flex h-3 items-center gap-1 self-stretch text-[10px] font-medium uppercase leading-3 text-text-tertiary'>
            <div className='flex py-1 pr-2 grow flex-col items-start gap-0.5'>
              <span className='max-w-[calc(100%_-_30px)] text-ellipsis whitespace-nowrap overflow-hidden text-text-secondary font-inter text-[12px] font-medium leading-4'>{file.name}</span>
              <div className='flex h-3 items-center gap-1 self-stretch text-text-tertiary font-inter text-[10px] font-medium leading-3 uppercase'>
                <span>YAML</span>
                <span className='text-text-quaternary'>·</span>
                <span>{formatFileSize(file.size)}</span>
              </div>
            </div>
            <div className='hidden items-center pr-3 group-hover:flex'>
              <ActionButton onClick={removeFile}>
                <RiDeleteBinLine className='h-4 w-4 text-text-tertiary' />
              </ActionButton>
            <div className='hidden group-hover:flex items-center'>
              <Button onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.change')}</Button>
              <div className='mx-2 w-px h-4 bg-gray-200' />
              <div className='p-2 cursor-pointer' onClick={removeFile}>
                <RiDeleteBinLine className='w-4 h-4 text-text-tertiary' />
              </div>
            </div>
          </div>
        )}
app/components/app/duplicate-modal/index.tsx
@@ -1,8 +1,8 @@
'use client'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { RiCloseLine } from '@remixicon/react'
import AppIconPicker from '../../base/app-icon-picker'
import s from './style.module.css'
import cn from '@/utils/classnames'
import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
@@ -12,7 +12,6 @@
import { useProviderContext } from '@/context/provider-context'
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
import type { AppIconType } from '@/types/app'
import { noop } from 'lodash-es'
export type DuplicateAppModalProps = {
  appName: string
@@ -72,15 +71,13 @@
    <>
      <Modal
        isShow={show}
        onClose={noop}
        className={cn('relative !max-w-[480px]', 'px-8')}
        onClose={() => { }}
        className={cn(s.modal, '!max-w-[480px]', 'px-8')}
      >
        <div className='absolute right-4 top-4 cursor-pointer p-2' onClick={onHide}>
          <RiCloseLine className='h-4 w-4 text-text-tertiary' />
        </div>
        <div className='relative mb-9 mt-3 text-xl font-semibold leading-[30px] text-text-primary'>{t('app.duplicateTitle')}</div>
        <div className='system-sm-regular mb-9 text-text-secondary'>
          <div className='system-md-medium mb-2'>{t('explore.appCustomize.subTitle')}</div>
        <span className={s.close} onClick={onHide} />
        <div className={s.title}>{t('app.duplicateTitle')}</div>
        <div className={s.content}>
          <div className={s.subTitle}>{t('explore.appCustomize.subTitle')}</div>
          <div className='flex items-center justify-between space-x-2'>
            <AppIcon
              size='large'
@@ -97,10 +94,10 @@
              className='h-10'
            />
          </div>
          {isAppsFull && <AppsFull className='mt-4' loc='app-duplicate-create' />}
          {isAppsFull && <AppsFull loc='app-duplicate-create' />}
        </div>
        <div className='flex flex-row-reverse'>
          <Button disabled={isAppsFull} className='ml-2 w-24' variant='primary' onClick={submit}>{t('app.duplicate')}</Button>
          <Button disabled={isAppsFull} className='w-24 ml-2' variant='primary' onClick={submit}>{t('app.duplicate')}</Button>
          <Button className='w-24' onClick={onHide}>{t('common.operation.cancel')}</Button>
        </div>
      </Modal>
app/components/app/duplicate-modal/style.module.css
New file
@@ -0,0 +1,36 @@
.modal {
  position: relative;
}
.modal .close {
  position: absolute;
  right: 16px;
  top: 25px;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: center no-repeat url(~@/app/components/datasets/create/assets/close.svg);
  background-size: 16px;
  cursor: pointer;
}
.modal .title {
  @apply mb-9;
  font-weight: 600;
  font-size: 20px;
  line-height: 30px;
  color: #101828;
}
.modal .content {
  @apply mb-9;
  font-weight: 400;
  font-size: 14px;
  line-height: 20px;
  color: #101828;
}
.subTitle {
  margin-bottom: 8px;
  font-weight: 500;
}
app/components/app/log-annotation/index.tsx
@@ -30,7 +30,7 @@
      { value: PageType.log, text: t('appLog.title') },
      { value: PageType.annotation, text: t('appAnnotation.title') },
    ]
  }, [appDetail?.mode, t])
  }, [appDetail])
  if (!appDetail) {
    return (
@@ -41,7 +41,7 @@
  }
  return (
    <div className='flex h-full flex-col px-6 pt-3'>
    <div className='pt-3 px-6 h-full flex flex-col'>
      {appDetail.mode !== 'workflow' && (
        <TabSlider
          className='shrink-0'
@@ -52,7 +52,7 @@
          options={options}
        />
      )}
      <div className={cn('h-0 grow', appDetail.mode !== 'workflow' && 'mt-3')}>
      <div className={cn('grow h-0', appDetail.mode !== 'workflow' && 'mt-3')}>
        {pageType === PageType.log && appDetail.mode !== 'workflow' && (<Log appDetail={appDetail} />)}
        {pageType === PageType.annotation && (<Annotation appDetail={appDetail} />)}
        {pageType === PageType.log && appDetail.mode === 'workflow' && (<WorkflowLog appDetail={appDetail} />)}
app/components/app/log/filter.tsx
@@ -40,7 +40,7 @@
  if (!data)
    return null
  return (
    <div className='mb-2 flex flex-row flex-wrap items-center gap-2'>
    <div className='flex flex-row flex-wrap gap-2 items-center mb-2'>
      <Chip
        className='min-w-[150px]'
        panelClassName='w-[270px]'
@@ -80,7 +80,7 @@
      />
      {isChatMode && (
        <>
          <div className='h-3.5 w-px bg-divider-regular'></div>
          <div className='w-px h-3.5 bg-divider-regular'></div>
          <Sort
            order={queryParams.sort_by?.startsWith('-') ? '-' : ''}
            value={queryParams.sort_by?.replace('-', '') || 'created_at'}
app/components/app/log/index.tsx
@@ -37,10 +37,10 @@
  const pathname = usePathname()
  const pathSegments = pathname.split('/')
  pathSegments.pop()
  return <div className='flex h-full items-center justify-center'>
    <div className='box-border h-fit w-[560px] rounded-2xl bg-background-section-burn px-5 py-4'>
      <span className='system-md-semibold text-text-secondary'>{t('appLog.table.empty.element.title')}<ThreeDotsIcon className='relative -left-1.5 -top-3 inline' /></span>
      <div className='system-sm-regular mt-2 text-text-tertiary'>
  return <div className='flex items-center justify-center h-full'>
    <div className='bg-background-section-burn w-[560px] h-fit box-border px-5 py-4 rounded-2xl'>
      <span className='text-text-secondary system-md-semibold'>{t('appLog.table.empty.element.title')}<ThreeDotsIcon className='inline relative -top-3 -left-1.5' /></span>
      <div className='mt-2 text-text-tertiary system-sm-regular'>
        <Trans
          i18nKey="appLog.table.empty.element.content"
          components={{ shareLink: <Link href={`${pathSegments.join('/')}/overview`} className='text-util-colors-blue-blue-600' />, testLink: <Link href={appUrl} className='text-util-colors-blue-blue-600' target='_blank' rel='noopener noreferrer' /> }}
@@ -101,9 +101,9 @@
  const total = isChatMode ? chatConversations?.total : completionConversations?.total
  return (
    <div className='flex h-full grow flex-col'>
      <p className='system-sm-regular shrink-0 text-text-tertiary'>{t('appLog.description')}</p>
      <div className='flex max-h-[calc(100%-16px)] flex-1 grow flex-col py-4'>
    <div className='grow flex flex-col h-full'>
      <p className='shrink-0 text-text-tertiary system-sm-regular'>{t('appLog.description')}</p>
      <div className='grow max-h-[calc(100%-16px)] flex flex-col py-4 flex-1'>
        <Filter isChatMode={isChatMode} appId={appDetail.id} queryParams={queryParams} setQueryParams={setQueryParams} />
        {total === undefined
          ? <Loading type='app' />
app/components/app/log/list.tsx
@@ -41,7 +41,6 @@
import { buildChatItemTree, getThreadMessages } from '@/app/components/base/chat/utils'
import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
import cn from '@/utils/classnames'
import { noop } from 'lodash-es'
dayjs.extend(utc)
dayjs.extend(timezone)
@@ -73,19 +72,16 @@
const HandThumbIconWithCount: FC<{ count: number; iconType: 'up' | 'down' }> = ({ count, iconType }) => {
  const classname = iconType === 'up' ? 'text-primary-600 bg-primary-50' : 'text-red-600 bg-red-50'
  const Icon = iconType === 'up' ? HandThumbUpIcon : HandThumbDownIcon
  return <div className={`inline-flex w-fit items-center rounded-md p-1 text-xs ${classname} mr-1 last:mr-0`}>
    <Icon className={'mr-0.5 h-3 w-3 rounded-md'} />
  return <div className={`inline-flex items-center w-fit rounded-md p-1 text-xs ${classname} mr-1 last:mr-0`}>
    <Icon className={'h-3 w-3 mr-0.5 rounded-md'} />
    {count > 0 ? count : null}
  </div>
}
const statusTdRender = (statusCount: StatusCount) => {
  if (!statusCount)
    return null
  if (statusCount.partial_success + statusCount.failed === 0) {
    return (
      <div className='system-xs-semibold-uppercase inline-flex items-center gap-1'>
      <div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
        <Indicator color={'green'} />
        <span className='text-util-colors-green-green-600'>Success</span>
      </div>
@@ -93,7 +89,7 @@
  }
  else if (statusCount.failed === 0) {
    return (
      <div className='system-xs-semibold-uppercase inline-flex items-center gap-1'>
      <div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
        <Indicator color={'green'} />
        <span className='text-util-colors-green-green-600'>Partial Success</span>
      </div>
@@ -101,7 +97,7 @@
  }
  else {
    return (
      <div className='system-xs-semibold-uppercase inline-flex items-center gap-1'>
      <div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
        <Indicator color={'red'} />
        <span className='text-util-colors-red-red-600'>{statusCount.failed} {`${statusCount.failed > 1 ? 'Failures' : 'Failure'}`}</span>
      </div>
@@ -361,13 +357,13 @@
  }, [])
  return (
    <div ref={ref} className='flex h-full flex-col rounded-xl border-[0.5px] border-components-panel-border'>
    <div ref={ref} className='rounded-xl border-[0.5px] border-components-panel-border h-full flex flex-col'>
      {/* Panel Header */}
      <div className='flex shrink-0 items-center gap-2 rounded-t-xl bg-components-panel-bg pb-2 pl-4 pr-3 pt-3'>
      <div className='shrink-0 pl-4 pt-3 pr-3 pb-2 flex items-center gap-2 bg-components-panel-bg rounded-t-xl'>
        <div className='shrink-0'>
          <div className='system-xs-semibold-uppercase mb-0.5 text-text-primary'>{isChatMode ? t('appLog.detail.conversationId') : t('appLog.detail.time')}</div>
          <div className='mb-0.5 text-text-primary system-xs-semibold-uppercase'>{isChatMode ? t('appLog.detail.conversationId') : t('appLog.detail.time')}</div>
          {isChatMode && (
            <div className='system-2xs-regular-uppercase flex items-center text-text-secondary'>
            <div className='flex items-center text-text-secondary system-2xs-regular-uppercase'>
              <Tooltip
                popupContent={detail.id}
              >
@@ -377,19 +373,19 @@
            </div>
          )}
          {!isChatMode && (
            <div className='system-2xs-regular-uppercase text-text-secondary'>{formatTime(detail.created_at, t('appLog.dateTimeFormat') as string)}</div>
            <div className='text-text-secondary system-2xs-regular-uppercase'>{formatTime(detail.created_at, t('appLog.dateTimeFormat') as string)}</div>
          )}
        </div>
        <div className='flex grow flex-wrap items-center justify-end gap-y-1'>
        <div className='grow flex items-center flex-wrap gap-y-1 justify-end'>
          {!isAdvanced && <ModelInfo model={detail.model_config.model} />}
        </div>
        <ActionButton size='l' onClick={onClose}>
          <RiCloseLine className='h-4 w-4 text-text-tertiary' />
          <RiCloseLine className='w-4 h-4 text-text-tertiary' />
        </ActionButton>
      </div>
      {/* Panel Body */}
      <div className='shrink-0 px-1 pt-1'>
        <div className='rounded-t-xl bg-background-section-burn p-3 pb-2'>
      <div className='shrink-0 pt-1 px-1'>
        <div className='p-3 pb-2 rounded-t-xl bg-background-section-burn'>
          {(varList.length > 0 || (!isChatMode && message_files.length > 0)) && (
            <VarPanel
              varList={varList}
@@ -398,12 +394,12 @@
          )}
        </div>
      </div>
      <div className='mx-1 mb-1 grow overflow-auto rounded-b-xl bg-background-section-burn'>
      <div className='grow mx-1 mb-1 bg-background-section-burn rounded-b-xl overflow-auto'>
        {!isChatMode
          ? <div className="px-6 py-4">
            <div className='flex h-[18px] items-center space-x-3'>
              <div className='system-xs-semibold-uppercase text-text-tertiary'>{t('appLog.table.header.output')}</div>
              <div className='h-[1px] grow' style={{
              <div className='text-text-tertiary system-xs-semibold-uppercase'>{t('appLog.table.header.output')}</div>
              <div className='grow h-[1px]' style={{
                background: 'linear-gradient(270deg, rgba(243, 244, 246, 0) 0%, rgb(243, 244, 246) 100%)',
              }}></div>
            </div>
@@ -412,24 +408,26 @@
              content={detail.message.answer}
              messageId={detail.message.id}
              isError={false}
              onRetry={noop}
              onRetry={() => { }}
              isInstalledApp={false}
              supportFeedback
              feedback={detail.message.feedbacks.find((item: any) => item.from_source === 'admin')}
              onFeedback={feedback => onFeedback(detail.message.id, feedback)}
              supportAnnotation
              isShowTextToSpeech
              appId={appDetail?.id}
              varList={varList}
              siteInfo={null}
            />
          </div>
          : threadChatItems.length < 8
            ? <div className="mb-4 pt-4">
            ? <div className="pt-4 mb-4">
              <Chat
                config={{
                  appId: appDetail?.id,
                  text_to_speech: {
                    enabled: true,
                  },
                  questionEditEnable: false,
                  supportAnnotation: true,
                  annotation_reply: {
                    enabled: true,
@@ -463,7 +461,7 @@
                dataLength={threadChatItems.length}
                next={fetchData}
                hasMore={hasMore}
                loader={<div className='system-xs-regular text-center text-text-tertiary'>{t('appLog.detail.loading')}...</div>}
                loader={<div className='text-center text-text-tertiary system-xs-regular'>{t('appLog.detail.loading')}...</div>}
                // endMessage={<div className='text-center'>Nothing more to show</div>}
                // below props only if you need pull down functionality
                refreshFunction={fetchData}
@@ -485,7 +483,6 @@
                    text_to_speech: {
                      enabled: true,
                    },
                    questionEditEnable: false,
                    supportAnnotation: true,
                    annotation_reply: {
                      enabled: true,
@@ -549,7 +546,7 @@
      notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
      return true
    }
    catch {
    catch (err) {
      notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
      return false
    }
@@ -562,7 +559,7 @@
      notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
      return true
    }
    catch {
    catch (err) {
      notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
      return false
    }
@@ -593,7 +590,7 @@
      notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
      return true
    }
    catch {
    catch (err) {
      notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
      return false
    }
@@ -605,7 +602,7 @@
      notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
      return true
    }
    catch {
    catch (err) {
      notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
      return false
    }
@@ -635,10 +632,9 @@
  const [currentConversation, setCurrentConversation] = useState<ChatConversationGeneralDetail | CompletionConversationGeneralDetail | undefined>() // Currently selected conversation
  const isChatMode = appDetail.mode !== 'completion' // Whether the app is a chat app
  const isChatflow = appDetail.mode === 'advanced-chat' // Whether the app is a chatflow app
  const { setShowPromptLogModal, setShowAgentLogModal, setShowMessageLogModal } = useAppStore(useShallow(state => ({
  const { setShowPromptLogModal, setShowAgentLogModal } = useAppStore(useShallow(state => ({
    setShowPromptLogModal: state.setShowPromptLogModal,
    setShowAgentLogModal: state.setShowAgentLogModal,
    setShowMessageLogModal: state.setShowMessageLogModal,
  })))
  // Annotated data needs to be highlighted
@@ -646,8 +642,8 @@
    return (
      <Tooltip
        popupContent={
          <span className='inline-flex items-center text-xs text-text-tertiary'>
            <RiEditFill className='mr-1 h-3 w-3' />{`${t('appLog.detail.annotationTip', { user: annotation?.account?.name })} ${formatTime(annotation?.created_at || dayjs().unix(), 'MM-DD hh:mm A')}`}
          <span className='text-xs text-text-tertiary inline-flex items-center'>
            <RiEditFill className='w-3 h-3 mr-1' />{`${t('appLog.detail.annotationTip', { user: annotation?.account?.name })} ${formatTime(annotation?.created_at || dayjs().unix(), 'MM-DD hh:mm A')}`}
          </span>
        }
        popupClassName={(isHighlight && !isChatMode) ? '' : '!hidden'}
@@ -665,7 +661,6 @@
    setCurrentConversation(undefined)
    setShowPromptLogModal(false)
    setShowAgentLogModal(false)
    setShowMessageLogModal(false)
  }
  if (!logs)
@@ -676,41 +671,41 @@
      <table className={cn('mt-2 w-full min-w-[440px] border-collapse border-0')}>
        <thead className='system-xs-medium-uppercase text-text-tertiary'>
          <tr>
            <td className='w-5 whitespace-nowrap rounded-l-lg bg-background-section-burn pl-2 pr-1'></td>
            <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{isChatMode ? t('appLog.table.header.summary') : t('appLog.table.header.input')}</td>
            <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.endUser')}</td>
            {isChatflow && <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.status')}</td>}
            <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{isChatMode ? t('appLog.table.header.messageCount') : t('appLog.table.header.output')}</td>
            <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.userRate')}</td>
            <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.adminRate')}</td>
            <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.updatedTime')}</td>
            <td className='whitespace-nowrap rounded-r-lg bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.time')}</td>
            <td className='pl-2 pr-1 w-5 rounded-l-lg bg-background-section-burn whitespace-nowrap'></td>
            <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{isChatMode ? t('appLog.table.header.summary') : t('appLog.table.header.input')}</td>
            <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.endUser')}</td>
            {isChatflow && <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.status')}</td>}
            <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{isChatMode ? t('appLog.table.header.messageCount') : t('appLog.table.header.output')}</td>
            <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.userRate')}</td>
            <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.adminRate')}</td>
            <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.updatedTime')}</td>
            <td className='pl-3 py-1.5 rounded-r-lg bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.time')}</td>
          </tr>
        </thead>
        <tbody className="system-sm-regular text-text-secondary">
        <tbody className="text-text-secondary system-sm-regular">
          {logs.data.map((log: any) => {
            const endUser = log.from_end_user_session_id || log.from_account_name
            const leftValue = get(log, isChatMode ? 'name' : 'message.inputs.query') || (!isChatMode ? (get(log, 'message.query') || get(log, 'message.inputs.default_input')) : '') || ''
            const rightValue = get(log, isChatMode ? 'message_count' : 'message.answer')
            return <tr
              key={log.id}
              className={cn('cursor-pointer border-b border-divider-subtle hover:bg-background-default-hover', currentConversation?.id !== log.id ? '' : 'bg-background-default-hover')}
              className={cn('border-b border-divider-subtle hover:bg-background-default-hover cursor-pointer', currentConversation?.id !== log.id ? '' : 'bg-background-default-hover')}
              onClick={() => {
                setShowDrawer(true)
                setCurrentConversation(log)
              }}>
              <td className='h-4'>
                {!log.read_at && (
                  <div className='flex items-center p-3 pr-0.5'>
                    <span className='inline-block h-1.5 w-1.5 rounded bg-util-colors-blue-blue-500'></span>
                  <div className='p-3 pr-0.5 flex items-center'>
                    <span className='inline-block bg-util-colors-blue-blue-500 h-1.5 w-1.5 rounded'></span>
                  </div>
                )}
              </td>
              <td className='w-[160px] p-3 pr-2' style={{ maxWidth: isChatMode ? 300 : 200 }}>
              <td className='p-3 pr-2 w-[160px]' style={{ maxWidth: isChatMode ? 300 : 200 }}>
                {renderTdValue(leftValue || t('appLog.table.empty.noChat'), !leftValue, isChatMode && log.annotated)}
              </td>
              <td className='p-3 pr-2'>{renderTdValue(endUser || defaultValue, !endUser)}</td>
              {isChatflow && <td className='w-[160px] p-3 pr-2' style={{ maxWidth: isChatMode ? 300 : 200 }}>
              {isChatflow && <td className='p-3 pr-2 w-[160px]' style={{ maxWidth: isChatMode ? 300 : 200 }}>
                {statusTdRender(log.status_count)}
              </td>}
              <td className='p-3 pr-2' style={{ maxWidth: isChatMode ? 100 : 200 }}>
@@ -745,7 +740,7 @@
        onClose={onCloseDrawer}
        mask={isMobile}
        footer={null}
        panelClassName='mt-16 mx-2 sm:mr-2 mb-4 !p-0 !max-w-[640px] rounded-xl bg-components-panel-bg'
        panelClassname='mt-16 mx-2 sm:mr-2 mb-4 !p-0 !max-w-[640px] rounded-xl bg-components-panel-bg'
      >
        <DrawerContext.Provider value={{
          onClose: onCloseDrawer,
app/components/app/log/model-info.tsx
@@ -57,9 +57,9 @@
  return (
    <div className={cn('flex items-center rounded-lg')}>
      <div className='mr-px flex h-8 shrink-0 items-center gap-1 rounded-l-lg bg-components-input-bg-normal pl-1.5 pr-2'>
      <div className='shrink-0 flex items-center gap-1 mr-px h-8 pl-1.5 pr-2 rounded-l-lg bg-components-input-bg-normal'>
        <ModelIcon
          className='!h-5 !w-5'
          className='!w-5 !h-5'
          provider={currentProvider}
          modelName={currentModel?.model}
        />
@@ -80,20 +80,20 @@
            className='block'
          >
            <div className={cn(
              'cursor-pointer rounded-r-lg bg-components-button-tertiary-bg p-2 hover:bg-components-button-tertiary-bg-hover',
              'p-2 rounded-r-lg bg-components-button-tertiary-bg hover:bg-components-button-tertiary-bg-hover cursor-pointer',
              open && 'bg-components-button-tertiary-bg-hover',
            )}>
              <RiInformation2Line className='h-4 w-4 text-text-tertiary' />
            </div>
          </PortalToFollowElemTrigger>
          <PortalToFollowElemContent className='z-[1002]'>
            <div className='relative w-[280px] overflow-hidden rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg px-4 pb-2 pt-3 shadow-xl'>
              <div className='system-sm-semibold-uppercase mb-1 h-6 text-text-secondary'>{t('appLog.detail.modelParams')}</div>
            <div className='relative w-[280px] pt-3 px-4 pb-2 bg-components-panel-bg rounded-2xl border-[0.5px] border-components-panel-border shadow-xl overflow-hidden'>
              <div className='mb-1 h-6 text-text-secondary system-sm-semibold-uppercase'>{t('appLog.detail.modelParams')}</div>
              <div className='py-1'>
                {['temperature', 'top_p', 'presence_penalty', 'max_tokens', 'stop'].map((param: string, index: number) => {
                  return <div className='flex justify-between py-1.5' key={index}>
                    <span className='system-xs-medium-uppercase text-text-tertiary'>{PARAM_MAP[param as keyof typeof PARAM_MAP]}</span>
                    <span className='system-xs-medium-uppercase text-text-secondary'>{getParamValue(param)}</span>
                    <span className='text-text-tertiary system-xs-medium-uppercase'>{PARAM_MAP[param as keyof typeof PARAM_MAP]}</span>
                    <span className='text-text-secondary system-xs-medium-uppercase'>{getParamValue(param)}</span>
                  </div>
                })}
              </div>
app/components/app/log/var-panel.tsx
@@ -27,38 +27,38 @@
  return (
    <div className='rounded-[10px] border border-divider-subtle bg-chat-bubble-bg'>
      <div
        className={cn('flex cursor-pointer items-center gap-1 border-b border-divider-subtle px-3 pb-2 pt-2.5 text-text-secondary', isCollapse && 'border-0 pb-2.5')}
        className={cn('flex items-center gap-1 px-3 pt-2.5 pb-2 border-b border-divider-subtle text-text-secondary cursor-pointer', isCollapse && 'pb-2.5 border-0')}
        onClick={toggleCollapse}
      >
        <Variable02 className='h-4 w-4' />
        <div className='system-md-medium grow'>{t('appLog.detail.variables')}</div>
        <Variable02 className='w-4 h-4' />
        <div className='grow system-md-medium'>{t('appLog.detail.variables')}</div>
        {
          isCollapse
            ? <RiArrowRightSLine className='h-4 w-4' />
            : <RiArrowDownSLine className='h-4 w-4' />
            ? <RiArrowRightSLine className='w-4 h-4' />
            : <RiArrowDownSLine className='w-4 h-4' />
        }
      </div>
      {!isCollapse && (
        <div className='flex max-h-[500px] flex-col gap-2 overflow-y-auto p-3'>
        <div className='p-3 flex flex-col gap-2 max-h-[500px] overflow-y-auto'>
          {varList.map(({ label, value }, index) => (
            <div key={index} className='system-xs-medium flex py-2'>
              <div className='flex w-[128px] shrink-0 text-text-accent'>
            <div key={index} className='flex py-2 system-xs-medium'>
              <div className='shrink-0 w-[128px] flex text-text-accent'>
                <span className='shrink-0 opacity-60'>{'{{'}</span>
                <span className='truncate'>{label}</span>
                <span className='shrink-0 opacity-60'>{'}}'}</span>
              </div>
              <div className='whitespace-pre-wrap pl-2.5 text-text-secondary'>{value}</div>
              <div className='pl-2.5 whitespace-pre-wrap text-text-secondary'>{value}</div>
            </div>
          ))}
          {message_files.length > 0 && (
            <div className='mt-1 flex py-2'>
              <div className='system-xs-medium w-[128px] shrink-0 text-text-tertiary'>{t('appLog.detail.uploadImages')}</div>
              <div className='shrink-0 w-[128px] system-xs-medium text-text-tertiary'>{t('appLog.detail.uploadImages')}</div>
              <div className="flex space-x-2">
                {message_files.map((url, index) => (
                  <div
                    key={index}
                    className="ml-2.5 h-16 w-16 cursor-pointer rounded-lg bg-cover bg-center bg-no-repeat"
                    className="ml-2.5 w-16 h-16 rounded-lg bg-no-repeat bg-cover bg-center cursor-pointer"
                    style={{ backgroundImage: `url(${url})` }}
                    onClick={() => setImagePreviewUrl(url)}
                  />
app/components/app/overview/apikey-info-panel/index.tsx
@@ -27,8 +27,8 @@
    return null
  return (
    <div className={cn('border-components-panel-border bg-components-panel-bg', 'relative mb-6 rounded-2xl border p-8 shadow-md ')}>
      <div className={cn('text-[24px] font-semibold text-text-primary', isCloud ? 'flex h-8 items-center space-x-1' : 'mb-6 leading-8')}>
    <div className={cn('bg-[#EFF4FF] border-[#D1E0FF]', 'mb-6 relative  rounded-2xl shadow-md border  p-8 ')}>
      <div className={cn('text-[24px] text-gray-800 font-semibold', isCloud ? 'flex items-center h-8 space-x-1' : 'leading-8 mb-6')}>
        {isCloud && <em-emoji id={'😀'} />}
        {isCloud
          ? (
@@ -42,30 +42,30 @@
          )}
      </div>
      {isCloud && (
        <div className='mt-1 text-sm font-normal text-text-tertiary'>{t(`appOverview.apiKeyInfo.cloud.${'trial'}.description`)}</div>
        <div className='mt-1 text-sm text-gray-600 font-normal'>{t(`appOverview.apiKeyInfo.cloud.${'trial'}.description`)}</div>
      )}
      <Button
        variant='primary'
        className='mt-2 space-x-2'
        className='space-x-2'
        onClick={() => setShowAccountSettingModal({ payload: 'provider' })}
      >
        <div className='text-sm font-medium'>{t('appOverview.apiKeyInfo.setAPIBtn')}</div>
        <LinkExternal02 className='h-4 w-4' />
        <LinkExternal02 className='w-4 h-4' />
      </Button>
      {!isCloud && (
        <a
          className='mt-2 flex h-[26px] items-center space-x-1  p-1 text-xs font-medium text-[#155EEF]'
          className='mt-2 flex items-center h-[26px] text-xs  font-medium text-[#155EEF] p-1 space-x-1'
          href='https://cloud.dify.ai/apps'
          target='_blank' rel='noopener noreferrer'
        >
          <div>{t('appOverview.apiKeyInfo.tryCloud')}</div>
          <LinkExternal02 className='h-3 w-3' />
          <LinkExternal02 className='w-3 h-3' />
        </a>
      )}
      <div
        onClick={() => setIsShow(false)}
        className='absolute right-4 top-4 flex h-8 w-8 cursor-pointer items-center justify-center '>
        <RiCloseLine className='h-4 w-4 text-text-tertiary' />
        className='absolute right-4 top-4 flex items-center justify-center w-8 h-8 cursor-pointer '>
        <RiCloseLine className='w-4 h-4 text-gray-500' />
      </div>
    </div>
  )
app/components/app/overview/apikey-info-panel/progress/index.tsx
New file
@@ -0,0 +1,29 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import s from './style.module.css'
import cn from '@/utils/classnames'
export type IProgressProps = {
  className?: string
  value: number // percent
}
const Progress: FC<IProgressProps> = ({
  className,
  value,
}) => {
  const exhausted = value === 100
  return (
    <div className={cn(className, 'relative grow h-2 flex bg-gray-200 rounded-md overflow-hidden')}>
      <div
        className={cn(s.bar, exhausted && s['bar-error'], 'absolute top-0 left-0 right-0 bottom-0')}
        style={{ width: `${value}%` }}
      />
      {Array(10).fill(0).map((i, k) => (
        <div key={k} className={s['bar-item']} />
      ))}
    </div>
  )
}
export default React.memo(Progress)
app/components/app/overview/apikey-info-panel/progress/style.module.css
New file
@@ -0,0 +1,16 @@
.bar {
  background: linear-gradient(90deg, rgba(41, 112, 255, 0.9) 0%, rgba(21, 94, 239, 0.9) 100%);
}
.bar-error {
  background: linear-gradient(90deg, rgba(240, 68, 56, 0.72) 0%, rgba(217, 45, 32, 0.9) 100%);
}
.bar-item {
  width: 10%;
  border-right: 1px solid rgba(255, 255, 255, 0.5);
}
.bar-item:last-of-type {
  border-right: 0;
}
app/components/app/overview/appCard.tsx
@@ -1,14 +1,14 @@
'use client'
import type { HTMLProps } from 'react'
import React, { useMemo, useState } from 'react'
import {
  Cog8ToothIcon,
  DocumentTextIcon,
  PaintBrushIcon,
  RocketLaunchIcon,
} from '@heroicons/react/24/outline'
import { usePathname, useRouter } from 'next/navigation'
import { useTranslation } from 'react-i18next'
import {
  RiBookOpenLine,
  RiEqualizer2Line,
  RiExternalLinkLine,
  RiPaintBrushLine,
  RiWindowLine,
} from '@remixicon/react'
import SettingsModal from './settings'
import EmbeddedModal from './embedded'
import CustomizeModal from './customize'
@@ -18,6 +18,7 @@
import AppBasic from '@/app/components/app-sidebar/basic'
import { asyncRunSafe, randomString } from '@/utils'
import Button from '@/app/components/base/button'
import Tag from '@/app/components/base/tag'
import Switch from '@/app/components/base/switch'
import Divider from '@/app/components/base/divider'
import CopyFeedback from '@/app/components/base/copy-feedback'
@@ -27,12 +28,10 @@
import type { AppDetailResponse } from '@/models/app'
import { useAppContext } from '@/context/app-context'
import type { AppSSO } from '@/types/app'
import Indicator from '@/app/components/header/indicator'
export type IAppCardProps = {
  className?: string
  appInfo: AppDetailResponse & Partial<AppSSO>
  isInPanel?: boolean
  cardType?: 'api' | 'webapp'
  customBgColor?: string
  onChangeStatus: (val: boolean) => Promise<void>
@@ -40,9 +39,12 @@
  onGenerateCode?: () => Promise<void>
}
const EmbedIcon = ({ className = '' }: HTMLProps<HTMLDivElement>) => {
  return <div className={`${style.codeBrowserIcon} ${className}`}></div>
}
function AppCard({
  appInfo,
  isInPanel,
  cardType = 'webapp',
  customBgColor,
  onChangeStatus,
@@ -64,18 +66,17 @@
  const OPERATIONS_MAP = useMemo(() => {
    const operationsMap = {
      webapp: [
        { opName: t('appOverview.overview.appInfo.launch'), opIcon: RiExternalLinkLine },
        { opName: t('appOverview.overview.appInfo.preview'), opIcon: RocketLaunchIcon },
        { opName: t('appOverview.overview.appInfo.customize.entry'), opIcon: PaintBrushIcon },
      ] as { opName: string; opIcon: any }[],
      api: [{ opName: t('appOverview.overview.apiInfo.doc'), opIcon: RiBookOpenLine }],
      api: [{ opName: t('appOverview.overview.apiInfo.doc'), opIcon: DocumentTextIcon }],
      app: [],
    }
    if (appInfo.mode !== 'completion' && appInfo.mode !== 'workflow')
      operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.embedded.entry'), opIcon: RiWindowLine })
    operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.customize.entry'), opIcon: RiPaintBrushLine })
      operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.embedded.entry'), opIcon: EmbedIcon })
    if (isCurrentWorkspaceEditor)
      operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.settings.entry'), opIcon: RiEqualizer2Line })
      operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.settings.entry'), opIcon: Cog8ToothIcon })
    return operationsMap
  }, [isCurrentWorkspaceEditor, appInfo, t])
@@ -91,9 +92,13 @@
  const appUrl = `${app_base_url}/${appMode}/${access_token}`
  const apiUrl = appInfo?.api_base_url
  let bgColor = 'bg-primary-50 bg-opacity-40'
  if (cardType === 'api')
    bgColor = 'bg-purple-50'
  const genClickFuncByName = (opName: string) => {
    switch (opName) {
      case t('appOverview.overview.appInfo.launch'):
      case t('appOverview.overview.appInfo.preview'):
        return () => {
          window.open(appUrl, '_blank')
        }
@@ -130,11 +135,10 @@
  return (
    <div
      className={
        `${isInPanel ? 'border-l-[0.5px] border-t' : 'border-[0.5px] shadow-xs'} w-full max-w-full rounded-xl border-effects-highlight ${className ?? ''}`}
        `shadow-xs border-[0.5px] rounded-lg border-gray-200 ${className ?? ''}`}
    >
      <div className={`${customBgColor ?? 'bg-background-default'} rounded-xl`}>
        <div className='flex w-full flex-col items-start justify-center gap-3 self-stretch border-b-[0.5px] border-divider-subtle p-3'>
          <div className='flex w-full items-center gap-3 self-stretch'>
      <div className={`px-6 py-5 ${customBgColor ?? bgColor} rounded-lg`}>
        <div className="mb-2.5 flex flex-row items-start justify-between">
            <AppBasic
              iconType={cardType}
              icon={appInfo.icon}
@@ -146,34 +150,34 @@
                  : t('appOverview.overview.apiInfo.explanation')
              }
            />
            <div className='flex items-center gap-1'>
              <Indicator color={runningStatus ? 'green' : 'yellow'} />
              <div className={`${runningStatus ? 'text-text-success' : 'text-text-warning'} system-xs-semibold-uppercase`}>
          <div className="flex flex-row items-center h-9">
            <Tag className="mr-2" color={runningStatus ? 'green' : 'yellow'}>
                {runningStatus
                  ? t('appOverview.overview.status.running')
                  : t('appOverview.overview.status.disable')}
              </div>
            </div>
            </Tag>
            <Switch defaultValue={runningStatus} onChange={onChangeStatus} disabled={toggleDisabled} />
          </div>
          <div className='flex flex-col items-start justify-center self-stretch'>
            <div className="system-xs-medium pb-1 text-text-tertiary">
        </div>
        <div className="flex flex-col justify-center py-2">
          <div className="py-1">
            <div className="pb-1 text-xs text-gray-500">
              {isApp
                ? t('appOverview.overview.appInfo.accessibleAddress')
                : t('appOverview.overview.apiInfo.accessibleAddress')}
            </div>
            <div className="inline-flex h-9 w-full items-center gap-0.5 rounded-lg bg-components-input-bg-normal p-1 pl-2">
              <div className="flex h-4 min-w-0 flex-1 items-start justify-start gap-2 px-1">
                <div className="overflow-hidden text-ellipsis whitespace-nowrap text-xs font-medium text-text-secondary">
            <div className="w-full h-9 pl-2 pr-0.5 py-0.5 bg-black bg-opacity-2 rounded-lg border border-black border-opacity-5 justify-start items-center inline-flex">
              <div className="h-4 px-2 justify-start items-start gap-2 flex flex-1 min-w-0">
                <div className="text-gray-700 text-xs font-medium text-ellipsis overflow-hidden whitespace-nowrap">
                  {isApp ? appUrl : apiUrl}
                </div>
              </div>
              <Divider type="vertical" className="!h-3.5 shrink-0 !mx-0.5" />
              {isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} selectorId={randomString(8)} className={'hover:bg-gray-200'} />}
              <CopyFeedback
                content={isApp ? appUrl : apiUrl}
                className={'!size-6'}
                className={'hover:bg-gray-200'}
              />
              {isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} className='z-50 !size-6 rounded-md hover:bg-state-base-hover' selectorId={randomString(8)} />}
              {isApp && <Divider type="vertical" className="!mx-0.5 !h-3.5 shrink-0" />}
              {/* button copy link/ button regenerate */}
              {showConfirmDelete && (
                <Confirm
@@ -193,12 +197,12 @@
                  popupContent={t('appOverview.overview.appInfo.regenerate') || ''}
                >
                  <div
                    className="h-6 w-6 cursor-pointer rounded-md hover:bg-state-base-hover"
                    className="w-8 h-8 ml-0.5 cursor-pointer hover:bg-gray-200 rounded-lg"
                    onClick={() => setShowConfirmDelete(true)}
                  >
                    <div
                      className={
                        `h-full w-full ${style.refreshIcon} ${genLoading ? style.generateLogo : ''}`}
                        `w-full h-full ${style.refreshIcon} ${genLoading ? style.generateLogo : ''}`}
                    ></div>
                  </div>
                </Tooltip>
@@ -206,8 +210,8 @@
            </div>
          </div>
        </div>
        <div className={'flex items-center gap-1 self-stretch p-3'}>
          {!isApp && <SecretKeyButton appId={appInfo.id} />}
        <div className={'pt-2 flex flex-row items-center flex-wrap gap-y-2'}>
          {!isApp && <SecretKeyButton className='flex-shrink-0 !h-8 bg-white mr-2' textCls='!text-gray-700 font-medium' iconCls='stroke-[1.2px]' appId={appInfo.id} />}
          {OPERATIONS_MAP[cardType].map((op) => {
            const disabled
              = op.opName === t('appOverview.overview.appInfo.settings.entry')
@@ -215,9 +219,7 @@
                : !runningStatus
            return (
              <Button
                className="mr-1 min-w-[88px]"
                size="small"
                variant={'ghost'}
                className="mr-2"
                key={op.opName}
                onClick={genClickFuncByName(op.opName)}
                disabled={disabled}
@@ -228,9 +230,9 @@
                  }
                  popupClassName={disabled ? 'mt-[-8px]' : '!hidden'}
                >
                  <div className="flex items-center justify-center gap-[1px]">
                    <op.opIcon className="h-3.5 w-3.5" />
                    <div className={`${runningStatus ? 'text-text-tertiary' : 'text-components-button-ghost-text-disabled'} system-xs-medium px-[3px]`}>{op.opName}</div>
                  <div className="flex flex-row items-center">
                    <op.opIcon className="h-4 w-4 mr-1.5 stroke-[1.8px]" />
                    <span className="text-[13px]">{op.opName}</span>
                  </div>
                </Tooltip>
              </Button>
app/components/app/overview/appChart.tsx
@@ -230,22 +230,22 @@
  const sumData = isAvg ? (sum(yData) / yData.length) : sum(yData)
  return (
    <div className={`flex w-full flex-col rounded-xl bg-components-chart-bg px-6 py-4 shadow-xs ${className ?? ''}`}>
    <div className={`flex flex-col w-full px-6 py-4 border-[0.5px] rounded-lg border-gray-200 shadow-xs ${className ?? ''}`}>
      <div className='mb-3'>
        <Basic name={title} type={timePeriod} hoverTip={explanation} />
      </div>
      <div className='mb-4 flex-1'>
        <Basic
          isExtraInLine={CHART_TYPE_CONFIG[chartType].showTokens}
          name={chartType !== 'costs' ? (`${sumData.toLocaleString()} ${unit}`) : `${sumData < 1000 ? sumData : (`${formatNumber(Math.round(sumData / 1000))}k`)}`}
          name={chartType !== 'costs' ? (sumData.toLocaleString() + unit) : `${sumData < 1000 ? sumData : (`${formatNumber(Math.round(sumData / 1000))}k`)}`}
          type={!CHART_TYPE_CONFIG[chartType].showTokens
            ? ''
            : <span>{t('appOverview.analysis.tokenUsage.consumed')} Tokens<span className='text-sm'>
              <span className='ml-1 text-text-tertiary'>(</span>
              <span className='text-orange-400'>~{sum(statistics.map(item => Number.parseFloat(get(item, 'total_price', '0')))).toLocaleString('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 4 })}</span>
              <span className='text-text-tertiary'>)</span>
              <span className='ml-1 text-gray-500'>(</span>
              <span className='text-orange-400'>~{sum(statistics.map(item => parseFloat(get(item, 'total_price', '0')))).toLocaleString('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 4 })}</span>
              <span className='text-gray-500'>)</span>
            </span></span>}
          textStyle={{ main: `!text-3xl !font-normal ${sumData === 0 ? '!text-text-quaternary' : ''}` }} />
          textStyle={{ main: `!text-3xl !font-normal ${sumData === 0 ? '!text-gray-300' : ''}` }} />
      </div>
      <ReactECharts option={options} style={{ height: 160 }} />
    </div>
@@ -350,7 +350,6 @@
    isAvg
    unit={t('appOverview.analysis.tokenPS') as string}
    {...(noDataFlag && { yMax: 100 })}
    className="min-w-0"
  />
}
app/components/app/overview/customize/index.tsx
@@ -21,7 +21,7 @@
}
const StepNum: FC<{ children: React.ReactNode }> = ({ children }) =>
  <div className='mr-3 flex h-7 w-7 shrink-0 items-center justify-center rounded-2xl bg-util-colors-blue-blue-50 text-text-accent'>
  <div className='h-7 w-7 flex justify-center items-center flex-shrink-0 mr-3 text-primary-600 bg-primary-50 rounded-2xl'>
    {children}
  </div>
@@ -51,30 +51,30 @@
    description={t(`${prefixCustomize}.explanation`)}
    isShow={isShow}
    onClose={onClose}
    className='w-[640px] !max-w-2xl'
    className='!max-w-2xl w-[640px]'
    closable={true}
  >
    <div className='mt-4 w-full rounded-lg border-[0.5px] border-components-panel-border px-6 py-5'>
      <Tag bordered={true} hideBg={true} className='border-text-accent-secondary uppercase text-text-accent-secondary'>{t(`${prefixCustomize}.way`)} 1</Tag>
      <p className='system-sm-medium my-2 text-text-secondary'>{t(`${prefixCustomize}.way1.name`)}</p>
    <div className='w-full mt-4 px-6 py-5 border-gray-200 rounded-lg border-[0.5px]'>
      <Tag bordered={true} hideBg={true} className='text-primary-600 border-primary-600 uppercase'>{t(`${prefixCustomize}.way`)} 1</Tag>
      <p className='my-2 text-base font-medium text-gray-800'>{t(`${prefixCustomize}.way1.name`)}</p>
      <div className='flex py-4'>
        <StepNum>1</StepNum>
        <div className='flex flex-col'>
          <div className='text-text-primary'>{t(`${prefixCustomize}.way1.step1`)}</div>
          <div className='mb-2 mt-1 text-xs text-text-tertiary'>{t(`${prefixCustomize}.way1.step1Tip`)}</div>
          <div className='text-gray-900'>{t(`${prefixCustomize}.way1.step1`)}</div>
          <div className='text-gray-500 text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step1Tip`)}</div>
          <a href={`https://github.com/langgenius/${isChatApp ? 'webapp-conversation' : 'webapp-text-generator'}`} target='_blank' rel='noopener noreferrer'>
            <Button><GithubIcon className='mr-2 text-text-secondary' />{t(`${prefixCustomize}.way1.step1Operation`)}</Button>
            <Button><GithubIcon className='text-gray-800 mr-2' />{t(`${prefixCustomize}.way1.step1Operation`)}</Button>
          </a>
        </div>
      </div>
      <div className='flex pt-4'>
        <StepNum>2</StepNum>
        <div className='flex flex-col'>
          <div className='text-text-primary'>{t(`${prefixCustomize}.way1.step3`)}</div>
          <div className='mb-2 mt-1 text-xs text-text-tertiary'>{t(`${prefixCustomize}.way1.step2Tip`)}</div>
          <div className='text-gray-900'>{t(`${prefixCustomize}.way1.step3`)}</div>
          <div className='text-gray-500 text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step2Tip`)}</div>
          <a href="https://vercel.com/docs/concepts/deployments/git/vercel-for-github" target='_blank' rel='noopener noreferrer'>
            <Button>
              <div className='mr-1.5 border-b-[12px] border-l-[7px] border-r-[7px] border-t-0 border-solid border-text-primary border-l-transparent border-r-transparent border-t-transparent'></div>
              <div className='mr-1.5 border-solid border-t-0 border-r-[7px] border-l-[7px] border-b-[12px] border-r-transparent border-b-black border-l-transparent border-t-transparent'></div>
              <span>{t(`${prefixCustomize}.way1.step2Operation`)}</span>
            </Button>
          </a>
@@ -82,10 +82,10 @@
      </div>
      <div className='flex py-4'>
        <StepNum>3</StepNum>
        <div className='flex w-full flex-col overflow-hidden'>
          <div className='text-text-primary'>{t(`${prefixCustomize}.way1.step3`)}</div>
          <div className='mb-2 mt-1 text-xs text-text-tertiary'>{t(`${prefixCustomize}.way1.step3Tip`)}</div>
          <pre className='box-border select-text overflow-x-scroll rounded-lg border-[0.5px] border-components-panel-border bg-background-section px-4 py-3 text-xs font-medium text-text-secondary'>
        <div className='flex flex-col w-full overflow-hidden'>
          <div className='text-gray-900'>{t(`${prefixCustomize}.way1.step3`)}</div>
          <div className='text-gray-500 text-xs mt-1 mb-2'>{t(`${prefixCustomize}.way1.step3Tip`)}</div>
          <pre className='overflow-x-scroll box-border py-3 px-4 bg-gray-100 text-xs font-medium rounded-lg select-text'>
            NEXT_PUBLIC_APP_ID={`'${appId}'`} <br />
            NEXT_PUBLIC_APP_KEY={'\'<Web API Key From Dify>\''} <br />
            NEXT_PUBLIC_API_URL={`'${api_base_url}'`}
@@ -94,23 +94,23 @@
      </div>
    </div>
    <div className='mt-4 w-full rounded-lg border-[0.5px] border-components-panel-border px-6 py-5'>
      <Tag bordered={true} hideBg={true} className='border-text-accent-secondary uppercase text-text-accent-secondary'>{t(`${prefixCustomize}.way`)} 2</Tag>
      <p className='system-sm-medium my-2 text-text-secondary'>{t(`${prefixCustomize}.way2.name`)}</p>
    <div className='w-full mt-4 px-6 py-5 border-gray-200 rounded-lg border-[0.5px]'>
      <Tag bordered={true} hideBg={true} className='text-primary-600 border-primary-600 uppercase'>{t(`${prefixCustomize}.way`)} 2</Tag>
      <p className='mt-2 text-base font-medium text-gray-800'>{t(`${prefixCustomize}.way2.name`)}</p>
      <Button
        className='mt-2'
        onClick={() =>
          window.open(
            `https://docs.dify.ai/${locale !== LanguagesSupported[1]
              ? 'user-guide/launching-dify-apps/developing-with-apis'
              : `${locale.toLowerCase()}/guides/application-publishing/developing-with-apis`
              : `v/${locale.toLowerCase()}/guides/application-publishing/developing-with-apis`
            }`,
            '_blank',
          )
        }
      >
        <span className='text-sm text-text-secondary'>{t(`${prefixCustomize}.way2.operation`)}</span>
        <ArrowTopRightOnSquareIcon className='ml-1 h-4 w-4 shrink-0 text-text-secondary' />
        <span className='text-sm text-gray-800'>{t(`${prefixCustomize}.way2.operation`)}</span>
        <ArrowTopRightOnSquareIcon className='w-4 h-4 ml-1 text-gray-800 shrink-0' />
      </Button>
    </div>
  </Modal>
app/components/app/overview/embedded/index.tsx
@@ -1,19 +1,15 @@
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
  RiClipboardFill,
  RiClipboardLine,
} from '@remixicon/react'
import copy from 'copy-to-clipboard'
import style from './style.module.css'
import cn from '@/utils/classnames'
import Modal from '@/app/components/base/modal'
import copyStyle from '@/app/components/base/copy-btn/style.module.css'
import Tooltip from '@/app/components/base/tooltip'
import { useAppContext } from '@/context/app-context'
import { IS_CE_EDITION } from '@/config'
import type { SiteInfo } from '@/models/share'
import { useThemeContext } from '@/app/components/base/chat/embedded-chatbot/theme/theme-context'
import ActionButton from '@/app/components/base/action-button'
import cn from '@/utils/classnames'
type Props = {
  siteInfo?: SiteInfo
@@ -44,11 +40,7 @@
        : ''}${IS_CE_EDITION
          ? `,
  baseUrl: '${url}'`
          : ''},
  systemVariables: {
    // user_id: 'YOU CAN DEFINE USER ID HERE',
    // conversation_id: 'YOU CAN DEFINE CONVERSATION ID HERE, IT MUST BE A VALID UUID',
  },
  : ''}
 }
</script>
<script
@@ -111,7 +103,7 @@
  }
  const navigateToChromeUrl = () => {
    window.open('https://chrome.google.com/webstore/detail/dify-chatbot/ceehdapohffmjmkdcifjofadiaoeggaf', '_blank', 'noopener,noreferrer')
    window.open('https://chrome.google.com/webstore/detail/dify-chatbot/ceehdapohffmjmkdcifjofadiaoeggaf', '_blank')
  }
  useEffect(() => {
@@ -123,11 +115,11 @@
      title={t(`${prefixEmbedded}.title`)}
      isShow={isShow}
      onClose={onClose}
      className="w-[640px] !max-w-2xl"
      className="!max-w-2xl w-[640px]"
      wrapperClassName={className}
      closable={true}
    >
      <div className="system-sm-medium mb-4 mt-8 text-text-primary">
      <div className="mb-4 mt-8 text-gray-900 text-[14px] font-medium leading-tight">
        {t(`${prefixEmbedded}.explanation`)}
      </div>
      <div className="flex flex-wrap items-center justify-between gap-y-2">
@@ -149,39 +141,32 @@
        })}
      </div>
      {option === 'chromePlugin' && (
        <div className="mt-6 w-full">
          <div className={cn('inline-flex w-full items-center justify-center gap-2 rounded-lg py-3',
            'shrink-0 cursor-pointer bg-primary-600 text-white hover:bg-primary-600/75 hover:shadow-sm')}>
            <div className={`relative h-4 w-4 ${style.pluginInstallIcon}`}></div>
            <div className="font-['Inter'] text-sm font-medium leading-tight text-white" onClick={navigateToChromeUrl}>{t(`${prefixEmbedded}.chromePlugin`)}</div>
        <div className="w-full mt-6">
          <div className={cn('gap-2 py-3 justify-center items-center inline-flex w-full rounded-lg',
            'bg-primary-600 hover:bg-primary-600/75 hover:shadow-md cursor-pointer text-white hover:shadow-sm flex-shrink-0')}>
            <div className={`w-4 h-4 relative ${style.pluginInstallIcon}`}></div>
            <div className="text-white text-sm font-medium font-['Inter'] leading-tight" onClick={navigateToChromeUrl}>{t(`${prefixEmbedded}.chromePlugin`)}</div>
          </div>
        </div>
      )}
      <div className={cn('inline-flex w-full flex-col items-start justify-start rounded-lg border-[0.5px] border-components-panel-border bg-background-section',
      <div className={cn('w-full bg-gray-100 rounded-lg flex-col justify-start items-start inline-flex',
        'mt-6')}>
        <div className="inline-flex items-center justify-start gap-2 self-stretch rounded-t-lg bg-background-section-burn py-1  pl-3 pr-1">
          <div className="system-sm-medium shrink-0 grow text-text-secondary">
        <div className="inline-flex items-center self-stretch justify-start gap-2 py-1 pl-3 pr-1 border border-black rounded-tl-lg rounded-tr-lg bg-gray-50 border-opacity-5">
          <div className="grow shrink basis-0 text-slate-700 text-[13px] font-medium leading-none">
            {t(`${prefixEmbedded}.${option}`)}
          </div>
          <div className="flex items-center justify-center gap-1 p-2 rounded-lg">
          <Tooltip
            popupContent={
              (isCopied[option]
                ? t(`${prefixEmbedded}.copied`)
                : t(`${prefixEmbedded}.copy`)) || ''
            }
              popupContent={(isCopied[option] ? t(`${prefixEmbedded}.copied`) : t(`${prefixEmbedded}.copy`)) || ''}
          >
            <ActionButton>
              <div
                onClick={onClickCopy}
              >
                {isCopied[option] && <RiClipboardFill className='h-4 w-4' />}
                {!isCopied[option] && <RiClipboardLine className='h-4 w-4' />}
              <div className="w-8 h-8 rounded-lg cursor-pointer hover:bg-gray-100">
                <div onClick={onClickCopy} className={`w-full h-full ${copyStyle.copyIcon} ${isCopied[option] ? copyStyle.copied : ''}`}></div>
              </div>
            </ActionButton>
          </Tooltip>
        </div>
        <div className="flex w-full items-start justify-start gap-2 overflow-x-auto p-3">
          <div className="shrink grow basis-0 font-mono text-[13px] leading-tight text-text-secondary">
        </div>
        <div className="flex items-start justify-start w-full gap-2 p-3 overflow-x-auto">
          <div className="grow shrink basis-0 text-slate-700 text-[13px] leading-tight font-mono">
            <pre className='select-text'>{OPTION_MAP[option].getContent(appBaseUrl, accessToken, themeBuilder.theme?.primaryColor ?? '#1C64F2', isTestEnv)}</pre>
          </div>
        </div>
app/components/app/overview/embedded/style.module.css
app/components/app/overview/settings/index.tsx
@@ -162,20 +162,9 @@
      return check
    }
    const validatePrivacyPolicy = (privacyPolicy: string | null) => {
      if (privacyPolicy === null || privacyPolicy?.length === 0)
        return true
      return privacyPolicy.startsWith('http://') || privacyPolicy.startsWith('https://')
    }
    if (inputInfo !== null) {
      if (!validateColorHex(inputInfo.chatColorTheme)) {
        notify({ type: 'error', message: t(`${prefixSettings}.invalidHexMessage`) })
        return
      }
      if (!validatePrivacyPolicy(inputInfo.privacyPolicy)) {
        notify({ type: 'error', message: t(`${prefixSettings}.invalidPrivacyPolicy`) })
        return
      }
    }
@@ -232,24 +221,24 @@
        className='max-w-[520px] p-0'
      >
        {/* header */}
        <div className='pb-3 pl-6 pr-5 pt-5'>
        <div className='pl-6 pt-5 pr-5 pb-3'>
          <div className='flex items-center gap-1'>
            <div className='title-2xl-semi-bold grow text-text-primary'>{t(`${prefixSettings}.title`)}</div>
            <div className='grow text-text-primary title-2xl-semi-bold'>{t(`${prefixSettings}.title`)}</div>
            <ActionButton className='shrink-0' onClick={onHide}>
              <RiCloseLine className='h-4 w-4' />
              <RiCloseLine className='w-4 h-4' />
            </ActionButton>
          </div>
          <div className='system-xs-regular mt-0.5 text-text-tertiary'>
          <div className='mt-0.5 text-text-tertiary system-xs-regular'>
            <span>{t(`${prefixSettings}.modalTip`)}</span>
            <Link href={`${locale === LanguagesSupported[1] ? 'https://docs.dify.ai/zh-hans/guides/application-publishing/launch-your-webapp-quickly#she-zhi-ni-de-ai-zhan-dian' : 'https://docs.dify.ai/en/guides/application-publishing/launch-your-webapp-quickly/README'}`} target='_blank' rel='noopener noreferrer' className='text-text-accent'>{t('common.operation.learnMore')}</Link>
            <Link href={`${locale === LanguagesSupported[1] ? 'https://docs.dify.ai/zh-hans/guides/application-publishing/launch-your-webapp-quickly#she-zhi-ni-de-ai-zhan-dian' : 'https://docs.dify.ai/guides/application-publishing/launch-your-webapp-quickly#setting-up-your-ai-site'}`} target='_blank' rel='noopener noreferrer' className='text-text-accent'>{t('common.operation.learnMore')}</Link>
          </div>
        </div>
        {/* form body */}
        <div className='space-y-5 px-6 py-3'>
        <div className='px-6 py-3 space-y-5'>
          {/* name & icon */}
          <div className='flex gap-4'>
            <div className='grow'>
              <div className={cn('system-sm-semibold mb-1 py-1 text-text-secondary')}>{t(`${prefixSettings}.webName`)}</div>
              <div className={cn('mb-1 py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.webName`)}</div>
              <Input
                className='w-full'
                value={inputInfo.title}
@@ -269,46 +258,45 @@
          </div>
          {/* description */}
          <div className='relative'>
            <div className={cn('system-sm-semibold py-1 text-text-secondary')}>{t(`${prefixSettings}.webDesc`)}</div>
            <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.webDesc`)}</div>
            <Textarea
              className='mt-1'
              value={inputInfo.desc}
              onChange={e => onDesChange(e.target.value)}
              placeholder={t(`${prefixSettings}.webDescPlaceholder`) as string}
            />
            <p className={cn('body-xs-regular pb-0.5 text-text-tertiary')}>{t(`${prefixSettings}.webDescTip`)}</p>
            <p className={cn('pb-0.5 text-text-tertiary body-xs-regular')}>{t(`${prefixSettings}.webDescTip`)}</p>
          </div>
          <Divider className="my-0 h-px" />
          <Divider className="h-px my-0" />
          {/* answer icon */}
          {isChat && (
            <div className='w-full'>
              <div className='flex items-center justify-between'>
                <div className={cn('system-sm-semibold py-1 text-text-secondary')}>{t('app.answerIcon.title')}</div>
              <div className='flex justify-between items-center'>
                <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t('app.answerIcon.title')}</div>
                <Switch
                  defaultValue={inputInfo.use_icon_as_answer_icon}
                  onChange={v => setInputInfo({ ...inputInfo, use_icon_as_answer_icon: v })}
                />
              </div>
              <p className='body-xs-regular pb-0.5 text-text-tertiary'>{t('app.answerIcon.description')}</p>
              <p className='pb-0.5 text-text-tertiary body-xs-regular'>{t('app.answerIcon.description')}</p>
            </div>
          )}
          {/* language */}
          <div className='flex items-center'>
            <div className={cn('system-sm-semibold grow py-1 text-text-secondary')}>{t(`${prefixSettings}.language`)}</div>
            <div className={cn('grow py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.language`)}</div>
            <SimpleSelect
              wrapperClassName='w-[200px]'
              items={languages.filter(item => item.supported)}
              defaultValue={language}
              onSelect={item => setLanguage(item.value as Language)}
              notClearable
            />
          </div>
          {/* theme color */}
          {isChat && (
            <div className='flex items-center'>
              <div className='grow'>
                <div className={cn('system-sm-semibold py-1 text-text-secondary')}>{t(`${prefixSettings}.chatColorTheme`)}</div>
                <div className='body-xs-regular pb-0.5 text-text-tertiary'>{t(`${prefixSettings}.chatColorThemeDesc`)}</div>
                <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.chatColorTheme`)}</div>
                <div className='pb-0.5 body-xs-regular text-text-tertiary'>{t(`${prefixSettings}.chatColorThemeDesc`)}</div>
              </div>
              <div className='shrink-0'>
                <Input
@@ -317,7 +305,7 @@
                  onChange={onChange('chatColorTheme')}
                  placeholder='E.g #A020F0'
                />
                <div className='flex items-center justify-between'>
                <div className='flex justify-between items-center'>
                  <p className={cn('body-xs-regular text-text-tertiary')}>{t(`${prefixSettings}.chatColorThemeInverted`)}</p>
                  <Switch defaultValue={inputInfo.chatColorThemeInverted} onChange={v => setInputInfo({ ...inputInfo, chatColorThemeInverted: v })}></Switch>
                </div>
@@ -326,24 +314,24 @@
          )}
          {/* workflow detail */}
          <div className='w-full'>
            <div className='flex items-center justify-between'>
              <div className={cn('system-sm-semibold py-1 text-text-secondary')}>{t(`${prefixSettings}.workflow.subTitle`)}</div>
            <div className='flex justify-between items-center'>
              <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.workflow.subTitle`)}</div>
              <Switch
                disabled={!(appInfo.mode === 'workflow' || appInfo.mode === 'advanced-chat')}
                defaultValue={inputInfo.show_workflow_steps}
                onChange={v => setInputInfo({ ...inputInfo, show_workflow_steps: v })}
              />
            </div>
            <p className='body-xs-regular pb-0.5 text-text-tertiary'>{t(`${prefixSettings}.workflow.showDesc`)}</p>
            <p className='pb-0.5 text-text-tertiary body-xs-regular'>{t(`${prefixSettings}.workflow.showDesc`)}</p>
          </div>
          {/* SSO */}
          {systemFeatures.enable_web_sso_switch_component && (
            <>
              <Divider className="my-0 h-px" />
              <Divider className="h-px my-0" />
              <div className='w-full'>
                <p className='system-xs-medium-uppercase mb-1 text-text-tertiary'>{t(`${prefixSettings}.sso.label`)}</p>
                <div className='flex items-center justify-between'>
                  <div className={cn('system-sm-semibold py-1 text-text-secondary')}>{t(`${prefixSettings}.sso.title`)}</div>
                <p className='mb-1 system-xs-medium-uppercase text-text-tertiary'>{t(`${prefixSettings}.sso.label`)}</p>
                <div className='flex justify-between items-center'>
                  <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.sso.title`)}</div>
                  <Tooltip
                    disabled={systemFeatures.sso_enforced_for_web}
                    popupContent={
@@ -354,19 +342,19 @@
                    <Switch disabled={!systemFeatures.sso_enforced_for_web || !isCurrentWorkspaceEditor} defaultValue={systemFeatures.sso_enforced_for_web && inputInfo.enable_sso} onChange={v => setInputInfo({ ...inputInfo, enable_sso: v })}></Switch>
                  </Tooltip>
                </div>
                <p className='body-xs-regular pb-0.5 text-text-tertiary'>{t(`${prefixSettings}.sso.description`)}</p>
                <p className='pb-0.5 body-xs-regular text-text-tertiary'>{t(`${prefixSettings}.sso.description`)}</p>
              </div>
            </>
          )}
          {/* more settings switch */}
          <Divider className="my-0 h-px" />
          <Divider className="h-px my-0" />
          {!isShowMore && (
            <div className='flex cursor-pointer items-center' onClick={() => setIsShowMore(true)}>
            <div className='flex items-center cursor-pointer' onClick={() => setIsShowMore(true)}>
              <div className='grow'>
                <div className={cn('system-sm-semibold py-1 text-text-secondary')}>{t(`${prefixSettings}.more.entry`)}</div>
                <p className={cn('body-xs-regular pb-0.5 text-text-tertiary')}>{t(`${prefixSettings}.more.copyRightPlaceholder`)} & {t(`${prefixSettings}.more.privacyPolicyPlaceholder`)}</p>
                <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.more.entry`)}</div>
                <p className={cn('pb-0.5 text-text-tertiary body-xs-regular')}>{t(`${prefixSettings}.more.copyRightPlaceholder`)} & {t(`${prefixSettings}.more.privacyPolicyPlaceholder`)}</p>
              </div>
              <RiArrowRightSLine className='ml-1 h-4 w-4 shrink-0 text-text-secondary' />
              <RiArrowRightSLine className='shrink-0 ml-1 w-4 h-4 text-text-secondary'/>
            </div>
          )}
          {/* more settings */}
@@ -375,13 +363,13 @@
              {/* copyright */}
              <div className='w-full'>
                <div className='flex items-center'>
                  <div className='flex grow items-center'>
                    <div className={cn('system-sm-semibold mr-1 py-1 text-text-secondary')}>{t(`${prefixSettings}.more.copyright`)}</div>
                  <div className='grow flex items-center'>
                    <div className={cn('mr-1 py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.more.copyright`)}</div>
                    {/* upgrade button */}
                    {enableBilling && isFreePlan && (
                      <div className='h-[18px] select-none'>
                      <div className='select-none h-[18px]'>
                        <PremiumBadge size='s' color='blue' allowHover={true} onClick={handlePlanClick}>
                          <SparklesSoft className='flex h-3.5 w-3.5 items-center py-[1px] pl-[3px] text-components-premium-badge-indigo-text-stop-0' />
                          <SparklesSoft className='flex items-center py-[1px] pl-[3px] w-3.5 h-3.5 text-components-premium-badge-indigo-text-stop-0' />
                          <div className='system-xs-medium'>
                            <span className='p-1'>
                              {t('billing.upgradeBtn.encourageShort')}
@@ -405,7 +393,7 @@
                    />
                  </Tooltip>
                </div>
                <p className='body-xs-regular pb-0.5 text-text-tertiary'>{t(`${prefixSettings}.more.copyrightTip`)}</p>
                <p className='pb-0.5 text-text-tertiary body-xs-regular'>{t(`${prefixSettings}.more.copyrightTip`)}</p>
                {inputInfo.copyrightSwitchValue && (
                  <Input
                    className='mt-2 h-10'
@@ -417,11 +405,11 @@
              </div>
              {/* privacy policy */}
              <div className='w-full'>
                <div className={cn('system-sm-semibold py-1 text-text-secondary')}>{t(`${prefixSettings}.more.privacyPolicy`)}</div>
                <p className={cn('body-xs-regular pb-0.5 text-text-tertiary')}>
                <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.more.privacyPolicy`)}</div>
                <p className={cn('pb-0.5 body-xs-regular text-text-tertiary')}>
                  <Trans
                    i18nKey={`${prefixSettings}.more.privacyPolicyTip`}
                    components={{ privacyPolicyLink: <Link href={'https://dify.ai/privacy'} target='_blank' rel='noopener noreferrer' className='text-text-accent' /> }}
                    components={{ privacyPolicyLink: <Link href={'https://docs.dify.ai/user-agreement/privacy-policy'} target='_blank' rel='noopener noreferrer' className='text-text-accent' /> }}
                  />
                </p>
                <Input
@@ -433,8 +421,8 @@
              </div>
              {/* custom disclaimer */}
              <div className='w-full'>
                <div className={cn('system-sm-semibold py-1 text-text-secondary')}>{t(`${prefixSettings}.more.customDisclaimer`)}</div>
                <p className={cn('body-xs-regular pb-0.5 text-text-tertiary')}>{t(`${prefixSettings}.more.customDisclaimerTip`)}</p>
                <div className={cn('py-1 text-text-secondary system-sm-semibold')}>{t(`${prefixSettings}.more.customDisclaimer`)}</div>
                <p className={cn('pb-0.5 body-xs-regular text-text-tertiary')}>{t(`${prefixSettings}.more.customDisclaimerTip`)}</p>
                <Textarea
                  className='mt-1'
                  value={inputInfo.customDisclaimer}
@@ -446,13 +434,12 @@
          )}
        </div>
        {/* footer */}
        <div className='flex justify-end p-6 pt-5'>
        <div className='p-6 pt-5 flex justify-end'>
          <Button className='mr-2' onClick={onHide}>{t('common.operation.cancel')}</Button>
          <Button variant='primary' onClick={onClickSave} loading={saveLoading}>{t('common.operation.save')}</Button>
        </div>
      </Modal >
        {showAppIconPicker && (
          <div onClick={e => e.stopPropagation()}>
            <AppIconPicker
              onSelect={(payload) => {
                setAppIcon(payload)
@@ -465,10 +452,9 @@
                setShowAppIconPicker(false)
              }}
            />
          </div>
        )}
      </Modal>
    </>
  )
}
export default React.memo(SettingsModal)
app/components/app/switch-app-modal/index.tsx
@@ -6,6 +6,7 @@
import { useTranslation } from 'react-i18next'
import { RiCloseLine } from '@remixicon/react'
import AppIconPicker from '../../base/app-icon-picker'
import s from './style.module.css'
import cn from '@/utils/classnames'
import Checkbox from '@/app/components/base/checkbox'
import Button from '@/app/components/base/button'
@@ -23,7 +24,6 @@
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
import AppIcon from '@/app/components/base/app-icon'
import { useStore as useAppStore } from '@/app/components/app/store'
import { noop } from 'lodash-es'
type SwitchAppModalProps = {
  show: boolean
@@ -82,7 +82,7 @@
        removeOriginal ? replace : push,
      )
    }
    catch {
    catch (e) {
      notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
    }
  }
@@ -95,24 +95,24 @@
  return (
    <>
      <Modal
        className={cn('w-[600px] max-w-[600px] p-8')}
        className={cn('p-8 max-w-[600px] w-[600px]', s.bg)}
        isShow={show}
        onClose={noop}
        onClose={() => { }}
      >
        <div className='absolute right-4 top-4 cursor-pointer p-2' onClick={onClose}>
          <RiCloseLine className='h-4 w-4 text-text-tertiary' />
        <div className='absolute right-4 top-4 p-2 cursor-pointer' onClick={onClose}>
          <RiCloseLine className='w-4 h-4 text-gray-500' />
        </div>
        <div className='h-12 w-12 rounded-xl border-[0.5px] border-divider-regular bg-background-default-burn p-3 shadow-xl'>
          <AlertTriangle className='h-6 w-6 text-[rgb(247,144,9)]' />
        <div className='w-12 h-12 p-3 bg-white rounded-xl border-[0.5px] border-gray-100 shadow-xl'>
          <AlertTriangle className='w-6 h-6 text-[rgb(247,144,9)]' />
        </div>
        <div className='relative mt-3 text-xl font-semibold leading-[30px] text-text-primary'>{t('app.switch')}</div>
        <div className='my-1 text-sm leading-5 text-text-tertiary'>
        <div className='relative mt-3 text-xl font-semibold leading-[30px] text-gray-900'>{t('app.switch')}</div>
        <div className='my-1 text-gray-500 text-sm leading-5'>
          <span>{t('app.switchTipStart')}</span>
          <span className='font-medium text-text-secondary'>{t('app.switchTip')}</span>
          <span className='text-gray-700 font-medium'>{t('app.switchTip')}</span>
          <span>{t('app.switchTipEnd')}</span>
        </div>
        <div className='pb-4'>
          <div className='py-2 text-sm font-medium leading-[20px] text-text-primary'>{t('app.switchLabel')}</div>
          <div className='py-2 text-sm font-medium leading-[20px] text-gray-900'>{t('app.switchLabel')}</div>
          <div className='flex items-center justify-between space-x-2'>
            <AppIcon
              size='large'
@@ -127,7 +127,7 @@
              value={name}
              onChange={e => setName(e.target.value)}
              placeholder={t('app.newApp.appNamePlaceholder') || ''}
              className='h-10 grow'
              className='grow h-10'
            />
          </div>
          {showAppIconPicker && <AppIconPicker
@@ -144,10 +144,10 @@
          />}
        </div>
        {isAppsFull && <AppsFull loc='app-switch' />}
        <div className='flex items-center justify-between pt-6'>
        <div className='pt-6 flex justify-between items-center'>
          <div className='flex items-center'>
            <Checkbox className='shrink-0' checked={removeOriginal} onCheck={() => setRemoveOriginal(!removeOriginal)} />
            <div className="ml-2 cursor-pointer text-sm leading-5 text-text-secondary" onClick={() => setRemoveOriginal(!removeOriginal)}>{t('app.removeOriginal')}</div>
            <div className="ml-2 text-sm leading-5 text-gray-700 cursor-pointer" onClick={() => setRemoveOriginal(!removeOriginal)}>{t('app.removeOriginal')}</div>
          </div>
          <div className='flex items-center'>
            <Button className='mr-2' onClick={onClose}>{t('app.newApp.Cancel')}</Button>
app/components/app/switch-app-modal/style.module.css
New file
@@ -0,0 +1,3 @@
.bg {
  background: linear-gradient(180deg, rgba(247, 144, 9, 0.05) 0%, rgba(247, 144, 9, 0.00) 24.41%), #F9FAFB;
}
app/components/app/text-generate/index.tsx
New file
@@ -0,0 +1,26 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { format } from '@/service/base'
export type ITextGenerationProps = {
  value: string
  className?: string
}
const TextGeneration: FC<ITextGenerationProps> = ({
  value,
  className,
}) => {
  return (
    <div
      className={className}
      dangerouslySetInnerHTML={{
        __html: format(value),
      }}
    >
    </div>
  )
}
export default React.memo(TextGeneration)
app/components/app/text-generate/item/index.tsx
@@ -1,36 +1,35 @@
'use client'
import type { FC } from 'react'
import React, { useEffect, useState } from 'react'
import React, { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
  RiBookmark3Line,
  RiClipboardLine,
  RiFileList3Line,
  RiPlayList2Line,
  RiReplay15Line,
  RiSparklingFill,
  RiSparklingLine,
  RiThumbDownLine,
  RiThumbUpLine,
} from '@remixicon/react'
import copy from 'copy-to-clipboard'
import { useParams } from 'next/navigation'
import { HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline'
import { useBoolean } from 'ahooks'
import { HashtagIcon } from '@heroicons/react/24/solid'
import ResultTab from './result-tab'
import cn from '@/utils/classnames'
import { Markdown } from '@/app/components/base/markdown'
import Loading from '@/app/components/base/loading'
import Toast from '@/app/components/base/toast'
import AudioBtn from '@/app/components/base/audio-btn'
import type { FeedbackType } from '@/app/components/base/chat/chat/type'
import { fetchMoreLikeThis, updateFeedback } from '@/service/share'
import { File02 } from '@/app/components/base/icons/src/vender/line/files'
import { Bookmark } from '@/app/components/base/icons/src/vender/line/general'
import { Stars02 } from '@/app/components/base/icons/src/vender/line/weather'
import { RefreshCcw01 } from '@/app/components/base/icons/src/vender/line/arrows'
import AnnotationCtrlBtn from '@/app/components/base/features/new-feature-panel/annotation-reply/annotation-ctrl-btn'
import { fetchTextGenerationMessage } from '@/service/debug'
import EditReplyModal from '@/app/components/app/annotation/edit-annotation-modal'
import { useStore as useAppStore } from '@/app/components/app/store'
import WorkflowProcessItem from '@/app/components/base/chat/chat/answer/workflow-process'
import type { WorkflowProcess } from '@/app/components/base/chat/types'
import type { SiteInfo } from '@/models/share'
import { useChatContext } from '@/app/components/base/chat/chat/context'
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
import NewAudioButton from '@/app/components/base/new-audio-button'
import cn from '@/utils/classnames'
const MAX_DEPTH = 3
@@ -57,11 +56,30 @@
  taskId?: string
  controlClearMoreLikeThis?: number
  supportFeedback?: boolean
  supportAnnotation?: boolean
  isShowTextToSpeech?: boolean
  appId?: string
  varList?: { label: string; value: string | number | object }[]
  innerClassName?: string
  contentClassName?: string
  footerClassName?: string
  hideProcessDetail?: boolean
  siteInfo: SiteInfo | null
  inSidePanel?: boolean
}
export const SimpleBtn = ({ className, isDisabled, onClick, children }: {
  className?: string
  isDisabled?: boolean
  onClick?: () => void
  children: React.ReactNode
}) => (
  <div
    className={cn(isDisabled ? 'border-gray-100 text-gray-300' : 'border-gray-200 text-gray-700 cursor-pointer hover:border-gray-300 hover:shadow-sm', 'flex items-center h-7 px-3 rounded-md border text-xs  font-medium', className)}
    onClick={() => !isDisabled && onClick?.()}
  >
    {children}
  </div>
)
export const copyIcon = (
  <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -91,16 +109,22 @@
  taskId,
  controlClearMoreLikeThis,
  supportFeedback,
  supportAnnotation,
  isShowTextToSpeech,
  appId,
  varList,
  innerClassName,
  contentClassName,
  hideProcessDetail,
  siteInfo,
  inSidePanel,
}) => {
  const { t } = useTranslation()
  const params = useParams()
  const isTop = depth === 1
  const ref = useRef(null)
  const [completionRes, setCompletionRes] = useState('')
  const [childMessageId, setChildMessageId] = useState<string | null>(null)
  const hasChild = !!childMessageId
  const [childFeedback, setChildFeedback] = useState<FeedbackType>({
    rating: null,
  })
@@ -116,6 +140,8 @@
    setChildFeedback(childFeedback)
  }
  const [isShowReplyModal, setIsShowReplyModal] = useState(false)
  const question = (varList && varList?.length > 0) ? varList?.map(({ label, value }) => `${label}:${value}`).join('&') : ''
  const [isQuerying, { setTrue: startQuerying, setFalse: stopQuerying }] = useBoolean(false)
  const childProps = {
@@ -135,7 +161,6 @@
    controlClearMoreLikeThis,
    isWorkflow,
    siteInfo,
    taskId,
  }
  const handleMoreLikeThis = async () => {
@@ -152,6 +177,19 @@
    setChildMessageId(res.id)
    stopQuerying()
  }
  const mainStyle = (() => {
    const res: React.CSSProperties = !isTop
      ? {
        background: depth % 2 === 0 ? 'linear-gradient(90.07deg, #F9FAFB 0.05%, rgba(249, 250, 251, 0) 99.93%)' : '#fff',
      }
      : {}
    if (hasChild)
      res.boxShadow = '0px 1px 2px rgba(16, 24, 40, 0.05)'
    return res
  })()
  useEffect(() => {
    if (controlClearMoreLikeThis) {
@@ -190,45 +228,84 @@
    setShowPromptLogModal(true)
  }
  const [currentTab, setCurrentTab] = useState<string>('DETAIL')
  const showResultTabs = !!workflowProcessData?.resultText || !!workflowProcessData?.files?.length
  const switchTab = async (tab: string) => {
    setCurrentTab(tab)
  }
  useEffect(() => {
    if (workflowProcessData?.resultText || !!workflowProcessData?.files?.length)
      switchTab('RESULT')
    else
      switchTab('DETAIL')
  }, [workflowProcessData?.files?.length, workflowProcessData?.resultText])
  return (
  const ratingContent = (
    <>
      <div className={cn('relative', !isTop && 'mt-3', className)}>
        {isLoading && (
          <div className={cn('flex h-10 items-center', !inSidePanel && 'rounded-2xl border-t border-divider-subtle bg-chat-bubble-bg')}><Loading type='area' /></div>
      {!isWorkflow && !isError && messageId && !feedback?.rating && (
        <SimpleBtn className="!px-0">
          <>
            <div
              onClick={() => {
                onFeedback?.({
                  rating: 'like',
                })
              }}
              className='flex w-6 h-6 items-center justify-center rounded-md cursor-pointer hover:bg-gray-100'>
              <HandThumbUpIcon width={16} height={16} />
            </div>
            <div
              onClick={() => {
                onFeedback?.({
                  rating: 'dislike',
                })
              }}
              className='flex w-6 h-6 items-center justify-center rounded-md cursor-pointer hover:bg-gray-100'>
              <HandThumbDownIcon width={16} height={16} />
            </div>
          </>
        </SimpleBtn>
        )}
        {!isLoading && (
          <>
            {/* result content */}
            <div className={cn(
              'relative',
              !inSidePanel && 'rounded-2xl border-t border-divider-subtle bg-chat-bubble-bg',
            )}>
              {workflowProcessData && (
                <>
                  <div className={cn(
                    'p-3',
                    showResultTabs && 'border-b border-divider-subtle',
                  )}>
                    {taskId && (
                      <div className={cn('system-2xs-medium-uppercase mb-2 flex items-center text-text-accent-secondary', isError && 'text-text-destructive')}>
                        <RiPlayList2Line className='mr-1 h-3 w-3' />
                        <span>{t('share.generation.execution')}</span>
                        <span className='px-1'>·</span>
                        <span>{taskId}</span>
      {!isWorkflow && !isError && messageId && feedback?.rating === 'like' && (
        <div
          onClick={() => {
            onFeedback?.({
              rating: null,
            })
          }}
          className='flex w-7 h-7 items-center justify-center rounded-md cursor-pointer  !text-primary-600 border border-primary-200 bg-primary-100 hover:border-primary-300 hover:bg-primary-200'>
          <HandThumbUpIcon width={16} height={16} />
                      </div>
                    )}
      {!isWorkflow && !isError && messageId && feedback?.rating === 'dislike' && (
        <div
          onClick={() => {
            onFeedback?.({
              rating: null,
            })
          }}
          className='flex w-7 h-7 items-center justify-center rounded-md cursor-pointer  !text-red-600 border border-red-200 bg-red-100 hover:border-red-300 hover:bg-red-200'>
          <HandThumbDownIcon width={16} height={16} />
        </div>
      )}
    </>
  )
  const [currentTab, setCurrentTab] = useState<string>('DETAIL')
  return (
    <div ref={ref} className={cn(isTop ? `rounded-xl border ${!isError ? 'border-gray-200 bg-chat-bubble-bg' : 'border-[#FECDCA] bg-[#FEF3F2]'} ` : 'rounded-br-xl !mt-0', className)}
      style={isTop
        ? {
          boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)',
        }
        : {}}
    >
      {isLoading
        ? (
          <div className='flex items-center h-10'><Loading type='area' /></div>
        )
        : (
          <div
            className={cn(!isTop && 'rounded-br-xl border-l-2 border-primary-400', 'p-4', innerClassName)}
            style={mainStyle}
          >
            {(isTop && taskId) && (
              <div className='mb-2 text-gray-500 border border-gray-200 box-border flex items-center rounded-md italic text-[11px] pl-1 pr-1.5 font-medium w-fit group-hover:opacity-100'>
                <HashtagIcon className='w-3 h-3 text-gray-400 fill-current mr-1 stroke-current stroke-1' />
                {taskId}
              </div>)
            }
            <div className={`flex ${contentClassName}`}>
              <div className='grow w-0'>
                    {siteInfo && workflowProcessData && (
                      <WorkflowProcessItem
                        data={workflowProcessData}
@@ -238,77 +315,36 @@
                        readonly={!siteInfo.show_workflow_steps}
                      />
                    )}
                    {showResultTabs && (
                      <div className='flex items-center space-x-6 px-1'>
                        <div
                          className={cn(
                            'system-sm-semibold-uppercase cursor-pointer border-b-2 border-transparent py-3 text-text-tertiary',
                            currentTab === 'RESULT' && 'border-util-colors-blue-brand-blue-brand-600 text-text-primary',
                          )}
                          onClick={() => switchTab('RESULT')}
                        >{t('runLog.result')}</div>
                        <div
                          className={cn(
                            'system-sm-semibold-uppercase cursor-pointer border-b-2 border-transparent py-3 text-text-tertiary',
                            currentTab === 'DETAIL' && 'border-util-colors-blue-brand-blue-brand-600 text-text-primary',
                          )}
                          onClick={() => switchTab('DETAIL')}
                        >{t('runLog.detail')}</div>
                      </div>
                    )}
                  </div>
                  {!isError && (
                    <ResultTab data={workflowProcessData} content={content} currentTab={currentTab} />
                  )}
                </>
              )}
              {!workflowProcessData && taskId && (
                <div className={cn('system-2xs-medium-uppercase sticky left-0 top-0 flex w-full items-center rounded-t-2xl bg-components-actionbar-bg p-4 pb-3 text-text-accent-secondary', isError && 'text-text-destructive')}>
                  <RiPlayList2Line className='mr-1 h-3 w-3' />
                  <span>{t('share.generation.execution')}</span>
                  <span className='px-1'>·</span>
                  <span>{`${taskId}${depth > 1 ? `-${depth - 1}` : ''}`}</span>
                </div>
                {workflowProcessData && !isError && (
                  <ResultTab data={workflowProcessData} content={content} currentTab={currentTab} onCurrentTabChange={setCurrentTab} />
              )}
              {isError && (
                <div className='body-lg-regular p-4 pt-0 text-text-quaternary'>{t('share.generation.batchFailed.outputPlaceholder')}</div>
                  <div className='text-gray-400 text-sm'>{t('share.generation.batchFailed.outputPlaceholder')}</div>
              )}
              {!workflowProcessData && !isError && (typeof content === 'string') && (
                <div className={cn('p-4', taskId && 'pt-0')}>
                  <Markdown content={content} />
                </div>
              )}
            </div>
            {/* meta data */}
            <div className={cn(
              'system-xs-regular relative mt-1 h-4 px-4 text-text-quaternary',
              isMobile && ((childMessageId || isQuerying) && depth < 3) && 'pl-10',
            )}>
              {!isWorkflow && <span>{content?.length} {t('common.unit.char')}</span>}
              {/* action buttons */}
              <div className='absolute bottom-1 right-2 flex items-center'>
                {!isInWebApp && !isInstalledApp && !isResponding && (
                  <div className='ml-1 flex items-center gap-0.5 rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-md backdrop-blur-sm'>
                    <ActionButton disabled={isError || !messageId} onClick={handleOpenLogModal}>
                      <RiFileList3Line className='h-4 w-4' />
                      {/* <div>{t('common.operation.log')}</div> */}
                    </ActionButton>
                  </div>
                )}
                <div className='ml-1 flex items-center gap-0.5 rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-md backdrop-blur-sm'>
                  {moreLikeThis && (
                    <ActionButton state={depth === MAX_DEPTH ? ActionButtonState.Disabled : ActionButtonState.Default} disabled={depth === MAX_DEPTH} onClick={handleMoreLikeThis}>
                      <RiSparklingLine className='h-4 w-4' />
                    </ActionButton>
                  )}
                  {isShowTextToSpeech && (
                    <NewAudioButton
                      id={messageId!}
                      voice={config?.text_to_speech?.voice}
                    />
                  )}
            <div className='flex items-center justify-between mt-3'>
              <div className='flex items-center'>
                {
                  !isInWebApp && !isInstalledApp && !isResponding && (
                    <SimpleBtn
                      isDisabled={isError || !messageId}
                      className={cn(isMobile && '!px-1.5', 'space-x-1 mr-1')}
                      onClick={handleOpenLogModal}>
                      <File02 className='w-3.5 h-3.5' />
                      {!isMobile && <div>{t('common.operation.log')}</div>}
                    </SimpleBtn>
                  )
                }
                  {((currentTab === 'RESULT' && workflowProcessData?.resultText) || !isWorkflow) && (
                    <ActionButton disabled={isError || !messageId} onClick={() => {
                  <SimpleBtn
                    isDisabled={isError || !messageId}
                    className={cn(isMobile && '!px-1.5', 'space-x-1')}
                    onClick={() => {
                      const copyContent = isWorkflow ? workflowProcessData?.resultText : content
                      if (typeof copyContent === 'string')
                        copy(copyContent)
@@ -316,68 +352,117 @@
                        copy(JSON.stringify(copyContent))
                      Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') })
                    }}>
                      <RiClipboardLine className='h-4 w-4' />
                    </ActionButton>
                    <RiClipboardLine className='w-3.5 h-3.5' />
                    {!isMobile && <div>{t('common.operation.copy')}</div>}
                  </SimpleBtn>
                  )}
                  {isInWebApp && isError && (
                    <ActionButton onClick={onRetry}>
                      <RiReplay15Line className='h-4 w-4' />
                    </ActionButton>
                  )}
                  {isInWebApp && !isWorkflow && (
                    <ActionButton disabled={isError || !messageId} onClick={() => { onSave?.(messageId as string) }}>
                      <RiBookmark3Line className='h-4 w-4' />
                    </ActionButton>
                  )}
                </div>
                {(supportFeedback || isInWebApp) && !isWorkflow && !isError && messageId && (
                  <div className='ml-1 flex items-center gap-0.5 rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-md backdrop-blur-sm'>
                    {!feedback?.rating && (
                {isInWebApp && (
                      <>
                        <ActionButton onClick={() => onFeedback?.({ rating: 'like' })}>
                          <RiThumbUpLine className='h-4 w-4' />
                        </ActionButton>
                        <ActionButton onClick={() => onFeedback?.({ rating: 'dislike' })}>
                          <RiThumbDownLine className='h-4 w-4' />
                        </ActionButton>
                    {!isWorkflow && (
                      <SimpleBtn
                        isDisabled={isError || !messageId}
                        className={cn(isMobile && '!px-1.5', 'ml-2 space-x-1')}
                        onClick={() => { onSave?.(messageId as string) }}
                      >
                        <Bookmark className='w-3.5 h-3.5' />
                        {!isMobile && <div>{t('common.operation.save')}</div>}
                      </SimpleBtn>
                    )}
                    {(moreLikeThis && depth < MAX_DEPTH) && (
                      <SimpleBtn
                        isDisabled={isError || !messageId}
                        className={cn(isMobile && '!px-1.5', 'ml-2 space-x-1')}
                        onClick={handleMoreLikeThis}
                      >
                        <Stars02 className='w-3.5 h-3.5' />
                        {!isMobile && <div>{t('appDebug.feature.moreLikeThis.title')}</div>}
                      </SimpleBtn>
                    )}
                    {isError && (
                      <SimpleBtn
                        onClick={onRetry}
                        className={cn(isMobile && '!px-1.5', 'ml-2 space-x-1')}
                      >
                        <RefreshCcw01 className='w-3.5 h-3.5' />
                        {!isMobile && <div>{t('share.generation.batchFailed.retry')}</div>}
                      </SimpleBtn>
                    )}
                    {!isError && messageId && !isWorkflow && (
                      <div className="mx-3 w-[1px] h-[14px] bg-gray-200"></div>
                    )}
                    {ratingContent}
                      </>
                    )}
                    {feedback?.rating === 'like' && (
                      <ActionButton state={ActionButtonState.Active} onClick={() => onFeedback?.({ rating: null })}>
                        <RiThumbUpLine className='h-4 w-4' />
                      </ActionButton>
                {supportAnnotation && (
                  <>
                    <div className='ml-2 mr-1 h-[14px] w-[1px] bg-gray-200'></div>
                    <AnnotationCtrlBtn
                      appId={appId!}
                      messageId={messageId!}
                      className='ml-1'
                      query={question}
                      answer={content}
                      // not support cache. So can not be cached
                      cached={false}
                      onAdded={() => {
                      }}
                      onEdit={() => setIsShowReplyModal(true)}
                      onRemoved={() => { }}
                    />
                  </>
                    )}
                    {feedback?.rating === 'dislike' && (
                      <ActionButton state={ActionButtonState.Destructive} onClick={() => onFeedback?.({ rating: null })}>
                        <RiThumbDownLine className='h-4 w-4' />
                      </ActionButton>
                    )}
                <EditReplyModal
                  appId={appId!}
                  messageId={messageId!}
                  isShow={isShowReplyModal}
                  onHide={() => setIsShowReplyModal(false)}
                  query={question}
                  answer={content}
                  onAdded={() => { }}
                  onEdited={() => { }}
                  createdAt={0}
                  onRemove={() => { }}
                  onlyEditResponse
                />
                {supportFeedback && (
                  <div className='ml-1'>
                    {ratingContent}
                  </div>
                )}
              </div>
            </div>
            {/* more like this elements */}
            {!isTop && (
              <div className={cn(
                'absolute top-[-32px] flex h-[33px] w-4 justify-center',
                isMobile ? 'left-[17px]' : 'left-[50%] translate-x-[-50%]',
              )}>
                <div className='h-full w-0.5 bg-divider-regular'></div>
                <div className={cn(
                  'absolute left-0 flex h-4 w-4 items-center justify-center rounded-2xl border-[0.5px] border-divider-subtle bg-util-colors-blue-blue-500 shadow-xs',
                  isMobile ? 'top-[3.5px]' : 'top-2',
                )}>
                  <RiSparklingFill className='h-3 w-3 text-text-primary-on-surface' />
                </div>
              </div>
            )}
                {isShowTextToSpeech && (
                  <>
                    <div className='ml-2 mr-2 h-[14px] w-[1px] bg-gray-200'></div>
                    <AudioBtn
                      id={messageId!}
                      className={'mr-1'}
                      voice={config?.text_to_speech?.voice}
                    />
          </>
        )}
      </div>
              <div>
                {!workflowProcessData && (
                  <div className='text-xs text-gray-500'>{content?.length} {t('common.unit.char')}</div>
                )}
              </div>
            </div>
          </div>
        )}
      {((childMessageId || isQuerying) && depth < 3) && (
        <div className='pl-4'>
        <GenerationItem {...childProps as any} />
        </div>
      )}
    </>
    </div>
  )
}
export default React.memo(GenerationItem)
app/components/app/text-generate/item/result-tab.tsx
@@ -1,6 +1,9 @@
import {
  memo,
  useEffect,
} from 'react'
import { useTranslation } from 'react-i18next'
import cn from '@/utils/classnames'
import { Markdown } from '@/app/components/base/markdown'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
@@ -11,20 +14,53 @@
  data,
  content,
  currentTab,
  onCurrentTabChange,
}: {
  data?: WorkflowProcess
  content: any
  currentTab: string
  onCurrentTabChange: (tab: string) => void
}) => {
  const { t } = useTranslation()
  const switchTab = async (tab: string) => {
    onCurrentTabChange(tab)
  }
  useEffect(() => {
    if (data?.resultText || !!data?.files?.length)
      switchTab('RESULT')
    else
      switchTab('DETAIL')
  }, [data?.files?.length, data?.resultText])
  return (
    <>
    <div className='grow relative flex flex-col'>
      {(data?.resultText || !!data?.files?.length) && (
        <div className='shrink-0 flex items-center mb-2 border-b-[0.5px] border-[rgba(0,0,0,0.05)]'>
          <div
            className={cn(
              'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
              currentTab === 'RESULT' && '!border-[rgb(21,94,239)] text-gray-700',
            )}
            onClick={() => switchTab('RESULT')}
          >{t('runLog.result')}</div>
          <div
            className={cn(
              'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
              currentTab === 'DETAIL' && '!border-[rgb(21,94,239)] text-gray-700',
            )}
            onClick={() => switchTab('DETAIL')}
          >{t('runLog.detail')}</div>
        </div>
      )}
      <div className={cn('grow bg-white')}>
      {currentTab === 'RESULT' && (
        <div className='space-y-3 p-4'>
          <>
          {data?.resultText && <Markdown content={data?.resultText || ''} />}
          {!!data?.files?.length && (
            <div className='flex flex-col gap-2'>
              {data?.files.map((item: any) => (
                <div key={item.varName} className='system-xs-regular flex flex-col gap-1'>
                  <div key={item.varName} className='flex flex-col gap-1 system-xs-regular'>
                  <div className='py-1 text-text-tertiary '>{item.varName}</div>
                  <FileList
                    files={item.list}
@@ -36,10 +72,10 @@
              ))}
            </div>
          )}
        </div>
          </>
      )}
      {currentTab === 'DETAIL' && content && (
        <div className='p-4'>
          <div className='mt-1'>
          <CodeEditor
            readOnly
            title={<div>JSON OUTPUT</div>}
@@ -49,7 +85,8 @@
          />
        </div>
      )}
    </>
      </div>
    </div>
  )
}
app/components/app/text-generate/saved-items/index.tsx
@@ -1,19 +1,15 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import {
  RiClipboardLine,
  RiDeleteBinLine,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import copy from 'copy-to-clipboard'
import NoData from './no-data'
import cn from '@/utils/classnames'
import type { SavedMessage } from '@/models/debug'
import { Markdown } from '@/app/components/base/markdown'
import { SimpleBtn, copyIcon } from '@/app/components/app/text-generate/item'
import Toast from '@/app/components/base/toast'
import ActionButton from '@/app/components/base/action-button'
import NewAudioButton from '@/app/components/base/new-audio-button'
import AudioBtn from '@/app/components/base/audio-btn'
export type ISavedItemsProps = {
  className?: string
@@ -22,6 +18,12 @@
  onRemove: (id: string) => void
  onStartCreateContent: () => void
}
const removeIcon = (
  <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M5.25 1.75H8.75M1.75 3.5H12.25M11.0833 3.5L10.6742 9.63625C10.6129 10.5569 10.5822 11.0172 10.3833 11.3663C10.2083 11.6735 9.94422 11.9206 9.62597 12.0748C9.26448 12.25 8.80314 12.25 7.88045 12.25H6.11955C5.19686 12.25 4.73552 12.25 4.37403 12.0748C4.05577 11.9206 3.79172 11.6735 3.61666 11.3663C3.41781 11.0172 3.38713 10.5569 3.32575 9.63625L2.91667 3.5M5.83333 6.125V9.04167M8.16667 6.125V9.04167" stroke="#344054" strokeWidth="1.25" strokeLinecap="round" strokeLinejoin="round" />
  </svg>
)
const SavedItems: FC<ISavedItemsProps> = ({
  className,
@@ -33,37 +35,56 @@
  const { t } = useTranslation()
  return (
    <div className={cn('space-y-4', className)}>
    <div className={cn(className, 'space-y-3')}>
      {list.length === 0
        ? (
          <div className='px-6'>
          <NoData onStartCreateContent={onStartCreateContent} />
          </div>
        )
        : (<>
          {list.map(({ id, answer }) => (
            <div key={id} className='relative'>
              <div className={cn(
                'rounded-2xl bg-background-section-burn p-4',
              )}>
            <div
              key={id}
              className='p-4 rounded-xl  bg-gray-50'
              style={{
                boxShadow: '0px 1px 2px rgba(16, 24, 40, 0.05)',
              }}
            >
                <Markdown content={answer} />
              </div>
              <div className='system-xs-regular mt-1 h-4 px-4 text-text-quaternary'>
                <span>{answer.length} {t('common.unit.char')}</span>
              </div>
              <div className='absolute bottom-1 right-2'>
                <div className='ml-1 flex items-center gap-0.5 rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-md backdrop-blur-sm'>
                  {isShowTextToSpeech && <NewAudioButton value={answer}/>}
                  <ActionButton onClick={() => {
              <div className='flex items-center justify-between mt-3'>
                <div className='flex items-center space-x-2'>
                  <SimpleBtn
                    className='space-x-1'
                    onClick={() => {
                    copy(answer)
                    Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') })
                  }}>
                    <RiClipboardLine className='h-4 w-4' />
                  </ActionButton>
                  <ActionButton onClick={() => {
                    {copyIcon}
                    <div>{t('common.operation.copy')}</div>
                  </SimpleBtn>
                  <SimpleBtn
                    className='space-x-1'
                    onClick={() => {
                    onRemove(id)
                  }}>
                    <RiDeleteBinLine className='h-4 w-4' />
                  </ActionButton>
                    {removeIcon}
                    <div>{t('common.operation.remove')}</div>
                  </SimpleBtn>
                  {isShowTextToSpeech && (
                    <>
                      <div className='ml-2 mr-2 h-[14px] w-[1px] bg-gray-200'></div>
                      <AudioBtn
                        value={answer}
                        noCache={false}
                        className={'mr-1'}
                      />
                    </>
                  )}
                </div>
                <div className='text-xs text-gray-500'>{answer?.length} {t('common.unit.char')}</div>
              </div>
            </div>
          ))}
app/components/app/text-generate/saved-items/no-data/index.tsx
@@ -2,14 +2,21 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import {
  RiAddLine,
  RiBookmark3Line,
} from '@remixicon/react'
import { PlusIcon } from '@heroicons/react/24/outline'
import Button from '@/app/components/base/button'
export type INoDataProps = {
  onStartCreateContent: () => void
}
const markIcon = (
  <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M4.16699 6.5C4.16699 5.09987 4.16699 4.3998 4.43948 3.86502C4.67916 3.39462 5.06161 3.01217 5.53202 2.77248C6.0668 2.5 6.76686 2.5 8.16699 2.5H11.8337C13.2338 2.5 13.9339 2.5 14.4686 2.77248C14.939 3.01217 15.3215 3.39462 15.5612 3.86502C15.8337 4.3998 15.8337 5.09987 15.8337 6.5V17.5L10.0003 14.1667L4.16699 17.5V6.5Z" stroke="#667085" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
  </svg>
)
const lightIcon = (
  <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className="inline relative -top-3 -left-1.5"><path d="M5 6.5V5M8.93934 7.56066L10 6.5M10.0103 11.5H11.5103" stroke="#374151" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"></path></svg>
)
const NoData: FC<INoDataProps> = ({
  onStartCreateContent,
@@ -17,23 +24,25 @@
  const { t } = useTranslation()
  return (
    <div className='rounded-xl bg-background-section-burn p-6 '>
      <div className='flex h-10 w-10 items-center justify-center rounded-[10px] border-[0.5px] border-components-card-border bg-components-card-bg-alt shadow-lg backdrop-blur-sm'>
        <RiBookmark3Line className='h-4 w-4 text-text-accent'/>
    <div className='mt-[60px] px-5 py-4 rounded-2xl bg-gray-50 '>
      <div className='flex items-center justify-center w-11 h-11 border border-gray-100 rounded-lg'>
        {markIcon}
      </div>
      <div className='mt-3'>
        <span className='system-xl-semibold text-text-secondary'>{t('share.generation.savedNoData.title')}</span>
      <div className='mt-2'>
        <span className='text-gray-700 font-semibold'>{t('share.generation.savedNoData.title')}</span>
        {lightIcon}
      </div>
      <div className='system-sm-regular mt-1 text-text-tertiary'>
      <div className='mt-2 text-gray-500 text-[13px] font-normal'>
        {t('share.generation.savedNoData.description')}
      </div>
      <Button
        variant='primary'
        className='mt-3'
        className='mt-4'
        onClick={onStartCreateContent}
      >
        <RiAddLine className='mr-1 h-4 w-4' />
        <div className='flex items-center space-x-2 text-primary-600 text-[13px] font-medium'>
          <PlusIcon className='w-4 h-4' />
        <span>{t('share.generation.savedNoData.startCreateContent')}</span>
        </div>
      </Button>
    </div>
  )
app/components/app/type-selector/index.tsx
@@ -9,7 +9,7 @@
  PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import { BubbleTextMod, ChatBot, ListSparkle, Logic } from '@/app/components/base/icons/src/vender/solid/communication'
import type { AppMode } from '@/types/app'
import { type AppMode } from '@/types/app'
export type AppSelectorProps = {
  value: Array<AppMode>
  onChange: (value: AppSelectorProps['value']) => void
@@ -33,19 +33,19 @@
          className='block'
        >
          <div className={cn(
            'flex cursor-pointer items-center justify-between space-x-1 rounded-md px-2 hover:bg-state-base-hover',
            'flex items-center justify-between rounded-md cursor-pointer px-2 space-x-1 hover:bg-state-base-hover',
          )}>
            <AppTypeSelectTrigger values={value} />
            {value && value.length > 0 && <div className='h-4 w-4' onClick={(e) => {
            {value && value.length > 0 && <div className='w-4 h-4' onClick={(e) => {
              e.stopPropagation()
              onChange([])
            }}>
              <RiCloseCircleFill className='h-3.5 w-3.5 cursor-pointer text-text-quaternary hover:text-text-tertiary' />
              <RiCloseCircleFill className='w-3.5 h-3.5 text-text-quaternary hover:text-text-tertiary cursor-pointer' />
            </div>}
          </div>
        </PortalToFollowElemTrigger>
        <PortalToFollowElemContent className='z-[1002]'>
          <ul className='relative w-[240px] rounded-xl border border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg backdrop-blur-[5px]'>
          <ul className='relative p-1 w-[240px] bg-components-panel-bg-blur backdrop-blur-[5px] rounded-xl shadow-lg border border-components-panel-border'>
            {allTypes.map(mode => (
              <AppTypeSelectorItem key={mode} type={mode}
                checked={Boolean(value.length > 0 && value?.indexOf(mode) !== -1)}
@@ -69,25 +69,25 @@
  const { t } = useTranslation()
  if (!values || values.length === 0) {
    return <div className={cn(
      'flex h-8 items-center justify-between gap-1',
      'flex items-center justify-between gap-1 h-8',
    )}>
      <RiFilter3Line className='h-4 w-4 text-text-tertiary' />
      <div className='system-sm-medium min-w-[65px] grow text-center text-text-tertiary'>{t('app.typeSelector.all')}</div>
      <RiArrowDownSLine className='h-4 w-4 text-text-tertiary' />
      <RiFilter3Line className='w-4 h-4 text-text-tertiary' />
      <div className='grow min-w-[65px] text-center system-sm-medium text-text-tertiary'>{t('app.typeSelector.all')}</div>
      <RiArrowDownSLine className='w-4 h-4 text-text-tertiary' />
    </div>
  }
  if (values.length === 1) {
    return <div className={cn(
      'flex h-8 flex-nowrap items-center justify-between gap-1',
      'flex items-center justify-between gap-1 h-8 flex-nowrap',
    )}>
      <AppTypeIcon type={values[0]} />
      <div className='line-clamp-1 flex flex-1 items-center text-center'>
      <div className='flex flex-1 items-center text-center line-clamp-1'>
        <AppTypeLabel type={values[0]} className="system-sm-medium text-components-menu-item-text" />
      </div>
    </div>
  }
  return <div className={cn(
    'relative flex h-8 items-center justify-between -space-x-2',
    'flex items-center justify-between h-8 -space-x-2 relative',
  )}>
    {values.map((mode, index) => (<AppTypeIcon key={mode} type={mode} wrapperClassName='border border-components-panel-on-panel-item-bg' style={{ zIndex: 5 - index }} />))}
  </div>
@@ -99,7 +99,7 @@
  onClick: () => void
}
function AppTypeSelectorItem({ checked, type, onClick }: AppTypeSelectorItemProps) {
  return <li className='flex cursor-pointer items-center space-x-2 rounded-lg py-1 pl-2 pr-1 hover:bg-state-base-hover' onClick={onClick}>
  return <li className='flex items-center space-x-2 pl-2 py-1 pr-1 rounded-lg cursor-pointer hover:bg-state-base-hover' onClick={onClick}>
    <Checkbox checked={checked} />
    <AppTypeIcon type={type} />
    <div className='grow p-1 pl-0'>
@@ -116,8 +116,8 @@
}
export function AppTypeIcon({ type, className, wrapperClassName, style }: AppTypeIconProps) {
  const wrapperClassNames = cn('inline-flex h-5 w-5 items-center justify-center rounded-md border border-divider-regular', wrapperClassName)
  const iconClassNames = cn('h-3.5 w-3.5 text-components-avatar-shape-fill-stop-100', className)
  const wrapperClassNames = cn('w-5 h-5 inline-flex items-center justify-center rounded-md border border-divider-regular', wrapperClassName)
  const iconClassNames = cn('w-3.5 h-3.5 text-components-avatar-shape-fill-stop-100', className)
  if (type === 'chat') {
    return <div style={style} className={cn(wrapperClassNames, 'bg-components-icon-bg-blue-solid')}>
      <ChatBot className={iconClassNames} />
app/components/app/workflow-log/detail.tsx
@@ -13,11 +13,11 @@
  const { t } = useTranslation()
  return (
    <div className='relative flex grow flex-col pt-3'>
      <span className='absolute right-3 top-4 z-20 cursor-pointer p-1' onClick={onClose}>
        <RiCloseLine className='h-4 w-4 text-text-tertiary' />
    <div className='grow relative flex flex-col pt-3'>
      <span className='absolute right-3 top-4 p-1 cursor-pointer z-20' onClick={onClose}>
        <RiCloseLine className='w-4 h-4 text-text-tertiary' />
      </span>
      <h1 className='system-xl-semibold shrink-0 px-4 py-1 text-text-primary'>{t('appLog.runDetail.workflowTitle')}</h1>
      <h1 className='shrink-0 px-4 py-1 text-text-primary system-xl-semibold'>{t('appLog.runDetail.workflowTitle')}</h1>
      <Run runID={runID}/>
    </div>
  )
app/components/app/workflow-log/filter.tsx
@@ -2,27 +2,9 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import dayjs from 'dayjs'
import { RiCalendarLine } from '@remixicon/react'
import quarterOfYear from 'dayjs/plugin/quarterOfYear'
import type { QueryParam } from './index'
import Chip from '@/app/components/base/chip'
import Input from '@/app/components/base/input'
dayjs.extend(quarterOfYear)
const today = dayjs()
export const TIME_PERIOD_MAPPING: { [key: string]: { value: number; name: string } } = {
  1: { value: 0, name: 'today' },
  2: { value: 7, name: 'last7days' },
  3: { value: 28, name: 'last4weeks' },
  4: { value: today.diff(today.subtract(3, 'month'), 'day'), name: 'last3months' },
  5: { value: today.diff(today.subtract(12, 'month'), 'day'), name: 'last12months' },
  6: { value: today.diff(today.startOf('month'), 'day'), name: 'monthToDate' },
  7: { value: today.diff(today.startOf('quarter'), 'day'), name: 'quarterToDate' },
  8: { value: today.diff(today.startOf('year'), 'day'), name: 'yearToDate' },
  9: { value: -1, name: 'allTime' },
}
type IFilterProps = {
  queryParams: QueryParam
@@ -32,7 +14,7 @@
const Filter: FC<IFilterProps> = ({ queryParams, setQueryParams }: IFilterProps) => {
  const { t } = useTranslation()
  return (
    <div className='mb-2 flex flex-row flex-wrap gap-2'>
    <div className='flex flex-row flex-wrap gap-2 mb-2'>
      <Chip
        value={queryParams.status || 'all'}
        onSelect={(item) => {
@@ -44,17 +26,6 @@
          { value: 'failed', name: 'Fail' },
          { value: 'stopped', name: 'Stop' },
        ]}
      />
      <Chip
        className='min-w-[150px]'
        panelClassName='w-[270px]'
        leftIcon={<RiCalendarLine className='h-4 w-4 text-text-secondary' />}
        value={queryParams.period}
        onSelect={(item) => {
          setQueryParams({ ...queryParams, period: item.value })
        }}
        onClear={() => setQueryParams({ ...queryParams, period: '9' })}
        items={Object.entries(TIME_PERIOD_MAPPING).map(([k, v]) => ({ value: k, name: t(`appLog.filter.period.${v.name}`) }))}
      />
      <Input
        wrapperClassName='w-[200px]'
app/components/app/workflow-log/index.tsx
@@ -4,30 +4,21 @@
import useSWR from 'swr'
import { usePathname } from 'next/navigation'
import { useDebounce } from 'ahooks'
import { omit } from 'lodash-es'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
import { Trans, useTranslation } from 'react-i18next'
import Link from 'next/link'
import List from './list'
import Filter, { TIME_PERIOD_MAPPING } from './filter'
import Filter from './filter'
import Pagination from '@/app/components/base/pagination'
import Loading from '@/app/components/base/loading'
import { fetchWorkflowLogs } from '@/service/log'
import { APP_PAGE_LIMIT } from '@/config'
import type { App, AppMode } from '@/types/app'
import { useAppContext } from '@/context/app-context'
dayjs.extend(utc)
dayjs.extend(timezone)
export type ILogsProps = {
  appDetail: App
}
export type QueryParam = {
  period: string
  status?: string
  keyword?: string
}
@@ -42,10 +33,10 @@
  const pathname = usePathname()
  const pathSegments = pathname.split('/')
  pathSegments.pop()
  return <div className='flex h-full items-center justify-center'>
    <div className='box-border h-fit w-[560px] rounded-2xl bg-background-section-burn px-5 py-4'>
      <span className='system-md-semibold text-text-secondary'>{t('appLog.table.empty.element.title')}<ThreeDotsIcon className='relative -left-1.5 -top-3 inline' /></span>
      <div className='system-sm-regular mt-2 text-text-tertiary'>
  return <div className='flex items-center justify-center h-full'>
    <div className='bg-background-section-burn w-[560px] h-fit box-border px-5 py-4 rounded-2xl'>
      <span className='text-text-secondary system-md-semibold'>{t('appLog.table.empty.element.title')}<ThreeDotsIcon className='inline relative -top-3 -left-1.5' /></span>
      <div className='mt-2 text-text-tertiary system-sm-regular'>
        <Trans
          i18nKey="appLog.table.empty.element.content"
          components={{ shareLink: <Link href={`${pathSegments.join('/')}/overview`} className='text-util-colors-blue-blue-600' />, testLink: <Link href={appUrl} className='text-util-colors-blue-blue-600' target='_blank' rel='noopener noreferrer' /> }}
@@ -57,8 +48,7 @@
const Logs: FC<ILogsProps> = ({ appDetail }) => {
  const { t } = useTranslation()
  const { userProfile: { timezone } } = useAppContext()
  const [queryParams, setQueryParams] = useState<QueryParam>({ status: 'all', period: '2' })
  const [queryParams, setQueryParams] = useState<QueryParam>({ status: 'all' })
  const [currPage, setCurrPage] = React.useState<number>(0)
  const debouncedQueryParams = useDebounce(queryParams, { wait: 500 })
  const [limit, setLimit] = React.useState<number>(APP_PAGE_LIMIT)
@@ -68,13 +58,6 @@
    limit,
    ...(debouncedQueryParams.status !== 'all' ? { status: debouncedQueryParams.status } : {}),
    ...(debouncedQueryParams.keyword ? { keyword: debouncedQueryParams.keyword } : {}),
    ...((debouncedQueryParams.period !== '9')
      ? {
        created_at__after: dayjs().subtract(TIME_PERIOD_MAPPING[debouncedQueryParams.period].value, 'day').startOf('day').tz(timezone).format('YYYY-MM-DDTHH:mm:ssZ'),
        created_at__before: dayjs().endOf('day').tz(timezone).format('YYYY-MM-DDTHH:mm:ssZ'),
      }
      : {}),
    ...omit(debouncedQueryParams, ['period', 'status']),
  }
  const getWebAppType = (appType: AppMode) => {
@@ -90,10 +73,10 @@
  const total = workflowLogs?.total
  return (
    <div className='flex h-full flex-col'>
      <h1 className='system-xl-semibold text-text-primary'>{t('appLog.workflowTitle')}</h1>
      <p className='system-sm-regular text-text-tertiary'>{t('appLog.workflowSubtitle')}</p>
      <div className='flex max-h-[calc(100%-16px)] flex-1 flex-col py-4'>
    <div className='flex flex-col h-full'>
      <h1 className='text-text-primary system-xl-semibold'>{t('appLog.workflowTitle')}</h1>
      <p className='text-text-tertiary system-sm-regular'>{t('appLog.workflowSubtitle')}</p>
      <div className='flex flex-col py-4 flex-1 max-h-[calc(100%-16px)]'>
        <Filter queryParams={queryParams} setQueryParams={setQueryParams} />
        {/* workflow log */}
        {total === undefined
app/components/app/workflow-log/list.tsx
@@ -33,7 +33,7 @@
  const statusTdRender = (status: string) => {
    if (status === 'succeeded') {
      return (
        <div className='system-xs-semibold-uppercase inline-flex items-center gap-1'>
        <div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
          <Indicator color={'green'} />
          <span className='text-util-colors-green-green-600'>Success</span>
        </div>
@@ -41,7 +41,7 @@
    }
    if (status === 'failed') {
      return (
        <div className='system-xs-semibold-uppercase inline-flex items-center gap-1'>
        <div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
          <Indicator color={'red'} />
          <span className='text-util-colors-red-red-600'>Fail</span>
        </div>
@@ -49,7 +49,7 @@
    }
    if (status === 'stopped') {
      return (
        <div className='system-xs-semibold-uppercase inline-flex items-center gap-1'>
        <div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
          <Indicator color={'yellow'} />
          <span className='text-util-colors-warning-warning-600'>Stop</span>
        </div>
@@ -57,7 +57,7 @@
    }
    if (status === 'running') {
      return (
        <div className='system-xs-semibold-uppercase inline-flex items-center gap-1'>
        <div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
          <Indicator color={'blue'} />
          <span className='text-util-colors-blue-light-blue-light-600'>Running</span>
        </div>
@@ -65,7 +65,7 @@
    }
    if (status === 'partial-succeeded') {
      return (
        <div className='system-xs-semibold-uppercase inline-flex items-center gap-1'>
        <div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
          <Indicator color={'green'} />
          <span className='text-util-colors-green-green-600'>Partial Success</span>
        </div>
@@ -87,32 +87,32 @@
      <table className={cn('mt-2 w-full min-w-[440px] border-collapse border-0')}>
        <thead className='system-xs-medium-uppercase text-text-tertiary'>
          <tr>
            <td className='w-5 whitespace-nowrap rounded-l-lg bg-background-section-burn pl-2 pr-1'></td>
            <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.startTime')}</td>
            <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.status')}</td>
            <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.runtime')}</td>
            <td className='whitespace-nowrap bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.tokens')}</td>
            <td className='whitespace-nowrap rounded-r-lg bg-background-section-burn py-1.5 pl-3'>{t('appLog.table.header.user')}</td>
            <td className='pl-2 pr-1 w-5 rounded-l-lg bg-background-section-burn whitespace-nowrap'></td>
            <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.startTime')}</td>
            <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.status')}</td>
            <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.runtime')}</td>
            <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.tokens')}</td>
            <td className='pl-3 py-1.5 rounded-r-lg bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.user')}</td>
          </tr>
        </thead>
        <tbody className="system-sm-regular text-text-secondary">
        <tbody className="text-text-secondary system-sm-regular">
          {logs.data.map((log: WorkflowAppLogDetail) => {
            const endUser = log.created_by_end_user ? log.created_by_end_user.session_id : log.created_by_account ? log.created_by_account.name : defaultValue
            return <tr
              key={log.id}
              className={cn('cursor-pointer border-b border-divider-subtle hover:bg-background-default-hover', currentLog?.id !== log.id ? '' : 'bg-background-default-hover')}
              className={cn('border-b border-divider-subtle hover:bg-background-default-hover cursor-pointer', currentLog?.id !== log.id ? '' : 'bg-background-default-hover')}
              onClick={() => {
                setCurrentLog(log)
                setShowDrawer(true)
              }}>
              <td className='h-4'>
                {!log.read_at && (
                  <div className='flex items-center p-3 pr-0.5'>
                    <span className='inline-block h-1.5 w-1.5 rounded bg-util-colors-blue-blue-500'></span>
                  <div className='p-3 pr-0.5 flex items-center'>
                    <span className='inline-block bg-util-colors-blue-blue-500 h-1.5 w-1.5 rounded'></span>
                  </div>
                )}
              </td>
              <td className='w-[160px] p-3 pr-2'>{formatTime(log.created_at, t('appLog.dateTimeFormat') as string)}</td>
              <td className='p-3 pr-2 w-[160px]'>{formatTime(log.created_at, t('appLog.dateTimeFormat') as string)}</td>
              <td className='p-3 pr-2'>{statusTdRender(log.workflow_run.status)}</td>
              <td className='p-3 pr-2'>
                <div className={cn(
@@ -134,7 +134,7 @@
        onClose={onCloseDrawer}
        mask={isMobile}
        footer={null}
        panelClassName='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[600px] rounded-xl border border-components-panel-border'
        panelClassname='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[600px] rounded-xl border border-components-panel-border'
      >
        <DetailPanel onClose={onCloseDrawer} runID={currentLog?.workflow_run.id || ''} />
      </Drawer>
app/components/base/action-button/index.css
@@ -2,11 +2,9 @@
@layer components {
    .action-btn {
        @apply inline-flex justify-center items-center cursor-pointer text-text-tertiary hover:text-text-secondary hover:bg-state-base-hover
    }
    .action-btn-hover {
        @apply bg-state-base-hover
        @apply inline-flex justify-center items-center cursor-pointer text-text-tertiary
        hover:text-text-secondary
        hover:bg-state-base-hover
    }
    .action-btn-disabled {
@@ -31,15 +29,21 @@
    }
    .action-btn.action-btn-active {
        @apply text-text-accent bg-state-accent-active hover:bg-state-accent-active-alt
        @apply
        text-text-accent
        bg-state-accent-active
        hover:bg-state-accent-active-alt
    }
    .action-btn.action-btn-disabled {
        @apply text-text-disabled
        @apply
        text-text-disabled
    }
    .action-btn.action-btn-destructive {
        @apply text-text-destructive bg-state-destructive-hover
        @apply
        text-text-destructive
        bg-state-destructive-hover
    }
}
app/components/base/action-button/index.tsx
@@ -8,7 +8,6 @@
  Active = 'active',
  Disabled = 'disabled',
  Default = '',
  Hover = 'hover',
}
const actionButtonVariants = cva(
@@ -29,7 +28,7 @@
)
export type ActionButtonProps = {
  size?: 'xs' | 's' | 'm' | 'l' | 'xl'
  size?: 'xs' | 'm' | 'l' | 'xl'
  state?: ActionButtonState
  styleCss?: CSSProperties
} & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<typeof actionButtonVariants>
@@ -42,8 +41,6 @@
      return 'action-btn-active'
    case ActionButtonState.Disabled:
      return 'action-btn-disabled'
    case ActionButtonState.Hover:
      return 'action-btn-hover'
    default:
      return ''
  }
app/components/base/agent-log-modal/detail.tsx
@@ -36,7 +36,7 @@
  const [list, setList] = useState<AgentIteration[]>([])
  const tools = useMemo(() => {
    const res = uniq(flatten(runDetail?.iterations.map((iteration) => {
    const res = uniq(flatten(runDetail?.iterations.map((iteration: any) => {
      return iteration.tool_calls.map((tool: any) => tool.tool_name).filter(Boolean)
    })).filter(Boolean))
    return res
@@ -79,28 +79,28 @@
  }, [appDetail, conversationID, messageID])
  return (
    <div className='relative flex grow flex-col'>
    <div className='grow relative flex flex-col'>
      {/* tab */}
      <div className='flex shrink-0 items-center border-b-[0.5px] border-divider-regular px-4'>
      <div className='shrink-0 flex items-center px-4 border-b-[0.5px] border-[rgba(0,0,0,0.05)]'>
        <div
          className={cn(
            'mr-6 cursor-pointer border-b-2 border-transparent py-3 text-[13px] font-semibold leading-[18px] text-text-tertiary',
            currentTab === 'DETAIL' && '!border-[rgb(21,94,239)] text-text-secondary',
            'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
            currentTab === 'DETAIL' && '!border-[rgb(21,94,239)] text-gray-700',
          )}
          onClick={() => switchTab('DETAIL')}
        >{t('runLog.detail')}</div>
        <div
          className={cn(
            'mr-6 cursor-pointer border-b-2 border-transparent py-3 text-[13px] font-semibold leading-[18px] text-text-tertiary',
            currentTab === 'TRACING' && '!border-[rgb(21,94,239)] text-text-secondary',
            'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
            currentTab === 'TRACING' && '!border-[rgb(21,94,239)] text-gray-700',
          )}
          onClick={() => switchTab('TRACING')}
        >{t('runLog.tracing')}</div>
      </div>
      {/* panel detail */}
      <div className={cn('h-0 grow overflow-y-auto rounded-b-2xl bg-components-panel-bg', currentTab !== 'DETAIL' && '!bg-background-section')}>
      <div className={cn('grow bg-white h-0 overflow-y-auto rounded-b-2xl', currentTab !== 'DETAIL' && '!bg-gray-50')}>
        {loading && (
          <div className='flex h-full items-center justify-center bg-components-panel-bg'>
          <div className='flex h-full items-center justify-center bg-white'>
            <Loading />
          </div>
        )}
app/components/base/agent-log-modal/index.tsx
@@ -35,7 +35,7 @@
  return (
    <div
      className={cn('relative z-10 flex flex-col rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg py-3 shadow-xl')}
      className={cn('relative flex flex-col py-3 bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl z-10')}
      style={{
        width: 480,
        position: 'fixed',
@@ -45,9 +45,9 @@
      }}
      ref={ref}
    >
      <h1 className='text-md shrink-0 px-4 py-1 font-semibold text-text-primary'>{t('appLog.runDetail.workflowTitle')}</h1>
      <span className='absolute right-3 top-4 z-20 cursor-pointer p-1' onClick={onCancel}>
        <RiCloseLine className='h-4 w-4 text-text-tertiary' />
      <h1 className='shrink-0 px-4 py-1 text-md font-semibold text-gray-900'>{t('appLog.runDetail.workflowTitle')}</h1>
      <span className='absolute right-3 top-4 p-1 cursor-pointer z-20' onClick={onCancel}>
        <RiCloseLine className='w-4 h-4 text-gray-500' />
      </span>
      <AgentLogDetail
        conversationID={currentLogItem.conversationId}
app/components/base/agent-log-modal/iteration.tsx
@@ -2,9 +2,8 @@
import { useTranslation } from 'react-i18next'
import type { FC } from 'react'
import ToolCall from './tool-call'
import Divider from '@/app/components/base/divider'
import type { AgentIteration } from '@/models/log'
import cn from '@/utils/classnames'
import type { AgentIteration } from '@/models/log'
type Props = {
  isFinal: boolean
@@ -19,12 +18,12 @@
    <div className={cn('px-4 py-2')}>
      <div className='flex items-center'>
        {isFinal && (
          <div className='mr-3 shrink-0 text-xs font-semibold leading-[18px] text-text-tertiary'>{t('appLog.agentLogDetail.finalProcessing')}</div>
          <div className='shrink-0 mr-3 text-gray-500 text-xs leading-[18px] font-semibold'>{t('appLog.agentLogDetail.finalProcessing')}</div>
        )}
        {!isFinal && (
          <div className='mr-3 shrink-0 text-xs font-semibold leading-[18px] text-text-tertiary'>{`${t('appLog.agentLogDetail.iteration').toUpperCase()} ${index}`}</div>
          <div className='shrink-0 mr-3 text-gray-500 text-xs leading-[18px] font-semibold'>{`${t('appLog.agentLogDetail.iteration').toUpperCase()} ${index}`}</div>
        )}
        <Divider bgStyle='gradient' className='mx-0 h-[1px] grow'/>
        <div className='grow h-[1px] bg-gradient-to-r from-[#f3f4f6] to-gray-50'></div>
      </div>
      <ToolCall
        isLLM
app/components/base/agent-log-modal/result.tsx
@@ -36,7 +36,7 @@
  const { formatTime } = useTimestamp()
  return (
    <div className='bg-components-panel-bg py-2'>
    <div className='bg-white py-2'>
      <div className='px-4 py-2'>
        <StatusPanel
          status='succeeded'
@@ -45,7 +45,7 @@
          error={error}
        />
      </div>
      <div className='flex flex-col gap-2 px-4 py-2'>
      <div className='px-4 py-2 flex flex-col gap-2'>
        <CodeEditor
          readOnly
          title={<div>INPUT</div>}
@@ -62,57 +62,57 @@
        />
      </div>
      <div className='px-4 py-2'>
        <div className='h-[0.5px] bg-divider-regular opacity-5' />
        <div className='h-[0.5px] bg-black opacity-5' />
      </div>
      <div className='px-4 py-2'>
        <div className='relative'>
          <div className='h-6 text-xs font-medium leading-6 text-text-tertiary'>{t('runLog.meta.title')}</div>
          <div className='h-6 leading-6 text-gray-500 text-xs font-medium'>{t('runLog.meta.title')}</div>
          <div className='py-1'>
            <div className='flex'>
              <div className='w-[104px] shrink-0 truncate px-2 py-[5px] text-xs leading-[18px] text-text-tertiary'>{t('runLog.meta.status')}</div>
              <div className='grow px-2 py-[5px] text-xs leading-[18px] text-text-primary'>
              <div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('runLog.meta.status')}</div>
              <div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
                <span>SUCCESS</span>
              </div>
            </div>
            <div className='flex'>
              <div className='w-[104px] shrink-0 truncate px-2 py-[5px] text-xs leading-[18px] text-text-tertiary'>{t('runLog.meta.executor')}</div>
              <div className='grow px-2 py-[5px] text-xs leading-[18px] text-text-primary'>
              <div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('runLog.meta.executor')}</div>
              <div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
                <span>{created_by || 'N/A'}</span>
              </div>
            </div>
            <div className='flex'>
              <div className='w-[104px] shrink-0 truncate px-2 py-[5px] text-xs leading-[18px] text-text-tertiary'>{t('runLog.meta.startTime')}</div>
              <div className='grow px-2 py-[5px] text-xs leading-[18px] text-text-primary'>
              <div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('runLog.meta.startTime')}</div>
              <div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
                <span>{formatTime(Date.parse(created_at) / 1000, t('appLog.dateTimeFormat') as string)}</span>
              </div>
            </div>
            <div className='flex'>
              <div className='w-[104px] shrink-0 truncate px-2 py-[5px] text-xs leading-[18px] text-text-tertiary'>{t('runLog.meta.time')}</div>
              <div className='grow px-2 py-[5px] text-xs leading-[18px] text-text-primary'>
              <div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('runLog.meta.time')}</div>
              <div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
                <span>{`${elapsed_time?.toFixed(3)}s`}</span>
              </div>
            </div>
            <div className='flex'>
              <div className='w-[104px] shrink-0 truncate px-2 py-[5px] text-xs leading-[18px] text-text-tertiary'>{t('runLog.meta.tokens')}</div>
              <div className='grow px-2 py-[5px] text-xs leading-[18px] text-text-primary'>
              <div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('runLog.meta.tokens')}</div>
              <div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
                <span>{`${total_tokens || 0} Tokens`}</span>
              </div>
            </div>
            <div className='flex'>
              <div className='w-[104px] shrink-0 truncate px-2 py-[5px] text-xs leading-[18px] text-text-tertiary'>{t('appLog.agentLogDetail.agentMode')}</div>
              <div className='grow px-2 py-[5px] text-xs leading-[18px] text-text-primary'>
              <div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('appLog.agentLogDetail.agentMode')}</div>
              <div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
                <span>{agentMode === 'function_call' ? t('appDebug.agent.agentModeType.functionCall') : t('appDebug.agent.agentModeType.ReACT')}</span>
              </div>
            </div>
            <div className='flex'>
              <div className='w-[104px] shrink-0 truncate px-2 py-[5px] text-xs leading-[18px] text-text-tertiary'>{t('appLog.agentLogDetail.toolUsed')}</div>
              <div className='grow px-2 py-[5px] text-xs leading-[18px] text-text-primary'>
              <div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('appLog.agentLogDetail.toolUsed')}</div>
              <div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
                <span>{tools?.length ? tools?.join(', ') : 'Null'}</span>
              </div>
            </div>
            <div className='flex'>
              <div className='w-[104px] shrink-0 truncate px-2 py-[5px] text-xs leading-[18px] text-text-tertiary'>{t('appLog.agentLogDetail.iterations')}</div>
              <div className='grow px-2 py-[5px] text-xs leading-[18px] text-text-primary'>
              <div className='shrink-0 w-[104px] px-2 py-[5px] text-gray-500 text-xs leading-[18px] truncate'>{t('appLog.agentLogDetail.iterations')}</div>
              <div className='grow px-2 py-[5px] text-gray-900 text-xs leading-[18px]'>
                <span>{iterations}</span>
              </div>
            </div>
app/components/base/agent-log-modal/tool-call.tsx
@@ -33,7 +33,7 @@
    if (time < 1)
      return `${(time * 1000).toFixed(3)} ms`
    if (time > 60)
      return `${Number.parseInt(Math.round(time / 60).toString())} m ${(time % 60).toFixed(3)} s`
      return `${parseInt(Math.round(time / 60).toString())} m ${(time % 60).toFixed(3)} s`
    return `${time.toFixed(3)} s`
  }
@@ -41,32 +41,32 @@
    if (tokens < 1000)
      return tokens
    if (tokens >= 1000 && tokens < 1000000)
      return `${Number.parseFloat((tokens / 1000).toFixed(3))}K`
      return `${parseFloat((tokens / 1000).toFixed(3))}K`
    if (tokens >= 1000000)
      return `${Number.parseFloat((tokens / 1000000).toFixed(3))}M`
      return `${parseFloat((tokens / 1000000).toFixed(3))}M`
  }
  return (
    <div className={cn('py-1')}>
      <div className={cn('group rounded-2xl border border-components-panel-border bg-background-default shadow-xs transition-all hover:shadow-md')}>
      <div className={cn('group transition-all bg-white border border-gray-100 rounded-2xl shadow-xs hover:shadow-md')}>
        <div
          className={cn(
            'flex cursor-pointer items-center py-3 pl-[6px] pr-3',
            'flex items-center py-3 pl-[6px] pr-3 cursor-pointer',
            !collapseState && '!pb-2',
          )}
          onClick={() => setCollapseState(!collapseState)}
        >
          <ChevronRight
            className={cn(
              'mr-1 h-3 w-3 shrink-0 text-text-quaternary transition-all group-hover:text-text-tertiary',
              'shrink-0 w-3 h-3 mr-1 text-gray-400 transition-all group-hover:text-gray-500',
              !collapseState && 'rotate-90',
            )}
          />
          <BlockIcon className={cn('mr-2 shrink-0')} type={isLLM ? BlockEnum.LLM : BlockEnum.Tool} toolIcon={toolCall.tool_icon} />
          <BlockIcon className={cn('shrink-0 mr-2')} type={isLLM ? BlockEnum.LLM : BlockEnum.Tool} toolIcon={toolCall.tool_icon} />
          <div className={cn(
            'grow truncate text-[13px] font-semibold leading-[16px] text-text-secondary',
            'grow text-gray-700 text-[13px] leading-[16px] font-semibold truncate',
          )} title={toolName}>{toolName}</div>
          <div className='shrink-0 text-xs leading-[18px] text-text-tertiary'>
          <div className='shrink-0 text-gray-500 text-xs leading-[18px]'>
            {toolCall.time_cost && (
              <span>{getTime(toolCall.time_cost || 0)}</span>
            )}
@@ -75,17 +75,17 @@
            )}
          </div>
          {toolCall.status === 'success' && (
            <RiCheckboxCircleLine className='ml-2 h-3.5 w-3.5 shrink-0 text-[#12B76A]' />
            <RiCheckboxCircleLine className='shrink-0 ml-2 w-3.5 h-3.5 text-[#12B76A]' />
          )}
          {toolCall.status === 'error' && (
            <RiErrorWarningLine className='ml-2 h-3.5 w-3.5 shrink-0 text-[#F04438]' />
            <RiErrorWarningLine className='shrink-0 ml-2 w-3.5 h-3.5 text-[#F04438]' />
          )}
        </div>
        {!collapseState && (
          <div className='pb-2'>
            <div className={cn('px-[10px] py-1')}>
              {toolCall.status === 'error' && (
                <div className='rounded-lg border-[0.5px] border-[rbga(0,0,0,0.05)] bg-[#fef3f2] px-3 py-[10px] text-xs leading-[18px] text-[#d92d20] shadow-xs'>{toolCall.error}</div>
                <div className='px-3 py-[10px] bg-[#fef3f2] rounded-lg border-[0.5px] border-[rbga(0,0,0,0.05)] text-xs leading-[18px] text-[#d92d20] shadow-xs'>{toolCall.error}</div>
              )}
            </div>
            {toolCall.tool_input && (
app/components/base/agent-log-modal/tracing.tsx
@@ -9,7 +9,7 @@
const TracingPanel: FC<TracingPanelProps> = ({ list }) => {
  return (
    <div className='bg-background-section'>
    <div className='bg-gray-50'>
      {list.map((iteration, index) => (
        <Iteration
          key={index}
app/components/base/answer-icon/index.tsx
@@ -38,7 +38,7 @@
    style={{ background: background || '#D5F5F6' }}
  >
    {isValidImageIcon
      ? <img src={imageUrl} className="h-full w-full rounded-full" alt="answer icon" />
      ? <img src={imageUrl} className="w-full h-full rounded-full" alt="answer icon" />
      : (icon && icon !== '') ? <em-emoji id={icon} /> : <em-emoji id='🤖' />
    }
  </div>
app/components/base/app-icon-picker/ImageInput.tsx
@@ -4,7 +4,6 @@
import { createRef, useEffect, useState } from 'react'
import Cropper, { type Area, type CropperProps } from 'react-easy-crop'
import classNames from 'classnames'
import { useTranslation } from 'react-i18next'
import { ImagePlus } from '../icons/src/vender/line/images'
import { useDraggableUploader } from './hooks'
@@ -27,7 +26,6 @@
  cropShape,
  onImageInput,
}) => {
  const { t } = useTranslation()
  const [inputImage, setInputImage] = useState<{ file: File; url: string }>()
  const [isAnimatedImage, setIsAnimatedImage] = useState<boolean>(false)
  useEffect(() => {
@@ -103,10 +101,10 @@
        {
          !inputImage
            ? <>
              <ImagePlus className="pointer-events-none mb-3 h-[30px] w-[30px]" />
              <div className="mb-[2px] text-sm font-medium">
                <span className="pointer-events-none">{t('common.imageInput.dropImageHere')}&nbsp;</span>
                <button className="text-components-button-primary-bg" onClick={() => inputRef.current?.click()}>{t('common.imageInput.browse')}</button>
              <ImagePlus className="w-[30px] h-[30px] mb-3 pointer-events-none" />
              <div className="text-sm font-medium mb-[2px]">
                <span className="pointer-events-none">Drop your image here, or&nbsp;</span>
                <button className="text-components-button-primary-bg" onClick={() => inputRef.current?.click()}>browse</button>
                <input
                  ref={inputRef} type="file" className="hidden"
                  onClick={e => ((e.target as HTMLInputElement).value = '')}
@@ -114,7 +112,7 @@
                  onChange={handleLocalFileInput}
                />
              </div>
              <div className="pointer-events-none">{t('common.imageInput.supportedFormats')}</div>
              <div className="text-xs pointer-events-none">Supports PNG, JPG, JPEG, WEBP and GIF</div>
            </>
            : handleShowImage()
        }
app/components/base/app-icon-picker/index.tsx
@@ -15,7 +15,6 @@
import type { AppIconType, ImageFile } from '@/types/app'
import cn from '@/utils/classnames'
import { DISABLE_UPLOAD_IMAGE_AS_ICON } from '@/config'
import { noop } from 'lodash-es'
export type AppIconEmojiSelection = {
  type: 'emoji'
@@ -108,19 +107,19 @@
  }
  return <Modal
    onClose={noop}
    onClose={() => { }}
    isShow
    closable={false}
    wrapperClassName={className}
    className={cn(s.container, '!w-[362px] !p-0')}
  >
    {!DISABLE_UPLOAD_IMAGE_AS_ICON && <div className="w-full p-2 pb-0">
      <div className='flex items-center justify-center gap-2 rounded-xl bg-background-body p-1'>
    {!DISABLE_UPLOAD_IMAGE_AS_ICON && <div className="p-2 pb-0 w-full">
      <div className='p-1 flex items-center justify-center gap-2 bg-background-body rounded-xl'>
        {tabs.map(tab => (
          <button
            key={tab.key}
            className={`
                        flex h-8 flex-1 shrink-0 items-center justify-center rounded-xl p-2 text-sm font-medium
                        p-2 flex-1 flex justify-center items-center h-8 rounded-xl text-sm shrink-0 font-medium
                        ${activeTab === tab.key && 'bg-components-main-nav-nav-button-bg-active shadow-md'}
                      `}
            onClick={() => setActiveTab(tab.key as AppIconType)}
@@ -135,7 +134,7 @@
    <ImageInput className={activeTab === 'image' ? 'block' : 'hidden'} onImageInput={handleImageInput} />
    <Divider className='m-0' />
    <div className='flex w-full items-center justify-center gap-2 p-3'>
    <div className='w-full flex items-center justify-center p-3 gap-2'>
      <Button className='w-full' onClick={() => onClose?.()}>
        {t('app.iconPicker.cancel')}
      </Button>
app/components/base/app-icon/index.tsx
@@ -61,8 +61,8 @@
    onClick={onClick}
  >
    {isValidImageIcon
      ? <img src={imageUrl} className="h-full w-full" alt="app icon" />
      // eslint-disable-next-line @next/next/no-img-element
      ? <img src={imageUrl} className="w-full h-full" alt="app icon" />
      : (innerIcon || ((icon && icon !== '') ? <em-emoji id={icon} /> : <em-emoji id='🤖' />))
    }
  </span>
app/components/base/app-unavailable.tsx
@@ -17,8 +17,8 @@
  const { t } = useTranslation()
  return (
    <div className='flex h-screen w-screen items-center justify-center'>
      <h1 className='mr-5 h-[50px] pr-5 text-[24px] font-medium leading-[50px]'
    <div className='flex items-center justify-center w-screen h-screen'>
      <h1 className='mr-5 h-[50px] leading-[50px] pr-5 text-[24px] font-medium'
        style={{
          borderRight: '1px solid rgba(0,0,0,.3)',
        }}>{code}</h1>
app/components/base/audio-btn/audio.player.manager.ts
@@ -1,6 +1,6 @@
import AudioPlayer from '@/app/components/base/audio-btn/audio'
declare global {
  // eslint-disable-next-line ts/consistent-type-definitions
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
  interface AudioPlayerManager {
    instance: AudioPlayerManager
  }
@@ -12,6 +12,9 @@
  private audioPlayers: AudioPlayer | null = null
  private msgId: string | undefined
  private constructor() {
  }
  public static getInstance(): AudioPlayerManager {
    if (!AudioPlayerManager.instance) {
      AudioPlayerManager.instance = new AudioPlayerManager()
@@ -21,7 +24,7 @@
    return AudioPlayerManager.instance
  }
  public getAudioPlayer(url: string, isPublic: boolean, id: string | undefined, msgContent: string | null | undefined, voice: string | undefined, callback: ((event: string) => void) | null): AudioPlayer {
  public getAudioPlayer(url: string, isPublic: boolean, id: string | undefined, msgContent: string | null | undefined, voice: string | undefined, callback: ((event: string) => {}) | null): AudioPlayer {
    if (this.msgId && this.msgId === id && this.audioPlayers) {
      this.audioPlayers.setCallback(callback)
      return this.audioPlayers
@@ -33,7 +36,7 @@
          this.audioPlayers.cacheBuffers = []
          this.audioPlayers.sourceBuffer?.abort()
        }
        catch {
        catch (e) {
        }
      }
app/components/base/audio-btn/audio.ts
@@ -2,7 +2,7 @@
import { textToAudioStream } from '@/service/share'
declare global {
  // eslint-disable-next-line ts/consistent-type-definitions
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
  interface Window {
    ManagedMediaSource: any
  }
@@ -21,9 +21,9 @@
  isLoadData = false
  url: string
  isPublic: boolean
  callback: ((event: string) => void) | null
  callback: ((event: string) => {}) | null
  constructor(streamUrl: string, isPublic: boolean, msgId: string | undefined, msgContent: string | null | undefined, voice: string | undefined, callback: ((event: string) => void) | null) {
  constructor(streamUrl: string, isPublic: boolean, msgId: string | undefined, msgContent: string | null | undefined, voice: string | undefined, callback: ((event: string) => {}) | null) {
    this.audioContext = new AudioContext()
    this.msgId = msgId
    this.msgContent = msgContent
@@ -68,7 +68,7 @@
    })
  }
  public setCallback(callback: ((event: string) => void) | null) {
  public setCallback(callback: ((event: string) => {}) | null) {
    this.callback = callback
    if (callback) {
      this.audio.addEventListener('ended', () => {
@@ -125,7 +125,7 @@
        this.receiveAudioData(value)
      }
    }
    catch {
    catch (error) {
      this.isLoadData = false
      this.callback && this.callback('error')
    }
@@ -211,6 +211,10 @@
    this.audioContext.suspend()
  }
  private cancer() {
  }
  private receiveAudioData(unit8Array: Uint8Array) {
    if (!unit8Array) {
      this.finishStream()
app/components/base/audio-btn/index.tsx
@@ -29,7 +29,7 @@
  const params = useParams()
  const pathname = usePathname()
  const audio_finished_call = (event: string): void => {
  const audio_finished_call = (event: string): any => {
    switch (event) {
      case 'ended':
        setAudioState('ended')
@@ -87,18 +87,18 @@
      >
        <button
          disabled={audioState === 'loading'}
          className={`box-border flex h-6 w-6 cursor-pointer items-center justify-center ${isAudition ? 'p-0.5' : 'rounded-md bg-white p-0'}`}
          className={`box-border w-6 h-6 flex items-center justify-center cursor-pointer ${isAudition ? 'p-0.5' : 'p-0 rounded-md bg-white'}`}
          onClick={handleToggle}
        >
          {audioState === 'loading'
            ? (
              <div className='flex h-full w-full items-center justify-center rounded-md'>
              <div className='w-full h-full rounded-md flex items-center justify-center'>
                <Loading />
              </div>
            )
            : (
              <div className={'flex h-full w-full items-center justify-center rounded-md hover:bg-gray-50'}>
                <div className={`h-4 w-4 ${(audioState === 'playing') ? s.pauseIcon : s.playIcon}`}></div>
              <div className={`w-full h-full rounded-md flex items-center justify-center ${!isAudition ? 'hover:bg-gray-50' : 'hover:bg-gray-50'}`}>
                <div className={`w-4 h-4 ${(audioState === 'playing') ? s.pauseIcon : s.playIcon}`}></div>
              </div>
            )}
        </button>
app/components/base/audio-btn/style.module.css
app/components/base/audio-gallery/AudioPlayer.module.css
New file
@@ -0,0 +1,119 @@
.audioPlayer {
  display: flex;
  flex-direction: row;
  align-items: center;
  background-color: #ffffff;
  border-radius: 10px;
  padding: 8px;
  min-width: 240px;
  max-width: 420px;
  max-height: 40px;
  backdrop-filter: blur(5px);
  border: 1px solid rgba(16, 24, 40, 0.08);
  box-shadow: 0 1px 2px rgba(9, 9, 11, 0.05);
  gap: 8px;
}
.playButton {
  display: inline-flex;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background-color: #296DFF;
  color: white;
  border: none;
  cursor: pointer;
  align-items: center;
  justify-content: center;
  transition: background-color 0.1s;
  flex-shrink: 0;
}
.playButton:hover {
  background-color: #3367d6;
}
.playButton:disabled {
  background-color: #bdbdbf;
}
.audioControls {
  flex-grow: 1;
}
.progressBarContainer {
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.waveform {
  position: relative;
  display: flex;
  cursor: pointer;
  height: 24px;
  width: 100%;
  flex-grow: 1;
  align-items: center;
  justify-content: center;
}
.progressBar {
  position: absolute;
  top: 0;
  left: 0;
  opacity: 0.5;
  border-radius: 2px;
  flex: none;
  order: 55;
  flex-grow: 0;
  height: 100%;
  background-color: rgba(66, 133, 244, 0.3);
  pointer-events: none;
}
.timeDisplay {
  /* position: absolute; */
  color: #296DFF;
  border-radius: 2px;
  order: 0;
  height: 100%;
  width: 50px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* .currentTime {
    position: absolute;
    bottom: calc(100% + 5px);
    transform: translateX(-50%);
    background-color: rgba(255,255,255,.8);
    padding: 2px 4px;
    border-radius:10px;
    box-shadow: 0 1px 5px rgba(0, 0, 0, 0.08);
} */
.duration {
  background-color: rgba(255, 255, 255, 0.8);
  padding: 2px 4px;
  border-radius: 10px;
}
.source_unavailable {
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  position: absolute;
  color: #bdbdbf;
}
.playButton svg path,
.playButton svg rect{
   fill:currentColor;
}
app/components/base/audio-gallery/AudioPlayer.tsx
@@ -1,13 +1,7 @@
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { t } from 'i18next'
import {
  RiPauseCircleFill,
  RiPlayLargeFill,
} from '@remixicon/react'
import styles from './AudioPlayer.module.css'
import Toast from '@/app/components/base/toast'
import useTheme from '@/hooks/use-theme'
import { Theme } from '@/types/app'
import cn from '@/utils/classnames'
type AudioPlayerProps = {
  src: string
@@ -24,7 +18,6 @@
  const [hasStartedPlaying, setHasStartedPlaying] = useState(false)
  const [hoverTime, setHoverTime] = useState(0)
  const [isAudioAvailable, setIsAudioAvailable] = useState(true)
  const { theme } = useTheme()
  useEffect(() => {
    const audio = audioRef.current
@@ -62,7 +55,7 @@
    audio.load()
    // Delayed generation of waveform data
    // eslint-disable-next-line ts/no-use-before-define
    // eslint-disable-next-line @typescript-eslint/no-use-before-define
    const timer = setTimeout(() => generateWaveformData(src), 1000)
    return () => {
@@ -124,7 +117,7 @@
      setWaveformData(normalizedWaveform)
      setIsAudioAvailable(true)
    }
    catch {
    catch (error) {
      const waveform: number[] = []
      let prevValue = Math.random()
@@ -237,11 +230,11 @@
      let color
      if (index * barWidth <= playedWidth)
        color = theme === Theme.light ? '#296DFF' : '#84ABFF'
        color = '#296DFF'
      else if ((index * barWidth / width) * duration <= hoverTime)
        color = theme === Theme.light ? 'rgba(21,90,239,.40)' : 'rgba(200, 206, 218, 0.28)'
        color = 'rgba(21,90,239,.40)'
      else
        color = theme === Theme.light ? 'rgba(21,90,239,.20)' : 'rgba(200, 206, 218, 0.14)'
        color = 'rgba(21,90,239,.20)'
      const barHeight = value * height
      const rectX = index * barWidth
@@ -260,7 +253,7 @@
        ctx.fillRect(rectX, rectY, rectWidth, rectHeight)
      }
    })
  }, [currentTime, duration, hoverTime, theme, waveformData])
  }, [currentTime, duration, hoverTime, waveformData])
  useEffect(() => {
    drawWaveform()
@@ -286,32 +279,40 @@
  }, [duration])
  return (
    <div className='flex h-9 min-w-[240px] max-w-[420px] items-end gap-2 rounded-[10px] border border-components-panel-border-subtle bg-components-chat-input-audio-bg-alt p-2 shadow-xs backdrop-blur-sm'>
    <div className={styles.audioPlayer}>
      <audio ref={audioRef} src={src} preload="auto"/>
      <button className='inline-flex shrink-0 cursor-pointer items-center justify-center border-none text-text-accent transition-all hover:text-text-accent-secondary disabled:text-components-button-primary-bg-disabled' onClick={togglePlay} disabled={!isAudioAvailable}>
      <button className={styles.playButton} onClick={togglePlay} disabled={!isAudioAvailable}>
        {isPlaying
          ? (
            <RiPauseCircleFill className='h-5 w-5' />
            <svg viewBox="0 0 24 24" width="16" height="16">
              <rect x="7" y="6" width="3" height="12" rx="1.5" ry="1.5"/>
              <rect x="15" y="6" width="3" height="12" rx="1.5" ry="1.5"/>
            </svg>
          )
          : (
            <RiPlayLargeFill className='h-5 w-5' />
            <svg viewBox="0 0 24 24" width="16" height="16">
              <path d="M8 5v14l11-7z" fill="currentColor"/>
            </svg>
          )}
      </button>
      <div className={cn(isAudioAvailable && 'grow')} hidden={!isAudioAvailable}>
        <div className='flex h-8 items-center justify-center'>
      <div className={isAudioAvailable ? styles.audioControls : styles.audioControls_disabled} hidden={!isAudioAvailable}>
        <div className={styles.progressBarContainer}>
          <canvas
            ref={canvasRef}
            className='relative flex h-6 w-full grow cursor-pointer items-center justify-center'
            className={styles.waveform}
            onClick={handleCanvasInteraction}
            onMouseMove={handleMouseMove}
            onMouseDown={handleCanvasInteraction}
          />
          <div className='system-xs-medium inline-flex min-w-[50px] items-center justify-center text-text-accent-secondary'>
            <span className='rounded-[10px] px-0.5 py-1'>{formatTime(duration)}</span>
          {/* <div className={styles.currentTime} style={{ left: `${(currentTime / duration) * 81}%`, bottom: '29px' }}>
            {formatTime(currentTime)}
          </div> */}
          <div className={styles.timeDisplay}>
            <span className={styles.duration}>{formatTime(duration)}</span>
          </div>
        </div>
      </div>
      <div className='absolute left-0 top-0 flex h-full w-full items-center justify-center text-text-quaternary' hidden={isAudioAvailable}>{t('common.operation.audioSourceUnavailable')}</div>
      <div className={styles.source_unavailable} hidden={isAudioAvailable}>{t('common.operation.audioSourceUnavailable')}</div>
    </div>
  )
}
app/components/base/auto-height-textarea/common.tsx
@@ -1,13 +1,13 @@
import { useEffect, useRef } from 'react'
import { forwardRef, useEffect, useRef } from 'react'
import cn from '@/utils/classnames'
type AutoHeightTextareaProps =
  & React.DetailedHTMLProps<React.TextareaHTMLAttributes<HTMLTextAreaElement>, HTMLTextAreaElement>
  & { outerClassName?: string }
const AutoHeightTextarea = (
const AutoHeightTextarea = forwardRef<HTMLTextAreaElement, AutoHeightTextareaProps>(
  (
  {
    ref: outRef,
    outerClassName,
    value,
    className,
@@ -15,9 +15,8 @@
    autoFocus,
    disabled,
    ...rest
  }: AutoHeightTextareaProps & {
    ref: React.RefObject<HTMLTextAreaElement>;
  },
    outRef,
) => {
  const innerRef = useRef<HTMLTextAreaElement>(null)
  const ref = outRef || innerRef
@@ -31,7 +30,7 @@
    }
  }, [autoFocus, disabled, ref])
  return (
    (<div className={outerClassName}>
      <div className={outerClassName}>
      <div className='relative'>
        <div className={cn(className, 'invisible whitespace-pre-wrap break-all')}>
          {!value ? placeholder : `${value}`.replace(/\n$/, '\n ')}
@@ -39,15 +38,16 @@
        <textarea
          ref={ref}
          placeholder={placeholder}
          className={cn(className, 'absolute inset-0 h-full w-full resize-none appearance-none border-none outline-none disabled:bg-transparent')}
            className={cn(className, 'disabled:bg-transparent absolute inset-0 outline-none border-none appearance-none resize-none w-full h-full')}
          value={value}
          disabled={disabled}
          {...rest}
        />
      </div>
    </div>)
      </div>
  )
}
  },
)
AutoHeightTextarea.displayName = 'AutoHeightTextarea'
app/components/base/auto-height-textarea/index.tsx
@@ -1,4 +1,4 @@
import { useEffect, useRef } from 'react'
import { forwardRef, useEffect, useRef } from 'react'
import cn from '@/utils/classnames'
import { sleep } from '@/utils'
@@ -16,23 +16,10 @@
  onKeyUp?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void
}
const AutoHeightTextarea = (
  {
    ref: outerRef,
    value,
    onChange,
    placeholder,
    className,
    wrapperClassName,
    minHeight = 36,
    maxHeight = 96,
    autoFocus,
    controlFocus,
    onKeyDown,
    onKeyUp,
  }: IProps & {
    ref: React.RefObject<unknown>;
  },
const AutoHeightTextarea = forwardRef(
  (
    { value, onChange, placeholder, className, wrapperClassName, minHeight = 36, maxHeight = 96, autoFocus, controlFocus, onKeyDown, onKeyUp }: IProps,
    outerRef: any,
) => {
  // eslint-disable-next-line react-hooks/rules-of-hooks
  const ref = outerRef || useRef<HTMLTextAreaElement>(null)
@@ -66,8 +53,8 @@
  }, [controlFocus])
  return (
    (<div className={`relative ${wrapperClassName}`}>
      <div className={cn(className, 'invisible overflow-y-auto whitespace-pre-wrap  break-all')} style={{
      <div className={`relative ${wrapperClassName}`}>
        <div className={cn(className, 'invisible whitespace-pre-wrap break-all  overflow-y-auto')} style={{
        minHeight,
        maxHeight,
        paddingRight: (value && value.trim().length > 10000) ? 140 : 130,
@@ -87,9 +74,10 @@
        onKeyUp={onKeyUp}
        value={value}
      />
    </div>)
      </div>
  )
}
  },
)
AutoHeightTextarea.displayName = 'AutoHeightTextarea'
app/components/base/avatar/index.tsx
@@ -42,7 +42,7 @@
      style={style}
    >
      <div
        className={cn(textClassName, 'scale-[0.4] text-center text-white')}
        className={cn(textClassName, 'text-center text-white scale-[0.4]')}
        style={style}
      >
        {name[0].toLocaleUpperCase()}
app/components/base/badge.tsx
@@ -1,13 +1,11 @@
import type { ReactNode } from 'react'
import { memo } from 'react'
import cn from '@/utils/classnames'
type BadgeProps = {
  className?: string
  text?: ReactNode
  children?: ReactNode
  text?: string
  children?: React.ReactNode
  uppercase?: boolean
  hasRedCornerMark?: boolean
}
const Badge = ({
@@ -15,20 +13,15 @@
  text,
  children,
  uppercase = true,
  hasRedCornerMark,
}: BadgeProps) => {
  return (
    <div
      className={cn(
        'relative inline-flex h-5 items-center rounded-[5px] border border-divider-deep px-[5px] leading-3 text-text-tertiary',
        'inline-flex items-center px-[5px] h-5 rounded-[5px] border border-divider-deep leading-3 text-text-tertiary',
        uppercase ? 'system-2xs-medium-uppercase' : 'system-xs-medium',
        className,
      )}
    >
      {hasRedCornerMark && (
        <div className='absolute right-[-2px] top-[-2px] h-1.5 w-1.5 rounded-[2px] border border-components-badge-status-light-error-border-inner bg-components-badge-status-light-error-bg shadow-sm'>
        </div>
      )}
      {children || text}
    </div>
  )
app/components/base/block-input/index.tsx
@@ -134,8 +134,8 @@
      {textAreaContent}
      {/* footer */}
      {!readonly && (
        <div className='flex pb-2 pl-4'>
          <div className="h-[18px] rounded-md bg-gray-100 px-1 text-xs leading-[18px] text-gray-500">{currentValue?.length}</div>
        <div className='pl-4 pb-2 flex'>
          <div className="h-[18px] leading-[18px] px-1 rounded-md bg-gray-100 text-xs text-gray-500">{currentValue?.length}</div>
        </div>
      )}
app/components/base/button/add-button.tsx
@@ -14,8 +14,8 @@
  onClick,
}) => {
  return (
    <div className={cn(className, 'cursor-pointer select-none rounded-md p-1 hover:bg-state-base-hover')} onClick={onClick}>
      <RiAddLine className='h-4 w-4 text-text-tertiary' />
    <div className={cn(className, 'p-1 rounded-md cursor-pointer hover:bg-state-base-hover select-none')} onClick={onClick}>
      <RiAddLine className='w-4 h-4 text-text-tertiary' />
    </div>
  )
}
app/components/base/button/index.css
app/components/base/button/index.spec.tsx
@@ -4,29 +4,16 @@
afterEach(cleanup)
// https://testing-library.com/docs/queries/about
describe('Button', () => {
  describe('Button text', () => {
    test('Button text should be same as children', async () => {
      const { getByRole, container } = render(<Button>Click me</Button>)
      expect(getByRole('button').textContent).toBe('Click me')
      expect(container.querySelector('button')?.textContent).toBe('Click me')
    })
  })
  describe('Button loading', () => {
    test('Loading button text should include same as children', async () => {
      const { getByRole } = render(<Button loading>Click me</Button>)
      expect(getByRole('button').textContent?.includes('Loading')).toBe(true)
    })
    test('Not loading button text should include same as children', async () => {
      const { getByRole } = render(<Button loading={false}>Click me</Button>)
      expect(getByRole('button').textContent?.includes('Loading')).toBe(false)
    })
    test('Loading button should have loading classname', async () => {
      const animClassName = 'anim-breath'
      const { getByRole } = render(<Button loading spinnerClassName={animClassName}>Click me</Button>)
      expect(getByRole('button').getElementsByClassName('animate-spin')[0]?.className).toContain(animClassName)
    })
  })
@@ -46,56 +33,9 @@
      expect(getByRole('button').className).toContain('btn-warning')
    })
    test('Button should have secondary variant', async () => {
      const { getByRole } = render(<Button variant='secondary'>Click me</Button>)
      expect(getByRole('button').className).toContain('btn-secondary')
    })
    test('Button should have secondary-accent variant', async () => {
      const { getByRole } = render(<Button variant='secondary-accent'>Click me</Button>)
      expect(getByRole('button').className).toContain('btn-secondary-accent')
    })
    test('Button should have ghost variant', async () => {
      const { getByRole } = render(<Button variant='ghost'>Click me</Button>)
      expect(getByRole('button').className).toContain('btn-ghost')
    })
    test('Button should have ghost-accent variant', async () => {
      const { getByRole } = render(<Button variant='ghost-accent'>Click me</Button>)
      expect(getByRole('button').className).toContain('btn-ghost-accent')
    })
    test('Button disabled should have disabled variant', async () => {
      const { getByRole } = render(<Button disabled>Click me</Button>)
      expect(getByRole('button').className).toContain('btn-disabled')
    })
  })
  describe('Button size', () => {
    test('Button should have default size', async () => {
      const { getByRole } = render(<Button>Click me</Button>)
      expect(getByRole('button').className).toContain('btn-medium')
    })
    test('Button should have small size', async () => {
      const { getByRole } = render(<Button size='small'>Click me</Button>)
      expect(getByRole('button').className).toContain('btn-small')
    })
    test('Button should have medium size', async () => {
      const { getByRole } = render(<Button size='medium'>Click me</Button>)
      expect(getByRole('button').className).toContain('btn-medium')
    })
    test('Button should have large size', async () => {
      const { getByRole } = render(<Button size='large'>Click me</Button>)
      expect(getByRole('button').className).toContain('btn-large')
    })
  })
  describe('Button destructive', () => {
    test('Button should have destructive classname', async () => {
      const { getByRole } = render(<Button destructive>Click me</Button>)
      expect(getByRole('button').className).toContain('btn-destructive')
    })
  })
@@ -105,6 +45,5 @@
      const { getByRole } = render(<Button onClick={onClick}>Click me</Button>)
      fireEvent.click(getByRole('button'))
      expect(onClick).toHaveBeenCalled()
    })
  })
})
app/components/base/button/index.stories.tsx
@@ -99,7 +99,7 @@
    variant: 'primary',
    children: (
      <>
        <RocketLaunchIcon className="mr-1.5 h-4 w-4 stroke-[1.8px]" />
        <RocketLaunchIcon className="h-4 w-4 mr-1.5 stroke-[1.8px]" />
        Launch
      </>
    ),
app/components/base/button/index.tsx
@@ -34,11 +34,10 @@
  destructive?: boolean
  loading?: boolean
  styleCss?: CSSProperties
  spinnerClassName?: string
} & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<typeof buttonVariants>
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, destructive, loading, styleCss, children, spinnerClassName, ...props }, ref) => {
  ({ className, variant, size, destructive, loading, styleCss, children, ...props }, ref) => {
    return (
      <button
        type='button'
@@ -51,7 +50,7 @@
        {...props}
      >
        {children}
        {loading && <Spinner loading={loading} className={classNames('!text-white !h-3 !w-3 !border-2 !ml-1', spinnerClassName)} />}
        {loading && <Spinner loading={loading} className='!text-white !h-3 !w-3 !border-2 !ml-1' />}
      </button>
    )
  },
app/components/base/chat/__tests__/utils.spec.ts
@@ -1,4 +1,4 @@
import { get } from 'lodash-es'
import { get } from 'lodash'
import { buildChatItemTree, getThreadMessages } from '../utils'
import type { ChatItemInTree } from '../types'
import branchedTestMessages from './branchedTestMessages.json'
@@ -263,7 +263,7 @@
    expect(tree7).toMatchSnapshot()
  })
  const partialMessages2 = partialMessages as ChatItemInTree[]
  const partialMessages2 = (partialMessages as ChatItemInTree[])
  const tree8 = buildChatItemTree(partialMessages2)
  it('should work with partial messages 2', () => {
    expect(tree8).toMatchSnapshot()
app/components/base/chat/chat-with-history/chat-wrapper.tsx
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useCallback, useEffect, useMemo } from 'react'
import Chat from '../chat'
import type {
  ChatConfig,
@@ -9,20 +9,14 @@
import { useChat } from '../chat/hooks'
import { getLastAnswer, isValidGeneratedAnswer } from '../utils'
import { useChatWithHistoryContext } from './context'
import { InputVarType } from '@/app/components/workflow/types'
import { TransferMethod } from '@/types/app'
import InputsForm from '@/app/components/base/chat/chat-with-history/inputs-form'
import Header from './header'
import ConfigPanel from './config-panel'
import {
  fetchSuggestedQuestions,
  getUrl,
  stopChatMessageResponding,
} from '@/service/share'
import AppIcon from '@/app/components/base/app-icon'
import AnswerIcon from '@/app/components/base/answer-icon'
import SuggestedQuestions from '@/app/components/base/chat/chat/answer/suggested-questions'
import { Markdown } from '@/app/components/base/markdown'
import cn from '@/utils/classnames'
import type { FileEntity } from '../../file-uploader/types'
const ChatWrapper = () => {
  const {
@@ -30,10 +24,8 @@
    appPrevChatTree,
    currentConversationId,
    currentConversationItem,
    currentConversationInputs,
    inputsForms,
    newConversationInputs,
    newConversationInputsRef,
    handleNewConversationCompleted,
    isMobile,
    isInstalledApp,
@@ -43,10 +35,6 @@
    currentChatInstanceRef,
    appData,
    themeBuilder,
    sidebarCollapseState,
    clearChatList,
    setClearChatList,
    setIsResponding,
  } = useChatWithHistoryContext()
  const appConfig = useMemo(() => {
    const config = appParams || {}
@@ -66,51 +54,17 @@
    setTargetMessageId,
    handleSend,
    handleStop,
    isResponding: respondingState,
    isResponding,
    suggestedQuestions,
  } = useChat(
    appConfig,
    {
      inputs: (currentConversationId ? currentConversationInputs : newConversationInputs) as any,
      inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any,
      inputsForm: inputsForms,
    },
    appPrevChatTree,
    taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId),
    clearChatList,
    setClearChatList,
  )
  const inputsFormValue = currentConversationId ? currentConversationInputs : newConversationInputsRef?.current
  const inputDisabled = useMemo(() => {
    let hasEmptyInput = ''
    let fileIsUploading = false
    const requiredVars = inputsForms.filter(({ required }) => required)
    if (requiredVars.length) {
      requiredVars.forEach(({ variable, label, type }) => {
        if (hasEmptyInput)
          return
        if (fileIsUploading)
          return
        if (!inputsFormValue?.[variable])
          hasEmptyInput = label as string
        if ((type === InputVarType.singleFile || type === InputVarType.multiFiles) && inputsFormValue?.[variable]) {
          const files = inputsFormValue[variable]
          if (Array.isArray(files))
            fileIsUploading = files.find(item => item.transferMethod === TransferMethod.local_file && !item.uploadedId)
          else
            fileIsUploading = files.transferMethod === TransferMethod.local_file && !files.uploadedId
        }
      })
    }
    if (hasEmptyInput)
      return true
    if (fileIsUploading)
      return true
    return false
  }, [inputsFormValue, inputsForms])
  useEffect(() => {
    if (currentChatInstanceRef.current)
@@ -118,15 +72,11 @@
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])
  useEffect(() => {
    setIsResponding(respondingState)
  }, [respondingState, setIsResponding])
  const doSend: OnSend = useCallback((message, files, isRegenerate = false, parentAnswer: ChatItem | null = null) => {
    const data: any = {
      query: message,
      files,
      inputs: currentConversationId ? currentConversationInputs : newConversationInputs,
      inputs: currentConversationId ? currentConversationItem?.inputs : newConversationInputs,
      conversation_id: currentConversationId,
      parent_message_id: (isRegenerate ? parentAnswer?.id : getLastAnswer(chatList)?.id) || null,
    }
@@ -140,85 +90,59 @@
        isPublicAPI: !isInstalledApp,
      },
    )
  }, [chatList, handleNewConversationCompleted, handleSend, currentConversationId, currentConversationInputs, newConversationInputs, isInstalledApp, appId])
  }, [
    chatList,
    handleNewConversationCompleted,
    handleSend,
    currentConversationId,
    currentConversationItem,
    newConversationInputs,
    isInstalledApp,
    appId,
  ])
  const doRegenerate = useCallback((chatItem: ChatItemInTree, editedQuestion?: { message: string, files?: FileEntity[] }) => {
    const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)!
  const doRegenerate = useCallback((chatItem: ChatItemInTree) => {
    const question = chatList.find(item => item.id === chatItem.parentMessageId)!
    const parentAnswer = chatList.find(item => item.id === question.parentMessageId)
    doSend(editedQuestion ? editedQuestion.message : question.content,
      editedQuestion ? editedQuestion.files : question.message_files,
      true,
      isValidGeneratedAnswer(parentAnswer) ? parentAnswer : null,
    )
    doSend(question.content, question.message_files, true, isValidGeneratedAnswer(parentAnswer) ? parentAnswer : null)
  }, [chatList, doSend])
  const messageList = useMemo(() => {
    if (currentConversationId)
      return chatList
    return chatList.filter(item => !item.isOpeningStatement)
  }, [chatList, currentConversationId])
  const [collapsed, setCollapsed] = useState(!!currentConversationId)
  const chatNode = useMemo(() => {
    if (!inputsForms.length)
      return null
    if (isMobile) {
      if (!currentConversationId)
        return <InputsForm collapsed={collapsed} setCollapsed={setCollapsed} />
      return null
    if (inputsForms.length) {
      return (
        <>
          <Header
            isMobile={isMobile}
            title={currentConversationItem?.name || ''}
          />
          {
            !currentConversationId && (
              <div className={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}>
                <div className='mb-6' />
                <ConfigPanel />
                <div
                  className='my-6 h-[1px]'
                  style={{ background: 'linear-gradient(90deg, rgba(242, 244, 247, 0.00) 0%, #F2F4F7 49.17%, rgba(242, 244, 247, 0.00) 100%)' }}
                />
              </div>
            )
    }
    else {
      return <InputsForm collapsed={collapsed} setCollapsed={setCollapsed} />
        </>
      )
    }
  }, [inputsForms.length, isMobile, currentConversationId, collapsed])
  const welcome = useMemo(() => {
    const welcomeMessage = chatList.find(item => item.isOpeningStatement)
    if (respondingState)
      return null
    if (currentConversationId)
      return null
    if (!welcomeMessage)
      return null
    if (!collapsed && inputsForms.length > 0)
      return null
    if (welcomeMessage.suggestedQuestions && welcomeMessage.suggestedQuestions?.length > 0) {
      return (
        <div className='flex min-h-[50vh] items-center justify-center px-4 py-12'>
          <div className='flex max-w-[720px] grow gap-4'>
            <AppIcon
              size='xl'
              iconType={appData?.site.icon_type}
              icon={appData?.site.icon}
              background={appData?.site.icon_background}
              imageUrl={appData?.site.icon_url}
      <Header
        isMobile={isMobile}
        title={currentConversationItem?.name || ''}
            />
            <div className='w-0 grow'>
              <div className='body-lg-regular grow rounded-2xl bg-chat-bubble-bg px-4 py-3 text-text-primary'>
                <Markdown content={welcomeMessage.content} />
                <SuggestedQuestions item={welcomeMessage} />
              </div>
            </div>
          </div>
        </div>
      )
    }
    return (
      <div className={cn('flex h-[50vh] flex-col items-center justify-center gap-3 py-12')}>
        <AppIcon
          size='xl'
          iconType={appData?.site.icon_type}
          icon={appData?.site.icon}
          background={appData?.site.icon_background}
          imageUrl={appData?.site.icon_url}
        />
        <div className='max-w-[768px] px-4'>
          <Markdown className='!body-2xl-regular !text-text-tertiary' content={welcomeMessage.content} />
        </div>
      </div>
    )
  }, [appData?.site.icon, appData?.site.icon_background, appData?.site.icon_type, appData?.site.icon_url, chatList, collapsed, currentConversationId, inputsForms.length, respondingState])
  }, [
    currentConversationId,
    inputsForms,
    currentConversationItem,
    isMobile,
  ])
  const answerIcon = (appData?.site && appData.site.use_icon_as_answer_icon)
    ? <AnswerIcon
@@ -231,27 +155,22 @@
  return (
    <div
      className='h-full overflow-hidden bg-chatbot-bg'
      className='h-full bg-chatbot-bg overflow-hidden'
    >
      <Chat
        appData={appData}
        config={appConfig}
        chatList={messageList}
        isResponding={respondingState}
        chatContainerInnerClassName={`mx-auto pt-6 w-full max-w-[768px] ${isMobile && 'px-4'}`}
        chatList={chatList}
        isResponding={isResponding}
        chatContainerInnerClassName={`mx-auto pt-6 w-full max-w-[720px] ${isMobile && 'px-4'}`}
        chatFooterClassName='pb-4'
        chatFooterInnerClassName={`mx-auto w-full max-w-[768px] ${isMobile ? 'px-2' : 'px-4'}`}
        chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}
        onSend={doSend}
        inputs={currentConversationId ? currentConversationInputs as any : newConversationInputs}
        inputs={currentConversationId ? currentConversationItem?.inputs as any : newConversationInputs}
        inputsForm={inputsForms}
        onRegenerate={doRegenerate}
        onStopResponding={handleStop}
        chatNode={
          <>
            {chatNode}
            {welcome}
          </>
        }
        chatNode={chatNode}
        allToolIcons={appMeta?.tool_icons || {}}
        onFeedback={handleFeedback}
        suggestedQuestions={suggestedQuestions}
@@ -259,9 +178,6 @@
        hideProcessDetail
        themeBuilder={themeBuilder}
        switchSibling={siblingMessageId => setTargetMessageId(siblingMessageId)}
        inputDisabled={inputDisabled}
        isMobile={isMobile}
        sidebarCollapseState={sidebarCollapseState}
      />
    </div>
  )
app/components/base/chat/chat-with-history/config-panel/form-input.tsx
New file
@@ -0,0 +1,47 @@
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { memo } from 'react'
import Textarea from '@/app/components/base/textarea'
type InputProps = {
  form: any
  value: string
  onChange: (variable: string, value: string) => void
}
const FormInput: FC<InputProps> = ({
  form,
  value,
  onChange,
}) => {
  const { t } = useTranslation()
  const {
    type,
    label,
    required,
    max_length,
    variable,
  } = form
  if (type === 'paragraph') {
    return (
      <Textarea
        value={value}
        className='resize-none'
        onChange={e => onChange(variable, e.target.value)}
        placeholder={`${label}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
      />
    )
  }
  return (
    <input
      className='grow h-9 rounded-lg bg-gray-100 px-2.5 outline-none appearance-none'
      value={value || ''}
      maxLength={max_length}
      onChange={e => onChange(variable, e.target.value)}
      placeholder={`${label}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
    />
  )
}
export default memo(FormInput)
app/components/base/chat/chat-with-history/config-panel/form.tsx
New file
@@ -0,0 +1,117 @@
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useChatWithHistoryContext } from '../context'
import Input from './form-input'
import { PortalSelect } from '@/app/components/base/select'
import { InputVarType } from '@/app/components/workflow/types'
import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader'
const Form = () => {
  const { t } = useTranslation()
  const {
    appParams,
    inputsForms,
    newConversationInputs,
    newConversationInputsRef,
    handleNewConversationInputsChange,
    isMobile,
  } = useChatWithHistoryContext()
  const handleFormChange = useCallback((variable: string, value: any) => {
    handleNewConversationInputsChange({
      ...newConversationInputsRef.current,
      [variable]: value,
    })
  }, [newConversationInputsRef, handleNewConversationInputsChange])
  const renderField = (form: any) => {
    const {
      label,
      required,
      variable,
      options,
    } = form
    if (form.type === 'text-input' || form.type === 'paragraph') {
      return (
        <Input
          form={form}
          value={newConversationInputs[variable]}
          onChange={handleFormChange}
        />
      )
    }
    if (form.type === 'number') {
      return (
        <input
          className="grow h-9 rounded-lg bg-gray-100 px-2.5 outline-none appearance-none"
          type="number"
          value={newConversationInputs[variable] || ''}
          onChange={e => handleFormChange(variable, e.target.value)}
          placeholder={`${label}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
        />
      )
    }
    if (form.type === InputVarType.singleFile) {
      return (
        <FileUploaderInAttachmentWrapper
          value={newConversationInputs[variable] ? [newConversationInputs[variable]] : []}
          onChange={files => handleFormChange(variable, files[0])}
          fileConfig={{
            allowed_file_types: form.allowed_file_types,
            allowed_file_extensions: form.allowed_file_extensions,
            allowed_file_upload_methods: form.allowed_file_upload_methods,
            number_limits: 1,
            fileUploadConfig: (appParams as any).system_parameters,
          }}
        />
      )
    }
    if (form.type === InputVarType.multiFiles) {
      return (
        <FileUploaderInAttachmentWrapper
          value={newConversationInputs[variable]}
          onChange={files => handleFormChange(variable, files)}
          fileConfig={{
            allowed_file_types: form.allowed_file_types,
            allowed_file_extensions: form.allowed_file_extensions,
            allowed_file_upload_methods: form.allowed_file_upload_methods,
            number_limits: form.max_length,
            fileUploadConfig: (appParams as any).system_parameters,
          }}
        />
      )
    }
    return (
      <PortalSelect
        popupClassName='w-[200px]'
        value={newConversationInputs[variable]}
        items={options.map((option: string) => ({ value: option, name: option }))}
        onSelect={item => handleFormChange(variable, item.value as string)}
        placeholder={`${label}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
      />
    )
  }
  if (!inputsForms.length)
    return null
  return (
    <div className='mb-4 py-2'>
      {
        inputsForms.map(form => (
          <div
            key={form.variable}
            className={`flex mb-3 last-of-type:mb-0 text-sm text-gray-900 ${isMobile && '!flex-wrap'}`}
          >
            <div className={`shrink-0 mr-2 py-2 w-[128px] ${isMobile && '!w-full'}`}>{form.label}</div>
            {renderField(form)}
          </div>
        ))
      }
    </div>
  )
}
export default Form
app/components/base/chat/chat-with-history/config-panel/index.tsx
New file
@@ -0,0 +1,172 @@
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useChatWithHistoryContext } from '../context'
import Form from './form'
import Button from '@/app/components/base/button'
import AppIcon from '@/app/components/base/app-icon'
import { MessageDotsCircle } from '@/app/components/base/icons/src/vender/solid/communication'
import { Edit02 } from '@/app/components/base/icons/src/vender/line/general'
import { Star06 } from '@/app/components/base/icons/src/vender/solid/shapes'
import LogoSite from '@/app/components/base/logo/logo-site'
const ConfigPanel = () => {
  const { t } = useTranslation()
  const {
    appData,
    inputsForms,
    handleStartChat,
    showConfigPanelBeforeChat,
    isMobile,
  } = useChatWithHistoryContext()
  const [collapsed, setCollapsed] = useState(true)
  const customConfig = appData?.custom_config
  const site = appData?.site
  return (
    <div className='flex flex-col max-h-[80%] w-full max-w-[720px]'>
      <div
        className={`
          grow rounded-xl overflow-y-auto
          ${showConfigPanelBeforeChat && 'border-[0.5px] border-gray-100 shadow-lg'}
          ${!showConfigPanelBeforeChat && collapsed && 'border border-indigo-100'}
          ${!showConfigPanelBeforeChat && !collapsed && 'border-[0.5px] border-gray-100 shadow-lg'}
        `}
      >
        <div
          className={`
            flex flex-wrap px-6 py-4 rounded-t-xl bg-indigo-25
            ${isMobile && '!px-4 !py-3'}
          `}
        >
          {
            showConfigPanelBeforeChat && (
              <>
                <div className='flex items-center h-8 text-2xl font-semibold text-gray-800'>
                  <AppIcon
                    iconType={appData?.site.icon_type}
                    icon={appData?.site.icon}
                    background='transparent'
                    imageUrl={appData?.site.icon_url}
                    size='small'
                    className="mr-2"
                  />
                  {appData?.site.title}
                </div>
                {
                  appData?.site.description && (
                    <div className='mt-2 w-full text-sm text-gray-500'>
                      {appData?.site.description}
                    </div>
                  )
                }
              </>
            )
          }
          {
            !showConfigPanelBeforeChat && collapsed && (
              <>
                <Star06 className='mr-1 mt-1 w-4 h-4 text-indigo-600' />
                <div className='grow py-[3px] text-[13px] text-indigo-600 leading-[18px] font-medium'>
                  {t('share.chat.configStatusDes')}
                </div>
                <Button
                  variant='secondary-accent'
                  size='small'
                  className='shrink-0'
                  onClick={() => setCollapsed(false)}
                >
                  <Edit02 className='mr-1 w-3 h-3' />
                  {t('common.operation.edit')}
                </Button>
              </>
            )
          }
          {
            !showConfigPanelBeforeChat && !collapsed && (
              <>
                <Star06 className='mr-1 mt-1 w-4 h-4 text-indigo-600' />
                <div className='grow py-[3px] text-[13px] text-indigo-600 leading-[18px] font-medium'>
                  {t('share.chat.privatePromptConfigTitle')}
                </div>
              </>
            )
          }
        </div>
        {
          !collapsed && !showConfigPanelBeforeChat && (
            <div className='p-6 rounded-b-xl'>
              <Form />
              <div className={`pl-[136px] flex items-center ${isMobile && '!pl-0'}`}>
                <Button
                  variant='primary'
                  className='mr-2'
                  onClick={() => {
                    setCollapsed(true)
                    handleStartChat()
                  }}
                >
                  {t('common.operation.save')}
                </Button>
                <Button
                  onClick={() => setCollapsed(true)}
                >
                  {t('common.operation.cancel')}
                </Button>
              </div>
            </div>
          )
        }
        {
          showConfigPanelBeforeChat && (
            <div className='p-6 rounded-b-xl'>
              <Form />
              <Button
                className={`${inputsForms.length && !isMobile && 'ml-[136px]'}`}
                variant='primary'
                size='large'
                onClick={handleStartChat}
              >
                <MessageDotsCircle className='mr-2 w-4 h-4 text-white' />
                {t('share.chat.startChat')}
              </Button>
            </div>
          )
        }
      </div>
      {
        showConfigPanelBeforeChat && (site || customConfig) && (
          <div className='mt-4 flex flex-wrap justify-between items-center py-2 text-xs text-gray-400'>
            {site?.privacy_policy
              ? <div className={`flex items-center ${isMobile && 'w-full justify-end'}`}>{t('share.chat.privacyPolicyLeft')}
                <a
                  className='text-gray-500 px-1'
                  href={site?.privacy_policy}
                  target='_blank' rel='noopener noreferrer'>{t('share.chat.privacyPolicyMiddle')}</a>
                {t('share.chat.privacyPolicyRight')}
              </div>
              : <div>
              </div>}
            {
              customConfig?.remove_webapp_brand
                ? null
                : (
                  <div className={`flex items-center justify-end ${isMobile && 'w-full'}`}>
                    <div className='flex items-center pr-3 space-x-3'>
                      <span className='uppercase'>{t('share.chat.poweredBy')}</span>
                      {
                        customConfig?.replace_webapp_logo
                          ? <img src={customConfig?.replace_webapp_logo} alt='logo' className='block w-auto h-5' />
                          : <LogoSite className='!h-5' />
                      }
                    </div>
                  </div>
                )
            }
          </div>
        )
      }
    </div>
  )
}
export default ConfigPanel
app/components/base/chat/chat-with-history/context.tsx
@@ -15,7 +15,6 @@
  AppMeta,
  ConversationItem,
} from '@/models/share'
import { noop } from 'lodash-es'
export type ChatWithHistoryContextValue = {
  appInfoError?: any
@@ -29,12 +28,13 @@
  appPrevChatTree: ChatItemInTree[]
  pinnedConversationList: AppConversationData['data']
  conversationList: AppConversationData['data']
  showConfigPanelBeforeChat: boolean
  newConversationInputs: Record<string, any>
  newConversationInputsRef: RefObject<Record<string, any>>
  handleNewConversationInputsChange: (v: Record<string, any>) => void
  inputsForms: any[]
  handleNewConversation: () => void
  handleStartChat: (callback?: any) => void
  handleStartChat: () => void
  handleChangeConversation: (conversationId: string) => void
  handlePinConversation: (conversationId: string) => void
  handleUnpinConversation: (conversationId: string) => void
@@ -49,14 +49,6 @@
  handleFeedback: (messageId: string, feedback: Feedback) => void
  currentChatInstanceRef: RefObject<{ handleStop: () => void }>
  themeBuilder?: ThemeBuilder
  sidebarCollapseState?: boolean
  handleSidebarCollapse: (state: boolean) => void
  clearChatList?: boolean
  setClearChatList: (state: boolean) => void
  isResponding?: boolean
  setIsResponding: (state: boolean) => void,
  currentConversationInputs: Record<string, any> | null,
  setCurrentConversationInputs: (v: Record<string, any>) => void,
}
export const ChatWithHistoryContext = createContext<ChatWithHistoryContextValue>({
@@ -64,31 +56,24 @@
  appPrevChatTree: [],
  pinnedConversationList: [],
  conversationList: [],
  showConfigPanelBeforeChat: false,
  newConversationInputs: {},
  newConversationInputsRef: { current: {} },
  handleNewConversationInputsChange: noop,
  handleNewConversationInputsChange: () => {},
  inputsForms: [],
  handleNewConversation: noop,
  handleStartChat: noop,
  handleChangeConversation: noop,
  handlePinConversation: noop,
  handleUnpinConversation: noop,
  handleDeleteConversation: noop,
  handleNewConversation: () => {},
  handleStartChat: () => {},
  handleChangeConversation: () => {},
  handlePinConversation: () => {},
  handleUnpinConversation: () => {},
  handleDeleteConversation: () => {},
  conversationRenaming: false,
  handleRenameConversation: noop,
  handleNewConversationCompleted: noop,
  handleRenameConversation: () => {},
  handleNewConversationCompleted: () => {},
  chatShouldReloadKey: '',
  isMobile: false,
  isInstalledApp: false,
  handleFeedback: noop,
  currentChatInstanceRef: { current: { handleStop: noop } },
  sidebarCollapseState: false,
  handleSidebarCollapse: noop,
  clearChatList: false,
  setClearChatList: noop,
  isResponding: false,
  setIsResponding: noop,
  currentConversationInputs: {},
  setCurrentConversationInputs: noop,
  handleFeedback: () => {},
  currentChatInstanceRef: { current: { handleStop: () => {} } },
})
export const useChatWithHistoryContext = () => useContext(ChatWithHistoryContext)
app/components/base/chat/chat-with-history/header-in-mobile.tsx
@@ -1,77 +1,29 @@
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
  RiMenuLine,
} from '@remixicon/react'
import { useState } from 'react'
import { useChatWithHistoryContext } from './context'
import Operation from './header/operation'
import Sidebar from './sidebar'
import MobileOperationDropdown from './header/mobile-operation-dropdown'
import AppIcon from '@/app/components/base/app-icon'
import ActionButton from '@/app/components/base/action-button'
import { Message3Fill } from '@/app/components/base/icons/src/public/other'
import InputsFormContent from '@/app/components/base/chat/chat-with-history/inputs-form/content'
import Confirm from '@/app/components/base/confirm'
import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
import type { ConversationItem } from '@/models/share'
import {
  Edit05,
  Menu01,
} from '@/app/components/base/icons/src/vender/line/general'
const HeaderInMobile = () => {
  const {
    appData,
    currentConversationId,
    currentConversationItem,
    pinnedConversationList,
    handleNewConversation,
    handlePinConversation,
    handleUnpinConversation,
    handleDeleteConversation,
    handleRenameConversation,
    conversationRenaming,
    inputsForms,
  } = useChatWithHistoryContext()
  const { t } = useTranslation()
  const isPin = pinnedConversationList.some(item => item.id === currentConversationId)
  const [showConfirm, setShowConfirm] = useState<ConversationItem | null>(null)
  const [showRename, setShowRename] = useState<ConversationItem | null>(null)
  const handleOperate = useCallback((type: string) => {
    if (type === 'pin')
      handlePinConversation(currentConversationId)
    if (type === 'unpin')
      handleUnpinConversation(currentConversationId)
    if (type === 'delete')
      setShowConfirm(currentConversationItem as any)
    if (type === 'rename')
      setShowRename(currentConversationItem as any)
  }, [currentConversationId, currentConversationItem, handlePinConversation, handleUnpinConversation])
  const handleCancelConfirm = useCallback(() => {
    setShowConfirm(null)
  }, [])
  const handleDelete = useCallback(() => {
    if (showConfirm)
      handleDeleteConversation(showConfirm.id, { onSuccess: handleCancelConfirm })
  }, [showConfirm, handleDeleteConversation, handleCancelConfirm])
  const handleCancelRename = useCallback(() => {
    setShowRename(null)
  }, [])
  const handleRename = useCallback((newName: string) => {
    if (showRename)
      handleRenameConversation(showRename.id, newName, { onSuccess: handleCancelRename })
  }, [showRename, handleRenameConversation, handleCancelRename])
  const [showSidebar, setShowSidebar] = useState(false)
  const [showChatSettings, setShowChatSettings] = useState(false)
  return (
    <>
      <div className='flex shrink-0 items-center gap-1 bg-mask-top2bottom-gray-50-to-transparent px-2 py-3'>
        <ActionButton size='l' className='shrink-0' onClick={() => setShowSidebar(true)}>
          <RiMenuLine className='h-[18px] w-[18px]' />
        </ActionButton>
        <div className='flex grow items-center justify-center'>
          {!currentConversationId && (
            <>
      <div className='shrink-0 flex items-center px-3 h-[44px] border-b-[0.5px] border-b-gray-200'>
        <div
          className='shrink-0 flex items-center justify-center w-8 h-8 rounded-lg'
          onClick={() => setShowSidebar(true)}
        >
          <Menu01 className='w-4 h-4 text-gray-700' />
        </div>
        <div className='grow flex justify-center items-center px-3'>
              <AppIcon
                className='mr-2'
                size='tiny'
@@ -80,71 +32,29 @@
                imageUrl={appData?.site.icon_url}
                background={appData?.site.icon_background}
              />
              <div className='system-md-semibold truncate text-text-secondary'>
          <div className='py-1 text-base font-semibold text-gray-800 truncate'>
                {appData?.site.title}
              </div>
            </>
          )}
          {currentConversationId && (
            <Operation
              title={currentConversationItem?.name || ''}
              isPinned={!!isPin}
              togglePin={() => handleOperate(isPin ? 'unpin' : 'pin')}
              isShowDelete
              isShowRenameConversation
              onRenameConversation={() => handleOperate('rename')}
              onDelete={() => handleOperate('delete')}
            />
          )}
        </div>
        <MobileOperationDropdown
          handleResetChat={handleNewConversation}
          handleViewChatSettings={() => setShowChatSettings(true)}
          hideViewChatSettings={inputsForms.length < 1}
        />
        <div
          className='shrink-0 flex items-center justify-center w-8 h-8 rounded-lg'
          onClick={handleNewConversation}
        >
          <Edit05 className='w-4 h-4 text-gray-700' />
      </div>
      {showSidebar && (
        <div className='fixed inset-0 z-50 flex bg-background-overlay p-1'
      </div>
      {
        showSidebar && (
          <div className='fixed inset-0 z-50'
            style={{ backgroundColor: 'rgba(35, 56, 118, 0.2)' }}
          onClick={() => setShowSidebar(false)}
        >
          <div className='flex h-full w-[calc(100vw_-_40px)] rounded-xl bg-components-panel-bg shadow-lg backdrop-blur-sm' onClick={e => e.stopPropagation()}>
            <div className='inline-block h-full bg-white' onClick={e => e.stopPropagation()}>
            <Sidebar />
          </div>
        </div>
      )}
      {showChatSettings && (
        <div className='fixed inset-0 z-50 flex justify-end bg-background-overlay p-1'
          onClick={() => setShowChatSettings(false)}
        >
          <div className='flex h-full w-[calc(100vw_-_40px)] flex-col rounded-xl bg-components-panel-bg shadow-lg backdrop-blur-sm' onClick={e => e.stopPropagation()}>
            <div className='flex items-center gap-3 rounded-t-2xl border-b border-divider-subtle px-4 py-3'>
              <Message3Fill className='h-6 w-6 shrink-0' />
              <div className='system-xl-semibold grow text-text-secondary'>{t('share.chat.chatSettingsTitle')}</div>
            </div>
            <div className='p-4'>
              <InputsFormContent />
            </div>
          </div>
        </div>
      )}
      {!!showConfirm && (
        <Confirm
          title={t('share.chat.deleteConversation.title')}
          content={t('share.chat.deleteConversation.content') || ''}
          isShow
          onCancel={handleCancelConfirm}
          onConfirm={handleDelete}
        />
      )}
      {showRename && (
        <RenameModal
          isShow
          onClose={handleCancelRename}
          saveLoading={conversationRenaming}
          name={showRename?.name || ''}
          onSave={handleRename}
        />
      )}
        )
      }
    </>
  )
}
app/components/base/chat/chat-with-history/header.tsx
New file
@@ -0,0 +1,25 @@
import type { FC } from 'react'
import { memo } from 'react'
type HeaderProps = {
  title: string
  isMobile: boolean
}
const Header: FC<HeaderProps> = ({
  title,
  isMobile,
}) => {
  return (
    <div
      className={`
      sticky top-0 flex items-center px-8 h-16 bg-white/80 text-base font-medium
      text-gray-900 border-b-[0.5px] border-b-gray-100 backdrop-blur-md z-10
      ${isMobile && '!h-12'}
      `}
    >
      {title}
    </div>
  )
}
export default memo(Header)
app/components/base/chat/chat-with-history/hooks.tsx
@@ -16,7 +16,7 @@
  Feedback,
} from '../types'
import { CONVERSATION_ID_INFO } from '../constants'
import { buildChatItemTree, getProcessedSystemVariablesFromUrlParams } from '../utils'
import { buildChatItemTree } from '../utils'
import { addFileInfos, sortAgentSorts } from '../../../tools/utils'
import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
import {
@@ -42,7 +42,6 @@
import { useAppFavicon } from '@/hooks/use-app-favicon'
import { InputVarType } from '@/app/components/workflow/types'
import { TransferMethod } from '@/types/app'
import { noop } from 'lodash-es'
function getFormattedChatList(messages: any[]) {
  const newChatList: ChatItem[] = []
@@ -52,7 +51,7 @@
      id: `question-${item.id}`,
      content: item.query,
      isAnswer: false,
      message_files: getProcessedFilesFromResponse(questionFiles.map((item: any) => ({ ...item, related_id: item.id, upload_file_id: item.upload_file_id }))),
      message_files: getProcessedFilesFromResponse(questionFiles.map((item: any) => ({ ...item, related_id: item.id }))),
      parentMessageId: item.parent_message_id || undefined,
    })
    const answerFiles = item.message_files?.filter((file: any) => file.belongs_to === 'assistant') || []
@@ -63,7 +62,7 @@
      feedback: item.feedback,
      isAnswer: true,
      citation: item.retriever_resources,
      message_files: getProcessedFilesFromResponse(answerFiles.map((item: any) => ({ ...item, related_id: item.id, upload_file_id: item.upload_file_id }))),
      message_files: getProcessedFilesFromResponse(answerFiles.map((item: any) => ({ ...item, related_id: item.id }))),
      parentMessageId: `question-${item.id}`,
    })
  })
@@ -106,49 +105,24 @@
  }, [isInstalledApp, installedAppInfo, appInfo])
  const appId = useMemo(() => appData?.app_id, [appData])
  const [userId, setUserId] = useState<string>()
  useEffect(() => {
    getProcessedSystemVariablesFromUrlParams().then(({ user_id }) => {
      setUserId(user_id)
    })
  }, [])
  useEffect(() => {
    if (appData?.site.default_language)
      changeLanguage(appData.site.default_language)
  }, [appData])
  const [sidebarCollapseState, setSidebarCollapseState] = useState<boolean>(false)
  const handleSidebarCollapse = useCallback((state: boolean) => {
    if (appId) {
      setSidebarCollapseState(state)
      localStorage.setItem('webappSidebarCollapse', state ? 'collapsed' : 'expanded')
    }
  }, [appId, setSidebarCollapseState])
  useEffect(() => {
    if (appId) {
      const localState = localStorage.getItem('webappSidebarCollapse')
      setSidebarCollapseState(localState === 'collapsed')
    }
  }, [appId])
  const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<Record<string, Record<string, string>>>(CONVERSATION_ID_INFO, {
  const [conversationIdInfo, setConversationIdInfo] = useLocalStorageState<Record<string, string>>(CONVERSATION_ID_INFO, {
    defaultValue: {},
  })
  const currentConversationId = useMemo(() => conversationIdInfo?.[appId || '']?.[userId || 'DEFAULT'] || '', [appId, conversationIdInfo, userId])
  const currentConversationId = useMemo(() => conversationIdInfo?.[appId || ''] || '', [appId, conversationIdInfo])
  const handleConversationIdInfoChange = useCallback((changeConversationId: string) => {
    if (appId) {
      let prevValue = conversationIdInfo?.[appId || '']
      if (typeof prevValue === 'string')
        prevValue = {}
      setConversationIdInfo({
        ...conversationIdInfo,
        [appId || '']: {
          ...prevValue,
          [userId || 'DEFAULT']: changeConversationId,
        },
        [appId || '']: changeConversationId,
      })
    }
  }, [appId, conversationIdInfo, setConversationIdInfo, userId])
  }, [appId, conversationIdInfo, setConversationIdInfo])
  const [showConfigPanelBeforeChat, setShowConfigPanelBeforeChat] = useState(true)
  const [newConversationId, setNewConversationId] = useState('')
  const chatShouldReloadKey = useMemo(() => {
@@ -164,8 +138,6 @@
  const { data: appConversationData, isLoading: appConversationDataLoading, mutate: mutateAppConversationData } = useSWR(['appConversationData', isInstalledApp, appId, false], () => fetchConversations(isInstalledApp, appId, undefined, false, 100))
  const { data: appChatListData, isLoading: appChatListDataLoading } = useSWR(chatShouldReloadKey ? ['appChatList', chatShouldReloadKey, isInstalledApp, appId] : null, () => fetchChatList(chatShouldReloadKey, isInstalledApp, appId))
  const [clearChatList, setClearChatList] = useState(false)
  const [isResponding, setIsResponding] = useState(false)
  const appPrevChatTree = useMemo(
    () => (currentConversationId && appChatListData?.data.length)
      ? buildChatItemTree(getFormattedChatList(appChatListData.data))
@@ -277,17 +249,6 @@
    return conversationItem
  }, [conversationList, currentConversationId, pinnedConversationList])
  const currentConversationLatestInputs = useMemo(() => {
    if (!currentConversationId || !appChatListData?.data.length)
      return newConversationInputsRef.current || {}
    return appChatListData.data.slice().pop().inputs || {}
  }, [appChatListData, currentConversationId])
  const [currentConversationInputs, setCurrentConversationInputs] = useState<Record<string, any>>(currentConversationLatestInputs || {})
  useEffect(() => {
    if (currentConversationItem)
      setCurrentConversationInputs(currentConversationLatestInputs || {})
  }, [currentConversationItem, currentConversationLatestInputs])
  const { notify } = useToastContext()
  const checkInputsRequired = useCallback((silent?: boolean) => {
    let hasEmptyInput = ''
@@ -326,27 +287,37 @@
    return true
  }, [inputsForms, notify, t])
  const handleStartChat = useCallback((callback: any) => {
  const handleStartChat = useCallback(() => {
    if (checkInputsRequired()) {
      setShowConfigPanelBeforeChat(false)
      setShowNewConversationItemInList(true)
      callback?.()
    }
  }, [setShowNewConversationItemInList, checkInputsRequired])
  const currentChatInstanceRef = useRef<{ handleStop: () => void }>({ handleStop: noop })
  }, [setShowConfigPanelBeforeChat, setShowNewConversationItemInList, checkInputsRequired])
  const currentChatInstanceRef = useRef<{ handleStop: () => void }>({ handleStop: () => { } })
  const handleChangeConversation = useCallback((conversationId: string) => {
    currentChatInstanceRef.current.handleStop()
    setNewConversationId('')
    handleConversationIdInfoChange(conversationId)
    if (conversationId)
      setClearChatList(false)
  }, [handleConversationIdInfoChange, setClearChatList])
    if (conversationId === '' && !checkInputsRequired(true))
      setShowConfigPanelBeforeChat(true)
    else
      setShowConfigPanelBeforeChat(false)
  }, [handleConversationIdInfoChange, setShowConfigPanelBeforeChat, checkInputsRequired])
  const handleNewConversation = useCallback(() => {
    currentChatInstanceRef.current.handleStop()
    setShowNewConversationItemInList(true)
    setNewConversationId('')
    if (showNewConversationItemInList) {
    handleChangeConversation('')
    }
    else if (currentConversationId) {
      handleConversationIdInfoChange('')
      setShowConfigPanelBeforeChat(true)
      setShowNewConversationItemInList(true)
    handleNewConversationInputsChange({})
    setClearChatList(true)
  }, [handleChangeConversation, setShowNewConversationItemInList, handleNewConversationInputsChange, setClearChatList])
    }
  }, [handleChangeConversation, currentConversationId, handleConversationIdInfoChange, setShowConfigPanelBeforeChat, setShowNewConversationItemInList, showNewConversationItemInList, handleNewConversationInputsChange])
  const handleUpdateConversationList = useCallback(() => {
    mutateAppConversationData()
    mutateAppPinnedConversationData()
@@ -464,6 +435,8 @@
    appPrevChatTree,
    pinnedConversationList,
    conversationList,
    showConfigPanelBeforeChat,
    setShowConfigPanelBeforeChat,
    setShowNewConversationItemInList,
    newConversationInputs,
    newConversationInputsRef,
@@ -483,13 +456,5 @@
    chatShouldReloadKey,
    handleFeedback,
    currentChatInstanceRef,
    sidebarCollapseState,
    handleSidebarCollapse,
    clearChatList,
    setClearChatList,
    isResponding,
    setIsResponding,
    currentConversationInputs,
    setCurrentConversationInputs,
  }
}
app/components/base/chat/chat-with-history/index.tsx
@@ -11,15 +11,14 @@
} from './context'
import { useChatWithHistory } from './hooks'
import Sidebar from './sidebar'
import Header from './header'
import HeaderInMobile from './header-in-mobile'
import ConfigPanel from './config-panel'
import ChatWrapper from './chat-wrapper'
import type { InstalledApp } from '@/models/explore'
import Loading from '@/app/components/base/loading'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { checkOrSetAccessToken } from '@/app/components/share/utils'
import AppUnavailable from '@/app/components/base/app-unavailable'
import cn from '@/utils/classnames'
type ChatWithHistoryProps = {
  className?: string
@@ -31,17 +30,17 @@
    appInfoError,
    appData,
    appInfoLoading,
    appPrevChatTree,
    showConfigPanelBeforeChat,
    appChatListDataLoading,
    chatShouldReloadKey,
    isMobile,
    themeBuilder,
    sidebarCollapseState,
  } = useChatWithHistoryContext()
  const isSidebarCollapsed = sidebarCollapseState
  const chatReady = (!showConfigPanelBeforeChat || !!appPrevChatTree.length)
  const customConfig = appData?.custom_config
  const site = appData?.site
  const [showSidePanel, setShowSidePanel] = useState(false)
  useEffect(() => {
    themeBuilder?.buildTheme(site?.chat_color_theme, site?.chat_color_theme_inverted)
@@ -66,44 +65,35 @@
  }
  return (
    <div className={cn(
      'flex h-full bg-background-default-burn',
      isMobile && 'flex-col',
      className,
    )}>
      {!isMobile && (
        <div className={cn(
          'flex w-[236px] flex-col p-1 pr-0 transition-all duration-200 ease-in-out',
          isSidebarCollapsed && 'w-0 overflow-hidden !p-0',
        )}>
    <div className={`h-full flex bg-white ${className} ${isMobile && 'flex-col'}`}>
      {
        !isMobile && (
          <Sidebar />
        </div>
      )}
      {isMobile && (
        )
      }
      {
        isMobile && (
        <HeaderInMobile />
      )}
      <div className={cn('relative grow p-2', isMobile && 'h-[calc(100%_-_56px)] p-0')}>
        {isSidebarCollapsed && (
          <div
            className={cn(
              'absolute top-0 z-20 flex h-full w-[256px] flex-col p-2 transition-all duration-500 ease-in-out',
              showSidePanel ? 'left-0' : 'left-[-248px]',
            )}
            onMouseEnter={() => setShowSidePanel(true)}
            onMouseLeave={() => setShowSidePanel(false)}
          >
            <Sidebar isPanel />
        )
      }
      <div className={`grow overflow-hidden ${showConfigPanelBeforeChat && !appPrevChatTree.length && 'flex items-center justify-center'}`}>
        {
          showConfigPanelBeforeChat && !appChatListDataLoading && !appPrevChatTree.length && (
            <div className={`flex w-full items-center justify-center h-full ${isMobile && 'px-4'}`}>
              <ConfigPanel />
          </div>
        )}
        <div className={cn('flex h-full flex-col overflow-hidden border-[0,5px] border-components-panel-border-subtle bg-chatbot-bg', isMobile ? 'rounded-t-2xl' : 'rounded-2xl')}>
          {!isMobile && <Header />}
          {appChatListDataLoading && (
          )
        }
        {
          appChatListDataLoading && chatReady && (
            <Loading type='app' />
          )}
          {!appChatListDataLoading && (
          )
        }
        {
          chatReady && !appChatListDataLoading && (
            <ChatWrapper key={chatShouldReloadKey} />
          )}
        </div>
          )
        }
      </div>
    </div>
  )
@@ -133,6 +123,7 @@
    appPrevChatTree,
    pinnedConversationList,
    conversationList,
    showConfigPanelBeforeChat,
    newConversationInputs,
    newConversationInputsRef,
    handleNewConversationInputsChange,
@@ -151,14 +142,6 @@
    appId,
    handleFeedback,
    currentChatInstanceRef,
    sidebarCollapseState,
    handleSidebarCollapse,
    clearChatList,
    setClearChatList,
    isResponding,
    setIsResponding,
    currentConversationInputs,
    setCurrentConversationInputs,
  } = useChatWithHistory(installedAppInfo)
  return (
@@ -174,6 +157,7 @@
      appPrevChatTree,
      pinnedConversationList,
      conversationList,
      showConfigPanelBeforeChat,
      newConversationInputs,
      newConversationInputsRef,
      handleNewConversationInputsChange,
@@ -194,14 +178,6 @@
      handleFeedback,
      currentChatInstanceRef,
      themeBuilder,
      sidebarCollapseState,
      handleSidebarCollapse,
      clearChatList,
      setClearChatList,
      isResponding,
      setIsResponding,
      currentConversationInputs,
      setCurrentConversationInputs,
    }}>
      <ChatWithHistory className={className} />
    </ChatWithHistoryContext.Provider>
app/components/base/chat/chat-with-history/sidebar/index.tsx
@@ -3,34 +3,22 @@
  useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import {
  RiEditBoxLine,
  RiExpandRightLine,
  RiLayoutLeft2Line,
} from '@remixicon/react'
import { useChatWithHistoryContext } from '../context'
import List from './list'
import AppIcon from '@/app/components/base/app-icon'
import ActionButton from '@/app/components/base/action-button'
import Button from '@/app/components/base/button'
import List from '@/app/components/base/chat/chat-with-history/sidebar/list'
import MenuDropdown from '@/app/components/share/text-generation/menu-dropdown'
import { Edit05 } from '@/app/components/base/icons/src/vender/line/general'
import type { ConversationItem } from '@/models/share'
import Confirm from '@/app/components/base/confirm'
import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import type { ConversationItem } from '@/models/share'
import cn from '@/utils/classnames'
type Props = {
  isPanel?: boolean
}
const Sidebar = ({ isPanel }: Props) => {
const Sidebar = () => {
  const { t } = useTranslation()
  const {
    appData,
    handleNewConversation,
    pinnedConversationList,
    conversationList,
    handleNewConversation,
    currentConversationId,
    handleChangeConversation,
    handlePinConversation,
@@ -38,13 +26,8 @@
    conversationRenaming,
    handleRenameConversation,
    handleDeleteConversation,
    sidebarCollapseState,
    handleSidebarCollapse,
    isMobile,
    isResponding,
  } = useChatWithHistoryContext()
  const isSidebarCollapsed = sidebarCollapseState
  const [showConfirm, setShowConfirm] = useState<ConversationItem | null>(null)
  const [showRename, setShowRename] = useState<ConversationItem | null>(null)
@@ -77,43 +60,37 @@
  }, [showRename, handleRenameConversation, handleCancelRename])
  return (
    <div className={cn(
      'flex w-full grow flex-col',
      isPanel && 'rounded-xl border-[0.5px] border-components-panel-border-subtle bg-components-panel-bg shadow-lg',
    )}>
      <div className={cn(
        'flex shrink-0 items-center gap-3 p-3 pr-2',
      )}>
        <div className='shrink-0'>
    <div className='shrink-0 h-full flex flex-col w-[240px] border-r border-r-gray-100'>
      {
        !isMobile && (
          <div className='shrink-0 flex p-4'>
          <AppIcon
            size='large'
              className='mr-3'
              size='small'
            iconType={appData?.site.icon_type}
            icon={appData?.site.icon}
            background={appData?.site.icon_background}
            imageUrl={appData?.site.icon_url}
          />
            <div className='py-1 text-base font-semibold text-gray-800'>
              {appData?.site.title}
        </div>
        <div className={cn('system-md-semibold grow truncate text-text-secondary')}>{appData?.site.title}</div>
        {!isMobile && isSidebarCollapsed && (
          <ActionButton size='l' onClick={() => handleSidebarCollapse(false)}>
            <RiExpandRightLine className='h-[18px] w-[18px]' />
          </ActionButton>
        )}
        {!isMobile && !isSidebarCollapsed && (
          <ActionButton size='l' onClick={() => handleSidebarCollapse(true)}>
            <RiLayoutLeft2Line className='h-[18px] w-[18px]' />
          </ActionButton>
        )}
      </div>
      <div className='shrink-0 px-3 py-4'>
        <Button variant='secondary-accent' disabled={isResponding} className='w-full justify-center' onClick={handleNewConversation}>
          <RiEditBoxLine className='mr-1 h-4 w-4' />
        )
      }
      <div className='shrink-0 p-4'>
        <Button
          variant='secondary-accent'
          className='justify-start w-full'
          onClick={handleNewConversation}
        >
          <Edit05 className='mr-2 w-4 h-4' />
          {t('share.chat.newChat')}
        </Button>
      </div>
      <div className='h-0 grow space-y-2 overflow-y-auto px-3 pt-4'>
        {/* pinned list */}
        {!!pinnedConversationList.length && (
      <div className='grow px-4 py-2 overflow-y-auto'>
        {
          !!pinnedConversationList.length && (
          <div className='mb-4'>
            <List
              isPin
@@ -124,8 +101,10 @@
              currentConversationId={currentConversationId}
            />
          </div>
        )}
        {!!conversationList.length && (
          )
        }
        {
          !!conversationList.length && (
          <List
            title={(pinnedConversationList.length && t('share.chat.unpinnedTitle')) || ''}
            list={conversationList}
@@ -133,27 +112,14 @@
            onOperate={handleOperate}
            currentConversationId={currentConversationId}
          />
        )}
          )
        }
      </div>
      <div className='flex shrink-0 items-center justify-between p-3'>
        <MenuDropdown placement='top-start' data={appData?.site} />
        {/* powered by */}
        <div className='shrink-0'>
          {!appData?.custom_config?.remove_webapp_brand && (
            <div className={cn(
              'flex shrink-0 items-center gap-1.5 px-1',
            )}>
              <div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
              {appData?.custom_config?.replace_webapp_logo && (
                <img src={appData?.custom_config?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' />
              )}
              {!appData?.custom_config?.replace_webapp_logo && (
                <DifyLogo size='small' />
              )}
      {appData?.site.copyright && (
        <div className='px-4 pb-4 text-xs text-gray-400'>
          © {(new Date()).getFullYear()} {appData?.site.copyright}
            </div>
          )}
        </div>
      </div>
      {!!showConfirm && (
        <Confirm
          title={t('share.chat.deleteConversation.title')}
app/components/base/chat/chat-with-history/sidebar/item.tsx
@@ -5,8 +5,8 @@
} from 'react'
import { useHover } from 'ahooks'
import type { ConversationItem } from '@/models/share'
import Operation from '@/app/components/base/chat/chat-with-history/sidebar/operation'
import cn from '@/utils/classnames'
import { MessageDotsCircle } from '@/app/components/base/icons/src/vender/solid/communication'
import ItemOperation from '@/app/components/explore/item-operation'
type ItemProps = {
  isPin?: boolean
@@ -24,23 +24,23 @@
}) => {
  const ref = useRef(null)
  const isHovering = useHover(ref)
  const isSelected = currentConversationId === item.id
  return (
    <div
      ref={ref}
      key={item.id}
      className={cn(
        'system-sm-medium group flex cursor-pointer rounded-lg p-1 pl-3 text-components-menu-item-text hover:bg-state-base-hover',
        isSelected && 'bg-state-accent-active text-text-accent hover:bg-state-accent-active',
      )}
      className={`
        flex mb-0.5 last-of-type:mb-0 py-1.5 pl-3 pr-1.5 text-sm font-medium text-gray-700
        rounded-lg cursor-pointer hover:bg-gray-50 group
        ${currentConversationId === item.id && 'text-primary-600 bg-primary-50'}
      `}
      onClick={() => onChangeConversation(item.id)}
    >
      <div className='grow truncate p-1 pl-0' title={item.name}>{item.name}</div>
      <MessageDotsCircle className={`shrink-0 mt-1 mr-2 w-4 h-4 text-gray-400 ${currentConversationId === item.id && 'text-primary-600'}`} />
      <div className='grow py-0.5 break-all' title={item.name}>{item.name}</div>
      {item.id !== '' && (
        <div className='shrink-0' onClick={e => e.stopPropagation()}>
          <Operation
            isActive={isSelected}
        <div className='shrink-0 h-6' onClick={e => e.stopPropagation()}>
          <ItemOperation
            isPinned={!!isPin}
            isItemHovering={isHovering}
            togglePin={() => onOperate(isPin ? 'unpin' : 'pin', item)}
app/components/base/chat/chat-with-history/sidebar/list.tsx
@@ -19,11 +19,16 @@
  currentConversationId,
}) => {
  return (
    <div className='space-y-0.5'>
      {title && (
        <div className='system-xs-medium-uppercase px-3 pb-1 pt-2 text-text-tertiary'>{title}</div>
      )}
      {list.map(item => (
    <div>
      {
        title && (
          <div className='mb-0.5 px-3 h-[26px] text-xs font-medium text-gray-500'>
            {title}
          </div>
        )
      }
      {
        list.map(item => (
        <Item
          key={item.id}
          isPin={isPin}
@@ -32,7 +37,8 @@
          onChangeConversation={onChangeConversation}
          currentConversationId={currentConversationId}
        />
      ))}
        ))
      }
    </div>
  )
}
app/components/base/chat/chat-with-history/sidebar/rename-modal.tsx
@@ -4,7 +4,6 @@
import { useTranslation } from 'react-i18next'
import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
export type IRenameModalProps = {
  isShow: boolean
@@ -30,16 +29,16 @@
      isShow={isShow}
      onClose={onClose}
    >
      <div className={'mt-6 text-sm font-medium leading-[21px] text-text-primary'}>{t('common.chat.conversationName')}</div>
      <Input className='mt-2 h-10 w-full'
      <div className={'mt-6 font-medium text-sm leading-[21px] text-gray-900'}>{t('common.chat.conversationName')}</div>
      <input className={'mt-2 w-full rounded-lg h-10 box-border px-3 text-sm leading-10 bg-gray-100'}
        value={tempName}
        onChange={e => setTempName(e.target.value)}
        placeholder={t('common.chat.conversationNamePlaceholder') || ''}
      />
      <div className='mt-10 flex justify-end'>
        <Button className='mr-2 shrink-0' onClick={onClose}>{t('common.operation.cancel')}</Button>
        <Button variant='primary' className='shrink-0' onClick={() => onSave(tempName)} loading={saveLoading}>{t('common.operation.save')}</Button>
        <Button className='mr-2 flex-shrink-0' onClick={onClose}>{t('common.operation.cancel')}</Button>
        <Button variant='primary' className='flex-shrink-0' onClick={() => onSave(tempName)} loading={saveLoading}>{t('common.operation.save')}</Button>
      </div>
    </Modal>
  )
app/components/base/chat/chat/answer/__mocks__/markdownContentSVG.ts
app/components/base/chat/chat/answer/__mocks__/workflowProcess.ts
@@ -46,7 +46,6 @@
      parent_parallel_id: null,
      parent_parallel_start_node_id: null,
      iteration_id: null,
      loop_id: null,
    },
    {
      extras: {},
@@ -108,7 +107,6 @@
      parent_parallel_id: null,
      parent_parallel_start_node_id: null,
      iteration_id: null,
      loop_id: null,
    },
    {
      extras: {},
app/components/base/chat/chat/answer/agent-content.tsx
@@ -11,12 +11,10 @@
type AgentContentProps = {
  item: ChatItem
  responding?: boolean
  content?: string
}
const AgentContent: FC<AgentContentProps> = ({
  item,
  responding,
  content,
}) => {
  const {
    annotation,
@@ -28,7 +26,7 @@
  return (
    <div>
      {content ? <Markdown content={content} /> : agent_thoughts?.map((thought, index) => (
      {agent_thoughts?.map((thought, index) => (
        <div key={index} className='px-2 py-1'>
          {thought.thought && (
            <Markdown content={thought.thought} />
app/components/base/chat/chat/answer/index.tsx
@@ -2,7 +2,7 @@
  FC,
  ReactNode,
} from 'react'
import { memo, useCallback, useEffect, useRef, useState } from 'react'
import { memo, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import type {
  ChatConfig,
@@ -19,9 +19,9 @@
import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
import type { AppData } from '@/models/share'
import AnswerIcon from '@/app/components/base/answer-icon'
import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
import cn from '@/utils/classnames'
import { FileList } from '@/app/components/base/file-uploader'
import ContentSwitch from '../content-switch'
type AnswerProps = {
  item: ChatItem
@@ -100,28 +100,21 @@
    }
  }, [])
  const handleSwitchSibling = useCallback((direction: 'prev' | 'next') => {
    if (direction === 'prev')
      item.prevSibling && switchSibling?.(item.prevSibling)
    else
      item.nextSibling && switchSibling?.(item.nextSibling)
  }, [switchSibling, item.prevSibling, item.nextSibling])
  return (
    <div className='mb-2 flex last:mb-0'>
      <div className='relative h-10 w-10 shrink-0'>
    <div className='flex mb-2 last:mb-0'>
      <div className='shrink-0 relative w-10 h-10'>
        {answerIcon || <AnswerIcon />}
        {responding && (
          <div className='absolute left-[-3px] top-[-3px] flex h-4 w-4 items-center rounded-full border-[0.5px] border-divider-subtle bg-background-section-burn pl-[6px] shadow-xs'>
          <div className='absolute -top-[3px] -left-[3px] pl-[6px] flex items-center w-4 h-4 bg-white rounded-full shadow-xs border-[0.5px] border-gray-50'>
            <LoadingAnim type='avatar' />
          </div>
        )}
      </div>
      <div className='chat-answer-container group ml-4 w-0 grow pb-4' ref={containerRef}>
      <div className='chat-answer-container group grow w-0 ml-4' ref={containerRef}>
        <div className={cn('group relative pr-10', chatAnswerContainerInner)}>
          <div
            ref={contentRef}
            className={cn('body-lg-regular relative inline-block max-w-full rounded-2xl bg-chat-bubble-bg px-4 py-3 text-text-primary', workflowProcess && 'w-full')}
            className={cn('relative inline-block px-4 py-3 max-w-full bg-chat-bubble-bg rounded-2xl body-lg-regular text-text-primary', workflowProcess && 'w-full')}
          >
            {
              !responding && (
@@ -160,7 +153,7 @@
            }
            {
              responding && !content && !hasAgentThoughts && (
                <div className='flex h-5 w-6 items-center justify-center'>
                <div className='flex items-center justify-center w-6 h-5'>
                  <LoadingAnim type='text' />
                </div>
              )
@@ -171,11 +164,10 @@
              )
            }
            {
              (hasAgentThoughts) && (
              hasAgentThoughts && (
                <AgentContent
                  item={item}
                  responding={responding}
                  content={content}
                />
              )
            }
@@ -215,17 +207,23 @@
                <Citation data={citation} showHitInfo={config?.supportCitationHitInfo} />
              )
            }
            {
              item.siblingCount && item.siblingCount > 1 && item.siblingIndex !== undefined && (
                <ContentSwitch
                  count={item.siblingCount}
                  currentIndex={item.siblingIndex}
                  prevDisabled={!item.prevSibling}
                  nextDisabled={!item.nextSibling}
                  switchSibling={handleSwitchSibling}
                />
              )
            }
            {item.siblingCount && item.siblingCount > 1 && item.siblingIndex !== undefined && <div className="pt-3.5 flex justify-center items-center text-sm">
              <button
                className={`${item.prevSibling ? 'opacity-100' : 'opacity-30'}`}
                disabled={!item.prevSibling}
                onClick={() => item.prevSibling && switchSibling?.(item.prevSibling)}
              >
                <ChevronRight className="w-[14px] h-[14px] rotate-180 text-text-primary" />
              </button>
              <span className="px-2 text-xs text-text-primary">{item.siblingIndex + 1} / {item.siblingCount}</span>
              <button
                className={`${item.nextSibling ? 'opacity-100' : 'opacity-30'}`}
                disabled={!item.nextSibling}
                onClick={() => item.nextSibling && switchSibling?.(item.nextSibling)}
              >
                <ChevronRight className="w-[14px] h-[14px] text-text-primary" />
              </button>
            </div>}
          </div>
        </div>
        <More more={more} />
@@ -234,6 +232,4 @@
  )
}
export default memo(Answer, (prevProps, nextProps) =>
  prevProps.responding === false && nextProps.responding === false,
)
export default memo(Answer)
app/components/base/chat/chat/answer/more.tsx
@@ -13,25 +13,25 @@
  const { t } = useTranslation()
  return (
    <div className='system-xs-regular mt-1 flex items-center text-text-quaternary opacity-0 group-hover:opacity-100'>
    <div className='flex items-center mt-1 h-[18px] text-xs text-gray-400 opacity-0 group-hover:opacity-100'>
      {
        more && (
          <>
            <div
              className='mr-2 max-w-[33.3%] shrink-0 truncate'
              className='mr-2 shrink-0 truncate max-w-[33.3%]'
              title={`${t('appLog.detail.timeConsuming')} ${more.latency}${t('appLog.detail.second')}`}
            >
              {`${t('appLog.detail.timeConsuming')} ${more.latency}${t('appLog.detail.second')}`}
            </div>
            <div
              className='max-w-[33.3%] shrink-0 truncate'
              className='shrink-0 truncate max-w-[33.3%]'
              title={`${t('appLog.detail.tokenCost')} ${formatNumber(more.tokens)}`}
            >
              {`${t('appLog.detail.tokenCost')} ${formatNumber(more.tokens)}`}
            </div>
            <div className='mx-2 shrink-0'>·</div>
            <div className='shrink-0 mx-2'>·</div>
            <div
              className='max-w-[33.3%] shrink-0 truncate'
              className='shrink-0 truncate max-w-[33.3%]'
              title={more.time}
            >
              {more.time}
app/components/base/chat/chat/answer/operation.tsx
@@ -5,22 +5,21 @@
  useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import {
  RiClipboardLine,
  RiResetLeftLine,
  RiThumbDownLine,
  RiThumbUpLine,
} from '@remixicon/react'
import type { ChatItem } from '../../types'
import { useChatContext } from '../context'
import copy from 'copy-to-clipboard'
import Toast from '@/app/components/base/toast'
import AnnotationCtrlButton from '@/app/components/base/features/new-feature-panel/annotation-reply/annotation-ctrl-button'
import EditReplyModal from '@/app/components/app/annotation/edit-annotation-modal'
import Log from '@/app/components/base/chat/chat/log'
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
import NewAudioButton from '@/app/components/base/new-audio-button'
import RegenerateBtn from '@/app/components/base/regenerate-btn'
import cn from '@/utils/classnames'
import CopyBtn from '@/app/components/base/copy-btn'
import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
import AudioBtn from '@/app/components/base/audio-btn'
import AnnotationCtrlBtn from '@/app/components/base/features/new-feature-panel/annotation-reply/annotation-ctrl-btn'
import EditReplyModal from '@/app/components/app/annotation/edit-annotation-modal'
import {
  ThumbsDown,
  ThumbsUp,
} from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
import Tooltip from '@/app/components/base/tooltip'
import Log from '@/app/components/base/chat/chat/log'
type OperationProps = {
  item: ChatItem
@@ -61,6 +60,7 @@
    adminFeedback,
    agent_thoughts,
  } = item
  const hasAnnotation = !!annotation?.id
  const [localFeedback, setLocalFeedback] = useState(config?.supportAnnotation ? adminFeedback : feedback)
  const content = useMemo(() => {
@@ -81,13 +81,13 @@
  const operationWidth = useMemo(() => {
    let width = 0
    if (!isOpeningStatement)
      width += 26
      width += 28
    if (!isOpeningStatement && showPromptLog)
      width += 28 + 8
      width += 102 + 8
    if (!isOpeningStatement && config?.text_to_speech?.enabled)
      width += 26
      width += 33
    if (!isOpeningStatement && config?.supportAnnotation && config?.annotation_reply?.enabled)
      width += 26
      width += 56 + 8
    if (config?.supportFeedback && !localFeedback?.rating && onFeedback && !isOpeningStatement)
      width += 60 + 8
    if (config?.supportFeedback && localFeedback?.rating && onFeedback && !isOpeningStatement)
@@ -102,78 +102,121 @@
      <div
        className={cn(
          'absolute flex justify-end gap-1',
          hasWorkflowProcess && '-bottom-4 right-2',
          !positionRight && '-bottom-4 right-2',
          hasWorkflowProcess && '-top-3.5 -right-3.5',
          !positionRight && '-top-3.5 -right-3.5',
          !hasWorkflowProcess && positionRight && '!top-[9px]',
        )}
        style={(!hasWorkflowProcess && positionRight) ? { left: contentWidth + 8 } : {}}
      >
        {showPromptLog && !isOpeningStatement && (
          <div className='hidden group-hover:block'>
            <Log logItem={item} />
          </div>
        )}
        {!isOpeningStatement && (
          <div className='ml-1 hidden items-center gap-0.5 rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-md backdrop-blur-sm group-hover:flex'>
          <CopyBtn
            value={content}
            className='hidden group-hover:block'
          />
        )}
        {!isOpeningStatement && (showPromptLog || config?.text_to_speech?.enabled) && (
          <div className='hidden group-hover:flex items-center w-max h-[28px] p-0.5 rounded-lg bg-white border-[0.5px] border-gray-100 shadow-md shrink-0'>
            {showPromptLog && (
              <>
                <Log logItem={item} />
                <div className='mx-1 w-[1px] h-[14px] bg-gray-200' />
              </>
            )}
            {(config?.text_to_speech?.enabled) && (
              <NewAudioButton
              <>
                <AudioBtn
                id={id}
                value={content}
                  noCache={false}
                voice={config?.text_to_speech?.voice}
                  className='hidden group-hover:block'
              />
            )}
            <ActionButton onClick={() => {
              copy(content)
              Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') })
            }}>
              <RiClipboardLine className='h-4 w-4' />
            </ActionButton>
            {!noChatInput && (
              <ActionButton onClick={() => onRegenerate?.(item)}>
                <RiResetLeftLine className='h-4 w-4' />
              </ActionButton>
            )}
            {(config?.supportAnnotation && config.annotation_reply?.enabled) && (
              <AnnotationCtrlButton
                appId={config?.appId || ''}
                messageId={id}
                cached={!!annotation?.id}
                query={question}
                answer={content}
                onAdded={(id, authorName) => onAnnotationAdded?.(id, authorName, question, content, index)}
                onEdit={() => setIsShowReplyModal(true)}
              />
            )}
          </div>
        )}
        {!isOpeningStatement && config?.supportFeedback && !localFeedback?.rating && onFeedback && (
          <div className='ml-1 hidden items-center gap-0.5 rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-md backdrop-blur-sm group-hover:flex'>
            {!localFeedback?.rating && (
              <>
                <ActionButton onClick={() => handleFeedback('like')}>
                  <RiThumbUpLine className='h-4 w-4' />
                </ActionButton>
                <ActionButton onClick={() => handleFeedback('dislike')}>
                  <RiThumbDownLine className='h-4 w-4' />
                </ActionButton>
              </>
            )}
          </div>
        )}
        {!isOpeningStatement && config?.supportFeedback && localFeedback?.rating && onFeedback && (
          <div className='ml-1 flex items-center gap-0.5 rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-md backdrop-blur-sm'>
            {localFeedback?.rating === 'like' && (
              <ActionButton state={ActionButtonState.Active} onClick={() => handleFeedback(null)}>
                <RiThumbUpLine className='h-4 w-4' />
              </ActionButton>
        {(!isOpeningStatement && config?.supportAnnotation && config.annotation_reply?.enabled) && (
          <AnnotationCtrlBtn
            appId={config?.appId || ''}
            messageId={id}
            annotationId={annotation?.id || ''}
            className='hidden group-hover:block ml-1 shrink-0'
            cached={hasAnnotation}
            query={question}
            answer={content}
            onAdded={(id, authorName) => onAnnotationAdded?.(id, authorName, question, content, index)}
            onEdit={() => setIsShowReplyModal(true)}
            onRemoved={() => onAnnotationRemoved?.(index)}
          />
            )}
            {localFeedback?.rating === 'dislike' && (
              <ActionButton state={ActionButtonState.Destructive} onClick={() => handleFeedback(null)}>
                <RiThumbDownLine className='h-4 w-4' />
              </ActionButton>
            )}
        {
          annotation?.id && (
            <div
              className='relative box-border flex items-center justify-center h-7 w-7 p-0.5 rounded-lg bg-white cursor-pointer text-[#444CE7] shadow-md group-hover:hidden'
            >
              <div className='p-1 rounded-lg bg-[#EEF4FF] '>
                <MessageFast className='w-4 h-4' />
          </div>
        )}
            </div>
          )
        }
        {
          !isOpeningStatement && !noChatInput && <RegenerateBtn className='hidden group-hover:block mr-1' onClick={() => onRegenerate?.(item)} />
        }
        {
          config?.supportFeedback && !localFeedback?.rating && onFeedback && !isOpeningStatement && (
            <div className='hidden group-hover:flex shrink-0 items-center px-0.5 bg-white border-[0.5px] border-gray-100 shadow-md text-gray-500 rounded-lg'>
              <Tooltip popupContent={t('appDebug.operation.agree')}>
                <div
                  className='flex items-center justify-center mr-0.5 w-6 h-6 rounded-md hover:bg-black/5 hover:text-gray-800 cursor-pointer'
                  onClick={() => handleFeedback('like')}
                >
                  <ThumbsUp className='w-4 h-4' />
                </div>
              </Tooltip>
              <Tooltip
                popupContent={t('appDebug.operation.disagree')}
              >
                <div
                  className='flex items-center justify-center w-6 h-6 rounded-md hover:bg-black/5 hover:text-gray-800 cursor-pointer'
                  onClick={() => handleFeedback('dislike')}
                >
                  <ThumbsDown className='w-4 h-4' />
                </div>
              </Tooltip>
            </div>
          )
        }
        {
          config?.supportFeedback && localFeedback?.rating && onFeedback && !isOpeningStatement && (
            <Tooltip
              popupContent={localFeedback.rating === 'like' ? t('appDebug.operation.cancelAgree') : t('appDebug.operation.cancelDisagree')}
            >
              <div
                className={`
                  flex items-center justify-center w-7 h-7 rounded-[10px] border-[2px] border-white cursor-pointer
                  ${localFeedback.rating === 'like' && 'bg-blue-50 text-blue-600'}
                  ${localFeedback.rating === 'dislike' && 'bg-red-100 text-red-600'}
                `}
                onClick={() => handleFeedback(null)}
              >
                {
                  localFeedback.rating === 'like' && (
                    <ThumbsUp className='w-4 h-4' />
                  )
                }
                {
                  localFeedback.rating === 'dislike' && (
                    <ThumbsDown className='w-4 h-4' />
                  )
                }
              </div>
            </Tooltip>
          )
        }
      </div>
      <EditReplyModal
        isShow={isShowReplyModal}
app/components/base/chat/chat/answer/suggested-questions.tsx
@@ -10,7 +10,6 @@
  item,
}) => {
  const { onSend } = useChatContext()
  const {
    isOpeningStatement,
    suggestedQuestions,
@@ -24,7 +23,7 @@
      {suggestedQuestions.filter(q => !!q && q.trim()).map((question, index) => (
        <div
          key={index}
          className='system-sm-medium mr-1 mt-1 inline-flex max-w-full shrink-0 cursor-pointer flex-wrap rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-3.5 py-2 text-components-button-secondary-accent-text shadow-xs last:mr-0 hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover'
          className='mt-1 mr-1 max-w-full last:mr-0 shrink-0 py-[5px] leading-[18px] items-center px-4 rounded-lg border border-gray-200 shadow-xs bg-white text-xs font-medium text-primary-600 cursor-pointer'
          onClick={() => onSend?.(question)}
        >
          {question}
app/components/base/chat/chat/answer/tool-detail.tsx
@@ -30,34 +30,34 @@
    >
      <div
        className={cn(
          'system-xs-medium flex cursor-pointer items-center px-2.5 py-2 text-text-tertiary',
          'flex items-center system-xs-medium text-text-tertiary px-2.5 py-2 cursor-pointer',
          expand && 'pb-1.5',
        )}
        onClick={() => setExpand(!expand)}
      >
        {isFinished && <RiHammerFill className='mr-1 h-3.5 w-3.5' />}
        {!isFinished && <RiLoader2Line className='mr-1 h-3.5 w-3.5 animate-spin' />}
        {isFinished && <RiHammerFill className='mr-1 w-3.5 h-3.5' />}
        {!isFinished && <RiLoader2Line className='mr-1 w-3.5 h-3.5 animate-spin' />}
        {t(`tools.thought.${isFinished ? 'used' : 'using'}`)}
        <div className='mx-1 text-text-secondary'>{toolLabel}</div>
        {!expand && <RiArrowRightSLine className='h-4 w-4' />}
        {expand && <RiArrowDownSLine className='ml-auto h-4 w-4' />}
        {!expand && <RiArrowRightSLine className='w-4 h-4' />}
        {expand && <RiArrowDownSLine className='ml-auto w-4 h-4' />}
      </div>
      {
        expand && (
          <>
            <div className='mx-1 mb-0.5 rounded-[10px] bg-components-panel-on-panel-item-bg text-text-secondary'>
              <div className='system-xs-semibold-uppercase flex h-7 items-center justify-between px-2 pt-1'>
            <div className='mb-0.5 mx-1 rounded-[10px] bg-components-panel-on-panel-item-bg text-text-secondary'>
              <div className='flex items-center justify-between px-2 pt-1 h-7 system-xs-semibold-uppercase'>
                {t('tools.thought.requestTitle')}
              </div>
              <div className='code-xs-regular break-words px-3 pb-2 pt-1'>
              <div className='pt-1 px-3 pb-2 code-xs-regular break-words'>
                {input}
              </div>
            </div>
            <div className='mx-1 mb-1 rounded-[10px] bg-components-panel-on-panel-item-bg text-text-secondary'>
              <div className='system-xs-semibold-uppercase flex h-7 items-center justify-between px-2 pt-1'>
              <div className='flex items-center justify-between px-2 pt-1 h-7 system-xs-semibold-uppercase'>
                {t('tools.thought.responseTitle')}
              </div>
              <div className='code-xs-regular break-words px-3 pb-2 pt-1'>
              <div className='pt-1 px-3 pb-2 code-xs-regular break-words'>
                {output}
              </div>
            </div>
app/components/base/chat/chat/answer/workflow-process.tsx
@@ -1,5 +1,7 @@
import {
  useCallback,
  useEffect,
  useMemo,
  useState,
} from 'react'
import {
@@ -13,6 +15,7 @@
import cn from '@/utils/classnames'
import { CheckCircle } from '@/app/components/base/icons/src/vender/solid/general'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
import { useStore as useAppStore } from '@/app/components/app/store'
type WorkflowProcessProps = {
  data: WorkflowProcess
@@ -24,6 +27,7 @@
}
const WorkflowProcessItem = ({
  data,
  item,
  expand = false,
  hideInfo = false,
  hideProcessDetail = false,
@@ -35,44 +39,72 @@
  const succeeded = data.status === WorkflowRunningStatus.Succeeded
  const failed = data.status === WorkflowRunningStatus.Failed || data.status === WorkflowRunningStatus.Stopped
  const background = useMemo(() => {
    if (collapse)
      return 'linear-gradient(90deg, rgba(200, 206, 218, 0.20) 0%, rgba(200, 206, 218, 0.04) 100%)'
    if (running && !collapse)
      return 'linear-gradient(180deg, #E1E4EA 0%, #EAECF0 100%)'
    if (succeeded && !collapse)
      return 'linear-gradient(180deg, #ECFDF3 0%, #F6FEF9 100%)'
    if (failed && !collapse)
      return 'linear-gradient(180deg, #FEE4E2 0%, #FEF3F2 100%)'
  }, [running, succeeded, failed, collapse])
  useEffect(() => {
    setCollapse(!expand)
  }, [expand])
  const setCurrentLogItem = useAppStore(s => s.setCurrentLogItem)
  const setShowMessageLogModal = useAppStore(s => s.setShowMessageLogModal)
  const setCurrentLogModalActiveTab = useAppStore(s => s.setCurrentLogModalActiveTab)
  const showIterationDetail = useCallback(() => {
    setCurrentLogItem(item)
    setCurrentLogModalActiveTab('TRACING')
    setShowMessageLogModal(true)
  }, [item, setCurrentLogItem, setCurrentLogModalActiveTab, setShowMessageLogModal])
  const showRetryDetail = useCallback(() => {
    setCurrentLogItem(item)
    setCurrentLogModalActiveTab('TRACING')
    setShowMessageLogModal(true)
  }, [item, setCurrentLogItem, setCurrentLogModalActiveTab, setShowMessageLogModal])
  return (
    <div
      className={cn(
        '-mx-1 rounded-xl px-2.5',
        collapse ? 'border-l-[0.25px] border-components-panel-border py-[7px]' : 'border-[0.5px] border-components-panel-border-subtle px-1 pb-1 pt-[7px]',
        running && !collapse && 'bg-background-section-burn',
        succeeded && !collapse && 'bg-state-success-hover',
        failed && !collapse && 'bg-state-destructive-hover',
        collapse && 'bg-workflow-process-bg',
        '-mx-1 px-2.5 rounded-xl border-[0.5px]',
        collapse ? 'py-[7px] border-components-panel-border' : 'pt-[7px] px-1 pb-1 border-components-panel-border-subtle',
      )}
      style={{
        background,
      }}
    >
      <div
        className={cn('flex cursor-pointer items-center', !collapse && 'px-1.5', readonly && 'cursor-default')}
        className={cn('flex items-center cursor-pointer', !collapse && 'px-1.5', readonly && 'cursor-default')}
        onClick={() => !readonly && setCollapse(!collapse)}
      >
        {
          running && (
            <RiLoader2Line className='mr-1 h-3.5 w-3.5 shrink-0 animate-spin text-text-tertiary' />
            <RiLoader2Line className='shrink-0 mr-1 w-3.5 h-3.5 text-text-tertiary' />
          )
        }
        {
          succeeded && (
            <CheckCircle className='mr-1 h-3.5 w-3.5 shrink-0 text-text-success' />
            <CheckCircle className='shrink-0 mr-1 w-3.5 h-3.5 text-text-success' />
          )
        }
        {
          failed && (
            <RiErrorWarningFill className='mr-1 h-3.5 w-3.5 shrink-0 text-text-destructive' />
            <RiErrorWarningFill className='shrink-0 mr-1 w-3.5 h-3.5 text-text-destructive' />
          )
        }
        <div className={cn('system-xs-medium text-text-secondary', !collapse && 'grow')}>
          {t('workflow.common.workflowProcess')}
        </div>
        {!readonly && <RiArrowRightSLine className={cn('ml-1 h-4 w-4 text-text-tertiary', !collapse && 'rotate-90')} />}
        {!readonly && <RiArrowRightSLine className={`'ml-1 w-4 h-4 text-text-tertiary' ${collapse ? '' : 'rotate-90'}`} />}
      </div>
      {
        !collapse && !readonly && (
@@ -80,6 +112,8 @@
            {
              <TracingPanel
                list={data.tracing}
                onShowIterationDetail={showIterationDetail}
                onShowRetryDetail={showRetryDetail}
                hideNodeInfo={hideInfo}
                hideNodeProcessDetail={hideProcessDetail}
              />
app/components/base/chat/chat/chat-input-area/hooks.ts
@@ -3,22 +3,23 @@
  useRef,
  useState,
} from 'react'
import type { TextAreaRef } from 'rc-textarea'
export const useTextAreaHeight = () => {
  const wrapperRef = useRef<HTMLDivElement>(null)
  const textareaRef = useRef<HTMLTextAreaElement | undefined>(undefined)
  const textareaRef = useRef<TextAreaRef>(null)
  const textValueRef = useRef<HTMLDivElement>(null)
  const holdSpaceRef = useRef<HTMLDivElement>(null)
  const [isMultipleLine, setIsMultipleLine] = useState(false)
  const handleComputeHeight = useCallback(() => {
    const textareaElement = textareaRef.current
    const textareaElement = textareaRef.current?.resizableTextArea.textArea
    if (wrapperRef.current && textareaElement && textValueRef.current && holdSpaceRef.current) {
      const { width: wrapperWidth } = wrapperRef.current.getBoundingClientRect()
      const { height: textareaHeight } = textareaElement.getBoundingClientRect()
      const { width: textValueWidth } = textValueRef.current.getBoundingClientRect()
      const { width: holdSpaceWidth } = holdSpaceRef.current.getBoundingClientRect()
      if (textareaHeight > 32) {
        setIsMultipleLine(true)
      }
app/components/base/chat/chat/chat-input-area/index.tsx
@@ -3,7 +3,7 @@
  useRef,
  useState,
} from 'react'
import Textarea from 'react-textarea-autosize'
import Textarea from 'rc-textarea'
import { useTranslation } from 'react-i18next'
import Recorder from 'js-audio-recorder'
import type {
@@ -40,7 +40,6 @@
  inputsForm?: InputForm[]
  theme?: Theme | null
  isResponding?: boolean
  disabled?: boolean
}
const ChatInputArea = ({
  showFeatureBar,
@@ -54,7 +53,6 @@
  inputsForm = [],
  theme,
  isResponding,
  disabled,
}: ChatInputAreaProps) => {
  const { t } = useTranslation()
  const { notify } = useToastContext()
@@ -80,7 +78,6 @@
  const { checkInputsForm } = useCheckInputsForms()
  const historyRef = useRef([''])
  const [currentIndex, setCurrentIndex] = useState(-1)
  const isComposingRef = useRef(false)
  const handleSend = () => {
    if (isResponding) {
      notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') })
@@ -104,21 +101,8 @@
      }
    }
  }
  const handleCompositionStart = () => {
    // e: React.CompositionEvent<HTMLTextAreaElement>
    isComposingRef.current = true
  }
  const handleCompositionEnd = () => {
    // safari or some browsers will trigger compositionend before keydown.
    // delay 50ms for safari.
    setTimeout(() => {
      isComposingRef.current = false
    }, 50)
  }
  const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
    if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) {
      // if isComposing, exit
      if (isComposingRef.current) return
      e.preventDefault()
      setQuery(query.replace(/\n$/, ''))
      historyRef.current.push(query)
@@ -169,41 +153,38 @@
    <>
      <div
        className={cn(
          'relative z-10 rounded-xl border border-components-chat-input-border bg-components-panel-bg-blur pb-[9px] shadow-md',
          'relative pb-[9px] bg-components-panel-bg-blur border border-components-chat-input-border rounded-xl shadow-md z-10',
          isDragActive && 'border border-dashed border-components-option-card-option-selected-border',
          disabled && 'pointer-events-none border-components-panel-border opacity-50 shadow-none',
        )}
      >
        <div className='relative max-h-[158px] overflow-y-auto overflow-x-hidden px-[9px] pt-[9px]'>
        <div className='relative px-[9px] pt-[9px] max-h-[158px] overflow-x-hidden overflow-y-auto'>
          <FileListInChatInput fileConfig={visionConfig!} />
          <div
            ref={wrapperRef}
            className='flex items-center justify-between'
          >
            <div className='relative flex w-full grow items-center'>
            <div className='flex items-center relative grow w-full'>
              <div
                ref={textValueRef}
                className='body-lg-regular pointer-events-none invisible absolute h-auto w-auto whitespace-pre p-1 leading-6'
                className='absolute w-auto h-auto p-1 leading-6 body-lg-regular pointer-events-none whitespace-pre invisible'
              >
                {query}
              </div>
              <Textarea
                ref={ref => textareaRef.current = ref as any}
                ref={textareaRef}
                className={cn(
                  'body-lg-regular w-full resize-none bg-transparent p-1 leading-6 text-text-tertiary outline-none',
                  'p-1 w-full leading-6 body-lg-regular text-text-tertiary outline-none',
                )}
                placeholder={t('common.chat.inputPlaceholder') || ''}
                autoFocus
                minRows={1}
                autoSize={{ minRows: 1 }}
                onResize={handleTextareaResize}
                value={query}
                onChange={(e) => {
                  setQuery(e.target.value)
                  setTimeout(handleTextareaResize, 0)
                  handleTextareaResize()
                }}
                onKeyDown={handleKeyDown}
                onCompositionStart={handleCompositionStart}
                onCompositionEnd={handleCompositionEnd}
                onPaste={handleClipboardPasteFile}
                onDragEnter={handleDragFileEnter}
                onDragLeave={handleDragFileLeave}
app/components/base/chat/chat/chat-input-area/operation.tsx
@@ -1,4 +1,7 @@
import { memo } from 'react'
import {
  forwardRef,
  memo,
} from 'react'
import {
  RiMicLine,
  RiSendPlane2Fill,
@@ -20,22 +23,17 @@
  onSend: () => void
  theme?: Theme | null
}
const Operation = (
  {
    ref,
const Operation = forwardRef<HTMLDivElement, OperationProps>(({
    fileConfig,
    speechToTextConfig,
    onShowVoiceInput,
    onSend,
    theme,
  }: OperationProps & {
    ref: React.RefObject<HTMLDivElement>;
  },
) => {
}, ref) => {
  return (
    <div
      className={cn(
        'flex shrink-0 items-center justify-end',
        'shrink-0 flex items-center justify-end',
      )}
    >
      <div
@@ -50,13 +48,13 @@
                size='l'
                onClick={onShowVoiceInput}
              >
                <RiMicLine className='h-5 w-5' />
                <RiMicLine className='w-5 h-5' />
              </ActionButton>
            )
          }
        </div>
        <Button
          className='ml-3 w-8 px-0'
          className='ml-3 px-0 w-8'
          variant='primary'
          onClick={onSend}
          style={
@@ -67,12 +65,12 @@
              : {}
          }
        >
          <RiSendPlane2Fill className='h-4 w-4' />
          <RiSendPlane2Fill className='w-4 h-4' />
        </Button>
      </div>
    </div>
  )
}
})
Operation.displayName = 'Operation'
export default memo(Operation)
app/components/base/chat/chat/citation/index.tsx
@@ -76,18 +76,18 @@
  const resourcesLength = resources.length
  return (
    <div className='-mb-1 mt-3'>
      <div className='system-xs-medium mb-2 flex items-center text-text-tertiary'>
    <div className='mt-3 -mb-1'>
      <div className='flex items-center mb-2 text-xs font-medium text-gray-500'>
        {t('common.chat.citation.title')}
        <div className='ml-2 h-[1px] grow bg-divider-regular' />
        <div className='grow ml-2 h-[1px] bg-black/5' />
      </div>
      <div className='relative flex flex-wrap'>
        {
          resources.map((res, index) => (
            <div
              key={index}
              className='absolute left-0 top-0 -z-10 mb-1 mr-1 h-7 w-auto max-w-[240px] whitespace-nowrap pl-7 pr-2 text-xs opacity-0'
              ref={(ele: any) => (elesRef.current[index] = ele!)}
              className='absolute top-0 left-0 w-auto mr-1 mb-1 pl-7 pr-2 max-w-[240px] h-7 text-xs whitespace-nowrap opacity-0 -z-10'
              ref={ele => (elesRef.current[index] = ele!)}
            >
              {res.documentName}
            </div>
@@ -95,7 +95,7 @@
        }
        {
          resources.slice(0, showMore ? resourcesLength : limitNumberInOneLine).map((res, index) => (
            <div key={index} className='mb-1 mr-1 cursor-pointer'>
            <div key={index} className='mr-1 mb-1 cursor-pointer'>
              <Popup
                data={res}
                showHitInfo={showHitInfo}
@@ -106,13 +106,13 @@
        {
          limitNumberInOneLine < resourcesLength && (
            <div
              className='system-xs-medium flex h-7 cursor-pointer items-center rounded-lg bg-components-panel-bg px-2 text-text-tertiary'
              className='flex items-center px-2 h-7 bg-white rounded-lg text-xs font-medium text-gray-500 cursor-pointer'
              onClick={() => setShowMore(v => !v)}
            >
              {
                !showMore
                  ? `+ ${resourcesLength - limitNumberInOneLine}`
                  : <RiArrowDownSLine className='h-4 w-4 rotate-180 text-text-tertiary' />
                  : <RiArrowDownSLine className='w-4 h-4 text-gray-600 rotate-180' />
              }
            </div>
          )
app/components/base/chat/chat/citation/popup.tsx
@@ -47,29 +47,29 @@
      }}
    >
      <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
        <div className='flex h-7 max-w-[240px] items-center rounded-lg bg-components-button-secondary-bg px-2'>
          <FileIcon type={fileType} className='mr-1 h-4 w-4 shrink-0' />
          <div className='truncate text-xs text-text-tertiary'>{data.documentName}</div>
        <div className='flex items-center px-2 max-w-[240px] h-7 bg-white rounded-lg'>
          <FileIcon type={fileType} className='shrink-0 mr-1 w-4 h-4' />
          <div className='text-xs text-gray-600 truncate'>{data.documentName}</div>
        </div>
      </PortalToFollowElemTrigger>
      <PortalToFollowElemContent style={{ zIndex: 1000 }}>
        <div className='max-w-[360px] rounded-xl bg-background-section-burn shadow-lg'>
          <div className='px-4 pb-2 pt-3'>
            <div className='flex h-[18px] items-center'>
              <FileIcon type={fileType} className='mr-1 h-4 w-4 shrink-0' />
              <div className='system-xs-medium truncate text-text-tertiary'>{data.documentName}</div>
        <div className='max-w-[360px] bg-gray-50 rounded-xl shadow-lg'>
          <div className='px-4 pt-3 pb-2'>
            <div className='flex items-center h-[18px]'>
              <FileIcon type={fileType} className='shrink-0 mr-1 w-4 h-4' />
              <div className='text-xs font-medium text-gray-600 truncate'>{data.documentName}</div>
            </div>
          </div>
          <div className='max-h-[450px] overflow-y-auto rounded-lg bg-components-panel-bg px-4 py-0.5'>
          <div className='px-4 py-0.5 max-h-[450px] bg-white rounded-lg overflow-y-auto'>
            <div className='w-full'>
              {
                data.sources.map((source, index) => (
                  <Fragment key={index}>
                    <div className='group py-3'>
                      <div className='mb-2 flex items-center justify-between'>
                        <div className='flex h-5 items-center rounded-md border border-divider-subtle px-1.5'>
                          <Hash02 className='mr-0.5 h-3 w-3 text-text-quaternary' />
                          <div className='text-[11px] font-medium text-text-tertiary'>
                      <div className='flex items-center justify-between mb-2'>
                        <div className='flex items-center px-1.5 h-5 border border-gray-200 rounded-md'>
                          <Hash02 className='mr-0.5 w-3 h-3 text-gray-400' />
                          <div className='text-[11px] font-medium text-gray-500'>
                            {source.segment_position || index + 1}
                          </div>
                        </div>
@@ -77,31 +77,31 @@
                          showHitInfo && (
                            <Link
                              href={`/datasets/${source.dataset_id}/documents/${source.document_id}`}
                              className='hidden h-[18px] items-center text-xs text-text-accent group-hover:flex'>
                              className='hidden items-center h-[18px] text-xs text-primary-600 group-hover:flex'>
                              {t('common.chat.citation.linkToDataset')}
                              <ArrowUpRight className='ml-1 h-3 w-3' />
                              <ArrowUpRight className='ml-1 w-3 h-3' />
                            </Link>
                          )
                        }
                      </div>
                      <div className='break-words text-[13px] text-text-secondary'>{source.content}</div>
                      <div className='text-[13px] text-gray-800 break-words'>{source.content}</div>
                      {
                        showHitInfo && (
                          <div className='system-xs-medium mt-2 flex flex-wrap items-center text-text-quaternary'>
                          <div className='flex items-center mt-2 text-xs font-medium text-gray-500 flex-wrap'>
                            <Tooltip
                              text={t('common.chat.citation.characters')}
                              data={source.word_count}
                              icon={<TypeSquare className='mr-1 h-3 w-3' />}
                              icon={<TypeSquare className='mr-1 w-3 h-3' />}
                            />
                            <Tooltip
                              text={t('common.chat.citation.hitCount')}
                              data={source.hit_count}
                              icon={<Target04 className='mr-1 h-3 w-3' />}
                              icon={<Target04 className='mr-1 w-3 h-3' />}
                            />
                            <Tooltip
                              text={t('common.chat.citation.vectorHash')}
                              data={source.index_node_hash?.substring(0, 7)}
                              icon={<BezierCurve03 className='mr-1 h-3 w-3' />}
                              icon={<BezierCurve03 className='mr-1 w-3 h-3' />}
                            />
                            {
                              source.score && (
@@ -114,7 +114,7 @@
                    </div>
                    {
                      index !== data.sources.length - 1 && (
                        <div className='my-1 h-[1px] bg-divider-regular' />
                        <div className='my-1 h-[1px] bg-black/5' />
                      )
                    }
                  </Fragment>
app/components/base/chat/chat/citation/progress-tooltip.tsx
@@ -27,15 +27,15 @@
        onMouseEnter={() => setOpen(true)}
        onMouseLeave={() => setOpen(false)}
      >
        <div className='flex grow items-center'>
          <div className='mr-1 h-1.5 w-16 overflow-hidden rounded-[3px] border border-components-progress-gray-border'>
            <div className='h-full bg-components-progress-gray-progress' style={{ width: `${data * 100}%` }}></div>
        <div className='grow flex items-center'>
          <div className='mr-1 w-16 h-1.5 rounded-[3px] border border-gray-400 overflow-hidden'>
            <div className='bg-gray-400 h-full' style={{ width: `${data * 100}%` }}></div>
          </div>
          {data}
        </div>
      </PortalToFollowElemTrigger>
      <PortalToFollowElemContent style={{ zIndex: 1001 }}>
        <div className='system-xs-medium rounded-lg bg-components-tooltip-bg p-3 text-text-quaternary shadow-lg'>
        <div className='p-3 bg-white text-xs font-medium text-gray-500 rounded-lg shadow-lg'>
          {t('common.chat.citation.hitScore')} {data}
        </div>
      </PortalToFollowElemContent>
app/components/base/chat/chat/citation/tooltip.tsx
@@ -29,13 +29,13 @@
        onMouseEnter={() => setOpen(true)}
        onMouseLeave={() => setOpen(false)}
      >
        <div className='mr-6 flex items-center'>
        <div className='flex items-center mr-6'>
          {icon}
          {data}
        </div>
      </PortalToFollowElemTrigger>
      <PortalToFollowElemContent style={{ zIndex: 1001 }}>
        <div className='system-xs-medium rounded-lg bg-components-tooltip-bg p-3 text-text-quaternary shadow-lg'>
        <div className='p-3 bg-white text-xs font-medium text-gray-500 rounded-lg shadow-lg'>
          {text} {data}
        </div>
      </PortalToFollowElemContent>
app/components/base/chat/chat/hooks.ts
@@ -34,7 +34,6 @@
  getProcessedFiles,
  getProcessedFilesFromResponse,
} from '@/app/components/base/file-uploader/utils'
import { noop } from 'lodash-es'
type GetAbortController = (abortController: AbortController) => void
type SendCallback = {
@@ -52,8 +51,6 @@
  },
  prevChatTree?: ChatItemInTree[],
  stopChat?: (taskId: string) => void,
  clearChatList?: boolean,
  clearChatListCallback?: (state: boolean) => void,
) => {
  const { t } = useTranslation()
  const { formatTime } = useTimestamp()
@@ -93,7 +90,7 @@
      }
      else {
        ret.unshift({
          id: 'opening-statement',
          id: `${Date.now()}`,
          content: getIntroduction(config.opening_statement),
          isAnswer: true,
          isOpeningStatement: true,
@@ -166,13 +163,12 @@
      suggestedQuestionsAbortControllerRef.current.abort()
  }, [stopChat, handleResponding])
  const handleRestart = useCallback((cb?: any) => {
  const handleRestart = useCallback(() => {
    conversationId.current = ''
    taskIdRef.current = ''
    handleStop()
    setChatTree([])
    setSuggestQuestions([])
    cb?.()
  }, [handleStop])
  const updateCurrentQAOnTree = useCallback(({
@@ -309,7 +305,7 @@
      else
        ttsUrl = `/apps/${params.appId}/text-to-audio`
    }
    const player = AudioPlayerManager.getInstance().getAudioPlayer(ttsUrl, ttsIsPublic, uuidV4(), 'none', 'none', noop)
    const player = AudioPlayerManager.getInstance().getAudioPlayer(ttsUrl, ttsIsPublic, uuidV4(), 'none', 'none', (_: any): any => {})
    ssePost(
      url,
      {
@@ -401,7 +397,6 @@
              )
              setSuggestQuestions(data)
            }
            // eslint-disable-next-line unused-imports/no-unused-vars
            catch (e) {
              setSuggestQuestions([])
            }
@@ -424,8 +419,6 @@
          const response = responseItem as any
          if (thought.message_id && !hasSetResponseId)
            response.id = thought.message_id
          if (thought.conversation_id)
            response.conversationId = thought.conversation_id
          if (response.agent_thoughts.length === 0) {
            response.agent_thoughts.push(thought)
@@ -514,7 +507,7 @@
          responseItem.workflowProcess!.tracing!.push({
            ...iterationStartedData,
            status: WorkflowRunningStatus.Running,
          })
          } as any)
          updateCurrentQAOnTree({
            placeholderQuestionId,
            questionItem,
@@ -530,7 +523,7 @@
            ...tracing[iterationIndex],
            ...iterationFinishedData,
            status: WorkflowRunningStatus.Succeeded,
          }
          } as any
          updateCurrentQAOnTree({
            placeholderQuestionId,
@@ -543,13 +536,10 @@
          if (nodeStartedData.iteration_id)
            return
          if (data.loop_id)
            return
          responseItem.workflowProcess!.tracing!.push({
            ...nodeStartedData,
            status: WorkflowRunningStatus.Running,
          })
          } as any)
          updateCurrentQAOnTree({
            placeholderQuestionId,
            questionItem,
@@ -561,14 +551,11 @@
          if (nodeFinishedData.iteration_id)
            return
          if (data.loop_id)
            return
          const currentIndex = responseItem.workflowProcess!.tracing!.findIndex((item) => {
            if (!item.execution_metadata?.parallel_id)
              return item.node_id === nodeFinishedData.node_id
            return item.node_id === nodeFinishedData.node_id && (item.execution_metadata?.parallel_id === nodeFinishedData.execution_metadata?.parallel_id)
            return item.node_id === nodeFinishedData.node_id && (item.execution_metadata?.parallel_id === nodeFinishedData.execution_metadata.parallel_id)
          })
          responseItem.workflowProcess!.tracing[currentIndex] = nodeFinishedData as any
@@ -587,35 +574,6 @@
        },
        onTTSEnd: (messageId: string, audio: string) => {
          player.playAudioWithAudio(audio, false)
        },
        onLoopStart: ({ data: loopStartedData }) => {
          responseItem.workflowProcess!.tracing!.push({
            ...loopStartedData,
            status: WorkflowRunningStatus.Running,
          })
          updateCurrentQAOnTree({
            placeholderQuestionId,
            questionItem,
            responseItem,
            parentId: data.parent_message_id,
          })
        },
        onLoopFinish: ({ data: loopFinishedData }) => {
          const tracing = responseItem.workflowProcess!.tracing!
          const loopIndex = tracing.findIndex(item => item.node_id === loopFinishedData.node_id
            && (item.execution_metadata?.parallel_id === loopFinishedData.execution_metadata?.parallel_id || item.parallel_id === loopFinishedData.execution_metadata?.parallel_id))!
          tracing[loopIndex] = {
            ...tracing[loopIndex],
            ...loopFinishedData,
            status: WorkflowRunningStatus.Succeeded,
          }
          updateCurrentQAOnTree({
            placeholderQuestionId,
            questionItem,
            responseItem,
            parentId: data.parent_message_id,
          })
        },
      })
    return true
@@ -687,11 +645,6 @@
      } as Annotation,
    })
  }, [chatList, updateChatTreeNode])
  useEffect(() => {
    if (clearChatList)
      handleRestart(() => clearChatListCallback?.(false))
  }, [clearChatList, clearChatListCallback, handleRestart])
  return {
    chatList,
app/components/base/chat/chat/index.tsx
@@ -70,9 +70,6 @@
  showFileUpload?: boolean
  onFeatureBarClick?: (state: boolean) => void
  noSpacing?: boolean
  inputDisabled?: boolean
  isMobile?: boolean
  sidebarCollapseState?: boolean
}
const Chat: FC<ChatProps> = ({
@@ -109,9 +106,6 @@
  showFileUpload,
  onFeatureBarClick,
  noSpacing,
  inputDisabled,
  isMobile,
  sidebarCollapseState,
}) => {
  const { t } = useTranslation()
  const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal, showAgentLogModal, setShowAgentLogModal } = useAppStore(useShallow(state => ({
@@ -166,28 +160,19 @@
  useEffect(() => {
    if (chatFooterRef.current && chatContainerRef.current) {
      // container padding bottom
      const resizeContainerObserver = new ResizeObserver((entries) => {
      const resizeObserver = new ResizeObserver((entries) => {
        for (const entry of entries) {
          const { blockSize } = entry.borderBoxSize[0]
          chatContainerRef.current!.style.paddingBottom = `${blockSize}px`
          handleScrollToBottom()
        }
      })
      resizeContainerObserver.observe(chatFooterRef.current)
      // footer width
      const resizeFooterObserver = new ResizeObserver((entries) => {
        for (const entry of entries) {
          const { inlineSize } = entry.borderBoxSize[0]
          chatFooterRef.current!.style.width = `${inlineSize}px`
        }
      })
      resizeFooterObserver.observe(chatContainerRef.current)
      resizeObserver.observe(chatFooterRef.current)
      return () => {
        resizeContainerObserver.disconnect()
        resizeFooterObserver.disconnect()
        resizeObserver.disconnect()
      }
    }
  }, [handleScrollToBottom])
@@ -196,19 +181,13 @@
    const chatContainer = chatContainerRef.current
    if (chatContainer) {
      const setUserScrolled = () => {
        // eslint-disable-next-line sonarjs/no-gratuitous-expressions
        if (chatContainer) // its in event callback, chatContainer may be null
          userScrolledRef.current = chatContainer.scrollHeight - chatContainer.scrollTop > chatContainer.clientHeight
        if (chatContainer)
          userScrolledRef.current = chatContainer.scrollHeight - chatContainer.scrollTop >= chatContainer.clientHeight + 300
      }
      chatContainer.addEventListener('scroll', setUserScrolled)
      return () => chatContainer.removeEventListener('scroll', setUserScrolled)
    }
  }, [])
  useEffect(() => {
    if (!sidebarCollapseState)
      setTimeout(() => handleWindowResize(), 200)
  }, [handleWindowResize, sidebarCollapseState])
  const hasTryToAsk = config?.suggested_questions_after_answer?.enabled && !!suggestedQuestions?.length && onSend
@@ -265,8 +244,6 @@
                    item={item}
                    questionIcon={questionIcon}
                    theme={themeBuilder?.theme}
                    enableEdit={config?.questionEditEnable}
                    switchSibling={switchSibling}
                  />
                )
              })
@@ -274,8 +251,11 @@
          </div>
        </div>
        <div
          className={`absolute bottom-0 flex justify-center bg-chat-input-mask ${(hasTryToAsk || !noChatInput || !noStopResponding) && chatFooterClassName}`}
          className={`absolute bottom-0 ${(hasTryToAsk || !noChatInput || !noStopResponding) && chatFooterClassName}`}
          ref={chatFooterRef}
          style={{
            background: 'linear-gradient(0deg, #F9FAFB 40%, rgba(255, 255, 255, 0.00) 100%)',
          }}
        >
          <div
            ref={chatFooterInnerRef}
@@ -283,10 +263,10 @@
          >
            {
              !noStopResponding && isResponding && (
                <div className='mb-2 flex justify-center'>
                <div className='flex justify-center mb-2'>
                  <Button onClick={onStopResponding}>
                    <StopCircle className='mr-[5px] h-3.5 w-3.5 text-gray-500' />
                    <span className='text-xs font-normal text-gray-500'>{t('appDebug.operation.stopResponding')}</span>
                    <StopCircle className='mr-[5px] w-3.5 h-3.5 text-gray-500' />
                    <span className='text-xs text-gray-500 font-normal'>{t('appDebug.operation.stopResponding')}</span>
                  </Button>
                </div>
              )
@@ -296,14 +276,12 @@
                <TryToAsk
                  suggestedQuestions={suggestedQuestions}
                  onSend={onSend}
                  isMobile={isMobile}
                />
              )
            }
            {
              !noChatInput && (
                <ChatInputArea
                  disabled={inputDisabled}
                  showFeatureBar={showFeatureBar}
                  showFileUpload={showFileUpload}
                  featureBarDisabled={isResponding}
app/components/base/chat/chat/loading-anim/style.module.css
app/components/base/chat/chat/log/index.tsx
@@ -1,8 +1,8 @@
import type { FC } from 'react'
import { RiFileList3Line } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { File02 } from '@/app/components/base/icons/src/vender/line/files'
import type { IChatItem } from '@/app/components/base/chat/chat/type'
import { useStore as useAppStore } from '@/app/components/app/store'
import ActionButton from '@/app/components/base/action-button'
type LogProps = {
  logItem: IChatItem
@@ -10,6 +10,7 @@
const Log: FC<LogProps> = ({
  logItem,
}) => {
  const { t } = useTranslation()
  const setCurrentLogItem = useAppStore(s => s.setCurrentLogItem)
  const setShowPromptLogModal = useAppStore(s => s.setShowPromptLogModal)
  const setShowAgentLogModal = useAppStore(s => s.setShowAgentLogModal)
@@ -19,7 +20,7 @@
  return (
    <div
      className='ml-1 flex items-center gap-0.5 rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-md backdrop-blur-sm'
      className='shrink-0 p-1 flex items-center justify-center rounded-[6px] font-medium text-gray-500 hover:bg-gray-50 cursor-pointer hover:text-gray-700'
      onClick={(e) => {
        e.stopPropagation()
        e.nativeEvent.stopImmediatePropagation()
@@ -32,9 +33,8 @@
          setShowPromptLogModal(true)
      }}
    >
      <ActionButton>
        <RiFileList3Line className='h-4 w-4' />
      </ActionButton>
      <File02 className='mr-1 w-4 h-4' />
      <div className='text-xs leading-4'>{runID ? t('appLog.viewLog') : isAgent ? t('appLog.agentLog') : t('appLog.promptLog')}</div>
    </div>
  )
}
app/components/base/chat/chat/question.stories.tsx
@@ -26,8 +26,8 @@
      content: 'You are a helpful assistant.',
    } satisfies ChatItem,
    theme: undefined,
    questionIcon: <div className='h-full w-full rounded-full border-[0.5px] border-black/5'>
      <User className='h-full w-full' />
    questionIcon: <div className='w-full h-full rounded-full border-[0.5px] border-black/5'>
      <User className='w-full h-full' />
    </div>,
  },
}
app/components/base/chat/chat/question.tsx
@@ -4,169 +4,54 @@
} from 'react'
import {
  memo,
  useCallback,
  useEffect,
  useRef,
  useState,
} from 'react'
import type { ChatItem } from '../types'
import type { Theme } from '../embedded-chatbot/theme/theme-context'
import { CssTransform } from '../embedded-chatbot/theme/utils'
import ContentSwitch from './content-switch'
import { User } from '@/app/components/base/icons/src/public/avatar'
import { Markdown } from '@/app/components/base/markdown'
import { FileList } from '@/app/components/base/file-uploader'
import ActionButton from '../../action-button'
import { RiClipboardLine, RiEditLine } from '@remixicon/react'
import Toast from '../../toast'
import copy from 'copy-to-clipboard'
import { useTranslation } from 'react-i18next'
import cn from '@/utils/classnames'
import Textarea from 'react-textarea-autosize'
import Button from '../../button'
import { useChatContext } from './context'
type QuestionProps = {
  item: ChatItem
  questionIcon?: ReactNode
  theme: Theme | null | undefined
  enableEdit?: boolean
  switchSibling?: (siblingMessageId: string) => void
}
const Question: FC<QuestionProps> = ({
  item,
  questionIcon,
  theme,
  enableEdit = true,
  switchSibling,
}) => {
  const { t } = useTranslation()
  const {
    content,
    message_files,
  } = item
  const {
    onRegenerate,
  } = useChatContext()
  const [isEditing, setIsEditing] = useState(false)
  const [editedContent, setEditedContent] = useState(content)
  const [contentWidth, setContentWidth] = useState(0)
  const contentRef = useRef<HTMLDivElement>(null)
  const handleEdit = useCallback(() => {
    setIsEditing(true)
    setEditedContent(content)
  }, [content])
  const handleResend = useCallback(() => {
    setIsEditing(false)
    onRegenerate?.(item, { message: editedContent, files: message_files })
  }, [editedContent, message_files, item, onRegenerate])
  const handleCancelEditing = useCallback(() => {
    setIsEditing(false)
    setEditedContent(content)
  }, [content])
  const handleSwitchSibling = useCallback((direction: 'prev' | 'next') => {
    if (direction === 'prev')
      item.prevSibling && switchSibling?.(item.prevSibling)
    else
      item.nextSibling && switchSibling?.(item.nextSibling)
  }, [switchSibling, item.prevSibling, item.nextSibling])
  const getContentWidth = () => {
    if (contentRef.current)
      setContentWidth(contentRef.current?.clientWidth)
  }
  useEffect(() => {
    if (!contentRef.current)
      return
    const resizeObserver = new ResizeObserver(() => {
      getContentWidth()
    })
    resizeObserver.observe(contentRef.current)
    return () => {
      resizeObserver.disconnect()
    }
  }, [])
  return (
    <div className='mb-2 flex justify-end last:mb-0'>
      <div className={cn('group relative mr-4 flex max-w-full items-start pl-14', isEditing && 'flex-1')}>
        <div className={cn('mr-2 gap-1', isEditing ? 'hidden' : 'flex')}>
    <div className='flex justify-end mb-2 last:mb-0 pl-14'>
      <div className='group relative mr-4 max-w-full'>
          <div
            className="absolute hidden gap-0.5 rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-md backdrop-blur-sm group-hover:flex"
            style={{ right: contentWidth + 8 }}
          >
            <ActionButton onClick={() => {
              copy(content)
              Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') })
            }}>
              <RiClipboardLine className='h-4 w-4' />
            </ActionButton>
            {enableEdit && <ActionButton onClick={handleEdit}>
              <RiEditLine className='h-4 w-4' />
            </ActionButton>}
          </div>
        </div>
        <div
          ref={contentRef}
          className='w-full rounded-2xl bg-[#D1E9FF]/50 px-4 py-3 text-sm text-gray-900'
          className='px-4 py-3 bg-[#D1E9FF]/50 rounded-2xl text-sm text-gray-900'
          style={theme?.chatBubbleColorStyle ? CssTransform(theme.chatBubbleColorStyle) : {}}
        >
          {
            !!message_files?.length && (
              <FileList
                className='mb-2'
                files={message_files}
                showDeleteAction={false}
                showDownloadAction={true}
              />
            )
          }
          { !isEditing
            ? <Markdown content={content} />
            : <div className="
                flex flex-col gap-2 rounded-xl
                border border-components-chat-input-border bg-components-panel-bg-blur p-[9px] shadow-md
              ">
              <div className="max-h-[158px] overflow-y-auto overflow-x-hidden">
                <Textarea
                  className={cn(
                    'body-lg-regular w-full p-1 leading-6 text-text-tertiary outline-none',
                  )}
                  autoFocus
                  minRows={1}
                  value={editedContent}
                  onChange={e => setEditedContent(e.target.value)}
                />
              </div>
              <div className="flex justify-end gap-2">
                <Button variant='ghost' onClick={handleCancelEditing}>{t('common.operation.cancel')}</Button>
                <Button variant='primary' onClick={handleResend}>{t('common.chat.resend')}</Button>
              </div>
            </div> }
          { !isEditing && <ContentSwitch
            count={item.siblingCount}
            currentIndex={item.siblingIndex}
            prevDisabled={!item.prevSibling}
            nextDisabled={!item.nextSibling}
            switchSibling={handleSwitchSibling}
          />}
          <Markdown content={content} />
        </div>
        <div className='mt-1 h-[18px]' />
      </div>
      <div className='h-10 w-10 shrink-0'>
      <div className='shrink-0 w-10 h-10'>
        {
          questionIcon || (
            <div className='h-full w-full rounded-full border-[0.5px] border-black/5'>
              <User className='h-full w-full' />
            <div className='w-full h-full rounded-full border-[0.5px] border-black/5'>
              <User className='w-full h-full' />
            </div>
          )
        }
Diff truncated after the above file
app/components/base/chat/chat/thought/index.tsx app/components/base/chat/chat/thought/panel.tsx app/components/base/chat/chat/thought/tool.tsx app/components/base/chat/chat/try-to-ask.tsx app/components/base/chat/chat/type.ts app/components/base/chat/chat/utils.ts app/components/base/chat/embedded-chatbot/chat-wrapper.tsx app/components/base/chat/embedded-chatbot/config-panel/form-input.tsx app/components/base/chat/embedded-chatbot/config-panel/form.tsx app/components/base/chat/embedded-chatbot/config-panel/index.tsx app/components/base/chat/embedded-chatbot/context.tsx app/components/base/chat/embedded-chatbot/header.tsx app/components/base/chat/embedded-chatbot/hooks.tsx app/components/base/chat/embedded-chatbot/index.tsx app/components/base/chat/embedded-chatbot/theme/theme-context.ts app/components/base/chat/embedded-chatbot/theme/utils.ts app/components/base/chat/types.ts app/components/base/chat/utils.ts app/components/base/checkbox/assets/mixed.svg app/components/base/checkbox/index.module.css app/components/base/checkbox/index.tsx app/components/base/chip/index.tsx app/components/base/confirm/index.tsx app/components/base/copy-btn/index.tsx app/components/base/copy-btn/style.module.css app/components/base/copy-feedback/index.tsx app/components/base/copy-icon/index.tsx app/components/base/corner-label/index.tsx app/components/base/dialog/index.tsx app/components/base/divider/with-label.tsx app/components/base/drawer-plus/index.tsx app/components/base/drawer/index.tsx app/components/base/dropdown/index.tsx app/components/base/emoji-picker/Inner.tsx app/components/base/emoji-picker/index.tsx app/components/base/emoji-picker/style.module.css app/components/base/features/context.tsx app/components/base/features/new-feature-panel/annotation-reply/annotation-ctrl-btn/index.tsx app/components/base/features/new-feature-panel/annotation-reply/config-param-modal.tsx app/components/base/features/new-feature-panel/annotation-reply/config-param.tsx app/components/base/features/new-feature-panel/annotation-reply/index.tsx app/components/base/features/new-feature-panel/annotation-reply/score-slider/base-slider/index.tsx app/components/base/features/new-feature-panel/annotation-reply/score-slider/base-slider/style.module.css app/components/base/features/new-feature-panel/annotation-reply/score-slider/index.tsx app/components/base/features/new-feature-panel/citation.tsx app/components/base/features/new-feature-panel/conversation-opener/index.tsx app/components/base/features/new-feature-panel/conversation-opener/modal.tsx app/components/base/features/new-feature-panel/dialog-wrapper.tsx app/components/base/features/new-feature-panel/feature-bar.tsx app/components/base/features/new-feature-panel/feature-card.tsx app/components/base/features/new-feature-panel/file-upload/index.tsx app/components/base/features/new-feature-panel/file-upload/setting-content.tsx app/components/base/features/new-feature-panel/file-upload/setting-modal.tsx app/components/base/features/new-feature-panel/follow-up.tsx app/components/base/features/new-feature-panel/image-upload/index.tsx app/components/base/features/new-feature-panel/index.tsx app/components/base/features/new-feature-panel/moderation/form-generation.tsx app/components/base/features/new-feature-panel/moderation/index.tsx app/components/base/features/new-feature-panel/moderation/moderation-content.tsx app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx app/components/base/features/new-feature-panel/more-like-this.tsx app/components/base/features/new-feature-panel/speech-to-text.tsx app/components/base/features/new-feature-panel/text-to-speech/index.tsx app/components/base/features/new-feature-panel/text-to-speech/param-config-content.tsx app/components/base/features/new-feature-panel/text-to-speech/voice-settings.tsx app/components/base/file-uploader/audio-preview.tsx app/components/base/file-uploader/file-from-link-or-local/index.tsx app/components/base/file-uploader/file-image-render.tsx app/components/base/file-uploader/file-input.tsx app/components/base/file-uploader/file-list-in-log.tsx app/components/base/file-uploader/file-uploader-in-attachment/file-item.tsx app/components/base/file-uploader/file-uploader-in-attachment/index.tsx app/components/base/file-uploader/file-uploader-in-chat-input/file-image-item.tsx app/components/base/file-uploader/file-uploader-in-chat-input/file-item.tsx app/components/base/file-uploader/file-uploader-in-chat-input/index.tsx app/components/base/file-uploader/hooks.ts app/components/base/file-uploader/pdf-preview.tsx app/components/base/file-uploader/store.tsx app/components/base/file-uploader/utils.ts app/components/base/file-uploader/video-preview.tsx app/components/base/fullscreen-modal/index.tsx app/components/base/ga/index.tsx app/components/base/grid-mask/index.tsx app/components/base/icons/IconBase.tsx app/components/base/icons/script.js app/components/base/icons/src/image/llm/BaichuanTextCn.tsx app/components/base/icons/src/image/llm/Minimax.tsx app/components/base/icons/src/image/llm/MinimaxText.tsx app/components/base/icons/src/image/llm/Tongyi.tsx app/components/base/icons/src/image/llm/TongyiText.tsx app/components/base/icons/src/image/llm/TongyiTextCn.tsx app/components/base/icons/src/image/llm/Wxyy.tsx app/components/base/icons/src/image/llm/WxyyText.tsx app/components/base/icons/src/image/llm/WxyyTextCn.tsx app/components/base/icons/src/public/avatar/Robot.json app/components/base/icons/src/public/avatar/Robot.tsx app/components/base/icons/src/public/avatar/User.json app/components/base/icons/src/public/avatar/User.tsx app/components/base/icons/src/public/billing/Sparkles.json app/components/base/icons/src/public/billing/Sparkles.tsx app/components/base/icons/src/public/billing/index.ts app/components/base/icons/src/public/common/D.json app/components/base/icons/src/public/common/D.tsx app/components/base/icons/src/public/common/DiagonalDividingLine.json app/components/base/icons/src/public/common/DiagonalDividingLine.tsx app/components/base/icons/src/public/common/Dify.json app/components/base/icons/src/public/common/Dify.tsx app/components/base/icons/src/public/common/Github.json app/components/base/icons/src/public/common/Github.tsx app/components/base/icons/src/public/common/Highlight.json app/components/base/icons/src/public/common/Highlight.tsx app/components/base/icons/src/public/common/Line3.json app/components/base/icons/src/public/common/Line3.tsx app/components/base/icons/src/public/common/Lock.json app/components/base/icons/src/public/common/Lock.tsx app/components/base/icons/src/public/common/MessageChatSquare.json app/components/base/icons/src/public/common/MessageChatSquare.tsx app/components/base/icons/src/public/common/MultiPathRetrieval.json app/components/base/icons/src/public/common/MultiPathRetrieval.tsx app/components/base/icons/src/public/common/NTo1Retrieval.json app/components/base/icons/src/public/common/NTo1Retrieval.tsx app/components/base/icons/src/public/common/Notion.json app/components/base/icons/src/public/common/Notion.tsx app/components/base/icons/src/public/common/SparklesSoft.json app/components/base/icons/src/public/common/SparklesSoft.tsx app/components/base/icons/src/public/common/index.ts app/components/base/icons/src/public/files/Csv.json app/components/base/icons/src/public/files/Csv.tsx app/components/base/icons/src/public/files/Doc.json app/components/base/icons/src/public/files/Doc.tsx app/components/base/icons/src/public/files/Docx.json app/components/base/icons/src/public/files/Docx.tsx app/components/base/icons/src/public/files/Html.json app/components/base/icons/src/public/files/Html.tsx app/components/base/icons/src/public/files/Json.json app/components/base/icons/src/public/files/Json.tsx app/components/base/icons/src/public/files/Md.json app/components/base/icons/src/public/files/Md.tsx app/components/base/icons/src/public/files/Pdf.json app/components/base/icons/src/public/files/Pdf.tsx app/components/base/icons/src/public/files/Txt.json app/components/base/icons/src/public/files/Txt.tsx app/components/base/icons/src/public/files/Unknown.json app/components/base/icons/src/public/files/Unknown.tsx app/components/base/icons/src/public/files/Xlsx.json app/components/base/icons/src/public/files/Xlsx.tsx app/components/base/icons/src/public/files/Yaml.json app/components/base/icons/src/public/files/Yaml.tsx app/components/base/icons/src/public/knowledge/Chunk.json app/components/base/icons/src/public/knowledge/Chunk.tsx app/components/base/icons/src/public/knowledge/Collapse.json app/components/base/icons/src/public/knowledge/Collapse.tsx app/components/base/icons/src/public/knowledge/GeneralType.json app/components/base/icons/src/public/knowledge/GeneralType.tsx app/components/base/icons/src/public/knowledge/LayoutRight2LineMod.json app/components/base/icons/src/public/knowledge/LayoutRight2LineMod.tsx app/components/base/icons/src/public/knowledge/ParentChildType.json app/components/base/icons/src/public/knowledge/ParentChildType.tsx app/components/base/icons/src/public/knowledge/SelectionMod.json app/components/base/icons/src/public/knowledge/SelectionMod.tsx app/components/base/icons/src/public/llm/Anthropic.json app/components/base/icons/src/public/llm/Anthropic.tsx app/components/base/icons/src/public/llm/AnthropicText.json app/components/base/icons/src/public/llm/AnthropicText.tsx app/components/base/icons/src/public/llm/AzureOpenaiService.json app/components/base/icons/src/public/llm/AzureOpenaiService.tsx app/components/base/icons/src/public/llm/AzureOpenaiServiceText.json app/components/base/icons/src/public/llm/AzureOpenaiServiceText.tsx app/components/base/icons/src/public/llm/Azureai.json app/components/base/icons/src/public/llm/Azureai.tsx app/components/base/icons/src/public/llm/AzureaiText.json app/components/base/icons/src/public/llm/AzureaiText.tsx app/components/base/icons/src/public/llm/Baichuan.json app/components/base/icons/src/public/llm/Baichuan.tsx app/components/base/icons/src/public/llm/BaichuanText.json app/components/base/icons/src/public/llm/BaichuanText.tsx app/components/base/icons/src/public/llm/Chatglm.json app/components/base/icons/src/public/llm/Chatglm.tsx app/components/base/icons/src/public/llm/ChatglmText.json app/components/base/icons/src/public/llm/ChatglmText.tsx app/components/base/icons/src/public/llm/Cohere.json app/components/base/icons/src/public/llm/Cohere.tsx app/components/base/icons/src/public/llm/CohereText.json app/components/base/icons/src/public/llm/CohereText.tsx app/components/base/icons/src/public/llm/Gpt3.json app/components/base/icons/src/public/llm/Gpt3.tsx app/components/base/icons/src/public/llm/Gpt4.json app/components/base/icons/src/public/llm/Gpt4.tsx app/components/base/icons/src/public/llm/Huggingface.json app/components/base/icons/src/public/llm/Huggingface.tsx app/components/base/icons/src/public/llm/HuggingfaceText.json app/components/base/icons/src/public/llm/HuggingfaceText.tsx app/components/base/icons/src/public/llm/HuggingfaceTextHub.json app/components/base/icons/src/public/llm/HuggingfaceTextHub.tsx app/components/base/icons/src/public/llm/IflytekSpark.json app/components/base/icons/src/public/llm/IflytekSpark.tsx app/components/base/icons/src/public/llm/IflytekSparkText.json app/components/base/icons/src/public/llm/IflytekSparkText.tsx app/components/base/icons/src/public/llm/IflytekSparkTextCn.json app/components/base/icons/src/public/llm/IflytekSparkTextCn.tsx app/components/base/icons/src/public/llm/Jina.json app/components/base/icons/src/public/llm/Jina.tsx app/components/base/icons/src/public/llm/JinaText.json app/components/base/icons/src/public/llm/JinaText.tsx app/components/base/icons/src/public/llm/Localai.json app/components/base/icons/src/public/llm/Localai.tsx app/components/base/icons/src/public/llm/LocalaiText.json app/components/base/icons/src/public/llm/LocalaiText.tsx app/components/base/icons/src/public/llm/Microsoft.json app/components/base/icons/src/public/llm/Microsoft.tsx app/components/base/icons/src/public/llm/OpenaiBlack.json app/components/base/icons/src/public/llm/OpenaiBlack.tsx app/components/base/icons/src/public/llm/OpenaiBlue.json app/components/base/icons/src/public/llm/OpenaiBlue.tsx app/components/base/icons/src/public/llm/OpenaiGreen.json app/components/base/icons/src/public/llm/OpenaiGreen.tsx app/components/base/icons/src/public/llm/OpenaiText.json app/components/base/icons/src/public/llm/OpenaiText.tsx app/components/base/icons/src/public/llm/OpenaiTransparent.json app/components/base/icons/src/public/llm/OpenaiTransparent.tsx app/components/base/icons/src/public/llm/OpenaiViolet.json app/components/base/icons/src/public/llm/OpenaiViolet.tsx app/components/base/icons/src/public/llm/Openllm.json app/components/base/icons/src/public/llm/Openllm.tsx app/components/base/icons/src/public/llm/OpenllmText.json app/components/base/icons/src/public/llm/OpenllmText.tsx app/components/base/icons/src/public/llm/Replicate.json app/components/base/icons/src/public/llm/Replicate.tsx app/components/base/icons/src/public/llm/ReplicateText.json app/components/base/icons/src/public/llm/ReplicateText.tsx app/components/base/icons/src/public/llm/XorbitsInference.json app/components/base/icons/src/public/llm/XorbitsInference.tsx app/components/base/icons/src/public/llm/XorbitsInferenceText.json app/components/base/icons/src/public/llm/XorbitsInferenceText.tsx app/components/base/icons/src/public/llm/Zhipuai.json app/components/base/icons/src/public/llm/Zhipuai.tsx app/components/base/icons/src/public/llm/ZhipuaiText.json app/components/base/icons/src/public/llm/ZhipuaiText.tsx app/components/base/icons/src/public/llm/ZhipuaiTextCn.json app/components/base/icons/src/public/llm/ZhipuaiTextCn.tsx app/components/base/icons/src/public/llm/index.ts app/components/base/icons/src/public/model/Checked.json app/components/base/icons/src/public/model/Checked.tsx app/components/base/icons/src/public/other/DefaultToolIcon.json app/components/base/icons/src/public/other/DefaultToolIcon.tsx app/components/base/icons/src/public/other/Icon3Dots.json app/components/base/icons/src/public/other/Icon3Dots.tsx app/components/base/icons/src/public/other/RowStruct.json app/components/base/icons/src/public/other/RowStruct.tsx app/components/base/icons/src/public/other/index.ts app/components/base/icons/src/public/plugins/Google.json app/components/base/icons/src/public/plugins/Google.tsx app/components/base/icons/src/public/plugins/WebReader.json app/components/base/icons/src/public/plugins/WebReader.tsx app/components/base/icons/src/public/plugins/Wikipedia.json app/components/base/icons/src/public/plugins/Wikipedia.tsx app/components/base/icons/src/public/plugins/index.ts app/components/base/icons/src/public/thought/DataSet.json app/components/base/icons/src/public/thought/DataSet.tsx app/components/base/icons/src/public/thought/Loading.json app/components/base/icons/src/public/thought/Loading.tsx app/components/base/icons/src/public/thought/Search.json app/components/base/icons/src/public/thought/Search.tsx app/components/base/icons/src/public/thought/ThoughtList.json app/components/base/icons/src/public/thought/ThoughtList.tsx app/components/base/icons/src/public/thought/WebReader.json app/components/base/icons/src/public/thought/WebReader.tsx app/components/base/icons/src/public/tracing/LangfuseIcon.json app/components/base/icons/src/public/tracing/LangfuseIcon.tsx app/components/base/icons/src/public/tracing/LangfuseIconBig.json app/components/base/icons/src/public/tracing/LangfuseIconBig.tsx app/components/base/icons/src/public/tracing/LangsmithIcon.json app/components/base/icons/src/public/tracing/LangsmithIcon.tsx app/components/base/icons/src/public/tracing/LangsmithIconBig.json app/components/base/icons/src/public/tracing/LangsmithIconBig.tsx app/components/base/icons/src/public/tracing/OpikIcon.json app/components/base/icons/src/public/tracing/OpikIcon.tsx app/components/base/icons/src/public/tracing/OpikIconBig.json app/components/base/icons/src/public/tracing/OpikIconBig.tsx app/components/base/icons/src/public/tracing/TracingIcon.json app/components/base/icons/src/public/tracing/TracingIcon.tsx app/components/base/icons/src/public/tracing/index.ts app/components/base/icons/src/vender/features/Citations.tsx app/components/base/icons/src/vender/features/ContentModeration.tsx app/components/base/icons/src/vender/features/Document.tsx app/components/base/icons/src/vender/features/FolderUpload.tsx app/components/base/icons/src/vender/features/LoveMessage.tsx app/components/base/icons/src/vender/features/MessageFast.tsx app/components/base/icons/src/vender/features/Microphone01.tsx app/components/base/icons/src/vender/features/TextToAudio.tsx app/components/base/icons/src/vender/features/VirtualAssistant.tsx app/components/base/icons/src/vender/features/Vision.tsx app/components/base/icons/src/vender/line/alertsAndFeedback/AlertTriangle.tsx app/components/base/icons/src/vender/line/alertsAndFeedback/ThumbsDown.tsx app/components/base/icons/src/vender/line/alertsAndFeedback/ThumbsUp.tsx app/components/base/icons/src/vender/line/arrows/ArrowNarrowLeft.tsx app/components/base/icons/src/vender/line/arrows/ArrowUpRight.tsx app/components/base/icons/src/vender/line/arrows/ChevronDownDouble.tsx app/components/base/icons/src/vender/line/arrows/ChevronRight.tsx app/components/base/icons/src/vender/line/arrows/ChevronSelectorVertical.tsx app/components/base/icons/src/vender/line/arrows/RefreshCcw01.tsx app/components/base/icons/src/vender/line/arrows/RefreshCw05.tsx app/components/base/icons/src/vender/line/arrows/ReverseLeft.tsx app/components/base/icons/src/vender/line/communication/AiText.tsx app/components/base/icons/src/vender/line/communication/ChatBot.tsx app/components/base/icons/src/vender/line/communication/ChatBotSlim.tsx app/components/base/icons/src/vender/line/communication/CuteRobot.tsx app/components/base/icons/src/vender/line/communication/MessageCheckRemove.tsx app/components/base/icons/src/vender/line/communication/MessageFastPlus.tsx app/components/base/icons/src/vender/line/development/ArtificialBrain.tsx app/components/base/icons/src/vender/line/development/BarChartSquare02.tsx app/components/base/icons/src/vender/line/development/BracketsX.tsx app/components/base/icons/src/vender/line/development/CodeBrowser.tsx app/components/base/icons/src/vender/line/development/Container.tsx app/components/base/icons/src/vender/line/development/Database01.tsx app/components/base/icons/src/vender/line/development/Database03.tsx app/components/base/icons/src/vender/line/development/FileHeart02.tsx app/components/base/icons/src/vender/line/development/GitBranch01.tsx app/components/base/icons/src/vender/line/development/PromptEngineering.tsx app/components/base/icons/src/vender/line/development/PuzzlePiece01.tsx app/components/base/icons/src/vender/line/development/TerminalSquare.tsx app/components/base/icons/src/vender/line/development/Variable.tsx app/components/base/icons/src/vender/line/development/Webhooks.tsx app/components/base/icons/src/vender/line/editor/AlignLeft.tsx app/components/base/icons/src/vender/line/editor/BezierCurve03.tsx app/components/base/icons/src/vender/line/editor/Colors.tsx app/components/base/icons/src/vender/line/editor/ImageIndentLeft.tsx app/components/base/icons/src/vender/line/editor/LeftIndent02.tsx app/components/base/icons/src/vender/line/editor/LetterSpacing01.tsx app/components/base/icons/src/vender/line/editor/TypeSquare.tsx app/components/base/icons/src/vender/line/editor/index.ts app/components/base/icons/src/vender/line/education/BookOpen01.tsx app/components/base/icons/src/vender/line/files/Clipboard.tsx app/components/base/icons/src/vender/line/files/ClipboardCheck.tsx app/components/base/icons/src/vender/line/files/File02.tsx app/components/base/icons/src/vender/line/files/FileArrow01.tsx app/components/base/icons/src/vender/line/files/FileCheck02.tsx app/components/base/icons/src/vender/line/files/FileDownload02.tsx app/components/base/icons/src/vender/line/files/FilePlus01.tsx app/components/base/icons/src/vender/line/files/FilePlus02.tsx app/components/base/icons/src/vender/line/files/FileText.tsx app/components/base/icons/src/vender/line/files/FileUpload.tsx app/components/base/icons/src/vender/line/files/Folder.tsx app/components/base/icons/src/vender/line/financeAndECommerce/Balance.tsx app/components/base/icons/src/vender/line/financeAndECommerce/CoinsStacked01.tsx app/components/base/icons/src/vender/line/financeAndECommerce/GoldCoin.tsx app/components/base/icons/src/vender/line/financeAndECommerce/ReceiptList.tsx app/components/base/icons/src/vender/line/financeAndECommerce/Tag01.tsx app/components/base/icons/src/vender/line/financeAndECommerce/Tag03.tsx app/components/base/icons/src/vender/line/general/AtSign.tsx app/components/base/icons/src/vender/line/general/Bookmark.tsx app/components/base/icons/src/vender/line/general/Check.tsx app/components/base/icons/src/vender/line/general/CheckDone01.tsx app/components/base/icons/src/vender/line/general/ChecklistSquare.tsx app/components/base/icons/src/vender/line/general/DotsGrid.tsx app/components/base/icons/src/vender/line/general/Edit02.tsx app/components/base/icons/src/vender/line/general/Edit04.tsx app/components/base/icons/src/vender/line/general/Edit05.tsx app/components/base/icons/src/vender/line/general/Hash02.tsx app/components/base/icons/src/vender/line/general/InfoCircle.tsx app/components/base/icons/src/vender/line/general/Link03.tsx app/components/base/icons/src/vender/line/general/LinkExternal02.tsx app/components/base/icons/src/vender/line/general/LogIn04.tsx app/components/base/icons/src/vender/line/general/LogOut01.tsx app/components/base/icons/src/vender/line/general/LogOut04.tsx app/components/base/icons/src/vender/line/general/Menu01.tsx app/components/base/icons/src/vender/line/general/Pin01.tsx app/components/base/icons/src/vender/line/general/Pin02.tsx app/components/base/icons/src/vender/line/general/Plus02.tsx app/components/base/icons/src/vender/line/general/Refresh.tsx app/components/base/icons/src/vender/line/general/Settings01.tsx app/components/base/icons/src/vender/line/general/Settings04.tsx app/components/base/icons/src/vender/line/general/Target04.tsx app/components/base/icons/src/vender/line/general/Upload03.tsx app/components/base/icons/src/vender/line/general/UploadCloud01.tsx app/components/base/icons/src/vender/line/general/X.tsx app/components/base/icons/src/vender/line/images/ImagePlus.tsx app/components/base/icons/src/vender/line/layout/AlignLeft01.tsx app/components/base/icons/src/vender/line/layout/AlignRight01.tsx app/components/base/icons/src/vender/line/layout/Grid01.tsx app/components/base/icons/src/vender/line/layout/LayoutGrid02.tsx app/components/base/icons/src/vender/line/mapsAndTravel/Globe01.tsx app/components/base/icons/src/vender/line/mapsAndTravel/Route.tsx app/components/base/icons/src/vender/line/mediaAndDevices/Microphone01.tsx app/components/base/icons/src/vender/line/mediaAndDevices/PlayCircle.tsx app/components/base/icons/src/vender/line/mediaAndDevices/SlidersH.tsx app/components/base/icons/src/vender/line/mediaAndDevices/Speaker.tsx app/components/base/icons/src/vender/line/mediaAndDevices/Stop.tsx app/components/base/icons/src/vender/line/mediaAndDevices/StopCircle.tsx app/components/base/icons/src/vender/line/others/Apps02.tsx app/components/base/icons/src/vender/line/others/BubbleX.tsx app/components/base/icons/src/vender/line/others/Colors.tsx app/components/base/icons/src/vender/line/others/DragHandle.tsx app/components/base/icons/src/vender/line/others/Env.tsx app/components/base/icons/src/vender/line/others/Exchange02.tsx app/components/base/icons/src/vender/line/others/FileCode.tsx app/components/base/icons/src/vender/line/others/GlobalVariable.tsx app/components/base/icons/src/vender/line/others/Icon3Dots.tsx app/components/base/icons/src/vender/line/others/LongArrowLeft.tsx app/components/base/icons/src/vender/line/others/LongArrowRight.tsx app/components/base/icons/src/vender/line/others/Tools.tsx app/components/base/icons/src/vender/line/shapes/CubeOutline.tsx app/components/base/icons/src/vender/line/time/ClockFastForward.tsx app/components/base/icons/src/vender/line/time/ClockPlay.tsx app/components/base/icons/src/vender/line/time/ClockPlaySlim.tsx app/components/base/icons/src/vender/line/time/ClockRefresh.tsx app/components/base/icons/src/vender/line/users/User01.tsx app/components/base/icons/src/vender/line/users/Users01.tsx app/components/base/icons/src/vender/line/weather/Stars02.tsx app/components/base/icons/src/vender/other/Generator.tsx app/components/base/icons/src/vender/other/ReplayLine.tsx app/components/base/icons/src/vender/other/index.ts app/components/base/icons/src/vender/solid/FinanceAndECommerce/GoldCoin.tsx app/components/base/icons/src/vender/solid/FinanceAndECommerce/Scales02.tsx app/components/base/icons/src/vender/solid/alertsAndFeedback/AlertTriangle.tsx app/components/base/icons/src/vender/solid/arrows/ChevronDown.tsx app/components/base/icons/src/vender/solid/arrows/HighPriority.tsx app/components/base/icons/src/vender/solid/communication/AiText.tsx app/components/base/icons/src/vender/solid/communication/BubbleTextMod.tsx app/components/base/icons/src/vender/solid/communication/ChatBot.tsx app/components/base/icons/src/vender/solid/communication/CuteRobot.tsx app/components/base/icons/src/vender/solid/communication/EditList.tsx app/components/base/icons/src/vender/solid/communication/ListSparkle.tsx app/components/base/icons/src/vender/solid/communication/Logic.tsx app/components/base/icons/src/vender/solid/communication/MessageDotsCircle.tsx app/components/base/icons/src/vender/solid/communication/MessageFast.tsx app/components/base/icons/src/vender/solid/communication/MessageHeartCircle.tsx app/components/base/icons/src/vender/solid/communication/MessageSmileSquare.tsx app/components/base/icons/src/vender/solid/communication/Send03.tsx app/components/base/icons/src/vender/solid/development/ApiConnection.tsx app/components/base/icons/src/vender/solid/development/ApiConnectionMod.tsx app/components/base/icons/src/vender/solid/development/BarChartSquare02.tsx app/components/base/icons/src/vender/solid/development/Container.tsx app/components/base/icons/src/vender/solid/development/Database02.tsx app/components/base/icons/src/vender/solid/development/Database03.tsx app/components/base/icons/src/vender/solid/development/FileHeart02.tsx app/components/base/icons/src/vender/solid/development/PatternRecognition.tsx app/components/base/icons/src/vender/solid/development/PromptEngineering.tsx app/components/base/icons/src/vender/solid/development/PuzzlePiece01.tsx app/components/base/icons/src/vender/solid/development/Semantic.tsx app/components/base/icons/src/vender/solid/development/TerminalSquare.tsx app/components/base/icons/src/vender/solid/development/Variable02.tsx app/components/base/icons/src/vender/solid/editor/Brush01.tsx app/components/base/icons/src/vender/solid/editor/Citations.tsx app/components/base/icons/src/vender/solid/editor/Colors.tsx app/components/base/icons/src/vender/solid/editor/Paragraph.tsx app/components/base/icons/src/vender/solid/editor/TypeSquare.tsx app/components/base/icons/src/vender/solid/education/Beaker02.tsx app/components/base/icons/src/vender/solid/education/BubbleText.tsx app/components/base/icons/src/vender/solid/education/Heart02.tsx app/components/base/icons/src/vender/solid/education/Unblur.tsx app/components/base/icons/src/vender/solid/files/File05.tsx app/components/base/icons/src/vender/solid/files/FileSearch02.tsx app/components/base/icons/src/vender/solid/files/Folder.tsx app/components/base/icons/src/vender/solid/files/index.ts app/components/base/icons/src/vender/solid/general/AnswerTriangle.tsx app/components/base/icons/src/vender/solid/general/CheckCircle.tsx app/components/base/icons/src/vender/solid/general/CheckDone01.tsx app/components/base/icons/src/vender/solid/general/Download02.tsx app/components/base/icons/src/vender/solid/general/Edit03.tsx app/components/base/icons/src/vender/solid/general/Edit04.tsx app/components/base/icons/src/vender/solid/general/Eye.tsx app/components/base/icons/src/vender/solid/general/MessageClockCircle.tsx app/components/base/icons/src/vender/solid/general/PlusCircle.tsx app/components/base/icons/src/vender/solid/general/QuestionTriangle.tsx app/components/base/icons/src/vender/solid/general/SearchMd.tsx app/components/base/icons/src/vender/solid/general/Target04.tsx app/components/base/icons/src/vender/solid/general/Tool03.tsx app/components/base/icons/src/vender/solid/general/XCircle.tsx app/components/base/icons/src/vender/solid/general/ZapFast.tsx app/components/base/icons/src/vender/solid/general/ZapNarrow.tsx app/components/base/icons/src/vender/solid/general/index.ts app/components/base/icons/src/vender/solid/layout/Grid01.tsx app/components/base/icons/src/vender/solid/mapsAndTravel/Globe06.tsx app/components/base/icons/src/vender/solid/mapsAndTravel/Route.tsx app/components/base/icons/src/vender/solid/mediaAndDevices/MagicBox.tsx app/components/base/icons/src/vender/solid/mediaAndDevices/MagicEyes.tsx app/components/base/icons/src/vender/solid/mediaAndDevices/MagicWand.tsx app/components/base/icons/src/vender/solid/mediaAndDevices/Microphone01.tsx app/components/base/icons/src/vender/solid/mediaAndDevices/Play.tsx app/components/base/icons/src/vender/solid/mediaAndDevices/Robot.tsx app/components/base/icons/src/vender/solid/mediaAndDevices/Sliders02.tsx app/components/base/icons/src/vender/solid/mediaAndDevices/Speaker.tsx app/components/base/icons/src/vender/solid/mediaAndDevices/StopCircle.tsx app/components/base/icons/src/vender/solid/mediaAndDevices/index.ts app/components/base/icons/src/vender/solid/security/Lock01.tsx app/components/base/icons/src/vender/solid/shapes/Corner.tsx app/components/base/icons/src/vender/solid/shapes/Star04.tsx app/components/base/icons/src/vender/solid/shapes/Star06.tsx app/components/base/icons/src/vender/solid/users/User01.tsx app/components/base/icons/src/vender/solid/users/UserEdit02.tsx app/components/base/icons/src/vender/solid/users/Users01.tsx app/components/base/icons/src/vender/solid/users/UsersPlus.tsx app/components/base/icons/src/vender/workflow/Answer.tsx app/components/base/icons/src/vender/workflow/Assigner.tsx app/components/base/icons/src/vender/workflow/Code.tsx app/components/base/icons/src/vender/workflow/DocsExtractor.tsx app/components/base/icons/src/vender/workflow/End.tsx app/components/base/icons/src/vender/workflow/Home.tsx app/components/base/icons/src/vender/workflow/Http.tsx app/components/base/icons/src/vender/workflow/IfElse.tsx app/components/base/icons/src/vender/workflow/Iteration.tsx app/components/base/icons/src/vender/workflow/IterationStart.tsx app/components/base/icons/src/vender/workflow/Jinja.tsx app/components/base/icons/src/vender/workflow/KnowledgeRetrieval.tsx app/components/base/icons/src/vender/workflow/ListFilter.tsx app/components/base/icons/src/vender/workflow/Llm.tsx app/components/base/icons/src/vender/workflow/ParameterExtractor.tsx app/components/base/icons/src/vender/workflow/QuestionClassifier.tsx app/components/base/icons/src/vender/workflow/TemplatingTransform.tsx app/components/base/icons/src/vender/workflow/VariableX.tsx app/components/base/icons/src/vender/workflow/index.ts app/components/base/image-gallery/index.tsx app/components/base/image-gallery/style.module.css app/components/base/image-uploader/audio-preview.tsx app/components/base/image-uploader/chat-image-uploader.tsx app/components/base/image-uploader/image-link-input.tsx app/components/base/image-uploader/image-list.tsx app/components/base/image-uploader/image-preview.tsx app/components/base/image-uploader/text-generation-image-uploader.tsx app/components/base/image-uploader/uploader.tsx app/components/base/image-uploader/video-preview.tsx app/components/base/input-number/index.tsx app/components/base/input/index.tsx app/components/base/linked-apps-panel/index.tsx app/components/base/list-empty/index.tsx app/components/base/loading/index.tsx app/components/base/logo/logo-embedded-chat-avatar.tsx app/components/base/logo/logo-embedded-chat-header.tsx app/components/base/logo/logo-site.tsx app/components/base/markdown-blocks/button.tsx app/components/base/markdown-blocks/form.tsx app/components/base/markdown.tsx app/components/base/mermaid/index.tsx app/components/base/message-log-modal/index.tsx app/components/base/modal/index.tsx app/components/base/notion-icon/index.module.css app/components/base/notion-icon/index.tsx app/components/base/notion-page-selector/base.module.css app/components/base/notion-page-selector/base.tsx app/components/base/notion-page-selector/notion-page-selector-modal/index.module.css app/components/base/notion-page-selector/notion-page-selector-modal/index.tsx app/components/base/notion-page-selector/page-selector/index.module.css app/components/base/notion-page-selector/page-selector/index.tsx app/components/base/notion-page-selector/search-input/index.module.css app/components/base/notion-page-selector/search-input/index.tsx app/components/base/notion-page-selector/workspace-selector/index.module.css app/components/base/notion-page-selector/workspace-selector/index.tsx app/components/base/pagination/hook.ts app/components/base/pagination/index.tsx app/components/base/pagination/pagination.tsx app/components/base/pagination/type.ts app/components/base/param-item/index.tsx app/components/base/param-item/score-threshold-item.tsx app/components/base/param-item/top-k-item.tsx app/components/base/popover/index.tsx app/components/base/portal-to-follow-elem/index.tsx app/components/base/premium-badge/index.css app/components/base/premium-badge/index.tsx app/components/base/progress-bar/index.tsx app/components/base/prompt-editor/constants.tsx app/components/base/prompt-editor/index.tsx app/components/base/prompt-editor/plugins/component-picker-block/hooks.tsx app/components/base/prompt-editor/plugins/component-picker-block/index.tsx app/components/base/prompt-editor/plugins/component-picker-block/menu.tsx app/components/base/prompt-editor/plugins/component-picker-block/prompt-option.tsx app/components/base/prompt-editor/plugins/component-picker-block/variable-option.tsx app/components/base/prompt-editor/plugins/context-block/component.tsx app/components/base/prompt-editor/plugins/context-block/context-block-replacement-block.tsx app/components/base/prompt-editor/plugins/context-block/index.tsx app/components/base/prompt-editor/plugins/context-block/node.tsx app/components/base/prompt-editor/plugins/custom-text/node.tsx app/components/base/prompt-editor/plugins/history-block/component.tsx app/components/base/prompt-editor/plugins/history-block/history-block-replacement-block.tsx app/components/base/prompt-editor/plugins/history-block/index.tsx app/components/base/prompt-editor/plugins/history-block/node.tsx app/components/base/prompt-editor/plugins/on-blur-or-focus-block.tsx app/components/base/prompt-editor/plugins/placeholder.tsx app/components/base/prompt-editor/plugins/query-block/component.tsx app/components/base/prompt-editor/plugins/query-block/node.tsx app/components/base/prompt-editor/plugins/variable-value-block/node.tsx app/components/base/prompt-editor/plugins/workflow-variable-block/component.tsx app/components/base/prompt-editor/plugins/workflow-variable-block/index.tsx app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx app/components/base/prompt-editor/plugins/workflow-variable-block/workflow-variable-block-replacement-block.tsx app/components/base/prompt-editor/types.ts app/components/base/prompt-editor/utils.ts app/components/base/prompt-log-modal/card.tsx app/components/base/prompt-log-modal/index.tsx app/components/base/qrcode/index.tsx app/components/base/qrcode/style.module.css app/components/base/radio-card/index.tsx app/components/base/radio-card/simple/index.tsx app/components/base/radio-card/simple/style.module.css app/components/base/radio/component/group/index.tsx app/components/base/radio/component/radio/index.tsx app/components/base/radio/ui.tsx app/components/base/regenerate-btn/index.tsx app/components/base/search-input/index.tsx app/components/base/select/index.tsx app/components/base/select/locale.tsx app/components/base/skeleton/index.tsx app/components/base/slider/index.tsx app/components/base/slider/style.css app/components/base/sort/index.tsx app/components/base/spinner/index.tsx app/components/base/svg-gallery/index.tsx app/components/base/svg/index.tsx app/components/base/switch/index.tsx app/components/base/tab-header/index.tsx app/components/base/tab-header/style.module.css app/components/base/tab-slider-new/index.tsx app/components/base/tab-slider-plain/index.tsx app/components/base/tab-slider/index.tsx app/components/base/tag-input/index.tsx app/components/base/tag-management/filter.tsx app/components/base/tag-management/index.tsx app/components/base/tag-management/selector.tsx app/components/base/tag-management/style.module.css app/components/base/tag-management/tag-item-editor.tsx app/components/base/tag-management/tag-remove-modal.tsx app/components/base/text-generation/types.ts app/components/base/textarea/index.tsx app/components/base/toast/index.tsx app/components/base/toast/style.module.css app/components/base/tooltip/index.tsx app/components/base/video-gallery/index.tsx app/components/base/voice-input/index.module.css app/components/base/voice-input/index.tsx app/components/billing/annotation-full/index.tsx app/components/billing/annotation-full/modal.tsx app/components/billing/annotation-full/style.module.css app/components/billing/apps-full-in-dialog/index.tsx app/components/billing/apps-full-in-dialog/style.module.css app/components/billing/apps-full/index.tsx app/components/billing/apps-full/style.module.css app/components/billing/billing-page/index.tsx app/components/billing/config.ts app/components/billing/header-billing-btn/index.tsx app/components/billing/plan/index.tsx app/components/billing/pricing/index.tsx app/components/billing/pricing/plan-item.tsx app/components/billing/pricing/select-plan-range.tsx app/components/billing/priority-label/index.tsx app/components/billing/progress-bar/index.tsx app/components/billing/type.ts app/components/billing/upgrade-btn/index.tsx app/components/billing/upgrade-btn/style.module.css app/components/billing/usage-info/apps-info.tsx app/components/billing/usage-info/index.tsx app/components/billing/usage-info/vector-space-info.tsx app/components/billing/vector-space-full/index.tsx app/components/billing/vector-space-full/style.module.css app/components/browser-initor.tsx app/components/custom/custom-page/index.tsx app/components/custom/custom-web-app-brand/index.tsx app/components/custom/custom-web-app-brand/style.module.css app/components/custom/style.module.css app/components/datasets/api/index.tsx app/components/datasets/chunk.tsx app/components/datasets/common/chunking-mode-label.tsx app/components/datasets/common/document-picker/document-list.tsx app/components/datasets/common/document-picker/index.tsx app/components/datasets/common/document-picker/preview-document-picker.tsx app/components/datasets/common/document-status-with-action/index-failed.tsx app/components/datasets/common/document-status-with-action/status-with-action.tsx app/components/datasets/common/economical-retrieval-method-config/index.tsx app/components/datasets/common/retrieval-method-config/index.tsx app/components/datasets/common/retrieval-method-info/index.tsx app/components/datasets/common/retrieval-param-config/index.tsx app/components/datasets/create/embedding-process/index.tsx app/components/datasets/create/empty-dataset-creation-modal/index.module.css app/components/datasets/create/empty-dataset-creation-modal/index.tsx app/components/datasets/create/file-preview/index.module.css app/components/datasets/create/file-preview/index.tsx app/components/datasets/create/file-uploader/index.tsx app/components/datasets/create/index.tsx app/components/datasets/create/notion-page-preview/index.module.css app/components/datasets/create/notion-page-preview/index.tsx app/components/datasets/create/step-one/index.module.css app/components/datasets/create/step-one/index.tsx app/components/datasets/create/step-three/index.tsx app/components/datasets/create/step-two/index.module.css app/components/datasets/create/step-two/index.tsx app/components/datasets/create/step-two/inputs.tsx app/components/datasets/create/step-two/language-select/index.tsx app/components/datasets/create/step-two/option-card.tsx app/components/datasets/create/step-two/preview-item/index.tsx app/components/datasets/create/step-two/unescape.ts app/components/datasets/create/stepper/index.tsx app/components/datasets/create/stepper/step.tsx app/components/datasets/create/steps-nav-bar/index.module.css app/components/datasets/create/steps-nav-bar/index.tsx app/components/datasets/create/stop-embedding-modal/index.tsx app/components/datasets/create/top-bar/index.tsx app/components/datasets/create/website/base/checkbox-with-label.tsx app/components/datasets/create/website/base/crawled-result-item.tsx app/components/datasets/create/website/base/crawled-result.tsx app/components/datasets/create/website/base/crawling.tsx app/components/datasets/create/website/base/error-message.tsx app/components/datasets/create/website/base/field.tsx app/components/datasets/create/website/base/input.tsx app/components/datasets/create/website/base/options-wrap.tsx app/components/datasets/create/website/base/url-input.tsx app/components/datasets/create/website/firecrawl/header.tsx app/components/datasets/create/website/firecrawl/index.tsx app/components/datasets/create/website/firecrawl/options.tsx app/components/datasets/create/website/index.module.css app/components/datasets/create/website/index.tsx app/components/datasets/create/website/jina-reader/base/checkbox-with-label.tsx app/components/datasets/create/website/jina-reader/base/error-message.tsx app/components/datasets/create/website/jina-reader/base/field.tsx app/components/datasets/create/website/jina-reader/base/input.tsx app/components/datasets/create/website/jina-reader/base/options-wrap.tsx app/components/datasets/create/website/jina-reader/crawled-result-item.tsx app/components/datasets/create/website/jina-reader/crawled-result.tsx app/components/datasets/create/website/jina-reader/crawling.tsx app/components/datasets/create/website/jina-reader/header.tsx app/components/datasets/create/website/jina-reader/index.tsx app/components/datasets/create/website/jina-reader/mock-crawl-result.ts app/components/datasets/create/website/jina-reader/options.tsx app/components/datasets/create/website/no-data.tsx app/components/datasets/create/website/preview.tsx app/components/datasets/documents/detail/batch-modal/csv-downloader.tsx app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx app/components/datasets/documents/detail/batch-modal/index.tsx app/components/datasets/documents/detail/completed/SegmentCard.tsx app/components/datasets/documents/detail/completed/child-segment-detail.tsx app/components/datasets/documents/detail/completed/child-segment-list.tsx app/components/datasets/documents/detail/completed/common/action-buttons.tsx app/components/datasets/documents/detail/completed/common/add-another.tsx app/components/datasets/documents/detail/completed/common/batch-action.tsx app/components/datasets/documents/detail/completed/common/chunk-content.tsx app/components/datasets/documents/detail/completed/common/dot.tsx app/components/datasets/documents/detail/completed/common/empty.tsx app/components/datasets/documents/detail/completed/common/full-screen-drawer.tsx app/components/datasets/documents/detail/completed/common/keywords.tsx app/components/datasets/documents/detail/completed/common/regeneration-modal.tsx app/components/datasets/documents/detail/completed/common/segment-index-tag.tsx app/components/datasets/documents/detail/completed/common/tag.tsx app/components/datasets/documents/detail/completed/display-toggle.tsx app/components/datasets/documents/detail/completed/index.tsx app/components/datasets/documents/detail/completed/new-child-segment.tsx app/components/datasets/documents/detail/completed/segment-card.tsx app/components/datasets/documents/detail/completed/segment-detail.tsx app/components/datasets/documents/detail/completed/segment-list.tsx app/components/datasets/documents/detail/completed/skeleton/full-doc-list-skeleton.tsx app/components/datasets/documents/detail/completed/skeleton/general-list-skeleton.tsx app/components/datasets/documents/detail/completed/skeleton/paragraph-list-skeleton.tsx app/components/datasets/documents/detail/completed/skeleton/parent-chunk-card-skeleton.tsx app/components/datasets/documents/detail/completed/status-item.tsx app/components/datasets/documents/detail/embedding/index.tsx app/components/datasets/documents/detail/embedding/skeleton/index.tsx app/components/datasets/documents/detail/index.tsx app/components/datasets/documents/detail/metadata/index.tsx app/components/datasets/documents/detail/new-segment.tsx app/components/datasets/documents/detail/segment-add/index.tsx app/components/datasets/documents/detail/settings/index.tsx app/components/datasets/documents/index.tsx app/components/datasets/documents/list.tsx app/components/datasets/documents/rename-modal.tsx app/components/datasets/documents/style.module.css app/components/datasets/external-api/external-api-modal/Form.tsx app/components/datasets/external-api/external-api-modal/index.tsx app/components/datasets/external-api/external-api-panel/index.tsx app/components/datasets/external-api/external-knowledge-api-card/index.tsx app/components/datasets/external-knowledge-base/create/ExternalApiSelect.tsx app/components/datasets/external-knowledge-base/create/ExternalApiSelection.tsx app/components/datasets/external-knowledge-base/create/InfoPanel.tsx app/components/datasets/external-knowledge-base/create/KnowledgeBaseInfo.tsx app/components/datasets/external-knowledge-base/create/RetrievalSettings.tsx app/components/datasets/external-knowledge-base/create/index.tsx app/components/datasets/formatted-text/flavours/edit-slice.tsx app/components/datasets/formatted-text/flavours/preview-slice.tsx app/components/datasets/formatted-text/flavours/shared.tsx app/components/datasets/hit-testing/components/child-chunks-item.tsx app/components/datasets/hit-testing/components/chunk-detail-modal.tsx app/components/datasets/hit-testing/components/result-item-external.tsx app/components/datasets/hit-testing/components/result-item-footer.tsx app/components/datasets/hit-testing/components/result-item-meta.tsx app/components/datasets/hit-testing/components/result-item.tsx app/components/datasets/hit-testing/components/score.tsx app/components/datasets/hit-testing/index.tsx app/components/datasets/hit-testing/modify-external-retrieval-modal.tsx app/components/datasets/hit-testing/modify-retrieval-modal.tsx app/components/datasets/hit-testing/style.module.css app/components/datasets/hit-testing/textarea.tsx app/components/datasets/preview/container.tsx app/components/datasets/preview/header.tsx app/components/datasets/rename-modal/index.tsx app/components/datasets/settings/form/index.tsx app/components/datasets/settings/index-method-radio/index.tsx app/components/datasets/settings/permission-selector/index.tsx app/components/develop/code.tsx app/components/develop/doc.tsx app/components/develop/index.tsx app/components/develop/md.tsx app/components/develop/secret-key/input-copy.tsx app/components/develop/secret-key/secret-key-button.tsx app/components/develop/secret-key/secret-key-generate.tsx app/components/develop/secret-key/secret-key-modal.tsx app/components/develop/secret-key/style.module.css app/components/develop/template/template.en.mdx app/components/develop/template/template.ja.mdx app/components/develop/template/template.zh.mdx app/components/develop/template/template_advanced_chat.en.mdx app/components/develop/template/template_advanced_chat.ja.mdx app/components/develop/template/template_advanced_chat.zh.mdx app/components/develop/template/template_chat.en.mdx app/components/develop/template/template_chat.ja.mdx app/components/develop/template/template_chat.zh.mdx app/components/develop/template/template_workflow.en.mdx app/components/develop/template/template_workflow.ja.mdx app/components/develop/template/template_workflow.zh.mdx app/components/explore/app-card/index.tsx app/components/explore/app-list/index.tsx app/components/explore/app-list/style.module.css app/components/explore/category.tsx app/components/explore/create-app-modal/index.tsx app/components/explore/index.tsx app/components/explore/installed-app/index.tsx app/components/explore/item-operation/index.tsx app/components/explore/item-operation/style.module.css app/components/explore/sidebar/app-nav-item/index.tsx app/components/explore/sidebar/app-nav-item/style.module.css app/components/explore/sidebar/index.tsx app/components/header/account-about/index.module.css app/components/header/account-about/index.tsx app/components/header/account-dropdown/index.tsx app/components/header/account-dropdown/workplace-selector/index.module.css app/components/header/account-dropdown/workplace-selector/index.tsx app/components/header/account-setting/Integrations-page/index.module.css app/components/header/account-setting/Integrations-page/index.tsx app/components/header/account-setting/api-based-extension-page/empty.tsx app/components/header/account-setting/api-based-extension-page/index.tsx app/components/header/account-setting/api-based-extension-page/item.tsx app/components/header/account-setting/api-based-extension-page/modal.tsx app/components/header/account-setting/api-based-extension-page/selector.tsx app/components/header/account-setting/collapse/index.tsx app/components/header/account-setting/data-source-page/data-source-notion/index.tsx app/components/header/account-setting/data-source-page/data-source-notion/operate/index.tsx app/components/header/account-setting/data-source-page/data-source-website/config-firecrawl-modal.tsx app/components/header/account-setting/data-source-page/data-source-website/config-jina-reader-modal.tsx app/components/header/account-setting/data-source-page/data-source-website/index.tsx app/components/header/account-setting/data-source-page/index.tsx app/components/header/account-setting/data-source-page/panel/config-item.tsx app/components/header/account-setting/data-source-page/panel/index.tsx app/components/header/account-setting/data-source-page/panel/style.module.css app/components/header/account-setting/index.module.css app/components/header/account-setting/index.tsx app/components/header/account-setting/key-validator/KeyInput.tsx app/components/header/account-setting/key-validator/Operate.tsx app/components/header/account-setting/key-validator/ValidateStatus.tsx app/components/header/account-setting/key-validator/index.tsx app/components/header/account-setting/language-page/index.module.css app/components/header/account-setting/members-page/index.tsx app/components/header/account-setting/members-page/invite-modal/index.tsx app/components/header/account-setting/members-page/invite-modal/role-selector.tsx app/components/header/account-setting/members-page/invited-modal/index.module.css app/components/header/account-setting/members-page/invited-modal/index.tsx app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx app/components/header/account-setting/members-page/operation/index.module.css app/components/header/account-setting/members-page/operation/index.tsx app/components/header/account-setting/model-provider-page/declarations.ts app/components/header/account-setting/model-provider-page/hooks.ts app/components/header/account-setting/model-provider-page/index.tsx app/components/header/account-setting/model-provider-page/model-icon/index.tsx app/components/header/account-setting/model-provider-page/model-modal/Form.tsx app/components/header/account-setting/model-provider-page/model-modal/Input.tsx app/components/header/account-setting/model-provider-page/model-modal/index.tsx app/components/header/account-setting/model-provider-page/model-modal/model-load-balancing-entry-modal.tsx app/components/header/account-setting/model-provider-page/model-name/index.tsx app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx app/components/header/account-setting/model-provider-page/model-parameter-modal/stop-sequence.tsx app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx app/components/header/account-setting/model-provider-page/model-selector/deprecated-model-trigger.tsx app/components/header/account-setting/model-provider-page/model-selector/empty-trigger.tsx app/components/header/account-setting/model-provider-page/model-selector/feature-icon.tsx app/components/header/account-setting/model-provider-page/model-selector/index.tsx app/components/header/account-setting/model-provider-page/model-selector/model-trigger.tsx app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx app/components/header/account-setting/model-provider-page/model-selector/popup.tsx app/components/header/account-setting/model-provider-page/model-selector/rerank-trigger.tsx app/components/header/account-setting/model-provider-page/provider-added-card/add-model-button.tsx app/components/header/account-setting/model-provider-page/provider-added-card/cooldown-timer.tsx app/components/header/account-setting/model-provider-page/provider-added-card/credential-panel.tsx app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx app/components/header/account-setting/model-provider-page/provider-added-card/priority-use-tip.tsx app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx app/components/header/account-setting/model-provider-page/provider-added-card/tab.tsx app/components/header/account-setting/model-provider-page/provider-card/index.module.css app/components/header/account-setting/model-provider-page/provider-card/index.tsx app/components/header/account-setting/model-provider-page/provider-icon/index.tsx app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx app/components/header/account-setting/model-provider-page/utils.ts app/components/header/account-setting/plugin-page/index.tsx app/components/header/app-back/index.tsx app/components/header/app-nav/index.tsx app/components/header/app-selector/index.tsx app/components/header/dataset-nav/index.tsx app/components/header/env-nav/index.tsx app/components/header/explore-nav/index.tsx app/components/header/github-star/index.tsx app/components/header/index.module.css app/components/header/index.tsx app/components/header/license-env/index.tsx app/components/header/maintenance-notice.tsx app/components/header/nav/index.tsx app/components/header/nav/nav-selector/index.tsx app/components/header/tools-nav/index.tsx app/components/i18n-server.tsx app/components/i18n.tsx app/components/sentry-initor.tsx app/components/share/text-generation/index.tsx app/components/share/text-generation/no-data/index.tsx app/components/share/text-generation/result/content.tsx app/components/share/text-generation/result/header.tsx app/components/share/text-generation/result/index.tsx app/components/share/text-generation/run-batch/csv-download/index.tsx app/components/share/text-generation/run-batch/csv-reader/index.tsx app/components/share/text-generation/run-batch/csv-reader/style.module.css app/components/share/text-generation/run-batch/index.tsx app/components/share/text-generation/run-batch/res-download/index.tsx app/components/share/text-generation/run-once/index.tsx app/components/share/text-generation/style.module.css app/components/share/utils.ts app/components/signin/countdown.tsx app/components/swr-initor.tsx app/components/tools/add-tool-modal/category.tsx app/components/tools/add-tool-modal/empty.tsx app/components/tools/add-tool-modal/index.tsx app/components/tools/add-tool-modal/tools.tsx app/components/tools/add-tool-modal/type.tsx app/components/tools/edit-custom-collection-modal/config-credentials.tsx app/components/tools/edit-custom-collection-modal/examples.ts app/components/tools/edit-custom-collection-modal/get-schema.tsx app/components/tools/edit-custom-collection-modal/index.tsx app/components/tools/edit-custom-collection-modal/test-api.tsx app/components/tools/labels/constant.ts app/components/tools/labels/filter.tsx app/components/tools/labels/selector.tsx app/components/tools/labels/store.ts app/components/tools/provider-list.tsx app/components/tools/provider/card.tsx app/components/tools/provider/contribute.tsx app/components/tools/provider/custom-create-card.tsx app/components/tools/provider/detail.tsx app/components/tools/provider/grid_bg.svg app/components/tools/provider/tool-item.tsx app/components/tools/setting/build-in/config-credentials.tsx app/components/tools/types.ts app/components/tools/utils/to-form-schema.ts app/components/tools/workflow-tool/configure-button.tsx app/components/tools/workflow-tool/confirm-modal/index.tsx app/components/tools/workflow-tool/confirm-modal/style.module.css app/components/tools/workflow-tool/index.tsx app/components/tools/workflow-tool/method-selector.tsx app/components/workflow/block-icon.tsx app/components/workflow/block-selector/all-tools.tsx app/components/workflow/block-selector/blocks.tsx app/components/workflow/block-selector/constants.tsx app/components/workflow/block-selector/hooks.ts app/components/workflow/block-selector/index-bar.tsx app/components/workflow/block-selector/index.tsx app/components/workflow/block-selector/tabs.tsx app/components/workflow/block-selector/tools.tsx app/components/workflow/block-selector/types.ts app/components/workflow/candidate-node.tsx app/components/workflow/constants.ts app/components/workflow/context.tsx app/components/workflow/custom-edge.tsx app/components/workflow/dsl-export-confirm-modal.tsx app/components/workflow/header/chat-variable-button.tsx app/components/workflow/header/checklist.tsx app/components/workflow/header/editing-title.tsx app/components/workflow/header/env-button.tsx app/components/workflow/header/global-variable-button.tsx app/components/workflow/header/index.tsx app/components/workflow/header/restoring-title.tsx app/components/workflow/header/run-and-history.tsx app/components/workflow/header/running-title.tsx app/components/workflow/header/undo-redo.tsx app/components/workflow/header/version-history-item.tsx app/components/workflow/header/version-history-modal.tsx app/components/workflow/header/view-history.tsx app/components/workflow/header/view-workflow-history.tsx app/components/workflow/help-line/index.tsx app/components/workflow/hooks/index.ts app/components/workflow/hooks/use-checklist.ts app/components/workflow/hooks/use-edges-interactions.ts app/components/workflow/hooks/use-helpline.ts app/components/workflow/hooks/use-nodes-data.ts app/components/workflow/hooks/use-nodes-interactions.ts app/components/workflow/hooks/use-nodes-sync-draft.ts app/components/workflow/hooks/use-workflow-interactions.ts app/components/workflow/hooks/use-workflow-run.ts app/components/workflow/hooks/use-workflow-start-run.tsx app/components/workflow/hooks/use-workflow-template.ts app/components/workflow/hooks/use-workflow-variables.ts app/components/workflow/hooks/use-workflow.ts app/components/workflow/index.tsx app/components/workflow/limit-tips.tsx app/components/workflow/nodes/_base/components/add-button.tsx app/components/workflow/nodes/_base/components/add-variable-popup-with-position.tsx app/components/workflow/nodes/_base/components/add-variable-popup.tsx app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx app/components/workflow/nodes/_base/components/before-run-form/form.tsx app/components/workflow/nodes/_base/components/before-run-form/index.tsx app/components/workflow/nodes/_base/components/code-generator-button.tsx app/components/workflow/nodes/_base/components/collapse/field-collapse.tsx app/components/workflow/nodes/_base/components/collapse/index.tsx app/components/workflow/nodes/_base/components/editor/base.tsx app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars.tsx app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx app/components/workflow/nodes/_base/components/editor/code-editor/style.css app/components/workflow/nodes/_base/components/editor/text-editor.tsx app/components/workflow/nodes/_base/components/error-handle/default-value.tsx app/components/workflow/nodes/_base/components/error-handle/error-handle-on-node.tsx app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx app/components/workflow/nodes/_base/components/error-handle/error-handle-tip.tsx app/components/workflow/nodes/_base/components/error-handle/error-handle-type-selector.tsx app/components/workflow/nodes/_base/components/error-handle/fail-branch-card.tsx app/components/workflow/nodes/_base/components/field.tsx app/components/workflow/nodes/_base/components/file-type-item.tsx app/components/workflow/nodes/_base/components/file-upload-setting.tsx app/components/workflow/nodes/_base/components/help-link.tsx app/components/workflow/nodes/_base/components/info-panel.tsx app/components/workflow/nodes/_base/components/input-number-with-slider.tsx app/components/workflow/nodes/_base/components/input-support-select-var.tsx app/components/workflow/nodes/_base/components/list-no-data-placeholder.tsx app/components/workflow/nodes/_base/components/memory-config.tsx app/components/workflow/nodes/_base/components/next-step/add.tsx app/components/workflow/nodes/_base/components/next-step/container.tsx app/components/workflow/nodes/_base/components/next-step/index.tsx app/components/workflow/nodes/_base/components/next-step/item.tsx app/components/workflow/nodes/_base/components/next-step/line.tsx app/components/workflow/nodes/_base/components/next-step/operator.tsx app/components/workflow/nodes/_base/components/node-control.tsx app/components/workflow/nodes/_base/components/node-handle.tsx app/components/workflow/nodes/_base/components/node-resizer.tsx app/components/workflow/nodes/_base/components/option-card.tsx app/components/workflow/nodes/_base/components/output-vars.tsx app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx app/components/workflow/nodes/_base/components/panel-operator/index.tsx app/components/workflow/nodes/_base/components/panel-operator/panel-operator-popup.tsx app/components/workflow/nodes/_base/components/prompt/editor.tsx app/components/workflow/nodes/_base/components/readonly-input-with-select-var.tsx app/components/workflow/nodes/_base/components/remove-button.tsx app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx app/components/workflow/nodes/_base/components/retry/retry-on-panel.tsx app/components/workflow/nodes/_base/components/retry/style.module.css app/components/workflow/nodes/_base/components/selector.tsx app/components/workflow/nodes/_base/components/support-var-input/index.tsx app/components/workflow/nodes/_base/components/title-description-input.tsx app/components/workflow/nodes/_base/components/toggle-expand-btn.tsx app/components/workflow/nodes/_base/components/variable-tag.tsx app/components/workflow/nodes/_base/components/variable/assigned-var-reference-popup.tsx app/components/workflow/nodes/_base/components/variable/constant-field.tsx app/components/workflow/nodes/_base/components/variable/output-var-list.tsx app/components/workflow/nodes/_base/components/variable/utils.ts app/components/workflow/nodes/_base/components/variable/var-list.tsx app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx app/components/workflow/nodes/_base/components/variable/var-type-picker.tsx app/components/workflow/nodes/_base/hooks/use-available-var-list.ts app/components/workflow/nodes/_base/hooks/use-node-help-link.ts app/components/workflow/nodes/_base/hooks/use-node-info.ts app/components/workflow/nodes/_base/hooks/use-one-step-run.ts app/components/workflow/nodes/_base/hooks/use-toggle-expend.ts app/components/workflow/nodes/_base/node.tsx app/components/workflow/nodes/_base/panel.tsx app/components/workflow/nodes/answer/panel.tsx app/components/workflow/nodes/assigner/components/operation-selector.tsx app/components/workflow/nodes/assigner/components/var-list/index.tsx app/components/workflow/nodes/assigner/default.ts app/components/workflow/nodes/assigner/node.tsx app/components/workflow/nodes/assigner/panel.tsx app/components/workflow/nodes/assigner/types.ts app/components/workflow/nodes/assigner/use-config.ts app/components/workflow/nodes/code/code-parser.ts app/components/workflow/nodes/code/dependency-picker.tsx app/components/workflow/nodes/code/panel.tsx app/components/workflow/nodes/code/use-config.ts app/components/workflow/nodes/code/utils.ts app/components/workflow/nodes/constants.ts app/components/workflow/nodes/document-extractor/default.ts app/components/workflow/nodes/document-extractor/node.tsx app/components/workflow/nodes/document-extractor/panel.tsx app/components/workflow/nodes/document-extractor/use-config.ts app/components/workflow/nodes/end/default.ts app/components/workflow/nodes/end/node.tsx app/components/workflow/nodes/end/panel.tsx app/components/workflow/nodes/http/components/api-input.tsx app/components/workflow/nodes/http/components/authorization/index.tsx app/components/workflow/nodes/http/components/authorization/radio-group.tsx app/components/workflow/nodes/http/components/curl-panel.tsx app/components/workflow/nodes/http/components/edit-body/index.tsx app/components/workflow/nodes/http/components/key-value/bulk-edit/index.tsx app/components/workflow/nodes/http/components/key-value/key-value-edit/index.tsx app/components/workflow/nodes/http/components/key-value/key-value-edit/input-item.tsx app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx app/components/workflow/nodes/http/components/timeout/index.tsx app/components/workflow/nodes/http/hooks/use-key-value-list.ts app/components/workflow/nodes/http/node.tsx app/components/workflow/nodes/http/panel.tsx app/components/workflow/nodes/http/use-config.ts app/components/workflow/nodes/if-else/components/condition-add.tsx app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx app/components/workflow/nodes/if-else/components/condition-list/condition-var-selector.tsx app/components/workflow/nodes/if-else/components/condition-list/index.tsx app/components/workflow/nodes/if-else/components/condition-number-input.tsx app/components/workflow/nodes/if-else/components/condition-value.tsx app/components/workflow/nodes/if-else/components/condition-wrap.tsx app/components/workflow/nodes/if-else/default.ts app/components/workflow/nodes/if-else/node.tsx app/components/workflow/nodes/if-else/panel.tsx app/components/workflow/nodes/if-else/types.ts app/components/workflow/nodes/if-else/use-config.ts app/components/workflow/nodes/if-else/use-is-var-file-attribute.ts app/components/workflow/nodes/iteration-start/index.tsx app/components/workflow/nodes/iteration/add-block.tsx app/components/workflow/nodes/iteration/node.tsx app/components/workflow/nodes/iteration/panel.tsx app/components/workflow/nodes/iteration/use-interactions.ts app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx app/components/workflow/nodes/knowledge-retrieval/node.tsx app/components/workflow/nodes/knowledge-retrieval/panel.tsx app/components/workflow/nodes/knowledge-retrieval/types.ts app/components/workflow/nodes/knowledge-retrieval/use-config.ts app/components/workflow/nodes/knowledge-retrieval/utils.ts app/components/workflow/nodes/list-operator/components/extract-input.tsx app/components/workflow/nodes/list-operator/components/filter-condition.tsx app/components/workflow/nodes/list-operator/components/limit-config.tsx app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx app/components/workflow/nodes/list-operator/node.tsx app/components/workflow/nodes/list-operator/panel.tsx app/components/workflow/nodes/list-operator/use-config.ts app/components/workflow/nodes/llm/components/config-prompt-item.tsx app/components/workflow/nodes/llm/components/config-prompt.tsx app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx app/components/workflow/nodes/llm/components/resolution-picker.tsx app/components/workflow/nodes/llm/node.tsx app/components/workflow/nodes/llm/panel.tsx app/components/workflow/nodes/llm/types.ts app/components/workflow/nodes/llm/use-config.ts app/components/workflow/nodes/llm/utils.ts app/components/workflow/nodes/parameter-extractor/components/extract-parameter/import-from-tool.tsx app/components/workflow/nodes/parameter-extractor/components/extract-parameter/item.tsx app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx app/components/workflow/nodes/parameter-extractor/node.tsx app/components/workflow/nodes/parameter-extractor/panel.tsx app/components/workflow/nodes/parameter-extractor/use-config.ts app/components/workflow/nodes/question-classifier/components/class-item.tsx app/components/workflow/nodes/question-classifier/components/class-list.tsx app/components/workflow/nodes/question-classifier/node.tsx app/components/workflow/nodes/question-classifier/panel.tsx app/components/workflow/nodes/question-classifier/use-config.ts app/components/workflow/nodes/question-classifier/utils.ts app/components/workflow/nodes/start/components/var-item.tsx app/components/workflow/nodes/start/components/var-list.tsx app/components/workflow/nodes/start/node.tsx app/components/workflow/nodes/start/panel.tsx app/components/workflow/nodes/template-transform/panel.tsx app/components/workflow/nodes/tool/components/input-var-list.tsx app/components/workflow/nodes/tool/node.tsx app/components/workflow/nodes/tool/panel.tsx app/components/workflow/nodes/tool/types.ts app/components/workflow/nodes/tool/use-config.ts app/components/workflow/nodes/variable-assigner/components/add-variable/index.tsx app/components/workflow/nodes/variable-assigner/components/node-group-item.tsx app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx app/components/workflow/nodes/variable-assigner/components/var-list/index.tsx app/components/workflow/nodes/variable-assigner/default.ts app/components/workflow/nodes/variable-assigner/node.tsx app/components/workflow/nodes/variable-assigner/panel.tsx app/components/workflow/nodes/variable-assigner/use-config.ts app/components/workflow/note-node/index.tsx app/components/workflow/note-node/note-editor/context.tsx app/components/workflow/note-node/note-editor/editor.tsx app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/component.tsx app/components/workflow/note-node/note-editor/theme/theme.css app/components/workflow/note-node/note-editor/toolbar/color-picker.tsx app/components/workflow/note-node/note-editor/toolbar/command.tsx app/components/workflow/note-node/note-editor/toolbar/divider.tsx app/components/workflow/note-node/note-editor/toolbar/font-size-selector.tsx app/components/workflow/note-node/note-editor/toolbar/index.tsx app/components/workflow/note-node/note-editor/toolbar/operator.tsx app/components/workflow/note-node/note-editor/utils.ts app/components/workflow/operator/add-block.tsx app/components/workflow/operator/control.tsx app/components/workflow/operator/index.tsx app/components/workflow/operator/tip-popup.tsx app/components/workflow/operator/zoom-in-out.tsx app/components/workflow/panel-contextmenu.tsx app/components/workflow/panel/chat-record/index.tsx app/components/workflow/panel/chat-record/user-input.tsx app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx app/components/workflow/panel/chat-variable-panel/components/object-value-list.tsx app/components/workflow/panel/chat-variable-panel/components/variable-item.tsx app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx app/components/workflow/panel/chat-variable-panel/index.tsx app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx app/components/workflow/panel/debug-and-preview/conversation-variable-modal.tsx app/components/workflow/panel/debug-and-preview/empty.tsx app/components/workflow/panel/debug-and-preview/hooks.ts app/components/workflow/panel/debug-and-preview/index.tsx app/components/workflow/panel/debug-and-preview/user-input.tsx app/components/workflow/panel/env-panel/env-item.tsx app/components/workflow/panel/env-panel/index.tsx app/components/workflow/panel/env-panel/variable-modal.tsx app/components/workflow/panel/env-panel/variable-trigger.tsx app/components/workflow/panel/global-variable-panel/index.tsx app/components/workflow/panel/global-variable-panel/item.tsx app/components/workflow/panel/index.tsx app/components/workflow/panel/inputs-panel.tsx app/components/workflow/panel/record.tsx app/components/workflow/panel/workflow-preview.tsx app/components/workflow/run/index.tsx app/components/workflow/run/iteration-result-panel.tsx app/components/workflow/run/meta.tsx app/components/workflow/run/node.tsx app/components/workflow/run/output-panel.tsx app/components/workflow/run/result-panel.tsx app/components/workflow/run/result-text.tsx app/components/workflow/run/retry-result-panel.tsx app/components/workflow/run/status-container.tsx app/components/workflow/run/status.tsx app/components/workflow/run/tracing-panel.tsx app/components/workflow/shortcuts-name.tsx app/components/workflow/store.ts app/components/workflow/style.css app/components/workflow/types.ts app/components/workflow/update-dsl-modal.tsx app/components/workflow/utils.ts app/components/workflow/workflow-history-store.tsx app/forgot-password/ChangePasswordForm.tsx app/forgot-password/ForgotPasswordForm.tsx app/forgot-password/page.tsx app/init/InitPasswordPopup.tsx app/init/page.tsx app/install/installForm.tsx app/install/page.tsx app/layout.tsx app/page.module.css app/page.tsx app/reset-password/check-code/page.tsx app/reset-password/layout.tsx app/reset-password/page.tsx app/reset-password/set-password/page.tsx app/signin/_header.tsx app/signin/assets/background.png app/signin/check-code/page.tsx app/signin/components/mail-and-code-auth.tsx app/signin/components/mail-and-password-auth.tsx app/signin/components/sso-auth.tsx app/signin/invite-settings/page.tsx app/signin/layout.tsx app/signin/normalForm.tsx app/signin/oneMoreStep.tsx app/signin/page.module.css app/styles/globals.css app/styles/markdown.scss app/styles/preflight.css config/index.ts context/app-context.tsx context/datasets-context.tsx context/debug-configuration.ts context/explore-context.ts context/i18n.ts context/modal-context.tsx context/provider-context.tsx docker/entrypoint.sh hooks/use-metadata.ts hooks/use-pay.tsx hooks/use-tab-searchparams.ts hooks/use-timestamp.ts i18n/auto-gen-i18n.js i18n/check-i18n.js i18n/de-DE/app-overview.ts i18n/de-DE/app.ts i18n/de-DE/billing.ts i18n/de-DE/common.ts i18n/de-DE/custom.ts i18n/de-DE/dataset-creation.ts i18n/de-DE/dataset-documents.ts i18n/de-DE/dataset-hit-testing.ts i18n/de-DE/dataset-settings.ts i18n/de-DE/dataset.ts i18n/de-DE/explore.ts i18n/de-DE/run-log.ts i18n/de-DE/share-app.ts i18n/de-DE/tools.ts i18n/de-DE/workflow.ts i18n/en-US/app-debug.ts i18n/en-US/app-overview.ts i18n/en-US/app.ts i18n/en-US/billing.ts i18n/en-US/common.ts i18n/en-US/custom.ts i18n/en-US/dataset-creation.ts i18n/en-US/dataset-documents.ts i18n/en-US/dataset-settings.ts i18n/en-US/dataset.ts i18n/en-US/explore.ts i18n/en-US/login.ts i18n/en-US/run-log.ts i18n/en-US/share-app.ts i18n/en-US/tools.ts i18n/en-US/workflow.ts i18n/es-ES/app-overview.ts i18n/es-ES/app.ts i18n/es-ES/billing.ts i18n/es-ES/common.ts i18n/es-ES/custom.ts i18n/es-ES/dataset-creation.ts i18n/es-ES/dataset-documents.ts i18n/es-ES/dataset-settings.ts i18n/es-ES/dataset.ts i18n/es-ES/explore.ts i18n/es-ES/run-log.ts i18n/es-ES/share-app.ts i18n/es-ES/tools.ts i18n/es-ES/workflow.ts i18n/fa-IR/app-overview.ts i18n/fa-IR/app.ts i18n/fa-IR/billing.ts i18n/fa-IR/common.ts i18n/fa-IR/custom.ts i18n/fa-IR/dataset-creation.ts i18n/fa-IR/dataset-documents.ts i18n/fa-IR/dataset-settings.ts i18n/fa-IR/dataset.ts i18n/fa-IR/explore.ts i18n/fa-IR/run-log.ts i18n/fa-IR/share-app.ts i18n/fa-IR/tools.ts i18n/fa-IR/workflow.ts i18n/fr-FR/app-overview.ts i18n/fr-FR/app.ts i18n/fr-FR/billing.ts i18n/fr-FR/common.ts i18n/fr-FR/custom.ts i18n/fr-FR/dataset-creation.ts i18n/fr-FR/dataset-documents.ts i18n/fr-FR/dataset-settings.ts i18n/fr-FR/dataset.ts i18n/fr-FR/explore.ts i18n/fr-FR/run-log.ts i18n/fr-FR/share-app.ts i18n/fr-FR/tools.ts i18n/fr-FR/workflow.ts i18n/hi-IN/app-overview.ts i18n/hi-IN/app.ts i18n/hi-IN/billing.ts i18n/hi-IN/common.ts i18n/hi-IN/custom.ts i18n/hi-IN/dataset-creation.ts i18n/hi-IN/dataset-documents.ts i18n/hi-IN/dataset-settings.ts i18n/hi-IN/dataset.ts i18n/hi-IN/explore.ts i18n/hi-IN/run-log.ts i18n/hi-IN/share-app.ts i18n/hi-IN/tools.ts i18n/hi-IN/workflow.ts i18n/i18next-config.ts i18n/index.ts i18n/it-IT/app-overview.ts i18n/it-IT/app.ts i18n/it-IT/billing.ts i18n/it-IT/common.ts i18n/it-IT/custom.ts i18n/it-IT/dataset-creation.ts i18n/it-IT/dataset-documents.ts i18n/it-IT/dataset-settings.ts i18n/it-IT/dataset.ts i18n/it-IT/explore.ts i18n/it-IT/run-log.ts i18n/it-IT/share-app.ts i18n/it-IT/tools.ts i18n/it-IT/workflow.ts i18n/ja-JP/app-annotation.ts i18n/ja-JP/app-overview.ts i18n/ja-JP/app.ts i18n/ja-JP/billing.ts i18n/ja-JP/common.ts i18n/ja-JP/custom.ts i18n/ja-JP/dataset-creation.ts i18n/ja-JP/dataset-documents.ts i18n/ja-JP/dataset-settings.ts i18n/ja-JP/dataset.ts i18n/ja-JP/explore.ts i18n/ja-JP/run-log.ts i18n/ja-JP/share-app.ts i18n/ja-JP/tools.ts i18n/ja-JP/workflow.ts i18n/ko-KR/app-overview.ts i18n/ko-KR/app.ts i18n/ko-KR/billing.ts i18n/ko-KR/common.ts i18n/ko-KR/custom.ts i18n/ko-KR/dataset-creation.ts i18n/ko-KR/dataset-documents.ts i18n/ko-KR/dataset-settings.ts i18n/ko-KR/dataset.ts i18n/ko-KR/explore.ts i18n/ko-KR/run-log.ts i18n/ko-KR/share-app.ts i18n/ko-KR/tools.ts i18n/ko-KR/workflow.ts i18n/language.ts i18n/pl-PL/app-overview.ts i18n/pl-PL/app.ts i18n/pl-PL/billing.ts i18n/pl-PL/common.ts i18n/pl-PL/custom.ts i18n/pl-PL/dataset-creation.ts i18n/pl-PL/dataset-documents.ts i18n/pl-PL/dataset-settings.ts i18n/pl-PL/dataset.ts i18n/pl-PL/explore.ts i18n/pl-PL/run-log.ts i18n/pl-PL/share-app.ts i18n/pl-PL/tools.ts i18n/pl-PL/workflow.ts i18n/pt-BR/app-overview.ts i18n/pt-BR/app.ts i18n/pt-BR/billing.ts i18n/pt-BR/common.ts i18n/pt-BR/custom.ts i18n/pt-BR/dataset-creation.ts i18n/pt-BR/dataset-documents.ts i18n/pt-BR/dataset-settings.ts i18n/pt-BR/dataset.ts i18n/pt-BR/explore.ts i18n/pt-BR/run-log.ts i18n/pt-BR/share-app.ts i18n/pt-BR/tools.ts i18n/pt-BR/workflow.ts i18n/ro-RO/app-overview.ts i18n/ro-RO/app.ts i18n/ro-RO/billing.ts i18n/ro-RO/common.ts i18n/ro-RO/custom.ts i18n/ro-RO/dataset-creation.ts i18n/ro-RO/dataset-documents.ts i18n/ro-RO/dataset-settings.ts i18n/ro-RO/dataset.ts i18n/ro-RO/explore.ts i18n/ro-RO/run-log.ts i18n/ro-RO/share-app.ts i18n/ro-RO/tools.ts i18n/ro-RO/workflow.ts i18n/ru-RU/app-overview.ts i18n/ru-RU/app.ts i18n/ru-RU/billing.ts i18n/ru-RU/common.ts i18n/ru-RU/custom.ts i18n/ru-RU/dataset-creation.ts i18n/ru-RU/dataset-documents.ts i18n/ru-RU/dataset-settings.ts i18n/ru-RU/dataset.ts i18n/ru-RU/explore.ts i18n/ru-RU/run-log.ts i18n/ru-RU/share-app.ts i18n/ru-RU/tools.ts i18n/ru-RU/workflow.ts i18n/server.ts i18n/sl-SI/app-overview.ts i18n/sl-SI/app.ts i18n/sl-SI/billing.ts i18n/sl-SI/common.ts i18n/sl-SI/custom.ts i18n/sl-SI/dataset-creation.ts i18n/sl-SI/dataset-documents.ts i18n/sl-SI/dataset-settings.ts i18n/sl-SI/dataset.ts i18n/sl-SI/explore.ts i18n/sl-SI/run-log.ts i18n/sl-SI/share-app.ts i18n/sl-SI/tools.ts i18n/sl-SI/workflow.ts i18n/th-TH/app-debug.ts i18n/th-TH/app-overview.ts i18n/th-TH/app.ts i18n/th-TH/billing.ts i18n/th-TH/common.ts i18n/th-TH/custom.ts i18n/th-TH/dataset-creation.ts i18n/th-TH/dataset-documents.ts i18n/th-TH/dataset-settings.ts i18n/th-TH/dataset.ts i18n/th-TH/explore.ts i18n/th-TH/run-log.ts i18n/th-TH/share-app.ts i18n/th-TH/tools.ts i18n/th-TH/workflow.ts i18n/tr-TR/app-overview.ts i18n/tr-TR/app.ts i18n/tr-TR/billing.ts i18n/tr-TR/common.ts i18n/tr-TR/custom.ts i18n/tr-TR/dataset-creation.ts i18n/tr-TR/dataset-documents.ts i18n/tr-TR/dataset-settings.ts i18n/tr-TR/dataset.ts i18n/tr-TR/explore.ts i18n/tr-TR/run-log.ts i18n/tr-TR/share-app.ts i18n/tr-TR/tools.ts i18n/tr-TR/workflow.ts i18n/uk-UA/app-overview.ts i18n/uk-UA/app.ts i18n/uk-UA/billing.ts i18n/uk-UA/common.ts i18n/uk-UA/custom.ts i18n/uk-UA/dataset-creation.ts i18n/uk-UA/dataset-documents.ts i18n/uk-UA/dataset-settings.ts i18n/uk-UA/dataset.ts i18n/uk-UA/explore.ts i18n/uk-UA/run-log.ts i18n/uk-UA/share-app.ts i18n/uk-UA/tools.ts i18n/uk-UA/workflow.ts i18n/vi-VN/app-overview.ts i18n/vi-VN/app.ts i18n/vi-VN/billing.ts i18n/vi-VN/common.ts i18n/vi-VN/custom.ts i18n/vi-VN/dataset-creation.ts i18n/vi-VN/dataset-documents.ts i18n/vi-VN/dataset-settings.ts i18n/vi-VN/dataset.ts i18n/vi-VN/explore.ts i18n/vi-VN/run-log.ts i18n/vi-VN/share-app.ts i18n/vi-VN/tools.ts i18n/vi-VN/workflow.ts i18n/zh-Hans/app-debug.ts i18n/zh-Hans/app-overview.ts i18n/zh-Hans/app.ts i18n/zh-Hans/billing.ts i18n/zh-Hans/common.ts i18n/zh-Hans/custom.ts i18n/zh-Hans/dataset-creation.ts i18n/zh-Hans/dataset-documents.ts i18n/zh-Hans/dataset-settings.ts i18n/zh-Hans/dataset.ts i18n/zh-Hans/explore.ts i18n/zh-Hans/run-log.ts i18n/zh-Hans/share-app.ts i18n/zh-Hans/tools.ts i18n/zh-Hans/workflow.ts i18n/zh-Hant/app-overview.ts i18n/zh-Hant/app.ts i18n/zh-Hant/billing.ts i18n/zh-Hant/common.ts i18n/zh-Hant/custom.ts i18n/zh-Hant/dataset-creation.ts i18n/zh-Hant/dataset-documents.ts i18n/zh-Hant/dataset-settings.ts i18n/zh-Hant/dataset.ts i18n/zh-Hant/explore.ts i18n/zh-Hant/run-log.ts i18n/zh-Hant/share-app.ts i18n/zh-Hant/tools.ts i18n/zh-Hant/workflow.ts jest.config.ts middleware.ts models/app.ts models/common.ts models/datasets.ts models/debug.ts next.config.js package.json public/embed.js public/embed.min.js public/favicon.ico public/logo/logo-embedded-chat-header.png public/logo/logo-site-dark.png public/logo/logo-site.png public/vs/language/typescript/tsWorker.js service/base.ts service/common.ts service/datasets.ts service/knowledge/use-create-dataset.ts service/knowledge/use-dateset.ts service/knowledge/use-document.ts service/log.ts service/refresh-token.ts service/share.ts service/tools.ts service/use-base.ts service/use-workflow.ts service/workflow.ts tailwind.config.js themes/dark.css themes/light.css themes/manual-dark.css themes/manual-light.css themes/tailwind-theme-var-define.ts types/app.ts types/feature.ts types/workflow.ts typography.js utils/app-redirection.ts utils/classnames.spec.ts utils/format.spec.ts utils/format.ts utils/index.ts utils/model-config.ts utils/timezone.json utils/var.ts yarn.lock