From dfcc0f8863b58d87ae579450b65509a54f7eb49d Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Fri, 17 Apr 2026 16:46:11 +0800 Subject: [PATCH 01/15] refactor(dify-ui): finish primitive migration from web/base/ui to @langgenius/dify-ui (#35349) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .github/workflows/web-tests.yml | 31 +++ codecov.yml | 9 + eslint-suppressions.json | 5 - packages/dify-ui/.gitignore | 3 + packages/dify-ui/.storybook/main.ts | 27 +++ packages/dify-ui/.storybook/preview.tsx | 31 +++ packages/dify-ui/.storybook/storybook.css | 19 ++ packages/dify-ui/AGENTS.md | 11 +- packages/dify-ui/package.json | 93 ++++++++- .../alert-dialog/__tests__/index.spec.tsx | 0 .../src}/alert-dialog/index.stories.tsx | 2 +- .../dify-ui/src}/alert-dialog/index.tsx | 6 +- .../src}/avatar/__tests__/index.spec.tsx | 0 .../dify-ui/src}/avatar/index.stories.tsx | 2 +- .../dify-ui/src}/avatar/index.tsx | 2 +- .../src}/button/__tests__/index.spec.tsx | 0 .../dify-ui/src}/button/index.stories.tsx | 2 +- .../dify-ui/src}/button/index.tsx | 2 +- .../context-menu/__tests__/index.spec.tsx | 0 .../src}/context-menu/index.stories.tsx | 2 +- .../dify-ui/src}/context-menu/index.tsx | 13 +- .../src}/dialog/__tests__/index.spec.tsx | 0 .../dify-ui/src}/dialog/index.stories.tsx | 2 +- .../dify-ui/src}/dialog/index.tsx | 2 +- .../dropdown-menu/__tests__/index.spec.tsx | 0 .../src}/dropdown-menu/index.stories.tsx | 2 +- .../dify-ui/src}/dropdown-menu/index.tsx | 12 +- .../number-field/__tests__/index.spec.tsx | 0 .../src}/number-field/index.stories.tsx | 4 +- .../dify-ui/src}/number-field/index.tsx | 2 +- .../dify-ui/src}/overlay-shared.ts | 0 .../ui => packages/dify-ui/src}/placement.ts | 0 .../src}/popover/__tests__/index.spec.tsx | 0 .../dify-ui/src}/popover/index.stories.tsx | 6 +- .../dify-ui/src}/popover/index.tsx | 8 +- .../src}/scroll-area/__tests__/index.spec.tsx | 0 .../src}/scroll-area/index.stories.tsx | 18 +- .../dify-ui/src}/scroll-area/index.tsx | 2 +- .../src}/select/__tests__/index.spec.tsx | 0 .../dify-ui/src}/select/index.stories.tsx | 2 +- .../dify-ui/src}/select/index.tsx | 11 +- .../src}/slider/__tests__/index.spec.tsx | 0 .../dify-ui/src}/slider/index.stories.tsx | 2 +- .../dify-ui/src}/slider/index.tsx | 2 +- .../src}/switch/__tests__/index.spec.tsx | 3 +- .../dify-ui/src}/switch/index.stories.tsx | 14 +- .../dify-ui/src}/switch/index.tsx | 85 ++++++--- .../src}/toast/__tests__/index.spec.tsx | 0 .../dify-ui/src}/toast/index.stories.tsx | 2 +- .../dify-ui/src}/toast/index.tsx | 2 +- .../src}/tooltip/__tests__/index.spec.tsx | 0 .../dify-ui/src}/tooltip/index.stories.tsx | 4 +- .../dify-ui/src}/tooltip/index.tsx | 8 +- packages/dify-ui/tailwind.config.ts | 23 +++ packages/dify-ui/tests/setup.ts | 44 +++++ packages/dify-ui/tsconfig.json | 12 +- packages/dify-ui/vite.config.ts | 27 +++ packages/tsconfig/nextjs.json | 2 +- packages/tsconfig/package.json | 2 +- packages/tsconfig/{web.json => react.json} | 0 pnpm-lock.yaml | 179 ++++++++++++++++++ pnpm-workspace.yaml | 1 + web/.storybook/preview.tsx | 2 +- web/__tests__/app/app-publisher-flow.test.tsx | 2 +- .../apps/app-card-operations-flow.test.tsx | 2 +- .../billing/cloud-plan-payment-flow.test.tsx | 2 +- .../billing/self-hosted-plan-flow.test.tsx | 2 +- .../datasets/create-dataset-flow.test.tsx | 2 +- .../datasets/dataset-settings-flow.test.tsx | 4 +- .../explore/sidebar-lifecycle-flow.test.tsx | 4 +- .../plugins/plugin-install-flow.test.ts | 2 +- .../dsl-export-import-flow.test.ts | 2 +- .../tools/tool-provider-detail-flow.test.tsx | 2 +- .../[appId]/overview/card-view.tsx | 2 +- .../[appId]/overview/tracing/config-popup.tsx | 2 +- .../[appId]/overview/tracing/panel.tsx | 2 +- .../tracing/provider-config-modal.tsx | 22 +-- .../(humanInputLayout)/form/[token]/form.tsx | 4 +- .../webapp-reset-password/check-code/page.tsx | 4 +- .../webapp-reset-password/page.tsx | 4 +- .../set-password/page.tsx | 4 +- .../webapp-signin/check-code/page.tsx | 4 +- .../components/external-member-sso-auth.tsx | 2 +- .../components/mail-and-code-auth.tsx | 4 +- .../components/mail-and-password-auth.tsx | 4 +- .../webapp-signin/components/sso-auth.tsx | 4 +- .../account-page/AvatarWithEdit.tsx | 10 +- .../account-page/email-change-modal.tsx | 6 +- .../(commonLayout)/account-page/index.tsx | 6 +- web/app/account/(commonLayout)/avatar.tsx | 2 +- .../delete-account/components/check-email.tsx | 2 +- .../delete-account/components/feed-back.tsx | 4 +- .../components/verify-email.tsx | 2 +- web/app/account/(commonLayout)/header.tsx | 2 +- web/app/account/oauth/authorize/page.tsx | 6 +- web/app/activate/activateForm.tsx | 2 +- .../__tests__/app-sidebar-dropdown.spec.tsx | 2 +- .../dataset-sidebar-dropdown.spec.tsx | 2 +- .../__tests__/app-info-detail-panel.spec.tsx | 2 +- .../__tests__/app-operations.spec.tsx | 4 +- .../__tests__/use-app-info-actions.spec.ts | 2 +- .../app-info/app-info-detail-panel.tsx | 2 +- .../app-sidebar/app-info/app-info-modals.tsx | 10 +- .../app-sidebar/app-info/app-operations.tsx | 10 +- .../app-info/use-app-info-actions.ts | 2 +- .../app-sidebar/app-sidebar-dropdown.tsx | 10 +- .../__tests__/dropdown-callbacks.spec.tsx | 2 +- .../app-sidebar/dataset-info/dropdown.tsx | 30 +-- .../app-sidebar/dataset-sidebar-dropdown.tsx | 10 +- .../components/app-sidebar/toggle-button.tsx | 2 +- .../app/annotation/__tests__/index.spec.tsx | 2 +- .../__tests__/index.spec.tsx | 2 +- .../annotation/add-annotation-modal/index.tsx | 4 +- .../app/annotation/batch-action.tsx | 14 +- .../__tests__/csv-uploader.spec.tsx | 2 +- .../__tests__/index.spec.tsx | 2 +- .../csv-uploader.tsx | 4 +- .../batch-add-annotation-modal/index.tsx | 4 +- .../index.tsx | 6 +- .../__tests__/index.spec.tsx | 2 +- .../edit-annotation-modal/edit-item/index.tsx | 2 +- .../edit-annotation-modal/index.tsx | 14 +- .../app/annotation/header-opts/index.tsx | 12 +- web/app/components/app/annotation/index.tsx | 4 +- .../remove-annotation-confirm-modal/index.tsx | 6 +- .../view-annotation-modal/index.tsx | 16 +- .../__tests__/access-control.spec.tsx | 2 +- .../__tests__/index.spec.tsx | 2 +- .../add-member-or-group-pop.tsx | 6 +- .../app/app-access-control/index.tsx | 4 +- .../specific-groups-or-members.tsx | 2 +- .../app-publisher/__tests__/index.spec.tsx | 2 +- .../publish-with-multiple-model.spec.tsx | 2 +- .../__tests__/version-info-modal.spec.tsx | 4 +- .../app/app-publisher/features-wrapper.tsx | 16 +- .../components/app/app-publisher/index.tsx | 6 +- .../publish-with-multiple-model.tsx | 10 +- .../components/app/app-publisher/sections.tsx | 12 +- .../app/app-publisher/version-info-modal.tsx | 4 +- .../warning-mask/cannot-query-dataset.tsx | 2 +- .../base/warning-mask/formatting-changed.tsx | 2 +- .../__tests__/advanced-prompt-input.spec.tsx | 2 +- .../__tests__/simple-prompt-input.spec.tsx | 2 +- .../config-prompt/advanced-prompt-input.tsx | 14 +- .../config-prompt/confirm-add-var/index.tsx | 2 +- .../conversation-history/edit-modal.tsx | 2 +- .../app/configuration/config-prompt/index.tsx | 2 +- .../config-prompt/simple-prompt-input.tsx | 12 +- .../config-var/__tests__/index.spec.tsx | 2 +- .../__tests__/form-fields.spec.tsx | 4 +- .../__tests__/index-logic.spec.tsx | 2 +- .../config-modal/__tests__/index.spec.tsx | 2 +- .../config-var/config-modal/form-fields.tsx | 12 +- .../config-var/config-modal/index.tsx | 2 +- .../app/configuration/config-var/index.tsx | 22 +-- .../configuration/config-var/modal-foot.tsx | 2 +- .../app/configuration/config-vision/index.tsx | 2 +- .../config-vision/param-config.tsx | 4 +- .../config/agent-setting-button.tsx | 2 +- .../agent-setting/__tests__/index.spec.tsx | 2 +- .../config/agent/agent-setting/index.tsx | 4 +- .../config/agent/agent-tools/index.tsx | 4 +- .../agent-tools/setting-built-in-tool.tsx | 2 +- .../__tests__/get-automatic-res.spec.tsx | 2 +- .../automatic/__tests__/result.spec.tsx | 4 +- .../config/automatic/automatic-btn.tsx | 2 +- .../config/automatic/get-automatic-res.tsx | 22 +-- .../configuration/config/automatic/result.tsx | 4 +- .../__tests__/get-code-generator-res.spec.tsx | 2 +- .../code-generator/get-code-generator-res.tsx | 22 +-- .../app/configuration/config/config-audio.tsx | 4 +- .../configuration/config/config-document.tsx | 4 +- .../app/configuration/configuration-view.tsx | 20 +- .../configuration/ctrl-btn-group/index.tsx | 2 +- .../__tests__/config-content.spec.tsx | 2 +- .../params-config/__tests__/index.spec.tsx | 2 +- .../params-config/config-content.tsx | 4 +- .../dataset-config/params-config/index.tsx | 4 +- .../params-config/weighted-score.tsx | 2 +- .../dataset-config/select-dataset/index.tsx | 2 +- .../settings-modal/__tests__/index.spec.tsx | 2 +- .../dataset-config/settings-modal/index.tsx | 4 +- .../debug/__tests__/index.spec.tsx | 2 +- .../__tests__/chat-item.spec.tsx | 2 +- .../debug-with-multiple-model/chat-item.tsx | 2 +- .../debug-with-multiple-model/debug-item.tsx | 8 +- .../debug/debug-with-single-model/index.tsx | 2 +- .../app/configuration/debug/index.tsx | 14 +- .../__tests__/use-configuration-utils.spec.ts | 2 +- .../hooks/use-configuration-utils.ts | 2 +- .../prompt-value-panel/index.tsx | 2 +- .../external-data-tool-modal.spec.tsx | 2 +- .../tools/external-data-tool-modal.tsx | 8 +- .../app/create-app-dialog/app-card/index.tsx | 2 +- .../app-list/__tests__/index.spec.tsx | 2 +- .../app/create-app-dialog/app-list/index.tsx | 2 +- .../create-app-modal/__tests__/index.spec.tsx | 2 +- .../components/app/create-app-modal/index.tsx | 6 +- .../__tests__/index.spec.tsx | 2 +- .../__tests__/uploader.spec.tsx | 4 +- .../dsl-confirm-modal.tsx | 2 +- .../app/create-from-dsl-modal/index.tsx | 4 +- .../app/create-from-dsl-modal/uploader.tsx | 2 +- .../duplicate-modal/__tests__/index.spec.tsx | 2 +- .../components/app/duplicate-modal/index.tsx | 4 +- .../components/app/in-site-message/index.tsx | 2 +- .../app/log/__tests__/model-info.spec.tsx | 2 +- web/app/components/app/log/list.tsx | 2 +- web/app/components/app/log/model-info.tsx | 2 +- .../overview/__tests__/trigger-card.spec.tsx | 4 +- .../app/overview/apikey-info-panel/index.tsx | 2 +- .../app/overview/app-card-sections.tsx | 14 +- web/app/components/app/overview/app-card.tsx | 2 +- .../app/overview/customize/index.tsx | 2 +- .../settings/__tests__/index.spec.tsx | 2 +- .../app/overview/settings/index.tsx | 6 +- .../components/app/overview/trigger-card.tsx | 2 +- .../switch-app-modal/__tests__/index.spec.tsx | 2 +- .../components/app/switch-app-modal/index.tsx | 22 +-- .../item/__tests__/action-groups.spec.tsx | 2 +- .../item/__tests__/index.spec.tsx | 2 +- .../app/text-generate/item/action-groups.tsx | 2 +- .../app/text-generate/item/index.tsx | 2 +- .../saved-items/__tests__/index.spec.tsx | 4 +- .../app/text-generate/saved-items/index.tsx | 2 +- .../saved-items/no-data/index.tsx | 2 +- .../components/app/type-selector/index.tsx | 10 +- .../apps/__tests__/app-card.spec.tsx | 16 +- web/app/components/apps/app-card.tsx | 24 +-- .../agent-log-modal/__tests__/detail.spec.tsx | 2 +- .../agent-log-modal/__tests__/index.spec.tsx | 2 +- .../base/agent-log-modal/detail.tsx | 2 +- .../base/agent-log-modal/index.stories.tsx | 2 +- .../components/base/app-icon-picker/index.tsx | 2 +- .../base/audio-btn/__tests__/audio.spec.ts | 2 +- web/app/components/base/audio-btn/audio.ts | 2 +- .../base/audio-gallery/AudioPlayer.tsx | 2 +- .../__tests__/AudioPlayer.spec.tsx | 2 +- .../base/block-input/__tests__/index.spec.tsx | 2 +- web/app/components/base/block-input/index.tsx | 2 +- .../__tests__/hooks.spec.tsx | 2 +- .../chat/chat-with-history/chat-wrapper.tsx | 2 +- .../chat-with-history/header-in-mobile.tsx | 14 +- .../chat/chat-with-history/header/index.tsx | 18 +- .../header/mobile-operation-dropdown.tsx | 8 +- .../chat-with-history/header/operation.tsx | 10 +- .../base/chat/chat-with-history/hooks.tsx | 2 +- .../chat-with-history/inputs-form/index.tsx | 2 +- .../chat/chat-with-history/sidebar/index.tsx | 20 +- .../chat-with-history/sidebar/operation.tsx | 12 +- .../sidebar/rename-modal.tsx | 2 +- .../check-input-forms-hooks.spec.tsx | 2 +- .../base/chat/chat/__tests__/hooks.spec.tsx | 2 +- .../chat/chat/__tests__/question.spec.tsx | 2 +- .../chat/answer/__tests__/operation.spec.tsx | 2 +- .../human-input-content/human-input-form.tsx | 4 +- .../base/chat/chat/answer/operation.tsx | 2 +- .../chat-input-area/__tests__/index.spec.tsx | 2 +- .../base/chat/chat/chat-input-area/index.tsx | 2 +- .../chat/chat/chat-input-area/operation.tsx | 2 +- .../base/chat/chat/check-input-forms-hooks.ts | 2 +- web/app/components/base/chat/chat/hooks.ts | 2 +- web/app/components/base/chat/chat/index.tsx | 2 +- .../components/base/chat/chat/question.tsx | 4 +- .../components/base/chat/chat/try-to-ask.tsx | 2 +- .../embedded-chatbot/__tests__/hooks.spec.tsx | 2 +- .../chat/embedded-chatbot/chat-wrapper.tsx | 2 +- .../base/chat/embedded-chatbot/hooks.tsx | 2 +- .../inputs-form/__tests__/content.spec.tsx | 2 +- .../embedded-chatbot/inputs-form/index.tsx | 2 +- .../components/base/checkbox-list/index.tsx | 2 +- .../date-picker/footer.tsx | 2 +- .../time-picker/footer.tsx | 2 +- .../year-and-month-picker/footer.tsx | 2 +- web/app/components/base/drawer/index.tsx | 2 +- .../components/base/emoji-picker/index.tsx | 2 +- .../components/base/error-boundary/index.tsx | 2 +- .../__tests__/annotation-ctrl-button.spec.tsx | 2 +- .../__tests__/config-param-modal.spec.tsx | 2 +- .../annotation-ctrl-button.tsx | 2 +- .../annotation-reply/config-param-modal.tsx | 4 +- .../annotation-reply/index.tsx | 2 +- .../annotation-reply/score-slider/index.tsx | 2 +- .../conversation-opener/index.tsx | 2 +- .../conversation-opener/modal.tsx | 2 +- .../new-feature-panel/feature-bar.tsx | 2 +- .../new-feature-panel/feature-card.tsx | 2 +- .../new-feature-panel/file-upload/index.tsx | 2 +- .../file-upload/setting-content.tsx | 2 +- .../new-feature-panel/image-upload/index.tsx | 2 +- .../moderation-setting-modal.spec.tsx | 2 +- .../new-feature-panel/moderation/index.tsx | 2 +- .../moderation/moderation-content.tsx | 2 +- .../moderation/moderation-setting-modal.tsx | 4 +- .../text-to-speech/index.tsx | 2 +- .../text-to-speech/param-config-content.tsx | 2 +- .../file-uploader/__tests__/hooks.spec.ts | 2 +- .../file-from-link-or-local/index.tsx | 2 +- .../index.stories.tsx | 2 +- .../file-uploader-in-attachment/index.tsx | 2 +- .../file-image-item.tsx | 2 +- .../file-uploader-in-chat-input/file-item.tsx | 2 +- .../index.stories.tsx | 2 +- .../components/base/file-uploader/hooks.ts | 2 +- .../form/components/field/number-input.tsx | 8 +- .../base/form/components/form/actions.tsx | 2 +- .../__tests__/use-check-validated.spec.ts | 2 +- .../base/form/hooks/use-check-validated.ts | 2 +- .../components/base/form/index.stories.tsx | 2 +- .../image-uploader/__tests__/hooks.spec.ts | 2 +- .../__tests__/image-preview.spec.tsx | 2 +- .../components/base/image-uploader/hooks.ts | 2 +- .../base/image-uploader/image-link-input.tsx | 2 +- .../base/image-uploader/image-preview.tsx | 2 +- .../base/inline-delete-confirm/index.tsx | 2 +- .../base/markdown-blocks/button.tsx | 2 +- .../components/base/markdown-blocks/form.tsx | 6 +- .../components/base/modal-like-wrap/index.tsx | 2 +- web/app/components/base/modal/index.tsx | 2 +- web/app/components/base/modal/modal.tsx | 6 +- .../base/notion-connector/index.tsx | 2 +- web/app/components/base/pagination/index.tsx | 2 +- web/app/components/base/param-item/index.tsx | 8 +- .../base/portal-to-follow-elem/index.tsx | 10 +- .../plugins/hitl-input-block/input-field.tsx | 2 +- .../workflow-variable-block/component.tsx | 2 +- web/app/components/base/select/index.tsx | 2 +- web/app/components/base/switch/skeleton.tsx | 41 ---- .../base/tag-input/__tests__/index.spec.tsx | 2 +- .../base/tag-input/__tests__/interop.spec.tsx | 4 +- web/app/components/base/tag-input/index.tsx | 2 +- .../tag-management/__tests__/index.spec.tsx | 2 +- .../tag-management/__tests__/panel.spec.tsx | 2 +- .../__tests__/selector.spec.tsx | 2 +- .../__tests__/tag-item-editor.spec.tsx | 2 +- .../components/base/tag-management/filter.tsx | 10 +- .../base/tag-management/index.stories.tsx | 2 +- .../components/base/tag-management/index.tsx | 2 +- .../components/base/tag-management/panel.tsx | 2 +- .../base/tag-management/selector.tsx | 6 +- .../base/tag-management/tag-item-editor.tsx | 14 +- .../base/tag-management/tag-remove-modal.tsx | 2 +- .../text-generation/__tests__/hooks.spec.ts | 2 +- .../components/base/text-generation/hooks.ts | 2 +- web/app/components/base/tooltip/index.tsx | 2 +- .../base/user-avatar-list/index.tsx | 4 +- .../billing/apps-full-in-dialog/index.tsx | 2 +- .../billing/plan-upgrade-modal/index.tsx | 2 +- web/app/components/billing/plan/index.tsx | 2 +- .../billing/pricing/__tests__/dialog.spec.tsx | 2 +- .../billing/pricing/__tests__/header.spec.tsx | 2 +- web/app/components/billing/pricing/header.tsx | 4 +- web/app/components/billing/pricing/index.tsx | 8 +- .../plan-switcher/plan-range-switcher.tsx | 2 +- .../cloud-plan-item/__tests__/index.spec.tsx | 2 +- .../pricing/plans/cloud-plan-item/index.tsx | 2 +- .../__tests__/index.spec.tsx | 2 +- .../plans/self-hosted-plan-item/index.tsx | 2 +- .../components/billing/upgrade-btn/index.tsx | 2 +- .../custom-page/__tests__/index.spec.tsx | 2 +- .../components/chat-preview-card.tsx | 2 +- .../components/workflow-preview-card.tsx | 2 +- .../__tests__/use-web-app-brand.spec.tsx | 2 +- .../hooks/use-web-app-brand.ts | 2 +- .../custom/custom-web-app-brand/index.tsx | 4 +- .../__tests__/auto-disabled-document.spec.tsx | 4 +- .../auto-disabled-document.tsx | 2 +- .../datasets/common/image-previewer/index.tsx | 2 +- .../hooks/__tests__/use-upload.spec.tsx | 4 +- .../common/image-uploader/hooks/use-upload.ts | 2 +- .../image-uploader-in-chunk/image-item.tsx | 2 +- .../image-item.tsx | 2 +- .../__tests__/index.spec.tsx | 6 +- .../common/retrieval-param-config/index.tsx | 4 +- .../__tests__/index.spec.tsx | 2 +- .../__tests__/uploader.spec.tsx | 2 +- .../dsl-confirm-modal.tsx | 2 +- .../hooks/__tests__/use-dsl-import.spec.tsx | 2 +- .../hooks/use-dsl-import.ts | 2 +- .../create-from-dsl-modal/index.tsx | 2 +- .../create-from-dsl-modal/uploader.tsx | 2 +- .../datasets/create-from-pipeline/header.tsx | 2 +- .../list/__tests__/create-card.spec.tsx | 4 +- .../create-from-pipeline/list/create-card.tsx | 2 +- .../__tests__/edit-pipeline-info.spec.tsx | 4 +- .../template-card/__tests__/index.spec.tsx | 4 +- .../list/template-card/actions.tsx | 8 +- .../list/template-card/details/index.tsx | 2 +- .../list/template-card/edit-pipeline-info.tsx | 4 +- .../list/template-card/index.tsx | 14 +- .../create/embedding-process/index.tsx | 2 +- .../__tests__/index.spec.tsx | 2 +- .../empty-dataset-creation-modal/index.tsx | 4 +- .../hooks/__tests__/use-file-upload.spec.tsx | 2 +- .../file-uploader/hooks/use-file-upload.ts | 2 +- .../step-one/components/next-step-button.tsx | 2 +- .../components/general-chunking-options.tsx | 2 +- .../components/indexing-mode-section.tsx | 2 +- .../create/step-two/components/inputs.tsx | 12 +- .../components/parent-child-options.tsx | 2 +- .../step-two/components/step-two-footer.tsx | 2 +- .../__tests__/use-document-creation.spec.ts | 2 +- .../step-two/hooks/use-document-creation.ts | 2 +- .../datasets/create/step-two/index.tsx | 2 +- .../language-select/__tests__/index.spec.tsx | 4 +- .../create/step-two/language-select/index.tsx | 2 +- .../create/stop-embedding-modal/index.tsx | 2 +- .../website/base/crawled-result-item.tsx | 2 +- .../datasets/create/website/base/header.tsx | 2 +- .../create/website/base/url-input.tsx | 2 +- .../create/website/firecrawl/index.tsx | 2 +- .../website/jina-reader/base/url-input.tsx | 2 +- .../create/website/jina-reader/index.tsx | 2 +- .../datasets/create/website/no-data.tsx | 2 +- .../create/website/watercrawl/index.tsx | 2 +- .../components/__tests__/operations.spec.tsx | 2 +- .../__tests__/rename-modal.spec.tsx | 2 +- .../__tests__/use-document-actions.spec.ts | 2 +- .../hooks/use-document-actions.ts | 2 +- .../documents/components/documents-header.tsx | 2 +- .../documents/components/empty-element.tsx | 2 +- .../documents/components/operations.tsx | 24 +-- .../documents/components/rename-modal.tsx | 4 +- .../create-from-pipeline/actions/index.tsx | 2 +- .../base/__tests__/header.spec.tsx | 2 +- .../data-source/base/header.tsx | 2 +- .../__tests__/use-local-file-upload.spec.tsx | 2 +- .../online-documents/__tests__/index.spec.tsx | 4 +- .../data-source/online-documents/index.tsx | 2 +- .../online-drive/__tests__/index.spec.tsx | 4 +- .../online-drive/connect/index.tsx | 2 +- .../header/breadcrumbs/dropdown/index.tsx | 6 +- .../file-list/list/empty-search-result.tsx | 2 +- .../data-source/online-drive/header.tsx | 2 +- .../data-source/online-drive/index.tsx | 2 +- .../__tests__/crawled-result-item.spec.tsx | 2 +- .../base/crawled-result-item.tsx | 2 +- .../base/options/__tests__/index.spec.tsx | 4 +- .../website-crawl/base/options/index.tsx | 4 +- .../create-from-pipeline/left-header.tsx | 2 +- .../online-document-preview.spec.tsx | 4 +- .../preview/chunk-preview.tsx | 2 +- .../preview/online-document-preview.tsx | 2 +- .../__tests__/components.spec.tsx | 4 +- .../process-documents/__tests__/form.spec.tsx | 4 +- .../__tests__/header.spec.tsx | 2 +- .../process-documents/actions.tsx | 2 +- .../process-documents/form.tsx | 2 +- .../process-documents/header.tsx | 2 +- .../processing/embedding-process/index.tsx | 2 +- .../documents/detail/__tests__/index.spec.tsx | 2 +- .../detail/__tests__/new-segment.spec.tsx | 2 +- .../__tests__/csv-uploader.spec.tsx | 2 +- .../detail/batch-modal/csv-uploader.tsx | 4 +- .../documents/detail/batch-modal/index.tsx | 2 +- .../detail/completed/__tests__/index.spec.tsx | 2 +- .../__tests__/new-child-segment.spec.tsx | 2 +- .../completed/common/action-buttons.tsx | 2 +- .../detail/completed/common/batch-action.tsx | 18 +- .../completed/common/regeneration-modal.tsx | 2 +- .../__tests__/use-child-segment-data.spec.ts | 2 +- .../__tests__/use-segment-list-data.spec.ts | 2 +- .../completed/hooks/use-child-segment-data.ts | 2 +- .../completed/hooks/use-segment-list-data.ts | 2 +- .../detail/completed/new-child-segment.tsx | 2 +- .../detail/completed/segment-card/index.tsx | 20 +- .../detail/embedding/__tests__/index.spec.tsx | 2 +- .../documents/detail/embedding/index.tsx | 2 +- .../datasets/documents/detail/index.tsx | 2 +- .../detail/metadata/__tests__/index.spec.tsx | 2 +- .../metadata/components/doc-type-selector.tsx | 2 +- .../__tests__/use-metadata-state.spec.ts | 2 +- .../metadata/hooks/use-metadata-state.ts | 2 +- .../documents/detail/metadata/index.tsx | 2 +- .../datasets/documents/detail/new-segment.tsx | 2 +- .../documents/detail/segment-add/index.tsx | 10 +- .../pipeline-settings/left-header.tsx | 2 +- .../process-documents/actions.tsx | 2 +- .../status-item/__tests__/index.spec.tsx | 2 +- .../datasets/documents/status-item/index.tsx | 4 +- .../__tests__/index.spec.tsx | 2 +- .../external-api/external-api-modal/index.tsx | 18 +- .../external-api/external-api-panel/index.tsx | 2 +- .../external-knowledge-api-card/index.tsx | 18 +- .../connector/__tests__/index.spec.tsx | 4 +- .../connector/index.tsx | 2 +- .../create/ExternalApiSelection.tsx | 2 +- .../external-knowledge-base/create/index.tsx | 2 +- .../datasets/extra-info/api-access/card.tsx | 2 +- .../datasets/extra-info/api-access/index.tsx | 2 +- .../datasets/extra-info/service-api/card.tsx | 2 +- .../datasets/extra-info/service-api/index.tsx | 2 +- .../modify-external-retrieval-modal.spec.tsx | 2 +- .../__tests__/modify-retrieval-modal.spec.tsx | 4 +- .../components/query-input/index.tsx | 2 +- .../modify-external-retrieval-modal.tsx | 2 +- .../hit-testing/modify-retrieval-modal.tsx | 4 +- .../__tests__/operations.spec.tsx | 2 +- .../components/dataset-card-modals.tsx | 6 +- .../components/operations-dropdown.tsx | 4 +- .../__tests__/use-dataset-card-state.spec.ts | 10 +- .../hooks/use-dataset-card-state.ts | 2 +- .../datasets/list/dataset-card/operations.tsx | 6 +- web/app/components/datasets/list/index.tsx | 4 +- .../datasets/metadata/add-metadata-button.tsx | 2 +- .../__tests__/modal.spec.tsx | 2 +- .../edit-metadata-batch/input-combined.tsx | 6 +- .../metadata/edit-metadata-batch/modal.tsx | 4 +- .../use-batch-edit-document-metadata.spec.ts | 2 +- .../use-edit-dataset-metadata.spec.ts | 2 +- .../__tests__/use-metadata-document.spec.ts | 2 +- .../hooks/use-batch-edit-document-metadata.ts | 2 +- .../hooks/use-edit-dataset-metadata.ts | 2 +- .../metadata/hooks/use-metadata-document.ts | 2 +- .../__tests__/create-metadata-modal.spec.tsx | 2 +- .../dataset-metadata-drawer.spec.tsx | 2 +- .../__tests__/select-metadata-modal.spec.tsx | 2 +- .../create-metadata-modal.tsx | 2 +- .../dataset-metadata-drawer.tsx | 28 +-- .../select-metadata-modal.tsx | 4 +- .../metadata/metadata-document/index.tsx | 2 +- .../metadata/metadata-document/no-data.tsx | 2 +- .../rename-modal/__tests__/index.spec.tsx | 2 +- .../datasets/rename-modal/index.tsx | 4 +- .../settings/form/__tests__/index.spec.tsx | 4 +- .../hooks/__tests__/use-form-state.spec.ts | 10 +- .../settings/form/hooks/use-form-state.ts | 2 +- .../datasets/settings/form/index.tsx | 2 +- .../settings/index-method/keyword-number.tsx | 12 +- .../settings/permission-selector/index.tsx | 2 +- .../settings/summary-index-setting.tsx | 2 +- .../develop/secret-key/secret-key-button.tsx | 2 +- .../secret-key/secret-key-generate.tsx | 2 +- .../develop/secret-key/secret-key-modal.tsx | 20 +- web/app/components/explore/app-card/index.tsx | 2 +- web/app/components/explore/app-list/index.tsx | 2 +- .../explore/create-app-modal/index.tsx | 6 +- .../item-operation/__tests__/index.spec.tsx | 2 +- .../explore/item-operation/index.tsx | 12 +- .../explore/sidebar/__tests__/index.spec.tsx | 4 +- web/app/components/explore/sidebar/index.tsx | 18 +- .../explore/try-app/app-info/index.tsx | 2 +- web/app/components/explore/try-app/index.tsx | 2 +- web/app/components/goto-anything/index.tsx | 2 +- .../components/header/account-about/index.tsx | 2 +- .../__tests__/compliance.spec.tsx | 4 +- .../__tests__/support.spec.tsx | 4 +- .../header/account-dropdown/compliance.tsx | 8 +- .../header/account-dropdown/index.tsx | 4 +- .../header/account-dropdown/support.tsx | 2 +- .../__tests__/index.spec.tsx | 6 +- .../workplace-selector/index.tsx | 6 +- .../__tests__/menu-dialog.dialog.spec.tsx | 2 +- .../__tests__/modal.spec.tsx | 2 +- .../api-based-extension-page/index.tsx | 2 +- .../api-based-extension-page/item.tsx | 16 +- .../api-based-extension-page/modal.tsx | 4 +- .../data-source-page-new/card.tsx | 14 +- .../data-source-page-new/configure.tsx | 2 +- .../data-source-page-new/item.tsx | 2 +- .../data-source-page-new/operator.tsx | 14 +- .../header/account-setting/index.tsx | 4 +- .../language-page/__tests__/index.spec.tsx | 2 +- .../account-setting/language-page/index.tsx | 2 +- .../__tests__/dialog.spec.tsx | 2 +- .../__tests__/index.spec.tsx | 2 +- .../edit-workspace-modal/index.tsx | 6 +- .../account-setting/members-page/index.tsx | 4 +- .../members-page/invite-button.tsx | 2 +- .../invite-modal/__tests__/index.spec.tsx | 4 +- .../members-page/invite-modal/index.tsx | 6 +- .../invite-modal/role-selector.tsx | 6 +- .../members-page/invited-modal/index.tsx | 6 +- .../invited-modal/invitation-link.tsx | 2 +- .../members-page/operation/index.tsx | 8 +- .../__tests__/index.spec.tsx | 2 +- .../transfer-ownership-modal/index.tsx | 4 +- .../member-selector.tsx | 2 +- .../header/account-setting/menu-dialog.tsx | 2 +- .../model-auth/add-custom-model.tsx | 6 +- .../model-auth/authorized/credential-item.tsx | 2 +- .../model-auth/authorized/index.tsx | 18 +- .../model-auth/config-model.tsx | 2 +- .../model-auth/config-provider.tsx | 6 +- .../hooks/__tests__/use-auth.spec.tsx | 4 +- .../model-auth/hooks/use-auth.ts | 2 +- .../manage-custom-model-credentials.tsx | 6 +- .../switch-credential-in-load-balancing.tsx | 2 +- .../model-modal/__tests__/dialog.spec.tsx | 4 +- .../model-provider-page/model-modal/index.tsx | 28 +-- .../__tests__/parameter-item.select.spec.tsx | 4 +- .../__tests__/parameter-item.spec.tsx | 2 +- .../configuration-button.tsx | 2 +- .../model-parameter-modal/index.tsx | 10 +- .../model-parameter-modal/parameter-item.tsx | 8 +- .../presets-parameter.tsx | 12 +- .../model-parameter-modal/trigger.tsx | 2 +- .../model-selector/__tests__/popover.spec.tsx | 2 +- .../__tests__/popup-item.spec.tsx | 2 +- .../model-selector/index.tsx | 4 +- .../model-selector/model-selector-trigger.tsx | 2 +- .../model-selector/popup-item.tsx | 10 +- .../model-selector/popup.tsx | 2 +- .../__tests__/credential-panel.spec.tsx | 2 +- .../model-load-balancing-modal.spec.tsx | 4 +- .../use-change-provider-priority.spec.ts | 2 +- .../__tests__/dialog.spec.tsx | 2 +- .../use-activate-credential.spec.tsx | 2 +- .../model-auth-dropdown/api-key-section.tsx | 2 +- .../model-auth-dropdown/dropdown-content.tsx | 6 +- .../model-auth-dropdown/index.tsx | 8 +- .../usage-priority-section.tsx | 2 +- .../use-activate-credential.ts | 2 +- .../provider-added-card/model-list-item.tsx | 2 +- .../model-load-balancing-configs.tsx | 2 +- .../model-load-balancing-modal.tsx | 16 +- .../provider-added-card/priority-selector.tsx | 2 +- .../provider-card-actions.tsx | 4 +- .../provider-added-card/quota-panel.tsx | 2 +- .../use-change-provider-priority.ts | 2 +- .../__tests__/index.spec.tsx | 4 +- .../system-model-selector/index.tsx | 12 +- .../plugin-page/SerpapiPlugin.tsx | 2 +- .../__tests__/SerpapiPlugin.spec.tsx | 2 +- .../plugin-page/__tests__/index.spec.tsx | 4 +- .../install-plugin/__tests__/hooks.spec.ts | 2 +- .../plugins/install-plugin/base/installed.tsx | 2 +- .../plugins/install-plugin/hooks.ts | 2 +- .../install-bundle/steps/install.tsx | 2 +- .../install-bundle/steps/installed.tsx | 2 +- .../__tests__/index.spec.tsx | 2 +- .../install-from-github/index.tsx | 2 +- .../install-from-github/steps/loaded.tsx | 2 +- .../steps/selectPackage.tsx | 2 +- .../install-from-github/steps/setURL.tsx | 2 +- .../steps/install.tsx | 2 +- .../steps/uploading.tsx | 2 +- .../steps/install.tsx | 2 +- .../plugins/marketplace/list/card-wrapper.tsx | 2 +- .../sort-dropdown/__tests__/index.spec.tsx | 2 +- .../marketplace/sort-dropdown/index.tsx | 4 +- .../__tests__/api-key-modal.spec.tsx | 2 +- .../__tests__/authorize-components.spec.tsx | 2 +- .../__tests__/oauth-client-settings.spec.tsx | 2 +- .../authorize/add-api-key-button.tsx | 4 +- .../authorize/add-oauth-button.tsx | 4 +- .../plugin-auth/authorize/api-key-modal.tsx | 2 +- .../authorize/oauth-client-settings.tsx | 4 +- .../authorized-in-data-source-node.tsx | 2 +- .../plugin-auth/authorized-in-node.tsx | 2 +- .../authorized/__tests__/index.spec.tsx | 2 +- .../plugins/plugin-auth/authorized/index.tsx | 20 +- .../plugins/plugin-auth/authorized/item.tsx | 2 +- .../__tests__/use-plugin-auth-action.spec.ts | 2 +- .../hooks/use-plugin-auth-action.ts | 2 +- .../plugin-auth/plugin-auth-in-agent.tsx | 2 +- .../plugin-auth-in-datasource-node.tsx | 2 +- .../__tests__/detail-header.spec.tsx | 2 +- .../__tests__/endpoint-card.spec.tsx | 2 +- .../__tests__/endpoint-modal.spec.tsx | 2 +- .../__tests__/operation-dropdown.spec.tsx | 2 +- .../datasource-action-list.tsx | 2 +- .../detail-header/__tests__/index.spec.tsx | 4 +- .../components/header-modals.tsx | 4 +- .../__tests__/use-plugin-operations.spec.ts | 2 +- .../hooks/use-plugin-operations.ts | 2 +- .../detail-header/index.tsx | 4 +- .../plugin-detail-panel/endpoint-card.tsx | 20 +- .../plugin-detail-panel/endpoint-list.tsx | 2 +- .../plugin-detail-panel/endpoint-modal.tsx | 4 +- .../model-selector/__tests__/index.spec.tsx | 2 +- .../__tests__/tts-params-panel.spec.tsx | 4 +- .../model-selector/index.tsx | 8 +- .../model-selector/tts-params-panel.tsx | 2 +- .../operation-dropdown.tsx | 8 +- .../__tests__/delete-confirm.spec.tsx | 4 +- .../__tests__/log-viewer.spec.tsx | 2 +- .../__tests__/selector-entry.spec.tsx | 2 +- .../__tests__/selector-view.spec.tsx | 2 +- .../__tests__/subscription-card.spec.tsx | 2 +- .../create/__tests__/common-modal.spec.tsx | 2 +- .../create/__tests__/index.spec.tsx | 4 +- .../create/__tests__/oauth-client.spec.tsx | 2 +- .../use-common-modal-state.helpers.spec.ts | 2 +- .../__tests__/use-common-modal-state.spec.ts | 2 +- .../__tests__/use-oauth-client-state.spec.ts | 2 +- .../hooks/use-common-modal-state.helpers.ts | 2 +- .../create/hooks/use-common-modal-state.ts | 2 +- .../create/hooks/use-oauth-client-state.ts | 2 +- .../subscription-list/create/index.tsx | 4 +- .../subscription-list/create/oauth-client.tsx | 4 +- .../subscription-list/delete-confirm.tsx | 10 +- .../edit/__tests__/apikey-edit-modal.spec.tsx | 2 +- .../edit/__tests__/index.spec.tsx | 2 +- .../edit/__tests__/manual-edit-modal.spec.tsx | 2 +- .../edit/__tests__/oauth-edit-modal.spec.tsx | 2 +- .../edit/apikey-edit-modal.tsx | 2 +- .../edit/manual-edit-modal.tsx | 2 +- .../edit/oauth-edit-modal.tsx | 2 +- .../subscription-list/log-viewer.tsx | 2 +- .../tool-selector/__tests__/index.spec.tsx | 2 +- .../__tests__/reasoning-config-form.spec.tsx | 4 +- .../__tests__/tool-credentials-form.spec.tsx | 2 +- .../components/reasoning-config-form.tsx | 2 +- .../components/tool-credentials-form.tsx | 4 +- .../tool-selector/components/tool-item.tsx | 4 +- .../plugin-item/__tests__/action.spec.tsx | 2 +- .../components/plugins/plugin-item/action.tsx | 14 +- .../plugins/plugin-mutation-model/index.tsx | 2 +- .../plugin-page/__tests__/debug-info.spec.tsx | 2 +- .../install-plugin-dropdown.spec.tsx | 4 +- .../__tests__/use-reference-setting.spec.ts | 4 +- .../plugins/plugin-page/debug-info.tsx | 2 +- .../plugins/plugin-page/empty/index.tsx | 2 +- .../components/plugins/plugin-page/index.tsx | 2 +- .../plugin-page/install-plugin-dropdown.tsx | 14 +- .../components/error-plugin-item.tsx | 2 +- .../components/plugin-task-list.tsx | 2 +- .../plugin-page/plugin-tasks/index.tsx | 10 +- .../plugins/plugin-page/plugins-panel.tsx | 2 +- .../plugin-page/use-reference-setting.ts | 2 +- web/app/components/plugins/provider-card.tsx | 2 +- .../__tests__/plugins-picker.spec.tsx | 2 +- .../__tests__/strategy-picker.spec.tsx | 2 +- .../auto-update-setting/plugins-picker.tsx | 2 +- .../auto-update-setting/strategy-picker.tsx | 2 +- .../plugins/reference-setting-modal/index.tsx | 2 +- .../__tests__/from-market-place.spec.tsx | 6 +- .../update-plugin/__tests__/index.spec.tsx | 2 +- .../update-plugin/downgrade-warning.tsx | 2 +- .../update-plugin/from-market-place.tsx | 14 +- .../update-plugin/plugin-version-picker.tsx | 12 +- .../components/__tests__/conversion.spec.tsx | 4 +- ...blish-as-knowledge-pipeline-modal.spec.tsx | 2 +- .../__tests__/update-dsl-modal.spec.tsx | 4 +- .../rag-pipeline/components/conversion.tsx | 12 +- .../editor/form/__tests__/index.spec.tsx | 2 +- .../panel/input-field/editor/form/index.tsx | 4 +- .../field-list/__tests__/hooks.spec.ts | 2 +- .../field-list/__tests__/index.spec.tsx | 2 +- .../panel/input-field/field-list/hooks.ts | 2 +- .../components/panel/input-field/index.tsx | 2 +- .../test-run/preparation/actions/index.tsx | 2 +- .../__tests__/index.spec.tsx | 2 +- .../__tests__/options.spec.tsx | 2 +- .../document-processing/actions.tsx | 2 +- .../document-processing/options.tsx | 2 +- .../test-run/result/result-preview/index.tsx | 2 +- .../publish-as-knowledge-pipeline-modal.tsx | 2 +- .../__tests__/index.spec.tsx | 2 +- .../input-field-button.tsx | 2 +- .../publisher/__tests__/index.spec.tsx | 2 +- .../publisher/__tests__/popup.spec.tsx | 4 +- .../rag-pipeline-header/publisher/index.tsx | 2 +- .../rag-pipeline-header/publisher/popup.tsx | 24 +-- .../components/update-dsl-modal.tsx | 2 +- .../components/version-mismatch-modal.tsx | 2 +- .../hooks/__tests__/index.spec.ts | 2 +- .../hooks/__tests__/use-DSL.spec.ts | 2 +- .../__tests__/use-update-dsl-modal.spec.ts | 2 +- .../components/rag-pipeline/hooks/use-DSL.ts | 2 +- .../hooks/use-update-dsl-modal.ts | 2 +- .../use-text-generation-app-state.spec.ts | 2 +- .../hooks/use-text-generation-app-state.ts | 2 +- .../share/text-generation/index.tsx | 2 +- .../share/text-generation/menu-dropdown.tsx | 14 +- .../result/__tests__/index.spec.tsx | 2 +- .../share/text-generation/result/index.tsx | 4 +- .../share/text-generation/run-batch/index.tsx | 2 +- .../run-batch/res-download/index.tsx | 2 +- .../share/text-generation/run-once/index.tsx | 2 +- .../__tests__/get-schema.spec.tsx | 2 +- .../__tests__/index.spec.tsx | 2 +- .../config-credentials.tsx | 2 +- .../get-schema.tsx | 4 +- .../edit-custom-collection-modal/index.tsx | 4 +- .../edit-custom-collection-modal/test-api.tsx | 2 +- web/app/components/tools/labels/filter.tsx | 10 +- .../tools/mcp/__tests__/modal.spec.tsx | 2 +- .../__tests__/operation-dropdown.spec.tsx | 2 +- .../components/tools/mcp/detail/content.tsx | 20 +- .../tools/mcp/detail/operation-dropdown.tsx | 12 +- .../components/tools/mcp/headers-input.tsx | 2 +- .../tools/mcp/hooks/use-mcp-modal-form.ts | 2 +- .../components/tools/mcp/mcp-server-modal.tsx | 2 +- .../components/tools/mcp/mcp-service-card.tsx | 22 +-- web/app/components/tools/mcp/modal.tsx | 4 +- .../components/tools/mcp/provider-card.tsx | 12 +- .../mcp/sections/authentication-section.tsx | 2 +- .../__tests__/custom-create-card.spec.tsx | 2 +- .../tools/provider/__tests__/detail.spec.tsx | 2 +- .../tools/provider/custom-create-card.tsx | 2 +- web/app/components/tools/provider/detail.tsx | 22 +-- .../__tests__/config-credentials.spec.tsx | 2 +- .../setting/build-in/config-credentials.tsx | 4 +- .../__tests__/configure-button.spec.tsx | 2 +- .../workflow-tool/__tests__/index.spec.tsx | 2 +- .../tools/workflow-tool/configure-button.tsx | 2 +- .../workflow-tool/confirm-modal/index.tsx | 2 +- .../__tests__/use-configure-button.spec.ts | 2 +- .../hooks/use-configure-button.ts | 2 +- .../components/tools/workflow-tool/index.tsx | 4 +- .../__tests__/features-trigger.spec.tsx | 2 +- .../workflow-header/features-trigger.tsx | 4 +- .../workflow-onboarding-modal/index.tsx | 2 +- .../hooks/__tests__/use-DSL.spec.ts | 2 +- .../__tests__/use-workflow-run-utils.spec.ts | 2 +- .../components/workflow-app/hooks/use-DSL.ts | 2 +- .../hooks/use-workflow-run-utils.ts | 2 +- .../__tests__/update-dsl-modal.spec.tsx | 4 +- .../__tests__/workflow-edge-events.spec.tsx | 2 +- .../__tests__/tool-picker.spec.tsx | 2 +- .../block-selector/all-start-blocks.tsx | 2 +- .../workflow/block-selector/all-tools.tsx | 2 +- .../market-place-plugin/action.tsx | 14 +- .../workflow/block-selector/tool-picker.tsx | 2 +- .../workflow/comment/comment-input.spec.tsx | 2 +- .../workflow/comment/comment-input.tsx | 2 +- .../workflow/comment/mention-input.spec.tsx | 2 +- .../workflow/comment/mention-input.tsx | 4 +- .../workflow/comment/thread.spec.tsx | 6 +- .../components/workflow/comment/thread.tsx | 14 +- .../workflow/dsl-export-confirm-modal.tsx | 2 +- .../components/workflow/edge-contextmenu.tsx | 10 +- .../header/__tests__/header-layouts.spec.tsx | 2 +- .../header/__tests__/run-mode.spec.tsx | 2 +- .../__tests__/test-run-menu-helpers.spec.tsx | 2 +- .../header/__tests__/test-run-menu.spec.tsx | 2 +- .../workflow/header/chat-variable-button.tsx | 2 +- .../header/checklist/__tests__/index.spec.tsx | 2 +- .../checklist/__tests__/plugin-group.spec.tsx | 2 +- .../workflow/header/checklist/index.tsx | 12 +- .../header/checklist/plugin-group.tsx | 4 +- .../components/workflow/header/env-button.tsx | 2 +- .../header/global-variable-button.tsx | 2 +- .../workflow/header/header-in-restoring.tsx | 4 +- .../header/header-in-view-history.tsx | 2 +- .../workflow/header/online-users.tsx | 12 +- .../components/workflow/header/run-mode.tsx | 2 +- .../workflow/header/test-run-menu-helpers.tsx | 2 +- .../workflow/header/test-run-menu.tsx | 2 +- .../header/version-history-button.tsx | 2 +- .../hooks/__tests__/use-checklist.spec.ts | 2 +- .../__tests__/use-leader-restore.spec.ts | 2 +- .../workflow/hooks/use-checklist.ts | 2 +- .../workflow/hooks/use-leader-restore.ts | 2 +- web/app/components/workflow/index.tsx | 20 +- .../__tests__/file-support.spec.tsx | 2 +- .../nodes/_base/components/add-button.tsx | 2 +- .../nodes/_base/components/agent-strategy.tsx | 14 +- .../before-run-form/__tests__/index.spec.tsx | 4 +- .../components/before-run-form/index.tsx | 4 +- .../nodes/_base/components/config-vision.tsx | 2 +- .../error-handle-type-selector.tsx | 2 +- .../components/input-number-with-slider.tsx | 2 +- .../components/install-plugin-button.tsx | 2 +- .../layout/__tests__/field-title.spec.tsx | 2 +- .../_base/components/layout/field-title.tsx | 2 +- .../nodes/_base/components/memory-config.tsx | 4 +- .../next-step/__tests__/operator.spec.tsx | 4 +- .../nodes/_base/components/next-step/item.tsx | 2 +- .../_base/components/next-step/operator.tsx | 12 +- .../nodes/_base/components/node-control.tsx | 10 +- .../_base/components/panel-operator/index.tsx | 10 +- .../nodes/_base/components/prompt/editor.tsx | 4 +- .../components/remove-effect-var-confirm.tsx | 6 +- .../_base/components/retry/retry-on-panel.tsx | 4 +- .../__tests__/output-var-list.spec.tsx | 2 +- .../components/variable/output-var-list.tsx | 2 +- .../_base/components/variable/var-list.tsx | 2 +- .../variable/var-reference-picker.trigger.tsx | 2 +- .../variable-label/base/variable-label.tsx | 2 +- .../workflow-panel/last-run/no-data.tsx | 2 +- .../workflow-panel/last-run/use-last-run.ts | 2 +- .../nodes/_base/hooks/use-one-step-run.ts | 4 +- .../workflow/nodes/_base/node-sections.tsx | 2 +- .../__tests__/operation-selector.spec.tsx | 2 +- .../components/operation-selector.tsx | 6 +- .../components/workflow/nodes/code/panel.tsx | 2 +- .../nodes/data-source-empty/index.tsx | 2 +- .../nodes/data-source/before-run-form.tsx | 2 +- .../components/__tests__/curl-panel.spec.tsx | 4 +- .../http/components/authorization/index.tsx | 2 +- .../nodes/http/components/curl-panel.tsx | 4 +- .../key-value/key-value-edit/item.tsx | 10 +- .../components/workflow/nodes/http/panel.tsx | 2 +- .../human-input/__tests__/panel.spec.tsx | 4 +- .../__tests__/button-style-dropdown.spec.tsx | 2 +- .../__tests__/form-content-preview.spec.tsx | 2 +- .../components/__tests__/user-action.spec.tsx | 4 +- .../components/button-style-dropdown.tsx | 2 +- .../__tests__/email-configure-modal.spec.tsx | 2 +- .../delivery-method/email-configure-modal.tsx | 6 +- .../delivery-method/method-item.tsx | 4 +- .../recipient/__tests__/index.spec.tsx | 5 +- .../delivery-method/recipient/email-item.tsx | 2 +- .../delivery-method/recipient/index.tsx | 2 +- .../delivery-method/recipient/member-list.tsx | 2 +- .../recipient/member-selector.tsx | 2 +- .../delivery-method/test-email-sender.tsx | 2 +- .../delivery-method/upgrade-modal.tsx | 2 +- .../components/form-content-preview.tsx | 4 +- .../components/single-run-form.tsx | 6 +- .../human-input/components/user-action.tsx | 4 +- .../workflow/nodes/human-input/panel.tsx | 4 +- .../if-else/components/condition-add.tsx | 2 +- .../condition-list/condition-operator.tsx | 2 +- .../components/condition-number-input.tsx | 2 +- .../if-else/components/condition-wrap.tsx | 2 +- .../workflow/nodes/if-else/panel.tsx | 2 +- .../iteration/__tests__/integration.spec.tsx | 4 +- .../workflow/nodes/iteration/node.tsx | 2 +- .../workflow/nodes/iteration/panel.tsx | 4 +- .../components/chunk-structure/index.tsx | 2 +- .../components/chunk-structure/selector.tsx | 2 +- .../components/index-method.tsx | 2 +- .../search-method-option.tsx | 2 +- .../top-k-and-score-threshold.tsx | 10 +- .../components/metadata/add-condition.tsx | 2 +- .../condition-list/condition-operator.tsx | 2 +- .../condition-list/condition-value-method.tsx | 2 +- .../metadata-filter-selector.tsx | 2 +- .../components/metadata/metadata-trigger.tsx | 2 +- .../components/retrieval-config.tsx | 2 +- .../list-operator/__tests__/panel.spec.tsx | 5 +- .../__tests__/limit-config.spec.tsx | 4 +- .../list-operator/components/limit-config.tsx | 2 +- .../workflow/nodes/list-operator/panel.tsx | 2 +- .../__tests__/json-importer.spec.tsx | 2 +- .../json-importer.tsx | 4 +- .../json-schema-config.tsx | 4 +- .../generated-result.tsx | 2 +- .../json-schema-generator/index.tsx | 2 +- .../json-schema-generator/prompt-editor.tsx | 2 +- .../visual-editor/add-field.tsx | 2 +- .../edit-card/advanced-actions.tsx | 2 +- .../edit-card/required-switch.tsx | 2 +- .../visual-editor/hooks.ts | 2 +- .../llm/components/panel-memory-section.tsx | 2 +- .../llm/components/panel-output-section.tsx | 4 +- .../components/reasoning-format-config.tsx | 2 +- .../nodes/llm/components/structure-output.tsx | 2 +- .../components/workflow/nodes/llm/panel.tsx | 2 +- .../nodes/loop/__tests__/integration.spec.tsx | 2 +- .../nodes/loop/components/condition-add.tsx | 2 +- .../condition-list/condition-operator.tsx | 2 +- .../components/condition-number-input.tsx | 2 +- .../nodes/loop/components/condition-wrap.tsx | 2 +- .../loop/components/loop-variables/item.tsx | 2 +- .../__tests__/integration.spec.tsx | 4 +- .../__tests__/update.spec.tsx | 4 +- .../components/extract-parameter/update.tsx | 6 +- .../nodes/start/__tests__/use-config.spec.ts | 2 +- .../nodes/start/components/var-list.tsx | 2 +- .../workflow/nodes/start/use-config.ts | 2 +- .../nodes/tool/components/tool-form/item.tsx | 2 +- .../workflow/nodes/tool/hooks/use-config.ts | 2 +- .../components/trigger-form/item.tsx | 2 +- .../components/on-minute-selector.tsx | 2 +- .../__tests__/use-config.spec.tsx | 4 +- .../workflow/nodes/trigger-webhook/panel.tsx | 20 +- .../nodes/trigger-webhook/use-config.ts | 2 +- .../__tests__/integration.spec.tsx | 4 +- .../__tests__/var-group-item.spec.tsx | 4 +- .../components/add-variable/index.tsx | 2 +- .../components/var-group-item.tsx | 2 +- .../nodes/variable-assigner/panel.tsx | 2 +- .../__tests__/hooks.spec.tsx | 2 +- .../plugins/link-editor-plugin/component.tsx | 2 +- .../plugins/link-editor-plugin/hooks.ts | 2 +- .../toolbar/__tests__/operator.spec.tsx | 2 +- .../note-editor/toolbar/operator.tsx | 14 +- .../operator/__tests__/more-actions.spec.tsx | 2 +- .../workflow/operator/more-actions.tsx | 14 +- .../workflow/operator/zoom-in-out.tsx | 14 +- .../panel/__tests__/inputs-panel.spec.tsx | 2 +- .../panel/__tests__/workflow-preview.spec.tsx | 4 +- .../__tests__/object-value-item.spec.tsx | 2 +- .../__tests__/variable-modal.spec.tsx | 4 +- .../components/array-bool-list.tsx | 2 +- .../components/array-value-list.tsx | 2 +- .../components/object-value-item.tsx | 2 +- .../components/variable-modal-trigger.tsx | 4 +- .../components/variable-modal.sections.tsx | 2 +- .../components/variable-modal.tsx | 4 +- .../comments-panel/__tests__/index.spec.tsx | 4 +- .../workflow/panel/comments-panel/index.tsx | 2 +- .../debug-and-preview/__tests__/hooks.spec.ts | 2 +- .../__tests__/hooks/handle-resume.spec.ts | 2 +- .../__tests__/hooks/handle-send.spec.ts | 2 +- .../hooks/handle-stop-restart.spec.ts | 2 +- .../__tests__/hooks/misc.spec.ts | 2 +- .../__tests__/hooks/opening-statement.spec.ts | 2 +- .../__tests__/hooks/sse-callbacks.spec.ts | 2 +- .../workflow/panel/debug-and-preview/hooks.ts | 2 +- .../__tests__/variable-modal.spec.tsx | 4 +- .../panel/env-panel/variable-modal.tsx | 4 +- .../panel/env-panel/variable-trigger.tsx | 4 +- .../workflow/panel/inputs-panel.tsx | 2 +- .../context-menu/__tests__/menu-item.spec.tsx | 2 +- .../context-menu/index.tsx | 8 +- .../context-menu/menu-item.tsx | 2 +- .../delete-confirm-modal.tsx | 2 +- .../panel/version-history-panel/empty.tsx | 2 +- .../filter/filter-switch.tsx | 2 +- .../panel/version-history-panel/index.tsx | 2 +- .../restore-confirm-modal.tsx | 2 +- .../workflow/panel/workflow-preview.tsx | 4 +- .../workflow/run/__tests__/index.spec.tsx | 2 +- .../__tests__/agent-log-nav-more.spec.tsx | 2 +- .../workflow/run/agent-log/agent-log-item.tsx | 2 +- .../run/agent-log/agent-log-nav-more.tsx | 8 +- .../workflow/run/agent-log/agent-log-nav.tsx | 2 +- web/app/components/workflow/run/index.tsx | 2 +- .../iteration-log/iteration-log-trigger.tsx | 2 +- .../run/loop-log/loop-log-trigger.tsx | 2 +- .../run/retry-log/retry-log-trigger.tsx | 2 +- .../workflow/selection-contextmenu.tsx | 16 +- .../components/workflow/update-dsl-modal.tsx | 4 +- .../__tests__/value-content-sections.spec.tsx | 2 +- .../workflow/variable-inspect/group.tsx | 2 +- .../workflow/variable-inspect/left.tsx | 2 +- .../workflow/variable-inspect/listening.tsx | 2 +- .../components/zoom-in-out.tsx | 14 +- .../education-apply/education-apply-page.tsx | 4 +- .../education-apply/expire-notice-modal.tsx | 2 +- web/app/education-apply/user-info.tsx | 4 +- .../education-apply/verify-state-modal.tsx | 2 +- .../forgot-password/ChangePasswordForm.tsx | 4 +- .../forgot-password/ForgotPasswordForm.tsx | 4 +- web/app/init/InitPasswordPopup.tsx | 4 +- web/app/install/installForm.tsx | 4 +- web/app/layout.tsx | 4 +- web/app/reset-password/check-code/page.tsx | 4 +- web/app/reset-password/page.tsx | 4 +- web/app/reset-password/set-password/page.tsx | 4 +- web/app/signin/check-code/page.tsx | 4 +- .../signin/components/mail-and-code-auth.tsx | 4 +- .../components/mail-and-password-auth.tsx | 4 +- web/app/signin/components/social-auth.tsx | 2 +- web/app/signin/components/sso-auth.tsx | 4 +- web/app/signin/invite-settings/page.tsx | 4 +- web/app/signin/normal-form.tsx | 2 +- web/app/signin/one-more-step.tsx | 4 +- web/app/signup/check-code/page.tsx | 4 +- web/app/signup/components/input-mail.tsx | 4 +- web/app/signup/set-password/page.tsx | 4 +- web/context/provider-context-provider.tsx | 2 +- web/docs/overlay-migration.md | 16 +- web/eslint.constants.mjs | 8 +- web/hooks/use-import-dsl.ts | 2 +- web/hooks/use-pay.tsx | 6 +- web/service/base.ts | 2 +- web/service/fetch.spec.ts | 2 +- web/service/fetch.ts | 2 +- web/tailwind.config.ts | 2 + 1056 files changed, 2547 insertions(+), 2069 deletions(-) create mode 100644 packages/dify-ui/.gitignore create mode 100644 packages/dify-ui/.storybook/main.ts create mode 100644 packages/dify-ui/.storybook/preview.tsx create mode 100644 packages/dify-ui/.storybook/storybook.css rename {web/app/components/base/ui => packages/dify-ui/src}/alert-dialog/__tests__/index.spec.tsx (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/alert-dialog/index.stories.tsx (99%) rename {web/app/components/base/ui => packages/dify-ui/src}/alert-dialog/index.tsx (94%) rename {web/app/components/base/ui => packages/dify-ui/src}/avatar/__tests__/index.spec.tsx (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/avatar/index.stories.tsx (97%) rename {web/app/components/base/ui => packages/dify-ui/src}/avatar/index.tsx (98%) rename {web/app/components/base/ui => packages/dify-ui/src}/button/__tests__/index.spec.tsx (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/button/index.stories.tsx (97%) rename {web/app/components/base/ui => packages/dify-ui/src}/button/index.tsx (99%) rename {web/app/components/base/ui => packages/dify-ui/src}/context-menu/__tests__/index.spec.tsx (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/context-menu/index.stories.tsx (99%) rename {web/app/components/base/ui => packages/dify-ui/src}/context-menu/index.tsx (95%) rename {web/app/components/base/ui => packages/dify-ui/src}/dialog/__tests__/index.spec.tsx (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/dialog/index.stories.tsx (99%) rename {web/app/components/base/ui => packages/dify-ui/src}/dialog/index.tsx (98%) rename {web/app/components/base/ui => packages/dify-ui/src}/dropdown-menu/__tests__/index.spec.tsx (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/dropdown-menu/index.stories.tsx (99%) rename {web/app/components/base/ui => packages/dify-ui/src}/dropdown-menu/index.tsx (95%) rename {web/app/components/base/ui => packages/dify-ui/src}/number-field/__tests__/index.spec.tsx (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/number-field/index.stories.tsx (98%) rename {web/app/components/base/ui => packages/dify-ui/src}/number-field/index.tsx (99%) rename {web/app/components/base/ui => packages/dify-ui/src}/overlay-shared.ts (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/placement.ts (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/popover/__tests__/index.spec.tsx (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/popover/index.stories.tsx (97%) rename {web/app/components/base/ui => packages/dify-ui/src}/popover/index.tsx (91%) rename {web/app/components/base/ui => packages/dify-ui/src}/scroll-area/__tests__/index.spec.tsx (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/scroll-area/index.stories.tsx (98%) rename {web/app/components/base/ui => packages/dify-ui/src}/scroll-area/index.tsx (98%) rename {web/app/components/base/ui => packages/dify-ui/src}/select/__tests__/index.spec.tsx (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/select/index.stories.tsx (99%) rename {web/app/components/base/ui => packages/dify-ui/src}/select/index.tsx (96%) rename {web/app/components/base/ui => packages/dify-ui/src}/slider/__tests__/index.spec.tsx (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/slider/index.stories.tsx (96%) rename {web/app/components/base/ui => packages/dify-ui/src}/slider/index.tsx (98%) rename {web/app/components/base => packages/dify-ui/src}/switch/__tests__/index.spec.tsx (99%) rename {web/app/components/base => packages/dify-ui/src}/switch/index.stories.tsx (96%) rename {web/app/components/base => packages/dify-ui/src}/switch/index.tsx (72%) rename {web/app/components/base/ui => packages/dify-ui/src}/toast/__tests__/index.spec.tsx (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/toast/index.stories.tsx (99%) rename {web/app/components/base/ui => packages/dify-ui/src}/toast/index.tsx (99%) rename {web/app/components/base/ui => packages/dify-ui/src}/tooltip/__tests__/index.spec.tsx (100%) rename {web/app/components/base/ui => packages/dify-ui/src}/tooltip/index.stories.tsx (98%) rename {web/app/components/base/ui => packages/dify-ui/src}/tooltip/index.tsx (90%) create mode 100644 packages/dify-ui/tailwind.config.ts create mode 100644 packages/dify-ui/tests/setup.ts create mode 100644 packages/dify-ui/vite.config.ts rename packages/tsconfig/{web.json => react.json} (100%) delete mode 100644 web/app/components/base/switch/skeleton.tsx diff --git a/.github/workflows/web-tests.yml b/.github/workflows/web-tests.yml index f3ab4c62c7..dcee8863ce 100644 --- a/.github/workflows/web-tests.yml +++ b/.github/workflows/web-tests.yml @@ -89,3 +89,34 @@ jobs: flags: web env: CODECOV_TOKEN: ${{ env.CODECOV_TOKEN }} + + dify-ui-test: + name: dify-ui Tests + runs-on: ubuntu-latest + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + defaults: + run: + shell: bash + working-directory: ./packages/dify-ui + + steps: + - name: Checkout code + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Setup web environment + uses: ./.github/actions/setup-web + + - name: Run dify-ui tests + run: vp test run --coverage --silent=passed-only + + - name: Report coverage + if: ${{ env.CODECOV_TOKEN != '' }} + uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 + with: + directory: packages/dify-ui/coverage + flags: dify-ui + env: + CODECOV_TOKEN: ${{ env.CODECOV_TOKEN }} diff --git a/codecov.yml b/codecov.yml index 54ac2a4b36..a506087698 100644 --- a/codecov.yml +++ b/codecov.yml @@ -3,6 +3,10 @@ coverage: project: default: target: auto + # Absorb sub-percent coverage noise (rounding, trivial added lines, CVA variants, etc). + threshold: 1% + # Deleting covered code during refactors/migrations must not regress base. + removed_code_behavior: adjust_base flags: web: @@ -10,6 +14,11 @@ flags: - "web/" carryforward: true + dify-ui: + paths: + - "packages/dify-ui/" + carryforward: true + api: paths: - "api/" diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 173a3a7bd7..477391e2de 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -2113,11 +2113,6 @@ "count": 1 } }, - "web/app/components/base/switch/index.stories.tsx": { - "ts/no-explicit-any": { - "count": 1 - } - }, "web/app/components/base/tab-slider/index.tsx": { "react/set-state-in-effect": { "count": 2 diff --git a/packages/dify-ui/.gitignore b/packages/dify-ui/.gitignore new file mode 100644 index 0000000000..befe881885 --- /dev/null +++ b/packages/dify-ui/.gitignore @@ -0,0 +1,3 @@ +/coverage +/dist +/storybook-static diff --git a/packages/dify-ui/.storybook/main.ts b/packages/dify-ui/.storybook/main.ts new file mode 100644 index 0000000000..c8b7ee8e3f --- /dev/null +++ b/packages/dify-ui/.storybook/main.ts @@ -0,0 +1,27 @@ +import type { StorybookConfig } from '@storybook/react-vite' +import tailwindcss from '@tailwindcss/vite' +import { mergeConfig } from 'vite' + +const config: StorybookConfig = { + stories: ['../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], + addons: [ + '@storybook/addon-links', + '@storybook/addon-docs', + '@storybook/addon-themes', + '@chromatic-com/storybook', + ], + framework: '@storybook/react-vite', + core: { + disableWhatsNewNotifications: true, + }, + docs: { + defaultName: 'Documentation', + }, + async viteFinal(config) { + return mergeConfig(config, { + plugins: [tailwindcss()], + }) + }, +} + +export default config diff --git a/packages/dify-ui/.storybook/preview.tsx b/packages/dify-ui/.storybook/preview.tsx new file mode 100644 index 0000000000..a5bfc5d8af --- /dev/null +++ b/packages/dify-ui/.storybook/preview.tsx @@ -0,0 +1,31 @@ +import type { Preview } from '@storybook/react-vite' +import { withThemeByDataAttribute } from '@storybook/addon-themes' +import './storybook.css' + +export const decorators = [ + withThemeByDataAttribute({ + themes: { + light: 'light', + dark: 'dark', + }, + defaultTheme: 'light', + attributeName: 'data-theme', + }), +] + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + docs: { + toc: true, + }, + }, + tags: ['autodocs'], +} + +export default preview diff --git a/packages/dify-ui/.storybook/storybook.css b/packages/dify-ui/.storybook/storybook.css new file mode 100644 index 0000000000..e9796fd046 --- /dev/null +++ b/packages/dify-ui/.storybook/storybook.css @@ -0,0 +1,19 @@ +@import 'tailwindcss'; + +@config '../tailwind.config.ts'; + +@import '../src/styles/styles.css'; + +html { + color-scheme: light; +} + +html[data-theme='dark'] { + color-scheme: dark; +} + +body { + background: var(--color-components-panel-bg); + color: var(--color-text-primary, #101828); + font-family: Inter, ui-sans-serif, system-ui, sans-serif; +} diff --git a/packages/dify-ui/AGENTS.md b/packages/dify-ui/AGENTS.md index ecc968e130..651b117070 100644 --- a/packages/dify-ui/AGENTS.md +++ b/packages/dify-ui/AGENTS.md @@ -1,6 +1,15 @@ # @langgenius/dify-ui -This package provides shared design tokens (colors, shadows, typography), the `cn()` utility, and a Tailwind CSS preset consumed by `web/`. +Shared design tokens, the `cn()` utility, a Tailwind CSS preset, and headless primitive components consumed by `web/`. + +## Component Authoring Rules + +- Use `@base-ui/react` primitives + `cva` + `cn`. +- Inside dify-ui, cross-component imports use relative paths (`../button`). External consumers use subpath exports (`@langgenius/dify-ui/button`). +- No imports from `web/`. No dependencies on next / i18next / ky / jotai / zustand. +- One component per folder: `src//index.tsx`, optional `index.stories.tsx` and `__tests__/index.spec.tsx`. Add a matching `./` subpath to `package.json#exports`. +- Props pattern: `Omit & VariantProps & { /* custom */ }`. +- When a component accepts a prop typed from a shared internal module, `export type` it from that component so consumers import it from the component subpath. ## Border Radius: Figma Token → Tailwind Class Mapping diff --git a/packages/dify-ui/package.json b/packages/dify-ui/package.json index b54fde9b89..2b78b25ed6 100644 --- a/packages/dify-ui/package.json +++ b/packages/dify-ui/package.json @@ -12,18 +12,109 @@ "./cn": { "types": "./src/cn.ts", "import": "./src/cn.ts" + }, + "./alert-dialog": { + "types": "./src/alert-dialog/index.tsx", + "import": "./src/alert-dialog/index.tsx" + }, + "./avatar": { + "types": "./src/avatar/index.tsx", + "import": "./src/avatar/index.tsx" + }, + "./button": { + "types": "./src/button/index.tsx", + "import": "./src/button/index.tsx" + }, + "./context-menu": { + "types": "./src/context-menu/index.tsx", + "import": "./src/context-menu/index.tsx" + }, + "./dialog": { + "types": "./src/dialog/index.tsx", + "import": "./src/dialog/index.tsx" + }, + "./dropdown-menu": { + "types": "./src/dropdown-menu/index.tsx", + "import": "./src/dropdown-menu/index.tsx" + }, + "./number-field": { + "types": "./src/number-field/index.tsx", + "import": "./src/number-field/index.tsx" + }, + "./popover": { + "types": "./src/popover/index.tsx", + "import": "./src/popover/index.tsx" + }, + "./scroll-area": { + "types": "./src/scroll-area/index.tsx", + "import": "./src/scroll-area/index.tsx" + }, + "./select": { + "types": "./src/select/index.tsx", + "import": "./src/select/index.tsx" + }, + "./slider": { + "types": "./src/slider/index.tsx", + "import": "./src/slider/index.tsx" + }, + "./switch": { + "types": "./src/switch/index.tsx", + "import": "./src/switch/index.tsx" + }, + "./toast": { + "types": "./src/toast/index.tsx", + "import": "./src/toast/index.tsx" + }, + "./tooltip": { + "types": "./src/tooltip/index.tsx", + "import": "./src/tooltip/index.tsx" } }, "scripts": { + "storybook": "storybook dev", + "storybook:build": "storybook build", + "test": "vp test", + "test:watch": "vp test --watch", "type-check": "tsc" }, + "peerDependencies": { + "@base-ui/react": "catalog:", + "class-variance-authority": "catalog:", + "react": "catalog:", + "react-dom": "catalog:", + "tailwindcss": "catalog:" + }, "dependencies": { "clsx": "catalog:", "tailwind-merge": "catalog:" }, "devDependencies": { + "@base-ui/react": "catalog:", + "@chromatic-com/storybook": "catalog:", "@dify/tsconfig": "workspace:*", + "@egoist/tailwindcss-icons": "catalog:", + "@iconify-json/ri": "catalog:", + "@storybook/addon-docs": "catalog:", + "@storybook/addon-links": "catalog:", + "@storybook/addon-themes": "catalog:", + "@storybook/react-vite": "catalog:", + "@tailwindcss/vite": "catalog:", + "@testing-library/jest-dom": "catalog:", + "@testing-library/react": "catalog:", + "@testing-library/user-event": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "@vitejs/plugin-react": "catalog:", + "@vitest/coverage-v8": "catalog:", + "class-variance-authority": "catalog:", + "happy-dom": "catalog:", + "react": "catalog:", + "react-dom": "catalog:", + "storybook": "catalog:", "tailwindcss": "catalog:", - "typescript": "catalog:" + "typescript": "catalog:", + "vite": "catalog:", + "vite-plus": "catalog:", + "vitest": "catalog:" } } diff --git a/web/app/components/base/ui/alert-dialog/__tests__/index.spec.tsx b/packages/dify-ui/src/alert-dialog/__tests__/index.spec.tsx similarity index 100% rename from web/app/components/base/ui/alert-dialog/__tests__/index.spec.tsx rename to packages/dify-ui/src/alert-dialog/__tests__/index.spec.tsx diff --git a/web/app/components/base/ui/alert-dialog/index.stories.tsx b/packages/dify-ui/src/alert-dialog/index.stories.tsx similarity index 99% rename from web/app/components/base/ui/alert-dialog/index.stories.tsx rename to packages/dify-ui/src/alert-dialog/index.stories.tsx index c9deaa53ed..0b6f60f01e 100644 --- a/web/app/components/base/ui/alert-dialog/index.stories.tsx +++ b/packages/dify-ui/src/alert-dialog/index.stories.tsx @@ -1,4 +1,4 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import type { Meta, StoryObj } from '@storybook/react-vite' import { useState } from 'react' import { AlertDialog, diff --git a/web/app/components/base/ui/alert-dialog/index.tsx b/packages/dify-ui/src/alert-dialog/index.tsx similarity index 94% rename from web/app/components/base/ui/alert-dialog/index.tsx rename to packages/dify-ui/src/alert-dialog/index.tsx index 5f2af43df8..7b432c87dc 100644 --- a/web/app/components/base/ui/alert-dialog/index.tsx +++ b/packages/dify-ui/src/alert-dialog/index.tsx @@ -1,10 +1,10 @@ 'use client' import type { ComponentPropsWithoutRef, ReactNode } from 'react' -import type { ButtonProps } from '@/app/components/base/ui/button' +import type { ButtonProps } from '../button' import { AlertDialog as BaseAlertDialog } from '@base-ui/react/alert-dialog' -import { cn } from '@langgenius/dify-ui/cn' -import { Button } from '@/app/components/base/ui/button' +import { Button } from '../button' +import { cn } from '../cn' export const AlertDialog = BaseAlertDialog.Root export const AlertDialogTrigger = BaseAlertDialog.Trigger diff --git a/web/app/components/base/ui/avatar/__tests__/index.spec.tsx b/packages/dify-ui/src/avatar/__tests__/index.spec.tsx similarity index 100% rename from web/app/components/base/ui/avatar/__tests__/index.spec.tsx rename to packages/dify-ui/src/avatar/__tests__/index.spec.tsx diff --git a/web/app/components/base/ui/avatar/index.stories.tsx b/packages/dify-ui/src/avatar/index.stories.tsx similarity index 97% rename from web/app/components/base/ui/avatar/index.stories.tsx rename to packages/dify-ui/src/avatar/index.stories.tsx index 22de82e6db..f8b90c0b16 100644 --- a/web/app/components/base/ui/avatar/index.stories.tsx +++ b/packages/dify-ui/src/avatar/index.stories.tsx @@ -1,4 +1,4 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import type { Meta, StoryObj } from '@storybook/react-vite' import { Avatar, AvatarFallback, AvatarRoot } from '.' const meta = { diff --git a/web/app/components/base/ui/avatar/index.tsx b/packages/dify-ui/src/avatar/index.tsx similarity index 98% rename from web/app/components/base/ui/avatar/index.tsx rename to packages/dify-ui/src/avatar/index.tsx index 6fca2e87dc..8cf893fbbd 100644 --- a/web/app/components/base/ui/avatar/index.tsx +++ b/packages/dify-ui/src/avatar/index.tsx @@ -1,6 +1,6 @@ import type { ImageLoadingStatus } from '@base-ui/react/avatar' import { Avatar as BaseAvatar } from '@base-ui/react/avatar' -import { cn } from '@langgenius/dify-ui/cn' +import { cn } from '../cn' const avatarSizeClasses = { 'xxs': { root: 'size-4', text: 'text-[7px]' }, diff --git a/web/app/components/base/ui/button/__tests__/index.spec.tsx b/packages/dify-ui/src/button/__tests__/index.spec.tsx similarity index 100% rename from web/app/components/base/ui/button/__tests__/index.spec.tsx rename to packages/dify-ui/src/button/__tests__/index.spec.tsx diff --git a/web/app/components/base/ui/button/index.stories.tsx b/packages/dify-ui/src/button/index.stories.tsx similarity index 97% rename from web/app/components/base/ui/button/index.stories.tsx rename to packages/dify-ui/src/button/index.stories.tsx index 40dce31dd4..b70a76f431 100644 --- a/web/app/components/base/ui/button/index.stories.tsx +++ b/packages/dify-ui/src/button/index.stories.tsx @@ -1,4 +1,4 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import type { Meta, StoryObj } from '@storybook/react-vite' import { Button } from '.' diff --git a/web/app/components/base/ui/button/index.tsx b/packages/dify-ui/src/button/index.tsx similarity index 99% rename from web/app/components/base/ui/button/index.tsx rename to packages/dify-ui/src/button/index.tsx index 73d5ecc3c9..03e5c4a937 100644 --- a/web/app/components/base/ui/button/index.tsx +++ b/packages/dify-ui/src/button/index.tsx @@ -1,8 +1,8 @@ import type { Button as BaseButtonNS } from '@base-ui/react/button' import type { VariantProps } from 'class-variance-authority' import { Button as BaseButton } from '@base-ui/react/button' -import { cn } from '@langgenius/dify-ui/cn' import { cva } from 'class-variance-authority' +import { cn } from '../cn' const buttonVariants = cva( 'inline-flex cursor-pointer items-center justify-center whitespace-nowrap outline-hidden focus-visible:ring-2 focus-visible:ring-state-accent-solid data-[disabled]:cursor-not-allowed', diff --git a/web/app/components/base/ui/context-menu/__tests__/index.spec.tsx b/packages/dify-ui/src/context-menu/__tests__/index.spec.tsx similarity index 100% rename from web/app/components/base/ui/context-menu/__tests__/index.spec.tsx rename to packages/dify-ui/src/context-menu/__tests__/index.spec.tsx diff --git a/web/app/components/base/ui/context-menu/index.stories.tsx b/packages/dify-ui/src/context-menu/index.stories.tsx similarity index 99% rename from web/app/components/base/ui/context-menu/index.stories.tsx rename to packages/dify-ui/src/context-menu/index.stories.tsx index b3b43399f6..0be5727e7a 100644 --- a/web/app/components/base/ui/context-menu/index.stories.tsx +++ b/packages/dify-ui/src/context-menu/index.stories.tsx @@ -1,4 +1,4 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import type { Meta, StoryObj } from '@storybook/react-vite' import { useState } from 'react' import { ContextMenu, diff --git a/web/app/components/base/ui/context-menu/index.tsx b/packages/dify-ui/src/context-menu/index.tsx similarity index 95% rename from web/app/components/base/ui/context-menu/index.tsx rename to packages/dify-ui/src/context-menu/index.tsx index c92c4d6ab9..fc2ae1d454 100644 --- a/web/app/components/base/ui/context-menu/index.tsx +++ b/packages/dify-ui/src/context-menu/index.tsx @@ -1,10 +1,10 @@ 'use client' import type { ReactNode } from 'react' -import type { OverlayItemVariant } from '@/app/components/base/ui/overlay-shared' -import type { Placement } from '@/app/components/base/ui/placement' +import type { OverlayItemVariant } from '../overlay-shared' +import type { Placement } from '../placement' import { ContextMenu as BaseContextMenu } from '@base-ui/react/context-menu' -import { cn } from '@langgenius/dify-ui/cn' +import { cn } from '../cn' import { overlayBackdropClassName, overlayDestructiveClassName, @@ -14,8 +14,11 @@ import { overlayPopupBaseClassName, overlayRowClassName, overlaySeparatorClassName, -} from '@/app/components/base/ui/overlay-shared' -import { parsePlacement } from '@/app/components/base/ui/placement' +} from '../overlay-shared' +import { parsePlacement } from '../placement' + +/** @public */ +export type { Placement } export const ContextMenu = BaseContextMenu.Root export const ContextMenuTrigger = BaseContextMenu.Trigger diff --git a/web/app/components/base/ui/dialog/__tests__/index.spec.tsx b/packages/dify-ui/src/dialog/__tests__/index.spec.tsx similarity index 100% rename from web/app/components/base/ui/dialog/__tests__/index.spec.tsx rename to packages/dify-ui/src/dialog/__tests__/index.spec.tsx diff --git a/web/app/components/base/ui/dialog/index.stories.tsx b/packages/dify-ui/src/dialog/index.stories.tsx similarity index 99% rename from web/app/components/base/ui/dialog/index.stories.tsx rename to packages/dify-ui/src/dialog/index.stories.tsx index 0e8f478520..f9caa0d8c5 100644 --- a/web/app/components/base/ui/dialog/index.stories.tsx +++ b/packages/dify-ui/src/dialog/index.stories.tsx @@ -1,4 +1,4 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import type { Meta, StoryObj } from '@storybook/react-vite' import { useState } from 'react' import { Dialog, diff --git a/web/app/components/base/ui/dialog/index.tsx b/packages/dify-ui/src/dialog/index.tsx similarity index 98% rename from web/app/components/base/ui/dialog/index.tsx rename to packages/dify-ui/src/dialog/index.tsx index bc82230f58..c24acd5924 100644 --- a/web/app/components/base/ui/dialog/index.tsx +++ b/packages/dify-ui/src/dialog/index.tsx @@ -9,7 +9,7 @@ import type { ReactNode } from 'react' import { Dialog as BaseDialog } from '@base-ui/react/dialog' -import { cn } from '@langgenius/dify-ui/cn' +import { cn } from '../cn' export const Dialog = BaseDialog.Root /** @public */ diff --git a/web/app/components/base/ui/dropdown-menu/__tests__/index.spec.tsx b/packages/dify-ui/src/dropdown-menu/__tests__/index.spec.tsx similarity index 100% rename from web/app/components/base/ui/dropdown-menu/__tests__/index.spec.tsx rename to packages/dify-ui/src/dropdown-menu/__tests__/index.spec.tsx diff --git a/web/app/components/base/ui/dropdown-menu/index.stories.tsx b/packages/dify-ui/src/dropdown-menu/index.stories.tsx similarity index 99% rename from web/app/components/base/ui/dropdown-menu/index.stories.tsx rename to packages/dify-ui/src/dropdown-menu/index.stories.tsx index ae0ad61c68..f73b33ac8b 100644 --- a/web/app/components/base/ui/dropdown-menu/index.stories.tsx +++ b/packages/dify-ui/src/dropdown-menu/index.stories.tsx @@ -1,4 +1,4 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import type { Meta, StoryObj } from '@storybook/react-vite' import { useState } from 'react' import { DropdownMenu, diff --git a/web/app/components/base/ui/dropdown-menu/index.tsx b/packages/dify-ui/src/dropdown-menu/index.tsx similarity index 95% rename from web/app/components/base/ui/dropdown-menu/index.tsx rename to packages/dify-ui/src/dropdown-menu/index.tsx index b0c034f3cb..f742625964 100644 --- a/web/app/components/base/ui/dropdown-menu/index.tsx +++ b/packages/dify-ui/src/dropdown-menu/index.tsx @@ -1,10 +1,10 @@ 'use client' import type { ReactNode } from 'react' -import type { OverlayItemVariant } from '@/app/components/base/ui/overlay-shared' -import type { Placement } from '@/app/components/base/ui/placement' +import type { OverlayItemVariant } from '../overlay-shared' +import type { Placement } from '../placement' import { Menu } from '@base-ui/react/menu' -import { cn } from '@langgenius/dify-ui/cn' +import { cn } from '../cn' import { overlayDestructiveClassName, overlayIndicatorClassName, @@ -13,8 +13,10 @@ import { overlayPopupBaseClassName, overlayRowClassName, overlaySeparatorClassName, -} from '@/app/components/base/ui/overlay-shared' -import { parsePlacement } from '@/app/components/base/ui/placement' +} from '../overlay-shared' +import { parsePlacement } from '../placement' + +export type { Placement } export const DropdownMenu = Menu.Root export const DropdownMenuTrigger = Menu.Trigger diff --git a/web/app/components/base/ui/number-field/__tests__/index.spec.tsx b/packages/dify-ui/src/number-field/__tests__/index.spec.tsx similarity index 100% rename from web/app/components/base/ui/number-field/__tests__/index.spec.tsx rename to packages/dify-ui/src/number-field/__tests__/index.spec.tsx diff --git a/web/app/components/base/ui/number-field/index.stories.tsx b/packages/dify-ui/src/number-field/index.stories.tsx similarity index 98% rename from web/app/components/base/ui/number-field/index.stories.tsx rename to packages/dify-ui/src/number-field/index.stories.tsx index 6abb93bf11..a436d997e6 100644 --- a/web/app/components/base/ui/number-field/index.stories.tsx +++ b/packages/dify-ui/src/number-field/index.stories.tsx @@ -1,5 +1,4 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' -import { cn } from '@langgenius/dify-ui/cn' +import type { Meta, StoryObj } from '@storybook/react-vite' import { useId, useState } from 'react' import { NumberField, @@ -10,6 +9,7 @@ import { NumberFieldInput, NumberFieldUnit, } from '.' +import { cn } from '../cn' type DemoFieldProps = { label: string diff --git a/web/app/components/base/ui/number-field/index.tsx b/packages/dify-ui/src/number-field/index.tsx similarity index 99% rename from web/app/components/base/ui/number-field/index.tsx rename to packages/dify-ui/src/number-field/index.tsx index 944aa52470..70e66dd91b 100644 --- a/web/app/components/base/ui/number-field/index.tsx +++ b/packages/dify-ui/src/number-field/index.tsx @@ -3,8 +3,8 @@ import type { VariantProps } from 'class-variance-authority' import type { HTMLAttributes } from 'react' import { NumberField as BaseNumberField } from '@base-ui/react/number-field' -import { cn } from '@langgenius/dify-ui/cn' import { cva } from 'class-variance-authority' +import { cn } from '../cn' export const NumberField = BaseNumberField.Root export type NumberFieldRootProps = BaseNumberField.Root.Props diff --git a/web/app/components/base/ui/overlay-shared.ts b/packages/dify-ui/src/overlay-shared.ts similarity index 100% rename from web/app/components/base/ui/overlay-shared.ts rename to packages/dify-ui/src/overlay-shared.ts diff --git a/web/app/components/base/ui/placement.ts b/packages/dify-ui/src/placement.ts similarity index 100% rename from web/app/components/base/ui/placement.ts rename to packages/dify-ui/src/placement.ts diff --git a/web/app/components/base/ui/popover/__tests__/index.spec.tsx b/packages/dify-ui/src/popover/__tests__/index.spec.tsx similarity index 100% rename from web/app/components/base/ui/popover/__tests__/index.spec.tsx rename to packages/dify-ui/src/popover/__tests__/index.spec.tsx diff --git a/web/app/components/base/ui/popover/index.stories.tsx b/packages/dify-ui/src/popover/index.stories.tsx similarity index 97% rename from web/app/components/base/ui/popover/index.stories.tsx rename to packages/dify-ui/src/popover/index.stories.tsx index 8dbae184de..dcea5018ab 100644 --- a/web/app/components/base/ui/popover/index.stories.tsx +++ b/packages/dify-ui/src/popover/index.stories.tsx @@ -1,5 +1,6 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' -import type { Placement } from '../placement' +import type { Meta, StoryObj } from '@storybook/react-vite' +import type { Placement } from '.' +import { Button } from '@langgenius/dify-ui/button' import { useState } from 'react' import { Popover, @@ -9,7 +10,6 @@ import { PopoverTitle, PopoverTrigger, } from '.' -import { Button } from '../button' const triggerButtonClassName = 'rounded-lg border border-divider-subtle bg-components-button-secondary-bg px-3 py-1.5 text-sm text-text-secondary shadow-xs hover:bg-state-base-hover' diff --git a/web/app/components/base/ui/popover/index.tsx b/packages/dify-ui/src/popover/index.tsx similarity index 91% rename from web/app/components/base/ui/popover/index.tsx rename to packages/dify-ui/src/popover/index.tsx index dbde17ae21..32d111a1b8 100644 --- a/web/app/components/base/ui/popover/index.tsx +++ b/packages/dify-ui/src/popover/index.tsx @@ -1,10 +1,12 @@ 'use client' import type { ReactNode } from 'react' -import type { Placement } from '@/app/components/base/ui/placement' +import type { Placement } from '../placement' import { Popover as BasePopover } from '@base-ui/react/popover' -import { cn } from '@langgenius/dify-ui/cn' -import { parsePlacement } from '@/app/components/base/ui/placement' +import { cn } from '../cn' +import { parsePlacement } from '../placement' + +export type { Placement } export const Popover = BasePopover.Root export const PopoverTrigger = BasePopover.Trigger diff --git a/web/app/components/base/ui/scroll-area/__tests__/index.spec.tsx b/packages/dify-ui/src/scroll-area/__tests__/index.spec.tsx similarity index 100% rename from web/app/components/base/ui/scroll-area/__tests__/index.spec.tsx rename to packages/dify-ui/src/scroll-area/__tests__/index.spec.tsx diff --git a/web/app/components/base/ui/scroll-area/index.stories.tsx b/packages/dify-ui/src/scroll-area/index.stories.tsx similarity index 98% rename from web/app/components/base/ui/scroll-area/index.stories.tsx rename to packages/dify-ui/src/scroll-area/index.stories.tsx index dbe8161f8f..e1f8f9cfb5 100644 --- a/web/app/components/base/ui/scroll-area/index.stories.tsx +++ b/packages/dify-ui/src/scroll-area/index.stories.tsx @@ -1,8 +1,6 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import type { Meta, StoryObj } from '@storybook/react-vite' import type { ReactNode } from 'react' -import { cn } from '@langgenius/dify-ui/cn' import * as React from 'react' -import AppIcon from '@/app/components/base/app-icon' import { ScrollAreaContent, ScrollAreaCorner, @@ -11,6 +9,7 @@ import { ScrollAreaThumb, ScrollAreaViewport, } from '.' +import { cn } from '../cn' const meta = { title: 'Base/UI/ScrollArea', @@ -490,12 +489,13 @@ const ExploreSidebarWebAppsPane = () => { )} >
- +
+ {item.icon} +
{item.name} diff --git a/web/app/components/base/ui/scroll-area/index.tsx b/packages/dify-ui/src/scroll-area/index.tsx similarity index 98% rename from web/app/components/base/ui/scroll-area/index.tsx rename to packages/dify-ui/src/scroll-area/index.tsx index 300946b1bf..4e1832e661 100644 --- a/web/app/components/base/ui/scroll-area/index.tsx +++ b/packages/dify-ui/src/scroll-area/index.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from 'react' import { ScrollArea as BaseScrollArea } from '@base-ui/react/scroll-area' -import { cn } from '@langgenius/dify-ui/cn' +import { cn } from '../cn' export const ScrollAreaRoot = BaseScrollArea.Root type ScrollAreaRootProps = BaseScrollArea.Root.Props diff --git a/web/app/components/base/ui/select/__tests__/index.spec.tsx b/packages/dify-ui/src/select/__tests__/index.spec.tsx similarity index 100% rename from web/app/components/base/ui/select/__tests__/index.spec.tsx rename to packages/dify-ui/src/select/__tests__/index.spec.tsx diff --git a/web/app/components/base/ui/select/index.stories.tsx b/packages/dify-ui/src/select/index.stories.tsx similarity index 99% rename from web/app/components/base/ui/select/index.stories.tsx rename to packages/dify-ui/src/select/index.stories.tsx index 027d0f74df..6dc832a291 100644 --- a/web/app/components/base/ui/select/index.stories.tsx +++ b/packages/dify-ui/src/select/index.stories.tsx @@ -1,4 +1,4 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import type { Meta, StoryObj } from '@storybook/react-vite' import { useState } from 'react' import { Select, diff --git a/web/app/components/base/ui/select/index.tsx b/packages/dify-ui/src/select/index.tsx similarity index 96% rename from web/app/components/base/ui/select/index.tsx rename to packages/dify-ui/src/select/index.tsx index 7de0088af4..62478fad21 100644 --- a/web/app/components/base/ui/select/index.tsx +++ b/packages/dify-ui/src/select/index.tsx @@ -2,15 +2,18 @@ import type { VariantProps } from 'class-variance-authority' import type { ReactNode } from 'react' -import type { Placement } from '@/app/components/base/ui/placement' +import type { Placement } from '../placement' import { Select as BaseSelect } from '@base-ui/react/select' -import { cn } from '@langgenius/dify-ui/cn' import { cva } from 'class-variance-authority' +import { cn } from '../cn' import { overlayLabelClassName, overlaySeparatorClassName, -} from '@/app/components/base/ui/overlay-shared' -import { parsePlacement } from '@/app/components/base/ui/placement' +} from '../overlay-shared' +import { parsePlacement } from '../placement' + +/** @public */ +export type { Placement } export const Select = BaseSelect.Root export const SelectValue = BaseSelect.Value diff --git a/web/app/components/base/ui/slider/__tests__/index.spec.tsx b/packages/dify-ui/src/slider/__tests__/index.spec.tsx similarity index 100% rename from web/app/components/base/ui/slider/__tests__/index.spec.tsx rename to packages/dify-ui/src/slider/__tests__/index.spec.tsx diff --git a/web/app/components/base/ui/slider/index.stories.tsx b/packages/dify-ui/src/slider/index.stories.tsx similarity index 96% rename from web/app/components/base/ui/slider/index.stories.tsx rename to packages/dify-ui/src/slider/index.stories.tsx index 91bc0d3ecb..a48e202142 100644 --- a/web/app/components/base/ui/slider/index.stories.tsx +++ b/packages/dify-ui/src/slider/index.stories.tsx @@ -1,4 +1,4 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import type { Meta, StoryObj } from '@storybook/react-vite' import type * as React from 'react' import { useState } from 'react' import { Slider } from '.' diff --git a/web/app/components/base/ui/slider/index.tsx b/packages/dify-ui/src/slider/index.tsx similarity index 98% rename from web/app/components/base/ui/slider/index.tsx rename to packages/dify-ui/src/slider/index.tsx index ad1bc0ba8f..cfe1361598 100644 --- a/web/app/components/base/ui/slider/index.tsx +++ b/packages/dify-ui/src/slider/index.tsx @@ -1,7 +1,7 @@ 'use client' import { Slider as BaseSlider } from '@base-ui/react/slider' -import { cn } from '@langgenius/dify-ui/cn' +import { cn } from '../cn' /** @public */ export const SliderRoot = BaseSlider.Root diff --git a/web/app/components/base/switch/__tests__/index.spec.tsx b/packages/dify-ui/src/switch/__tests__/index.spec.tsx similarity index 99% rename from web/app/components/base/switch/__tests__/index.spec.tsx rename to packages/dify-ui/src/switch/__tests__/index.spec.tsx index 0d46095ebd..055b6e0791 100644 --- a/web/app/components/base/switch/__tests__/index.spec.tsx +++ b/packages/dify-ui/src/switch/__tests__/index.spec.tsx @@ -1,8 +1,7 @@ import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { describe, expect, it, vi } from 'vitest' -import Switch from '../index' -import { SwitchSkeleton } from '../skeleton' +import { Switch, SwitchSkeleton } from '../index' const getThumb = (switchElement: HTMLElement) => switchElement.querySelector('span') diff --git a/web/app/components/base/switch/index.stories.tsx b/packages/dify-ui/src/switch/index.stories.tsx similarity index 96% rename from web/app/components/base/switch/index.stories.tsx rename to packages/dify-ui/src/switch/index.stories.tsx index 1b3a52dcc4..f43b9ae154 100644 --- a/web/app/components/base/switch/index.stories.tsx +++ b/packages/dify-ui/src/switch/index.stories.tsx @@ -1,16 +1,16 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import type { Meta, StoryObj } from '@storybook/react-vite' +import type { ComponentProps } from 'react' import { useState, useTransition } from 'react' -import Switch from '.' -import { SwitchSkeleton } from './skeleton' +import { Switch, SwitchSkeleton } from '.' const meta = { - title: 'Base/Data Entry/Switch', + title: 'Base/UI/Switch', component: Switch, parameters: { layout: 'centered', docs: { description: { - component: 'Toggle switch built on Base UI with CVA variants, Figma-aligned design tokens, loading spinner, and skeleton placeholder. Import `Switch` for the toggle and `SwitchSkeleton` from `./skeleton` for loading placeholders.', + component: 'Toggle switch built on Base UI with CVA variants, Figma-aligned design tokens, loading spinner, and skeleton placeholder. Import `Switch` and `SwitchSkeleton` from `@langgenius/dify-ui/switch`.', }, }, }, @@ -42,7 +42,7 @@ const meta = { export default meta type Story = StoryObj -const SwitchDemo = (args: any) => { +const SwitchDemo = (args: Partial>) => { const [enabled, setEnabled] = useState(args.checked ?? false) return ( @@ -338,7 +338,7 @@ export const Skeleton: Story = { parameters: { docs: { description: { - story: '`SwitchSkeleton` renders a non-interactive placeholder with `bg-text-quaternary opacity-20`. Imported separately from `./skeleton`.', + story: '`SwitchSkeleton` renders a non-interactive placeholder with `bg-text-quaternary opacity-20`. Exported from `@langgenius/dify-ui/switch` alongside `Switch`.', }, }, }, diff --git a/web/app/components/base/switch/index.tsx b/packages/dify-ui/src/switch/index.tsx similarity index 72% rename from web/app/components/base/switch/index.tsx rename to packages/dify-ui/src/switch/index.tsx index 942a2291d0..dd15ef6f79 100644 --- a/web/app/components/base/switch/index.tsx +++ b/packages/dify-ui/src/switch/index.tsx @@ -1,10 +1,11 @@ 'use client' +import type { Switch as BaseSwitchNS } from '@base-ui/react/switch' import type { VariantProps } from 'class-variance-authority' +import type { HTMLAttributes } from 'react' import { Switch as BaseSwitch } from '@base-ui/react/switch' -import { cn } from '@langgenius/dify-ui/cn' import { cva } from 'class-variance-authority' -import * as React from 'react' +import { cn } from '../cn' const switchRootStateClassName = 'bg-components-toggle-bg-unchecked hover:bg-components-toggle-bg-unchecked-hover data-checked:bg-components-toggle-bg data-checked:hover:bg-components-toggle-bg-hover data-disabled:cursor-not-allowed data-disabled:bg-components-toggle-bg-unchecked-disabled data-disabled:hover:bg-components-toggle-bg-unchecked-disabled data-disabled:data-checked:bg-components-toggle-bg-disabled data-disabled:data-checked:hover:bg-components-toggle-bg-disabled' @@ -61,46 +62,35 @@ const spinnerSizeConfig: Partial void - 'size'?: SwitchSize - 'disabled'?: boolean - 'loading'?: boolean - 'className'?: string - 'aria-label'?: string - 'aria-labelledby'?: string - 'data-testid'?: string -} +export type SwitchProps + = Omit + & VariantProps + & { + onCheckedChange?: (checked: boolean) => void + loading?: boolean + className?: string + } -const Switch = ({ - ref, +export function Switch({ checked, - onCheckedChange, size = 'md', - disabled = false, + disabled, loading = false, className, - 'aria-label': ariaLabel, - 'aria-labelledby': ariaLabelledBy, - 'data-testid': dataTestid, -}: SwitchProps & { - ref?: React.Ref -}) => { + onCheckedChange, + ...props +}: SwitchProps) { const isDisabled = disabled || loading - const spinner = loading ? spinnerSizeConfig[size] : undefined + const spinner = loading && size ? spinnerSizeConfig[size] : undefined return ( onCheckedChange?.(value)} disabled={isDisabled} aria-busy={loading || undefined} - aria-label={ariaLabel} - aria-labelledby={ariaLabelledBy} className={cn(switchRootVariants({ size }), className)} - data-testid={dataTestid} + onCheckedChange={value => onCheckedChange?.(value)} + {...props} > , 'className'> + & VariantProps + & { + className?: string + } + +export function SwitchSkeleton({ + size = 'md', + className, + ...props +}: SwitchSkeletonProps) { + return ( +
+ ) +} diff --git a/web/app/components/base/ui/toast/__tests__/index.spec.tsx b/packages/dify-ui/src/toast/__tests__/index.spec.tsx similarity index 100% rename from web/app/components/base/ui/toast/__tests__/index.spec.tsx rename to packages/dify-ui/src/toast/__tests__/index.spec.tsx diff --git a/web/app/components/base/ui/toast/index.stories.tsx b/packages/dify-ui/src/toast/index.stories.tsx similarity index 99% rename from web/app/components/base/ui/toast/index.stories.tsx rename to packages/dify-ui/src/toast/index.stories.tsx index d292e3434d..cbfb944f19 100644 --- a/web/app/components/base/ui/toast/index.stories.tsx +++ b/packages/dify-ui/src/toast/index.stories.tsx @@ -1,4 +1,4 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' +import type { Meta, StoryObj } from '@storybook/react-vite' import type { ReactNode } from 'react' import { toast } from '.' diff --git a/web/app/components/base/ui/toast/index.tsx b/packages/dify-ui/src/toast/index.tsx similarity index 99% rename from web/app/components/base/ui/toast/index.tsx rename to packages/dify-ui/src/toast/index.tsx index 1f9fea5173..a479621563 100644 --- a/web/app/components/base/ui/toast/index.tsx +++ b/packages/dify-ui/src/toast/index.tsx @@ -7,7 +7,7 @@ import type { } from '@base-ui/react/toast' import type { ReactNode } from 'react' import { Toast as BaseToast } from '@base-ui/react/toast' -import { cn } from '@langgenius/dify-ui/cn' +import { cn } from '../cn' type ToastData = Record type ToastToneStyle = { diff --git a/web/app/components/base/ui/tooltip/__tests__/index.spec.tsx b/packages/dify-ui/src/tooltip/__tests__/index.spec.tsx similarity index 100% rename from web/app/components/base/ui/tooltip/__tests__/index.spec.tsx rename to packages/dify-ui/src/tooltip/__tests__/index.spec.tsx diff --git a/web/app/components/base/ui/tooltip/index.stories.tsx b/packages/dify-ui/src/tooltip/index.stories.tsx similarity index 98% rename from web/app/components/base/ui/tooltip/index.stories.tsx rename to packages/dify-ui/src/tooltip/index.stories.tsx index 16d0675c44..dca3be32f3 100644 --- a/web/app/components/base/ui/tooltip/index.stories.tsx +++ b/packages/dify-ui/src/tooltip/index.stories.tsx @@ -1,5 +1,5 @@ -import type { Meta, StoryObj } from '@storybook/nextjs-vite' -import type { Placement } from '../placement' +import type { Meta, StoryObj } from '@storybook/react-vite' +import type { Placement } from '.' import { useState } from 'react' import { Tooltip, diff --git a/web/app/components/base/ui/tooltip/index.tsx b/packages/dify-ui/src/tooltip/index.tsx similarity index 90% rename from web/app/components/base/ui/tooltip/index.tsx rename to packages/dify-ui/src/tooltip/index.tsx index b84921e351..e0fcd7c5c3 100644 --- a/web/app/components/base/ui/tooltip/index.tsx +++ b/packages/dify-ui/src/tooltip/index.tsx @@ -1,10 +1,12 @@ 'use client' import type { ReactNode } from 'react' -import type { Placement } from '@/app/components/base/ui/placement' +import type { Placement } from '../placement' import { Tooltip as BaseTooltip } from '@base-ui/react/tooltip' -import { cn } from '@langgenius/dify-ui/cn' -import { parsePlacement } from '@/app/components/base/ui/placement' +import { cn } from '../cn' +import { parsePlacement } from '../placement' + +export type { Placement } type TooltipContentVariant = 'default' | 'plain' diff --git a/packages/dify-ui/tailwind.config.ts b/packages/dify-ui/tailwind.config.ts new file mode 100644 index 0000000000..bcf1731775 --- /dev/null +++ b/packages/dify-ui/tailwind.config.ts @@ -0,0 +1,23 @@ +import type { Config } from 'tailwindcss' +import { getIconCollections, iconsPlugin } from '@egoist/tailwindcss-icons' +import difyUIPreset from './src/tailwind-preset' + +const config: Config = { + content: [ + './src/**/*.{js,ts,jsx,tsx,mdx}', + './.storybook/**/*.{js,ts,jsx,tsx,mdx}', + ], + presets: [difyUIPreset], + plugins: [ + iconsPlugin({ + collections: getIconCollections(['ri']), + extraProperties: { + width: '1rem', + height: '1rem', + display: 'block', + }, + }), + ], +} + +export default config diff --git a/packages/dify-ui/tests/setup.ts b/packages/dify-ui/tests/setup.ts new file mode 100644 index 0000000000..e1ea15af2b --- /dev/null +++ b/packages/dify-ui/tests/setup.ts @@ -0,0 +1,44 @@ +import { act, cleanup } from '@testing-library/react' +import '@testing-library/jest-dom/vitest' + +if (typeof Element !== 'undefined' && !Element.prototype.getAnimations) + Element.prototype.getAnimations = () => [] + +if (typeof document !== 'undefined' && !document.getAnimations) + document.getAnimations = () => [] + +if (typeof globalThis.ResizeObserver === 'undefined') { + globalThis.ResizeObserver = class { + observe() { + return undefined + } + + unobserve() { + return undefined + } + + disconnect() { + return undefined + } + } +} + +if (typeof globalThis.IntersectionObserver === 'undefined') { + globalThis.IntersectionObserver = class { + readonly root: Element | Document | null = null + readonly rootMargin: string = '' + readonly scrollMargin: string = '' + readonly thresholds: ReadonlyArray = [] + constructor(_callback: IntersectionObserverCallback, _options?: IntersectionObserverInit) { /* noop */ } + observe(_target: Element) { /* noop */ } + unobserve(_target: Element) { /* noop */ } + disconnect() { /* noop */ } + takeRecords(): IntersectionObserverEntry[] { return [] } + } +} + +afterEach(async () => { + await act(async () => { + cleanup() + }) +}) diff --git a/packages/dify-ui/tsconfig.json b/packages/dify-ui/tsconfig.json index b31c48ead6..678e1de897 100644 --- a/packages/dify-ui/tsconfig.json +++ b/packages/dify-ui/tsconfig.json @@ -1,14 +1,10 @@ { - "extends": "@dify/tsconfig/base.json", + "extends": "@dify/tsconfig/react.json", "compilerOptions": { - "rootDir": "src", - "declaration": true, - "declarationMap": true, - "noEmit": false, - "outDir": "dist", - "sourceMap": true, + "rootDir": ".", + "types": ["vitest/globals", "@testing-library/jest-dom"], "isolatedModules": true, "verbatimModuleSyntax": true }, - "include": ["src"] + "include": ["src", "tests", ".storybook", "tailwind.config.ts", "vite.config.ts"] } diff --git a/packages/dify-ui/vite.config.ts b/packages/dify-ui/vite.config.ts new file mode 100644 index 0000000000..a20b7fb35c --- /dev/null +++ b/packages/dify-ui/vite.config.ts @@ -0,0 +1,27 @@ +import react from '@vitejs/plugin-react' +import { defineConfig } from 'vite-plus' + +const isCI = !!process.env.CI + +export default defineConfig({ + plugins: [react()], + resolve: { + tsconfigPaths: true, + }, + test: { + environment: 'happy-dom', + globals: true, + setupFiles: ['./tests/setup.ts'], + coverage: { + provider: 'v8', + include: ['src/**/*.{ts,tsx}'], + exclude: [ + 'src/**/*.stories.{ts,tsx}', + 'src/**/__tests__/**', + 'src/themes/**', + 'src/styles/**', + ], + reporter: isCI ? ['json', 'json-summary'] : ['text', 'json', 'json-summary'], + }, + }, +}) diff --git a/packages/tsconfig/nextjs.json b/packages/tsconfig/nextjs.json index 81c6436a97..5beddd55ed 100644 --- a/packages/tsconfig/nextjs.json +++ b/packages/tsconfig/nextjs.json @@ -1,5 +1,5 @@ { - "extends": "./web.json", + "extends": "./react.json", "compilerOptions": { "plugins": [ { diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json index 52cafc5bb3..7a5b20c1db 100644 --- a/packages/tsconfig/package.json +++ b/packages/tsconfig/package.json @@ -6,6 +6,6 @@ "./base.json": "./base.json", "./nextjs.json": "./nextjs.json", "./node.json": "./node.json", - "./web.json": "./web.json" + "./react.json": "./react.json" } } diff --git a/packages/tsconfig/web.json b/packages/tsconfig/react.json similarity index 100% rename from packages/tsconfig/web.json rename to packages/tsconfig/react.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 914bc342e2..1e0994d0a2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -135,6 +135,9 @@ catalogs: '@storybook/react': specifier: 10.3.5 version: 10.3.5 + '@storybook/react-vite': + specifier: 10.3.5 + version: 10.3.5 '@streamdown/math': specifier: 1.0.2 version: 1.0.2 @@ -632,15 +635,87 @@ importers: specifier: 'catalog:' version: 3.5.0 devDependencies: + '@base-ui/react': + specifier: 'catalog:' + version: 1.4.0(@date-fns/tz@1.4.1)(@types/react@19.2.14)(date-fns@4.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@chromatic-com/storybook': + specifier: 'catalog:' + version: 5.1.2(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)) '@dify/tsconfig': specifier: workspace:* version: link:../tsconfig + '@egoist/tailwindcss-icons': + specifier: 'catalog:' + version: 1.9.2(tailwindcss@4.2.2) + '@iconify-json/ri': + specifier: 'catalog:' + version: 1.2.10 + '@storybook/addon-docs': + specifier: 'catalog:' + version: 10.3.5(@types/react@19.2.14)(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(rollup@4.59.0)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(webpack@5.105.4(esbuild@0.27.2)) + '@storybook/addon-links': + specifier: 'catalog:' + version: 10.3.5(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)) + '@storybook/addon-themes': + specifier: 'catalog:' + version: 10.3.5(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)) + '@storybook/react-vite': + specifier: 'catalog:' + version: 10.3.5(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(rollup@4.59.0)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(typescript@6.0.2)(webpack@5.105.4(esbuild@0.27.2)) + '@tailwindcss/vite': + specifier: 'catalog:' + version: 4.2.2(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3)) + '@testing-library/jest-dom': + specifier: 'catalog:' + version: 6.9.1 + '@testing-library/react': + specifier: 'catalog:' + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@testing-library/user-event': + specifier: 'catalog:' + version: 14.6.1(@testing-library/dom@10.4.1) + '@types/react': + specifier: 'catalog:' + version: 19.2.14 + '@types/react-dom': + specifier: 'catalog:' + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: 'catalog:' + version: 6.0.1(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3)) + '@vitest/coverage-v8': + specifier: 'catalog:' + version: 4.1.4(@voidzero-dev/vite-plus-test@0.1.18) + class-variance-authority: + specifier: 'catalog:' + version: 0.7.1 + happy-dom: + specifier: 'catalog:' + version: 20.9.0 + react: + specifier: 'catalog:' + version: 19.2.5 + react-dom: + specifier: 'catalog:' + version: 19.2.5(react@19.2.5) + storybook: + specifier: 'catalog:' + version: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) tailwindcss: specifier: 'catalog:' version: 4.2.2 typescript: specifier: 'catalog:' version: 6.0.2 + vite: + specifier: npm:@voidzero-dev/vite-plus-core@0.1.18 + version: '@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3)' + vite-plus: + specifier: 'catalog:' + version: 0.1.18(@types/node@25.6.0)(@vitest/coverage-v8@4.1.4)(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(happy-dom@20.9.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3) + vitest: + specifier: npm:@voidzero-dev/vite-plus-test@0.1.18 + version: '@voidzero-dev/vite-plus-test@0.1.18(@types/node@25.6.0)(@vitest/coverage-v8@4.1.4)(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(happy-dom@20.9.0)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3)' packages/iconify-collections: devDependencies: @@ -11202,6 +11277,23 @@ snapshots: - vite - webpack + '@storybook/addon-docs@10.3.5(@types/react@19.2.14)(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(rollup@4.59.0)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(webpack@5.105.4(esbuild@0.27.2))': + dependencies: + '@mdx-js/react': 3.1.1(@types/react@19.2.14)(react@19.2.5) + '@storybook/csf-plugin': 10.3.5(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(rollup@4.59.0)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(webpack@5.105.4(esbuild@0.27.2)) + '@storybook/icons': 2.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@storybook/react-dom-shim': 10.3.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + storybook: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@types/react' + - esbuild + - rollup + - vite + - webpack + '@storybook/addon-links@10.3.5(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))': dependencies: '@storybook/global': 5.0.0 @@ -11229,6 +11321,17 @@ snapshots: - rollup - webpack + '@storybook/builder-vite@10.3.5(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(rollup@4.59.0)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(webpack@5.105.4(esbuild@0.27.2))': + dependencies: + '@storybook/csf-plugin': 10.3.5(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(rollup@4.59.0)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(webpack@5.105.4(esbuild@0.27.2)) + storybook: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + ts-dedent: 2.2.0 + vite: '@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3)' + transitivePeerDependencies: + - esbuild + - rollup + - webpack + '@storybook/csf-plugin@10.3.5(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(rollup@4.59.0)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(webpack@5.105.4(esbuild@0.27.2)(uglify-js@3.19.3))': dependencies: storybook: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) @@ -11239,6 +11342,16 @@ snapshots: vite: '@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3)' webpack: 5.105.4(esbuild@0.27.2)(uglify-js@3.19.3) + '@storybook/csf-plugin@10.3.5(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(rollup@4.59.0)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(webpack@5.105.4(esbuild@0.27.2))': + dependencies: + storybook: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + unplugin: 2.3.11 + optionalDependencies: + esbuild: 0.27.2 + rollup: 4.59.0 + vite: '@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3)' + webpack: 5.105.4(esbuild@0.27.2) + '@storybook/global@5.0.0': {} '@storybook/icons@2.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': @@ -11296,6 +11409,28 @@ snapshots: - typescript - webpack + '@storybook/react-vite@10.3.5(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(rollup@4.59.0)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(typescript@6.0.2)(webpack@5.105.4(esbuild@0.27.2))': + dependencies: + '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(typescript@6.0.2) + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) + '@storybook/builder-vite': 10.3.5(@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3))(esbuild@0.27.2)(rollup@4.59.0)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(webpack@5.105.4(esbuild@0.27.2)) + '@storybook/react': 10.3.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(typescript@6.0.2) + empathic: 2.0.0 + magic-string: 0.30.21 + react: 19.2.5 + react-docgen: 8.0.3 + react-dom: 19.2.5(react@19.2.5) + resolve: 1.22.11 + storybook: 10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + tsconfig-paths: 4.2.0 + vite: '@voidzero-dev/vite-plus-core@0.1.18(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(sass@1.98.0)(terser@5.46.1)(tsx@4.21.0)(typescript@6.0.2)(yaml@2.8.3)' + transitivePeerDependencies: + - esbuild + - rollup + - supports-color + - typescript + - webpack + '@storybook/react@10.3.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(storybook@10.3.5(@testing-library/dom@10.4.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(typescript@6.0.2)': dependencies: '@storybook/global': 5.0.0 @@ -16589,6 +16724,17 @@ snapshots: esbuild: 0.27.2 uglify-js: 3.19.3 + terser-webpack-plugin@5.4.0(esbuild@0.27.2)(webpack@5.105.4(esbuild@0.27.2)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + terser: 5.46.1 + webpack: 5.105.4(esbuild@0.27.2) + optionalDependencies: + esbuild: 0.27.2 + optional: true + terser@5.46.1: dependencies: '@jridgewell/source-map': 0.3.11 @@ -17118,6 +17264,39 @@ snapshots: webpack-virtual-modules@0.6.2: {} + webpack@5.105.4(esbuild@0.27.2): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) + browserslist: 4.28.1 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.20.1 + es-module-lexer: 2.0.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.2 + terser-webpack-plugin: 5.4.0(esbuild@0.27.2)(webpack@5.105.4(esbuild@0.27.2)) + watchpack: 2.5.1 + webpack-sources: 3.3.4 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + optional: true + webpack@5.105.4(esbuild@0.27.2)(uglify-js@3.19.3): dependencies: '@types/eslint-scope': 3.7.7 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0bd1303fb3..12fea26d68 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -55,6 +55,7 @@ catalog: '@storybook/addon-themes': 10.3.5 '@storybook/nextjs-vite': 10.3.5 '@storybook/react': 10.3.5 + '@storybook/react-vite': 10.3.5 '@streamdown/math': 1.0.2 '@svgdotjs/svg.js': 3.2.5 '@t3-oss/env-nextjs': 0.13.11 diff --git a/web/.storybook/preview.tsx b/web/.storybook/preview.tsx index a9144e7128..92b5baab0d 100644 --- a/web/.storybook/preview.tsx +++ b/web/.storybook/preview.tsx @@ -1,8 +1,8 @@ import type { Preview } from '@storybook/react' import type { Resource } from 'i18next' +import { ToastHost } from '@langgenius/dify-ui/toast' import { withThemeByDataAttribute } from '@storybook/addon-themes' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import { ToastHost } from '../app/components/base/ui/toast' import { I18nClientProvider as I18N } from '../app/components/provider/i18n' import commonEnUS from '../i18n/en-US/common.json' diff --git a/web/__tests__/app/app-publisher-flow.test.tsx b/web/__tests__/app/app-publisher-flow.test.tsx index 4f24fc310c..9c09acf6a1 100644 --- a/web/__tests__/app/app-publisher-flow.test.tsx +++ b/web/__tests__/app/app-publisher-flow.test.tsx @@ -106,7 +106,7 @@ vi.mock('@/service/apps', () => ({ fetchAppDetailDirect: (...args: unknown[]) => mockFetchAppDetailDirect(...args), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: (...args: unknown[]) => mockToastError(...args), }, diff --git a/web/__tests__/apps/app-card-operations-flow.test.tsx b/web/__tests__/apps/app-card-operations-flow.test.tsx index 4b1c05e7ae..8c3219794d 100644 --- a/web/__tests__/apps/app-card-operations-flow.test.tsx +++ b/web/__tests__/apps/app-card-operations-flow.test.tsx @@ -31,7 +31,7 @@ const toastMocks = vi.hoisted(() => ({ })) const mockRouterPush = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: (message: string, options?: Record) => toastMocks.mockNotify({ type: 'success', message, ...options }), error: (message: string, options?: Record) => toastMocks.mockNotify({ type: 'error', message, ...options }), diff --git a/web/__tests__/billing/cloud-plan-payment-flow.test.tsx b/web/__tests__/billing/cloud-plan-payment-flow.test.tsx index 0c1efbe1af..de145a73e6 100644 --- a/web/__tests__/billing/cloud-plan-payment-flow.test.tsx +++ b/web/__tests__/billing/cloud-plan-payment-flow.test.tsx @@ -8,10 +8,10 @@ * and workspace manager permission enforcement. */ import type { BasicPlan } from '@/app/components/billing/type' +import { toast, ToastHost } from '@langgenius/dify-ui/toast' import { cleanup, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import * as React from 'react' -import { toast, ToastHost } from '@/app/components/base/ui/toast' import { ALL_PLANS } from '@/app/components/billing/config' import { PlanRange } from '@/app/components/billing/pricing/plan-switcher/plan-range-switcher' import CloudPlanItem from '@/app/components/billing/pricing/plans/cloud-plan-item' diff --git a/web/__tests__/billing/self-hosted-plan-flow.test.tsx b/web/__tests__/billing/self-hosted-plan-flow.test.tsx index a3386d0092..f2b5cd3531 100644 --- a/web/__tests__/billing/self-hosted-plan-flow.test.tsx +++ b/web/__tests__/billing/self-hosted-plan-flow.test.tsx @@ -1,3 +1,4 @@ +import { toast, ToastHost } from '@langgenius/dify-ui/toast' /** * Integration test: Self-Hosted Plan Flow * @@ -10,7 +11,6 @@ import { cleanup, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import * as React from 'react' -import { toast, ToastHost } from '@/app/components/base/ui/toast' import { contactSalesUrl, getStartedWithCommunityUrl, getWithPremiumUrl } from '@/app/components/billing/config' import SelfHostedPlanItem from '@/app/components/billing/pricing/plans/self-hosted-plan-item' import { SelfHostedPlan } from '@/app/components/billing/type' diff --git a/web/__tests__/datasets/create-dataset-flow.test.tsx b/web/__tests__/datasets/create-dataset-flow.test.tsx index 34d64d8c43..c8504a10c7 100644 --- a/web/__tests__/datasets/create-dataset-flow.test.tsx +++ b/web/__tests__/datasets/create-dataset-flow.test.tsx @@ -33,7 +33,7 @@ vi.mock('@/service/knowledge/use-dataset', () => ({ useInvalidDatasetList: () => vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ default: { notify: vi.fn() }, toast: { success: vi.fn(), diff --git a/web/__tests__/datasets/dataset-settings-flow.test.tsx b/web/__tests__/datasets/dataset-settings-flow.test.tsx index 7c53401b15..eaafcafe62 100644 --- a/web/__tests__/datasets/dataset-settings-flow.test.tsx +++ b/web/__tests__/datasets/dataset-settings-flow.test.tsx @@ -59,7 +59,7 @@ vi.mock('@/app/components/datasets/common/check-rerank-model', () => ({ isReRankModelSelected: () => true, })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: mockToastError, success: vi.fn(), @@ -318,7 +318,7 @@ describe('Dataset Settings Flow - Cross-Module Configuration Cascade', () => { describe('Form Submission Validation → All Fields Together', () => { it('should reject empty name on save', async () => { - const { toast } = await import('@/app/components/base/ui/toast') + const { toast } = await import('@langgenius/dify-ui/toast') const { result } = renderHook(() => useFormState()) act(() => { diff --git a/web/__tests__/explore/sidebar-lifecycle-flow.test.tsx b/web/__tests__/explore/sidebar-lifecycle-flow.test.tsx index 64dd5321ac..35c8175a36 100644 --- a/web/__tests__/explore/sidebar-lifecycle-flow.test.tsx +++ b/web/__tests__/explore/sidebar-lifecycle-flow.test.tsx @@ -53,8 +53,8 @@ vi.mock('@/service/use-explore', () => ({ }), })) -vi.mock('@/app/components/base/ui/toast', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, toast: { diff --git a/web/__tests__/plugins/plugin-install-flow.test.ts b/web/__tests__/plugins/plugin-install-flow.test.ts index dd5a18b724..d975eb7167 100644 --- a/web/__tests__/plugins/plugin-install-flow.test.ts +++ b/web/__tests__/plugins/plugin-install-flow.test.ts @@ -10,7 +10,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { checkForUpdates, fetchReleases, handleUpload } from '@/app/components/plugins/install-plugin/hooks' const mockToastNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign((message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }), { success: (message: string) => mockToastNotify({ type: 'success', message }), error: (message: string) => mockToastNotify({ type: 'error', message }), diff --git a/web/__tests__/rag-pipeline/dsl-export-import-flow.test.ts b/web/__tests__/rag-pipeline/dsl-export-import-flow.test.ts index cdf7aba4f6..cc97065d8f 100644 --- a/web/__tests__/rag-pipeline/dsl-export-import-flow.test.ts +++ b/web/__tests__/rag-pipeline/dsl-export-import-flow.test.ts @@ -20,7 +20,7 @@ const mockToast = { promise: vi.fn(), } -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: mockToast, })) const mockEventEmitter = { emit: vi.fn() } diff --git a/web/__tests__/tools/tool-provider-detail-flow.test.tsx b/web/__tests__/tools/tool-provider-detail-flow.test.tsx index c52871c946..9cf4772152 100644 --- a/web/__tests__/tools/tool-provider-detail-flow.test.tsx +++ b/web/__tests__/tools/tool-provider-detail-flow.test.tsx @@ -133,7 +133,7 @@ vi.mock('@/app/components/base/drawer', () => ({ ), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ default: { notify: vi.fn() }, toast: { success: vi.fn(), diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx index 289e50c257..8ef0b65e0f 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx @@ -5,6 +5,7 @@ import type { BlockEnum } from '@/app/components/workflow/types' import type { UpdateAppSiteCodeResponse } from '@/models/app' import type { App } from '@/types/app' import type { I18nKeysByPrefix } from '@/types/i18n' +import { toast } from '@langgenius/dify-ui/toast' import * as React from 'react' import { useCallback, useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' @@ -12,7 +13,6 @@ import AppCard from '@/app/components/app/overview/app-card' import TriggerCard from '@/app/components/app/overview/trigger-card' import { useStore as useAppStore } from '@/app/components/app/store' import Loading from '@/app/components/base/loading' -import { toast } from '@/app/components/base/ui/toast' import MCPServiceCard from '@/app/components/tools/mcp/mcp-service-card' import { collaborationManager } from '@/app/components/workflow/collaboration/core/collaboration-manager' import { webSocketClient } from '@/app/components/workflow/collaboration/core/websocket-manager' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx index 3efd58ac40..471ab86e12 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx @@ -2,12 +2,12 @@ import type { FC, JSX } from 'react' import type { AliyunConfig, ArizeConfig, DatabricksConfig, LangFuseConfig, LangSmithConfig, MLflowConfig, OpikConfig, PhoenixConfig, TencentConfig, WeaveConfig } from './type' import { cn } from '@langgenius/dify-ui/cn' +import { Switch } from '@langgenius/dify-ui/switch' import { useBoolean } from 'ahooks' import * as React from 'react' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' -import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' import Indicator from '@/app/components/header/indicator' import ProviderConfigModal from './provider-config-modal' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx index 9c42f85825..35fafb2a79 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx @@ -3,6 +3,7 @@ import type { FC } from 'react' import type { AliyunConfig, ArizeConfig, DatabricksConfig, LangFuseConfig, LangSmithConfig, MLflowConfig, OpikConfig, PhoenixConfig, TencentConfig, WeaveConfig } from './type' import type { TracingStatus } from '@/models/app' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowDownDoubleLine, RiEqualizer2Line, @@ -14,7 +15,6 @@ import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import { AliyunIcon, ArizeIcon, DatabricksIcon, LangfuseIcon, LangsmithIcon, MlflowIcon, OpikIcon, PhoenixIcon, TencentIcon, WeaveIcon } from '@/app/components/base/icons/src/public/tracing' import Loading from '@/app/components/base/loading' -import { toast } from '@/app/components/base/ui/toast' import Indicator from '@/app/components/header/indicator' import { useAppContext } from '@/context/app-context' import { usePathname } from '@/next/navigation' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx index 6ff8725f63..4f2497ad71 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx @@ -1,6 +1,17 @@ 'use client' import type { FC } from 'react' import type { AliyunConfig, ArizeConfig, DatabricksConfig, LangFuseConfig, LangSmithConfig, MLflowConfig, OpikConfig, PhoenixConfig, TencentConfig, WeaveConfig } from './type' +import { + AlertDialog, + AlertDialogActions, + AlertDialogCancelButton, + AlertDialogConfirmButton, + AlertDialogContent, + AlertDialogDescription, + AlertDialogTitle, +} from '@langgenius/dify-ui/alert-dialog' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { useBoolean } from 'ahooks' import * as React from 'react' import { useCallback, useState } from 'react' @@ -12,17 +23,6 @@ import { PortalToFollowElem, PortalToFollowElemContent, } from '@/app/components/base/portal-to-follow-elem' -import { - AlertDialog, - AlertDialogActions, - AlertDialogCancelButton, - AlertDialogConfirmButton, - AlertDialogContent, - AlertDialogDescription, - AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { addTracingConfig, removeTracingConfig, updateTracingConfig } from '@/service/apps' import { docURL } from './config' import Field from './field' diff --git a/web/app/(humanInputLayout)/form/[token]/form.tsx b/web/app/(humanInputLayout)/form/[token]/form.tsx index be30585101..76dcd24293 100644 --- a/web/app/(humanInputLayout)/form/[token]/form.tsx +++ b/web/app/(humanInputLayout)/form/[token]/form.tsx @@ -1,8 +1,9 @@ 'use client' -import type { ButtonProps } from '@/app/components/base/ui/button' +import type { ButtonProps } from '@langgenius/dify-ui/button' import type { FormInputItem, UserAction } from '@/app/components/workflow/nodes/human-input/types' import type { SiteInfo } from '@/models/share' import type { HumanInputFormError } from '@/service/use-share' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiCheckboxCircleFill, @@ -19,7 +20,6 @@ import ExpirationTime from '@/app/components/base/chat/chat/answer/human-input-c import { getButtonStyle } from '@/app/components/base/chat/chat/answer/human-input-content/utils' import Loading from '@/app/components/base/loading' import DifyLogo from '@/app/components/base/logo/dify-logo' -import { Button } from '@/app/components/base/ui/button' import useDocumentTitle from '@/hooks/use-document-title' import { useParams } from '@/next/navigation' import { useGetHumanInputForm, useSubmitHumanInputForm } from '@/service/use-share' diff --git a/web/app/(shareLayout)/webapp-reset-password/check-code/page.tsx b/web/app/(shareLayout)/webapp-reset-password/check-code/page.tsx index d19e5a7d2d..d5b3237b12 100644 --- a/web/app/(shareLayout)/webapp-reset-password/check-code/page.tsx +++ b/web/app/(shareLayout)/webapp-reset-password/check-code/page.tsx @@ -1,10 +1,10 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import Countdown from '@/app/components/signin/countdown' import { useLocale } from '@/context/i18n' diff --git a/web/app/(shareLayout)/webapp-reset-password/page.tsx b/web/app/(shareLayout)/webapp-reset-password/page.tsx index cb6ece219c..2174ac0e8e 100644 --- a/web/app/(shareLayout)/webapp-reset-password/page.tsx +++ b/web/app/(shareLayout)/webapp-reset-password/page.tsx @@ -1,11 +1,11 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowLeftLine, RiLockPasswordLine } from '@remixicon/react' import { noop } from 'es-toolkit/function' import { useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown' import { emailRegex } from '@/config' import { useLocale } from '@/context/i18n' diff --git a/web/app/(shareLayout)/webapp-reset-password/set-password/page.tsx b/web/app/(shareLayout)/webapp-reset-password/set-password/page.tsx index df102e609a..9a290f8789 100644 --- a/web/app/(shareLayout)/webapp-reset-password/set-password/page.tsx +++ b/web/app/(shareLayout)/webapp-reset-password/set-password/page.tsx @@ -1,12 +1,12 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiCheckboxCircleFill } from '@remixicon/react' import { useCountDown } from 'ahooks' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { validPassword } from '@/config' import { useRouter, useSearchParams } from '@/next/navigation' import { changeWebAppPasswordWithToken } from '@/service/common' diff --git a/web/app/(shareLayout)/webapp-signin/check-code/page.tsx b/web/app/(shareLayout)/webapp-signin/check-code/page.tsx index b17fff4e52..c8d3351ded 100644 --- a/web/app/(shareLayout)/webapp-signin/check-code/page.tsx +++ b/web/app/(shareLayout)/webapp-signin/check-code/page.tsx @@ -1,11 +1,11 @@ 'use client' import type { FormEvent } from 'react' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react' import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import Countdown from '@/app/components/signin/countdown' import { useLocale } from '@/context/i18n' import { useWebAppStore } from '@/context/web-app-context' diff --git a/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx b/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx index 9b4a369908..fe6b157c1e 100644 --- a/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx +++ b/web/app/(shareLayout)/webapp-signin/components/external-member-sso-auth.tsx @@ -1,9 +1,9 @@ 'use client' +import { toast } from '@langgenius/dify-ui/toast' import * as React from 'react' import { useCallback, useEffect } from 'react' import AppUnavailable from '@/app/components/base/app-unavailable' import Loading from '@/app/components/base/loading' -import { toast } from '@/app/components/base/ui/toast' import { useGlobalPublicStore } from '@/context/global-public-context' import { useRouter, useSearchParams } from '@/next/navigation' import { fetchWebOAuth2SSOUrl, fetchWebOIDCSSOUrl, fetchWebSAMLSSOUrl } from '@/service/share' diff --git a/web/app/(shareLayout)/webapp-signin/components/mail-and-code-auth.tsx b/web/app/(shareLayout)/webapp-signin/components/mail-and-code-auth.tsx index f600dba8b2..2d2fde1808 100644 --- a/web/app/(shareLayout)/webapp-signin/components/mail-and-code-auth.tsx +++ b/web/app/(shareLayout)/webapp-signin/components/mail-and-code-auth.tsx @@ -1,9 +1,9 @@ +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { noop } from 'es-toolkit/function' import { useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown' import { emailRegex } from '@/config' import { useLocale } from '@/context/i18n' diff --git a/web/app/(shareLayout)/webapp-signin/components/mail-and-password-auth.tsx b/web/app/(shareLayout)/webapp-signin/components/mail-and-password-auth.tsx index 7fe5363927..79607a77df 100644 --- a/web/app/(shareLayout)/webapp-signin/components/mail-and-password-auth.tsx +++ b/web/app/(shareLayout)/webapp-signin/components/mail-and-password-auth.tsx @@ -1,10 +1,10 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { noop } from 'es-toolkit/function' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { emailRegex } from '@/config' import { useLocale } from '@/context/i18n' import { useWebAppStore } from '@/context/web-app-context' diff --git a/web/app/(shareLayout)/webapp-signin/components/sso-auth.tsx b/web/app/(shareLayout)/webapp-signin/components/sso-auth.tsx index d8759cc1d6..53ff80ced5 100644 --- a/web/app/(shareLayout)/webapp-signin/components/sso-auth.tsx +++ b/web/app/(shareLayout)/webapp-signin/components/sso-auth.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { useRouter, useSearchParams } from '@/next/navigation' import { fetchMembersOAuth2SSOUrl, fetchMembersOIDCSSOUrl, fetchMembersSAMLSSOUrl } from '@/service/share' import { SSOProtocol } from '@/types/feature' diff --git a/web/app/account/(commonLayout)/account-page/AvatarWithEdit.tsx b/web/app/account/(commonLayout)/account-page/AvatarWithEdit.tsx index 05048008b4..429eece383 100644 --- a/web/app/account/(commonLayout)/account-page/AvatarWithEdit.tsx +++ b/web/app/account/(commonLayout)/account-page/AvatarWithEdit.tsx @@ -1,9 +1,13 @@ 'use client' +import type { AvatarProps } from '@langgenius/dify-ui/avatar' import type { Area } from 'react-easy-crop' import type { OnImageInput } from '@/app/components/base/app-icon-picker/ImageInput' -import type { AvatarProps } from '@/app/components/base/ui/avatar' import type { ImageFile } from '@/types/app' +import { Avatar } from '@langgenius/dify-ui/avatar' +import { Button } from '@langgenius/dify-ui/button' +import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog' +import { toast } from '@langgenius/dify-ui/toast' import { RiDeleteBin5Line, RiPencilLine } from '@remixicon/react' import * as React from 'react' import { useCallback, useState } from 'react' @@ -12,10 +16,6 @@ import ImageInput from '@/app/components/base/app-icon-picker/ImageInput' import getCroppedImg from '@/app/components/base/app-icon-picker/utils' import Divider from '@/app/components/base/divider' import { useLocalFileUploader } from '@/app/components/base/image-uploader/hooks' -import { Avatar } from '@/app/components/base/ui/avatar' -import { Button } from '@/app/components/base/ui/button' -import { Dialog, DialogContent } from '@/app/components/base/ui/dialog' -import { toast } from '@/app/components/base/ui/toast' import { DISABLE_UPLOAD_IMAGE_AS_ICON } from '@/config' import { updateUserProfile } from '@/service/common' diff --git a/web/app/account/(commonLayout)/account-page/email-change-modal.tsx b/web/app/account/(commonLayout)/account-page/email-change-modal.tsx index 1b91c801bd..f3bb71c2d2 100644 --- a/web/app/account/(commonLayout)/account-page/email-change-modal.tsx +++ b/web/app/account/(commonLayout)/account-page/email-change-modal.tsx @@ -1,12 +1,12 @@ import type { ResponseError } from '@/service/fetch' +import { Button } from '@langgenius/dify-ui/button' +import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog' +import { toast } from '@langgenius/dify-ui/toast' import { RiCloseLine } from '@remixicon/react' import * as React from 'react' import { useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { Dialog, DialogContent } from '@/app/components/base/ui/dialog' -import { toast } from '@/app/components/base/ui/toast' import { useRouter } from '@/next/navigation' import { checkEmailExisted, diff --git a/web/app/account/(commonLayout)/account-page/index.tsx b/web/app/account/(commonLayout)/account-page/index.tsx index 8ad1b45467..a26fa942db 100644 --- a/web/app/account/(commonLayout)/account-page/index.tsx +++ b/web/app/account/(commonLayout)/account-page/index.tsx @@ -1,6 +1,9 @@ 'use client' import type { IItem } from '@/app/components/header/account-setting/collapse' import type { App } from '@/types/app' +import { Button } from '@langgenius/dify-ui/button' +import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog' +import { toast } from '@langgenius/dify-ui/toast' import { RiGraduationCapFill, } from '@remixicon/react' @@ -10,9 +13,6 @@ import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import Input from '@/app/components/base/input' import PremiumBadge from '@/app/components/base/premium-badge' -import { Button } from '@/app/components/base/ui/button' -import { Dialog, DialogContent } from '@/app/components/base/ui/dialog' -import { toast } from '@/app/components/base/ui/toast' import Collapse from '@/app/components/header/account-setting/collapse' import { IS_CE_EDITION, validPassword } from '@/config' import { useGlobalPublicStore } from '@/context/global-public-context' diff --git a/web/app/account/(commonLayout)/avatar.tsx b/web/app/account/(commonLayout)/avatar.tsx index 538e09b0fd..0893b130c4 100644 --- a/web/app/account/(commonLayout)/avatar.tsx +++ b/web/app/account/(commonLayout)/avatar.tsx @@ -1,5 +1,6 @@ 'use client' import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react' +import { Avatar } from '@langgenius/dify-ui/avatar' import { RiGraduationCapFill, } from '@remixicon/react' @@ -8,7 +9,6 @@ import { useTranslation } from 'react-i18next' import { resetUser } from '@/app/components/base/amplitude/utils' import { LogOut01 } from '@/app/components/base/icons/src/vender/line/general' import PremiumBadge from '@/app/components/base/premium-badge' -import { Avatar } from '@/app/components/base/ui/avatar' import { useProviderContext } from '@/context/provider-context' import { useRouter } from '@/next/navigation' import { useLogout, useUserProfile } from '@/service/use-common' diff --git a/web/app/account/(commonLayout)/delete-account/components/check-email.tsx b/web/app/account/(commonLayout)/delete-account/components/check-email.tsx index 3c7b7112f3..7e3351a10a 100644 --- a/web/app/account/(commonLayout)/delete-account/components/check-email.tsx +++ b/web/app/account/(commonLayout)/delete-account/components/check-email.tsx @@ -1,8 +1,8 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' import { useAppContext } from '@/context/app-context' import Link from '@/next/link' import { useSendDeleteAccountEmail } from '../state' diff --git a/web/app/account/(commonLayout)/delete-account/components/feed-back.tsx b/web/app/account/(commonLayout)/delete-account/components/feed-back.tsx index 9690bf8ed1..ee1e72e6e7 100644 --- a/web/app/account/(commonLayout)/delete-account/components/feed-back.tsx +++ b/web/app/account/(commonLayout)/delete-account/components/feed-back.tsx @@ -1,10 +1,10 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import CustomDialog from '@/app/components/base/dialog' import Textarea from '@/app/components/base/textarea' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { useAppContext } from '@/context/app-context' import { useRouter } from '@/next/navigation' import { useLogout } from '@/service/use-common' diff --git a/web/app/account/(commonLayout)/delete-account/components/verify-email.tsx b/web/app/account/(commonLayout)/delete-account/components/verify-email.tsx index 01e19e51b1..91c0d7737d 100644 --- a/web/app/account/(commonLayout)/delete-account/components/verify-email.tsx +++ b/web/app/account/(commonLayout)/delete-account/components/verify-email.tsx @@ -1,8 +1,8 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' import Countdown from '@/app/components/signin/countdown' import Link from '@/next/link' import { useAccountDeleteStore, useConfirmDeleteAccount, useSendDeleteAccountEmail } from '../state' diff --git a/web/app/account/(commonLayout)/header.tsx b/web/app/account/(commonLayout)/header.tsx index 688a1d7729..f0912d45d5 100644 --- a/web/app/account/(commonLayout)/header.tsx +++ b/web/app/account/(commonLayout)/header.tsx @@ -1,9 +1,9 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' import { RiArrowRightUpLine, RiRobot2Line } from '@remixicon/react' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import DifyLogo from '@/app/components/base/logo/dify-logo' -import { Button } from '@/app/components/base/ui/button' import { useGlobalPublicStore } from '@/context/global-public-context' import { useRouter } from '@/next/navigation' import Avatar from './avatar' diff --git a/web/app/account/oauth/authorize/page.tsx b/web/app/account/oauth/authorize/page.tsx index f370efcd3e..cd035ce16f 100644 --- a/web/app/account/oauth/authorize/page.tsx +++ b/web/app/account/oauth/authorize/page.tsx @@ -1,5 +1,8 @@ 'use client' +import { Avatar } from '@langgenius/dify-ui/avatar' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiAccountCircleLine, RiGlobalLine, @@ -11,9 +14,6 @@ import * as React from 'react' import { useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' -import { Avatar } from '@/app/components/base/ui/avatar' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import { setPostLoginRedirect } from '@/app/signin/utils/post-login-redirect' import { useRouter, useSearchParams } from '@/next/navigation' diff --git a/web/app/activate/activateForm.tsx b/web/app/activate/activateForm.tsx index 9b0a5f5b21..5fafa8eca1 100644 --- a/web/app/activate/activateForm.tsx +++ b/web/app/activate/activateForm.tsx @@ -1,9 +1,9 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' -import { Button } from '@/app/components/base/ui/button' import useDocumentTitle from '@/hooks/use-document-title' import { useRouter, useSearchParams } from '@/next/navigation' diff --git a/web/app/components/app-sidebar/__tests__/app-sidebar-dropdown.spec.tsx b/web/app/components/app-sidebar/__tests__/app-sidebar-dropdown.spec.tsx index 5e18bbc343..5557118c78 100644 --- a/web/app/components/app-sidebar/__tests__/app-sidebar-dropdown.spec.tsx +++ b/web/app/components/app-sidebar/__tests__/app-sidebar-dropdown.spec.tsx @@ -19,7 +19,7 @@ vi.mock('@/context/app-context', () => ({ }), })) -vi.mock('@/app/components/base/ui/dropdown-menu', () => { +vi.mock('@langgenius/dify-ui/dropdown-menu', () => { const DropdownMenuContext = React.createContext<{ isOpen: boolean, setOpen: (open: boolean) => void } | null>(null) const useDropdownMenuContext = () => { diff --git a/web/app/components/app-sidebar/__tests__/dataset-sidebar-dropdown.spec.tsx b/web/app/components/app-sidebar/__tests__/dataset-sidebar-dropdown.spec.tsx index 5060987cda..5c45df470a 100644 --- a/web/app/components/app-sidebar/__tests__/dataset-sidebar-dropdown.spec.tsx +++ b/web/app/components/app-sidebar/__tests__/dataset-sidebar-dropdown.spec.tsx @@ -21,7 +21,7 @@ vi.mock('@/hooks/use-knowledge', () => ({ }), })) -vi.mock('@/app/components/base/ui/dropdown-menu', () => { +vi.mock('@langgenius/dify-ui/dropdown-menu', () => { const DropdownMenuContext = React.createContext<{ isOpen: boolean, setOpen: (open: boolean) => void } | null>(null) const useDropdownMenuContext = () => { diff --git a/web/app/components/app-sidebar/app-info/__tests__/app-info-detail-panel.spec.tsx b/web/app/components/app-sidebar/app-info/__tests__/app-info-detail-panel.spec.tsx index 2171974253..9af90359f0 100644 --- a/web/app/components/app-sidebar/app-info/__tests__/app-info-detail-panel.spec.tsx +++ b/web/app/components/app-sidebar/app-info/__tests__/app-info-detail-panel.spec.tsx @@ -35,7 +35,7 @@ vi.mock('@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view', ), })) -vi.mock('@/app/components/base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: ({ children, onClick, className, size, variant }: { children: React.ReactNode onClick?: () => void diff --git a/web/app/components/app-sidebar/app-info/__tests__/app-operations.spec.tsx b/web/app/components/app-sidebar/app-info/__tests__/app-operations.spec.tsx index 461cedc20c..ff6aed2c71 100644 --- a/web/app/components/app-sidebar/app-info/__tests__/app-operations.spec.tsx +++ b/web/app/components/app-sidebar/app-info/__tests__/app-operations.spec.tsx @@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event' import * as React from 'react' import AppOperations from '../app-operations' -vi.mock('../../../base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: ({ children, onClick, className, size, variant, id, tabIndex, ...rest }: { 'children': React.ReactNode 'onClick'?: () => void @@ -30,7 +30,7 @@ vi.mock('../../../base/ui/button', () => ({ ), })) -vi.mock('../../../base/ui/dropdown-menu', () => { +vi.mock('@langgenius/dify-ui/dropdown-menu', () => { const DropdownMenuContext = React.createContext<{ isOpen: boolean, setOpen: (open: boolean) => void } | null>(null) const useDropdownMenuContext = () => { diff --git a/web/app/components/app-sidebar/app-info/__tests__/use-app-info-actions.spec.ts b/web/app/components/app-sidebar/app-info/__tests__/use-app-info-actions.spec.ts index 5b10b4c32b..23e5e51949 100644 --- a/web/app/components/app-sidebar/app-info/__tests__/use-app-info-actions.spec.ts +++ b/web/app/components/app-sidebar/app-info/__tests__/use-app-info-actions.spec.ts @@ -50,7 +50,7 @@ vi.mock('@/app/components/app/store', () => ({ }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign(toastMocks.api, { success: vi.fn((message, options) => toastMocks.call({ type: 'success', message, ...options })), error: vi.fn((message, options) => toastMocks.call({ type: 'error', message, ...options })), diff --git a/web/app/components/app-sidebar/app-info/app-info-detail-panel.tsx b/web/app/components/app-sidebar/app-info/app-info-detail-panel.tsx index 624630b179..9afa0063dc 100644 --- a/web/app/components/app-sidebar/app-info/app-info-detail-panel.tsx +++ b/web/app/components/app-sidebar/app-info/app-info-detail-panel.tsx @@ -1,6 +1,7 @@ import type { Operation } from './app-operations' import type { AppInfoModalType } from './use-app-info-actions' import type { App, AppSSO } from '@/types/app' +import { Button } from '@langgenius/dify-ui/button' import { RiDeleteBinLine, RiEditLine, @@ -14,7 +15,6 @@ import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import CardView from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view' import ContentDialog from '@/app/components/base/content-dialog' -import { Button } from '@/app/components/base/ui/button' import { AppModeEnum } from '@/types/app' import AppIcon from '../../base/app-icon' import { getAppModeLabel } from './app-mode-labels' diff --git a/web/app/components/app-sidebar/app-info/app-info-modals.tsx b/web/app/components/app-sidebar/app-info/app-info-modals.tsx index fa84ecc0b6..7d142e0cc6 100644 --- a/web/app/components/app-sidebar/app-info/app-info-modals.tsx +++ b/web/app/components/app-sidebar/app-info/app-info-modals.tsx @@ -3,10 +3,6 @@ import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-moda import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal' import type { EnvironmentVariable } from '@/app/components/workflow/types' import type { App, AppSSO } from '@/types/app' -import * as React from 'react' -import { useState } from 'react' -import { useTranslation } from 'react-i18next' -import Input from '@/app/components/base/input' import { AlertDialog, AlertDialogActions, @@ -15,7 +11,11 @@ import { AlertDialogContent, AlertDialogDescription, AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' +} from '@langgenius/dify-ui/alert-dialog' +import * as React from 'react' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import Input from '@/app/components/base/input' import dynamic from '@/next/dynamic' const SwitchAppModal = dynamic(() => import('@/app/components/app/switch-app-modal'), { ssr: false }) diff --git a/web/app/components/app-sidebar/app-info/app-operations.tsx b/web/app/components/app-sidebar/app-info/app-operations.tsx index 095fb31206..cc6afd739c 100644 --- a/web/app/components/app-sidebar/app-info/app-operations.tsx +++ b/web/app/components/app-sidebar/app-info/app-operations.tsx @@ -1,15 +1,15 @@ import type { JSX } from 'react' -import { RiMoreLine } from '@remixicon/react' -import { cloneElement, useEffect, useMemo, useRef, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' +import { Button } from '@langgenius/dify-ui/button' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, -} from '../../base/ui/dropdown-menu' +} from '@langgenius/dify-ui/dropdown-menu' +import { RiMoreLine } from '@remixicon/react' +import { cloneElement, useEffect, useMemo, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' export type Operation = { id: string diff --git a/web/app/components/app-sidebar/app-info/use-app-info-actions.ts b/web/app/components/app-sidebar/app-info/use-app-info-actions.ts index 3192d48f81..5ca0e85c18 100644 --- a/web/app/components/app-sidebar/app-info/use-app-info-actions.ts +++ b/web/app/components/app-sidebar/app-info/use-app-info-actions.ts @@ -1,10 +1,10 @@ import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal' import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal' import type { EnvironmentVariable } from '@/app/components/workflow/types' +import { toast } from '@langgenius/dify-ui/toast' import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useStore as useAppStore } from '@/app/components/app/store' -import { toast } from '@/app/components/base/ui/toast' import { NEED_REFRESH_APP_LIST_KEY } from '@/config' import { useProviderContext } from '@/context/provider-context' import { useRouter } from '@/next/navigation' diff --git a/web/app/components/app-sidebar/app-sidebar-dropdown.tsx b/web/app/components/app-sidebar/app-sidebar-dropdown.tsx index 617d14f426..0b5f13d918 100644 --- a/web/app/components/app-sidebar/app-sidebar-dropdown.tsx +++ b/web/app/components/app-sidebar/app-sidebar-dropdown.tsx @@ -1,5 +1,10 @@ import type { NavIcon } from './nav-link' import { cn } from '@langgenius/dify-ui/cn' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, +} from '@langgenius/dify-ui/dropdown-menu' import { RiEqualizer2Line, RiMenuLine, @@ -8,11 +13,6 @@ import * as React from 'react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import { useStore as useAppStore } from '@/app/components/app/store' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' import { useAppContext } from '@/context/app-context' import AppIcon from '../base/app-icon' import Divider from '../base/divider' diff --git a/web/app/components/app-sidebar/dataset-info/__tests__/dropdown-callbacks.spec.tsx b/web/app/components/app-sidebar/dataset-info/__tests__/dropdown-callbacks.spec.tsx index b514b6e095..ceb8302ee6 100644 --- a/web/app/components/app-sidebar/dataset-info/__tests__/dropdown-callbacks.spec.tsx +++ b/web/app/components/app-sidebar/dataset-info/__tests__/dropdown-callbacks.spec.tsx @@ -112,7 +112,7 @@ vi.mock('@/service/datasets', () => ({ deleteDataset: (...args: unknown[]) => mockDeleteDataset(...args), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: (...args: unknown[]) => mockToast(...args), })) diff --git a/web/app/components/app-sidebar/dataset-info/dropdown.tsx b/web/app/components/app-sidebar/dataset-info/dropdown.tsx index e69b3d7e32..8f3a25738a 100644 --- a/web/app/components/app-sidebar/dataset-info/dropdown.tsx +++ b/web/app/components/app-sidebar/dataset-info/dropdown.tsx @@ -1,9 +1,23 @@ import type { DataSet } from '@/models/datasets' +import { + AlertDialog, + AlertDialogActions, + AlertDialogCancelButton, + AlertDialogConfirmButton, + AlertDialogContent, + AlertDialogDescription, + AlertDialogTitle, +} from '@langgenius/dify-ui/alert-dialog' import { cn } from '@langgenius/dify-ui/cn' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, +} from '@langgenius/dify-ui/dropdown-menu' +import { toast } from '@langgenius/dify-ui/toast' import * as React from 'react' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import { toast } from '@/app/components/base/ui/toast' import { useSelector as useAppContextWithSelector } from '@/context/app-context' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import { useRouter } from '@/next/navigation' @@ -13,20 +27,6 @@ import { useInvalid } from '@/service/use-base' import { useExportPipelineDSL } from '@/service/use-pipeline' import { downloadBlob } from '@/utils/download' import ActionButton from '../../base/action-button' -import { - AlertDialog, - AlertDialogActions, - AlertDialogCancelButton, - AlertDialogConfirmButton, - AlertDialogContent, - AlertDialogDescription, - AlertDialogTitle, -} from '../../base/ui/alert-dialog' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuTrigger, -} from '../../base/ui/dropdown-menu' import RenameDatasetModal from '../../datasets/rename-modal' import Menu from './menu' diff --git a/web/app/components/app-sidebar/dataset-sidebar-dropdown.tsx b/web/app/components/app-sidebar/dataset-sidebar-dropdown.tsx index 3968a0df6f..805636fb55 100644 --- a/web/app/components/app-sidebar/dataset-sidebar-dropdown.tsx +++ b/web/app/components/app-sidebar/dataset-sidebar-dropdown.tsx @@ -1,17 +1,17 @@ import type { NavIcon } from './nav-link' import type { DataSet } from '@/models/datasets' import { cn } from '@langgenius/dify-ui/cn' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, +} from '@langgenius/dify-ui/dropdown-menu' import { RiMenuLine, } from '@remixicon/react' import * as React from 'react' import { useState } from 'react' import { useTranslation } from 'react-i18next' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import { useKnowledge } from '@/hooks/use-knowledge' import { DOC_FORM_TEXT } from '@/models/datasets' diff --git a/web/app/components/app-sidebar/toggle-button.tsx b/web/app/components/app-sidebar/toggle-button.tsx index 5800ff2263..6aca77fb4f 100644 --- a/web/app/components/app-sidebar/toggle-button.tsx +++ b/web/app/components/app-sidebar/toggle-button.tsx @@ -1,8 +1,8 @@ +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiArrowLeftSLine, RiArrowRightSLine } from '@remixicon/react' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import Tooltip from '../base/tooltip' import ShortcutsName from '../workflow/shortcuts-name' diff --git a/web/app/components/app/annotation/__tests__/index.spec.tsx b/web/app/components/app/annotation/__tests__/index.spec.tsx index cd9b127c7f..2bd94d03b0 100644 --- a/web/app/components/app/annotation/__tests__/index.spec.tsx +++ b/web/app/components/app/annotation/__tests__/index.spec.tsx @@ -2,9 +2,9 @@ import type { Mock } from 'vitest' import type { AnnotationItem } from '../type' import type { App } from '@/types/app' +import { toast } from '@langgenius/dify-ui/toast' import { act, fireEvent, render, screen, waitFor } from '@testing-library/react' import * as React from 'react' -import { toast } from '@/app/components/base/ui/toast' import { useProviderContext } from '@/context/provider-context' import { addAnnotation, diff --git a/web/app/components/app/annotation/add-annotation-modal/__tests__/index.spec.tsx b/web/app/components/app/annotation/add-annotation-modal/__tests__/index.spec.tsx index d7e46f9f92..497c623f3e 100644 --- a/web/app/components/app/annotation/add-annotation-modal/__tests__/index.spec.tsx +++ b/web/app/components/app/annotation/add-annotation-modal/__tests__/index.spec.tsx @@ -9,7 +9,7 @@ vi.mock('@/context/provider-context', () => ({ })) const mockToastNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ default: { notify: vi.fn(args => mockToastNotify(args)), }, diff --git a/web/app/components/app/annotation/add-annotation-modal/index.tsx b/web/app/components/app/annotation/add-annotation-modal/index.tsx index dbdf909c0a..ba987a8a8a 100644 --- a/web/app/components/app/annotation/add-annotation-modal/index.tsx +++ b/web/app/components/app/annotation/add-annotation-modal/index.tsx @@ -1,13 +1,13 @@ 'use client' import type { FC } from 'react' import type { AnnotationItemBasic } from '../type' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import * as React from 'react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import Checkbox from '@/app/components/base/checkbox' import Drawer from '@/app/components/base/drawer-plus' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import AnnotationFull from '@/app/components/billing/annotation-full' import { useProviderContext } from '@/context/provider-context' import EditItem, { EditItemType } from './edit-item' diff --git a/web/app/components/app/annotation/batch-action.tsx b/web/app/components/app/annotation/batch-action.tsx index da7d0c70dd..938dcb03bd 100644 --- a/web/app/components/app/annotation/batch-action.tsx +++ b/web/app/components/app/annotation/batch-action.tsx @@ -1,10 +1,4 @@ import type { FC } from 'react' -import { cn } from '@langgenius/dify-ui/cn' -import { RiDeleteBinLine } from '@remixicon/react' -import { useBoolean } from 'ahooks' -import * as React from 'react' -import { useTranslation } from 'react-i18next' -import Divider from '@/app/components/base/divider' import { AlertDialog, AlertDialogActions, @@ -12,7 +6,13 @@ import { AlertDialogConfirmButton, AlertDialogContent, AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' +} from '@langgenius/dify-ui/alert-dialog' +import { cn } from '@langgenius/dify-ui/cn' +import { RiDeleteBinLine } from '@remixicon/react' +import { useBoolean } from 'ahooks' +import * as React from 'react' +import { useTranslation } from 'react-i18next' +import Divider from '@/app/components/base/divider' const i18nPrefix = 'batchAction' diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/__tests__/csv-uploader.spec.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/__tests__/csv-uploader.spec.tsx index d26ab051ef..5fc1cd25e1 100644 --- a/web/app/components/app/annotation/batch-add-annotation-modal/__tests__/csv-uploader.spec.tsx +++ b/web/app/components/app/annotation/batch-add-annotation-modal/__tests__/csv-uploader.spec.tsx @@ -10,7 +10,7 @@ const toastMocks = vi.hoisted(() => ({ promise: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: (message: string, options?: Record) => toastMocks.notify({ type: 'success', message, ...options }), error: (message: string, options?: Record) => toastMocks.notify({ type: 'error', message, ...options }), diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/__tests__/index.spec.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/__tests__/index.spec.tsx index 3176b1addb..c5d7232e12 100644 --- a/web/app/components/app/annotation/batch-add-annotation-modal/__tests__/index.spec.tsx +++ b/web/app/components/app/annotation/batch-add-annotation-modal/__tests__/index.spec.tsx @@ -43,7 +43,7 @@ vi.mock('@/app/components/billing/annotation-full', () => ({ })) const mockNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ default: { notify: (args: unknown) => mockNotify(args), }, diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx index 02dcf27aea..dc63b5c9be 100644 --- a/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx +++ b/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx @@ -1,13 +1,13 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiDeleteBinLine } from '@remixicon/react' import * as React from 'react' import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { Csv as CSVIcon } from '@/app/components/base/icons/src/public/files' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' export type Props = { file: File | undefined diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/index.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/index.tsx index 20a35714e8..550794cce7 100644 --- a/web/app/components/app/annotation/batch-add-annotation-modal/index.tsx +++ b/web/app/components/app/annotation/batch-add-annotation-modal/index.tsx @@ -1,13 +1,13 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiCloseLine } from '@remixicon/react' import { noop } from 'es-toolkit/function' import * as React from 'react' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import AnnotationFull from '@/app/components/billing/annotation-full' import { useProviderContext } from '@/context/provider-context' import { annotationBatchImport, checkAnnotationBatchImportProgress } from '@/service/annotation' diff --git a/web/app/components/app/annotation/clear-all-annotations-confirm-modal/index.tsx b/web/app/components/app/annotation/clear-all-annotations-confirm-modal/index.tsx index b960e1d59c..6b0ef19e7b 100644 --- a/web/app/components/app/annotation/clear-all-annotations-confirm-modal/index.tsx +++ b/web/app/components/app/annotation/clear-all-annotations-confirm-modal/index.tsx @@ -1,8 +1,6 @@ 'use client' import type { FC } from 'react' -import * as React from 'react' -import { useTranslation } from 'react-i18next' import { AlertDialog, AlertDialogActions, @@ -10,7 +8,9 @@ import { AlertDialogConfirmButton, AlertDialogContent, AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' +} from '@langgenius/dify-ui/alert-dialog' +import * as React from 'react' +import { useTranslation } from 'react-i18next' type Props = { isShow: boolean diff --git a/web/app/components/app/annotation/edit-annotation-modal/__tests__/index.spec.tsx b/web/app/components/app/annotation/edit-annotation-modal/__tests__/index.spec.tsx index 4844017dde..733529dff8 100644 --- a/web/app/components/app/annotation/edit-annotation-modal/__tests__/index.spec.tsx +++ b/web/app/components/app/annotation/edit-annotation-modal/__tests__/index.spec.tsx @@ -1,6 +1,6 @@ +import { toast } from '@langgenius/dify-ui/toast' import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { toast } from '@/app/components/base/ui/toast' import EditAnnotationModal from '../index' const { mockAddAnnotation, mockEditAnnotation } = vi.hoisted(() => ({ diff --git a/web/app/components/app/annotation/edit-annotation-modal/edit-item/index.tsx b/web/app/components/app/annotation/edit-annotation-modal/edit-item/index.tsx index 4a62569816..609d7847bc 100644 --- a/web/app/components/app/annotation/edit-annotation-modal/edit-item/index.tsx +++ b/web/app/components/app/annotation/edit-annotation-modal/edit-item/index.tsx @@ -1,5 +1,6 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiDeleteBinLine, RiEditFill, RiEditLine } from '@remixicon/react' import * as React from 'react' @@ -7,7 +8,6 @@ import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { Robot, User } from '@/app/components/base/icons/src/public/avatar' import Textarea from '@/app/components/base/textarea' -import { Button } from '@/app/components/base/ui/button' export enum EditItemType { Query = 'query', diff --git a/web/app/components/app/annotation/edit-annotation-modal/index.tsx b/web/app/components/app/annotation/edit-annotation-modal/index.tsx index c3d5b140eb..8e690eca9b 100644 --- a/web/app/components/app/annotation/edit-annotation-modal/index.tsx +++ b/web/app/components/app/annotation/edit-annotation-modal/index.tsx @@ -1,10 +1,5 @@ 'use client' import type { FC } from 'react' -import * as React from 'react' -import { useState } from 'react' -import { useTranslation } from 'react-i18next' -import Drawer from '@/app/components/base/drawer-plus' -import { MessageCheckRemove } from '@/app/components/base/icons/src/vender/line/communication' import { AlertDialog, AlertDialogActions, @@ -12,8 +7,13 @@ import { AlertDialogConfirmButton, AlertDialogContent, AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' -import { toast } from '@/app/components/base/ui/toast' +} from '@langgenius/dify-ui/alert-dialog' +import { toast } from '@langgenius/dify-ui/toast' +import * as React from 'react' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import Drawer from '@/app/components/base/drawer-plus' +import { MessageCheckRemove } from '@/app/components/base/icons/src/vender/line/communication' import AnnotationFull from '@/app/components/billing/annotation-full' import { useProviderContext } from '@/context/provider-context' import useTimestamp from '@/hooks/use-timestamp' diff --git a/web/app/components/app/annotation/header-opts/index.tsx b/web/app/components/app/annotation/header-opts/index.tsx index 0871a722f8..fc27524c71 100644 --- a/web/app/components/app/annotation/header-opts/index.tsx +++ b/web/app/components/app/annotation/header-opts/index.tsx @@ -2,19 +2,19 @@ import type { FC } from 'react' import type { AnnotationItemBasic } from '../type' import { Menu, MenuButton, MenuItems, Transition } from '@headlessui/react' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, +} from '@langgenius/dify-ui/dropdown-menu' import * as React from 'react' import { Fragment, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useCSVDownloader, } from 'react-papaparse' -import { Button } from '@/app/components/base/ui/button' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' import { useLocale } from '@/context/i18n' import { LanguagesSupported } from '@/i18n-config/language' diff --git a/web/app/components/app/annotation/index.tsx b/web/app/components/app/annotation/index.tsx index 419aaba25d..cbad5b40ea 100644 --- a/web/app/components/app/annotation/index.tsx +++ b/web/app/components/app/annotation/index.tsx @@ -5,6 +5,8 @@ import type { AnnotationItem, AnnotationItemBasic } from './type' import type { AnnotationReplyConfig } from '@/models/debug' import type { App } from '@/types/app' import { cn } from '@langgenius/dify-ui/cn' +import { Switch } from '@langgenius/dify-ui/switch' +import { toast } from '@langgenius/dify-ui/toast' import { RiEqualizer2Line } from '@remixicon/react' import { useDebounce } from 'ahooks' import * as React from 'react' @@ -15,8 +17,6 @@ import ConfigParamModal from '@/app/components/base/features/new-feature-panel/a import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication' import Loading from '@/app/components/base/loading' import Pagination from '@/app/components/base/pagination' -import Switch from '@/app/components/base/switch' -import { toast } from '@/app/components/base/ui/toast' import AnnotationFullModal from '@/app/components/billing/annotation-full/modal' import { APP_PAGE_LIMIT } from '@/config' import { useProviderContext } from '@/context/provider-context' diff --git a/web/app/components/app/annotation/remove-annotation-confirm-modal/index.tsx b/web/app/components/app/annotation/remove-annotation-confirm-modal/index.tsx index 70e28b048c..864e098ca5 100644 --- a/web/app/components/app/annotation/remove-annotation-confirm-modal/index.tsx +++ b/web/app/components/app/annotation/remove-annotation-confirm-modal/index.tsx @@ -1,7 +1,5 @@ 'use client' import type { FC } from 'react' -import * as React from 'react' -import { useTranslation } from 'react-i18next' import { AlertDialog, AlertDialogActions, @@ -9,7 +7,9 @@ import { AlertDialogConfirmButton, AlertDialogContent, AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' +} from '@langgenius/dify-ui/alert-dialog' +import * as React from 'react' +import { useTranslation } from 'react-i18next' type Props = { isShow: boolean diff --git a/web/app/components/app/annotation/view-annotation-modal/index.tsx b/web/app/components/app/annotation/view-annotation-modal/index.tsx index 8a5b53aa87..c9f7e8a78f 100644 --- a/web/app/components/app/annotation/view-annotation-modal/index.tsx +++ b/web/app/components/app/annotation/view-annotation-modal/index.tsx @@ -1,6 +1,14 @@ 'use client' import type { FC } from 'react' import type { AnnotationItem, HitHistoryItem } from '../type' +import { + AlertDialog, + AlertDialogActions, + AlertDialogCancelButton, + AlertDialogConfirmButton, + AlertDialogContent, + AlertDialogTitle, +} from '@langgenius/dify-ui/alert-dialog' import { cn } from '@langgenius/dify-ui/cn' import * as React from 'react' import { useEffect, useState } from 'react' @@ -10,14 +18,6 @@ import Drawer from '@/app/components/base/drawer-plus' import { MessageCheckRemove } from '@/app/components/base/icons/src/vender/line/communication' import Pagination from '@/app/components/base/pagination' import TabSlider from '@/app/components/base/tab-slider-plain' -import { - AlertDialog, - AlertDialogActions, - AlertDialogCancelButton, - AlertDialogConfirmButton, - AlertDialogContent, - AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' import { APP_PAGE_LIMIT } from '@/config' import useTimestamp from '@/hooks/use-timestamp' import { fetchHitHistoryList } from '@/service/annotation' diff --git a/web/app/components/app/app-access-control/__tests__/access-control.spec.tsx b/web/app/components/app/app-access-control/__tests__/access-control.spec.tsx index 86ed6ac435..bbac21942e 100644 --- a/web/app/components/app/app-access-control/__tests__/access-control.spec.tsx +++ b/web/app/components/app/app-access-control/__tests__/access-control.spec.tsx @@ -1,9 +1,9 @@ /* eslint-disable ts/no-explicit-any */ import type { AccessControlAccount, AccessControlGroup, Subject } from '@/models/access-control' import type { App } from '@/types/app' +import { toast } from '@langgenius/dify-ui/toast' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { toast } from '@/app/components/base/ui/toast' import useAccessControlStore from '@/context/access-control-store' import { AccessMode, SubjectType } from '@/models/access-control' import AccessControlDialog from '../access-control-dialog' diff --git a/web/app/components/app/app-access-control/__tests__/index.spec.tsx b/web/app/components/app/app-access-control/__tests__/index.spec.tsx index 612d17d88a..05b86f0290 100644 --- a/web/app/components/app/app-access-control/__tests__/index.spec.tsx +++ b/web/app/components/app/app-access-control/__tests__/index.spec.tsx @@ -1,6 +1,6 @@ import type { App } from '@/types/app' +import { toast } from '@langgenius/dify-ui/toast' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import { toast } from '@/app/components/base/ui/toast' import useAccessControlStore from '@/context/access-control-store' import { AccessMode } from '@/models/access-control' import AccessControl from '../index' diff --git a/web/app/components/app/app-access-control/add-member-or-group-pop.tsx b/web/app/components/app/app-access-control/add-member-or-group-pop.tsx index 8c8087cf17..38f9c2ab50 100644 --- a/web/app/components/app/app-access-control/add-member-or-group-pop.tsx +++ b/web/app/components/app/app-access-control/add-member-or-group-pop.tsx @@ -1,14 +1,14 @@ 'use client' import type { AccessControlAccount, AccessControlGroup, Subject, SubjectAccount, SubjectGroup } from '@/models/access-control' import { FloatingOverlay } from '@floating-ui/react' +import { Avatar } from '@langgenius/dify-ui/avatar' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover' import { RiAddCircleFill, RiArrowRightSLine, RiOrganizationChart } from '@remixicon/react' import { useDebounce } from 'ahooks' import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { Avatar } from '@/app/components/base/ui/avatar' -import { Button } from '@/app/components/base/ui/button' -import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover' import { useSelector } from '@/context/app-context' import { SubjectType } from '@/models/access-control' import { useSearchForWhiteListCandidates } from '@/service/access-control' diff --git a/web/app/components/app/app-access-control/index.tsx b/web/app/components/app/app-access-control/index.tsx index aeaae3d46d..2997d4b4cf 100644 --- a/web/app/components/app/app-access-control/index.tsx +++ b/web/app/components/app/app-access-control/index.tsx @@ -2,11 +2,11 @@ import type { Subject } from '@/models/access-control' import type { App } from '@/types/app' import { Description as DialogDescription, DialogTitle } from '@headlessui/react' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiBuildingLine, RiGlobalLine, RiVerifiedBadgeLine } from '@remixicon/react' import { useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { useGlobalPublicStore } from '@/context/global-public-context' import { AccessMode, SubjectType } from '@/models/access-control' import { useUpdateAccessMode } from '@/service/access-control' diff --git a/web/app/components/app/app-access-control/specific-groups-or-members.tsx b/web/app/components/app/app-access-control/specific-groups-or-members.tsx index 5b5c8342fc..1caabb3ff9 100644 --- a/web/app/components/app/app-access-control/specific-groups-or-members.tsx +++ b/web/app/components/app/app-access-control/specific-groups-or-members.tsx @@ -1,9 +1,9 @@ 'use client' import type { AccessControlAccount, AccessControlGroup } from '@/models/access-control' +import { Avatar } from '@langgenius/dify-ui/avatar' import { RiAlertFill, RiCloseCircleFill, RiLockLine, RiOrganizationChart } from '@remixicon/react' import { useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' -import { Avatar } from '@/app/components/base/ui/avatar' import { AccessMode } from '@/models/access-control' import { useAppWhiteListSubjects } from '@/service/access-control' import useAccessControlStore from '../../../../context/access-control-store' diff --git a/web/app/components/app/app-publisher/__tests__/index.spec.tsx b/web/app/components/app/app-publisher/__tests__/index.spec.tsx index bba2f53afc..06d91e9400 100644 --- a/web/app/components/app/app-publisher/__tests__/index.spec.tsx +++ b/web/app/components/app/app-publisher/__tests__/index.spec.tsx @@ -93,7 +93,7 @@ vi.mock('@/service/use-workflow', () => ({ useInvalidateAppWorkflow: () => mockInvalidateAppWorkflow, })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: (...args: unknown[]) => mockToastError(...args), }, diff --git a/web/app/components/app/app-publisher/__tests__/publish-with-multiple-model.spec.tsx b/web/app/components/app/app-publisher/__tests__/publish-with-multiple-model.spec.tsx index 465252c6c4..16628d742a 100644 --- a/web/app/components/app/app-publisher/__tests__/publish-with-multiple-model.spec.tsx +++ b/web/app/components/app/app-publisher/__tests__/publish-with-multiple-model.spec.tsx @@ -22,7 +22,7 @@ vi.mock('../../header/account-setting/model-provider-page/model-icon', () => ({ default: ({ modelName }: { modelName: string }) => {modelName}, })) -vi.mock('@/app/components/base/ui/dropdown-menu', async () => { +vi.mock('@langgenius/dify-ui/dropdown-menu', async () => { const ReactModule = await vi.importActual('react') const OpenContext = ReactModule.createContext<{ open: boolean, setOpen: (nextOpen: boolean) => void } | null>(null) diff --git a/web/app/components/app/app-publisher/__tests__/version-info-modal.spec.tsx b/web/app/components/app/app-publisher/__tests__/version-info-modal.spec.tsx index ed86bb8571..4a461bd942 100644 --- a/web/app/components/app/app-publisher/__tests__/version-info-modal.spec.tsx +++ b/web/app/components/app/app-publisher/__tests__/version-info-modal.spec.tsx @@ -1,6 +1,6 @@ /* eslint-disable ts/no-explicit-any */ +import { toast } from '@langgenius/dify-ui/toast' import { fireEvent, render, screen } from '@testing-library/react' -import { toast } from '@/app/components/base/ui/toast' import VersionInfoModal from '../version-info-modal' vi.mock('react-i18next', () => ({ @@ -9,7 +9,7 @@ vi.mock('react-i18next', () => ({ }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: vi.fn(), }, diff --git a/web/app/components/app/app-publisher/features-wrapper.tsx b/web/app/components/app/app-publisher/features-wrapper.tsx index 7c39535701..8679b2830d 100644 --- a/web/app/components/app/app-publisher/features-wrapper.tsx +++ b/web/app/components/app/app-publisher/features-wrapper.tsx @@ -2,13 +2,6 @@ import type { AppPublisherProps } from '@/app/components/app/app-publisher' import type { ModelAndParameter } from '@/app/components/app/configuration/debug/types' import type { FileUpload } from '@/app/components/base/features/types' import type { PublishWorkflowParams } from '@/types/workflow' -import { produce } from 'immer' -import * as React from 'react' -import { useCallback, useState } from 'react' -import { useTranslation } from 'react-i18next' -import AppPublisher from '@/app/components/app/app-publisher' -import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' -import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' import { AlertDialog, AlertDialogActions, @@ -17,7 +10,14 @@ import { AlertDialogContent, AlertDialogDescription, AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' +} from '@langgenius/dify-ui/alert-dialog' +import { produce } from 'immer' +import * as React from 'react' +import { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import AppPublisher from '@/app/components/app/app-publisher' +import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' +import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' import { SupportUploadFileTypes } from '@/app/components/workflow/types' import { Resolution } from '@/types/app' diff --git a/web/app/components/app/app-publisher/index.tsx b/web/app/components/app/app-publisher/index.tsx index ce3698dbb7..562f2d7759 100644 --- a/web/app/components/app/app-publisher/index.tsx +++ b/web/app/components/app/app-publisher/index.tsx @@ -2,6 +2,9 @@ import type { ModelAndParameter } from '../configuration/debug/types' import type { CollaborationUpdate } from '@/app/components/workflow/collaboration/types/collaboration' import type { InputVar, Variable } from '@/app/components/workflow/types' import type { PublishWorkflowParams } from '@/types/workflow' +import { Button } from '@langgenius/dify-ui/button' +import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover' +import { toast } from '@langgenius/dify-ui/toast' import { useKeyPress } from 'ahooks' import { memo, @@ -15,8 +18,6 @@ import { useTranslation } from 'react-i18next' import EmbeddedModal from '@/app/components/app/overview/embedded' import { useStore as useAppStore } from '@/app/components/app/store' import { trackEvent } from '@/app/components/base/amplitude' -import { Button } from '@/app/components/base/ui/button' -import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover' import { collaborationManager } from '@/app/components/workflow/collaboration/core/collaboration-manager' import { webSocketClient } from '@/app/components/workflow/collaboration/core/websocket-manager' import { WorkflowContext } from '@/app/components/workflow/context' @@ -31,7 +32,6 @@ import { useInvalidateAppWorkflow } from '@/service/use-workflow' import { fetchPublishedWorkflow } from '@/service/workflow' import { AppModeEnum } from '@/types/app' import { basePath } from '@/utils/var' -import { toast } from '../../base/ui/toast' import { getKeyboardKeyCodeBySystem } from '../../workflow/utils' import AccessControl from '../app-access-control' import { diff --git a/web/app/components/app/app-publisher/publish-with-multiple-model.tsx b/web/app/components/app/app-publisher/publish-with-multiple-model.tsx index fbff371577..038b9328bb 100644 --- a/web/app/components/app/app-publisher/publish-with-multiple-model.tsx +++ b/web/app/components/app/app-publisher/publish-with-multiple-model.tsx @@ -1,16 +1,16 @@ import type { FC } from 'react' import type { ModelAndParameter } from '../configuration/debug/types' import type { Model, ModelItem } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { RiArrowDownSLine } from '@remixicon/react' -import { useState } from 'react' -import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' +import { Button } from '@langgenius/dify-ui/button' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' +} from '@langgenius/dify-ui/dropdown-menu' +import { RiArrowDownSLine } from '@remixicon/react' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import { useProviderContext } from '@/context/provider-context' import ModelIcon from '../../header/account-setting/model-provider-page/model-icon' diff --git a/web/app/components/app/app-publisher/sections.tsx b/web/app/components/app/app-publisher/sections.tsx index d4864a3763..57522095ae 100644 --- a/web/app/components/app/app-publisher/sections.tsx +++ b/web/app/components/app/app-publisher/sections.tsx @@ -2,16 +2,16 @@ import type { CSSProperties, ReactNode } from 'react' import type { ModelAndParameter } from '../configuration/debug/types' import type { AppPublisherProps } from './index' import type { PublishWorkflowParams } from '@/types/workflow' -import { useTranslation } from 'react-i18next' -import Divider from '@/app/components/base/divider' -import { CodeBrowser } from '@/app/components/base/icons/src/vender/line/development' -import Loading from '@/app/components/base/loading' -import { Button } from '@/app/components/base/ui/button' +import { Button } from '@langgenius/dify-ui/button' import { Tooltip, TooltipContent, TooltipTrigger, -} from '@/app/components/base/ui/tooltip' +} from '@langgenius/dify-ui/tooltip' +import { useTranslation } from 'react-i18next' +import Divider from '@/app/components/base/divider' +import { CodeBrowser } from '@/app/components/base/icons/src/vender/line/development' +import Loading from '@/app/components/base/loading' import UpgradeBtn from '@/app/components/billing/upgrade-btn' import WorkflowToolConfigureButton from '@/app/components/tools/workflow-tool/configure-button' import { appDefaultIconBackground } from '@/config' diff --git a/web/app/components/app/app-publisher/version-info-modal.tsx b/web/app/components/app/app-publisher/version-info-modal.tsx index d5f4c9c189..3fe4c61fb7 100644 --- a/web/app/components/app/app-publisher/version-info-modal.tsx +++ b/web/app/components/app/app-publisher/version-info-modal.tsx @@ -1,12 +1,12 @@ import type { FC } from 'react' import type { VersionHistory } from '@/types/workflow' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiCloseLine } from '@remixicon/react' import * as React from 'react' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import Input from '../../base/input' import Textarea from '../../base/textarea' diff --git a/web/app/components/app/configuration/base/warning-mask/cannot-query-dataset.tsx b/web/app/components/app/configuration/base/warning-mask/cannot-query-dataset.tsx index 029743dfb6..51b365f446 100644 --- a/web/app/components/app/configuration/base/warning-mask/cannot-query-dataset.tsx +++ b/web/app/components/app/configuration/base/warning-mask/cannot-query-dataset.tsx @@ -1,8 +1,8 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import WarningMask from '.' type IFormattingChangedProps = { diff --git a/web/app/components/app/configuration/base/warning-mask/formatting-changed.tsx b/web/app/components/app/configuration/base/warning-mask/formatting-changed.tsx index 4b25b023b2..07467a5ab1 100644 --- a/web/app/components/app/configuration/base/warning-mask/formatting-changed.tsx +++ b/web/app/components/app/configuration/base/warning-mask/formatting-changed.tsx @@ -1,8 +1,8 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import WarningMask from '.' type IFormattingChangedProps = { diff --git a/web/app/components/app/configuration/config-prompt/__tests__/advanced-prompt-input.spec.tsx b/web/app/components/app/configuration/config-prompt/__tests__/advanced-prompt-input.spec.tsx index f7cfff94fb..ced1ddcfe3 100644 --- a/web/app/components/app/configuration/config-prompt/__tests__/advanced-prompt-input.spec.tsx +++ b/web/app/components/app/configuration/config-prompt/__tests__/advanced-prompt-input.spec.tsx @@ -59,7 +59,7 @@ vi.mock('@/context/modal-context', () => ({ }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: (...args: unknown[]) => mockToastError(...args), }, diff --git a/web/app/components/app/configuration/config-prompt/__tests__/simple-prompt-input.spec.tsx b/web/app/components/app/configuration/config-prompt/__tests__/simple-prompt-input.spec.tsx index 0f8b9b032f..b4e6e0be2f 100644 --- a/web/app/components/app/configuration/config-prompt/__tests__/simple-prompt-input.spec.tsx +++ b/web/app/components/app/configuration/config-prompt/__tests__/simple-prompt-input.spec.tsx @@ -57,7 +57,7 @@ vi.mock('@/context/modal-context', () => ({ }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: (...args: unknown[]) => mockToastError(...args), }, diff --git a/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx b/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx index 1de6e6ce0c..5fd394ad45 100644 --- a/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx +++ b/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx @@ -2,7 +2,14 @@ import type { FC } from 'react' import type { ExternalDataTool } from '@/models/common' import type { PromptRole, PromptVariable } from '@/models/debug' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@langgenius/dify-ui/tooltip' import { RiDeleteBinLine, RiErrorWarningFill, @@ -20,13 +27,6 @@ import { } from '@/app/components/base/icons/src/vender/line/files' import PromptEditor from '@/app/components/base/prompt-editor' import { INSERT_VARIABLE_VALUE_BLOCK_COMMAND } from '@/app/components/base/prompt-editor/plugins/variable-block' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' -import { - Tooltip, - TooltipContent, - TooltipTrigger, -} from '@/app/components/base/ui/tooltip' import ConfigContext from '@/context/debug-configuration' import { useEventEmitterContextContext } from '@/context/event-emitter' import { useModalContext } from '@/context/modal-context' diff --git a/web/app/components/app/configuration/config-prompt/confirm-add-var/index.tsx b/web/app/components/app/configuration/config-prompt/confirm-add-var/index.tsx index 03755df018..8846530af2 100644 --- a/web/app/components/app/configuration/config-prompt/confirm-add-var/index.tsx +++ b/web/app/components/app/configuration/config-prompt/confirm-add-var/index.tsx @@ -1,9 +1,9 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useRef } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import VarHighlight from '../../base/var-highlight' type IConfirmAddVarProps = { diff --git a/web/app/components/app/configuration/config-prompt/conversation-history/edit-modal.tsx b/web/app/components/app/configuration/config-prompt/conversation-history/edit-modal.tsx index 477e1db4a0..7906a5f8da 100644 --- a/web/app/components/app/configuration/config-prompt/conversation-history/edit-modal.tsx +++ b/web/app/components/app/configuration/config-prompt/conversation-history/edit-modal.tsx @@ -1,11 +1,11 @@ 'use client' import type { FC } from 'react' import type { ConversationHistoriesRole } from '@/models/debug' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' type Props = { isShow: boolean diff --git a/web/app/components/app/configuration/config-prompt/index.tsx b/web/app/components/app/configuration/config-prompt/index.tsx index 8a77d4bb58..20463da174 100644 --- a/web/app/components/app/configuration/config-prompt/index.tsx +++ b/web/app/components/app/configuration/config-prompt/index.tsx @@ -2,6 +2,7 @@ import type { FC } from 'react' import type { PromptItem, PromptVariable } from '@/models/debug' import type { AppModeEnum } from '@/types/app' +import { Button } from '@langgenius/dify-ui/button' import { RiAddLine, } from '@remixicon/react' @@ -10,7 +11,6 @@ import * as React from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import AdvancedMessageInput from '@/app/components/app/configuration/config-prompt/advanced-prompt-input' -import { Button } from '@/app/components/base/ui/button' import { MAX_PROMPT_MESSAGE_LENGTH } from '@/config' import ConfigContext from '@/context/debug-configuration' import { PromptRole } from '@/models/debug' diff --git a/web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx b/web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx index 6b5c3acccb..2935504f15 100644 --- a/web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx +++ b/web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx @@ -4,6 +4,12 @@ import type { ExternalDataTool } from '@/models/common' import type { PromptVariable } from '@/models/debug' import type { GenRes } from '@/service/debug' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@langgenius/dify-ui/tooltip' import { useBoolean } from 'ahooks' import { noop } from 'es-toolkit/function' import { produce } from 'immer' @@ -18,12 +24,6 @@ import { useFeaturesStore } from '@/app/components/base/features/hooks' import PromptEditor from '@/app/components/base/prompt-editor' import { PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER } from '@/app/components/base/prompt-editor/plugins/update-block' import { INSERT_VARIABLE_VALUE_BLOCK_COMMAND } from '@/app/components/base/prompt-editor/plugins/variable-block' -import { toast } from '@/app/components/base/ui/toast' -import { - Tooltip, - TooltipContent, - TooltipTrigger, -} from '@/app/components/base/ui/tooltip' import ConfigContext from '@/context/debug-configuration' import { useEventEmitterContextContext } from '@/context/event-emitter' import { useModalContext } from '@/context/modal-context' diff --git a/web/app/components/app/configuration/config-var/__tests__/index.spec.tsx b/web/app/components/app/configuration/config-var/__tests__/index.spec.tsx index 4486489a20..51683ad948 100644 --- a/web/app/components/app/configuration/config-var/__tests__/index.spec.tsx +++ b/web/app/components/app/configuration/config-var/__tests__/index.spec.tsx @@ -2,10 +2,10 @@ import type { ReactNode } from 'react' import type { IConfigVarProps } from '../index' import type { ExternalDataTool } from '@/models/common' import type { PromptVariable } from '@/models/debug' +import { toast } from '@langgenius/dify-ui/toast' import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react' import * as React from 'react' import { vi } from 'vitest' -import { toast } from '@/app/components/base/ui/toast' import DebugConfigurationContext from '@/context/debug-configuration' import { AppModeEnum } from '@/types/app' diff --git a/web/app/components/app/configuration/config-var/config-modal/__tests__/form-fields.spec.tsx b/web/app/components/app/configuration/config-var/config-modal/__tests__/form-fields.spec.tsx index 8cc8775bf5..5f0f4129d6 100644 --- a/web/app/components/app/configuration/config-var/config-modal/__tests__/form-fields.spec.tsx +++ b/web/app/components/app/configuration/config-var/config-modal/__tests__/form-fields.spec.tsx @@ -44,8 +44,8 @@ vi.mock('@/app/components/base/select', () => ({ ), })) -vi.mock('@/app/components/base/ui/select', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/select', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, diff --git a/web/app/components/app/configuration/config-var/config-modal/__tests__/index-logic.spec.tsx b/web/app/components/app/configuration/config-var/config-modal/__tests__/index-logic.spec.tsx index 4888d284d2..77f2b54533 100644 --- a/web/app/components/app/configuration/config-var/config-modal/__tests__/index-logic.spec.tsx +++ b/web/app/components/app/configuration/config-var/config-modal/__tests__/index-logic.spec.tsx @@ -1,10 +1,10 @@ /* eslint-disable ts/no-explicit-any */ import type { InputVar } from '@/app/components/workflow/types' import type { App, AppSSO } from '@/types/app' +import { toast } from '@langgenius/dify-ui/toast' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import * as React from 'react' import { useStore } from '@/app/components/app/store' -import { toast } from '@/app/components/base/ui/toast' import { InputVarType } from '@/app/components/workflow/types' import DebugConfigurationContext from '@/context/debug-configuration' import { AppModeEnum } from '@/types/app' diff --git a/web/app/components/app/configuration/config-var/config-modal/__tests__/index.spec.tsx b/web/app/components/app/configuration/config-var/config-modal/__tests__/index.spec.tsx index d15d6527c5..094a293943 100644 --- a/web/app/components/app/configuration/config-var/config-modal/__tests__/index.spec.tsx +++ b/web/app/components/app/configuration/config-var/config-modal/__tests__/index.spec.tsx @@ -1,9 +1,9 @@ import type { InputVar } from '@/app/components/workflow/types' import type { App, AppSSO } from '@/types/app' +import { toast } from '@langgenius/dify-ui/toast' import { fireEvent, render, screen } from '@testing-library/react' import * as React from 'react' import { useStore } from '@/app/components/app/store' -import { toast } from '@/app/components/base/ui/toast' import { InputVarType } from '@/app/components/workflow/types' import { AppModeEnum } from '@/types/app' import ConfigModal from '../index' diff --git a/web/app/components/app/configuration/config-var/config-modal/form-fields.tsx b/web/app/components/app/configuration/config-var/config-modal/form-fields.tsx index 279a9279cf..748108e19a 100644 --- a/web/app/components/app/configuration/config-var/config-modal/form-fields.tsx +++ b/web/app/components/app/configuration/config-var/config-modal/form-fields.tsx @@ -3,11 +3,6 @@ import type { ChangeEvent, FC } from 'react' import type { Item as SelectOptionItem } from './type-select' import type { FileEntity } from '@/app/components/base/file-uploader/types' import type { InputVar, UploadFileSetting } from '@/app/components/workflow/types' -import * as React from 'react' -import Checkbox from '@/app/components/base/checkbox' -import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader' -import Input from '@/app/components/base/input' -import Textarea from '@/app/components/base/textarea' import { Select, SelectContent, @@ -16,7 +11,12 @@ import { SelectItemText, SelectTrigger, SelectValue, -} from '@/app/components/base/ui/select' +} from '@langgenius/dify-ui/select' +import * as React from 'react' +import Checkbox from '@/app/components/base/checkbox' +import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader' +import Input from '@/app/components/base/input' +import Textarea from '@/app/components/base/textarea' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' import FileUploadSetting from '@/app/components/workflow/nodes/_base/components/file-upload-setting' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' diff --git a/web/app/components/app/configuration/config-var/config-modal/index.tsx b/web/app/components/app/configuration/config-var/config-modal/index.tsx index 18484683fa..824b8a8d82 100644 --- a/web/app/components/app/configuration/config-var/config-modal/index.tsx +++ b/web/app/components/app/configuration/config-var/config-modal/index.tsx @@ -2,13 +2,13 @@ import type { ChangeEvent, FC } from 'react' import type { Item as SelectItem } from './type-select' import type { InputVar, InputVarType, MoreInfo } from '@/app/components/workflow/types' +import { toast } from '@langgenius/dify-ui/toast' import * as React from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { useStore as useAppStore } from '@/app/components/app/store' import Modal from '@/app/components/base/modal' -import { toast } from '@/app/components/base/ui/toast' import ConfigContext from '@/context/debug-configuration' import { AppModeEnum } from '@/types/app' import { checkKeys, getNewVarInWorkflow, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' diff --git a/web/app/components/app/configuration/config-var/index.tsx b/web/app/components/app/configuration/config-var/index.tsx index aca2817249..34c6bf5786 100644 --- a/web/app/components/app/configuration/config-var/index.tsx +++ b/web/app/components/app/configuration/config-var/index.tsx @@ -4,15 +4,6 @@ import type { InputVar } from '@/app/components/workflow/types' import type { ExternalDataTool } from '@/models/common' import type { PromptVariable } from '@/models/debug' import type { I18nKeysByPrefix } from '@/types/i18n' -import { cn } from '@langgenius/dify-ui/cn' -import { useBoolean } from 'ahooks' -import { produce } from 'immer' -import * as React from 'react' -import { useCallback, useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { ReactSortable } from 'react-sortablejs' -import { useContext } from 'use-context-selector' -import Tooltip from '@/app/components/base/tooltip' import { AlertDialog, AlertDialogActions, @@ -21,8 +12,17 @@ import { AlertDialogContent, AlertDialogDescription, AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' -import { toast } from '@/app/components/base/ui/toast' +} from '@langgenius/dify-ui/alert-dialog' +import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' +import { useBoolean } from 'ahooks' +import { produce } from 'immer' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { ReactSortable } from 'react-sortablejs' +import { useContext } from 'use-context-selector' +import Tooltip from '@/app/components/base/tooltip' import { InputVarType } from '@/app/components/workflow/types' import ConfigContext from '@/context/debug-configuration' import { useEventEmitterContextContext } from '@/context/event-emitter' diff --git a/web/app/components/app/configuration/config-var/modal-foot.tsx b/web/app/components/app/configuration/config-var/modal-foot.tsx index 3168d2a273..d8a869610f 100644 --- a/web/app/components/app/configuration/config-var/modal-foot.tsx +++ b/web/app/components/app/configuration/config-var/modal-foot.tsx @@ -1,8 +1,8 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' type IModalFootProps = { onConfirm: () => void diff --git a/web/app/components/app/configuration/config-vision/index.tsx b/web/app/components/app/configuration/config-vision/index.tsx index e1fd88eb3f..b9cb54cc34 100644 --- a/web/app/components/app/configuration/config-vision/index.tsx +++ b/web/app/components/app/configuration/config-vision/index.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' import { cn } from '@langgenius/dify-ui/cn' +import { Switch } from '@langgenius/dify-ui/switch' import { noop } from 'es-toolkit/function' import { produce } from 'immer' import * as React from 'react' @@ -10,7 +11,6 @@ import { useContext } from 'use-context-selector' // import { Resolution } from '@/types/app' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' import { Vision } from '@/app/components/base/icons/src/vender/features' -import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card' import { SupportUploadFileTypes } from '@/app/components/workflow/types' diff --git a/web/app/components/app/configuration/config-vision/param-config.tsx b/web/app/components/app/configuration/config-vision/param-config.tsx index 3be4c60aa9..51092f8ae7 100644 --- a/web/app/components/app/configuration/config-vision/param-config.tsx +++ b/web/app/components/app/configuration/config-vision/param-config.tsx @@ -1,11 +1,11 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover' import { RiSettings2Line } from '@remixicon/react' import { memo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' -import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover' import ParamConfigContent from './param-config-content' const ParamsConfig: FC = () => { diff --git a/web/app/components/app/configuration/config/agent-setting-button.tsx b/web/app/components/app/configuration/config/agent-setting-button.tsx index 0f1021e7ce..9831660571 100644 --- a/web/app/components/app/configuration/config/agent-setting-button.tsx +++ b/web/app/components/app/configuration/config/agent-setting-button.tsx @@ -1,11 +1,11 @@ 'use client' import type { FC } from 'react' import type { AgentConfig } from '@/models/debug' +import { Button } from '@langgenius/dify-ui/button' import { RiSettings2Line } from '@remixicon/react' import * as React from 'react' import { useState } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import AgentSetting from './agent/agent-setting' type Props = { diff --git a/web/app/components/app/configuration/config/agent/agent-setting/__tests__/index.spec.tsx b/web/app/components/app/configuration/config/agent/agent-setting/__tests__/index.spec.tsx index cdab20df16..08a6e0f62b 100644 --- a/web/app/components/app/configuration/config/agent/agent-setting/__tests__/index.spec.tsx +++ b/web/app/components/app/configuration/config/agent/agent-setting/__tests__/index.spec.tsx @@ -12,7 +12,7 @@ vi.mock('ahooks', async (importOriginal) => { } }) -vi.mock('@/app/components/base/ui/slider', () => ({ +vi.mock('@langgenius/dify-ui/slider', () => ({ Slider: (props: { className?: string, min?: number, max?: number, value: number, onValueChange: (value: number) => void }) => ( ({ }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: (...args: unknown[]) => mockToastError(...args), }, diff --git a/web/app/components/app/configuration/config/automatic/__tests__/result.spec.tsx b/web/app/components/app/configuration/config/automatic/__tests__/result.spec.tsx index 30f41b7828..54efa4a92c 100644 --- a/web/app/components/app/configuration/config/automatic/__tests__/result.spec.tsx +++ b/web/app/components/app/configuration/config/automatic/__tests__/result.spec.tsx @@ -1,5 +1,5 @@ +import { toast } from '@langgenius/dify-ui/toast' import { fireEvent, render, screen } from '@testing-library/react' -import { toast } from '@/app/components/base/ui/toast' import Result from '../result' import { GeneratorType } from '../types' @@ -19,7 +19,7 @@ vi.mock('copy-to-clipboard', () => ({ default: (...args: unknown[]) => mockCopy(...args), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: vi.fn(), }, diff --git a/web/app/components/app/configuration/config/automatic/automatic-btn.tsx b/web/app/components/app/configuration/config/automatic/automatic-btn.tsx index af14fb037d..f9e9fa362a 100644 --- a/web/app/components/app/configuration/config/automatic/automatic-btn.tsx +++ b/web/app/components/app/configuration/config/automatic/automatic-btn.tsx @@ -1,11 +1,11 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { RiSparklingFill, } from '@remixicon/react' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' type IAutomaticBtnProps = { onClick: () => void diff --git a/web/app/components/app/configuration/config/automatic/get-automatic-res.tsx b/web/app/components/app/configuration/config/automatic/get-automatic-res.tsx index d9200af773..aff540cecf 100644 --- a/web/app/components/app/configuration/config/automatic/get-automatic-res.tsx +++ b/web/app/components/app/configuration/config/automatic/get-automatic-res.tsx @@ -4,6 +4,17 @@ import type { FormValue } from '@/app/components/header/account-setting/model-pr // type import type { GenRes } from '@/service/debug' import type { AppModeEnum, CompletionParams, Model, ModelModeType } from '@/types/app' +import { + AlertDialog, + AlertDialogActions, + AlertDialogCancelButton, + AlertDialogConfirmButton, + AlertDialogContent, + AlertDialogDescription, + AlertDialogTitle, +} from '@langgenius/dify-ui/alert-dialog' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiDatabase2Line, RiFileExcel2Line, @@ -22,17 +33,6 @@ import { useTranslation } from 'react-i18next' import { Generator } from '@/app/components/base/icons/src/vender/other' import Loading from '@/app/components/base/loading' import Modal from '@/app/components/base/modal' -import { - AlertDialog, - AlertDialogActions, - AlertDialogCancelButton, - AlertDialogConfirmButton, - AlertDialogContent, - AlertDialogDescription, - AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' diff --git a/web/app/components/app/configuration/config/automatic/result.tsx b/web/app/components/app/configuration/config/automatic/result.tsx index 4beefe8386..057fd48aeb 100644 --- a/web/app/components/app/configuration/config/automatic/result.tsx +++ b/web/app/components/app/configuration/config/automatic/result.tsx @@ -1,12 +1,12 @@ 'use client' import type { FC } from 'react' import type { GenRes } from '@/service/debug' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiClipboardLine } from '@remixicon/react' import copy from 'copy-to-clipboard' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import CodeEditor from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor' import PromptRes from './prompt-res' import PromptResInWorkflow from './prompt-res-in-workflow' diff --git a/web/app/components/app/configuration/config/code-generator/__tests__/get-code-generator-res.spec.tsx b/web/app/components/app/configuration/config/code-generator/__tests__/get-code-generator-res.spec.tsx index 25039bae96..fa7e9b6bd0 100644 --- a/web/app/components/app/configuration/config/code-generator/__tests__/get-code-generator-res.spec.tsx +++ b/web/app/components/app/configuration/config/code-generator/__tests__/get-code-generator-res.spec.tsx @@ -21,7 +21,7 @@ vi.mock('react-i18next', () => ({ }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: (...args: unknown[]) => mockToastError(...args), }, diff --git a/web/app/components/app/configuration/config/code-generator/get-code-generator-res.tsx b/web/app/components/app/configuration/config/code-generator/get-code-generator-res.tsx index eb1ee7e10c..c69c0f5dae 100644 --- a/web/app/components/app/configuration/config/code-generator/get-code-generator-res.tsx +++ b/web/app/components/app/configuration/config/code-generator/get-code-generator-res.tsx @@ -3,6 +3,17 @@ import type { FormValue } from '@/app/components/header/account-setting/model-pr import type { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import type { GenRes } from '@/service/debug' import type { AppModeEnum, CompletionParams, Model, ModelModeType } from '@/types/app' +import { + AlertDialog, + AlertDialogActions, + AlertDialogCancelButton, + AlertDialogConfirmButton, + AlertDialogContent, + AlertDialogDescription, + AlertDialogTitle, +} from '@langgenius/dify-ui/alert-dialog' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { useBoolean, useSessionStorageState, @@ -13,17 +24,6 @@ import { useTranslation } from 'react-i18next' import { Generator } from '@/app/components/base/icons/src/vender/other' import Loading from '@/app/components/base/loading' import Modal from '@/app/components/base/modal' -import { - AlertDialog, - AlertDialogActions, - AlertDialogCancelButton, - AlertDialogConfirmButton, - AlertDialogContent, - AlertDialogDescription, - AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' diff --git a/web/app/components/app/configuration/config/config-audio.tsx b/web/app/components/app/configuration/config/config-audio.tsx index 15a52610e6..25b6d64eec 100644 --- a/web/app/components/app/configuration/config/config-audio.tsx +++ b/web/app/components/app/configuration/config/config-audio.tsx @@ -1,14 +1,14 @@ 'use client' import type { FC } from 'react' +import { Switch } from '@langgenius/dify-ui/switch' import { produce } from 'immer' import * as React from 'react' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' +import { useContext } from 'use-context-selector' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' import { Microphone01 } from '@/app/components/base/icons/src/vender/features' -import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' import { SupportUploadFileTypes } from '@/app/components/workflow/types' import ConfigContext from '@/context/debug-configuration' diff --git a/web/app/components/app/configuration/config/config-document.tsx b/web/app/components/app/configuration/config/config-document.tsx index 859fd39426..156c605267 100644 --- a/web/app/components/app/configuration/config/config-document.tsx +++ b/web/app/components/app/configuration/config/config-document.tsx @@ -1,14 +1,14 @@ 'use client' import type { FC } from 'react' +import { Switch } from '@langgenius/dify-ui/switch' import { produce } from 'immer' import * as React from 'react' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' +import { useContext } from 'use-context-selector' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' import { Document } from '@/app/components/base/icons/src/vender/features' -import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' import { SupportUploadFileTypes } from '@/app/components/workflow/types' import ConfigContext from '@/context/debug-configuration' diff --git a/web/app/components/app/configuration/configuration-view.tsx b/web/app/components/app/configuration/configuration-view.tsx index 35ab462ad1..3a9882db18 100644 --- a/web/app/components/app/configuration/configuration-view.tsx +++ b/web/app/components/app/configuration/configuration-view.tsx @@ -2,6 +2,16 @@ import type { FC } from 'react' import type { ConfigurationViewModel } from './hooks/use-configuration' import { CodeBracketIcon } from '@heroicons/react/20/solid' +import { + AlertDialog, + AlertDialogActions, + AlertDialogCancelButton, + AlertDialogConfirmButton, + AlertDialogContent, + AlertDialogDescription, + AlertDialogTitle, +} from '@langgenius/dify-ui/alert-dialog' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useTranslation } from 'react-i18next' import AppPublisher from '@/app/components/app/app-publisher/features-wrapper' @@ -15,16 +25,6 @@ import Drawer from '@/app/components/base/drawer' import { FeaturesProvider } from '@/app/components/base/features' import NewFeaturePanel from '@/app/components/base/features/new-feature-panel' import Loading from '@/app/components/base/loading' -import { - AlertDialog, - AlertDialogActions, - AlertDialogCancelButton, - AlertDialogConfirmButton, - AlertDialogContent, - AlertDialogDescription, - AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' -import { Button } from '@/app/components/base/ui/button' import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' import PluginDependency from '@/app/components/workflow/plugin-dependency' import ConfigContext from '@/context/debug-configuration' diff --git a/web/app/components/app/configuration/ctrl-btn-group/index.tsx b/web/app/components/app/configuration/ctrl-btn-group/index.tsx index fa8a8bdab3..6ac485c097 100644 --- a/web/app/components/app/configuration/ctrl-btn-group/index.tsx +++ b/web/app/components/app/configuration/ctrl-btn-group/index.tsx @@ -1,8 +1,8 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import s from './style.module.css' type IContrlBtnGroupProps = { diff --git a/web/app/components/app/configuration/dataset-config/params-config/__tests__/config-content.spec.tsx b/web/app/components/app/configuration/dataset-config/params-config/__tests__/config-content.spec.tsx index a17b39e4b4..80b9239524 100644 --- a/web/app/components/app/configuration/dataset-config/params-config/__tests__/config-content.spec.tsx +++ b/web/app/components/app/configuration/dataset-config/params-config/__tests__/config-content.spec.tsx @@ -3,9 +3,9 @@ import type { IndexingType } from '@/app/components/datasets/create/step-two' import type { DataSet } from '@/models/datasets' import type { DatasetConfigs } from '@/models/debug' import type { RetrievalConfig } from '@/types/app' +import { toast } from '@langgenius/dify-ui/toast' import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { toast } from '@/app/components/base/ui/toast' import { useCurrentProviderAndModel, useModelListAndDefaultModelAndCurrentProviderAndModel, diff --git a/web/app/components/app/configuration/dataset-config/params-config/__tests__/index.spec.tsx b/web/app/components/app/configuration/dataset-config/params-config/__tests__/index.spec.tsx index a892de7fee..b5924a5f23 100644 --- a/web/app/components/app/configuration/dataset-config/params-config/__tests__/index.spec.tsx +++ b/web/app/components/app/configuration/dataset-config/params-config/__tests__/index.spec.tsx @@ -1,9 +1,9 @@ import type { MockedFunction, MockInstance } from 'vitest' import type { DatasetConfigs } from '@/models/debug' +import { toast } from '@langgenius/dify-ui/toast' import { render, screen, waitFor, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' import * as React from 'react' -import { toast } from '@/app/components/base/ui/toast' import { useCurrentProviderAndModel, useModelListAndDefaultModelAndCurrentProviderAndModel, diff --git a/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx b/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx index bb260fb499..d0e6b2fe9f 100644 --- a/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx +++ b/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx @@ -10,14 +10,14 @@ import type { DatasetConfigs, } from '@/models/debug' import { cn } from '@langgenius/dify-ui/cn' +import { Switch } from '@langgenius/dify-ui/switch' +import { toast } from '@langgenius/dify-ui/toast' import { memo, useCallback, useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold-item' import TopKItem from '@/app/components/base/param-item/top-k-item' -import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' -import { toast } from '@/app/components/base/ui/toast' import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useCurrentProviderAndModel, useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' diff --git a/web/app/components/app/configuration/dataset-config/params-config/index.tsx b/web/app/components/app/configuration/dataset-config/params-config/index.tsx index ce78ad6e99..d4578af1d7 100644 --- a/web/app/components/app/configuration/dataset-config/params-config/index.tsx +++ b/web/app/components/app/configuration/dataset-config/params-config/index.tsx @@ -1,14 +1,14 @@ 'use client' import type { DataSet } from '@/models/datasets' import type { DatasetConfigs } from '@/models/debug' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiEqualizer2Line } from '@remixicon/react' import { memo, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useCurrentProviderAndModel, useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' import { diff --git a/web/app/components/app/configuration/dataset-config/params-config/weighted-score.tsx b/web/app/components/app/configuration/dataset-config/params-config/weighted-score.tsx index e5080f26e4..5c2cca05d3 100644 --- a/web/app/components/app/configuration/dataset-config/params-config/weighted-score.tsx +++ b/web/app/components/app/configuration/dataset-config/params-config/weighted-score.tsx @@ -1,7 +1,7 @@ +import { Slider } from '@langgenius/dify-ui/slider' import { noop } from 'es-toolkit/function' import { memo } from 'react' import { useTranslation } from 'react-i18next' -import { Slider } from '@/app/components/base/ui/slider' const weightedScoreSliderSlotClassNames = { track: 'bg-util-colors-teal-teal-500', diff --git a/web/app/components/app/configuration/dataset-config/select-dataset/index.tsx b/web/app/components/app/configuration/dataset-config/select-dataset/index.tsx index ff371fc2df..13d11574d5 100644 --- a/web/app/components/app/configuration/dataset-config/select-dataset/index.tsx +++ b/web/app/components/app/configuration/dataset-config/select-dataset/index.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' import type { DataSet } from '@/models/datasets' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { useInfiniteScroll } from 'ahooks' import * as React from 'react' @@ -10,7 +11,6 @@ import AppIcon from '@/app/components/base/app-icon' import Badge from '@/app/components/base/badge' import Loading from '@/app/components/base/loading' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import FeatureIcon from '@/app/components/header/account-setting/model-provider-page/model-selector/feature-icon' import { useKnowledge } from '@/hooks/use-knowledge' diff --git a/web/app/components/app/configuration/dataset-config/settings-modal/__tests__/index.spec.tsx b/web/app/components/app/configuration/dataset-config/settings-modal/__tests__/index.spec.tsx index d699a4baec..cf1df79d1e 100644 --- a/web/app/components/app/configuration/dataset-config/settings-modal/__tests__/index.spec.tsx +++ b/web/app/components/app/configuration/dataset-config/settings-modal/__tests__/index.spec.tsx @@ -19,7 +19,7 @@ const toastMocks = vi.hoisted(() => ({ promise: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign(toastMocks.call, { success: vi.fn((message: string, options?: Record) => toastMocks.call({ type: 'success', message, ...options })), error: vi.fn((message: string, options?: Record) => toastMocks.call({ type: 'error', message, ...options })), diff --git a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx index 872d081307..74e4ca5fe5 100644 --- a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx +++ b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx @@ -2,15 +2,15 @@ import type { FC } from 'react' import type { Member } from '@/models/common' import type { DataSet } from '@/models/datasets' import type { RetrievalConfig } from '@/types/app' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiCloseLine } from '@remixicon/react' import { isEqual } from 'es-toolkit/predicate' import { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import Textarea from '@/app/components/base/textarea' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model' import { IndexingType } from '@/app/components/datasets/create/step-two' import IndexMethod from '@/app/components/datasets/settings/index-method' diff --git a/web/app/components/app/configuration/debug/__tests__/index.spec.tsx b/web/app/components/app/configuration/debug/__tests__/index.spec.tsx index 4a50ecc626..c17a74e24c 100644 --- a/web/app/components/app/configuration/debug/__tests__/index.spec.tsx +++ b/web/app/components/app/configuration/debug/__tests__/index.spec.tsx @@ -46,7 +46,7 @@ const mockState = vi.hoisted(() => ({ }, })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign(mockState.mockToastCall, { success: vi.fn((message: string, options?: Record) => mockState.mockToastCall({ type: 'success', message, ...options })), diff --git a/web/app/components/app/configuration/debug/debug-with-multiple-model/__tests__/chat-item.spec.tsx b/web/app/components/app/configuration/debug/debug-with-multiple-model/__tests__/chat-item.spec.tsx index 61eb8f2ae8..719479498a 100644 --- a/web/app/components/app/configuration/debug/debug-with-multiple-model/__tests__/chat-item.spec.tsx +++ b/web/app/components/app/configuration/debug/debug-with-multiple-model/__tests__/chat-item.spec.tsx @@ -90,7 +90,7 @@ vi.mock('@/app/components/base/chat/chat', () => ({ }, })) -vi.mock('@/app/components/base/ui/avatar', () => ({ +vi.mock('@langgenius/dify-ui/avatar', () => ({ Avatar: ({ name }: { name: string }) =>
{name}
, })) diff --git a/web/app/components/app/configuration/debug/debug-with-multiple-model/chat-item.tsx b/web/app/components/app/configuration/debug/debug-with-multiple-model/chat-item.tsx index 56345890ff..d5b353c899 100644 --- a/web/app/components/app/configuration/debug/debug-with-multiple-model/chat-item.tsx +++ b/web/app/components/app/configuration/debug/debug-with-multiple-model/chat-item.tsx @@ -2,6 +2,7 @@ import type { FC } from 'react' import type { ModelAndParameter } from '../types' import type { InputForm } from '@/app/components/base/chat/chat/type' import type { ChatConfig, OnSend } from '@/app/components/base/chat/types' +import { Avatar } from '@langgenius/dify-ui/avatar' import { memo, useCallback, @@ -11,7 +12,6 @@ import Chat from '@/app/components/base/chat/chat' import { useChat } from '@/app/components/base/chat/chat/hooks' import { getLastAnswer } from '@/app/components/base/chat/utils' import { useFeatures } from '@/app/components/base/features/hooks' -import { Avatar } from '@/app/components/base/ui/avatar' import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useAppContext } from '@/context/app-context' import { useDebugConfigurationContext } from '@/context/debug-configuration' diff --git a/web/app/components/app/configuration/debug/debug-with-multiple-model/debug-item.tsx b/web/app/components/app/configuration/debug/debug-with-multiple-model/debug-item.tsx index 2e535baeac..e76bbb0728 100644 --- a/web/app/components/app/configuration/debug/debug-with-multiple-model/debug-item.tsx +++ b/web/app/components/app/configuration/debug/debug-with-multiple-model/debug-item.tsx @@ -1,15 +1,15 @@ import type { CSSProperties, FC } from 'react' import type { ModelAndParameter } from '../types' -import { memo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import ActionButton from '@/app/components/base/action-button' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' +} from '@langgenius/dify-ui/dropdown-menu' +import { memo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import ActionButton from '@/app/components/base/action-button' import { ModelStatusEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useDebugConfigurationContext } from '@/context/debug-configuration' import { useProviderContext } from '@/context/provider-context' diff --git a/web/app/components/app/configuration/debug/debug-with-single-model/index.tsx b/web/app/components/app/configuration/debug/debug-with-single-model/index.tsx index a9f9f1116b..aef3d5ef9c 100644 --- a/web/app/components/app/configuration/debug/debug-with-single-model/index.tsx +++ b/web/app/components/app/configuration/debug/debug-with-single-model/index.tsx @@ -1,13 +1,13 @@ import type { InputForm } from '@/app/components/base/chat/chat/type' import type { ChatConfig, ChatItem, OnSend } from '@/app/components/base/chat/types' import type { FileEntity } from '@/app/components/base/file-uploader/types' +import { Avatar } from '@langgenius/dify-ui/avatar' import { memo, useCallback, useImperativeHandle, useMemo } from 'react' import { useStore as useAppStore } from '@/app/components/app/store' import Chat from '@/app/components/base/chat/chat' import { useChat } from '@/app/components/base/chat/chat/hooks' import { getLastAnswer, isValidGeneratedAnswer } from '@/app/components/base/chat/utils' import { useFeatures } from '@/app/components/base/features/hooks' -import { Avatar } from '@/app/components/base/ui/avatar' import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useAppContext } from '@/context/app-context' import { useDebugConfigurationContext } from '@/context/debug-configuration' diff --git a/web/app/components/app/configuration/debug/index.tsx b/web/app/components/app/configuration/debug/index.tsx index 88bca7111c..87beccae24 100644 --- a/web/app/components/app/configuration/debug/index.tsx +++ b/web/app/components/app/configuration/debug/index.tsx @@ -5,6 +5,13 @@ import type { ModelAndParameter } from './types' import type { ModelParameterModalProps } from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' import type { Inputs } from '@/models/debug' import type { ModelConfig as BackendModelConfig, VisionFile, VisionSettings } from '@/types/app' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@langgenius/dify-ui/tooltip' import { RiAddLine, RiEqualizer2Line, @@ -28,13 +35,6 @@ import AgentLogModal from '@/app/components/base/agent-log-modal' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' import { RefreshCcw01 } from '@/app/components/base/icons/src/vender/line/arrows' import PromptLogModal from '@/app/components/base/prompt-log-modal' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' -import { - Tooltip, - TooltipContent, - TooltipTrigger, -} from '@/app/components/base/ui/tooltip' import { ModelFeatureEnum, ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' import { DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config' diff --git a/web/app/components/app/configuration/hooks/__tests__/use-configuration-utils.spec.ts b/web/app/components/app/configuration/hooks/__tests__/use-configuration-utils.spec.ts index c0353c233d..4c72e4690c 100644 --- a/web/app/components/app/configuration/hooks/__tests__/use-configuration-utils.spec.ts +++ b/web/app/components/app/configuration/hooks/__tests__/use-configuration-utils.spec.ts @@ -52,7 +52,7 @@ vi.mock('@/app/components/workflow/nodes/knowledge-retrieval/utils', async () => } }) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: (...args: unknown[]) => mockToastError(...args), success: (...args: unknown[]) => mockToastSuccess(...args), diff --git a/web/app/components/app/configuration/hooks/use-configuration-utils.ts b/web/app/components/app/configuration/hooks/use-configuration-utils.ts index 6911fdcd01..8a7b1c811e 100644 --- a/web/app/components/app/configuration/hooks/use-configuration-utils.ts +++ b/web/app/components/app/configuration/hooks/use-configuration-utils.ts @@ -4,9 +4,9 @@ import type { Collection } from '@/app/components/tools/types' import type { DataSet } from '@/models/datasets' import type { AnnotationReplyConfig, DatasetConfigs, ModelConfig, PromptVariable } from '@/models/debug' import type { ModelConfig as BackendModelConfig, UserInputFormItem, VisionSettings } from '@/types/app' +import { toast } from '@langgenius/dify-ui/toast' import { clone } from 'es-toolkit/object' import { produce } from 'immer' -import { toast } from '@/app/components/base/ui/toast' import { getMultipleRetrievalConfig, getSelectedDatasetsMode } from '@/app/components/workflow/nodes/knowledge-retrieval/utils' import { DEFAULT_AGENT_SETTING, DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config' import { PromptMode } from '@/models/debug' diff --git a/web/app/components/app/configuration/prompt-value-panel/index.tsx b/web/app/components/app/configuration/prompt-value-panel/index.tsx index a9df145ca1..94bc48da29 100644 --- a/web/app/components/app/configuration/prompt-value-panel/index.tsx +++ b/web/app/components/app/configuration/prompt-value-panel/index.tsx @@ -2,6 +2,7 @@ import type { FC } from 'react' import type { Inputs } from '@/models/debug' import type { VisionFile, VisionSettings } from '@/types/app' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiArrowDownSLine, @@ -19,7 +20,6 @@ import Input from '@/app/components/base/input' import Select from '@/app/components/base/select' import Textarea from '@/app/components/base/textarea' import Tooltip from '@/app/components/base/tooltip' -import { Button } from '@/app/components/base/ui/button' import BoolInput from '@/app/components/workflow/nodes/_base/components/before-run-form/bool-input' import ConfigContext from '@/context/debug-configuration' import { AppModeEnum, ModelModeType } from '@/types/app' diff --git a/web/app/components/app/configuration/tools/__tests__/external-data-tool-modal.spec.tsx b/web/app/components/app/configuration/tools/__tests__/external-data-tool-modal.spec.tsx index ceca00a9ae..2a725d88ca 100644 --- a/web/app/components/app/configuration/tools/__tests__/external-data-tool-modal.spec.tsx +++ b/web/app/components/app/configuration/tools/__tests__/external-data-tool-modal.spec.tsx @@ -11,7 +11,7 @@ vi.mock('react-i18next', () => ({ }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: (...args: unknown[]) => mockToastError(...args), }, diff --git a/web/app/components/app/configuration/tools/external-data-tool-modal.tsx b/web/app/components/app/configuration/tools/external-data-tool-modal.tsx index 68a1ee875d..b09b7b1c70 100644 --- a/web/app/components/app/configuration/tools/external-data-tool-modal.tsx +++ b/web/app/components/app/configuration/tools/external-data-tool-modal.tsx @@ -2,6 +2,10 @@ import type { FC } from 'react' import type { ExternalDataTool, } from '@/models/common' +import { Button } from '@langgenius/dify-ui/button' +import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog' +import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger, SelectValue } from '@langgenius/dify-ui/select' +import { toast } from '@langgenius/dify-ui/toast' import { noop } from 'es-toolkit/function' import { useState } from 'react' import { useTranslation } from 'react-i18next' @@ -9,10 +13,6 @@ import AppIcon from '@/app/components/base/app-icon' import EmojiPicker from '@/app/components/base/emoji-picker' import FormGeneration from '@/app/components/base/features/new-feature-panel/moderation/form-generation' import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education' -import { Button } from '@/app/components/base/ui/button' -import { Dialog, DialogContent } from '@/app/components/base/ui/dialog' -import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger, SelectValue } from '@/app/components/base/ui/select' -import { toast } from '@/app/components/base/ui/toast' import ApiBasedExtensionSelector from '@/app/components/header/account-setting/api-based-extension-page/selector' import { useDocLink, useLocale } from '@/context/i18n' import { useCodeBasedExtensions } from '@/service/use-common' diff --git a/web/app/components/app/create-app-dialog/app-card/index.tsx b/web/app/components/app/create-app-dialog/app-card/index.tsx index fef7199ca2..65bd74344a 100644 --- a/web/app/components/app/create-app-dialog/app-card/index.tsx +++ b/web/app/components/app/create-app-dialog/app-card/index.tsx @@ -1,6 +1,7 @@ 'use client' import type { App } from '@/models/explore' import { PlusIcon } from '@heroicons/react/20/solid' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiInformation2Line } from '@remixicon/react' import { useCallback } from 'react' @@ -8,7 +9,6 @@ import { useTranslation } from 'react-i18next' import { useContextSelector } from 'use-context-selector' import { trackEvent } from '@/app/components/base/amplitude' import AppIcon from '@/app/components/base/app-icon' -import { Button } from '@/app/components/base/ui/button' import AppListContext from '@/context/app-list-context' import { useGlobalPublicStore } from '@/context/global-public-context' import { AppTypeIcon, AppTypeLabel } from '../../type-selector' diff --git a/web/app/components/app/create-app-dialog/app-list/__tests__/index.spec.tsx b/web/app/components/app/create-app-dialog/app-list/__tests__/index.spec.tsx index 34024da54c..0c6462c2f9 100644 --- a/web/app/components/app/create-app-dialog/app-list/__tests__/index.spec.tsx +++ b/web/app/components/app/create-app-dialog/app-list/__tests__/index.spec.tsx @@ -86,7 +86,7 @@ vi.mock('@/app/components/explore/create-app-modal', () => ({ ) : null, })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: (...args: unknown[]) => mockToastSuccess(...args), error: (...args: unknown[]) => mockToastError(...args), diff --git a/web/app/components/app/create-app-dialog/app-list/index.tsx b/web/app/components/app/create-app-dialog/app-list/index.tsx index 19089d6364..1924de3893 100644 --- a/web/app/components/app/create-app-dialog/app-list/index.tsx +++ b/web/app/components/app/create-app-dialog/app-list/index.tsx @@ -3,6 +3,7 @@ import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal' import type { App } from '@/models/explore' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiRobot2Line } from '@remixicon/react' import { useDebounceFn } from 'ahooks' import * as React from 'react' @@ -12,7 +13,6 @@ import AppTypeSelector from '@/app/components/app/type-selector' import Divider from '@/app/components/base/divider' import Input from '@/app/components/base/input' import Loading from '@/app/components/base/loading' -import { toast } from '@/app/components/base/ui/toast' import CreateAppModal from '@/app/components/explore/create-app-modal' import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' import { NEED_REFRESH_APP_LIST_KEY } from '@/config' diff --git a/web/app/components/app/create-app-modal/__tests__/index.spec.tsx b/web/app/components/app/create-app-modal/__tests__/index.spec.tsx index 305b90981b..24fb21747f 100644 --- a/web/app/components/app/create-app-modal/__tests__/index.spec.tsx +++ b/web/app/components/app/create-app-modal/__tests__/index.spec.tsx @@ -41,7 +41,7 @@ const toastMocks = vi.hoisted(() => ({ mockToastSuccess: vi.fn(), mockToastError: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: toastMocks.mockToastSuccess, error: toastMocks.mockToastError, diff --git a/web/app/components/app/create-app-modal/index.tsx b/web/app/components/app/create-app-modal/index.tsx index 073107755f..2ff8a0aacd 100644 --- a/web/app/components/app/create-app-modal/index.tsx +++ b/web/app/components/app/create-app-modal/index.tsx @@ -1,8 +1,10 @@ 'use client' import type { AppIconSelection } from '../../base/app-icon-picker' -import { cn } from '@langgenius/dify-ui/cn' +import { Button } from '@langgenius/dify-ui/button' +import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowRightLine, RiArrowRightSLine, RiExchange2Fill } from '@remixicon/react' import { useDebounceFn, useKeyPress } from 'ahooks' import { useCallback, useEffect, useRef, useState } from 'react' @@ -13,8 +15,6 @@ import FullScreenModal from '@/app/components/base/fullscreen-modal' import { BubbleTextMod, ChatBot, ListSparkle, Logic } from '@/app/components/base/icons/src/vender/solid/communication' import Input from '@/app/components/base/input' import Textarea from '@/app/components/base/textarea' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import AppsFull from '@/app/components/billing/apps-full-in-dialog' import { NEED_REFRESH_APP_LIST_KEY } from '@/config' import { useAppContext } from '@/context/app-context' diff --git a/web/app/components/app/create-from-dsl-modal/__tests__/index.spec.tsx b/web/app/components/app/create-from-dsl-modal/__tests__/index.spec.tsx index ae555a872b..f56b815399 100644 --- a/web/app/components/app/create-from-dsl-modal/__tests__/index.spec.tsx +++ b/web/app/components/app/create-from-dsl-modal/__tests__/index.spec.tsx @@ -83,7 +83,7 @@ vi.mock('@/utils/app-redirection', () => ({ getRedirection: (...args: unknown[]) => mockGetRedirection(...args), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign( (...args: unknown[]) => toastMocks.call(...args), { diff --git a/web/app/components/app/create-from-dsl-modal/__tests__/uploader.spec.tsx b/web/app/components/app/create-from-dsl-modal/__tests__/uploader.spec.tsx index 8b935db118..ba097b355c 100644 --- a/web/app/components/app/create-from-dsl-modal/__tests__/uploader.spec.tsx +++ b/web/app/components/app/create-from-dsl-modal/__tests__/uploader.spec.tsx @@ -1,5 +1,5 @@ +import { toast } from '@langgenius/dify-ui/toast' import { fireEvent, render, screen } from '@testing-library/react' -import { toast } from '@/app/components/base/ui/toast' import Uploader from '../uploader' vi.mock('react-i18next', () => ({ @@ -8,7 +8,7 @@ vi.mock('react-i18next', () => ({ }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: vi.fn(), }, diff --git a/web/app/components/app/create-from-dsl-modal/dsl-confirm-modal.tsx b/web/app/components/app/create-from-dsl-modal/dsl-confirm-modal.tsx index 59ba840a07..d0c97e185c 100644 --- a/web/app/components/app/create-from-dsl-modal/dsl-confirm-modal.tsx +++ b/web/app/components/app/create-from-dsl-modal/dsl-confirm-modal.tsx @@ -1,6 +1,6 @@ +import { Button } from '@langgenius/dify-ui/button' import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' type DSLConfirmModalProps = { versions?: { diff --git a/web/app/components/app/create-from-dsl-modal/index.tsx b/web/app/components/app/create-from-dsl-modal/index.tsx index d4b857ee6b..4f99fe9027 100644 --- a/web/app/components/app/create-from-dsl-modal/index.tsx +++ b/web/app/components/app/create-from-dsl-modal/index.tsx @@ -1,7 +1,9 @@ 'use client' import type { MouseEventHandler } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiCloseLine } from '@remixicon/react' import { useDebounceFn, useKeyPress } from 'ahooks' import { noop } from 'es-toolkit/function' @@ -9,8 +11,6 @@ import { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import AppsFull from '@/app/components/billing/apps-full-in-dialog' import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' import { NEED_REFRESH_APP_LIST_KEY } from '@/config' diff --git a/web/app/components/app/create-from-dsl-modal/uploader.tsx b/web/app/components/app/create-from-dsl-modal/uploader.tsx index 1daa7e9f7b..aeecb280d0 100644 --- a/web/app/components/app/create-from-dsl-modal/uploader.tsx +++ b/web/app/components/app/create-from-dsl-modal/uploader.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiDeleteBinLine, RiUploadCloud2Line, @@ -10,7 +11,6 @@ import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import { Yaml as YamlIcon } from '@/app/components/base/icons/src/public/files' -import { toast } from '@/app/components/base/ui/toast' import { formatFileSize } from '@/utils/format' type Props = { diff --git a/web/app/components/app/duplicate-modal/__tests__/index.spec.tsx b/web/app/components/app/duplicate-modal/__tests__/index.spec.tsx index a2ff6bc2fd..ceec1aa3c0 100644 --- a/web/app/components/app/duplicate-modal/__tests__/index.spec.tsx +++ b/web/app/components/app/duplicate-modal/__tests__/index.spec.tsx @@ -20,7 +20,7 @@ vi.mock('@/context/provider-context', () => ({ }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: (...args: unknown[]) => toastErrorMock(...args), }, diff --git a/web/app/components/app/duplicate-modal/index.tsx b/web/app/components/app/duplicate-modal/index.tsx index c28ead7821..e55ac6cf66 100644 --- a/web/app/components/app/duplicate-modal/index.tsx +++ b/web/app/components/app/duplicate-modal/index.tsx @@ -1,6 +1,8 @@ 'use client' import type { AppIconType } from '@/types/app' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiCloseLine } from '@remixicon/react' import { noop } from 'es-toolkit/function' import * as React from 'react' @@ -9,8 +11,6 @@ import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import Input from '@/app/components/base/input' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import AppsFull from '@/app/components/billing/apps-full-in-dialog' import { useProviderContext } from '@/context/provider-context' import AppIconPicker from '../../base/app-icon-picker' diff --git a/web/app/components/app/in-site-message/index.tsx b/web/app/components/app/in-site-message/index.tsx index 5482086483..4038fb375d 100644 --- a/web/app/components/app/in-site-message/index.tsx +++ b/web/app/components/app/in-site-message/index.tsx @@ -1,10 +1,10 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { useEffect, useMemo, useState } from 'react' import { trackEvent } from '@/app/components/base/amplitude' import { MarkdownWithDirective } from '@/app/components/base/markdown-with-directive' -import { Button } from '@/app/components/base/ui/button' type InSiteMessageAction = 'link' | 'close' type InSiteMessageButtonType = 'primary' | 'default' diff --git a/web/app/components/app/log/__tests__/model-info.spec.tsx b/web/app/components/app/log/__tests__/model-info.spec.tsx index f41aaf4c00..44dcffe7bf 100644 --- a/web/app/components/app/log/__tests__/model-info.spec.tsx +++ b/web/app/components/app/log/__tests__/model-info.spec.tsx @@ -34,7 +34,7 @@ vi.mock('@/app/components/header/account-setting/model-provider-page/model-name' ), })) -vi.mock('@/app/components/base/ui/popover', async () => { +vi.mock('@langgenius/dify-ui/popover', async () => { const React = await import('react') const PopoverContext = React.createContext<{ open: boolean, onOpenChange?: (open: boolean) => void } | null>(null) diff --git a/web/app/components/app/log/list.tsx b/web/app/components/app/log/list.tsx index 551bae0212..da96d41804 100644 --- a/web/app/components/app/log/list.tsx +++ b/web/app/components/app/log/list.tsx @@ -9,6 +9,7 @@ import { HandThumbUpIcon, } from '@heroicons/react/24/outline' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiCloseLine, RiEditFill } from '@remixicon/react' import dayjs from 'dayjs' import timezone from 'dayjs/plugin/timezone' @@ -30,7 +31,6 @@ import Drawer from '@/app/components/base/drawer' import Loading from '@/app/components/base/loading' import MessageLogModal from '@/app/components/base/message-log-modal' import Tooltip from '@/app/components/base/tooltip' -import { toast } from '@/app/components/base/ui/toast' import { WorkflowContextProvider } from '@/app/components/workflow/context' import { useAppContext } from '@/context/app-context' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' diff --git a/web/app/components/app/log/model-info.tsx b/web/app/components/app/log/model-info.tsx index e768f30a50..314627d855 100644 --- a/web/app/components/app/log/model-info.tsx +++ b/web/app/components/app/log/model-info.tsx @@ -1,12 +1,12 @@ 'use client' import type { FC } from 'react' import { cn } from '@langgenius/dify-ui/cn' +import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover' import { RiInformation2Line, } from '@remixicon/react' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover' import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' 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' diff --git a/web/app/components/app/overview/__tests__/trigger-card.spec.tsx b/web/app/components/app/overview/__tests__/trigger-card.spec.tsx index d51a23bb05..7e183a3b15 100644 --- a/web/app/components/app/overview/__tests__/trigger-card.spec.tsx +++ b/web/app/components/app/overview/__tests__/trigger-card.spec.tsx @@ -73,8 +73,8 @@ vi.mock('@/app/components/workflow/block-icon', () => ({ ), })) -vi.mock('@/app/components/base/switch', () => ({ - default: ({ checked, onCheckedChange, disabled }: { checked: boolean, onCheckedChange: (v: boolean) => void, disabled: boolean }) => ( +vi.mock('@langgenius/dify-ui/switch', () => ({ + Switch: ({ checked, onCheckedChange, disabled }: { checked: boolean, onCheckedChange: (v: boolean) => void, disabled: boolean }) => ( , })) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/header.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/header.tsx index 034556d96f..a285946272 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/header.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/header.tsx @@ -1,10 +1,10 @@ import type { CredentialSelectorProps } from './credential-selector' +import { Button } from '@langgenius/dify-ui/button' import { RiBookOpenLine, RiEqualizer2Line } from '@remixicon/react' import * as React from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import Tooltip from '@/app/components/base/tooltip' -import { Button } from '@/app/components/base/ui/button' import CredentialSelector from './credential-selector' type HeaderProps = { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/hooks/__tests__/use-local-file-upload.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/hooks/__tests__/use-local-file-upload.spec.tsx index 80a8fc854d..dc20688e9e 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/hooks/__tests__/use-local-file-upload.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/hooks/__tests__/use-local-file-upload.spec.tsx @@ -18,7 +18,7 @@ const { mockNotify, mockToast } = vi.hoisted(() => { return { mockNotify, mockToast } }) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: mockToast, })) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/__tests__/index.spec.tsx index 6be0e28d31..c193638a6a 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/__tests__/index.spec.tsx @@ -37,8 +37,8 @@ const { mockToastError } = vi.hoisted(() => ({ mockToastError: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, toast: { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx index 5321986cd7..22bc8a65e0 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx @@ -1,11 +1,11 @@ import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' import type { DataSourceNotionPageMap, DataSourceNotionWorkspace } from '@/models/common' import type { DataSourceNodeCompletedResponse, DataSourceNodeErrorResponse } from '@/types/pipeline' +import { toast } from '@langgenius/dify-ui/toast' import { useCallback, useEffect, useMemo } from 'react' import { useShallow } from 'zustand/react/shallow' import Loading from '@/app/components/base/loading' import SearchInput from '@/app/components/base/notion-page-selector/search-input' -import { toast } from '@/app/components/base/ui/toast' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import { useDocLink } from '@/context/i18n' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/__tests__/index.spec.tsx index 6eed119ede..c8fdf49fd1 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/__tests__/index.spec.tsx @@ -49,8 +49,8 @@ const { mockToastError } = vi.hoisted(() => ({ mockToastError: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, toast: { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/connect/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/connect/index.tsx index 5b1b0a6b1a..6a7190161d 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/connect/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/connect/index.tsx @@ -1,7 +1,7 @@ import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' +import { Button } from '@langgenius/dify-ui/button' import { useTranslation } from 'react-i18next' import { Icon3Dots } from '@/app/components/base/icons/src/vender/line/others' -import { Button } from '@/app/components/base/ui/button' import BlockIcon from '@/app/components/workflow/block-icon' import { useToolIcon } from '@/app/components/workflow/hooks' import { BlockEnum } from '@/app/components/workflow/types' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/index.tsx index c6a90824ab..43b5fcc71a 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/index.tsx @@ -1,11 +1,11 @@ import { cn } from '@langgenius/dify-ui/cn' -import * as React from 'react' -import { useCallback, useState } from 'react' import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' +} from '@langgenius/dify-ui/dropdown-menu' +import * as React from 'react' +import { useCallback, useState } from 'react' import Menu from './menu' type DropdownProps = { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/empty-search-result.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/empty-search-result.tsx index ca110d3694..1691bf90b2 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/empty-search-result.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/empty-search-result.tsx @@ -1,7 +1,7 @@ +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useTranslation } from 'react-i18next' import { SearchMenu } from '@/app/components/base/icons/src/vender/knowledge' -import { Button } from '@/app/components/base/ui/button' type EmptySearchResultProps = { onResetKeywords: () => void diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/header.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/header.tsx index 5c12aaa68c..bc51751aef 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/header.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/header.tsx @@ -1,7 +1,7 @@ +import { Button } from '@langgenius/dify-ui/button' import { RiBookOpenLine, RiEqualizer2Line } from '@remixicon/react' import * as React from 'react' import Divider from '@/app/components/base/divider' -import { Button } from '@/app/components/base/ui/button' type HeaderProps = { onClickConfiguration?: () => void diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx index 2113e8841c..76614e3865 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx @@ -1,10 +1,10 @@ import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' import type { OnlineDriveFile } from '@/models/pipeline' import type { DataSourceNodeCompletedResponse, DataSourceNodeErrorResponse } from '@/types/pipeline' +import { toast } from '@langgenius/dify-ui/toast' import { produce } from 'immer' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useShallow } from 'zustand/react/shallow' -import { toast } from '@/app/components/base/ui/toast' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import { useDocLink } from '@/context/i18n' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/__tests__/crawled-result-item.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/__tests__/crawled-result-item.spec.tsx index 62dba84e30..cded02b431 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/__tests__/crawled-result-item.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/__tests__/crawled-result-item.spec.tsx @@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import CrawledResultItem from '../crawled-result-item' -vi.mock('@/app/components/base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: ({ children, onClick }: { children: React.ReactNode, onClick: () => void }) => ( ), diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result-item.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result-item.tsx index 664a251e25..19019b4dd7 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result-item.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result-item.tsx @@ -1,12 +1,12 @@ 'use client' import type { CrawlResultItem as CrawlResultItemType } from '@/models/datasets' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import * as React from 'react' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Checkbox from '@/app/components/base/checkbox' import Radio from '@/app/components/base/radio/ui' -import { Button } from '@/app/components/base/ui/button' type CrawledResultItemProps = { payload: CrawlResultItemType diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/__tests__/index.spec.tsx index cea569fa5f..2ab8ad6d4b 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/__tests__/index.spec.tsx @@ -10,8 +10,8 @@ const { mockToastError } = vi.hoisted(() => ({ mockToastError: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, toast: { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/index.tsx index c8a06ea807..46c6c6f462 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/index.tsx @@ -1,5 +1,7 @@ import type { RAGPipelineVariables } from '@/models/pipeline' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiPlayLargeLine } from '@remixicon/react' import { useBoolean } from 'ahooks' import { useEffect, useMemo } from 'react' @@ -8,8 +10,6 @@ import { useAppForm } from '@/app/components/base/form' import BaseField from '@/app/components/base/form/form-scenarios/base/field' import { generateZodSchema } from '@/app/components/base/form/form-scenarios/base/utils' import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/general' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { useConfigurations, useInitialData } from '@/app/components/rag-pipeline/hooks/use-input-fields' import { CrawlStep } from '@/models/datasets' diff --git a/web/app/components/datasets/documents/create-from-pipeline/left-header.tsx b/web/app/components/datasets/documents/create-from-pipeline/left-header.tsx index 925da57197..ab432d6962 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/left-header.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/left-header.tsx @@ -1,8 +1,8 @@ import type { Step } from './step-indicator' +import { Button } from '@langgenius/dify-ui/button' import { RiArrowLeftLine } from '@remixicon/react' import * as React from 'react' import Effect from '@/app/components/base/effect' -import { Button } from '@/app/components/base/ui/button' import Link from '@/next/link' import { useParams } from '@/next/navigation' import StepIndicator from './step-indicator' diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/__tests__/online-document-preview.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/__tests__/online-document-preview.spec.tsx index 1e094fedb0..96033a6f60 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/__tests__/online-document-preview.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/__tests__/online-document-preview.spec.tsx @@ -9,8 +9,8 @@ const { mockToastError } = vi.hoisted(() => ({ mockToastError: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, toast: { diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.tsx index 2d729ee079..9b56c6c8a3 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.tsx @@ -1,13 +1,13 @@ import type { NotionPage } from '@/models/common' import type { CrawlResultItem, CustomFile, DocumentItem, FileIndexingEstimateResponse } from '@/models/datasets' import type { OnlineDriveFile } from '@/models/pipeline' +import { Button } from '@langgenius/dify-ui/button' import { RiSearchEyeLine } from '@remixicon/react' import * as React from 'react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import Badge from '@/app/components/base/badge' import { SkeletonContainer, SkeletonPoint, SkeletonRectangle, SkeletonRow } from '@/app/components/base/skeleton' -import { Button } from '@/app/components/base/ui/button' import SummaryLabel from '@/app/components/datasets/documents/detail/completed/common/summary-label' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import { ChunkingMode } from '@/models/datasets' diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/online-document-preview.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/online-document-preview.tsx index 3e9b409a70..793ba1f22b 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/online-document-preview.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/online-document-preview.tsx @@ -1,12 +1,12 @@ 'use client' import type { NotionPage } from '@/models/common' +import { toast } from '@langgenius/dify-ui/toast' import { RiCloseLine } from '@remixicon/react' import * as React from 'react' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { Notion } from '@/app/components/base/icons/src/public/common' import { Markdown } from '@/app/components/base/markdown' -import { toast } from '@/app/components/base/ui/toast' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import { usePreviewOnlineDocument } from '@/service/use-pipeline' import { formatNumberAbbreviated } from '@/utils/format' diff --git a/web/app/components/datasets/documents/create-from-pipeline/process-documents/__tests__/components.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/process-documents/__tests__/components.spec.tsx index ff5f8afa66..16b6ef1373 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/process-documents/__tests__/components.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/process-documents/__tests__/components.spec.tsx @@ -11,8 +11,8 @@ const { mockToastError } = vi.hoisted(() => ({ mockToastError: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, toast: { diff --git a/web/app/components/datasets/documents/create-from-pipeline/process-documents/__tests__/form.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/process-documents/__tests__/form.spec.tsx index 09f28fc5da..dc54ba2757 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/process-documents/__tests__/form.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/process-documents/__tests__/form.spec.tsx @@ -8,8 +8,8 @@ const { mockToastError } = vi.hoisted(() => ({ mockToastError: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, toast: { diff --git a/web/app/components/datasets/documents/create-from-pipeline/process-documents/__tests__/header.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/process-documents/__tests__/header.spec.tsx index 7e9eabaeda..431fa76f2c 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/process-documents/__tests__/header.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/process-documents/__tests__/header.spec.tsx @@ -2,7 +2,7 @@ import { fireEvent, render, screen } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import Header from '../header' -vi.mock('@/app/components/base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: ({ children, onClick, disabled, variant }: { children: React.ReactNode, onClick: () => void, disabled?: boolean, variant: string }) => ( , })) -vi.mock('@/app/components/base/ui/alert-dialog', () => ({ +vi.mock('@langgenius/dify-ui/alert-dialog', () => ({ AlertDialog: ({ children, onOpenChange }: AlertDialogProps) => { latestAlertDialogOnOpenChange = onOpenChange return
{children}
diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx index 995da8bb98..a8f004f3a2 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx @@ -9,6 +9,20 @@ import type { FormRefObject, FormSchema, } from '@/app/components/base/form/types' +import { + AlertDialog, + AlertDialogActions, + AlertDialogCancelButton, + AlertDialogConfirmButton, + AlertDialogContent, + AlertDialogTitle, +} from '@langgenius/dify-ui/alert-dialog' +import { Button } from '@langgenius/dify-ui/button' +import { + Dialog, + DialogCloseButton, + DialogContent, +} from '@langgenius/dify-ui/dialog' import { memo, useCallback, @@ -22,20 +36,6 @@ import AuthForm from '@/app/components/base/form/form-scenarios/auth' import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general' import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security' import Loading from '@/app/components/base/loading' -import { - AlertDialog, - AlertDialogActions, - AlertDialogCancelButton, - AlertDialogConfirmButton, - AlertDialogContent, - AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' -import { Button } from '@/app/components/base/ui/button' -import { - Dialog, - DialogCloseButton, - DialogContent, -} from '@/app/components/base/ui/dialog' import { useAuth, useCredentialData, diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.select.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.select.spec.tsx index ae4a68bad7..c221071b82 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.select.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.select.spec.tsx @@ -6,8 +6,8 @@ vi.mock('../../hooks', () => ({ useLanguage: () => 'en_US', })) -vi.mock('@/app/components/base/ui/select', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/select', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.spec.tsx index 2e928c36cc..a57884df1d 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.spec.tsx @@ -11,7 +11,7 @@ vi.mock('../../hooks', () => ({ useLanguage: () => 'en_US', })) -vi.mock('@/app/components/base/ui/slider', () => ({ +vi.mock('@langgenius/dify-ui/slider', () => ({ Slider: ({ onValueChange }: { onValueChange: (v: number) => void }) => ( ), diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/configuration-button.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/configuration-button.tsx index 7545cf6a22..598db04ee9 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/configuration-button.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/configuration-button.tsx @@ -1,5 +1,5 @@ +import { Button } from '@langgenius/dify-ui/button' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import { ConfigurationMethodEnum } from '../declarations' type ConfigurationButtonProps = { diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx index 38b343bc19..6f37775052 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx @@ -14,16 +14,16 @@ import type { NodeOutPutVar, } from '@/app/components/workflow/types' import { cn } from '@langgenius/dify-ui/cn' -import { useMemo, useRef, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows' -import Loading from '@/app/components/base/loading' import { Popover, PopoverClose, PopoverContent, PopoverTrigger, -} from '@/app/components/base/ui/popover' +} from '@langgenius/dify-ui/popover' +import { useMemo, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows' +import Loading from '@/app/components/base/loading' import { PROVIDER_WITH_PRESET_TONE, STOP_PARAMETER_RULE, TONE_LIST } from '@/config' import { useModelParameterRules } from '@/service/use-common' import { diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx index 4cda97031f..f7e1962fd7 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx @@ -4,15 +4,15 @@ import type { NodeOutPutVar, } from '@/app/components/workflow/types' import { cn } from '@langgenius/dify-ui/cn' +import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger, SelectValue } from '@langgenius/dify-ui/select' +import { Slider } from '@langgenius/dify-ui/slider' +import { Switch } from '@langgenius/dify-ui/switch' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import PromptEditor from '@/app/components/base/prompt-editor' import Radio from '@/app/components/base/radio' -import Switch from '@/app/components/base/switch' import TagInput from '@/app/components/base/tag-input' -import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger, SelectValue } from '@/app/components/base/ui/select' -import { Slider } from '@/app/components/base/ui/slider' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' import { BlockEnum } from '@/app/components/workflow/types' import { useLanguage } from '../hooks' import { isNullOrUndefined } from '../utils' diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx index c2138c1c6f..cb537ab18f 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx @@ -1,15 +1,15 @@ import type { ReactNode } from 'react' -import { useTranslation } from 'react-i18next' -import { Brush01 } from '@/app/components/base/icons/src/vender/solid/editor' -import { Scales02 } from '@/app/components/base/icons/src/vender/solid/FinanceAndECommerce' -import { Target04 } from '@/app/components/base/icons/src/vender/solid/general' -import { Button } from '@/app/components/base/ui/button' +import { Button } from '@langgenius/dify-ui/button' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' +} from '@langgenius/dify-ui/dropdown-menu' +import { useTranslation } from 'react-i18next' +import { Brush01 } from '@/app/components/base/icons/src/vender/solid/editor' +import { Scales02 } from '@/app/components/base/icons/src/vender/solid/FinanceAndECommerce' +import { Target04 } from '@/app/components/base/icons/src/vender/solid/general' import { TONE_LIST } from '@/config' const toneI18nKeyMap = { diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx index 06823d1a74..d9cb29abbf 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx @@ -5,8 +5,8 @@ import type { ModelProvider, } from '../declarations' import { cn } from '@langgenius/dify-ui/cn' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useTranslation } from 'react-i18next' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' import { useProviderContext } from '@/context/provider-context' import ModelIcon from '../model-icon' import ModelName from '../model-name' diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popover.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popover.spec.tsx index e92bab1db5..d7501672f4 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popover.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popover.spec.tsx @@ -16,7 +16,7 @@ vi.mock('../../hooks', () => ({ }), })) -vi.mock('@/app/components/base/ui/popover', () => ({ +vi.mock('@langgenius/dify-ui/popover', () => ({ Popover: ({ children, onOpenChange }: PopoverProps) => { latestOnOpenChange = onOpenChange return
{children}
diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup-item.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup-item.spec.tsx index 6728791120..341a9c6abc 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup-item.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup-item.spec.tsx @@ -44,7 +44,7 @@ vi.mock('@/app/components/base/tooltip', () => ({ default: ({ children }: { children: React.ReactNode }) =>
{children}
, })) -vi.mock('@/app/components/base/ui/popover', () => ({ +vi.mock('@langgenius/dify-ui/popover', () => ({ Popover: ({ children }: { children: React.ReactNode }) =>
{children}
, PopoverTrigger: ({ render }: { render: React.ReactNode }) => <>{render}, PopoverContent: ({ children }: { children: React.ReactNode }) =>
{children}
, diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx index 44392a70a5..9241c592f5 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx @@ -5,12 +5,12 @@ import type { ModelFeatureEnum, ModelItem, } from '../declarations' -import { useState } from 'react' import { Popover, PopoverContent, PopoverTrigger, -} from '@/app/components/base/ui/popover' +} from '@langgenius/dify-ui/popover' +import { useState } from 'react' import { useCurrentProviderAndModel } from '../hooks' import ModelSelectorTrigger from './model-selector-trigger' import Popup from './popup' diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/model-selector-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/model-selector-trigger.tsx index d43324ca12..6b9bcae8dc 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/model-selector-trigger.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/model-selector-trigger.tsx @@ -5,8 +5,8 @@ import type { ModelItem, } from '../declarations' import { cn } from '@langgenius/dify-ui/cn' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useTranslation } from 'react-i18next' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' import { useProviderContext } from '@/context/provider-context' import { DERIVED_MODEL_STATUS_BADGE_I18N, diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx index e38cc07ca2..ed16cd7904 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx @@ -5,15 +5,15 @@ import type { ModelItem, } from '../declarations' import { cn } from '@langgenius/dify-ui/cn' -import { useCallback, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { CreditsCoin } from '@/app/components/base/icons/src/vender/line/financeAndECommerce' import { Popover, PopoverContent, PopoverTrigger, -} from '@/app/components/base/ui/popover' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' +} from '@langgenius/dify-ui/popover' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' +import { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { CreditsCoin } from '@/app/components/base/icons/src/vender/line/financeAndECommerce' import { useModalContext } from '@/context/modal-context' import { useProviderContext } from '@/context/provider-context' import { diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx index b40bc53a65..017194aaf5 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx @@ -5,11 +5,11 @@ import type { ModelItem, } from '../declarations' import type { ModelProviderQuotaGetPaid } from '@/types/model-provider' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { useTheme } from 'next-themes' import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' import checkTaskStatus from '@/app/components/plugins/install-plugin/base/check-task-status' import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list' diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/credential-panel.spec.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/credential-panel.spec.tsx index 4f51de9b41..f07f9652f8 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/credential-panel.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/credential-panel.spec.tsx @@ -32,7 +32,7 @@ vi.mock('@/context/global-public-context', () => ({ useSystemFeaturesQuery: () => ({ data: { trial_models: ['langgenius/openai/openai'] } }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ default: { notify: mockToastNotify }, toast: { success: (message: string) => mockToastNotify({ type: 'success', message }), diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-load-balancing-modal.spec.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-load-balancing-modal.spec.tsx index eedc04115c..c4794c9775 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-load-balancing-modal.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-load-balancing-modal.spec.tsx @@ -52,8 +52,8 @@ let mockCredentialData: CredentialData | undefined = { current_credential_name: 'Default', } -vi.mock('@/app/components/base/ui/toast', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, default: { diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/use-change-provider-priority.spec.ts b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/use-change-provider-priority.spec.ts index 57c5121014..7f6e7d393a 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/use-change-provider-priority.spec.ts +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/use-change-provider-priority.spec.ts @@ -16,7 +16,7 @@ const mockMutationOptions = vi.fn((options: Record) => ({ ...options, })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ default: { notify: (...args: unknown[]) => mockNotify(...args), }, diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/dialog.spec.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/dialog.spec.tsx index b4ec0e09d2..85ae2d7e52 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/dialog.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/dialog.spec.tsx @@ -34,7 +34,7 @@ vi.mock('../use-activate-credential', () => ({ }), })) -vi.mock('@/app/components/base/ui/alert-dialog', () => ({ +vi.mock('@langgenius/dify-ui/alert-dialog', () => ({ AlertDialog: ({ children, onOpenChange }: AlertDialogProps) => { latestOnOpenChange = onOpenChange return
{children}
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/use-activate-credential.spec.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/use-activate-credential.spec.tsx index 12acf479c0..aec9c9f691 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/use-activate-credential.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/use-activate-credential.spec.tsx @@ -1,6 +1,6 @@ import type { Credential, ModelProvider } from '../../../declarations' +import { toast } from '@langgenius/dify-ui/toast' import { act, renderHook } from '@testing-library/react' -import { toast } from '@/app/components/base/ui/toast' import { useActivateCredential } from '../use-activate-credential' const mockMutate = vi.fn() diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/api-key-section.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/api-key-section.tsx index e6f5e51d61..41645677b6 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/api-key-section.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/api-key-section.tsx @@ -1,7 +1,7 @@ import type { Credential, CustomModel, ModelProvider } from '../../declarations' +import { Button } from '@langgenius/dify-ui/button' import { memo } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import CredentialItem from '../../model-auth/authorized/credential-item' type ApiKeySectionProps = { diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/dropdown-content.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/dropdown-content.tsx index 415539383b..0e0f3a67b1 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/dropdown-content.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/dropdown-content.tsx @@ -1,7 +1,5 @@ import type { Credential, ModelProvider, PreferredProviderTypeEnum } from '../../declarations' import type { CredentialPanelState } from '../use-credential-panel-state' -import { memo, useCallback } from 'react' -import { useTranslation } from 'react-i18next' import { AlertDialog, AlertDialogActions, @@ -10,7 +8,9 @@ import { AlertDialogContent, AlertDialogDescription, AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' +} from '@langgenius/dify-ui/alert-dialog' +import { memo, useCallback } from 'react' +import { useTranslation } from 'react-i18next' import { ConfigurationMethodEnum } from '../../declarations' import { useAuth } from '../../model-auth/hooks' import ApiKeySection from './api-key-section' diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/index.tsx index fc710ef73f..67f73f6941 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/index.tsx @@ -1,13 +1,13 @@ import type { ModelProvider, PreferredProviderTypeEnum } from '../../declarations' import type { CardVariant, CredentialPanelState } from '../use-credential-panel-state' -import { memo, useCallback, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' +import { Button } from '@langgenius/dify-ui/button' import { Popover, PopoverContent, PopoverTrigger, -} from '@/app/components/base/ui/popover' +} from '@langgenius/dify-ui/popover' +import { memo, useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' import DropdownContent from './dropdown-content' type ModelAuthDropdownProps = { diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/usage-priority-section.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/usage-priority-section.tsx index 638f9d605b..4ec7ce3cef 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/usage-priority-section.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/usage-priority-section.tsx @@ -1,7 +1,7 @@ import type { UsagePriority } from '../use-credential-panel-state' import { cn } from '@langgenius/dify-ui/cn' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useTranslation } from 'react-i18next' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' import { PreferredProviderTypeEnum } from '../../declarations' type UsagePrioritySectionProps = { diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/use-activate-credential.ts b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/use-activate-credential.ts index 6f99c32296..db9f735e3a 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/use-activate-credential.ts +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/use-activate-credential.ts @@ -1,7 +1,7 @@ import type { Credential, ModelProvider } from '../../declarations' +import { toast } from '@langgenius/dify-ui/toast' import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { toast } from '@/app/components/base/ui/toast' import { useActiveProviderCredential } from '@/service/use-models' import { useUpdateModelList, useUpdateModelProviders } from '../../hooks' diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx index d8f0b5928c..305ef71c50 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx @@ -1,12 +1,12 @@ import type { ModelItem, ModelProvider } from '../declarations' import { cn } from '@langgenius/dify-ui/cn' +import { Switch } from '@langgenius/dify-ui/switch' import { useQueryClient } from '@tanstack/react-query' import { useDebounceFn } from 'ahooks' import { memo, useCallback } from 'react' import { useTranslation } from 'react-i18next' import Badge from '@/app/components/base/badge' import { Balance } from '@/app/components/base/icons/src/vender/line/financeAndECommerce' -import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' import { Plan } from '@/app/components/billing/type' import { useAppContext } from '@/context/app-context' diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx index ea4edace30..9b7b858c13 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx @@ -9,11 +9,11 @@ import type { ModelProvider, } from '../declarations' import { cn } from '@langgenius/dify-ui/cn' +import { Switch } from '@langgenius/dify-ui/switch' import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import Badge from '@/app/components/base/badge/index' import GridMask from '@/app/components/base/grid-mask' -import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' import UpgradeBtn from '@/app/components/billing/upgrade-btn' import s from '@/app/components/custom/style.module.css' diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx index 652630be67..34b9d1578f 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx @@ -1,9 +1,4 @@ import type { Credential, CustomConfigurationModelFixedFields, ModelItem, ModelLoadBalancingConfig, ModelLoadBalancingConfigEntry, ModelProvider } from '../declarations' -import { cn } from '@langgenius/dify-ui/cn' -import { memo, useCallback, useEffect, useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import Loading from '@/app/components/base/loading' -import Modal from '@/app/components/base/modal' import { AlertDialog, AlertDialogActions, @@ -11,9 +6,14 @@ import { AlertDialogConfirmButton, AlertDialogContent, AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' +} from '@langgenius/dify-ui/alert-dialog' +import { Button } from '@langgenius/dify-ui/button' +import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' +import { memo, useCallback, useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Loading from '@/app/components/base/loading' +import Modal from '@/app/components/base/modal' import { SwitchCredentialInLoadBalancing } from '@/app/components/header/account-setting/model-provider-page/model-auth' import { useGetModelCredential, useUpdateModelLoadBalancingConfig } from '@/service/use-models' import { ConfigurationMethodEnum, FormTypeEnum } from '../declarations' diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx index e71a219dcd..a74c400035 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx @@ -1,5 +1,6 @@ import type { FC } from 'react' import { Popover, PopoverButton, PopoverPanel, Transition } from '@headlessui/react' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiCheckLine, @@ -7,7 +8,6 @@ import { } from '@remixicon/react' import { Fragment } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import { PreferredProviderTypeEnum } from '../declarations' type SelectorProps = { diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/provider-card-actions.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/provider-card-actions.tsx index 2269354825..861c4afa7a 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/provider-card-actions.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/provider-card-actions.tsx @@ -1,11 +1,11 @@ import type { FC } from 'react' import type { PluginDetail } from '@/app/components/plugins/types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import Badge from '@/app/components/base/badge' -import { Button } from '@/app/components/base/ui/button' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' import { HeaderModals } from '@/app/components/plugins/plugin-detail-panel/detail-header/components' import { useDetailHeaderState, usePluginOperations } from '@/app/components/plugins/plugin-detail-panel/detail-header/hooks' import OperationDropdown from '@/app/components/plugins/plugin-detail-panel/operation-dropdown' diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx index 03d7c28a88..5d31db11f9 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx @@ -3,12 +3,12 @@ import type { ModelProvider } from '../declarations' import type { Plugin } from '@/app/components/plugins/types' import type { ModelProviderQuotaGetPaid } from '@/types/model-provider' import { cn } from '@langgenius/dify-ui/cn' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useBoolean } from 'ahooks' import * as React from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' import { useSystemFeaturesQuery } from '@/context/global-public-context' import useTimestamp from '@/hooks/use-timestamp' diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/use-change-provider-priority.ts b/web/app/components/header/account-setting/model-provider-page/provider-added-card/use-change-provider-priority.ts index 2a9e7e62ac..c339af4b2e 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/use-change-provider-priority.ts +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/use-change-provider-priority.ts @@ -1,7 +1,7 @@ import type { ModelProvider, PreferredProviderTypeEnum } from '../declarations' +import { toast } from '@langgenius/dify-ui/toast' import { useMutation, useQueryClient } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' -import { toast } from '@/app/components/base/ui/toast' import { consoleQuery } from '@/service/client' import { ConfigurationMethodEnum } from '../declarations' import { useUpdateModelList, useUpdateModelProviders } from '../hooks' diff --git a/web/app/components/header/account-setting/model-provider-page/system-model-selector/__tests__/index.spec.tsx b/web/app/components/header/account-setting/model-provider-page/system-model-selector/__tests__/index.spec.tsx index c1b27a2c04..8129ed721c 100644 --- a/web/app/components/header/account-setting/model-provider-page/system-model-selector/__tests__/index.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/system-model-selector/__tests__/index.spec.tsx @@ -43,8 +43,8 @@ vi.mock('@/context/provider-context', () => ({ }), })) -vi.mock('@/app/components/base/ui/toast', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, toast: { diff --git a/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx b/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx index 8b86923391..41bb3c3ab8 100644 --- a/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx @@ -3,21 +3,21 @@ import type { DefaultModel, DefaultModelResponse, } from '../declarations' -import { useState } from 'react' -import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' +import { Button } from '@langgenius/dify-ui/button' import { Dialog, DialogCloseButton, DialogContent, DialogTitle, -} from '@/app/components/base/ui/dialog' -import { toast } from '@/app/components/base/ui/toast' +} from '@langgenius/dify-ui/dialog' +import { toast } from '@langgenius/dify-ui/toast' import { Tooltip, TooltipContent, TooltipTrigger, -} from '@/app/components/base/ui/tooltip' +} from '@langgenius/dify-ui/tooltip' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' import { useAppContext } from '@/context/app-context' import { useProviderContext } from '@/context/provider-context' import { updateDefaultModel } from '@/service/common' diff --git a/web/app/components/header/account-setting/plugin-page/SerpapiPlugin.tsx b/web/app/components/header/account-setting/plugin-page/SerpapiPlugin.tsx index 7308691b54..31c4ce2c3a 100644 --- a/web/app/components/header/account-setting/plugin-page/SerpapiPlugin.tsx +++ b/web/app/components/header/account-setting/plugin-page/SerpapiPlugin.tsx @@ -1,7 +1,7 @@ import type { Form, ValidateValue } from '../key-validator/declarations' import type { PluginProvider } from '@/models/common' +import { toast } from '@langgenius/dify-ui/toast' import { useTranslation } from 'react-i18next' -import { toast } from '@/app/components/base/ui/toast' import { useAppContext } from '@/context/app-context' import SerpapiLogo from '../../assets/serpapi.png' import KeyValidator from '../key-validator' diff --git a/web/app/components/header/account-setting/plugin-page/__tests__/SerpapiPlugin.spec.tsx b/web/app/components/header/account-setting/plugin-page/__tests__/SerpapiPlugin.spec.tsx index 85d205713b..2055c0725d 100644 --- a/web/app/components/header/account-setting/plugin-page/__tests__/SerpapiPlugin.spec.tsx +++ b/web/app/components/header/account-setting/plugin-page/__tests__/SerpapiPlugin.spec.tsx @@ -32,7 +32,7 @@ const { mockToast } = vi.hoisted(() => { return { mockToast } }) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: mockToast, })) diff --git a/web/app/components/header/account-setting/plugin-page/__tests__/index.spec.tsx b/web/app/components/header/account-setting/plugin-page/__tests__/index.spec.tsx index ff58fdd182..ca0c41a83b 100644 --- a/web/app/components/header/account-setting/plugin-page/__tests__/index.spec.tsx +++ b/web/app/components/header/account-setting/plugin-page/__tests__/index.spec.tsx @@ -14,8 +14,8 @@ vi.mock('@/context/app-context', () => ({ useAppContext: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, diff --git a/web/app/components/plugins/install-plugin/__tests__/hooks.spec.ts b/web/app/components/plugins/install-plugin/__tests__/hooks.spec.ts index b4171de7f0..385b9e01e1 100644 --- a/web/app/components/plugins/install-plugin/__tests__/hooks.spec.ts +++ b/web/app/components/plugins/install-plugin/__tests__/hooks.spec.ts @@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { checkForUpdates, fetchReleases, handleUpload } from '../hooks' const mockNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign((...args: unknown[]) => mockNotify(...args), { success: (...args: unknown[]) => mockNotify(...args), error: (...args: unknown[]) => mockNotify(...args), diff --git a/web/app/components/plugins/install-plugin/base/installed.tsx b/web/app/components/plugins/install-plugin/base/installed.tsx index 088d587378..7b76767e77 100644 --- a/web/app/components/plugins/install-plugin/base/installed.tsx +++ b/web/app/components/plugins/install-plugin/base/installed.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' import type { Plugin, PluginDeclaration, PluginManifestInMarket } from '../../types' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useTranslation } from 'react-i18next' import Badge, { BadgeState } from '@/app/components/base/badge/index' -import { Button } from '@/app/components/base/ui/button' import Card from '../../card' import { pluginManifestInMarketToPluginProps, pluginManifestToCardPluginProps } from '../utils' diff --git a/web/app/components/plugins/install-plugin/hooks.ts b/web/app/components/plugins/install-plugin/hooks.ts index 4759953e30..e7086a7fba 100644 --- a/web/app/components/plugins/install-plugin/hooks.ts +++ b/web/app/components/plugins/install-plugin/hooks.ts @@ -1,5 +1,5 @@ import type { GitHubRepoReleaseResponse } from '../types' -import { toast } from '@/app/components/base/ui/toast' +import { toast } from '@langgenius/dify-ui/toast' import { uploadGitHub } from '@/service/plugins' import { compareVersion, getLatestVersion } from '@/utils/semver' diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx index df94331d58..a94cd8588d 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx @@ -2,12 +2,12 @@ import type { FC } from 'react' import type { Dependency, InstallStatus, InstallStatusResponse, Plugin, VersionInfo } from '../../../types' import type { ExposeRefs } from './install-multi' +import { Button } from '@langgenius/dify-ui/button' import { RiLoader2Line } from '@remixicon/react' import * as React from 'react' import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Checkbox from '@/app/components/base/checkbox' -import { Button } from '@/app/components/base/ui/button' import { useCanInstallPluginFromMarketplace } from '@/app/components/plugins/plugin-page/use-reference-setting' import { useMittContextSelector } from '@/context/mitt-context' import { useInstallOrUpdate, usePluginTaskList } from '@/service/use-plugins' diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/installed.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/installed.tsx index 1c64e73d62..3fbf0c13ec 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/steps/installed.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/steps/installed.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' import type { InstallStatus, Plugin } from '../../../types' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useTranslation } from 'react-i18next' import Badge, { BadgeState } from '@/app/components/base/badge/index' -import { Button } from '@/app/components/base/ui/button' import Card from '@/app/components/plugins/card' import { MARKETPLACE_API_PREFIX } from '@/config' import useGetIcon from '../../base/use-get-icon' diff --git a/web/app/components/plugins/install-plugin/install-from-github/__tests__/index.spec.tsx b/web/app/components/plugins/install-plugin/install-from-github/__tests__/index.spec.tsx index 2f35f484f0..e923de5e38 100644 --- a/web/app/components/plugins/install-plugin/install-from-github/__tests__/index.spec.tsx +++ b/web/app/components/plugins/install-plugin/install-from-github/__tests__/index.spec.tsx @@ -57,7 +57,7 @@ const createUpdatePayload = (overrides: Partial = {}): // Mock external dependencies const mockNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign((props: { type: string, message: string }) => mockNotify(props), { success: (message: string) => mockNotify({ type: 'success', message }), error: (message: string) => mockNotify({ type: 'error', message }), diff --git a/web/app/components/plugins/install-plugin/install-from-github/index.tsx b/web/app/components/plugins/install-plugin/install-from-github/index.tsx index f1e4d1b9bf..4ac0a3aa7f 100644 --- a/web/app/components/plugins/install-plugin/install-from-github/index.tsx +++ b/web/app/components/plugins/install-plugin/install-from-github/index.tsx @@ -4,11 +4,11 @@ import type { PluginDeclaration, UpdateFromGitHubPayload } from '../../types' import type { Item } from '@/app/components/base/select' import type { InstallState } from '@/app/components/plugins/types' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import * as React from 'react' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' -import { toast } from '@/app/components/base/ui/toast' import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' import { InstallStepFromGitHub } from '../../types' import Installed from '../base/installed' diff --git a/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx b/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx index c044445f19..ff7630e3a2 100644 --- a/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx +++ b/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx @@ -1,11 +1,11 @@ 'use client' import type { Plugin, PluginDeclaration, UpdateFromGitHubPayload } from '../../../types' +import { Button } from '@langgenius/dify-ui/button' import { RiLoader2Line } from '@remixicon/react' import * as React from 'react' import { useEffect } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed' import { updateFromGitHub } from '@/service/plugins' import { useInstallPackageFromGitHub, usePluginTaskList } from '@/service/use-plugins' diff --git a/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx b/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx index a8236b4b93..18c33def82 100644 --- a/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx +++ b/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx @@ -2,10 +2,10 @@ import type { PluginDeclaration, UpdateFromGitHubPayload } from '../../../types' import type { Item } from '@/app/components/base/select' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useTranslation } from 'react-i18next' import { PortalSelect } from '@/app/components/base/select' -import { Button } from '@/app/components/base/ui/button' import { handleUpload } from '../../hooks' const i18nPrefix = 'installFromGitHub' diff --git a/web/app/components/plugins/install-plugin/install-from-github/steps/setURL.tsx b/web/app/components/plugins/install-plugin/install-from-github/steps/setURL.tsx index 2a7012d40c..f1b149a8bf 100644 --- a/web/app/components/plugins/install-plugin/install-from-github/steps/setURL.tsx +++ b/web/app/components/plugins/install-plugin/install-from-github/steps/setURL.tsx @@ -1,8 +1,8 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' type SetURLProps = { repoUrl: string diff --git a/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx b/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx index 9ef52d7a5d..7c0a90f179 100644 --- a/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx +++ b/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx @@ -1,11 +1,11 @@ 'use client' import type { FC } from 'react' import type { PluginDeclaration } from '../../../types' +import { Button } from '@langgenius/dify-ui/button' import { RiLoader2Line } from '@remixicon/react' import * as React from 'react' import { useEffect, useMemo } from 'react' import { Trans, useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed' import { useAppContext } from '@/context/app-context' import { uninstallPlugin } from '@/service/plugins' diff --git a/web/app/components/plugins/install-plugin/install-from-local-package/steps/uploading.tsx b/web/app/components/plugins/install-plugin/install-from-local-package/steps/uploading.tsx index f9f9969817..e7c3b4cd0f 100644 --- a/web/app/components/plugins/install-plugin/install-from-local-package/steps/uploading.tsx +++ b/web/app/components/plugins/install-plugin/install-from-local-package/steps/uploading.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' import type { Dependency, PluginDeclaration } from '../../../types' +import { Button } from '@langgenius/dify-ui/button' import { RiLoader2Line } from '@remixicon/react' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import { uploadFile } from '@/service/plugins' import Card from '../../../card' diff --git a/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx b/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx index ac8b458406..91397f3189 100644 --- a/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx +++ b/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx @@ -1,11 +1,11 @@ 'use client' import type { FC } from 'react' import type { Plugin, PluginManifestInMarket } from '../../../types' +import { Button } from '@langgenius/dify-ui/button' import { RiLoader2Line } from '@remixicon/react' import * as React from 'react' import { useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed' import { useAppContext } from '@/context/app-context' import { useInstallPackageFromMarketPlace, usePluginDeclarationFromMarketPlace, usePluginTaskList, useUpdatePackageFromMarketPlace } from '@/service/use-plugins' diff --git a/web/app/components/plugins/marketplace/list/card-wrapper.tsx b/web/app/components/plugins/marketplace/list/card-wrapper.tsx index 6cab636392..9dc5bc3d78 100644 --- a/web/app/components/plugins/marketplace/list/card-wrapper.tsx +++ b/web/app/components/plugins/marketplace/list/card-wrapper.tsx @@ -1,12 +1,12 @@ 'use client' import type { Plugin } from '@/app/components/plugins/types' import { useLocale, useTranslation } from '#i18n' +import { Button } from '@langgenius/dify-ui/button' import { RiArrowRightUpLine } from '@remixicon/react' import { useBoolean } from 'ahooks' import { useTheme } from 'next-themes' import * as React from 'react' import { useMemo } from 'react' -import { Button } from '@/app/components/base/ui/button' import Card from '@/app/components/plugins/card' import CardMoreInfo from '@/app/components/plugins/card/card-more-info' import { useTags } from '@/app/components/plugins/hooks' diff --git a/web/app/components/plugins/marketplace/sort-dropdown/__tests__/index.spec.tsx b/web/app/components/plugins/marketplace/sort-dropdown/__tests__/index.spec.tsx index 990bb321de..5bf5b6bb99 100644 --- a/web/app/components/plugins/marketplace/sort-dropdown/__tests__/index.spec.tsx +++ b/web/app/components/plugins/marketplace/sort-dropdown/__tests__/index.spec.tsx @@ -31,7 +31,7 @@ vi.mock('../../atoms', () => ({ useMarketplaceSort: () => [mockSort, mockHandleSortChange], })) -vi.mock('@/app/components/base/ui/dropdown-menu', async () => { +vi.mock('@langgenius/dify-ui/dropdown-menu', async () => { const React = await import('react') const DropdownMenuContext = React.createContext<{ open: boolean, setOpen: (open: boolean) => void } | null>(null) diff --git a/web/app/components/plugins/marketplace/sort-dropdown/index.tsx b/web/app/components/plugins/marketplace/sort-dropdown/index.tsx index a47143de02..b8f5467fa1 100644 --- a/web/app/components/plugins/marketplace/sort-dropdown/index.tsx +++ b/web/app/components/plugins/marketplace/sort-dropdown/index.tsx @@ -1,12 +1,12 @@ 'use client' import { useTranslation } from '#i18n' -import { useState } from 'react' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' +} from '@langgenius/dify-ui/dropdown-menu' +import { useState } from 'react' import { useMarketplaceSort } from '../atoms' const SortDropdown = () => { diff --git a/web/app/components/plugins/plugin-auth/authorize/__tests__/api-key-modal.spec.tsx b/web/app/components/plugins/plugin-auth/authorize/__tests__/api-key-modal.spec.tsx index a4ddec8f51..2bfa94d2ed 100644 --- a/web/app/components/plugins/plugin-auth/authorize/__tests__/api-key-modal.spec.tsx +++ b/web/app/components/plugins/plugin-auth/authorize/__tests__/api-key-modal.spec.tsx @@ -15,7 +15,7 @@ const mockToast = { promise: vi.fn(), } -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: mockToast, })) const mockAddPluginCredential = vi.fn().mockResolvedValue({}) diff --git a/web/app/components/plugins/plugin-auth/authorize/__tests__/authorize-components.spec.tsx b/web/app/components/plugins/plugin-auth/authorize/__tests__/authorize-components.spec.tsx index c495d5501a..cba5c60654 100644 --- a/web/app/components/plugins/plugin-auth/authorize/__tests__/authorize-components.spec.tsx +++ b/web/app/components/plugins/plugin-auth/authorize/__tests__/authorize-components.spec.tsx @@ -104,7 +104,7 @@ const mockToast = { promise: vi.fn(), } -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: mockToast, })) // Factory function for creating test PluginPayload diff --git a/web/app/components/plugins/plugin-auth/authorize/__tests__/oauth-client-settings.spec.tsx b/web/app/components/plugins/plugin-auth/authorize/__tests__/oauth-client-settings.spec.tsx index 5f9b6f5695..2c86820202 100644 --- a/web/app/components/plugins/plugin-auth/authorize/__tests__/oauth-client-settings.spec.tsx +++ b/web/app/components/plugins/plugin-auth/authorize/__tests__/oauth-client-settings.spec.tsx @@ -14,7 +14,7 @@ const mockToast = { promise: vi.fn(), } -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: mockToast, })) const mockSetPluginOAuthCustomClient = vi.fn().mockResolvedValue({}) diff --git a/web/app/components/plugins/plugin-auth/authorize/add-api-key-button.tsx b/web/app/components/plugins/plugin-auth/authorize/add-api-key-button.tsx index 2db1751bdc..648a87dabc 100644 --- a/web/app/components/plugins/plugin-auth/authorize/add-api-key-button.tsx +++ b/web/app/components/plugins/plugin-auth/authorize/add-api-key-button.tsx @@ -1,11 +1,11 @@ +import type { ButtonProps } from '@langgenius/dify-ui/button' import type { PluginPayload } from '../types' import type { FormSchema } from '@/app/components/base/form/types' -import type { ButtonProps } from '@/app/components/base/ui/button' +import { Button } from '@langgenius/dify-ui/button' import { memo, useState, } from 'react' -import { Button } from '@/app/components/base/ui/button' import ApiKeyModal from './api-key-modal' export type AddApiKeyButtonProps = { diff --git a/web/app/components/plugins/plugin-auth/authorize/add-oauth-button.tsx b/web/app/components/plugins/plugin-auth/authorize/add-oauth-button.tsx index 31e87564e7..44b48db7a2 100644 --- a/web/app/components/plugins/plugin-auth/authorize/add-oauth-button.tsx +++ b/web/app/components/plugins/plugin-auth/authorize/add-oauth-button.tsx @@ -1,6 +1,7 @@ +import type { ButtonProps } from '@langgenius/dify-ui/button' import type { PluginPayload } from '../types' import type { FormSchema } from '@/app/components/base/form/types' -import type { ButtonProps } from '@/app/components/base/ui/button' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiClipboardLine, @@ -17,7 +18,6 @@ import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Badge from '@/app/components/base/badge' import { FormTypeEnum } from '@/app/components/base/form/types' -import { Button } from '@/app/components/base/ui/button' import { useRenderI18nObject } from '@/hooks/use-i18n' import { openOAuthPopup } from '@/hooks/use-oauth' import { diff --git a/web/app/components/plugins/plugin-auth/authorize/api-key-modal.tsx b/web/app/components/plugins/plugin-auth/authorize/api-key-modal.tsx index 5d8eafbe67..db513ecb6f 100644 --- a/web/app/components/plugins/plugin-auth/authorize/api-key-modal.tsx +++ b/web/app/components/plugins/plugin-auth/authorize/api-key-modal.tsx @@ -3,6 +3,7 @@ import type { FormRefObject, FormSchema, } from '@/app/components/base/form/types' +import { toast } from '@langgenius/dify-ui/toast' import { memo, useCallback, @@ -16,7 +17,6 @@ import AuthForm from '@/app/components/base/form/form-scenarios/auth' import { FormTypeEnum } from '@/app/components/base/form/types' import Loading from '@/app/components/base/loading' import Modal from '@/app/components/base/modal/modal' -import { toast } from '@/app/components/base/ui/toast' import { ReadmeEntrance } from '../../readme-panel/entrance' import { ReadmeShowType } from '../../readme-panel/store' import { diff --git a/web/app/components/plugins/plugin-auth/authorize/oauth-client-settings.tsx b/web/app/components/plugins/plugin-auth/authorize/oauth-client-settings.tsx index 625fcaa980..f52b76866a 100644 --- a/web/app/components/plugins/plugin-auth/authorize/oauth-client-settings.tsx +++ b/web/app/components/plugins/plugin-auth/authorize/oauth-client-settings.tsx @@ -3,6 +3,8 @@ import type { FormRefObject, FormSchema, } from '@/app/components/base/form/types' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { useForm, useStore, @@ -16,8 +18,6 @@ import { import { useTranslation } from 'react-i18next' import AuthForm from '@/app/components/base/form/form-scenarios/auth' import Modal from '@/app/components/base/modal/modal' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { ReadmeEntrance } from '../../readme-panel/entrance' import { ReadmeShowType } from '../../readme-panel/store' import { diff --git a/web/app/components/plugins/plugin-auth/authorized-in-data-source-node.tsx b/web/app/components/plugins/plugin-auth/authorized-in-data-source-node.tsx index 548b1fb175..13044d7bbc 100644 --- a/web/app/components/plugins/plugin-auth/authorized-in-data-source-node.tsx +++ b/web/app/components/plugins/plugin-auth/authorized-in-data-source-node.tsx @@ -1,10 +1,10 @@ +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiEqualizer2Line } from '@remixicon/react' import { memo, } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import Indicator from '@/app/components/header/indicator' type AuthorizedInDataSourceNodeProps = { diff --git a/web/app/components/plugins/plugin-auth/authorized-in-node.tsx b/web/app/components/plugins/plugin-auth/authorized-in-node.tsx index 048bbf3e8f..e4f55e82e3 100644 --- a/web/app/components/plugins/plugin-auth/authorized-in-node.tsx +++ b/web/app/components/plugins/plugin-auth/authorized-in-node.tsx @@ -2,6 +2,7 @@ import type { Credential, PluginPayload, } from './types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiArrowDownSLine } from '@remixicon/react' import { @@ -10,7 +11,6 @@ import { useState, } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import Indicator from '@/app/components/header/indicator' import { Authorized, diff --git a/web/app/components/plugins/plugin-auth/authorized/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-auth/authorized/__tests__/index.spec.tsx index a127067526..01e195b21b 100644 --- a/web/app/components/plugins/plugin-auth/authorized/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-auth/authorized/__tests__/index.spec.tsx @@ -57,7 +57,7 @@ const toastMocks = vi.hoisted(() => ({ promise: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign(toastMocks.call, { success: vi.fn((message: string, options?: Record) => toastMocks.call({ type: 'success', message, ...options })), error: vi.fn((message: string, options?: Record) => toastMocks.call({ type: 'error', message, ...options })), diff --git a/web/app/components/plugins/plugin-auth/authorized/index.tsx b/web/app/components/plugins/plugin-auth/authorized/index.tsx index 4729909a1f..fed2873b98 100644 --- a/web/app/components/plugins/plugin-auth/authorized/index.tsx +++ b/web/app/components/plugins/plugin-auth/authorized/index.tsx @@ -2,7 +2,17 @@ import type { Credential, PluginPayload } from '../types' import type { PortalToFollowElemOptions, } from '@/app/components/base/portal-to-follow-elem' +import { + AlertDialog, + AlertDialogActions, + AlertDialogCancelButton, + AlertDialogConfirmButton, + AlertDialogContent, + AlertDialogTitle, +} from '@langgenius/dify-ui/alert-dialog' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowDownSLine, } from '@remixicon/react' @@ -18,16 +28,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { - AlertDialog, - AlertDialogActions, - AlertDialogCancelButton, - AlertDialogConfirmButton, - AlertDialogContent, - AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import Indicator from '@/app/components/header/indicator' import Authorize from '../authorize' import ApiKeyModal from '../authorize/api-key-modal' diff --git a/web/app/components/plugins/plugin-auth/authorized/item.tsx b/web/app/components/plugins/plugin-auth/authorized/item.tsx index 82e3fc4563..9193238a55 100644 --- a/web/app/components/plugins/plugin-auth/authorized/item.tsx +++ b/web/app/components/plugins/plugin-auth/authorized/item.tsx @@ -1,4 +1,5 @@ import type { Credential } from '../types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiCheckLine, @@ -16,7 +17,6 @@ import ActionButton from '@/app/components/base/action-button' import Badge from '@/app/components/base/badge' import Input from '@/app/components/base/input' import Tooltip from '@/app/components/base/tooltip' -import { Button } from '@/app/components/base/ui/button' import Indicator from '@/app/components/header/indicator' import { CredentialTypeEnum } from '../types' diff --git a/web/app/components/plugins/plugin-auth/hooks/__tests__/use-plugin-auth-action.spec.ts b/web/app/components/plugins/plugin-auth/hooks/__tests__/use-plugin-auth-action.spec.ts index ddf8f83019..06af4a88ee 100644 --- a/web/app/components/plugins/plugin-auth/hooks/__tests__/use-plugin-auth-action.spec.ts +++ b/web/app/components/plugins/plugin-auth/hooks/__tests__/use-plugin-auth-action.spec.ts @@ -16,7 +16,7 @@ const toastMocks = vi.hoisted(() => ({ promise: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign(toastMocks.call, { success: vi.fn((message: string, options?: Record) => toastMocks.call({ type: 'success', message, ...options })), error: vi.fn((message: string, options?: Record) => toastMocks.call({ type: 'error', message, ...options })), diff --git a/web/app/components/plugins/plugin-auth/hooks/use-plugin-auth-action.ts b/web/app/components/plugins/plugin-auth/hooks/use-plugin-auth-action.ts index b4714ff96c..7c63753ee8 100644 --- a/web/app/components/plugins/plugin-auth/hooks/use-plugin-auth-action.ts +++ b/web/app/components/plugins/plugin-auth/hooks/use-plugin-auth-action.ts @@ -1,11 +1,11 @@ import type { PluginPayload } from '../types' +import { toast } from '@langgenius/dify-ui/toast' import { useCallback, useRef, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { toast } from '@/app/components/base/ui/toast' import { useDeletePluginCredentialHook, useSetPluginDefaultCredentialHook, diff --git a/web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx b/web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx index f54093f0f9..fd698f811c 100644 --- a/web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx +++ b/web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx @@ -2,6 +2,7 @@ import type { Credential, PluginPayload, } from './types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiArrowDownSLine } from '@remixicon/react' import { @@ -10,7 +11,6 @@ import { useState, } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import Indicator from '@/app/components/header/indicator' import Authorize from './authorize' import Authorized from './authorized' diff --git a/web/app/components/plugins/plugin-auth/plugin-auth-in-datasource-node.tsx b/web/app/components/plugins/plugin-auth/plugin-auth-in-datasource-node.tsx index e3df399a1c..5cdc893ddd 100644 --- a/web/app/components/plugins/plugin-auth/plugin-auth-in-datasource-node.tsx +++ b/web/app/components/plugins/plugin-auth/plugin-auth-in-datasource-node.tsx @@ -1,8 +1,8 @@ import type { ReactNode } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { RiAddLine } from '@remixicon/react' import { memo } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' type PluginAuthInDataSourceNodeProps = { children?: ReactNode diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/detail-header.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/detail-header.spec.tsx index 0eacbf3bd3..63cf25d039 100644 --- a/web/app/components/plugins/plugin-detail-panel/__tests__/detail-header.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/__tests__/detail-header.spec.tsx @@ -17,7 +17,7 @@ const { mockToast } = vi.hoisted(() => ({ }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: mockToast, })) diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx index 15c231cb17..1dab8bdf84 100644 --- a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx @@ -10,7 +10,7 @@ const mockDeleteEndpoint = vi.fn() const mockUpdateEndpoint = vi.fn() const mockToastNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign( (message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }), { diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-modal.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-modal.spec.tsx index 0be17f07d4..7ccd5065e5 100644 --- a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-modal.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-modal.spec.tsx @@ -6,7 +6,7 @@ import EndpointModal from '../endpoint-modal' const mockToastNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign( (message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }), { diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/operation-dropdown.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/operation-dropdown.spec.tsx index fa3d6f2266..db6ff57957 100644 --- a/web/app/components/plugins/plugin-detail-panel/__tests__/operation-dropdown.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/__tests__/operation-dropdown.spec.tsx @@ -14,7 +14,7 @@ vi.mock('@langgenius/dify-ui/cn', () => ({ cn: (...args: (string | undefined | false | null)[]) => args.filter(Boolean).join(' '), })) -vi.mock('@/app/components/base/ui/dropdown-menu', () => ({ +vi.mock('@langgenius/dify-ui/dropdown-menu', () => ({ DropdownMenu: ({ children, open }: { children: ReactNode, open: boolean }) => (
{children}
), diff --git a/web/app/components/plugins/plugin-detail-panel/datasource-action-list.tsx b/web/app/components/plugins/plugin-detail-panel/datasource-action-list.tsx index cafdb7b4b9..5bba7b823b 100644 --- a/web/app/components/plugins/plugin-detail-panel/datasource-action-list.tsx +++ b/web/app/components/plugins/plugin-detail-panel/datasource-action-list.tsx @@ -1,5 +1,5 @@ // import { useAppContext } from '@/context/app-context' -// import { Button } from '@/app/components/base/ui/button' +// import { Button } from '@langgenius/dify-ui/button' // import Indicator from '@/app/components/header/indicator' // import ToolItem from '@/app/components/tools/provider/tool-item' // import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials' diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header/__tests__/index.spec.tsx index 84a61cd643..24838fea59 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header/__tests__/index.spec.tsx @@ -45,7 +45,7 @@ vi.mock('@/app/components/base/action-button', () => ({ ), })) -vi.mock('@/app/components/base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: ({ children, onClick }: { children: React.ReactNode, onClick?: () => void }) => ( ), @@ -57,7 +57,7 @@ vi.mock('@/app/components/base/badge', () => ({ ), })) -vi.mock('@/app/components/base/ui/tooltip', () => ({ +vi.mock('@langgenius/dify-ui/tooltip', () => ({ Tooltip: ({ children }: { children: React.ReactNode }) =>
{children}
, TooltipTrigger: ({ render }: { render: React.ReactNode }) => <>{render}, TooltipContent: ({ children }: { children: React.ReactNode }) =>
{children}
, diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/components/header-modals.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header/components/header-modals.tsx index 3bf24a19b7..c2f6bb8210 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header/components/header-modals.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header/components/header-modals.tsx @@ -3,7 +3,6 @@ import type { FC } from 'react' import type { PluginDetail } from '../../../types' import type { ModalStates, VersionTarget } from '../hooks' -import { useTranslation } from 'react-i18next' import { AlertDialog, AlertDialogActions, @@ -12,7 +11,8 @@ import { AlertDialogContent, AlertDialogDescription, AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' +} from '@langgenius/dify-ui/alert-dialog' +import { useTranslation } from 'react-i18next' import PluginInfo from '@/app/components/plugins/plugin-page/plugin-info' import UpdateFromMarketplace from '@/app/components/plugins/update-plugin/from-market-place' import { useGetLanguage } from '@/context/i18n' diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/__tests__/use-plugin-operations.spec.ts b/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/__tests__/use-plugin-operations.spec.ts index b8873f1087..855530bf18 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/__tests__/use-plugin-operations.spec.ts +++ b/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/__tests__/use-plugin-operations.spec.ts @@ -33,7 +33,7 @@ const { } }) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign( (message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }), { diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/use-plugin-operations.ts b/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/use-plugin-operations.ts index 765c0e8a4e..62c762a35d 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/use-plugin-operations.ts +++ b/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/use-plugin-operations.ts @@ -2,10 +2,10 @@ import type { PluginDetail } from '../../../types' import type { ModalStates, VersionTarget } from './use-detail-header-state' +import { toast } from '@langgenius/dify-ui/toast' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { trackEvent } from '@/app/components/base/amplitude' -import { toast } from '@/app/components/base/ui/toast' import { useModalContext } from '@/context/modal-context' import { useProviderContext } from '@/context/provider-context' import { uninstallPlugin } from '@/service/plugins' diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx index 5a89afa2c9..1ed91a93f5 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx @@ -1,13 +1,13 @@ 'use client' import type { PluginDetail } from '../../types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Badge from '@/app/components/base/badge' -import { Button } from '@/app/components/base/ui/button' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' import { AuthCategory, PluginAuth } from '@/app/components/plugins/plugin-auth' import OperationDropdown from '@/app/components/plugins/plugin-detail-panel/operation-dropdown' import PluginVersionPicker from '@/app/components/plugins/update-plugin/plugin-version-picker' diff --git a/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx b/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx index a8729763ec..e1adc6282d 100644 --- a/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx +++ b/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx @@ -1,4 +1,14 @@ import type { EndpointListItem, PluginDetail } from '../types' +import { + AlertDialog, + AlertDialogActions, + AlertDialogCancelButton, + AlertDialogConfirmButton, + AlertDialogContent, + AlertDialogTitle, +} from '@langgenius/dify-ui/alert-dialog' +import { Switch } from '@langgenius/dify-ui/switch' +import { toast } from '@langgenius/dify-ui/toast' import { RiClipboardLine, RiDeleteBinLine, RiEditLine, RiLoginCircleLine } from '@remixicon/react' import { useBoolean } from 'ahooks' import copy from 'copy-to-clipboard' @@ -7,17 +17,7 @@ import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import { CopyCheck } from '@/app/components/base/icons/src/vender/line/files' -import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' -import { - AlertDialog, - AlertDialogActions, - AlertDialogCancelButton, - AlertDialogConfirmButton, - AlertDialogContent, - AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' -import { toast } from '@/app/components/base/ui/toast' import Indicator from '@/app/components/header/indicator' import { addDefaultValue, toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema' import { diff --git a/web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx b/web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx index c3c09db786..2d11305f6e 100644 --- a/web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx +++ b/web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx @@ -1,5 +1,6 @@ import type { PluginDetail } from '@/app/components/plugins/types' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiAddLine, RiApps2AddLine, @@ -11,7 +12,6 @@ import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Tooltip from '@/app/components/base/tooltip' -import { toast } from '@/app/components/base/ui/toast' import { toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema' import { useDocLink } from '@/context/i18n' import { diff --git a/web/app/components/plugins/plugin-detail-panel/endpoint-modal.tsx b/web/app/components/plugins/plugin-detail-panel/endpoint-modal.tsx index 4c694d4293..0fe0fff6df 100644 --- a/web/app/components/plugins/plugin-detail-panel/endpoint-modal.tsx +++ b/web/app/components/plugins/plugin-detail-panel/endpoint-modal.tsx @@ -2,14 +2,14 @@ import type { FC } from 'react' import type { FormSchema } from '../../base/form/types' import type { PluginDetail } from '../types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowRightUpLine, RiCloseLine } from '@remixicon/react' import * as React from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Drawer from '@/app/components/base/drawer' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form' import { useRenderI18nObject } from '@/hooks/use-i18n' import { ReadmeEntrance } from '../readme-panel/entrance' diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/index.spec.tsx index 107d42ada2..00ed9eb4f1 100644 --- a/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/index.spec.tsx @@ -9,7 +9,7 @@ import ModelParameterModal from '../index' // ==================== Mock Setup ==================== const mockToastNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign( (message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }), { diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/tts-params-panel.spec.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/tts-params-panel.spec.tsx index e423359872..1d000f7c35 100644 --- a/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/tts-params-panel.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/tts-params-panel.spec.tsx @@ -26,8 +26,8 @@ const MockSelectContext = React.createContext<{ onValueChange: () => {}, }) -vi.mock('@/app/components/base/ui/select', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/select', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx index 5f5c0494a9..d0dcab18ea 100644 --- a/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx @@ -9,14 +9,14 @@ import type { } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { TriggerProps } from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger' import { cn } from '@langgenius/dify-ui/cn' -import { useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' import { Popover, PopoverContent, PopoverTrigger, -} from '@/app/components/base/ui/popover' -import { toast } from '@/app/components/base/ui/toast' +} from '@langgenius/dify-ui/popover' +import { toast } from '@langgenius/dify-ui/toast' +import { useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' import { ModelStatusEnum, ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useModelList, diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/tts-params-panel.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/tts-params-panel.tsx index 1ab7ea2f11..7edf2f5d18 100644 --- a/web/app/components/plugins/plugin-detail-panel/model-selector/tts-params-panel.tsx +++ b/web/app/components/plugins/plugin-detail-panel/model-selector/tts-params-panel.tsx @@ -1,7 +1,7 @@ +import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger, SelectValue } from '@langgenius/dify-ui/select' import * as React from 'react' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger, SelectValue } from '@/app/components/base/ui/select' import { languages } from '@/i18n-config/language' type Props = { diff --git a/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx b/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx index 4b36e4612c..c7a1529a3e 100644 --- a/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx +++ b/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx @@ -1,16 +1,16 @@ 'use client' +import type { Placement } from '@langgenius/dify-ui/dropdown-menu' import type { FC } from 'react' -import type { Placement } from '@/app/components/base/ui/placement' import { cn } from '@langgenius/dify-ui/cn' -import * as React from 'react' -import { useTranslation } from 'react-i18next' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' +} from '@langgenius/dify-ui/dropdown-menu' +import * as React from 'react' +import { useTranslation } from 'react-i18next' import { useGlobalPublicStore } from '@/context/global-public-context' import { PluginSource } from '../types' diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/delete-confirm.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/delete-confirm.spec.tsx index f083c24f42..c2cc608b45 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/delete-confirm.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/delete-confirm.spec.tsx @@ -15,8 +15,8 @@ vi.mock('@/service/use-triggers', () => ({ useDeleteTriggerSubscription: () => ({ mutate: mockDelete, isPending: false }), })) -vi.mock('@/app/components/base/ui/toast', async (importOriginal) => { - const actual = await importOriginal() +vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => { + const actual = await importOriginal() return { ...actual, toast: { diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/log-viewer.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/log-viewer.spec.tsx index 351c1f9d2d..3594c10ce2 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/log-viewer.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/log-viewer.spec.tsx @@ -6,7 +6,7 @@ import LogViewer from '../log-viewer' const mockToastNotify = vi.fn() const mockWriteText = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign( (message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }), { diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-entry.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-entry.spec.tsx index 3c4ff83fc8..37d828591f 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-entry.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-entry.spec.tsx @@ -26,7 +26,7 @@ vi.mock('@/service/use-triggers', () => ({ useDeleteTriggerSubscription: () => ({ mutate: vi.fn(), isPending: false }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign(vi.fn(), { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-view.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-view.spec.tsx index 44cec53e28..2256e90e9d 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-view.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-view.spec.tsx @@ -25,7 +25,7 @@ vi.mock('@/service/use-triggers', () => ({ useDeleteTriggerSubscription: () => ({ mutate: mockDelete, isPending: false }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign(vi.fn(), { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/subscription-card.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/subscription-card.spec.tsx index 19edb65dfb..9a89aab7cf 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/subscription-card.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/subscription-card.spec.tsx @@ -29,7 +29,7 @@ vi.mock('@/service/use-triggers', () => ({ useDeleteTriggerSubscription: () => ({ mutate: vi.fn(), isPending: false }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign(vi.fn(), { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/common-modal.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/common-modal.spec.tsx index 72532ea38d..459b657f3f 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/common-modal.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/common-modal.spec.tsx @@ -122,7 +122,7 @@ vi.mock('@/utils/urlValidation', () => ({ })) const mockToastNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign((params: unknown) => mockToastNotify(params), { success: (message: unknown) => mockToastNotify({ type: 'success', message }), error: (message: unknown) => mockToastNotify({ type: 'error', message }), diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/index.spec.tsx index 0714fbf554..e1615e7b0a 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/index.spec.tsx @@ -1,8 +1,8 @@ import type { SimpleDetail } from '../../../store' import type { TriggerOAuthConfig, TriggerProviderApiEntity, TriggerSubscription, TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types' +import { toast } from '@langgenius/dify-ui/toast' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' -import { toast } from '@/app/components/base/ui/toast' import { SupportedCreationMethods } from '@/app/components/plugins/types' import { TriggerCredentialTypeEnum } from '@/app/components/workflow/block-selector/types' import { CreateButtonType, CreateSubscriptionButton, DEFAULT_METHOD } from '../index' @@ -34,7 +34,7 @@ vi.mock('@/app/components/base/portal-to-follow-elem', () => ({ }, })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign(vi.fn(), { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/oauth-client.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/oauth-client.spec.tsx index 5c4407b3c5..46b9499027 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/oauth-client.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/oauth-client.spec.tsx @@ -86,7 +86,7 @@ vi.mock('@/hooks/use-oauth', () => ({ })) const mockToastNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign( (message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }), { diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.helpers.spec.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.helpers.spec.ts index 61482e2912..87db85b56e 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.helpers.spec.ts +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.helpers.spec.ts @@ -27,7 +27,7 @@ const { mockIsPrivateOrLocalAddress: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: mockToastError, }, diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.spec.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.spec.ts index 399d3ba60c..26e4a69baa 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.spec.ts +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.spec.ts @@ -87,7 +87,7 @@ vi.mock('@/service/use-triggers', () => ({ })) const mockToastNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: (message: string) => mockToastNotify({ type: 'success', message }), error: (message: string) => mockToastNotify({ type: 'error', message }), diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-oauth-client-state.spec.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-oauth-client-state.spec.ts index cebfc947e7..82eddf501d 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-oauth-client-state.spec.ts +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-oauth-client-state.spec.ts @@ -77,7 +77,7 @@ vi.mock('@/hooks/use-oauth', () => ({ })) const mockToastNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign( (message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }), { diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.helpers.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.helpers.ts index 8df864c4fa..2754bbf39d 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.helpers.ts +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.helpers.ts @@ -3,8 +3,8 @@ import type { Dispatch, SetStateAction } from 'react' import type { FormRefObject } from '@/app/components/base/form/types' import type { TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types' import type { BuildTriggerSubscriptionPayload } from '@/service/use-triggers' +import { toast } from '@langgenius/dify-ui/toast' import { useEffect, useRef } from 'react' -import { toast } from '@/app/components/base/ui/toast' import { SupportedCreationMethods } from '@/app/components/plugins/types' import { isPrivateOrLocalAddress } from '@/utils/urlValidation' diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.ts index 29613c6f4f..0f40da2d58 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.ts +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.ts @@ -3,10 +3,10 @@ import type { SimpleDetail } from '../../../store' import type { SchemaItem } from '../components/modal-steps' import type { FormRefObject } from '@/app/components/base/form/types' import type { TriggerLogEntity, TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types' +import { toast } from '@langgenius/dify-ui/toast' import { debounce } from 'es-toolkit/compat' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { toast } from '@/app/components/base/ui/toast' import { SupportedCreationMethods } from '@/app/components/plugins/types' import { TriggerCredentialTypeEnum } from '@/app/components/workflow/block-selector/types' import { diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-oauth-client-state.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-oauth-client-state.ts index e5a5ded9df..25058e529c 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-oauth-client-state.ts +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-oauth-client-state.ts @@ -2,9 +2,9 @@ import type { FormRefObject } from '@/app/components/base/form/types' import type { TriggerOAuthClientParams, TriggerOAuthConfig, TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types' import type { ConfigureTriggerOAuthPayload } from '@/service/use-triggers' +import { toast } from '@langgenius/dify-ui/toast' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { toast } from '@/app/components/base/ui/toast' import { openOAuthPopup } from '@/hooks/use-oauth' import { useConfigureTriggerOAuth, diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx index 4861f30934..b007c89cb9 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx @@ -1,6 +1,8 @@ import type { Option } from '@/app/components/base/select/custom' import type { TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiAddLine, RiEqualizer2Line } from '@remixicon/react' import { useBoolean } from 'ahooks' import { useCallback, useMemo, useState } from 'react' @@ -9,8 +11,6 @@ import { ActionButton, ActionButtonState } from '@/app/components/base/action-bu import Badge from '@/app/components/base/badge' import CustomSelect from '@/app/components/base/select/custom' import Tooltip from '@/app/components/base/tooltip' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { openOAuthPopup } from '@/hooks/use-oauth' import { useInitiateTriggerOAuth, useTriggerOAuthConfig, useTriggerProviderInfo } from '@/service/use-triggers' import { SupportedCreationMethods } from '../../../types' diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx index c4ae63ab66..450324ae40 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx @@ -1,5 +1,7 @@ 'use client' import type { TriggerOAuthConfig, TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiClipboardLine, RiInformation2Fill, @@ -7,8 +9,6 @@ import { import { useTranslation } from 'react-i18next' import { BaseForm } from '@/app/components/base/form/components/base' import Modal from '@/app/components/base/modal/modal' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card' import { usePluginStore } from '../../store' import { ClientTypeEnum, useOAuthClientState } from './hooks/use-oauth-client-state' diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/delete-confirm.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/delete-confirm.tsx index a360a477e3..3599dc6260 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/delete-confirm.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/delete-confirm.tsx @@ -1,6 +1,3 @@ -import { useState } from 'react' -import { useTranslation } from 'react-i18next' -import Input from '@/app/components/base/input' import { AlertDialog, AlertDialogActions, @@ -9,8 +6,11 @@ import { AlertDialogContent, AlertDialogDescription, AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' -import { toast } from '@/app/components/base/ui/toast' +} from '@langgenius/dify-ui/alert-dialog' +import { toast } from '@langgenius/dify-ui/toast' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import Input from '@/app/components/base/input' import { useDeleteTriggerSubscription } from '@/service/use-triggers' import { useSubscriptionList } from './use-subscription-list' diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/apikey-edit-modal.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/apikey-edit-modal.spec.tsx index b33f0af8e9..6e192e025c 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/apikey-edit-modal.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/apikey-edit-modal.spec.tsx @@ -47,7 +47,7 @@ vi.mock('@/service/use-triggers', () => ({ useTriggerPluginDynamicOptions: () => ({ data: [], isLoading: false }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign((args: { type: string, message: string }) => mockToast(args), { success: (message: string) => mockToast({ type: 'success', message }), error: (message: string) => mockToast({ type: 'error', message }), diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/index.spec.tsx index 126d8e366d..6cf58b8972 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/index.spec.tsx @@ -13,7 +13,7 @@ import { OAuthEditModal } from '../oauth-edit-modal' // ==================== Mock Setup ==================== const mockToastNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign((message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }), { success: (message: string) => mockToastNotify({ type: 'success', message }), error: (message: string) => mockToastNotify({ type: 'error', message }), diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/manual-edit-modal.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/manual-edit-modal.spec.tsx index 4fa236783b..e5fa43268a 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/manual-edit-modal.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/manual-edit-modal.spec.tsx @@ -30,7 +30,7 @@ vi.mock('@/service/use-triggers', () => ({ useTriggerPluginDynamicOptions: () => ({ data: [], isLoading: false }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign((args: { type: string, message: string }) => mockToast(args), { success: (message: string) => mockToast({ type: 'success', message }), error: (message: string) => mockToast({ type: 'error', message }), diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/oauth-edit-modal.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/oauth-edit-modal.spec.tsx index 1927ae5a43..c860cd2818 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/oauth-edit-modal.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/oauth-edit-modal.spec.tsx @@ -30,7 +30,7 @@ vi.mock('@/service/use-triggers', () => ({ useTriggerPluginDynamicOptions: () => ({ data: [], isLoading: false }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign((args: { type: string, message: string }) => mockToast(args), { success: (message: string) => mockToast({ type: 'success', message }), error: (message: string) => mockToast({ type: 'error', message }), diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/apikey-edit-modal.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/apikey-edit-modal.tsx index eea9cb7ff0..f191ad41a8 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/apikey-edit-modal.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/apikey-edit-modal.tsx @@ -2,6 +2,7 @@ import type { FormRefObject, FormSchema } from '@/app/components/base/form/types' import type { ParametersSchema, PluginDetail } from '@/app/components/plugins/types' import type { TriggerSubscription } from '@/app/components/workflow/block-selector/types' +import { toast } from '@langgenius/dify-ui/toast' import { isEqual } from 'es-toolkit/predicate' import { useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -9,7 +10,6 @@ import { EncryptedBottom } from '@/app/components/base/encrypted-bottom' import { BaseForm } from '@/app/components/base/form/components/base' import { FormTypeEnum } from '@/app/components/base/form/types' import Modal from '@/app/components/base/modal/modal' -import { toast } from '@/app/components/base/ui/toast' import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance' import { useUpdateTriggerSubscription, useVerifyTriggerSubscription } from '@/service/use-triggers' import { parsePluginErrorMessage } from '@/utils/error-parser' diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/manual-edit-modal.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/manual-edit-modal.tsx index 333a800ffd..3b3fa1082f 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/manual-edit-modal.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/manual-edit-modal.tsx @@ -2,13 +2,13 @@ import type { FormRefObject, FormSchema } from '@/app/components/base/form/types' import type { ParametersSchema, PluginDetail } from '@/app/components/plugins/types' import type { TriggerSubscription } from '@/app/components/workflow/block-selector/types' +import { toast } from '@langgenius/dify-ui/toast' import { isEqual } from 'es-toolkit/predicate' import { useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import { BaseForm } from '@/app/components/base/form/components/base' import { FormTypeEnum } from '@/app/components/base/form/types' import Modal from '@/app/components/base/modal/modal' -import { toast } from '@/app/components/base/ui/toast' import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance' import { useUpdateTriggerSubscription } from '@/service/use-triggers' import { ReadmeShowType } from '../../../readme-panel/store' diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/oauth-edit-modal.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/oauth-edit-modal.tsx index 3937d82a0e..355a132bd2 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/oauth-edit-modal.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/oauth-edit-modal.tsx @@ -2,13 +2,13 @@ import type { FormRefObject, FormSchema } from '@/app/components/base/form/types' import type { ParametersSchema, PluginDetail } from '@/app/components/plugins/types' import type { TriggerSubscription } from '@/app/components/workflow/block-selector/types' +import { toast } from '@langgenius/dify-ui/toast' import { isEqual } from 'es-toolkit/predicate' import { useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import { BaseForm } from '@/app/components/base/form/components/base' import { FormTypeEnum } from '@/app/components/base/form/types' import Modal from '@/app/components/base/modal/modal' -import { toast } from '@/app/components/base/ui/toast' import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance' import { useUpdateTriggerSubscription } from '@/service/use-triggers' import { ReadmeShowType } from '../../../readme-panel/store' diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/log-viewer.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/log-viewer.tsx index 1960103822..b0b3efe853 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/log-viewer.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/log-viewer.tsx @@ -1,6 +1,7 @@ 'use client' import type { TriggerLogEntity } from '@/app/components/workflow/block-selector/types' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowDownSLine, RiArrowRightSLine, @@ -12,7 +13,6 @@ import dayjs from 'dayjs' import * as React from 'react' import { useState } from 'react' import { useTranslation } from 'react-i18next' -import { toast } from '@/app/components/base/ui/toast' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/__tests__/index.spec.tsx index 537e99d733..168e4f1eba 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/__tests__/index.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/__tests__/index.spec.tsx @@ -298,7 +298,7 @@ vi.mock('@/app/components/header/account-setting/model-provider-page/model-modal // Mock Toast - need to track notify calls for assertions const mockToastNotify = vi.fn() -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign((message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }), { success: (message: string) => mockToastNotify({ type: 'success', message }), error: (message: string) => mockToastNotify({ type: 'error', message }), diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/reasoning-config-form.spec.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/reasoning-config-form.spec.tsx index b4bfd22405..beab35595c 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/reasoning-config-form.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/reasoning-config-form.spec.tsx @@ -30,8 +30,8 @@ vi.mock('@/app/components/base/select', () => ({ ), })) -vi.mock('@/app/components/base/switch', () => ({ - default: ({ checked, onCheckedChange }: { checked: boolean, onCheckedChange: (checked: boolean) => void }) => ( +vi.mock('@langgenius/dify-ui/switch', () => ({ + Switch: ({ checked, onCheckedChange }: { checked: boolean, onCheckedChange: (checked: boolean) => void }) => ( diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/tool-credentials-form.spec.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/tool-credentials-form.spec.tsx index bd2bc0dd5c..2d50a2d2bf 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/tool-credentials-form.spec.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/tool-credentials-form.spec.tsx @@ -10,7 +10,7 @@ vi.mock('@langgenius/dify-ui/cn', () => ({ cn: (...args: unknown[]) => args.filter(Boolean).join(' '), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign(vi.fn(), { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/reasoning-config-form.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/reasoning-config-form.tsx index 4e6be7d81c..1c731d5eca 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/reasoning-config-form.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/reasoning-config-form.tsx @@ -7,6 +7,7 @@ import type { ValueSelector, } from '@/app/components/workflow/types' import { cn } from '@langgenius/dify-ui/cn' +import { Switch } from '@langgenius/dify-ui/switch' import { RiArrowRightUpLine, RiBracesLine, @@ -16,7 +17,6 @@ import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import { SimpleSelect } from '@/app/components/base/select' -import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-credentials-form.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-credentials-form.tsx index 90893c88ec..64068166d0 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-credentials-form.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-credentials-form.tsx @@ -2,7 +2,9 @@ import type { FC } from 'react' import type { Collection } from '@/app/components/tools/types' import type { ToolCredentialFormSchema } from '@/app/components/tools/utils/to-form-schema' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowRightUpLine, } from '@remixicon/react' @@ -10,8 +12,6 @@ import * as React from 'react' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form' import { addDefaultValue, toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema' import { useRenderI18nObject } from '@/hooks/use-i18n' diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-item.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-item.tsx index 3ba30790af..889243d507 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-item.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-item.tsx @@ -1,5 +1,7 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { Switch } from '@langgenius/dify-ui/switch' import { RiDeleteBinLine, RiEqualizer2Line, @@ -11,10 +13,8 @@ import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import AppIcon from '@/app/components/base/app-icon' import { Group } from '@/app/components/base/icons/src/vender/other' -import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' import { ToolTipContent } from '@/app/components/base/tooltip/content' -import { Button } from '@/app/components/base/ui/button' import Indicator from '@/app/components/header/indicator' import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button' import { useMCPToolAvailability } from '@/app/components/workflow/nodes/_base/components/mcp-tool-availability' diff --git a/web/app/components/plugins/plugin-item/__tests__/action.spec.tsx b/web/app/components/plugins/plugin-item/__tests__/action.spec.tsx index 0b0d9c7fc8..b0ecc839b3 100644 --- a/web/app/components/plugins/plugin-item/__tests__/action.spec.tsx +++ b/web/app/components/plugins/plugin-item/__tests__/action.spec.tsx @@ -26,7 +26,7 @@ const { mockToastNotify: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign( (message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }), { diff --git a/web/app/components/plugins/plugin-item/action.tsx b/web/app/components/plugins/plugin-item/action.tsx index ed401b534f..fe0ba932fc 100644 --- a/web/app/components/plugins/plugin-item/action.tsx +++ b/web/app/components/plugins/plugin-item/action.tsx @@ -2,11 +2,6 @@ import type { FC } from 'react' import type { MetaData } from '../types' import type { PluginCategoryEnum } from '@/app/components/plugins/types' -import { RiDeleteBinLine, RiInformation2Line, RiLoopLeftLine } from '@remixicon/react' -import { useBoolean } from 'ahooks' -import * as React from 'react' -import { useCallback } from 'react' -import { useTranslation } from 'react-i18next' import { AlertDialog, AlertDialogActions, @@ -14,8 +9,13 @@ import { AlertDialogConfirmButton, AlertDialogContent, AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' -import { toast } from '@/app/components/base/ui/toast' +} from '@langgenius/dify-ui/alert-dialog' +import { toast } from '@langgenius/dify-ui/toast' +import { RiDeleteBinLine, RiInformation2Line, RiLoopLeftLine } from '@remixicon/react' +import { useBoolean } from 'ahooks' +import * as React from 'react' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import { useModalContext } from '@/context/modal-context' import { uninstallPlugin } from '@/service/plugins' import { useInvalidateInstalledPluginList } from '@/service/use-plugins' diff --git a/web/app/components/plugins/plugin-mutation-model/index.tsx b/web/app/components/plugins/plugin-mutation-model/index.tsx index 4e8478f8ad..96a60fd938 100644 --- a/web/app/components/plugins/plugin-mutation-model/index.tsx +++ b/web/app/components/plugins/plugin-mutation-model/index.tsx @@ -1,10 +1,10 @@ import type { UseMutationResult } from '@tanstack/react-query' import type { FC, ReactNode } from 'react' import type { Plugin } from '../types' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { memo } from 'react' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' import Card from '@/app/components/plugins/card' type Props = { diff --git a/web/app/components/plugins/plugin-page/__tests__/debug-info.spec.tsx b/web/app/components/plugins/plugin-page/__tests__/debug-info.spec.tsx index 2af44ecf97..65a36f4009 100644 --- a/web/app/components/plugins/plugin-page/__tests__/debug-info.spec.tsx +++ b/web/app/components/plugins/plugin-page/__tests__/debug-info.spec.tsx @@ -15,7 +15,7 @@ vi.mock('@/service/use-plugins', () => ({ useDebugKey: () => mockDebugKey, })) -vi.mock('@/app/components/base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: ({ children }: { children: React.ReactNode }) => , })) diff --git a/web/app/components/plugins/plugin-page/__tests__/install-plugin-dropdown.spec.tsx b/web/app/components/plugins/plugin-page/__tests__/install-plugin-dropdown.spec.tsx index 1f249b16c6..2dd884e18b 100644 --- a/web/app/components/plugins/plugin-page/__tests__/install-plugin-dropdown.spec.tsx +++ b/web/app/components/plugins/plugin-page/__tests__/install-plugin-dropdown.spec.tsx @@ -35,13 +35,13 @@ vi.mock('@/app/components/base/icons/src/vender/solid/mediaAndDevices', () => ({ MagicBox: () => magic, })) -vi.mock('@/app/components/base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: ({ children, onClick, className, ...props }: React.ButtonHTMLAttributes) => ( ), })) -vi.mock('@/app/components/base/ui/dropdown-menu', async () => { +vi.mock('@langgenius/dify-ui/dropdown-menu', async () => { const React = await import('react') const DropdownMenuContext = React.createContext<{ isOpen: boolean, setOpen: (open: boolean) => void } | null>(null) diff --git a/web/app/components/plugins/plugin-page/__tests__/use-reference-setting.spec.ts b/web/app/components/plugins/plugin-page/__tests__/use-reference-setting.spec.ts index c3c415bb4b..efb31b5afb 100644 --- a/web/app/components/plugins/plugin-page/__tests__/use-reference-setting.spec.ts +++ b/web/app/components/plugins/plugin-page/__tests__/use-reference-setting.spec.ts @@ -1,7 +1,7 @@ +// Import mocks for assertions +import { toast } from '@langgenius/dify-ui/toast' import { renderHook, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' -// Import mocks for assertions -import { toast } from '@/app/components/base/ui/toast' import { useAppContext } from '@/context/app-context' import { useGlobalPublicStore } from '@/context/global-public-context' diff --git a/web/app/components/plugins/plugin-page/debug-info.tsx b/web/app/components/plugins/plugin-page/debug-info.tsx index 4b3590f15c..4e02fdc2be 100644 --- a/web/app/components/plugins/plugin-page/debug-info.tsx +++ b/web/app/components/plugins/plugin-page/debug-info.tsx @@ -1,5 +1,6 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { RiArrowRightUpLine, RiBugLine, @@ -7,7 +8,6 @@ import { import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' -import { Button } from '@/app/components/base/ui/button' import { useDocLink } from '@/context/i18n' import { useDebugKey } from '@/service/use-plugins' import KeyValueItem from '../base/key-value-item' diff --git a/web/app/components/plugins/plugin-page/empty/index.tsx b/web/app/components/plugins/plugin-page/empty/index.tsx index 5141519e35..e90918b4ab 100644 --- a/web/app/components/plugins/plugin-page/empty/index.tsx +++ b/web/app/components/plugins/plugin-page/empty/index.tsx @@ -1,4 +1,5 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' import { noop } from 'es-toolkit/function' import * as React from 'react' import { useEffect, useMemo, useRef, useState } from 'react' @@ -7,7 +8,6 @@ import { Group } from '@/app/components/base/icons/src/vender/other' import { FileZip } from '@/app/components/base/icons/src/vender/solid/files' import { Github } from '@/app/components/base/icons/src/vender/solid/general' import { MagicBox } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices' -import { Button } from '@/app/components/base/ui/button' import InstallFromGitHub from '@/app/components/plugins/install-plugin/install-from-github' import InstallFromLocalPackage from '@/app/components/plugins/install-plugin/install-from-local-package' import { SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config' diff --git a/web/app/components/plugins/plugin-page/index.tsx b/web/app/components/plugins/plugin-page/index.tsx index 05a6dd26e3..f80d37189a 100644 --- a/web/app/components/plugins/plugin-page/index.tsx +++ b/web/app/components/plugins/plugin-page/index.tsx @@ -2,6 +2,7 @@ import type { Dependency, PluginDeclaration, PluginManifestInMarket } from '../types' import type { PluginPageTab } from './context' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiBookOpenLine, @@ -14,7 +15,6 @@ import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import TabSlider from '@/app/components/base/tab-slider' import Tooltip from '@/app/components/base/tooltip' -import { Button } from '@/app/components/base/ui/button' import ReferenceSettingModal from '@/app/components/plugins/reference-setting-modal' import { MARKETPLACE_API_PREFIX, SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config' import { useGlobalPublicStore } from '@/context/global-public-context' diff --git a/web/app/components/plugins/plugin-page/install-plugin-dropdown.tsx b/web/app/components/plugins/plugin-page/install-plugin-dropdown.tsx index 9b98d16410..dee3dc17bb 100644 --- a/web/app/components/plugins/plugin-page/install-plugin-dropdown.tsx +++ b/web/app/components/plugins/plugin-page/install-plugin-dropdown.tsx @@ -1,6 +1,13 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@langgenius/dify-ui/dropdown-menu' import { RiAddLine, RiArrowDownSLine } from '@remixicon/react' import { noop } from 'es-toolkit/function' import { useEffect, useRef, useState } from 'react' @@ -8,13 +15,6 @@ import { useTranslation } from 'react-i18next' import { FileZip } from '@/app/components/base/icons/src/vender/solid/files' import { Github } from '@/app/components/base/icons/src/vender/solid/general' import { MagicBox } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices' -import { Button } from '@/app/components/base/ui/button' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' import InstallFromGitHub from '@/app/components/plugins/install-plugin/install-from-github' import InstallFromLocalPackage from '@/app/components/plugins/install-plugin/install-from-local-package' import { SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config' diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/components/error-plugin-item.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/components/error-plugin-item.tsx index 0129c65ee2..ab17dd202b 100644 --- a/web/app/components/plugins/plugin-page/plugin-tasks/components/error-plugin-item.tsx +++ b/web/app/components/plugins/plugin-page/plugin-tasks/components/error-plugin-item.tsx @@ -1,9 +1,9 @@ import type { FC } from 'react' import type { Plugin, PluginStatus } from '@/app/components/plugins/types' import type { Locale } from '@/i18n-config' +import { Button } from '@langgenius/dify-ui/button' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' import { PluginSource } from '@/app/components/plugins/types' import { fetchPluginInfoFromMarketPlace } from '@/service/plugins' diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/components/plugin-task-list.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/components/plugin-task-list.tsx index e0dcc5b6f8..24fcf85cde 100644 --- a/web/app/components/plugins/plugin-page/plugin-tasks/components/plugin-task-list.tsx +++ b/web/app/components/plugins/plugin-page/plugin-tasks/components/plugin-task-list.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { PluginStatus } from '@/app/components/plugins/types' +import { Button } from '@langgenius/dify-ui/button' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import { useGetLanguage } from '@/context/i18n' import ErrorPluginItem from './error-plugin-item' import PluginSection from './plugin-section' diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/index.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/index.tsx index 9a39b43bf6..7603cae33d 100644 --- a/web/app/components/plugins/plugin-page/plugin-tasks/index.tsx +++ b/web/app/components/plugins/plugin-page/plugin-tasks/index.tsx @@ -1,14 +1,14 @@ +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, +} from '@langgenius/dify-ui/dropdown-menu' import { useCallback, useMemo, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' import PluginTaskList from './components/plugin-task-list' import TaskStatusIndicator from './components/task-status-indicator' diff --git a/web/app/components/plugins/plugin-page/plugins-panel.tsx b/web/app/components/plugins/plugin-page/plugins-panel.tsx index 142c9329cc..a824d55dda 100644 --- a/web/app/components/plugins/plugin-page/plugins-panel.tsx +++ b/web/app/components/plugins/plugin-page/plugins-panel.tsx @@ -1,11 +1,11 @@ 'use client' import type { PluginDetail } from '../types' import type { FilterState } from './filter-management' +import { Button } from '@langgenius/dify-ui/button' import { useDebounceFn } from 'ahooks' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' -import { Button } from '@/app/components/base/ui/button' import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel' import { useGetLanguage } from '@/context/i18n' import { renderI18nObject } from '@/i18n-config' diff --git a/web/app/components/plugins/plugin-page/use-reference-setting.ts b/web/app/components/plugins/plugin-page/use-reference-setting.ts index 0a0e925207..b688bdc11e 100644 --- a/web/app/components/plugins/plugin-page/use-reference-setting.ts +++ b/web/app/components/plugins/plugin-page/use-reference-setting.ts @@ -1,6 +1,6 @@ +import { toast } from '@langgenius/dify-ui/toast' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { toast } from '@/app/components/base/ui/toast' import { useAppContext } from '@/context/app-context' import { useGlobalPublicStore } from '@/context/global-public-context' import { useInvalidateReferenceSettings, useMutationReferenceSettings, useReferenceSettings } from '@/service/use-plugins' diff --git a/web/app/components/plugins/provider-card.tsx b/web/app/components/plugins/provider-card.tsx index 7f07699771..06b611b355 100644 --- a/web/app/components/plugins/provider-card.tsx +++ b/web/app/components/plugins/provider-card.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' import type { Plugin } from './types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiArrowRightUpLine } from '@remixicon/react' import { useBoolean } from 'ahooks' @@ -8,7 +9,6 @@ import { useTheme } from 'next-themes' import * as React from 'react' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' import { getPluginLinkInMarketplace } from '@/app/components/plugins/marketplace/utils' import { useLocale } from '@/context/i18n' diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/plugins-picker.spec.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/plugins-picker.spec.tsx index 299eecce54..99d7a5fdc5 100644 --- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/plugins-picker.spec.tsx +++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/plugins-picker.spec.tsx @@ -5,7 +5,7 @@ import { AUTO_UPDATE_MODE } from '../types' const mockToolPicker = vi.fn() -vi.mock('@/app/components/base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: ({ children, }: { diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/strategy-picker.spec.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/strategy-picker.spec.tsx index e1e72fbde2..5be10ff146 100644 --- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/strategy-picker.spec.tsx +++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/strategy-picker.spec.tsx @@ -5,7 +5,7 @@ import { AUTO_UPDATE_STRATEGY } from '../types' let portalOpen = false -vi.mock('@/app/components/base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: ({ children, }: { diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/plugins-picker.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/plugins-picker.tsx index c0dbbea5c6..1bb4caeb3f 100644 --- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/plugins-picker.tsx +++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/plugins-picker.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { RiAddLine } from '@remixicon/react' import { useBoolean } from 'ahooks' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import NoPluginSelected from './no-plugin-selected' import PluginsSelected from './plugins-selected' import ToolPicker from './tool-picker' diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/strategy-picker.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/strategy-picker.tsx index 0f85b95e3c..22bfa6a30b 100644 --- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/strategy-picker.tsx +++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/strategy-picker.tsx @@ -1,3 +1,4 @@ +import { Button } from '@langgenius/dify-ui/button' import { RiArrowDownSLine, RiCheckLine, @@ -9,7 +10,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' import { AUTO_UPDATE_STRATEGY } from './types' const i18nPrefix = 'autoUpdate.strategy' diff --git a/web/app/components/plugins/reference-setting-modal/index.tsx b/web/app/components/plugins/reference-setting-modal/index.tsx index bbf19d8c0a..061777e38b 100644 --- a/web/app/components/plugins/reference-setting-modal/index.tsx +++ b/web/app/components/plugins/reference-setting-modal/index.tsx @@ -2,11 +2,11 @@ import type { FC } from 'react' import type { AutoUpdateConfig } from './auto-update-setting/types' import type { Permissions, ReferenceSetting } from '@/app/components/plugins/types' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' import { PermissionType } from '@/app/components/plugins/types' import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card' import { useGlobalPublicStore } from '@/context/global-public-context' diff --git a/web/app/components/plugins/update-plugin/__tests__/from-market-place.spec.tsx b/web/app/components/plugins/update-plugin/__tests__/from-market-place.spec.tsx index 9d9ad77106..3cc3ef78d1 100644 --- a/web/app/components/plugins/update-plugin/__tests__/from-market-place.spec.tsx +++ b/web/app/components/plugins/update-plugin/__tests__/from-market-place.spec.tsx @@ -22,7 +22,7 @@ const { mockToastError: vi.fn(), })) -vi.mock('@/app/components/base/ui/dialog', () => ({ +vi.mock('@langgenius/dify-ui/dialog', () => ({ Dialog: ({ children }: { children: React.ReactNode }) =>
{children}
, DialogContent: ({ children }: { children: React.ReactNode }) =>
{children}
, DialogTitle: ({ children }: { children: React.ReactNode }) =>
{children}
, @@ -37,7 +37,7 @@ vi.mock('@/app/components/base/badge/index', () => ({ default: ({ children }: { children: React.ReactNode }) =>
{children}
, })) -vi.mock('@/app/components/base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: ({ children, onClick, @@ -49,7 +49,7 @@ vi.mock('@/app/components/base/ui/button', () => ({ }) => , })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: mockToastError, }, diff --git a/web/app/components/plugins/update-plugin/__tests__/index.spec.tsx b/web/app/components/plugins/update-plugin/__tests__/index.spec.tsx index cdd7462342..d0492696ca 100644 --- a/web/app/components/plugins/update-plugin/__tests__/index.spec.tsx +++ b/web/app/components/plugins/update-plugin/__tests__/index.spec.tsx @@ -4,11 +4,11 @@ import type { UpdateFromMarketPlacePayload, UpdatePluginModalType, } from '../../types' +import { toast } from '@langgenius/dify-ui/toast' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import * as React from 'react' import { beforeEach, describe, expect, it, vi } from 'vitest' -import { toast } from '@/app/components/base/ui/toast' import { PluginCategoryEnum, PluginSource, TaskStatus } from '../../types' import DowngradeWarningModal from '../downgrade-warning' import FromGitHub from '../from-github' diff --git a/web/app/components/plugins/update-plugin/downgrade-warning.tsx b/web/app/components/plugins/update-plugin/downgrade-warning.tsx index da53ebfb12..801c8ede51 100644 --- a/web/app/components/plugins/update-plugin/downgrade-warning.tsx +++ b/web/app/components/plugins/update-plugin/downgrade-warning.tsx @@ -1,5 +1,5 @@ +import { Button } from '@langgenius/dify-ui/button' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' const i18nPrefix = 'autoUpdate.pluginDowngradeWarning' diff --git a/web/app/components/plugins/update-plugin/from-market-place.tsx b/web/app/components/plugins/update-plugin/from-market-place.tsx index bde5051808..52484e65aa 100644 --- a/web/app/components/plugins/update-plugin/from-market-place.tsx +++ b/web/app/components/plugins/update-plugin/from-market-place.tsx @@ -1,19 +1,19 @@ 'use client' import type { FC } from 'react' import type { UpdateFromMarketPlacePayload } from '../types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' -import * as React from 'react' -import { useCallback, useEffect, useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import Badge, { BadgeState } from '@/app/components/base/badge/index' -import { Button } from '@/app/components/base/ui/button' import { Dialog, DialogCloseButton, DialogContent, DialogTitle, -} from '@/app/components/base/ui/dialog' -import { toast } from '@/app/components/base/ui/toast' +} from '@langgenius/dify-ui/dialog' +import { toast } from '@langgenius/dify-ui/toast' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Badge, { BadgeState } from '@/app/components/base/badge/index' import Card from '@/app/components/plugins/card' import checkTaskStatus from '@/app/components/plugins/install-plugin/base/check-task-status' import { pluginManifestToCardPluginProps } from '@/app/components/plugins/install-plugin/utils' diff --git a/web/app/components/plugins/update-plugin/plugin-version-picker.tsx b/web/app/components/plugins/update-plugin/plugin-version-picker.tsx index d590f0c9eb..a76fd085ba 100644 --- a/web/app/components/plugins/update-plugin/plugin-version-picker.tsx +++ b/web/app/components/plugins/update-plugin/plugin-version-picker.tsx @@ -1,16 +1,16 @@ 'use client' +import type { Placement } from '@langgenius/dify-ui/popover' import type { FC } from 'react' -import type { Placement } from '@/app/components/base/ui/placement' import { cn } from '@langgenius/dify-ui/cn' -import * as React from 'react' -import { useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import Badge from '@/app/components/base/badge' import { Popover, PopoverContent, PopoverTrigger, -} from '@/app/components/base/ui/popover' +} from '@langgenius/dify-ui/popover' +import * as React from 'react' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import Badge from '@/app/components/base/badge' import useTimestamp from '@/hooks/use-timestamp' import { useVersionListOfPlugin } from '@/service/use-plugins' import { isEarlierThanVersion } from '@/utils/semver' diff --git a/web/app/components/rag-pipeline/components/__tests__/conversion.spec.tsx b/web/app/components/rag-pipeline/components/__tests__/conversion.spec.tsx index 967e697813..cb11e5baf4 100644 --- a/web/app/components/rag-pipeline/components/__tests__/conversion.spec.tsx +++ b/web/app/components/rag-pipeline/components/__tests__/conversion.spec.tsx @@ -37,11 +37,11 @@ const { mockToast } = vi.hoisted(() => { return { mockToast } }) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: mockToast, })) -vi.mock('@/app/components/base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: ({ children, onClick, ...props }: Record) => ( ), diff --git a/web/app/components/rag-pipeline/components/__tests__/publish-as-knowledge-pipeline-modal.spec.tsx b/web/app/components/rag-pipeline/components/__tests__/publish-as-knowledge-pipeline-modal.spec.tsx index f9eb858b30..7a99b7ab90 100644 --- a/web/app/components/rag-pipeline/components/__tests__/publish-as-knowledge-pipeline-modal.spec.tsx +++ b/web/app/components/rag-pipeline/components/__tests__/publish-as-knowledge-pipeline-modal.spec.tsx @@ -22,7 +22,7 @@ vi.mock('@/app/components/base/modal', () => ({ isShow ?
{children}
: null, })) -vi.mock('@/app/components/base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: ({ children, onClick, disabled, ...props }: Record) => ( @@ -83,7 +83,7 @@ vi.mock('@/app/components/base/ui/dropdown-menu', () => ({ DropdownMenuContent: ({ children }: { children: React.ReactNode }) =>
{children}
, })) -vi.mock('@/app/components/base/ui/tooltip', () => ({ +vi.mock('@langgenius/dify-ui/tooltip', () => ({ Tooltip: ({ children }: { children: React.ReactNode }) => <>{children}, TooltipTrigger: ({ children, diff --git a/web/app/components/workflow/comment/thread.tsx b/web/app/components/workflow/comment/thread.tsx index d0ab65db15..34e0092372 100644 --- a/web/app/components/workflow/comment/thread.tsx +++ b/web/app/components/workflow/comment/thread.tsx @@ -2,20 +2,20 @@ import type { FC, ReactNode } from 'react' import type { WorkflowCommentDetail, WorkflowCommentDetailReply } from '@/service/workflow-comment' +import { Avatar, AvatarFallback, AvatarImage, AvatarRoot } from '@langgenius/dify-ui/avatar' import { cn } from '@langgenius/dify-ui/cn' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, +} from '@langgenius/dify-ui/dropdown-menu' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { RiArrowDownSLine, RiArrowUpSLine, RiCheckboxCircleFill, RiCheckboxCircleLine, RiCloseLine, RiDeleteBinLine, RiMoreFill } from '@remixicon/react' import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useReactFlow, useViewport } from 'reactflow' import Divider from '@/app/components/base/divider' import InlineDeleteConfirm from '@/app/components/base/inline-delete-confirm' -import { Avatar, AvatarFallback, AvatarImage, AvatarRoot } from '@/app/components/base/ui/avatar' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' import { getUserColor } from '@/app/components/workflow/collaboration/utils/user-color' import { useAppContext } from '@/context/app-context' import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' diff --git a/web/app/components/workflow/dsl-export-confirm-modal.tsx b/web/app/components/workflow/dsl-export-confirm-modal.tsx index b256f8bd9f..6f60e19854 100644 --- a/web/app/components/workflow/dsl-export-confirm-modal.tsx +++ b/web/app/components/workflow/dsl-export-confirm-modal.tsx @@ -1,5 +1,6 @@ 'use client' import type { EnvironmentVariable } from '@/app/components/workflow/types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiCloseLine, RiLock2Line } from '@remixicon/react' import { noop } from 'es-toolkit/function' @@ -9,7 +10,6 @@ import { useTranslation } from 'react-i18next' import Checkbox from '@/app/components/base/checkbox' import { Env } from '@/app/components/base/icons/src/vender/line/others' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' export type DSLExportConfirmModalProps = { envList: EnvironmentVariable[] diff --git a/web/app/components/workflow/edge-contextmenu.tsx b/web/app/components/workflow/edge-contextmenu.tsx index 61b208fcbd..2b7f13190a 100644 --- a/web/app/components/workflow/edge-contextmenu.tsx +++ b/web/app/components/workflow/edge-contextmenu.tsx @@ -1,14 +1,14 @@ +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, +} from '@langgenius/dify-ui/context-menu' import { memo, useMemo, } from 'react' import { useTranslation } from 'react-i18next' import { useEdges } from 'reactflow' -import { - ContextMenu, - ContextMenuContent, - ContextMenuItem, -} from '@/app/components/base/ui/context-menu' import { useEdgesInteractions, usePanelInteractions } from './hooks' import ShortcutsName from './shortcuts-name' import { useStore } from './store' diff --git a/web/app/components/workflow/header/__tests__/header-layouts.spec.tsx b/web/app/components/workflow/header/__tests__/header-layouts.spec.tsx index f04a160967..1ddf013f8d 100644 --- a/web/app/components/workflow/header/__tests__/header-layouts.spec.tsx +++ b/web/app/components/workflow/header/__tests__/header-layouts.spec.tsx @@ -60,7 +60,7 @@ vi.mock('@/service/use-workflow', () => ({ useInvalidAllLastRun: () => mockInvalidAllLastRun, })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: (message: string) => mockNotify({ type: 'success', message }), error: (message: string) => mockNotify({ type: 'error', message }), diff --git a/web/app/components/workflow/header/__tests__/run-mode.spec.tsx b/web/app/components/workflow/header/__tests__/run-mode.spec.tsx index 7bc5ef1b0c..02b645e079 100644 --- a/web/app/components/workflow/header/__tests__/run-mode.spec.tsx +++ b/web/app/components/workflow/header/__tests__/run-mode.spec.tsx @@ -46,7 +46,7 @@ vi.mock('../../hooks/use-dynamic-test-run-options', () => ({ useDynamicTestRunOptions: () => mockDynamicOptions, })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: (message: string) => mockNotify({ type: 'success', message }), error: (message: string) => mockNotify({ type: 'error', message }), diff --git a/web/app/components/workflow/header/__tests__/test-run-menu-helpers.spec.tsx b/web/app/components/workflow/header/__tests__/test-run-menu-helpers.spec.tsx index 7df9cd091f..09452055db 100644 --- a/web/app/components/workflow/header/__tests__/test-run-menu-helpers.spec.tsx +++ b/web/app/components/workflow/header/__tests__/test-run-menu-helpers.spec.tsx @@ -10,7 +10,7 @@ import { useShortcutMenu, } from '../test-run-menu-helpers' -vi.mock('@/app/components/base/ui/dropdown-menu', async () => { +vi.mock('@langgenius/dify-ui/dropdown-menu', async () => { const React = await import('react') const DropdownMenuContext = React.createContext<{ open: boolean, setOpen: (open: boolean) => void } | null>(null) diff --git a/web/app/components/workflow/header/__tests__/test-run-menu.spec.tsx b/web/app/components/workflow/header/__tests__/test-run-menu.spec.tsx index 1d462bfc9c..8e93612dc0 100644 --- a/web/app/components/workflow/header/__tests__/test-run-menu.spec.tsx +++ b/web/app/components/workflow/header/__tests__/test-run-menu.spec.tsx @@ -5,7 +5,7 @@ import { act } from 'react' import * as React from 'react' import TestRunMenu, { TriggerType } from '../test-run-menu' -vi.mock('@/app/components/base/ui/dropdown-menu', async () => { +vi.mock('@langgenius/dify-ui/dropdown-menu', async () => { const React = await import('react') const DropdownMenuContext = React.createContext<{ open: boolean, setOpen: (open: boolean) => void } | null>(null) diff --git a/web/app/components/workflow/header/chat-variable-button.tsx b/web/app/components/workflow/header/chat-variable-button.tsx index 63ac90aad6..efac64bd1b 100644 --- a/web/app/components/workflow/header/chat-variable-button.tsx +++ b/web/app/components/workflow/header/chat-variable-button.tsx @@ -1,7 +1,7 @@ +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { memo } from 'react' import { BubbleX } from '@/app/components/base/icons/src/vender/line/others' -import { Button } from '@/app/components/base/ui/button' import { useStore } from '@/app/components/workflow/store' import useTheme from '@/hooks/use-theme' diff --git a/web/app/components/workflow/header/checklist/__tests__/index.spec.tsx b/web/app/components/workflow/header/checklist/__tests__/index.spec.tsx index 2c83747dc0..25150e2d04 100644 --- a/web/app/components/workflow/header/checklist/__tests__/index.spec.tsx +++ b/web/app/components/workflow/header/checklist/__tests__/index.spec.tsx @@ -47,7 +47,7 @@ vi.mock('../../../hooks', () => ({ }), })) -vi.mock('@/app/components/base/ui/popover', () => ({ +vi.mock('@langgenius/dify-ui/popover', () => ({ Popover: ({ children, onOpenChange }: PopoverProps) => { latestOnOpenChange = onOpenChange return
{children}
diff --git a/web/app/components/workflow/header/checklist/__tests__/plugin-group.spec.tsx b/web/app/components/workflow/header/checklist/__tests__/plugin-group.spec.tsx index 275c8727d1..78341c2174 100644 --- a/web/app/components/workflow/header/checklist/__tests__/plugin-group.spec.tsx +++ b/web/app/components/workflow/header/checklist/__tests__/plugin-group.spec.tsx @@ -1,7 +1,7 @@ import type { ChecklistItem } from '../../../hooks/use-checklist' +import { Popover, PopoverContent } from '@langgenius/dify-ui/popover' import { fireEvent, render, screen } from '@testing-library/react' import { beforeEach, describe, expect, it } from 'vitest' -import { Popover, PopoverContent } from '@/app/components/base/ui/popover' import { useStore as usePluginDependencyStore } from '../../../plugin-dependency/store' import { BlockEnum } from '../../../types' import { ChecklistPluginGroup } from '../plugin-group' diff --git a/web/app/components/workflow/header/checklist/index.tsx b/web/app/components/workflow/header/checklist/index.tsx index 83384c5583..9a54175c8c 100644 --- a/web/app/components/workflow/header/checklist/index.tsx +++ b/web/app/components/workflow/header/checklist/index.tsx @@ -3,6 +3,12 @@ import type { CommonEdgeType, } from '../../types' import { cn } from '@langgenius/dify-ui/cn' +import { + Popover, + PopoverClose, + PopoverContent, + PopoverTrigger, +} from '@langgenius/dify-ui/popover' import { memo, useMemo, @@ -12,12 +18,6 @@ import { useTranslation } from 'react-i18next' import { useEdges, } from 'reactflow' -import { - Popover, - PopoverClose, - PopoverContent, - PopoverTrigger, -} from '@/app/components/base/ui/popover' import useNodes from '@/app/components/workflow/store/workflow/use-nodes' import { useChecklist, diff --git a/web/app/components/workflow/header/checklist/plugin-group.tsx b/web/app/components/workflow/header/checklist/plugin-group.tsx index f74b314a10..91bc385595 100644 --- a/web/app/components/workflow/header/checklist/plugin-group.tsx +++ b/web/app/components/workflow/header/checklist/plugin-group.tsx @@ -1,10 +1,10 @@ import type { ChecklistItem } from '../../hooks/use-checklist' import type { BlockEnum } from '../../types' import type { Dependency } from '@/app/components/plugins/types' +import { Button } from '@langgenius/dify-ui/button' +import { PopoverClose } from '@langgenius/dify-ui/popover' import { memo, useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' -import { PopoverClose } from '@/app/components/base/ui/popover' import BlockIcon from '../../block-icon' import { useStore as usePluginDependencyStore } from '../../plugin-dependency/store' import { ItemIndicator } from './item-indicator' diff --git a/web/app/components/workflow/header/env-button.tsx b/web/app/components/workflow/header/env-button.tsx index 98ab6ba537..eec4af84c8 100644 --- a/web/app/components/workflow/header/env-button.tsx +++ b/web/app/components/workflow/header/env-button.tsx @@ -1,7 +1,7 @@ +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { memo } from 'react' import { Env } from '@/app/components/base/icons/src/vender/line/others' -import { Button } from '@/app/components/base/ui/button' import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks' import { useStore } from '@/app/components/workflow/store' import useTheme from '@/hooks/use-theme' diff --git a/web/app/components/workflow/header/global-variable-button.tsx b/web/app/components/workflow/header/global-variable-button.tsx index 63a4a427b6..f42677a3df 100644 --- a/web/app/components/workflow/header/global-variable-button.tsx +++ b/web/app/components/workflow/header/global-variable-button.tsx @@ -1,7 +1,7 @@ +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { memo } from 'react' import { GlobalVariable } from '@/app/components/base/icons/src/vender/line/others' -import { Button } from '@/app/components/base/ui/button' import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks' import { useStore } from '@/app/components/workflow/store' import useTheme from '@/hooks/use-theme' diff --git a/web/app/components/workflow/header/header-in-restoring.tsx b/web/app/components/workflow/header/header-in-restoring.tsx index e6ad4123ed..f07d28ff13 100644 --- a/web/app/components/workflow/header/header-in-restoring.tsx +++ b/web/app/components/workflow/header/header-in-restoring.tsx @@ -1,12 +1,12 @@ +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiHistoryLine } from '@remixicon/react' import { useCallback, } from 'react' import { useTranslation } from 'react-i18next' import { useFeaturesStore } from '@/app/components/base/features/hooks' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { useSelector as useAppContextSelector } from '@/context/app-context' import useTheme from '@/hooks/use-theme' import { useInvalidAllLastRun } from '@/service/use-workflow' diff --git a/web/app/components/workflow/header/header-in-view-history.tsx b/web/app/components/workflow/header/header-in-view-history.tsx index cd39ce605a..88077002c7 100644 --- a/web/app/components/workflow/header/header-in-view-history.tsx +++ b/web/app/components/workflow/header/header-in-view-history.tsx @@ -1,10 +1,10 @@ import type { ViewHistoryProps } from './view-history' +import { Button } from '@langgenius/dify-ui/button' import { useCallback, } from 'react' import { useTranslation } from 'react-i18next' import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows' -import { Button } from '@/app/components/base/ui/button' import Divider from '../../base/divider' import { useWorkflowRun, diff --git a/web/app/components/workflow/header/online-users.tsx b/web/app/components/workflow/header/online-users.tsx index cce2af0cdc..0f057f0787 100644 --- a/web/app/components/workflow/header/online-users.tsx +++ b/web/app/components/workflow/header/online-users.tsx @@ -1,17 +1,17 @@ 'use client' import type { OnlineUser } from '../collaboration/types/collaboration' import { ChevronDownIcon } from '@heroicons/react/20/solid' +import { AvatarFallback, AvatarImage, AvatarRoot } from '@langgenius/dify-ui/avatar' import { cn } from '@langgenius/dify-ui/cn' -import { useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { useReactFlow } from 'reactflow' -import { AvatarFallback, AvatarImage, AvatarRoot } from '@/app/components/base/ui/avatar' import { Popover, PopoverContent, PopoverTrigger, -} from '@/app/components/base/ui/popover' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' +} from '@langgenius/dify-ui/popover' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' +import { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { useReactFlow } from 'reactflow' import { useAppContext } from '@/context/app-context' import { getAvatar } from '@/service/common' import { useCollaboration } from '../collaboration/hooks/use-collaboration' diff --git a/web/app/components/workflow/header/run-mode.tsx b/web/app/components/workflow/header/run-mode.tsx index d98b4d6b18..923f6f0330 100644 --- a/web/app/components/workflow/header/run-mode.tsx +++ b/web/app/components/workflow/header/run-mode.tsx @@ -1,12 +1,12 @@ import type { TestRunMenuRef, TriggerOption } from './test-run-menu' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiLoader2Line, RiPlayLargeLine } from '@remixicon/react' import * as React from 'react' import { useCallback, useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import { trackEvent } from '@/app/components/base/amplitude' import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' -import { toast } from '@/app/components/base/ui/toast' import { useWorkflowRun, useWorkflowRunValidation, useWorkflowStartRun } from '@/app/components/workflow/hooks' import ShortcutsName from '@/app/components/workflow/shortcuts-name' import { useStore } from '@/app/components/workflow/store' diff --git a/web/app/components/workflow/header/test-run-menu-helpers.tsx b/web/app/components/workflow/header/test-run-menu-helpers.tsx index 4a25cd87a6..9f14190c54 100644 --- a/web/app/components/workflow/header/test-run-menu-helpers.tsx +++ b/web/app/components/workflow/header/test-run-menu-helpers.tsx @@ -1,12 +1,12 @@ /* eslint-disable react-refresh/only-export-components */ import type { MouseEvent, MouseEventHandler, ReactElement } from 'react' import type { TriggerOption } from './test-run-menu' +import { DropdownMenuItem } from '@langgenius/dify-ui/dropdown-menu' import { cloneElement, isValidElement, useEffect, } from 'react' -import { DropdownMenuItem } from '@/app/components/base/ui/dropdown-menu' import ShortcutsName from '../shortcuts-name' export type ShortcutMapping = { diff --git a/web/app/components/workflow/header/test-run-menu.tsx b/web/app/components/workflow/header/test-run-menu.tsx index 6540875e6b..ceaf38592f 100644 --- a/web/app/components/workflow/header/test-run-menu.tsx +++ b/web/app/components/workflow/header/test-run-menu.tsx @@ -1,7 +1,7 @@ import type { ShortcutMapping } from './test-run-menu-helpers' +import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@langgenius/dify-ui/dropdown-menu' import { forwardRef, useCallback, useImperativeHandle, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@/app/components/base/ui/dropdown-menu' import { OptionRow, SingleOptionTrigger, useShortcutMenu } from './test-run-menu-helpers' export enum TriggerType { diff --git a/web/app/components/workflow/header/version-history-button.tsx b/web/app/components/workflow/header/version-history-button.tsx index ef79c4e411..69dc2b4c4c 100644 --- a/web/app/components/workflow/header/version-history-button.tsx +++ b/web/app/components/workflow/header/version-history-button.tsx @@ -1,11 +1,11 @@ import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiHistoryLine } from '@remixicon/react' import { useKeyPress } from 'ahooks' import * as React from 'react' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import useTheme from '@/hooks/use-theme' import Tooltip from '../../base/tooltip' import ShortcutsName from '../shortcuts-name' diff --git a/web/app/components/workflow/hooks/__tests__/use-checklist.spec.ts b/web/app/components/workflow/hooks/__tests__/use-checklist.spec.ts index 891007ff0e..f6d2774611 100644 --- a/web/app/components/workflow/hooks/__tests__/use-checklist.spec.ts +++ b/web/app/components/workflow/hooks/__tests__/use-checklist.spec.ts @@ -89,7 +89,7 @@ vi.mock('../index', () => ({ useNodesMetaData: () => ({ nodes: [], nodesMap: mockNodesMap }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/workflow/hooks/__tests__/use-leader-restore.spec.ts b/web/app/components/workflow/hooks/__tests__/use-leader-restore.spec.ts index 5837f1484d..a050994f4c 100644 --- a/web/app/components/workflow/hooks/__tests__/use-leader-restore.spec.ts +++ b/web/app/components/workflow/hooks/__tests__/use-leader-restore.spec.ts @@ -66,7 +66,7 @@ vi.mock('@/app/components/base/features/hooks', () => ({ }), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { info: (...args: unknown[]) => mockToastInfo(...args), }, diff --git a/web/app/components/workflow/hooks/use-checklist.ts b/web/app/components/workflow/hooks/use-checklist.ts index abafcfd8eb..ae9cc0a4a2 100644 --- a/web/app/components/workflow/hooks/use-checklist.ts +++ b/web/app/components/workflow/hooks/use-checklist.ts @@ -16,6 +16,7 @@ import type { ModelItem } from '@/app/components/header/account-setting/model-pr import type { Emoji } from '@/app/components/tools/types' import type { DataSet } from '@/models/datasets' import type { I18nKeysWithPrefix } from '@/types/i18n' +import { toast } from '@langgenius/dify-ui/toast' import { useQueries, useQueryClient } from '@tanstack/react-query' import isDeepEqual from 'fast-deep-equal' import { @@ -27,7 +28,6 @@ import { import { useTranslation } from 'react-i18next' import { useEdges, useStoreApi } from 'reactflow' import { useStore as useAppStore } from '@/app/components/app/store' -import { toast } from '@/app/components/base/ui/toast' import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' import useNodes from '@/app/components/workflow/store/workflow/use-nodes' diff --git a/web/app/components/workflow/hooks/use-leader-restore.ts b/web/app/components/workflow/hooks/use-leader-restore.ts index 048bb932e0..da0036a4ea 100644 --- a/web/app/components/workflow/hooks/use-leader-restore.ts +++ b/web/app/components/workflow/hooks/use-leader-restore.ts @@ -1,11 +1,11 @@ import type { RestoreCompleteData, RestoreIntentData, RestoreRequestData } from '../collaboration/types/collaboration' import type { SyncCallback } from './use-nodes-sync-draft' +import { toast } from '@langgenius/dify-ui/toast' import { useCallback, useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import { useReactFlow } from 'reactflow' import { useStore as useAppStore } from '@/app/components/app/store' import { useFeaturesStore } from '@/app/components/base/features/hooks' -import { toast } from '@/app/components/base/ui/toast' import { useGlobalPublicStore } from '@/context/global-public-context' import { collaborationManager } from '../collaboration/core/collaboration-manager' import { useWorkflowStore } from '../store' diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx index 12122c5b72..c535196691 100644 --- a/web/app/components/workflow/index.tsx +++ b/web/app/components/workflow/index.tsx @@ -14,7 +14,17 @@ import type { Node, } from './types' import type { VarInInspect } from '@/types/workflow' +import { + AlertDialog, + AlertDialogActions, + AlertDialogCancelButton, + AlertDialogConfirmButton, + AlertDialogContent, + AlertDialogDescription, + AlertDialogTitle, +} from '@langgenius/dify-ui/alert-dialog' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { useEventListener, } from 'ahooks' @@ -41,16 +51,6 @@ import ReactFlow, { useReactFlow, useStoreApi, } from 'reactflow' -import { - AlertDialog, - AlertDialogActions, - AlertDialogCancelButton, - AlertDialogConfirmButton, - AlertDialogContent, - AlertDialogDescription, - AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' -import { toast } from '@/app/components/base/ui/toast' import { IS_DEV } from '@/config' import { useEventEmitterContextContext } from '@/context/event-emitter' import { diff --git a/web/app/components/workflow/nodes/_base/components/__tests__/file-support.spec.tsx b/web/app/components/workflow/nodes/_base/components/__tests__/file-support.spec.tsx index b58b045f92..1a3e004c7e 100644 --- a/web/app/components/workflow/nodes/_base/components/__tests__/file-support.spec.tsx +++ b/web/app/components/workflow/nodes/_base/components/__tests__/file-support.spec.tsx @@ -19,7 +19,7 @@ vi.mock('@/app/components/base/file-uploader/hooks', () => ({ useFileSizeLimit: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/workflow/nodes/_base/components/add-button.tsx b/web/app/components/workflow/nodes/_base/components/add-button.tsx index cd32ee0dc6..d64ab5d098 100644 --- a/web/app/components/workflow/nodes/_base/components/add-button.tsx +++ b/web/app/components/workflow/nodes/_base/components/add-button.tsx @@ -1,11 +1,11 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiAddLine, } from '@remixicon/react' import * as React from 'react' -import { Button } from '@/app/components/base/ui/button' type Props = { className?: string diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx index d13488a0b9..d20acd7f19 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx @@ -4,11 +4,6 @@ import type { NodeOutPutVar } from '../../../types' import type { ToolVarInputs } from '../../tool/types' import type { CredentialFormSchema, CredentialFormSchemaNumberInput, CredentialFormSchemaTextInput } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { PluginMeta } from '@/app/components/plugins/types' -import { noop } from 'es-toolkit/function' -import { memo } from 'react' -import { useTranslation } from 'react-i18next' -import { Agent } from '@/app/components/base/icons/src/vender/workflow' -import ListEmpty from '@/app/components/base/list-empty' import { NumberField, NumberFieldControls, @@ -16,8 +11,13 @@ import { NumberFieldGroup, NumberFieldIncrement, NumberFieldInput, -} from '@/app/components/base/ui/number-field' -import { Slider } from '@/app/components/base/ui/slider' +} from '@langgenius/dify-ui/number-field' +import { Slider } from '@langgenius/dify-ui/slider' +import { noop } from 'es-toolkit/function' +import { memo } from 'react' +import { useTranslation } from 'react-i18next' +import { Agent } from '@/app/components/base/icons/src/vender/workflow' +import ListEmpty from '@/app/components/base/list-empty' import { FormTypeEnum, ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form' diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/__tests__/index.spec.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/__tests__/index.spec.tsx index a8837f6392..39b9fd8888 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/__tests__/index.spec.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/__tests__/index.spec.tsx @@ -1,11 +1,11 @@ import type { Props as FormProps } from '../form' import type { BeforeRunFormProps } from '../index' +import { toast } from '@langgenius/dify-ui/toast' import { fireEvent, render, screen } from '@testing-library/react' -import { toast } from '@/app/components/base/ui/toast' import { BlockEnum, InputVarType } from '@/app/components/workflow/types' import BeforeRunForm from '../index' -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: vi.fn(), }, diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx index f42046a2ad..f15c60e117 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx @@ -5,12 +5,12 @@ import type { Emoji } from '@/app/components/tools/types' import type { SpecialResultPanelProps } from '@/app/components/workflow/run/special-result-panel' import type { NodeRunningStatus } from '@/app/components/workflow/types' import type { HumanInputFormData } from '@/types/workflow' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import * as React from 'react' import { useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import Split from '@/app/components/workflow/nodes/_base/components/split' import SingleRunForm from '@/app/components/workflow/nodes/human-input/components/single-run-form' import { BlockEnum } from '@/app/components/workflow/types' diff --git a/web/app/components/workflow/nodes/_base/components/config-vision.tsx b/web/app/components/workflow/nodes/_base/components/config-vision.tsx index 109650cf51..d21e53368d 100644 --- a/web/app/components/workflow/nodes/_base/components/config-vision.tsx +++ b/web/app/components/workflow/nodes/_base/components/config-vision.tsx @@ -1,11 +1,11 @@ 'use client' import type { FC } from 'react' import type { ValueSelector, Var, VisionSetting } from '@/app/components/workflow/types' +import { Switch } from '@langgenius/dify-ui/switch' import { produce } from 'immer' import * as React from 'react' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' import Field from '@/app/components/workflow/nodes/_base/components/field' import ResolutionPicker from '@/app/components/workflow/nodes/llm/components/resolution-picker' diff --git a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-type-selector.tsx b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-type-selector.tsx index 92ca3ed335..07b6519e41 100644 --- a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-type-selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-type-selector.tsx @@ -1,3 +1,4 @@ +import { Button } from '@langgenius/dify-ui/button' import { RiArrowDownSLine, RiCheckLine, @@ -9,7 +10,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' import { ErrorHandleTypeEnum } from './types' type ErrorHandleTypeSelectorProps = { diff --git a/web/app/components/workflow/nodes/_base/components/input-number-with-slider.tsx b/web/app/components/workflow/nodes/_base/components/input-number-with-slider.tsx index 4e253060d6..08afb50b80 100644 --- a/web/app/components/workflow/nodes/_base/components/input-number-with-slider.tsx +++ b/web/app/components/workflow/nodes/_base/components/input-number-with-slider.tsx @@ -1,8 +1,8 @@ 'use client' import type { FC } from 'react' +import { Slider } from '@langgenius/dify-ui/slider' import * as React from 'react' import { useCallback } from 'react' -import { Slider } from '@/app/components/base/ui/slider' export type InputNumberWithSliderProps = { value: number diff --git a/web/app/components/workflow/nodes/_base/components/install-plugin-button.tsx b/web/app/components/workflow/nodes/_base/components/install-plugin-button.tsx index f6e5434a2d..dd2611b395 100644 --- a/web/app/components/workflow/nodes/_base/components/install-plugin-button.tsx +++ b/web/app/components/workflow/nodes/_base/components/install-plugin-button.tsx @@ -1,9 +1,9 @@ import type { ComponentProps, MouseEventHandler } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiInstallLine, RiLoader2Line } from '@remixicon/react' import { useState } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import checkTaskStatus from '@/app/components/plugins/install-plugin/base/check-task-status' import { TaskStatus } from '@/app/components/plugins/types' import { useCheckInstalled, useInstallPackageFromMarketPlace } from '@/service/use-plugins' diff --git a/web/app/components/workflow/nodes/_base/components/layout/__tests__/field-title.spec.tsx b/web/app/components/workflow/nodes/_base/components/layout/__tests__/field-title.spec.tsx index abd630cccb..c11b5ecfc8 100644 --- a/web/app/components/workflow/nodes/_base/components/layout/__tests__/field-title.spec.tsx +++ b/web/app/components/workflow/nodes/_base/components/layout/__tests__/field-title.spec.tsx @@ -1,7 +1,7 @@ import { fireEvent, render, screen } from '@testing-library/react' import { FieldTitle } from '../field-title' -vi.mock('@/app/components/base/ui/tooltip', () => ({ +vi.mock('@langgenius/dify-ui/tooltip', () => ({ Tooltip: ({ children }: { children: React.ReactNode }) =>
{children}
, TooltipTrigger: ({ render }: { render: React.ReactNode }) => <>{render}, TooltipContent: ({ children }: { children: React.ReactNode }) =>
{children}
, diff --git a/web/app/components/workflow/nodes/_base/components/layout/field-title.tsx b/web/app/components/workflow/nodes/_base/components/layout/field-title.tsx index 5a478c25d3..205e75bf67 100644 --- a/web/app/components/workflow/nodes/_base/components/layout/field-title.tsx +++ b/web/app/components/workflow/nodes/_base/components/layout/field-title.tsx @@ -1,10 +1,10 @@ import type { ReactNode } from 'react' import { cn } from '@langgenius/dify-ui/cn' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { memo, useState, } from 'react' -import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' export type FieldTitleProps = { title?: string diff --git a/web/app/components/workflow/nodes/_base/components/memory-config.tsx b/web/app/components/workflow/nodes/_base/components/memory-config.tsx index 15254ad4f4..ed193490a5 100644 --- a/web/app/components/workflow/nodes/_base/components/memory-config.tsx +++ b/web/app/components/workflow/nodes/_base/components/memory-config.tsx @@ -2,13 +2,13 @@ import type { FC } from 'react' import type { Memory } from '../../../types' import { cn } from '@langgenius/dify-ui/cn' +import { Slider } from '@langgenius/dify-ui/slider' +import { Switch } from '@langgenius/dify-ui/switch' import { produce } from 'immer' import * as React from 'react' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import Switch from '@/app/components/base/switch' -import { Slider } from '@/app/components/base/ui/slider' import Field from '@/app/components/workflow/nodes/_base/components/field' import { MemoryRole } from '../../../types' diff --git a/web/app/components/workflow/nodes/_base/components/next-step/__tests__/operator.spec.tsx b/web/app/components/workflow/nodes/_base/components/next-step/__tests__/operator.spec.tsx index 9afa29642d..62cf571ae0 100644 --- a/web/app/components/workflow/nodes/_base/components/next-step/__tests__/operator.spec.tsx +++ b/web/app/components/workflow/nodes/_base/components/next-step/__tests__/operator.spec.tsx @@ -18,7 +18,7 @@ vi.mock('react-i18next', () => ({ }), })) -vi.mock('@/app/components/base/ui/dropdown-menu', async () => { +vi.mock('@langgenius/dify-ui/dropdown-menu', async () => { const React = await import('react') const DropdownMenuContext = React.createContext<{ open: boolean, setOpen: (open: boolean) => void } | null>(null) @@ -49,7 +49,7 @@ vi.mock('@/app/components/base/ui/dropdown-menu', async () => { } }) -vi.mock('@/app/components/base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: ({ children, className }: { children: ReactNode, className?: string }) => ( ), diff --git a/web/app/components/workflow/nodes/human-input/components/__tests__/user-action.spec.tsx b/web/app/components/workflow/nodes/human-input/components/__tests__/user-action.spec.tsx index 13f8463676..a47a012f49 100644 --- a/web/app/components/workflow/nodes/human-input/components/__tests__/user-action.spec.tsx +++ b/web/app/components/workflow/nodes/human-input/components/__tests__/user-action.spec.tsx @@ -27,7 +27,7 @@ vi.mock('@/app/components/base/input', () => ({ ), })) -vi.mock('@/app/components/base/ui/button', () => ({ +vi.mock('@langgenius/dify-ui/button', () => ({ Button: (props: { children?: ReactNode onClick?: () => void @@ -38,7 +38,7 @@ vi.mock('@/app/components/base/ui/button', () => ({ ), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ __esModule: true, toast: { success: (message: string) => mockNotify({ type: 'success', message }), diff --git a/web/app/components/workflow/nodes/human-input/components/button-style-dropdown.tsx b/web/app/components/workflow/nodes/human-input/components/button-style-dropdown.tsx index 363e0070c0..44ddbbfa34 100644 --- a/web/app/components/workflow/nodes/human-input/components/button-style-dropdown.tsx +++ b/web/app/components/workflow/nodes/human-input/components/button-style-dropdown.tsx @@ -1,4 +1,5 @@ import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiFontSize, @@ -11,7 +12,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' import { UserActionButtonType } from '../types' const i18nPrefix = 'nodes.humanInput' diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/email-configure-modal.spec.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/email-configure-modal.spec.tsx index cec9ffe69a..c5b5c680dc 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/email-configure-modal.spec.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/email-configure-modal.spec.tsx @@ -5,7 +5,7 @@ import EmailConfigureModal from '../email-configure-modal' const mockToastError = vi.hoisted(() => vi.fn()) const mockUseAppContextSelector = vi.hoisted(() => vi.fn()) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: (message: string) => mockToastError(message), }, diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx index 1d77248037..de38564d95 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx @@ -3,6 +3,9 @@ import type { Node, NodeOutPutVar, } from '@/app/components/workflow/types' +import { Button } from '@langgenius/dify-ui/button' +import { Switch } from '@langgenius/dify-ui/switch' +import { toast } from '@langgenius/dify-ui/toast' import { RiBugLine, RiCloseLine } from '@remixicon/react' import { noop } from 'es-toolkit/compat' import { memo, useCallback, useState } from 'react' @@ -10,9 +13,6 @@ import { Trans, useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import Input from '@/app/components/base/input' import Modal from '@/app/components/base/modal' -import Switch from '@/app/components/base/switch' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { useSelector as useAppContextWithSelector } from '@/context/app-context' import MailBodyInput from './mail-body-input' import Recipient from './recipient' diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx index eae2e293e1..9cd63e96dd 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx @@ -4,7 +4,9 @@ import type { Node, NodeOutPutVar, } from '@/app/components/workflow/types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { Switch } from '@langgenius/dify-ui/switch' import { RiDeleteBinLine, RiEqualizer2Line, @@ -16,9 +18,7 @@ import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' import Badge from '@/app/components/base/badge/index' -import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' -import { Button } from '@/app/components/base/ui/button' import Indicator from '@/app/components/header/indicator' import { useSelector as useAppContextWithSelector } from '@/context/app-context' import { DeliveryMethodType } from '../../types' diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/__tests__/index.spec.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/__tests__/index.spec.tsx index 7fe768781b..cd95a8e9e3 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/__tests__/index.spec.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/__tests__/index.spec.tsx @@ -17,9 +17,8 @@ vi.mock('@/service/use-common', () => ({ useMembers: () => mockUseMembers(), })) -vi.mock('@/app/components/base/switch', () => ({ - __esModule: true, - default: (props: { +vi.mock('@langgenius/dify-ui/switch', () => ({ + Switch: (props: { checked: boolean onCheckedChange: (value: boolean) => void }) => ( diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/email-item.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/email-item.tsx index 8d370266b0..604736b2a9 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/email-item.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/email-item.tsx @@ -1,10 +1,10 @@ import type { Recipient as RecipientItem } from '../../../types' import type { Member } from '@/models/common' +import { Avatar } from '@langgenius/dify-ui/avatar' import { cn } from '@langgenius/dify-ui/cn' import { RiCloseCircleFill, RiErrorWarningFill } from '@remixicon/react' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Avatar } from '@/app/components/base/ui/avatar' type Props = { email: string diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/index.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/index.tsx index 36483d33d6..42bd6df171 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/index.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/index.tsx @@ -1,10 +1,10 @@ import type { RecipientData, Recipient as RecipientItem } from '../../../types' import { cn } from '@langgenius/dify-ui/cn' +import { Switch } from '@langgenius/dify-ui/switch' import { RiGroupLine } from '@remixicon/react' import { produce } from 'immer' import { memo } from 'react' import { useTranslation } from 'react-i18next' -import Switch from '@/app/components/base/switch' import { useAppContext } from '@/context/app-context' import { useMembers } from '@/service/use-common' import EmailInput from './email-input' diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-list.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-list.tsx index c80c22639e..0e262e839e 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-list.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-list.tsx @@ -2,11 +2,11 @@ import type { FC } from 'react' import type { Recipient } from '@/app/components/workflow/nodes/human-input/types' import type { Member } from '@/models/common' +import { Avatar } from '@langgenius/dify-ui/avatar' import { cn } from '@langgenius/dify-ui/cn' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Avatar } from '@/app/components/base/ui/avatar' const i18nPrefix = 'nodes.humanInput' diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-selector.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-selector.tsx index 827e02c8bb..c2df6afb15 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-selector.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-selector.tsx @@ -2,6 +2,7 @@ import type { FC } from 'react' import type { Recipient } from '@/app/components/workflow/nodes/human-input/types' import type { Member } from '@/models/common' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiContactsBookLine, @@ -9,7 +10,6 @@ import { import { useState } from 'react' import { useTranslation } from 'react-i18next' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' import MemberList from './member-list' const i18nPrefix = 'nodes.humanInput' diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx index cef6d1380d..9ad52fa13e 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx @@ -4,6 +4,7 @@ import type { NodeOutPutVar, ValueSelector, } from '@/app/components/workflow/types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiArrowRightSFill, RiCloseLine } from '@remixicon/react' import { noop, unionBy } from 'es-toolkit/compat' @@ -13,7 +14,6 @@ import { useStore as useAppStore } from '@/app/components/app/store' import Divider from '@/app/components/base/divider' import Modal from '@/app/components/base/modal' import { getInputVars as doGetInputVars } from '@/app/components/base/prompt-editor/constants' -import { Button } from '@/app/components/base/ui/button' import FormItem from '@/app/components/workflow/nodes/_base/components/before-run-form/form-item' import { getNodeInfoById, diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/upgrade-modal.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/upgrade-modal.tsx index c7b46732d0..060ec2428c 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/upgrade-modal.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/upgrade-modal.tsx @@ -1,3 +1,4 @@ +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiMailSendFill, @@ -7,7 +8,6 @@ import { useTranslation } from 'react-i18next' import { SparklesSoft } from '@/app/components/base/icons/src/public/common' import Modal from '@/app/components/base/modal' import PremiumBadge from '@/app/components/base/premium-badge' -import { Button } from '@/app/components/base/ui/button' import { useModalContextSelector } from '@/context/modal-context' type UpgradeModalProps = { diff --git a/web/app/components/workflow/nodes/human-input/components/form-content-preview.tsx b/web/app/components/workflow/nodes/human-input/components/form-content-preview.tsx index 1364941edf..73e8850789 100644 --- a/web/app/components/workflow/nodes/human-input/components/form-content-preview.tsx +++ b/web/app/components/workflow/nodes/human-input/components/form-content-preview.tsx @@ -1,14 +1,14 @@ 'use client' +import type { ButtonProps } from '@langgenius/dify-ui/button' import type { FC } from 'react' import type { FormInputItem, UserAction } from '../types' -import type { ButtonProps } from '@/app/components/base/ui/button' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Badge from '@/app/components/base/badge' import { getButtonStyle } from '@/app/components/base/chat/chat/answer/human-input-content/utils' import { Markdown } from '@/app/components/base/markdown' -import { Button } from '@/app/components/base/ui/button' import { useStore } from '@/app/components/workflow/store' import useNodes from '@/app/components/workflow/store/workflow/use-nodes' import { Note, rehypeNotes, rehypeVariable, Variable } from './variable-in-markdown' diff --git a/web/app/components/workflow/nodes/human-input/components/single-run-form.tsx b/web/app/components/workflow/nodes/human-input/components/single-run-form.tsx index dc735830c6..6226b6c06f 100644 --- a/web/app/components/workflow/nodes/human-input/components/single-run-form.tsx +++ b/web/app/components/workflow/nodes/human-input/components/single-run-form.tsx @@ -1,15 +1,15 @@ 'use client' -import type { ButtonProps } from '@/app/components/base/ui/button' +import type { ButtonProps } from '@langgenius/dify-ui/button' import type { UserAction } from '@/app/components/workflow/nodes/human-input/types' import type { HumanInputFormData } from '@/types/workflow' +import { Button } from '@langgenius/dify-ui/button' import { RiArrowLeftLine } from '@remixicon/react' -import * as React from 'react' +import * as React from 'react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import ContentItem from '@/app/components/base/chat/chat/answer/human-input-content/content-item' import { getButtonStyle, initializeInputs, splitByOutputVar } from '@/app/components/base/chat/chat/answer/human-input-content/utils' -import { Button } from '@/app/components/base/ui/button' type Props = { nodeName: string diff --git a/web/app/components/workflow/nodes/human-input/components/user-action.tsx b/web/app/components/workflow/nodes/human-input/components/user-action.tsx index 6ef5609a64..a83ea4f8f2 100644 --- a/web/app/components/workflow/nodes/human-input/components/user-action.tsx +++ b/web/app/components/workflow/nodes/human-input/components/user-action.tsx @@ -1,13 +1,13 @@ import type { FC } from 'react' import type { UserAction } from '../types' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiDeleteBinLine, } from '@remixicon/react' import * as React from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import ButtonStyleDropdown from './button-style-dropdown' const i18nPrefix = 'nodes.humanInput' diff --git a/web/app/components/workflow/nodes/human-input/panel.tsx b/web/app/components/workflow/nodes/human-input/panel.tsx index 846b135fcf..b7b65e7de8 100644 --- a/web/app/components/workflow/nodes/human-input/panel.tsx +++ b/web/app/components/workflow/nodes/human-input/panel.tsx @@ -1,7 +1,9 @@ import type { FC } from 'react' import type { HumanInputNodeType } from './types' import type { NodePanelProps, Var } from '@/app/components/workflow/types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiAddLine, RiClipboardLine, @@ -17,8 +19,6 @@ import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Divider from '@/app/components/base/divider' import Tooltip from '@/app/components/base/tooltip' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' import Split from '@/app/components/workflow/nodes/_base/components/split' import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' diff --git a/web/app/components/workflow/nodes/if-else/components/condition-add.tsx b/web/app/components/workflow/nodes/if-else/components/condition-add.tsx index 3b6d729078..85a45ac5c7 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-add.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-add.tsx @@ -4,6 +4,7 @@ import type { ValueSelector, Var, } from '@/app/components/workflow/types' +import { Button } from '@langgenius/dify-ui/button' import { RiAddLine } from '@remixicon/react' import { useCallback, @@ -15,7 +16,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' type ConditionAddProps = { diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx index 998cc48205..83dc97f088 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx @@ -1,5 +1,6 @@ import type { ComparisonOperator } from '../../types' import type { VarType } from '@/app/components/workflow/types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiArrowDownSLine } from '@remixicon/react' import { @@ -12,7 +13,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' import { getOperators, isComparisonOperatorNeedTranslate } from '../../utils' const i18nPrefix = 'nodes.ifElse' diff --git a/web/app/components/workflow/nodes/if-else/components/condition-number-input.tsx b/web/app/components/workflow/nodes/if-else/components/condition-number-input.tsx index 7bcb4137b5..954f713a78 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-number-input.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-number-input.tsx @@ -2,6 +2,7 @@ import type { NodeOutPutVar, ValueSelector, } from '@/app/components/workflow/types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiArrowDownSLine } from '@remixicon/react' import { useBoolean } from 'ahooks' @@ -18,7 +19,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' import { VarType } from '@/app/components/workflow/types' import { variableTransformer } from '@/app/components/workflow/utils' diff --git a/web/app/components/workflow/nodes/if-else/components/condition-wrap.tsx b/web/app/components/workflow/nodes/if-else/components/condition-wrap.tsx index 1744ee6490..92d70b37b1 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-wrap.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-wrap.tsx @@ -2,6 +2,7 @@ import type { FC } from 'react' import type { Node, NodeOutPutVar, Var } from '../../../types' import type { CaseItem, HandleAddCondition, HandleAddSubVariableCondition, HandleRemoveCondition, handleRemoveSubVariableCondition, HandleToggleConditionLogicalOperator, HandleToggleSubVariableConditionLogicalOperator, HandleUpdateCondition, HandleUpdateSubVariableCondition } from '../types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiAddLine, @@ -14,7 +15,6 @@ import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { ReactSortable } from 'react-sortablejs' import { PortalSelect as Select } from '@/app/components/base/select' -import { Button } from '@/app/components/base/ui/button' import { VarType } from '../../../types' import { SUB_VARIABLES } from '../../constants' import { useGetAvailableVars } from '../../variable-assigner/hooks' diff --git a/web/app/components/workflow/nodes/if-else/panel.tsx b/web/app/components/workflow/nodes/if-else/panel.tsx index e8dae6fad9..221b00d1f7 100644 --- a/web/app/components/workflow/nodes/if-else/panel.tsx +++ b/web/app/components/workflow/nodes/if-else/panel.tsx @@ -1,6 +1,7 @@ import type { FC } from 'react' import type { IfElseNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' +import { Button } from '@langgenius/dify-ui/button' import { RiAddLine, } from '@remixicon/react' @@ -8,7 +9,6 @@ import { memo, } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import Field from '@/app/components/workflow/nodes/_base/components/field' import ConditionWrap from './components/condition-wrap' import useConfig from './use-config' diff --git a/web/app/components/workflow/nodes/iteration/__tests__/integration.spec.tsx b/web/app/components/workflow/nodes/iteration/__tests__/integration.spec.tsx index dc7538144e..26b938bf6e 100644 --- a/web/app/components/workflow/nodes/iteration/__tests__/integration.spec.tsx +++ b/web/app/components/workflow/nodes/iteration/__tests__/integration.spec.tsx @@ -1,9 +1,9 @@ import type { ReactNode } from 'react' import type { IterationNodeType } from '../types' import type { PanelProps } from '@/types/workflow' +import { toast } from '@langgenius/dify-ui/toast' import { fireEvent, render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { toast } from '@/app/components/base/ui/toast' import { ErrorHandleMode } from '@/app/components/workflow/types' import { BlockEnum, VarType } from '../../../types' import AddBlock from '../add-block' @@ -15,7 +15,7 @@ const mockHandleNodeAdd = vi.fn() const mockHandleNodeIterationRerender = vi.fn() let mockNodesReadOnly = false -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/workflow/nodes/iteration/node.tsx b/web/app/components/workflow/nodes/iteration/node.tsx index ad9cb3b0fe..a3aee372d7 100644 --- a/web/app/components/workflow/nodes/iteration/node.tsx +++ b/web/app/components/workflow/nodes/iteration/node.tsx @@ -2,6 +2,7 @@ import type { FC } from 'react' import type { IterationNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { memo, useEffect, @@ -13,7 +14,6 @@ import { useNodesInitialized, useViewport, } from 'reactflow' -import { toast } from '@/app/components/base/ui/toast' import { IterationStartNodeDumb } from '../iteration-start' import AddBlock from './add-block' import { useNodeIterationInteractions } from './use-interactions' diff --git a/web/app/components/workflow/nodes/iteration/panel.tsx b/web/app/components/workflow/nodes/iteration/panel.tsx index 7128c2d4b4..c9ed2de363 100644 --- a/web/app/components/workflow/nodes/iteration/panel.tsx +++ b/web/app/components/workflow/nodes/iteration/panel.tsx @@ -1,12 +1,12 @@ import type { FC } from 'react' import type { IterationNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' +import { Slider } from '@langgenius/dify-ui/slider' +import { Switch } from '@langgenius/dify-ui/switch' import * as React from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import Select from '@/app/components/base/select' -import Switch from '@/app/components/base/switch' -import { Slider } from '@/app/components/base/ui/slider' import Field from '@/app/components/workflow/nodes/_base/components/field' import { ErrorHandleMode } from '@/app/components/workflow/types' import { MAX_PARALLEL_LIMIT } from '@/config' diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/index.tsx b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/index.tsx index a7bba6879e..0920e8a8a7 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/index.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/index.tsx @@ -1,7 +1,7 @@ import type { ChunkStructureEnum } from '../../types' +import { Button } from '@langgenius/dify-ui/button' import { memo } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import { Field } from '@/app/components/workflow/nodes/_base/components/layout' import OptionCard from '../option-card' import { useChunkStructure } from './hooks' diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx index 5018bbc784..7ab8de508f 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx @@ -1,6 +1,7 @@ import type { ReactNode } from 'react' import type { ChunkStructureEnum } from '../../types' import type { Option } from './type' +import { Button } from '@langgenius/dify-ui/button' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { @@ -8,7 +9,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' import OptionCard from '../option-card' type SelectorProps = { diff --git a/web/app/components/workflow/nodes/knowledge-base/components/index-method.tsx b/web/app/components/workflow/nodes/knowledge-base/components/index-method.tsx index 12cc0f1eb5..70ff0e47eb 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/index-method.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/index-method.tsx @@ -1,4 +1,5 @@ import { cn } from '@langgenius/dify-ui/cn' +import { Slider } from '@langgenius/dify-ui/slider' import { RiQuestionLine } from '@remixicon/react' import { memo, @@ -11,7 +12,6 @@ import { } from '@/app/components/base/icons/src/vender/knowledge' import Input from '@/app/components/base/input' import Tooltip from '@/app/components/base/tooltip' -import { Slider } from '@/app/components/base/ui/slider' import { Field } from '@/app/components/workflow/nodes/_base/components/layout' import { ChunkStructureEnum, diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/search-method-option.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/search-method-option.tsx index fe1d863944..a1f601cce9 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/search-method-option.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/search-method-option.tsx @@ -8,6 +8,7 @@ import type { Option, } from './type' import { cn } from '@langgenius/dify-ui/cn' +import { Switch } from '@langgenius/dify-ui/switch' import { memo, useCallback, @@ -16,7 +17,6 @@ import { import { useTranslation } from 'react-i18next' import WeightedScoreComponent from '@/app/components/app/configuration/dataset-config/params-config/weighted-score' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' -import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' import { DEFAULT_WEIGHTED_SCORE } from '@/models/datasets' import { diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx index 5136d4897f..814b3cea6d 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx @@ -1,7 +1,3 @@ -import { memo, useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import Switch from '@/app/components/base/switch' -import Tooltip from '@/app/components/base/tooltip' import { NumberField, NumberFieldControls, @@ -9,7 +5,11 @@ import { NumberFieldGroup, NumberFieldIncrement, NumberFieldInput, -} from '@/app/components/base/ui/number-field' +} from '@langgenius/dify-ui/number-field' +import { Switch } from '@langgenius/dify-ui/switch' +import { memo, useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import Tooltip from '@/app/components/base/tooltip' import { env } from '@/env' export type TopKAndScoreThresholdProps = { diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/add-condition.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/add-condition.tsx index e94caf7b30..ea7870431a 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/add-condition.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/add-condition.tsx @@ -1,5 +1,6 @@ import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' import type { MetadataInDoc } from '@/models/datasets' +import { Button } from '@langgenius/dify-ui/button' import { RiAddLine, } from '@remixicon/react' @@ -15,7 +16,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' import MetadataIcon from './metadata-icon' const AddCondition = ({ diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx index d6828a5392..f14743ce9f 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx @@ -2,6 +2,7 @@ import type { ComparisonOperator, MetadataFilteringVariableType, } from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiArrowDownSLine } from '@remixicon/react' import { @@ -14,7 +15,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' import { getOperators, isComparisonOperatorNeedTranslate, diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx index b69bd05cdd..12d7c9cc69 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx @@ -1,3 +1,4 @@ +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiArrowDownSLine } from '@remixicon/react' import { capitalize } from 'es-toolkit/string' @@ -7,7 +8,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' export type ConditionValueMethodProps = { valueMethod?: string diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx index 67939a6448..396fd069e7 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx @@ -1,3 +1,4 @@ +import { Button } from '@langgenius/dify-ui/button' import { RiArrowDownSLine, RiCheckLine, @@ -9,7 +10,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' import { MetadataFilteringModeEnum } from '@/app/components/workflow/nodes/knowledge-retrieval/types' type MetadataFilterSelectorProps = { diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-trigger.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-trigger.tsx index 1553da1e1f..dd530ae679 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-trigger.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-trigger.tsx @@ -1,4 +1,5 @@ import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import { Button } from '@langgenius/dify-ui/button' import { RiFilter3Line } from '@remixicon/react' import { useEffect, @@ -10,7 +11,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' import MetadataPanel from './metadata-panel' const MetadataTrigger = ({ diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx index 3f8198a822..da71682b35 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx @@ -5,6 +5,7 @@ import type { MultipleRetrievalConfig, SingleRetrievalConfig } from '../types' import type { ModelParameterModalProps } from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' import type { DataSet } from '@/models/datasets' import type { DatasetConfigs } from '@/models/debug' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiEqualizer2Line } from '@remixicon/react' import * as React from 'react' @@ -16,7 +17,6 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Button } from '@/app/components/base/ui/button' import { DATASET_DEFAULT } from '@/config' import { RETRIEVE_TYPE } from '@/types/app' diff --git a/web/app/components/workflow/nodes/list-operator/__tests__/panel.spec.tsx b/web/app/components/workflow/nodes/list-operator/__tests__/panel.spec.tsx index 9e5147bc92..945ca70104 100644 --- a/web/app/components/workflow/nodes/list-operator/__tests__/panel.spec.tsx +++ b/web/app/components/workflow/nodes/list-operator/__tests__/panel.spec.tsx @@ -22,9 +22,8 @@ vi.mock('../use-config', () => ({ default: (...args: unknown[]) => mockUseConfig(...args), })) -vi.mock('@/app/components/base/switch', () => ({ - __esModule: true, - default: (props: { +vi.mock('@langgenius/dify-ui/switch', () => ({ + Switch: (props: { checked?: boolean disabled?: boolean onCheckedChange: (value: boolean) => void diff --git a/web/app/components/workflow/nodes/list-operator/components/__tests__/limit-config.spec.tsx b/web/app/components/workflow/nodes/list-operator/components/__tests__/limit-config.spec.tsx index fbc3356b32..1314b4600b 100644 --- a/web/app/components/workflow/nodes/list-operator/components/__tests__/limit-config.spec.tsx +++ b/web/app/components/workflow/nodes/list-operator/components/__tests__/limit-config.spec.tsx @@ -19,8 +19,8 @@ type MockSliderProps = { const mockSwitch = vi.fn<(props: MockSwitchProps) => void>() const mockSlider = vi.fn<(props: MockSliderProps) => void>() -vi.mock('@/app/components/base/switch', () => ({ - default: (props: MockSwitchProps) => { +vi.mock('@langgenius/dify-ui/switch', () => ({ + Switch: (props: MockSwitchProps) => { mockSwitch(props) return ( diff --git a/web/app/components/workflow/panel/comments-panel/index.tsx b/web/app/components/workflow/panel/comments-panel/index.tsx index 21b3c3c4e6..05480abb2b 100644 --- a/web/app/components/workflow/panel/comments-panel/index.tsx +++ b/web/app/components/workflow/panel/comments-panel/index.tsx @@ -1,10 +1,10 @@ import type { WorkflowCommentList } from '@/service/workflow-comment' import { cn } from '@langgenius/dify-ui/cn' +import { Switch } from '@langgenius/dify-ui/switch' import { RiCheckboxCircleFill, RiCheckboxCircleLine, RiCheckLine, RiCloseLine, RiFilter3Line } from '@remixicon/react' import { memo, useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' -import Switch from '@/app/components/base/switch' import { UserAvatarList } from '@/app/components/base/user-avatar-list' import { collaborationManager } from '@/app/components/workflow/collaboration/core/collaboration-manager' import { useWorkflowComment } from '@/app/components/workflow/hooks/use-workflow-comment' diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks.spec.ts index 425dd90e87..9a2b8a8df7 100644 --- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks.spec.ts +++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks.spec.ts @@ -26,7 +26,7 @@ vi.mock('@/service/workflow', () => ({ submitHumanInputForm: (...args: unknown[]) => mockSubmitHumanInputForm(...args), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-resume.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-resume.spec.ts index 04c19f50e9..5a608dcc5b 100644 --- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-resume.spec.ts +++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-resume.spec.ts @@ -27,7 +27,7 @@ vi.mock('@/service/workflow', () => ({ submitHumanInputForm: (...args: any[]) => mockSubmitHumanInputForm(...args), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-send.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-send.spec.ts index 0f6ae5d713..fb045c4723 100644 --- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-send.spec.ts +++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-send.spec.ts @@ -26,7 +26,7 @@ vi.mock('@/service/workflow', () => ({ submitHumanInputForm: (...args: any[]) => mockSubmitHumanInputForm(...args), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-stop-restart.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-stop-restart.spec.ts index d0b1908606..2868471bd3 100644 --- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-stop-restart.spec.ts +++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-stop-restart.spec.ts @@ -27,7 +27,7 @@ vi.mock('@/service/workflow', () => ({ submitHumanInputForm: (...args: any[]) => mockSubmitHumanInputForm(...args), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/misc.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/misc.spec.ts index 549fe08e05..cd2eaa0e00 100644 --- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/misc.spec.ts +++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/misc.spec.ts @@ -27,7 +27,7 @@ vi.mock('@/service/workflow', () => ({ submitHumanInputForm: (...args: any[]) => mockSubmitHumanInputForm(...args), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/opening-statement.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/opening-statement.spec.ts index 57db5502b5..cb45029340 100644 --- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/opening-statement.spec.ts +++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/opening-statement.spec.ts @@ -27,7 +27,7 @@ vi.mock('@/service/workflow', () => ({ submitHumanInputForm: (...args: any[]) => mockSubmitHumanInputForm(...args), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/sse-callbacks.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/sse-callbacks.spec.ts index 03857ce3d6..f8f3a02c5c 100644 --- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/sse-callbacks.spec.ts +++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/sse-callbacks.spec.ts @@ -26,7 +26,7 @@ vi.mock('@/service/workflow', () => ({ submitHumanInputForm: (...args: any[]) => mockSubmitHumanInputForm(...args), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/workflow/panel/debug-and-preview/hooks.ts b/web/app/components/workflow/panel/debug-and-preview/hooks.ts index 6eef69bfdb..a94e4c1edd 100644 --- a/web/app/components/workflow/panel/debug-and-preview/hooks.ts +++ b/web/app/components/workflow/panel/debug-and-preview/hooks.ts @@ -6,6 +6,7 @@ import type { } from '@/app/components/base/chat/types' import type { FileEntity } from '@/app/components/base/file-uploader/types' import type { IOtherOptions } from '@/service/base' +import { toast } from '@langgenius/dify-ui/toast' import { uniqBy } from 'es-toolkit/compat' import { produce, setAutoFreeze } from 'immer' import { @@ -26,7 +27,6 @@ import { getProcessedFiles, getProcessedFilesFromResponse, } from '@/app/components/base/file-uploader/utils' -import { toast } from '@/app/components/base/ui/toast' import { CUSTOM_NODE, } from '@/app/components/workflow/constants' diff --git a/web/app/components/workflow/panel/env-panel/__tests__/variable-modal.spec.tsx b/web/app/components/workflow/panel/env-panel/__tests__/variable-modal.spec.tsx index 7e97ef62dc..258784b692 100644 --- a/web/app/components/workflow/panel/env-panel/__tests__/variable-modal.spec.tsx +++ b/web/app/components/workflow/panel/env-panel/__tests__/variable-modal.spec.tsx @@ -1,14 +1,14 @@ import type { ReactElement } from 'react' import type { Shape } from '@/app/components/workflow/store/workflow' import type { EnvironmentVariable } from '@/app/components/workflow/types' +import { toast } from '@langgenius/dify-ui/toast' import { fireEvent, render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { toast } from '@/app/components/base/ui/toast' import { WorkflowContext } from '@/app/components/workflow/context' import { createWorkflowStore } from '@/app/components/workflow/store/workflow' import VariableModal from '../variable-modal' -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/workflow/panel/env-panel/variable-modal.tsx b/web/app/components/workflow/panel/env-panel/variable-modal.tsx index e824dd9f08..267c014e1d 100644 --- a/web/app/components/workflow/panel/env-panel/variable-modal.tsx +++ b/web/app/components/workflow/panel/env-panel/variable-modal.tsx @@ -1,5 +1,7 @@ import type { EnvironmentVariable } from '@/app/components/workflow/types' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiCloseLine } from '@remixicon/react' import * as React from 'react' import { useEffect } from 'react' @@ -7,8 +9,6 @@ import { useTranslation } from 'react-i18next' import { v4 as uuid4 } from 'uuid' import Input from '@/app/components/base/input' import Tooltip from '@/app/components/base/tooltip' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { useWorkflowStore } from '@/app/components/workflow/store' import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' diff --git a/web/app/components/workflow/panel/env-panel/variable-trigger.tsx b/web/app/components/workflow/panel/env-panel/variable-trigger.tsx index e075238e48..378fc1c1b5 100644 --- a/web/app/components/workflow/panel/env-panel/variable-trigger.tsx +++ b/web/app/components/workflow/panel/env-panel/variable-trigger.tsx @@ -1,10 +1,10 @@ 'use client' import type { EnvironmentVariable } from '@/app/components/workflow/types' +import { Button } from '@langgenius/dify-ui/button' +import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover' import { RiAddLine } from '@remixicon/react' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' -import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover' import VariableModal from '@/app/components/workflow/panel/env-panel/variable-modal' type Props = { diff --git a/web/app/components/workflow/panel/inputs-panel.tsx b/web/app/components/workflow/panel/inputs-panel.tsx index 4e6255a2c0..10004236dd 100644 --- a/web/app/components/workflow/panel/inputs-panel.tsx +++ b/web/app/components/workflow/panel/inputs-panel.tsx @@ -1,4 +1,5 @@ import type { StartNodeType } from '../nodes/start/types' +import { Button } from '@langgenius/dify-ui/button' import { memo, useCallback, @@ -10,7 +11,6 @@ import { useCheckInputsForms } from '@/app/components/base/chat/chat/check-input import { getProcessedInputs, } from '@/app/components/base/chat/chat/utils' -import { Button } from '@/app/components/base/ui/button' import { TransferMethod } from '../../base/text-generation/types' import { useWorkflowRun } from '../hooks' import { useHooksStore } from '../hooks-store' diff --git a/web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/menu-item.spec.tsx b/web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/menu-item.spec.tsx index 844e189067..7dfc362a90 100644 --- a/web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/menu-item.spec.tsx +++ b/web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/menu-item.spec.tsx @@ -1,6 +1,6 @@ +import { DropdownMenu, DropdownMenuContent } from '@langgenius/dify-ui/dropdown-menu' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { DropdownMenu, DropdownMenuContent } from '@/app/components/base/ui/dropdown-menu' import { VersionHistoryContextMenuOptions } from '../../../../types' import MenuItem from '../menu-item' diff --git a/web/app/components/workflow/panel/version-history-panel/context-menu/index.tsx b/web/app/components/workflow/panel/version-history-panel/context-menu/index.tsx index f063902753..1b90166f65 100644 --- a/web/app/components/workflow/panel/version-history-panel/context-menu/index.tsx +++ b/web/app/components/workflow/panel/version-history-panel/context-menu/index.tsx @@ -1,13 +1,13 @@ import type { FC } from 'react' -import { RiMoreFill } from '@remixicon/react' -import * as React from 'react' -import { Button } from '@/app/components/base/ui/button' +import { Button } from '@langgenius/dify-ui/button' import { DropdownMenu, DropdownMenuContent, DropdownMenuSeparator, DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' +} from '@langgenius/dify-ui/dropdown-menu' +import { RiMoreFill } from '@remixicon/react' +import * as React from 'react' import { VersionHistoryContextMenuOptions } from '../../../types' import MenuItem from './menu-item' import useContextMenu from './use-context-menu' diff --git a/web/app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx b/web/app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx index b1d9ef6ec5..0c0096ab25 100644 --- a/web/app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx +++ b/web/app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx @@ -1,8 +1,8 @@ import type { FC } from 'react' import type { VersionHistoryContextMenuOptions } from '../../../types' import { cn } from '@langgenius/dify-ui/cn' +import { DropdownMenuItem } from '@langgenius/dify-ui/dropdown-menu' import * as React from 'react' -import { DropdownMenuItem } from '@/app/components/base/ui/dropdown-menu' type MenuItemProps = { item: { diff --git a/web/app/components/workflow/panel/version-history-panel/delete-confirm-modal.tsx b/web/app/components/workflow/panel/version-history-panel/delete-confirm-modal.tsx index 4779be8984..1f7948c41b 100644 --- a/web/app/components/workflow/panel/version-history-panel/delete-confirm-modal.tsx +++ b/web/app/components/workflow/panel/version-history-panel/delete-confirm-modal.tsx @@ -1,9 +1,9 @@ import type { FC } from 'react' import type { VersionHistory } from '@/types/workflow' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' type DeleteConfirmModalProps = { isOpen: boolean diff --git a/web/app/components/workflow/panel/version-history-panel/empty.tsx b/web/app/components/workflow/panel/version-history-panel/empty.tsx index 2c8f6655e1..21751234d7 100644 --- a/web/app/components/workflow/panel/version-history-panel/empty.tsx +++ b/web/app/components/workflow/panel/version-history-panel/empty.tsx @@ -1,8 +1,8 @@ import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' import { RiHistoryLine } from '@remixicon/react' import * as React from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' type EmptyProps = { onResetFilter: () => void diff --git a/web/app/components/workflow/panel/version-history-panel/filter/filter-switch.tsx b/web/app/components/workflow/panel/version-history-panel/filter/filter-switch.tsx index 1df519d0f3..587dc43bdf 100644 --- a/web/app/components/workflow/panel/version-history-panel/filter/filter-switch.tsx +++ b/web/app/components/workflow/panel/version-history-panel/filter/filter-switch.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' +import { Switch } from '@langgenius/dify-ui/switch' import * as React from 'react' import { useTranslation } from 'react-i18next' -import Switch from '@/app/components/base/switch' type FilterSwitchProps = { enabled: boolean diff --git a/web/app/components/workflow/panel/version-history-panel/index.tsx b/web/app/components/workflow/panel/version-history-panel/index.tsx index 10dd0e794a..eb1f5c962e 100644 --- a/web/app/components/workflow/panel/version-history-panel/index.tsx +++ b/web/app/components/workflow/panel/version-history-panel/index.tsx @@ -1,5 +1,6 @@ 'use client' import type { VersionHistory } from '@/types/workflow' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowDownDoubleLine, RiCloseLine, RiLoader2Line } from '@remixicon/react' import copy from 'copy-to-clipboard' import * as React from 'react' @@ -7,7 +8,6 @@ import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import VersionInfoModal from '@/app/components/app/app-publisher/version-info-modal' import Divider from '@/app/components/base/divider' -import { toast } from '@/app/components/base/ui/toast' import { useSelector as useAppContextSelector } from '@/context/app-context' import { useDeleteWorkflow, useInvalidAllLastRun, useResetWorkflowVersionHistory, useRestoreWorkflow, useUpdateWorkflow, useWorkflowVersionHistory } from '@/service/use-workflow' import { useDSL, useWorkflowRefreshDraft, useWorkflowRun } from '../../hooks' diff --git a/web/app/components/workflow/panel/version-history-panel/restore-confirm-modal.tsx b/web/app/components/workflow/panel/version-history-panel/restore-confirm-modal.tsx index 695e6fe35c..9fc2c25742 100644 --- a/web/app/components/workflow/panel/version-history-panel/restore-confirm-modal.tsx +++ b/web/app/components/workflow/panel/version-history-panel/restore-confirm-modal.tsx @@ -1,9 +1,9 @@ import type { FC } from 'react' import type { VersionHistory } from '@/types/workflow' +import { Button } from '@langgenius/dify-ui/button' import * as React from 'react' import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' type RestoreConfirmModalProps = { isOpen: boolean diff --git a/web/app/components/workflow/panel/workflow-preview.tsx b/web/app/components/workflow/panel/workflow-preview.tsx index e28c494b20..89ae09c374 100644 --- a/web/app/components/workflow/panel/workflow-preview.tsx +++ b/web/app/components/workflow/panel/workflow-preview.tsx @@ -1,4 +1,6 @@ +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import copy from 'copy-to-clipboard' import { memo, @@ -8,8 +10,6 @@ import { } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { submitHumanInputForm } from '@/service/workflow' import { useWorkflowInteractions, diff --git a/web/app/components/workflow/run/__tests__/index.spec.tsx b/web/app/components/workflow/run/__tests__/index.spec.tsx index 87937dfb4f..39f26df97e 100644 --- a/web/app/components/workflow/run/__tests__/index.spec.tsx +++ b/web/app/components/workflow/run/__tests__/index.spec.tsx @@ -22,7 +22,7 @@ vi.mock('@/service/log', () => ({ fetchTracingList: (...args: unknown[]) => mockFetchTracingList(...args), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { error: (...args: unknown[]) => mockToastError(...args), }, diff --git a/web/app/components/workflow/run/agent-log/__tests__/agent-log-nav-more.spec.tsx b/web/app/components/workflow/run/agent-log/__tests__/agent-log-nav-more.spec.tsx index 35b32a5ce6..e283740613 100644 --- a/web/app/components/workflow/run/agent-log/__tests__/agent-log-nav-more.spec.tsx +++ b/web/app/components/workflow/run/agent-log/__tests__/agent-log-nav-more.spec.tsx @@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import AgentLogNavMore from '../agent-log-nav-more' -vi.mock('@/app/components/base/ui/dropdown-menu', async () => { +vi.mock('@langgenius/dify-ui/dropdown-menu', async () => { const React = await import('react') const DropdownMenuContext = React.createContext<{ open: boolean, setOpen: (open: boolean) => void } | null>(null) diff --git a/web/app/components/workflow/run/agent-log/agent-log-item.tsx b/web/app/components/workflow/run/agent-log/agent-log-item.tsx index e1714c5f35..67da9c7aa9 100644 --- a/web/app/components/workflow/run/agent-log/agent-log-item.tsx +++ b/web/app/components/workflow/run/agent-log/agent-log-item.tsx @@ -1,4 +1,5 @@ import type { AgentLogItemWithChildren } from '@/types/workflow' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { RiArrowRightSLine, @@ -8,7 +9,6 @@ import { useMemo, useState, } from 'react' -import { Button } from '@/app/components/base/ui/button' import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' import BlockIcon from '@/app/components/workflow/block-icon' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' diff --git a/web/app/components/workflow/run/agent-log/agent-log-nav-more.tsx b/web/app/components/workflow/run/agent-log/agent-log-nav-more.tsx index 77f3c778a6..5c8d100012 100644 --- a/web/app/components/workflow/run/agent-log/agent-log-nav-more.tsx +++ b/web/app/components/workflow/run/agent-log/agent-log-nav-more.tsx @@ -1,13 +1,13 @@ import type { AgentLogItemWithChildren } from '@/types/workflow' -import { RiMoreLine } from '@remixicon/react' -import { useState } from 'react' -import { Button } from '@/app/components/base/ui/button' +import { Button } from '@langgenius/dify-ui/button' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' +} from '@langgenius/dify-ui/dropdown-menu' +import { RiMoreLine } from '@remixicon/react' +import { useState } from 'react' type AgentLogNavMoreProps = { options: AgentLogItemWithChildren[] diff --git a/web/app/components/workflow/run/agent-log/agent-log-nav.tsx b/web/app/components/workflow/run/agent-log/agent-log-nav.tsx index 2548256cb1..7996595186 100644 --- a/web/app/components/workflow/run/agent-log/agent-log-nav.tsx +++ b/web/app/components/workflow/run/agent-log/agent-log-nav.tsx @@ -1,7 +1,7 @@ import type { AgentLogItemWithChildren } from '@/types/workflow' +import { Button } from '@langgenius/dify-ui/button' import { RiArrowLeftLine } from '@remixicon/react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import AgentLogNavMore from './agent-log-nav-more' type AgentLogNavProps = { diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx index bd71b662f5..d066690120 100644 --- a/web/app/components/workflow/run/index.tsx +++ b/web/app/components/workflow/run/index.tsx @@ -3,10 +3,10 @@ import type { FC } from 'react' import type { WorkflowRunDetailResponse } from '@/models/log' import type { NodeTracing } from '@/types/workflow' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' -import { toast } from '@/app/components/base/ui/toast' import { WorkflowRunningStatus } from '@/app/components/workflow/types' import { fetchRunDetail, fetchTracingList } from '@/service/log' import { useStore } from '../store' diff --git a/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx b/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx index 77b9425501..8fb857f344 100644 --- a/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx +++ b/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx @@ -2,10 +2,10 @@ import type { IterationDurationMap, NodeTracing, } from '@/types/workflow' +import { Button } from '@langgenius/dify-ui/button' import { RiArrowRightSLine } from '@remixicon/react' import { useTranslation } from 'react-i18next' import { Iteration } from '@/app/components/base/icons/src/vender/workflow' -import { Button } from '@/app/components/base/ui/button' import { NodeRunningStatus } from '@/app/components/workflow/types' type IterationLogTriggerProps = { diff --git a/web/app/components/workflow/run/loop-log/loop-log-trigger.tsx b/web/app/components/workflow/run/loop-log/loop-log-trigger.tsx index 42593e564f..277db8cef4 100644 --- a/web/app/components/workflow/run/loop-log/loop-log-trigger.tsx +++ b/web/app/components/workflow/run/loop-log/loop-log-trigger.tsx @@ -3,10 +3,10 @@ import type { LoopVariableMap, NodeTracing, } from '@/types/workflow' +import { Button } from '@langgenius/dify-ui/button' import { RiArrowRightSLine } from '@remixicon/react' import { useTranslation } from 'react-i18next' import { Loop } from '@/app/components/base/icons/src/vender/workflow' -import { Button } from '@/app/components/base/ui/button' type LoopLogTriggerProps = { nodeInfo: NodeTracing diff --git a/web/app/components/workflow/run/retry-log/retry-log-trigger.tsx b/web/app/components/workflow/run/retry-log/retry-log-trigger.tsx index 592b7775f1..84c7808df1 100644 --- a/web/app/components/workflow/run/retry-log/retry-log-trigger.tsx +++ b/web/app/components/workflow/run/retry-log/retry-log-trigger.tsx @@ -1,10 +1,10 @@ import type { NodeTracing } from '@/types/workflow' +import { Button } from '@langgenius/dify-ui/button' import { RiArrowRightSLine, RiRestartFill, } from '@remixicon/react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' type RetryLogTriggerProps = { nodeInfo: NodeTracing diff --git a/web/app/components/workflow/selection-contextmenu.tsx b/web/app/components/workflow/selection-contextmenu.tsx index c82f431ddf..26b5429df4 100644 --- a/web/app/components/workflow/selection-contextmenu.tsx +++ b/web/app/components/workflow/selection-contextmenu.tsx @@ -1,4 +1,12 @@ import type { Node } from './types' +import { + ContextMenu, + ContextMenuContent, + ContextMenuGroup, + ContextMenuItem, + ContextMenuLabel, + ContextMenuSeparator, +} from '@langgenius/dify-ui/context-menu' import { produce } from 'immer' import { memo, @@ -8,14 +16,6 @@ import { } from 'react' import { useTranslation } from 'react-i18next' import { useStore as useReactFlowStore } from 'reactflow' -import { - ContextMenu, - ContextMenuContent, - ContextMenuGroup, - ContextMenuItem, - ContextMenuLabel, - ContextMenuSeparator, -} from '@/app/components/base/ui/context-menu' import { useCollaborativeWorkflow } from '@/app/components/workflow/hooks/use-collaborative-workflow' import { useNodesInteractions, useNodesReadOnly, useNodesSyncDraft } from './hooks' import { useSelectionInteractions } from './hooks/use-selection-interactions' diff --git a/web/app/components/workflow/update-dsl-modal.tsx b/web/app/components/workflow/update-dsl-modal.tsx index 04d687ce73..cfa9c995eb 100644 --- a/web/app/components/workflow/update-dsl-modal.tsx +++ b/web/app/components/workflow/update-dsl-modal.tsx @@ -1,6 +1,8 @@ 'use client' import type { MouseEventHandler } from 'react' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiAlertFill, RiCloseLine, @@ -16,8 +18,6 @@ import { useTranslation } from 'react-i18next' import Uploader from '@/app/components/app/create-from-dsl-modal/uploader' import { useStore as useAppStore } from '@/app/components/app/store' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' import { useEventEmitterContextContext } from '@/context/event-emitter' import { diff --git a/web/app/components/workflow/variable-inspect/__tests__/value-content-sections.spec.tsx b/web/app/components/workflow/variable-inspect/__tests__/value-content-sections.spec.tsx index 2ebe527104..bba17a7c32 100644 --- a/web/app/components/workflow/variable-inspect/__tests__/value-content-sections.spec.tsx +++ b/web/app/components/workflow/variable-inspect/__tests__/value-content-sections.spec.tsx @@ -18,7 +18,7 @@ const toastMocks = vi.hoisted(() => ({ promise: vi.fn(), })) -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: Object.assign(toastMocks.call, { success: vi.fn(), error: vi.fn(), diff --git a/web/app/components/workflow/variable-inspect/group.tsx b/web/app/components/workflow/variable-inspect/group.tsx index 7d94226786..24786e4b1f 100644 --- a/web/app/components/workflow/variable-inspect/group.tsx +++ b/web/app/components/workflow/variable-inspect/group.tsx @@ -10,7 +10,7 @@ import { } from '@remixicon/react' import { useState } from 'react' import { useTranslation } from 'react-i18next' -// import { Button } from '@/app/components/base/ui/button' +// import { Button } from '@langgenius/dify-ui/button' import ActionButton from '@/app/components/base/action-button' import Tooltip from '@/app/components/base/tooltip' import BlockIcon from '@/app/components/workflow/block-icon' diff --git a/web/app/components/workflow/variable-inspect/left.tsx b/web/app/components/workflow/variable-inspect/left.tsx index f3450cea13..6d590eb281 100644 --- a/web/app/components/workflow/variable-inspect/left.tsx +++ b/web/app/components/workflow/variable-inspect/left.tsx @@ -1,10 +1,10 @@ import type { currentVarType } from './panel' import type { VarInInspect } from '@/types/workflow' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' // import { useState } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import { VarInInspectType } from '@/types/workflow' import useCurrentVars from '../hooks/use-inspect-vars-crud' import { useNodesInteractions } from '../hooks/use-nodes-interactions' diff --git a/web/app/components/workflow/variable-inspect/listening.tsx b/web/app/components/workflow/variable-inspect/listening.tsx index 4559665ffa..3994355d58 100644 --- a/web/app/components/workflow/variable-inspect/listening.tsx +++ b/web/app/components/workflow/variable-inspect/listening.tsx @@ -3,13 +3,13 @@ import type { FC } from 'react' import type { Node } from 'reactflow' import type { ScheduleTriggerNodeType } from '@/app/components/workflow/nodes/trigger-schedule/types' import type { WebhookTriggerNodeType } from '@/app/components/workflow/nodes/trigger-webhook/types' +import { Button } from '@langgenius/dify-ui/button' import copy from 'copy-to-clipboard' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useStoreApi } from 'reactflow' import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' import Tooltip from '@/app/components/base/tooltip' -import { Button } from '@/app/components/base/ui/button' import BlockIcon from '@/app/components/workflow/block-icon' import { useGetToolIcon } from '@/app/components/workflow/hooks/use-tool-icon' import { getNextExecutionTime } from '@/app/components/workflow/nodes/trigger-schedule/utils/execution-time-calculator' diff --git a/web/app/components/workflow/workflow-preview/components/zoom-in-out.tsx b/web/app/components/workflow/workflow-preview/components/zoom-in-out.tsx index 3f714c5ca9..9ea32caec3 100644 --- a/web/app/components/workflow/workflow-preview/components/zoom-in-out.tsx +++ b/web/app/components/workflow/workflow-preview/components/zoom-in-out.tsx @@ -1,5 +1,12 @@ import type { FC } from 'react' import { cn } from '@langgenius/dify-ui/cn' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@langgenius/dify-ui/dropdown-menu' import { Fragment, memo, @@ -10,13 +17,6 @@ import { useReactFlow, useViewport, } from 'reactflow' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from '@/app/components/base/ui/dropdown-menu' import TipPopup from '@/app/components/workflow/operator/tip-popup' import ShortcutsName from '@/app/components/workflow/shortcuts-name' diff --git a/web/app/education-apply/education-apply-page.tsx b/web/app/education-apply/education-apply-page.tsx index 7fda139aa9..93259c1ccb 100644 --- a/web/app/education-apply/education-apply-page.tsx +++ b/web/app/education-apply/education-apply-page.tsx @@ -1,5 +1,7 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiExternalLinkLine } from '@remixicon/react' import { noop } from 'es-toolkit/function' import { @@ -7,8 +9,6 @@ import { } from 'react' import { useTranslation } from 'react-i18next' import Checkbox from '@/app/components/base/checkbox' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { EDUCATION_VERIFYING_LOCALSTORAGE_ITEM } from '@/app/education-apply/constants' import { useDocLink } from '@/context/i18n' import { useProviderContext } from '@/context/provider-context' diff --git a/web/app/education-apply/expire-notice-modal.tsx b/web/app/education-apply/expire-notice-modal.tsx index ec88bba1b5..e54afaecde 100644 --- a/web/app/education-apply/expire-notice-modal.tsx +++ b/web/app/education-apply/expire-notice-modal.tsx @@ -1,9 +1,9 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' import { RiExternalLinkLine } from '@remixicon/react' import * as React from 'react' import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' -import { Button } from '@/app/components/base/ui/button' import { useDocLink } from '@/context/i18n' import { useModalContextSelector } from '@/context/modal-context' import useTimestamp from '@/hooks/use-timestamp' diff --git a/web/app/education-apply/user-info.tsx b/web/app/education-apply/user-info.tsx index 32d56b2780..be9b319038 100644 --- a/web/app/education-apply/user-info.tsx +++ b/web/app/education-apply/user-info.tsx @@ -1,7 +1,7 @@ +import { Avatar } from '@langgenius/dify-ui/avatar' +import { Button } from '@langgenius/dify-ui/button' import { useTranslation } from 'react-i18next' import { Triangle } from '@/app/components/base/icons/src/public/education' -import { Avatar } from '@/app/components/base/ui/avatar' -import { Button } from '@/app/components/base/ui/button' import { useAppContext } from '@/context/app-context' import { useRouter } from '@/next/navigation' import { useLogout } from '@/service/use-common' diff --git a/web/app/education-apply/verify-state-modal.tsx b/web/app/education-apply/verify-state-modal.tsx index c4d836f178..bb7439352e 100644 --- a/web/app/education-apply/verify-state-modal.tsx +++ b/web/app/education-apply/verify-state-modal.tsx @@ -1,3 +1,4 @@ +import { Button } from '@langgenius/dify-ui/button' import { RiExternalLinkLine, } from '@remixicon/react' @@ -5,7 +6,6 @@ import * as React from 'react' import { useEffect, useRef, useState } from 'react' import { createPortal } from 'react-dom' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import { useDocLink } from '@/context/i18n' type IConfirm = { diff --git a/web/app/forgot-password/ChangePasswordForm.tsx b/web/app/forgot-password/ChangePasswordForm.tsx index bf262181a9..deb520c436 100644 --- a/web/app/forgot-password/ChangePasswordForm.tsx +++ b/web/app/forgot-password/ChangePasswordForm.tsx @@ -1,11 +1,11 @@ 'use client' import { CheckCircleIcon } from '@heroicons/react/24/solid' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { validPassword } from '@/config' import { useSearchParams } from '@/next/navigation' import { changePasswordWithToken } from '@/service/common' diff --git a/web/app/forgot-password/ForgotPasswordForm.tsx b/web/app/forgot-password/ForgotPasswordForm.tsx index 7ce5e7d8dd..b4fba60182 100644 --- a/web/app/forgot-password/ForgotPasswordForm.tsx +++ b/web/app/forgot-password/ForgotPasswordForm.tsx @@ -1,15 +1,15 @@ 'use client' import type { InitValidateStatusResponse } from '@/models/common' +import { Button } from '@langgenius/dify-ui/button' + import { useStore } from '@tanstack/react-form' import * as React from 'react' - import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import * as z from 'zod' import { formContext, useAppForm } from '@/app/components/base/form' import { zodSubmitValidator } from '@/app/components/base/form/utils/zod-submit-validator' -import { Button } from '@/app/components/base/ui/button' import { useRouter } from '@/next/navigation' import { fetchInitValidateStatus, diff --git a/web/app/init/InitPasswordPopup.tsx b/web/app/init/InitPasswordPopup.tsx index b0c74978fa..6b25fa371a 100644 --- a/web/app/init/InitPasswordPopup.tsx +++ b/web/app/init/InitPasswordPopup.tsx @@ -1,9 +1,9 @@ 'use client' import type { InitValidateStatusResponse } from '@/models/common' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import useDocumentTitle from '@/hooks/use-document-title' import { useRouter } from '@/next/navigation' import { fetchInitValidateStatus, initValidate } from '@/service/common' diff --git a/web/app/install/installForm.tsx b/web/app/install/installForm.tsx index c068548561..d9b6e0c7ad 100644 --- a/web/app/install/installForm.tsx +++ b/web/app/install/installForm.tsx @@ -1,8 +1,9 @@ 'use client' import type { InitValidateStatusResponse, SetupStatusResponse } from '@/models/common' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' -import { useStore } from '@tanstack/react-form' +import { useStore } from '@tanstack/react-form' import * as React from 'react' import { useEffect } from 'react' import { useTranslation } from 'react-i18next' @@ -10,7 +11,6 @@ import * as z from 'zod' import { formContext, useAppForm } from '@/app/components/base/form' import { zodSubmitValidator } from '@/app/components/base/form/utils/zod-submit-validator' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' import { validPassword } from '@/config' import { LICENSE_LINK } from '@/constants/link' diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 63e506bd45..be0c854f02 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -1,4 +1,6 @@ import type { Viewport } from '@/next' +import { ToastHost } from '@langgenius/dify-ui/toast' +import { TooltipProvider } from '@langgenius/dify-ui/tooltip' import { Provider as JotaiProvider } from 'jotai/react' import { ThemeProvider } from 'next-themes' import { NuqsAdapter } from 'nuqs/adapters/next/app' @@ -6,8 +8,6 @@ import GlobalPublicStoreProvider from '@/context/global-public-context' import { TanstackQueryInitializer } from '@/context/query-client' import { getDatasetMap } from '@/env' import { getLocaleOnServer } from '@/i18n-config/server' -import { ToastHost } from './components/base/ui/toast' -import { TooltipProvider } from './components/base/ui/tooltip' import PartnerStackCookieRecorder from './components/billing/partner-stack/cookie-recorder' import CreateAppAttributionBootstrap from './components/create-app-attribution-bootstrap' import { AgentationLoader } from './components/devtools/agentation-loader' diff --git a/web/app/reset-password/check-code/page.tsx b/web/app/reset-password/check-code/page.tsx index 98ea828aae..b11935481e 100644 --- a/web/app/reset-password/check-code/page.tsx +++ b/web/app/reset-password/check-code/page.tsx @@ -1,10 +1,10 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import Countdown from '@/app/components/signin/countdown' import { useLocale } from '@/context/i18n' import { useRouter, useSearchParams } from '@/next/navigation' diff --git a/web/app/reset-password/page.tsx b/web/app/reset-password/page.tsx index d58fd7e9ff..63b950bd11 100644 --- a/web/app/reset-password/page.tsx +++ b/web/app/reset-password/page.tsx @@ -1,11 +1,11 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowLeftLine, RiLockPasswordLine } from '@remixicon/react' import { noop } from 'es-toolkit/function' import { useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { emailRegex } from '@/config' import { useLocale } from '@/context/i18n' import useDocumentTitle from '@/hooks/use-document-title' diff --git a/web/app/reset-password/set-password/page.tsx b/web/app/reset-password/set-password/page.tsx index 7277b6d30c..4a8cb2af9b 100644 --- a/web/app/reset-password/set-password/page.tsx +++ b/web/app/reset-password/set-password/page.tsx @@ -1,12 +1,12 @@ 'use client' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiCheckboxCircleFill } from '@remixicon/react' import { useCountDown } from 'ahooks' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { validPassword } from '@/config' import { useRouter, useSearchParams } from '@/next/navigation' import { changePasswordWithToken } from '@/service/common' diff --git a/web/app/signin/check-code/page.tsx b/web/app/signin/check-code/page.tsx index b86c984f31..fb52e0b5b7 100644 --- a/web/app/signin/check-code/page.tsx +++ b/web/app/signin/check-code/page.tsx @@ -1,12 +1,12 @@ 'use client' import type { FormEvent } from 'react' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react' import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { trackEvent } from '@/app/components/base/amplitude' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import Countdown from '@/app/components/signin/countdown' import { useLocale } from '@/context/i18n' diff --git a/web/app/signin/components/mail-and-code-auth.tsx b/web/app/signin/components/mail-and-code-auth.tsx index 5c5431c42a..41cf50854e 100644 --- a/web/app/signin/components/mail-and-code-auth.tsx +++ b/web/app/signin/components/mail-and-code-auth.tsx @@ -1,9 +1,9 @@ import type { FormEvent } from 'react' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown' import { emailRegex } from '@/config' import { useLocale } from '@/context/i18n' diff --git a/web/app/signin/components/mail-and-password-auth.tsx b/web/app/signin/components/mail-and-password-auth.tsx index ca93b9b391..6feaf11426 100644 --- a/web/app/signin/components/mail-and-password-auth.tsx +++ b/web/app/signin/components/mail-and-password-auth.tsx @@ -1,11 +1,11 @@ import type { ResponseError } from '@/service/fetch' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { noop } from 'es-toolkit/function' import { useState } from 'react' import { useTranslation } from 'react-i18next' import { trackEvent } from '@/app/components/base/amplitude' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { emailRegex } from '@/config' import { useLocale } from '@/context/i18n' import Link from '@/next/link' diff --git a/web/app/signin/components/social-auth.tsx b/web/app/signin/components/social-auth.tsx index 6eb9d62703..09fa528d1f 100644 --- a/web/app/signin/components/social-auth.tsx +++ b/web/app/signin/components/social-auth.tsx @@ -1,6 +1,6 @@ +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { useTranslation } from 'react-i18next' -import { Button } from '@/app/components/base/ui/button' import { API_PREFIX } from '@/config' import { useSearchParams } from '@/next/navigation' import { getPurifyHref } from '@/utils' diff --git a/web/app/signin/components/sso-auth.tsx b/web/app/signin/components/sso-auth.tsx index 5ead876741..32612e4edd 100644 --- a/web/app/signin/components/sso-auth.tsx +++ b/web/app/signin/components/sso-auth.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { useState } from 'react' import { useTranslation } from 'react-i18next' import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { useRouter, useSearchParams } from '@/next/navigation' import { getUserOAuth2SSOUrl, getUserOIDCSSOUrl, getUserSAMLSSOUrl } from '@/service/sso' import { SSOProtocol } from '@/types/feature' diff --git a/web/app/signin/invite-settings/page.tsx b/web/app/signin/invite-settings/page.tsx index 91973f07f9..fe3dac7153 100644 --- a/web/app/signin/invite-settings/page.tsx +++ b/web/app/signin/invite-settings/page.tsx @@ -1,5 +1,7 @@ 'use client' import type { Locale } from '@/i18n-config' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiAccountCircleLine } from '@remixicon/react' import { noop } from 'es-toolkit/function' import { useCallback, useState } from 'react' @@ -7,8 +9,6 @@ import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import Loading from '@/app/components/base/loading' import { SimpleSelect } from '@/app/components/base/select' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { LICENSE_LINK } from '@/constants/link' import { useGlobalPublicStore } from '@/context/global-public-context' import { setLocaleOnClient } from '@/i18n-config' diff --git a/web/app/signin/normal-form.tsx b/web/app/signin/normal-form.tsx index 4fd0eabf66..2ead90f068 100644 --- a/web/app/signin/normal-form.tsx +++ b/web/app/signin/normal-form.tsx @@ -1,9 +1,9 @@ import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import { RiContractLine, RiDoorLockLine, RiErrorWarningFill } from '@remixicon/react' import * as React from 'react' import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { toast } from '@/app/components/base/ui/toast' import { IS_CE_EDITION } from '@/config' import { useGlobalPublicStore } from '@/context/global-public-context' import Link from '@/next/link' diff --git a/web/app/signin/one-more-step.tsx b/web/app/signin/one-more-step.tsx index 6368533e2a..1f615d5e5a 100644 --- a/web/app/signin/one-more-step.tsx +++ b/web/app/signin/one-more-step.tsx @@ -1,11 +1,11 @@ 'use client' import type { Reducer } from 'react' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { useReducer } from 'react' import { useTranslation } from 'react-i18next' import { SimpleSelect } from '@/app/components/base/select' import Tooltip from '@/app/components/base/tooltip' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { LICENSE_LINK } from '@/constants/link' import { languages, LanguagesSupported } from '@/i18n-config/language' import Link from '@/next/link' diff --git a/web/app/signup/check-code/page.tsx b/web/app/signup/check-code/page.tsx index a71d5289fe..a6a8cadc13 100644 --- a/web/app/signup/check-code/page.tsx +++ b/web/app/signup/check-code/page.tsx @@ -1,11 +1,11 @@ 'use client' import type { MailSendResponse, MailValidityResponse } from '@/service/use-common' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import Countdown from '@/app/components/signin/countdown' import { useLocale } from '@/context/i18n' import { useRouter, useSearchParams } from '@/next/navigation' diff --git a/web/app/signup/components/input-mail.tsx b/web/app/signup/components/input-mail.tsx index ae7fbb986d..f4c5214c11 100644 --- a/web/app/signup/components/input-mail.tsx +++ b/web/app/signup/components/input-mail.tsx @@ -1,10 +1,10 @@ 'use client' import type { MailSendResponse } from '@/service/use-common' +import { Button } from '@langgenius/dify-ui/button' +import { toast } from '@langgenius/dify-ui/toast' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import Split from '@/app/signin/split' import { emailRegex } from '@/config' import { useGlobalPublicStore } from '@/context/global-public-context' diff --git a/web/app/signup/set-password/page.tsx b/web/app/signup/set-password/page.tsx index ffecd0bc0c..a8eb883078 100644 --- a/web/app/signup/set-password/page.tsx +++ b/web/app/signup/set-password/page.tsx @@ -1,13 +1,13 @@ 'use client' import type { MailRegisterResponse } from '@/service/use-common' +import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' +import { toast } from '@langgenius/dify-ui/toast' import Cookies from 'js-cookie' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { trackEvent } from '@/app/components/base/amplitude' import Input from '@/app/components/base/input' -import { Button } from '@/app/components/base/ui/button' -import { toast } from '@/app/components/base/ui/toast' import { validPassword } from '@/config' import { useRouter, useSearchParams } from '@/next/navigation' import { useMailRegister } from '@/service/use-common' diff --git a/web/context/provider-context-provider.tsx b/web/context/provider-context-provider.tsx index 078a954e31..0af0f24b9a 100644 --- a/web/context/provider-context-provider.tsx +++ b/web/context/provider-context-provider.tsx @@ -1,11 +1,11 @@ 'use client' import type { ReactNode } from 'react' +import { toast } from '@langgenius/dify-ui/toast' import { useQueryClient } from '@tanstack/react-query' import dayjs from 'dayjs' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { toast } from '@/app/components/base/ui/toast' import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils' import { defaultPlan } from '@/app/components/billing/config' import { parseCurrentPlan } from '@/app/components/billing/utils' diff --git a/web/docs/overlay-migration.md b/web/docs/overlay-migration.md index 29ef814fcd..0016f34e12 100644 --- a/web/docs/overlay-migration.md +++ b/web/docs/overlay-migration.md @@ -11,14 +11,14 @@ This document tracks the migration away from legacy overlay APIs. - `@/app/components/base/select` (including `custom` / `pure`) - `@/app/components/base/dialog` - Replacement primitives: - - `@/app/components/base/ui/tooltip` - - `@/app/components/base/ui/dropdown-menu` - - `@/app/components/base/ui/context-menu` - - `@/app/components/base/ui/popover` - - `@/app/components/base/ui/dialog` - - `@/app/components/base/ui/alert-dialog` - - `@/app/components/base/ui/select` - - `@/app/components/base/ui/toast` + - `@langgenius/dify-ui/tooltip` + - `@langgenius/dify-ui/dropdown-menu` + - `@langgenius/dify-ui/context-menu` + - `@langgenius/dify-ui/popover` + - `@langgenius/dify-ui/dialog` + - `@langgenius/dify-ui/alert-dialog` + - `@langgenius/dify-ui/select` + - `@langgenius/dify-ui/toast` - Tracking issue: ## ESLint policy diff --git a/web/eslint.constants.mjs b/web/eslint.constants.mjs index 57a14fc22d..46690035fb 100644 --- a/web/eslint.constants.mjs +++ b/web/eslint.constants.mjs @@ -33,7 +33,7 @@ export const OVERLAY_RESTRICTED_IMPORT_PATTERNS = [ '**/base/tooltip', '**/base/tooltip/index', ], - message: 'Deprecated: use @/app/components/base/ui/tooltip instead. See issue #32767.', + message: 'Deprecated: use @langgenius/dify-ui/tooltip instead. See issue #32767.', }, { group: [ @@ -41,7 +41,7 @@ export const OVERLAY_RESTRICTED_IMPORT_PATTERNS = [ '**/base/modal/index', '**/base/modal/modal', ], - message: 'Deprecated: use @/app/components/base/ui/dialog instead. See issue #32767.', + message: 'Deprecated: use @langgenius/dify-ui/dialog instead. See issue #32767.', }, { group: [ @@ -50,14 +50,14 @@ export const OVERLAY_RESTRICTED_IMPORT_PATTERNS = [ '**/base/select/custom', '**/base/select/pure', ], - message: 'Deprecated: use @/app/components/base/ui/select instead. See issue #32767.', + message: 'Deprecated: use @langgenius/dify-ui/select instead. See issue #32767.', }, { group: [ '**/base/dialog', '**/base/dialog/index', ], - message: 'Deprecated: use @/app/components/base/ui/dialog instead. See issue #32767.', + message: 'Deprecated: use @langgenius/dify-ui/dialog instead. See issue #32767.', }, ] diff --git a/web/hooks/use-import-dsl.ts b/web/hooks/use-import-dsl.ts index 6be67c22ac..1ece2a27f0 100644 --- a/web/hooks/use-import-dsl.ts +++ b/web/hooks/use-import-dsl.ts @@ -3,13 +3,13 @@ import type { DSLImportResponse, } from '@/models/app' import type { AppIconType } from '@/types/app' +import { toast } from '@langgenius/dify-ui/toast' import { useCallback, useRef, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { toast } from '@/app/components/base/ui/toast' import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' import { NEED_REFRESH_APP_LIST_KEY } from '@/config' import { useSelector } from '@/context/app-context' diff --git a/web/hooks/use-pay.tsx b/web/hooks/use-pay.tsx index 237913b433..593a33a1d2 100644 --- a/web/hooks/use-pay.tsx +++ b/web/hooks/use-pay.tsx @@ -1,7 +1,5 @@ 'use client' -import { useCallback, useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' import { AlertDialog, AlertDialogActions, @@ -9,7 +7,9 @@ import { AlertDialogContent, AlertDialogDescription, AlertDialogTitle, -} from '@/app/components/base/ui/alert-dialog' +} from '@langgenius/dify-ui/alert-dialog' +import { useCallback, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useRouter, useSearchParams } from '@/next/navigation' import { useNotionBinding } from '@/service/use-common' diff --git a/web/service/base.ts b/web/service/base.ts index 2063b5bc37..64d13ef59a 100644 --- a/web/service/base.ts +++ b/web/service/base.ts @@ -27,8 +27,8 @@ import type { WorkflowPausedResponse, WorkflowStartedResponse, } from '@/types/workflow' +import { toast } from '@langgenius/dify-ui/toast' import Cookies from 'js-cookie' -import { toast } from '@/app/components/base/ui/toast' import { API_PREFIX, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, IS_CE_EDITION, PASSPORT_HEADER_NAME, PUBLIC_API_PREFIX, WEB_APP_SHARE_CODE_HEADER_NAME } from '@/config' import { asyncRunSafe } from '@/utils' import { basePath } from '@/utils/var' diff --git a/web/service/fetch.spec.ts b/web/service/fetch.spec.ts index 0c01d32438..632f898e88 100644 --- a/web/service/fetch.spec.ts +++ b/web/service/fetch.spec.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { base } from './fetch' -vi.mock('@/app/components/base/ui/toast', () => ({ +vi.mock('@langgenius/dify-ui/toast', () => ({ toast: { add: vi.fn(), }, diff --git a/web/service/fetch.ts b/web/service/fetch.ts index 82870a8d2e..34bd07160a 100644 --- a/web/service/fetch.ts +++ b/web/service/fetch.ts @@ -1,8 +1,8 @@ import type { AfterResponseHook, BeforeRequestHook, Hooks } from 'ky' import type { IOtherOptions } from './base' +import { toast } from '@langgenius/dify-ui/toast' import Cookies from 'js-cookie' import ky, { HTTPError } from 'ky' -import { toast } from '@/app/components/base/ui/toast' import { API_PREFIX, APP_VERSION, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, IS_MARKETPLACE, MARKETPLACE_API_PREFIX, PASSPORT_HEADER_NAME, PUBLIC_API_PREFIX, WEB_APP_SHARE_CODE_HEADER_NAME } from '@/config' import { getWebAppAccessToken, getWebAppPassport } from './webapp-auth' diff --git a/web/tailwind.config.ts b/web/tailwind.config.ts index 32b889e707..21c0455177 100644 --- a/web/tailwind.config.ts +++ b/web/tailwind.config.ts @@ -6,9 +6,11 @@ const config: Config = { './app/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}', './context/**/*.{js,ts,jsx,tsx}', + '../packages/dify-ui/src/**/*.{ts,tsx}', './node_modules/streamdown/dist/*.js', './node_modules/@streamdown/math/dist/*.js', '!./**/*.{spec,test}.{js,ts,jsx,tsx}', + '!../packages/dify-ui/src/**/*.{spec,test}.{ts,tsx}', ], ...commonConfig, } From b9c300d57014c3601c1bccf2f2051ff50e6fe271 Mon Sep 17 00:00:00 2001 From: jerryzai Date: Fri, 17 Apr 2026 04:52:27 -0400 Subject: [PATCH 02/15] =?UTF-8?q?chore(api):=20migrate=20mail=20task=20and?= =?UTF-8?q?=20OAuth=20data=20source=20to=20use=20Session(db=E2=80=A6=20(#3?= =?UTF-8?q?5235)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Asuka Minato Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/libs/oauth_data_source.py | 131 +++++++++--------- .../mail_clean_document_notify_task.py | 121 ++++++++-------- 2 files changed, 128 insertions(+), 124 deletions(-) diff --git a/api/libs/oauth_data_source.py b/api/libs/oauth_data_source.py index 9b53918f24..934aacb45b 100644 --- a/api/libs/oauth_data_source.py +++ b/api/libs/oauth_data_source.py @@ -6,8 +6,8 @@ from flask_login import current_user from pydantic import TypeAdapter from sqlalchemy import select +from core.db.session_factory import session_factory from core.helper.http_client_pooling import get_pooled_http_client -from extensions.ext_database import db from libs.datetime_utils import naive_utc_now from models.source import DataSourceOauthBinding @@ -95,27 +95,28 @@ class NotionOAuth(OAuthDataSource): pages=pages, ) # save data source binding - data_source_binding = db.session.scalar( - select(DataSourceOauthBinding).where( - DataSourceOauthBinding.tenant_id == current_user.current_tenant_id, - DataSourceOauthBinding.provider == "notion", - DataSourceOauthBinding.access_token == access_token, + with session_factory.create_session() as session: + data_source_binding = session.scalar( + select(DataSourceOauthBinding).where( + DataSourceOauthBinding.tenant_id == current_user.current_tenant_id, + DataSourceOauthBinding.provider == "notion", + DataSourceOauthBinding.access_token == access_token, + ) ) - ) - if data_source_binding: - data_source_binding.source_info = SOURCE_INFO_STORAGE_ADAPTER.validate_python(source_info) - data_source_binding.disabled = False - data_source_binding.updated_at = naive_utc_now() - db.session.commit() - else: - new_data_source_binding = DataSourceOauthBinding( - tenant_id=current_user.current_tenant_id, - access_token=access_token, - source_info=SOURCE_INFO_STORAGE_ADAPTER.validate_python(source_info), - provider="notion", - ) - db.session.add(new_data_source_binding) - db.session.commit() + if data_source_binding: + data_source_binding.source_info = SOURCE_INFO_STORAGE_ADAPTER.validate_python(source_info) + data_source_binding.disabled = False + data_source_binding.updated_at = naive_utc_now() + session.commit() + else: + new_data_source_binding = DataSourceOauthBinding( + tenant_id=current_user.current_tenant_id, + access_token=access_token, + source_info=SOURCE_INFO_STORAGE_ADAPTER.validate_python(source_info), + provider="notion", + ) + session.add(new_data_source_binding) + session.commit() def save_internal_access_token(self, access_token: str) -> None: workspace_name = self.notion_workspace_name(access_token) @@ -130,55 +131,57 @@ class NotionOAuth(OAuthDataSource): pages=pages, ) # save data source binding - data_source_binding = db.session.scalar( - select(DataSourceOauthBinding).where( - DataSourceOauthBinding.tenant_id == current_user.current_tenant_id, - DataSourceOauthBinding.provider == "notion", - DataSourceOauthBinding.access_token == access_token, + with session_factory.create_session() as session: + data_source_binding = session.scalar( + select(DataSourceOauthBinding).where( + DataSourceOauthBinding.tenant_id == current_user.current_tenant_id, + DataSourceOauthBinding.provider == "notion", + DataSourceOauthBinding.access_token == access_token, + ) ) - ) - if data_source_binding: - data_source_binding.source_info = SOURCE_INFO_STORAGE_ADAPTER.validate_python(source_info) - data_source_binding.disabled = False - data_source_binding.updated_at = naive_utc_now() - db.session.commit() - else: - new_data_source_binding = DataSourceOauthBinding( - tenant_id=current_user.current_tenant_id, - access_token=access_token, - source_info=SOURCE_INFO_STORAGE_ADAPTER.validate_python(source_info), - provider="notion", - ) - db.session.add(new_data_source_binding) - db.session.commit() + if data_source_binding: + data_source_binding.source_info = SOURCE_INFO_STORAGE_ADAPTER.validate_python(source_info) + data_source_binding.disabled = False + data_source_binding.updated_at = naive_utc_now() + session.commit() + else: + new_data_source_binding = DataSourceOauthBinding( + tenant_id=current_user.current_tenant_id, + access_token=access_token, + source_info=SOURCE_INFO_STORAGE_ADAPTER.validate_python(source_info), + provider="notion", + ) + session.add(new_data_source_binding) + session.commit() def sync_data_source(self, binding_id: str) -> None: # save data source binding - data_source_binding = db.session.scalar( - select(DataSourceOauthBinding).where( - DataSourceOauthBinding.tenant_id == current_user.current_tenant_id, - DataSourceOauthBinding.provider == "notion", - DataSourceOauthBinding.id == binding_id, - DataSourceOauthBinding.disabled == False, + with session_factory.create_session() as session: + data_source_binding = session.scalar( + select(DataSourceOauthBinding).where( + DataSourceOauthBinding.tenant_id == current_user.current_tenant_id, + DataSourceOauthBinding.provider == "notion", + DataSourceOauthBinding.id == binding_id, + DataSourceOauthBinding.disabled == False, + ) ) - ) - if data_source_binding: - # get all authorized pages - pages = self.get_authorized_pages(data_source_binding.access_token) - source_info = NOTION_SOURCE_INFO_ADAPTER.validate_python(data_source_binding.source_info) - new_source_info = self._build_source_info( - workspace_name=source_info["workspace_name"], - workspace_icon=source_info["workspace_icon"], - workspace_id=source_info["workspace_id"], - pages=pages, - ) - data_source_binding.source_info = SOURCE_INFO_STORAGE_ADAPTER.validate_python(new_source_info) - data_source_binding.disabled = False - data_source_binding.updated_at = naive_utc_now() - db.session.commit() - else: - raise ValueError("Data source binding not found") + if data_source_binding: + # get all authorized pages + pages = self.get_authorized_pages(data_source_binding.access_token) + source_info = NOTION_SOURCE_INFO_ADAPTER.validate_python(data_source_binding.source_info) + new_source_info = self._build_source_info( + workspace_name=source_info["workspace_name"], + workspace_icon=source_info["workspace_icon"], + workspace_id=source_info["workspace_id"], + pages=pages, + ) + data_source_binding.source_info = SOURCE_INFO_STORAGE_ADAPTER.validate_python(new_source_info) + data_source_binding.disabled = False + data_source_binding.updated_at = naive_utc_now() + session.commit() + else: + raise ValueError("Data source binding not found") def get_authorized_pages(self, access_token: str) -> list[NotionPageSummary]: pages: list[NotionPageSummary] = [] diff --git a/api/schedule/mail_clean_document_notify_task.py b/api/schedule/mail_clean_document_notify_task.py index 8479cdfb0c..2cc0192a4a 100644 --- a/api/schedule/mail_clean_document_notify_task.py +++ b/api/schedule/mail_clean_document_notify_task.py @@ -7,8 +7,8 @@ from sqlalchemy import select import app from configs import dify_config +from core.db.session_factory import session_factory from enums.cloud_plan import CloudPlan -from extensions.ext_database import db from extensions.ext_mail import mail from libs.email_i18n import EmailType, get_email_i18n_service from models import Account, Tenant, TenantAccountJoin @@ -33,67 +33,68 @@ def mail_clean_document_notify_task(): # send document clean notify mail try: - dataset_auto_disable_logs = db.session.scalars( - select(DatasetAutoDisableLog).where(DatasetAutoDisableLog.notified == False) - ).all() - # group by tenant_id - dataset_auto_disable_logs_map: dict[str, list[DatasetAutoDisableLog]] = defaultdict(list) - for dataset_auto_disable_log in dataset_auto_disable_logs: - if dataset_auto_disable_log.tenant_id not in dataset_auto_disable_logs_map: - dataset_auto_disable_logs_map[dataset_auto_disable_log.tenant_id] = [] - dataset_auto_disable_logs_map[dataset_auto_disable_log.tenant_id].append(dataset_auto_disable_log) - url = f"{dify_config.CONSOLE_WEB_URL}/datasets" - for tenant_id, tenant_dataset_auto_disable_logs in dataset_auto_disable_logs_map.items(): - features = FeatureService.get_features(tenant_id) - plan = features.billing.subscription.plan - if plan != CloudPlan.SANDBOX: - knowledge_details = [] - # check tenant - tenant = db.session.scalar(select(Tenant).where(Tenant.id == tenant_id)) - if not tenant: - continue - # check current owner - current_owner_join = db.session.scalar( - select(TenantAccountJoin) - .where(TenantAccountJoin.tenant_id == tenant.id, TenantAccountJoin.role == "owner") - .limit(1) - ) - if not current_owner_join: - continue - account = db.session.scalar(select(Account).where(Account.id == current_owner_join.account_id)) - if not account: - continue + with session_factory.create_session() as session: + dataset_auto_disable_logs = session.scalars( + select(DatasetAutoDisableLog).where(DatasetAutoDisableLog.notified.is_(False)) + ).all() + # group by tenant_id + dataset_auto_disable_logs_map: dict[str, list[DatasetAutoDisableLog]] = defaultdict(list) + for dataset_auto_disable_log in dataset_auto_disable_logs: + if dataset_auto_disable_log.tenant_id not in dataset_auto_disable_logs_map: + dataset_auto_disable_logs_map[dataset_auto_disable_log.tenant_id] = [] + dataset_auto_disable_logs_map[dataset_auto_disable_log.tenant_id].append(dataset_auto_disable_log) + url = f"{dify_config.CONSOLE_WEB_URL}/datasets" + for tenant_id, tenant_dataset_auto_disable_logs in dataset_auto_disable_logs_map.items(): + features = FeatureService.get_features(tenant_id) + plan = features.billing.subscription.plan + if plan != CloudPlan.SANDBOX: + knowledge_details = [] + # check tenant + tenant = session.scalar(select(Tenant).where(Tenant.id == tenant_id)) + if not tenant: + continue + # check current owner + current_owner_join = session.scalar( + select(TenantAccountJoin) + .where(TenantAccountJoin.tenant_id == tenant.id, TenantAccountJoin.role == "owner") + .limit(1) + ) + if not current_owner_join: + continue + account = session.scalar(select(Account).where(Account.id == current_owner_join.account_id)) + if not account: + continue - dataset_auto_dataset_map = {} # type: ignore + dataset_auto_dataset_map = {} # type: ignore + for dataset_auto_disable_log in tenant_dataset_auto_disable_logs: + if dataset_auto_disable_log.dataset_id not in dataset_auto_dataset_map: + dataset_auto_dataset_map[dataset_auto_disable_log.dataset_id] = [] + dataset_auto_dataset_map[dataset_auto_disable_log.dataset_id].append( + dataset_auto_disable_log.document_id + ) + + for dataset_id, document_ids in dataset_auto_dataset_map.items(): + dataset = session.scalar(select(Dataset).where(Dataset.id == dataset_id)) + if dataset: + document_count = len(document_ids) + knowledge_details.append(rf"Knowledge base {dataset.name}: {document_count} documents") + if knowledge_details: + email_service = get_email_i18n_service() + email_service.send_email( + email_type=EmailType.DOCUMENT_CLEAN_NOTIFY, + language_code="en-US", + to=account.email, + template_context={ + "userName": account.email, + "knowledge_details": knowledge_details, + "url": url, + }, + ) + + # update notified to True for dataset_auto_disable_log in tenant_dataset_auto_disable_logs: - if dataset_auto_disable_log.dataset_id not in dataset_auto_dataset_map: - dataset_auto_dataset_map[dataset_auto_disable_log.dataset_id] = [] - dataset_auto_dataset_map[dataset_auto_disable_log.dataset_id].append( - dataset_auto_disable_log.document_id - ) - - for dataset_id, document_ids in dataset_auto_dataset_map.items(): - dataset = db.session.scalar(select(Dataset).where(Dataset.id == dataset_id)) - if dataset: - document_count = len(document_ids) - knowledge_details.append(rf"Knowledge base {dataset.name}: {document_count} documents") - if knowledge_details: - email_service = get_email_i18n_service() - email_service.send_email( - email_type=EmailType.DOCUMENT_CLEAN_NOTIFY, - language_code="en-US", - to=account.email, - template_context={ - "userName": account.email, - "knowledge_details": knowledge_details, - "url": url, - }, - ) - - # update notified to True - for dataset_auto_disable_log in tenant_dataset_auto_disable_logs: - dataset_auto_disable_log.notified = True - db.session.commit() + dataset_auto_disable_log.notified = True + session.commit() end_at = time.perf_counter() logger.info(click.style(f"Send document clean notify mail succeeded: latency: {end_at - start_at}", fg="green")) except Exception: From 96122692cba202fab96d14e72cb4810593dc6669 Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Fri, 17 Apr 2026 17:10:10 +0800 Subject: [PATCH 03/15] fix(web): keep workflow panel operator anchor mounted during menu close animation (#35363) --- .../nodes/_base/components/node-control.tsx | 24 +++++++------------ .../_base/components/panel-operator/index.tsx | 14 +++++------ 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/web/app/components/workflow/nodes/_base/components/node-control.tsx b/web/app/components/workflow/nodes/_base/components/node-control.tsx index 4f2980186d..439e097bc9 100644 --- a/web/app/components/workflow/nodes/_base/components/node-control.tsx +++ b/web/app/components/workflow/nodes/_base/components/node-control.tsx @@ -1,15 +1,12 @@ import type { FC } from 'react' import type { Node } from '../../../types' +import { cn } from '@langgenius/dify-ui/cn' import { Tooltip, TooltipContent, TooltipTrigger, } from '@langgenius/dify-ui/tooltip' -import { - memo, - useCallback, - useState, -} from 'react' +import { memo } from 'react' import { useTranslation } from 'react-i18next' import { Stop, @@ -31,23 +28,19 @@ const NodeControl: FC = ({ pluginInstallLocked, }) => { const { t } = useTranslation() - const [open, setOpen] = useState(false) const { handleNodeSelect } = useNodesInteractions() const workflowStore = useWorkflowStore() const isSingleRunning = data._singleRunningStatus === NodeRunningStatus.Running - const handleOpenChange = useCallback((newOpen: boolean) => { - setOpen(newOpen) - }, []) const isChildNode = !!(data.isInIteration || data.isInLoop) return ( ) +type BannerImpressionTrackerProps = { + banners: BannerType[] + accountId?: string + language: string + trackedBannerIdsRef: React.MutableRefObject> +} + +const BannerImpressionTracker: FC = ({ + banners, + accountId, + language, + trackedBannerIdsRef, +}) => { + const { selectedIndex } = useCarousel() + + useEffect(() => { + if (!accountId) + return + + const currentBanner = banners[selectedIndex] + if (!currentBanner || trackedBannerIdsRef.current.has(currentBanner.id)) + return + + trackEvent('explore_banner_impression', { + banner_id: currentBanner.id, + title: currentBanner.content.title, + sort: selectedIndex + 1, + link: currentBanner.link, + page: 'explore', + language, + account_id: accountId, + event_time: Date.now(), + }) + trackedBannerIdsRef.current.add(currentBanner.id) + }, [accountId, banners, language, selectedIndex, trackedBannerIdsRef]) + + return null +} + const Banner: FC = () => { const locale = useLocale() const { data: banners, isLoading, isError } = useGetBanners(locale) @@ -60,28 +100,6 @@ const Banner: FC = () => { } }, []) - useEffect(() => { - if (!accountId) - return - - enabledBanners.forEach((banner, index) => { - if (trackedBannerIdsRef.current.has(banner.id)) - return - - trackEvent('explore_banner_impression', { - banner_id: banner.id, - title: banner.content.title, - sort: index + 1, - link: banner.link, - page: 'explore', - language: locale, - account_id: accountId, - event_time: Date.now(), - }) - trackedBannerIdsRef.current.add(banner.id) - }) - }, [accountId, enabledBanners, locale]) - if (isLoading) return @@ -102,6 +120,12 @@ const Banner: FC = () => { onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} > + {enabledBanners.map((banner, index) => ( From 3c7d6739b5adb274261d3f98f6a011e19ee5bf4e Mon Sep 17 00:00:00 2001 From: Stephen Zhou Date: Fri, 17 Apr 2026 20:32:12 +0800 Subject: [PATCH 07/15] test: browser mode for dify ui (#35365) --- .github/workflows/web-tests.yml | 3 + eslint.config.mjs | 2 +- packages/dify-ui/package.json | 7 +- .../src/alert-dialog/__tests__/index.spec.tsx | 72 +- .../src/avatar/__tests__/index.spec.tsx | 82 +- .../src/button/__tests__/index.spec.tsx | 162 +- .../src/context-menu/__tests__/index.spec.tsx | 117 +- .../src/dialog/__tests__/index.spec.tsx | 40 +- .../dropdown-menu/__tests__/index.spec.tsx | 163 +- .../src/number-field/__tests__/index.spec.tsx | 136 +- .../src/popover/__tests__/index.spec.tsx | 46 +- .../src/scroll-area/__tests__/index.spec.tsx | 302 ++- .../src/select/__tests__/index.spec.tsx | 172 +- .../src/slider/__tests__/index.spec.tsx | 90 +- .../src/switch/__tests__/index.spec.tsx | 201 +- .../src/toast/__tests__/index.spec.tsx | 334 ++- .../src/tooltip/__tests__/index.spec.tsx | 53 +- packages/dify-ui/tests/setup.ts | 44 - packages/dify-ui/tsconfig.json | 8 +- packages/dify-ui/vite.config.ts | 9 +- pnpm-lock.yaml | 1903 +++-------------- pnpm-workspace.yaml | 91 +- 22 files changed, 1259 insertions(+), 2778 deletions(-) delete mode 100644 packages/dify-ui/tests/setup.ts diff --git a/.github/workflows/web-tests.yml b/.github/workflows/web-tests.yml index dcee8863ce..2a5cf19645 100644 --- a/.github/workflows/web-tests.yml +++ b/.github/workflows/web-tests.yml @@ -109,6 +109,9 @@ jobs: - name: Setup web environment uses: ./.github/actions/setup-web + - name: Install Chromium for Browser Mode + run: vp exec playwright install --with-deps chromium + - name: Run dify-ui tests run: vp test run --coverage --silent=passed-only diff --git a/eslint.config.mjs b/eslint.config.mjs index 5e81e95f2f..ae9fdaff01 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -13,6 +13,7 @@ export default antfu( '!e2e/**', '!eslint.config.mjs', '!package.json', + '!pnpm-workspace.yaml', '!vite.config.ts', ...original, ], @@ -35,7 +36,6 @@ export default antfu( }, }, e18e: false, - pnpm: false, }, markdownPreferences.configs.standard, { diff --git a/packages/dify-ui/package.json b/packages/dify-ui/package.json index 2b78b25ed6..b3430ab4ee 100644 --- a/packages/dify-ui/package.json +++ b/packages/dify-ui/package.json @@ -99,15 +99,12 @@ "@storybook/addon-themes": "catalog:", "@storybook/react-vite": "catalog:", "@tailwindcss/vite": "catalog:", - "@testing-library/jest-dom": "catalog:", - "@testing-library/react": "catalog:", - "@testing-library/user-event": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "@vitejs/plugin-react": "catalog:", "@vitest/coverage-v8": "catalog:", "class-variance-authority": "catalog:", - "happy-dom": "catalog:", + "playwright": "catalog:", "react": "catalog:", "react-dom": "catalog:", "storybook": "catalog:", @@ -115,6 +112,6 @@ "typescript": "catalog:", "vite": "catalog:", "vite-plus": "catalog:", - "vitest": "catalog:" + "vitest-browser-react": "catalog:" } } diff --git a/packages/dify-ui/src/alert-dialog/__tests__/index.spec.tsx b/packages/dify-ui/src/alert-dialog/__tests__/index.spec.tsx index 23fbcb19d6..5248be9a16 100644 --- a/packages/dify-ui/src/alert-dialog/__tests__/index.spec.tsx +++ b/packages/dify-ui/src/alert-dialog/__tests__/index.spec.tsx @@ -1,5 +1,4 @@ -import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import { describe, expect, it, vi } from 'vitest' +import { render } from 'vitest-browser-react' import { AlertDialog, AlertDialogActions, @@ -11,10 +10,12 @@ import { AlertDialogTrigger, } from '../index' +const asHTMLElement = (element: HTMLElement | SVGElement) => element as HTMLElement + describe('AlertDialog wrapper', () => { describe('Rendering', () => { - it('should render alert dialog content when dialog is open', () => { - render( + it('should render alert dialog content when dialog is open', async () => { + const screen = await render( Confirm Delete @@ -23,13 +24,12 @@ describe('AlertDialog wrapper', () => { , ) - const dialog = screen.getByRole('alertdialog') - expect(dialog).toHaveTextContent('Confirm Delete') - expect(dialog).toHaveTextContent('This action cannot be undone.') + await expect.element(screen.getByRole('alertdialog')).toHaveTextContent('Confirm Delete') + await expect.element(screen.getByRole('alertdialog')).toHaveTextContent('This action cannot be undone.') }) - it('should not render content when dialog is closed', () => { - render( + it('should not render content when dialog is closed', async () => { + const screen = await render( Hidden Title @@ -37,13 +37,13 @@ describe('AlertDialog wrapper', () => { , ) - expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument() + expect(screen.container.querySelector('[role="alertdialog"]')).not.toBeInTheDocument() }) }) describe('Props', () => { - it('should apply custom className to popup', () => { - render( + it('should apply custom className to popup', async () => { + const screen = await render( Title @@ -51,12 +51,11 @@ describe('AlertDialog wrapper', () => { , ) - const dialog = screen.getByRole('alertdialog') - expect(dialog).toHaveClass('custom-class') + await expect.element(screen.getByRole('alertdialog')).toHaveClass('custom-class') }) - it('should not render a close button by default', () => { - render( + it('should not render a close button by default', async () => { + const screen = await render( Title @@ -64,13 +63,13 @@ describe('AlertDialog wrapper', () => { , ) - expect(screen.queryByRole('button', { name: 'Close' })).not.toBeInTheDocument() + expect(() => screen.getByRole('button', { name: 'Close' }).element()).toThrow() }) }) describe('User Interactions', () => { it('should open and close dialog when trigger and cancel button are clicked', async () => { - render( + const screen = await render( Open Dialog @@ -83,21 +82,21 @@ describe('AlertDialog wrapper', () => { , ) - expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument() + expect(screen.container.querySelector('[role="alertdialog"]')).not.toBeInTheDocument() - fireEvent.click(screen.getByRole('button', { name: 'Open Dialog' })) - expect(await screen.findByRole('alertdialog')).toHaveTextContent('Action Required') + asHTMLElement(screen.getByRole('button', { name: 'Open Dialog' }).element()).click() + await expect.element(screen.getByRole('alertdialog')).toHaveTextContent('Action Required') - fireEvent.click(screen.getByRole('button', { name: 'Cancel' })) - await waitFor(() => { - expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument() + asHTMLElement(screen.getByRole('button', { name: 'Cancel' }).element()).click() + await vi.waitFor(() => { + expect(screen.container.querySelector('[role="alertdialog"]')).not.toBeInTheDocument() }) }) }) describe('Composition Helpers', () => { - it('should render actions wrapper and default confirm button styles', () => { - render( + it('should render actions wrapper and default confirm button styles', async () => { + const screen = await render( Action Required @@ -108,15 +107,14 @@ describe('AlertDialog wrapper', () => { , ) - expect(screen.getByTestId('actions')).toHaveClass('flex', 'items-start', 'justify-end', 'gap-2', 'self-stretch', 'p-6', 'custom-actions') - const confirmButton = screen.getByRole('button', { name: 'Confirm' }) - expect(confirmButton).toHaveClass('bg-components-button-destructive-primary-bg') + await expect.element(screen.getByTestId('actions')).toHaveClass('flex', 'items-start', 'justify-end', 'gap-2', 'self-stretch', 'p-6', 'custom-actions') + await expect.element(screen.getByRole('button', { name: 'Confirm' })).toHaveClass('bg-components-button-destructive-primary-bg') }) it('should keep dialog open after confirm click and close via cancel helper', async () => { const onConfirm = vi.fn() - render( + const screen = await render( Open Dialog @@ -129,16 +127,16 @@ describe('AlertDialog wrapper', () => { , ) - fireEvent.click(screen.getByRole('button', { name: 'Open Dialog' })) - expect(await screen.findByRole('alertdialog')).toBeInTheDocument() + asHTMLElement(screen.getByRole('button', { name: 'Open Dialog' }).element()).click() + await expect.element(screen.getByRole('alertdialog')).toBeInTheDocument() - fireEvent.click(screen.getByRole('button', { name: 'Confirm' })) + asHTMLElement(screen.getByRole('button', { name: 'Confirm' }).element()).click() expect(onConfirm).toHaveBeenCalledTimes(1) - expect(screen.getByRole('alertdialog')).toBeInTheDocument() + await expect.element(screen.getByRole('alertdialog')).toBeInTheDocument() - fireEvent.click(screen.getByRole('button', { name: 'Cancel' })) - await waitFor(() => { - expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument() + asHTMLElement(screen.getByRole('button', { name: 'Cancel' }).element()).click() + await vi.waitFor(() => { + expect(screen.container.querySelector('[role="alertdialog"]')).not.toBeInTheDocument() }) }) }) diff --git a/packages/dify-ui/src/avatar/__tests__/index.spec.tsx b/packages/dify-ui/src/avatar/__tests__/index.spec.tsx index 8a384139c2..b0ea496282 100644 --- a/packages/dify-ui/src/avatar/__tests__/index.spec.tsx +++ b/packages/dify-ui/src/avatar/__tests__/index.spec.tsx @@ -1,25 +1,25 @@ -import { render, screen } from '@testing-library/react' +import { render } from 'vitest-browser-react' import { Avatar, AvatarFallback, AvatarImage, AvatarRoot } from '..' describe('Avatar', () => { describe('Rendering', () => { - it('should keep the fallback visible when avatar URL is provided before image load', () => { - render() + it('should keep the fallback visible when avatar URL is provided before image load', async () => { + const screen = await render() - expect(screen.getByText('J')).toBeInTheDocument() + await expect.element(screen.getByText('J')).toBeInTheDocument() }) - it('should render fallback with uppercase initial when avatar is null', () => { - render() + it('should render fallback with uppercase initial when avatar is null', async () => { + const screen = await render() - expect(screen.queryByRole('img')).not.toBeInTheDocument() - expect(screen.getByText('A')).toBeInTheDocument() + expect(screen.container.querySelector('img')).not.toBeInTheDocument() + await expect.element(screen.getByText('A')).toBeInTheDocument() }) - it('should render the fallback when avatar is provided', () => { - render() + it('should render the fallback when avatar is provided', async () => { + const screen = await render() - expect(screen.getByText('J')).toBeInTheDocument() + await expect.element(screen.getByText('J')).toBeInTheDocument() }) }) @@ -33,36 +33,36 @@ describe('Avatar', () => { { size: 'xl' as const, expectedClass: 'size-10' }, { size: '2xl' as const, expectedClass: 'size-12' }, { size: '3xl' as const, expectedClass: 'size-16' }, - ])('should apply $expectedClass for size="$size"', ({ size, expectedClass }) => { - const { container } = render() + ])('should apply $expectedClass for size="$size"', async ({ size, expectedClass }) => { + const screen = await render() - const root = container.firstElementChild as HTMLElement + const root = screen.container.firstElementChild as HTMLElement expect(root).toHaveClass(expectedClass) }) - it('should default to md size when size is not specified', () => { - const { container } = render() + it('should default to md size when size is not specified', async () => { + const screen = await render() - const root = container.firstElementChild as HTMLElement + const root = screen.container.firstElementChild as HTMLElement expect(root).toHaveClass('size-8') }) }) describe('className prop', () => { - it('should merge className with avatar variant classes on root', () => { - const { container } = render( + it('should merge className with avatar variant classes on root', async () => { + const screen = await render( , ) - const root = container.firstElementChild as HTMLElement + const root = screen.container.firstElementChild as HTMLElement expect(root).toHaveClass('custom-class') expect(root).toHaveClass('rounded-full', 'bg-primary-600') }) }) describe('Primitives', () => { - it('should support composed avatar usage through exported primitives', () => { - render( + it('should support composed avatar usage through exported primitives', async () => { + const screen = await render( @@ -71,17 +71,17 @@ describe('Avatar', () => { , ) - expect(screen.getByTestId('avatar-root')).toHaveClass('size-6') - expect(screen.getByText('J')).toBeInTheDocument() - expect(screen.getByText('J')).toHaveStyle({ backgroundColor: 'rgb(1, 2, 3)' }) + await expect.element(screen.getByTestId('avatar-root')).toHaveClass('size-6') + await expect.element(screen.getByText('J')).toBeInTheDocument() + await expect.element(screen.getByText('J')).toHaveStyle({ backgroundColor: 'rgb(1, 2, 3)' }) }) }) describe('Edge Cases', () => { - it('should handle empty string name gracefully', () => { - const { container } = render() + it('should handle empty string name gracefully', async () => { + const screen = await render() - const fallback = container.querySelector('.text-white') as HTMLElement + const fallback = screen.container.querySelector('.text-white') as HTMLElement expect(fallback).toBeInTheDocument() expect(fallback.textContent).toBe('') }) @@ -89,23 +89,23 @@ describe('Avatar', () => { it.each([ { name: '中文名', expected: '中', label: 'Chinese characters' }, { name: '123User', expected: '1', label: 'number' }, - ])('should display first character when name starts with $label', ({ name, expected }) => { - render() + ])('should display first character when name starts with $label', async ({ name, expected }) => { + const screen = await render() - expect(screen.getByText(expected)).toBeInTheDocument() + await expect.element(screen.getByText(expected)).toBeInTheDocument() }) - it('should handle empty string avatar as falsy value', () => { - render() + it('should handle empty string avatar as falsy value', async () => { + const screen = await render() - expect(screen.queryByRole('img')).not.toBeInTheDocument() - expect(screen.getByText('T')).toBeInTheDocument() + expect(screen.container.querySelector('img')).not.toBeInTheDocument() + await expect.element(screen.getByText('T')).toBeInTheDocument() }) }) describe('onLoadingStatusChange', () => { - it('should render the fallback when avatar and onLoadingStatusChange are provided', () => { - render( + it('should render the fallback when avatar and onLoadingStatusChange are provided', async () => { + const screen = await render( { />, ) - expect(screen.getByText('J')).toBeInTheDocument() + await expect.element(screen.getByText('J')).toBeInTheDocument() }) - it('should not render image when avatar is null even with onLoadingStatusChange', () => { + it('should not render image when avatar is null even with onLoadingStatusChange', async () => { const onStatusChange = vi.fn() - render( + const screen = await render( , ) - expect(screen.queryByRole('img')).not.toBeInTheDocument() + expect(screen.container.querySelector('img')).not.toBeInTheDocument() }) }) }) diff --git a/packages/dify-ui/src/button/__tests__/index.spec.tsx b/packages/dify-ui/src/button/__tests__/index.spec.tsx index e7b9c92c91..f3694c29af 100644 --- a/packages/dify-ui/src/button/__tests__/index.spec.tsx +++ b/packages/dify-ui/src/button/__tests__/index.spec.tsx @@ -1,46 +1,48 @@ -import { fireEvent, render, screen } from '@testing-library/react' +import { render } from 'vitest-browser-react' import { Button } from '../index' +const asHTMLElement = (element: HTMLElement | SVGElement) => element as HTMLElement + describe('Button', () => { describe('rendering', () => { - it('renders children text', () => { - render() - expect(screen.getByRole('button')).toHaveTextContent('Click me') + it('renders children text', async () => { + const screen = await render() + await expect.element(screen.getByRole('button')).toHaveTextContent('Click me') }) - it('renders as a native button element by default', () => { - render() - expect(screen.getByRole('button').tagName).toBe('BUTTON') + it('renders as a native button element by default', async () => { + const screen = await render() + expect(screen.getByRole('button').element().tagName).toBe('BUTTON') }) - it('defaults to type="button"', () => { - render() - expect(screen.getByRole('button')).toHaveAttribute('type', 'button') + it('defaults to type="button"', async () => { + const screen = await render() + await expect.element(screen.getByRole('button')).toHaveAttribute('type', 'button') }) - it('allows type override to submit', () => { - render() - expect(screen.getByRole('button')).toHaveAttribute('type', 'submit') + it('allows type override to submit', async () => { + const screen = await render() + await expect.element(screen.getByRole('button')).toHaveAttribute('type', 'submit') }) - it('renders custom element via render prop', () => { - render() - const link = screen.getByRole('link') - expect(link).toHaveTextContent('Link') - expect(link).toHaveAttribute('href', '/test') + it('renders custom element via render prop', async () => { + const screen = await render() + const button = screen.getByRole('button', { name: 'Link' }).element() + expect(button.tagName).toBe('A') + expect(button).toHaveAttribute('href', '/test') }) - it('applies base layout classes', () => { - render() - const btn = screen.getByRole('button') + it('applies base layout classes', async () => { + const screen = await render() + const btn = screen.getByRole('button').element() expect(btn).toHaveClass('inline-flex', 'justify-center', 'items-center', 'cursor-pointer') }) }) describe('variants', () => { - it('applies default secondary variant', () => { - render() - const btn = screen.getByRole('button') + it('applies default secondary variant', async () => { + const screen = await render() + const btn = screen.getByRole('button').element() expect(btn).toHaveClass('bg-components-button-secondary-bg', 'text-components-button-secondary-text') }) @@ -51,124 +53,124 @@ describe('Button', () => { { variant: 'ghost' as const, expectedClass: 'text-components-button-ghost-text' }, { variant: 'ghost-accent' as const, expectedClass: 'hover:bg-state-accent-hover' }, { variant: 'tertiary' as const, expectedClass: 'bg-components-button-tertiary-bg' }, - ])('applies $variant variant', ({ variant, expectedClass }) => { - render() - expect(screen.getByRole('button')).toHaveClass(expectedClass) + ])('applies $variant variant', async ({ variant, expectedClass }) => { + const screen = await render() + await expect.element(screen.getByRole('button')).toHaveClass(expectedClass) }) - it('applies destructive tone with default variant', () => { - render() - expect(screen.getByRole('button')).toHaveClass('bg-components-button-destructive-secondary-bg') + it('applies destructive tone with default variant', async () => { + const screen = await render() + await expect.element(screen.getByRole('button')).toHaveClass('bg-components-button-destructive-secondary-bg') }) - it('applies destructive tone with primary variant', () => { - render() - expect(screen.getByRole('button')).toHaveClass('bg-components-button-destructive-primary-bg') + it('applies destructive tone with primary variant', async () => { + const screen = await render() + await expect.element(screen.getByRole('button')).toHaveClass('bg-components-button-destructive-primary-bg') }) - it('applies destructive tone with tertiary variant', () => { - render() - expect(screen.getByRole('button')).toHaveClass('bg-components-button-destructive-tertiary-bg') + it('applies destructive tone with tertiary variant', async () => { + const screen = await render() + await expect.element(screen.getByRole('button')).toHaveClass('bg-components-button-destructive-tertiary-bg') }) - it('applies destructive tone with ghost variant', () => { - render() - expect(screen.getByRole('button')).toHaveClass('text-components-button-destructive-ghost-text') + it('applies destructive tone with ghost variant', async () => { + const screen = await render() + await expect.element(screen.getByRole('button')).toHaveClass('text-components-button-destructive-ghost-text') }) }) describe('sizes', () => { - it('applies default medium size', () => { - render() - expect(screen.getByRole('button')).toHaveClass('h-8', 'rounded-lg') + it('applies default medium size', async () => { + const screen = await render() + await expect.element(screen.getByRole('button')).toHaveClass('h-8', 'rounded-lg') }) it.each([ { size: 'small' as const, expectedClass: 'h-6' }, { size: 'medium' as const, expectedClass: 'h-8' }, { size: 'large' as const, expectedClass: 'h-9' }, - ])('applies $size size', ({ size, expectedClass }) => { - render() - expect(screen.getByRole('button')).toHaveClass(expectedClass) + ])('applies $size size', async ({ size, expectedClass }) => { + const screen = await render() + await expect.element(screen.getByRole('button')).toHaveClass(expectedClass) }) }) describe('loading', () => { - it('shows spinner when loading', () => { - render() - expect(screen.getByRole('button').querySelector('[aria-hidden="true"]')).toBeInTheDocument() + it('shows spinner when loading', async () => { + const screen = await render() + expect(screen.getByRole('button').element().querySelector('[aria-hidden="true"]')).toBeInTheDocument() }) - it('hides spinner when not loading', () => { - render() - expect(screen.getByRole('button').querySelector('[aria-hidden="true"]')).not.toBeInTheDocument() + it('hides spinner when not loading', async () => { + const screen = await render() + expect(screen.getByRole('button').element().querySelector('[aria-hidden="true"]')).not.toBeInTheDocument() }) - it('auto-disables when loading', () => { - render() - expect(screen.getByRole('button')).toBeDisabled() + it('auto-disables when loading', async () => { + const screen = await render() + await expect.element(screen.getByRole('button')).toBeDisabled() }) - it('sets aria-busy when loading', () => { - render() - expect(screen.getByRole('button')).toHaveAttribute('aria-busy', 'true') + it('sets aria-busy when loading', async () => { + const screen = await render() + await expect.element(screen.getByRole('button')).toHaveAttribute('aria-busy', 'true') }) - it('does not set aria-busy when not loading', () => { - render() - expect(screen.getByRole('button')).not.toHaveAttribute('aria-busy') + it('does not set aria-busy when not loading', async () => { + const screen = await render() + await expect.element(screen.getByRole('button')).not.toHaveAttribute('aria-busy') }) }) describe('disabled', () => { - it('disables button when disabled prop is set', () => { - render() - expect(screen.getByRole('button')).toBeDisabled() + it('disables button when disabled prop is set', async () => { + const screen = await render() + await expect.element(screen.getByRole('button')).toBeDisabled() }) - it('keeps focusable when loading with focusableWhenDisabled', () => { - render() - const button = screen.getByRole('button') + it('keeps focusable when loading with focusableWhenDisabled', async () => { + const screen = await render() + const button = screen.getByRole('button').element() expect(button).toHaveAttribute('aria-disabled', 'true') }) }) describe('events', () => { - it('fires onClick when clicked', () => { + it('fires onClick when clicked', async () => { const onClick = vi.fn() - render() - fireEvent.click(screen.getByRole('button')) + const screen = await render() + await screen.getByRole('button').click() expect(onClick).toHaveBeenCalledTimes(1) }) - it('does not fire onClick when disabled', () => { + it('does not fire onClick when disabled', async () => { const onClick = vi.fn() - render() - fireEvent.click(screen.getByRole('button')) + const screen = await render() + asHTMLElement(screen.getByRole('button').element()).click() expect(onClick).not.toHaveBeenCalled() }) - it('does not fire onClick when loading', () => { + it('does not fire onClick when loading', async () => { const onClick = vi.fn() - render() - fireEvent.click(screen.getByRole('button')) + const screen = await render() + asHTMLElement(screen.getByRole('button').element()).click() expect(onClick).not.toHaveBeenCalled() }) }) describe('className merging', () => { - it('merges custom className with variant classes', () => { - render() - const btn = screen.getByRole('button') + it('merges custom className with variant classes', async () => { + const screen = await render() + const btn = screen.getByRole('button').element() expect(btn).toHaveClass('custom-class') expect(btn).toHaveClass('inline-flex') }) }) describe('ref forwarding', () => { - it('forwards ref to the button element', () => { + it('forwards ref to the button element', async () => { let buttonRef: HTMLButtonElement | null = null - render( + await render(
- + + + + +
) diff --git a/web/app/components/billing/progress-bar/__tests__/index.spec.tsx b/web/app/components/billing/progress-bar/__tests__/index.spec.tsx deleted file mode 100644 index 4310fab19d..0000000000 --- a/web/app/components/billing/progress-bar/__tests__/index.spec.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { render, screen } from '@testing-library/react' -import ProgressBar from '../index' - -describe('ProgressBar', () => { - describe('Normal Mode (determinate)', () => { - it('renders with provided percent and color', () => { - render() - - const bar = screen.getByTestId('billing-progress-bar') - expect(bar.getAttribute('style')).toContain('width: 42%') - }) - - it('caps width at 100% when percent exceeds max', () => { - render() - - const bar = screen.getByTestId('billing-progress-bar') - expect(bar.getAttribute('style')).toContain('width: 100%') - }) - - it('renders with default color when no color prop is provided', () => { - render() - - const bar = screen.getByTestId('billing-progress-bar') - expect(bar.getAttribute('style')).toContain('width: 20%') - }) - }) - - describe('Indeterminate Mode', () => { - it('should render indeterminate progress bar when indeterminate is true', () => { - render() - - expect(screen.getByTestId('billing-progress-bar-indeterminate')).toBeInTheDocument() - }) - - it('should not render normal progress bar when indeterminate is true', () => { - render() - - expect(screen.queryByTestId('billing-progress-bar')).not.toBeInTheDocument() - expect(screen.getByTestId('billing-progress-bar-indeterminate')).toBeInTheDocument() - }) - - it('should render with different width based on indeterminateFull prop', () => { - const { rerender } = render( - , - ) - - const bar = screen.getByTestId('billing-progress-bar-indeterminate') - const partialClassName = bar.className - - rerender( - , - ) - - const fullClassName = screen.getByTestId('billing-progress-bar-indeterminate').className - expect(partialClassName).not.toBe(fullClassName) - }) - }) -}) diff --git a/web/app/components/billing/progress-bar/index.tsx b/web/app/components/billing/progress-bar/index.tsx deleted file mode 100644 index dfc2bf6fa9..0000000000 --- a/web/app/components/billing/progress-bar/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { cn } from '@langgenius/dify-ui/cn' - -type ProgressBarProps = { - percent: number - color: string - indeterminate?: boolean - indeterminateFull?: boolean // For Sandbox users: full width stripe -} - -const ProgressBar = ({ - percent = 0, - color = 'bg-components-progress-bar-progress-solid', - indeterminate = false, - indeterminateFull = false, -}: ProgressBarProps) => { - if (indeterminate) { - return ( -
-
-
- ) - } - - return ( -
-
-
- ) -} - -export default ProgressBar diff --git a/web/app/components/billing/usage-info/__tests__/index.spec.tsx b/web/app/components/billing/usage-info/__tests__/index.spec.tsx index 3cbab5c662..073c2e7fe2 100644 --- a/web/app/components/billing/usage-info/__tests__/index.spec.tsx +++ b/web/app/components/billing/usage-info/__tests__/index.spec.tsx @@ -71,8 +71,8 @@ describe('UsageInfo', () => { expect(screen.getByText('billing.plansCommon.unlimited')).toBeInTheDocument() }) - it('applies distinct styling when usage is close to or exceeds the limit', () => { - const { rerender } = render( + it('applies the neutral / warning / error tone as usage crosses thresholds', () => { + const { rerender, container } = render( { />, ) - const normalBarClass = screen.getByTestId('billing-progress-bar').className + expect(container.querySelector('.bg-components-progress-bar-progress-solid')).toBeInTheDocument() rerender( { />, ) - const warningBarClass = screen.getByTestId('billing-progress-bar').className - expect(warningBarClass).not.toBe(normalBarClass) + expect(container.querySelector('.bg-components-progress-warning-progress')).toBeInTheDocument() rerender( { />, ) - const errorBarClass = screen.getByTestId('billing-progress-bar').className - expect(errorBarClass).not.toBe(normalBarClass) - expect(errorBarClass).not.toBe(warningBarClass) + expect(container.querySelector('.bg-components-progress-error-progress')).toBeInTheDocument() }) it('does not render the icon when hideIcon is true', () => { @@ -126,8 +123,8 @@ describe('UsageInfo', () => { describe('Storage Mode', () => { describe('Below Threshold', () => { - it('should render indeterminate progress bar when usage is below threshold', () => { - render( + it('should render the redacted placeholder when usage is below threshold', () => { + const { container } = render( { />, ) - expect(screen.getByTestId('billing-progress-bar-indeterminate')).toBeInTheDocument() - expect(screen.queryByTestId('billing-progress-bar')).not.toBeInTheDocument() + expect(container.querySelector('[aria-hidden="true"]')).toBeInTheDocument() + expect(screen.queryByRole('meter')).not.toBeInTheDocument() }) it('should display "< threshold" format when usage is below threshold (non-sandbox)', () => { @@ -183,8 +180,8 @@ describe('UsageInfo', () => { expect(screen.getAllByText('MB').length).toBeGreaterThanOrEqual(1) }) - it('should render different indeterminate bar widths for sandbox vs non-sandbox', () => { - const { rerender } = render( + it('should render different placeholder widths for sandbox vs non-sandbox', () => { + const { rerender, container } = render( { />, ) - const sandboxBarClass = screen.getByTestId('billing-progress-bar-indeterminate').className + const sandboxBarClass = container.querySelector('.bg-progress-bar-indeterminate-stripe')!.className rerender( { />, ) - const nonSandboxBarClass = screen.getByTestId('billing-progress-bar-indeterminate').className + const nonSandboxBarClass = container.querySelector('.bg-progress-bar-indeterminate-stripe')!.className expect(sandboxBarClass).not.toBe(nonSandboxBarClass) }) }) describe('Sandbox Full Capacity', () => { - it('should render determinate progress bar when sandbox usage >= threshold', () => { - render( + it('should render the Meter when sandbox usage >= threshold', () => { + const { container } = render( { />, ) - expect(screen.getByTestId('billing-progress-bar')).toBeInTheDocument() - expect(screen.queryByTestId('billing-progress-bar-indeterminate')).not.toBeInTheDocument() + expect(screen.getByRole('meter')).toBeInTheDocument() + expect(container.querySelector('[aria-hidden="true"]')).toBeNull() }) it('should display "threshold / threshold unit" format when sandbox is at full capacity', () => { @@ -258,8 +255,8 @@ describe('UsageInfo', () => { }) describe('Pro/Team Users Above Threshold', () => { - it('should render normal progress bar when usage >= threshold', () => { - render( + it('should render the Meter when usage >= threshold', () => { + const { container } = render( { />, ) - expect(screen.getByTestId('billing-progress-bar')).toBeInTheDocument() - expect(screen.queryByTestId('billing-progress-bar-indeterminate')).not.toBeInTheDocument() + expect(screen.getByRole('meter')).toBeInTheDocument() + expect(container.querySelector('[aria-hidden="true"]')).toBeNull() }) it('should display actual usage when usage >= threshold', () => { diff --git a/web/app/components/billing/usage-info/__tests__/vector-space-info.spec.tsx b/web/app/components/billing/usage-info/__tests__/vector-space-info.spec.tsx index 041845ab3b..43e132c00f 100644 --- a/web/app/components/billing/usage-info/__tests__/vector-space-info.spec.tsx +++ b/web/app/components/billing/usage-info/__tests__/vector-space-info.spec.tsx @@ -3,6 +3,8 @@ import { defaultPlan } from '../../config' import { Plan } from '../../type' import VectorSpaceInfo from '../vector-space-info' +const queryPlaceholder = () => document.body.querySelector('[aria-hidden="true"]') + // Mock provider context with configurable plan let mockPlanType = Plan.sandbox let mockVectorSpaceUsage = 30 @@ -55,16 +57,11 @@ describe('VectorSpaceInfo', () => { mockVectorSpaceUsage = 30 }) - it('should render indeterminate progress bar when usage is below threshold', () => { + it('should render the redacted placeholder when usage is below threshold', () => { render() - expect(screen.getByTestId('billing-progress-bar-indeterminate')).toBeInTheDocument() - }) - - it('should render indeterminate bar for sandbox users', () => { - render() - - expect(screen.getByTestId('billing-progress-bar-indeterminate')).toBeInTheDocument() + expect(queryPlaceholder()).toBeInTheDocument() + expect(screen.queryByRole('meter')).not.toBeInTheDocument() }) it('should display "< 50" format for sandbox below threshold', () => { @@ -80,11 +77,11 @@ describe('VectorSpaceInfo', () => { mockVectorSpaceUsage = 50 }) - it('should render determinate progress bar when at full capacity', () => { + it('should render the Meter when at full capacity', () => { render() - expect(screen.getByTestId('billing-progress-bar')).toBeInTheDocument() - expect(screen.queryByTestId('billing-progress-bar-indeterminate')).not.toBeInTheDocument() + expect(screen.getByRole('meter')).toBeInTheDocument() + expect(queryPlaceholder()).toBeNull() }) it('should display "50 / 50 MB" format when at full capacity', () => { @@ -101,10 +98,11 @@ describe('VectorSpaceInfo', () => { mockVectorSpaceUsage = 30 }) - it('should render indeterminate progress bar when usage is below threshold', () => { + it('should render the redacted placeholder when usage is below threshold', () => { render() - expect(screen.getByTestId('billing-progress-bar-indeterminate')).toBeInTheDocument() + expect(queryPlaceholder()).toBeInTheDocument() + expect(screen.queryByRole('meter')).not.toBeInTheDocument() }) it('should display "< 50 / total" format when below threshold', () => { @@ -121,11 +119,11 @@ describe('VectorSpaceInfo', () => { mockVectorSpaceUsage = 100 }) - it('should render normal progress bar when usage >= threshold', () => { + it('should render the Meter when usage >= threshold', () => { render() - expect(screen.getByTestId('billing-progress-bar')).toBeInTheDocument() - expect(screen.queryByTestId('billing-progress-bar-indeterminate')).not.toBeInTheDocument() + expect(screen.getByRole('meter')).toBeInTheDocument() + expect(queryPlaceholder()).toBeNull() }) it('should display actual usage when above threshold', () => { @@ -142,10 +140,11 @@ describe('VectorSpaceInfo', () => { mockVectorSpaceUsage = 30 }) - it('should render indeterminate progress bar when usage is below threshold', () => { + it('should render the redacted placeholder when usage is below threshold', () => { render() - expect(screen.getByTestId('billing-progress-bar-indeterminate')).toBeInTheDocument() + expect(queryPlaceholder()).toBeInTheDocument() + expect(screen.queryByRole('meter')).not.toBeInTheDocument() }) it('should display "< 50 / total" format when below threshold', () => { @@ -163,11 +162,11 @@ describe('VectorSpaceInfo', () => { mockVectorSpaceUsage = 100 }) - it('should render normal progress bar when usage >= threshold', () => { + it('should render the Meter when usage >= threshold', () => { render() - expect(screen.getByTestId('billing-progress-bar')).toBeInTheDocument() - expect(screen.queryByTestId('billing-progress-bar-indeterminate')).not.toBeInTheDocument() + expect(screen.getByRole('meter')).toBeInTheDocument() + expect(queryPlaceholder()).toBeNull() }) it('should display actual usage when above threshold', () => { @@ -179,23 +178,26 @@ describe('VectorSpaceInfo', () => { }) describe('Pro/Team Plan Usage States', () => { - const renderAndGetBarClass = (usage: number) => { + const findToneClass = (usage: number) => { mockPlanType = Plan.professional mockVectorSpaceUsage = usage - const { unmount } = render() - const className = screen.getByTestId('billing-progress-bar').className + const { container, unmount } = render() + const indicator = container.querySelector( + '[class*="bg-components-progress-"]:not([class*="progress-bar-bg"])', + ) + const className = indicator?.className ?? '' unmount() return className } - it('should show distinct progress bar styling at different usage levels', () => { - const normalClass = renderAndGetBarClass(100) - const warningClass = renderAndGetBarClass(4100) - const errorClass = renderAndGetBarClass(5200) + it('should apply neutral / warning / error tone at distinct usage levels', () => { + const normalClass = findToneClass(100) + const warningClass = findToneClass(4100) + const errorClass = findToneClass(5200) - expect(normalClass).not.toBe(warningClass) - expect(warningClass).not.toBe(errorClass) - expect(normalClass).not.toBe(errorClass) + expect(normalClass).toContain('bg-components-progress-bar-progress-solid') + expect(warningClass).toContain('bg-components-progress-warning-progress') + expect(errorClass).toContain('bg-components-progress-error-progress') }) }) @@ -214,16 +216,11 @@ describe('VectorSpaceInfo', () => { expect(screen.getByText('102400MB')).toBeInTheDocument() }) - it('should render indeterminate progress bar when usage is below threshold', () => { + it('should render the redacted placeholder when usage is below threshold', () => { render() - expect(screen.getByTestId('billing-progress-bar-indeterminate')).toBeInTheDocument() - }) - - it('should render indeterminate bar for enterprise below threshold', () => { - render() - - expect(screen.getByTestId('billing-progress-bar-indeterminate')).toBeInTheDocument() + expect(queryPlaceholder()).toBeInTheDocument() + expect(screen.queryByRole('meter')).not.toBeInTheDocument() }) it('should display "< 50 / total" format when below threshold', () => { @@ -241,11 +238,11 @@ describe('VectorSpaceInfo', () => { mockVectorSpaceTotal = 102400 // 100 GB }) - it('should render normal progress bar when usage >= threshold', () => { + it('should render the Meter when usage >= threshold', () => { render() - expect(screen.getByTestId('billing-progress-bar')).toBeInTheDocument() - expect(screen.queryByTestId('billing-progress-bar-indeterminate')).not.toBeInTheDocument() + expect(screen.getByRole('meter')).toBeInTheDocument() + expect(queryPlaceholder()).toBeNull() }) it('should display actual usage when above threshold', () => { diff --git a/web/app/components/billing/usage-info/index.tsx b/web/app/components/billing/usage-info/index.tsx index a88523c4a5..68e889f320 100644 --- a/web/app/components/billing/usage-info/index.tsx +++ b/web/app/components/billing/usage-info/index.tsx @@ -1,11 +1,12 @@ 'use client' -import type { ComponentType, FC } from 'react' +import type { MeterTone } from '@langgenius/dify-ui/meter' +import type { ComponentType, FC, ReactNode } from 'react' import { cn } from '@langgenius/dify-ui/cn' +import { MeterIndicator, MeterRoot, MeterTrack } from '@langgenius/dify-ui/meter' import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import { NUM_INFINITE } from '../config' -import ProgressBar from '../progress-bar' type Props = { className?: string @@ -26,8 +27,6 @@ type Props = { isSandboxPlan?: boolean } -const WARNING_THRESHOLD = 80 - const UsageInfo: FC = ({ className, Icon, @@ -47,20 +46,21 @@ const UsageInfo: FC = ({ }) => { const { t } = useTranslation() - // Special display logic for usage below threshold (only in storage mode) const isBelowThreshold = storageMode && usage < storageThreshold - // Sandbox at full capacity (usage >= threshold and it's sandbox plan) const isSandboxFull = storageMode && isSandboxPlan && usage >= storageThreshold - const percent = usage / total * 100 - const getProgressColor = () => { - if (percent >= 100) - return 'bg-components-progress-error-progress' - if (percent >= WARNING_THRESHOLD) - return 'bg-components-progress-warning-progress' - return 'bg-components-progress-bar-progress-solid' - } - const color = getProgressColor() + // Single source of truth: sandbox full is visually clamped to 100%; all other + // determinate cases show the real percent capped at 100. Tone derives from + // this, so we never need a separate tone override. + const rawPercent = total > 0 ? (usage / total) * 100 : 0 + const effectivePercent = isSandboxFull ? 100 : Math.min(rawPercent, 100) + const tone: MeterTone + = effectivePercent >= 100 + ? 'error' + : effectivePercent >= 80 + ? 'warning' + : 'neutral' + const isUnlimited = total === NUM_INFINITE let totalDisplay: string | number = isUnlimited ? t('plansCommon.unlimited', { ns: 'billing' }) : total if (!isUnlimited && unit && unitPosition === 'inline') @@ -68,35 +68,26 @@ const UsageInfo: FC = ({ const showUnit = !!unit && !isUnlimited && unitPosition === 'suffix' const resetText = resetHint ?? (typeof resetInDays === 'number' ? t('usagePage.resetsIn', { ns: 'billing', count: resetInDays }) : undefined) - const renderRightInfo = () => { - if (resetText) { - return ( + const rightInfo: ReactNode = resetText + ? (
{resetText}
) - } - if (showUnit) { - return ( -
- {unit} -
- ) - } - return null - } + : showUnit + ? ( +
+ {unit} +
+ ) + : null - // Render usage display - const renderUsageDisplay = () => { - // Storage mode: special display logic + const usageDisplay: ReactNode = (() => { if (storageMode) { - // Sandbox user at full capacity if (isSandboxFull) { return (
- - {storageThreshold} - + {storageThreshold} / {storageThreshold} @@ -106,7 +97,6 @@ const UsageInfo: FC = ({
) } - // Usage below threshold - show "< 50 MB" or "< 50 / 5GB" if (isBelowThreshold) { return (
@@ -125,7 +115,6 @@ const UsageInfo: FC = ({
) } - // Pro/Team users with usage >= threshold - show actual usage return (
{usage} @@ -135,7 +124,6 @@ const UsageInfo: FC = ({ ) } - // Default display (storageMode = false) return (
{usage} @@ -143,9 +131,32 @@ const UsageInfo: FC = ({ {totalDisplay}
) - } + })() - const renderWithTooltip = (children: React.ReactNode) => { + const bar: ReactNode = isBelowThreshold + ? ( + // Decorative "< N MB" placeholder — not a meter, not a progressbar. + ) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/credits-exhausted-alert.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/credits-exhausted-alert.tsx index 53abe1211d..69a7f488b3 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/credits-exhausted-alert.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/credits-exhausted-alert.tsx @@ -1,3 +1,4 @@ +import { MeterIndicator, MeterLabel, MeterRoot, MeterTrack } from '@langgenius/dify-ui/meter' import { Trans, useTranslation } from 'react-i18next' import { CreditsCoin } from '@/app/components/base/icons/src/vender/line/financeAndECommerce' import { useModalContextSelector } from '@/context/modal-context' @@ -21,7 +22,9 @@ export default function CreditsExhaustedAlert({ hasApiKeyFallback }: CreditsExha : 'modelProvider.card.creditsExhaustedDescription' const usedCredits = totalCredits - credits - const usagePercent = totalCredits > 0 ? Math.min((usedCredits / totalCredits) * 100, 100) : 100 + const hasTotal = totalCredits > 0 + const meterValue = hasTotal ? Math.min(usedCredits, totalCredits) : 1 + const meterMax = hasTotal ? totalCredits : 1 return (
@@ -45,11 +48,11 @@ export default function CreditsExhaustedAlert({ hasApiKeyFallback }: CreditsExha />
-
+
- + {t('modelProvider.card.usageLabel', { ns: 'common' })} - +
@@ -59,13 +62,10 @@ export default function CreditsExhaustedAlert({ hasApiKeyFallback }: CreditsExha
-
-
-
-
+ + + +
) } From 3e876e173af5d61b48e1710c5f9e3fd327a526f1 Mon Sep 17 00:00:00 2001 From: 99 Date: Sat, 18 Apr 2026 19:16:24 +0800 Subject: [PATCH 09/15] chore(api): adapt Graphon 0.2.2 upgrade (#35377) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../console/app/conversation_variables.py | 2 +- .../console/app/workflow_draft_variable.py | 6 +- .../service_api/app/conversation.py | 4 +- api/core/agent/base_agent_runner.py | 2 +- .../model_config/converter.py | 2 +- api/core/app/apps/agent_chat/app_runner.py | 2 +- .../easy_ui_based_generate_task_pipeline.py | 2 +- api/core/app/workflow/file_runtime.py | 7 +- api/core/app/workflow/layers/persistence.py | 2 +- api/core/datasource/datasource_manager.py | 4 +- api/core/entities/provider_configuration.py | 6 +- .../code_executor/template_transformer.py | 2 +- api/core/helper/moderation.py | 2 +- api/core/helper/ssrf_proxy.py | 44 +++ api/core/model_manager.py | 38 +- api/core/plugin/impl/model_runtime.py | 6 +- .../prompt/agent_history_prompt_transform.py | 2 +- api/core/rag/embedding/cached_embedding.py | 2 +- api/core/rag/extractor/word_extractor.py | 26 +- .../processor/paragraph_index_processor.py | 4 +- api/core/rag/retrieval/dataset_retrieval.py | 6 +- api/core/rag/splitter/fixed_text_splitter.py | 2 +- .../repositories/human_input_repository.py | 2 +- api/core/tools/tool_file_manager.py | 2 +- api/core/tools/tool_manager.py | 7 +- .../tools/utils/model_invocation_utils.py | 2 +- api/core/tools/workflow_as_tool/tool.py | 5 +- ...input_compat.py => human_input_adapter.py} | 88 ++++- api/core/workflow/node_factory.py | 30 +- api/core/workflow/node_runtime.py | 50 ++- api/core/workflow/nodes/agent/agent_node.py | 9 +- .../nodes/datasource/datasource_node.py | 10 +- .../knowledge_index/knowledge_index_node.py | 13 +- .../knowledge_retrieval_node.py | 33 +- api/factories/file_factory/builders.py | 20 +- api/fields/_value_type_serializer.py | 4 +- api/fields/conversation_variable_fields.py | 4 +- api/fields/workflow_fields.py | 2 +- api/models/human_input.py | 2 +- api/models/utils/file_input_compat.py | 128 ++++++- api/models/workflow.py | 13 +- .../src/dify_trace_tencent/tencent_trace.py | 36 +- .../tencent_trace/test_tencent_trace.py | 37 +- api/pyproject.toml | 2 +- api/services/app_service.py | 2 +- api/services/dataset_service.py | 2 +- .../human_input_delivery_test_service.py | 2 +- api/services/rag_pipeline/rag_pipeline.py | 2 +- api/services/variable_truncator.py | 2 +- .../workflow_draft_variable_service.py | 8 +- api/services/workflow_service.py | 15 +- api/tasks/mail_human_input_delivery_task.py | 2 +- .../test_datasource_node_integration.py | 24 +- .../workflow/nodes/test_code.py | 5 +- .../workflow/nodes/test_http.py | 10 +- .../workflow/nodes/test_llm.py | 5 +- .../nodes/test_parameter_extractor.py | 5 +- .../workflow/nodes/test_template_transform.py | 5 +- .../workflow/nodes/test_tool.py | 5 +- .../test_human_input_form_repository_impl.py | 2 +- .../test_human_input_resume_node_execution.py | 12 +- .../factories/test_storage_key_loader.py | 4 +- ...hemy_execution_extra_content_repository.py | 2 +- .../test_human_input_delivery_test.py | 2 +- .../test_human_input_delivery_test_service.py | 2 +- .../test_mail_human_input_delivery_task.py | 2 +- .../controllers/console/app/test_workflow.py | 2 +- .../app/workflow_draft_variables_test.py | 8 +- .../service_api/app/test_conversation.py | 28 ++ .../test_workflow_response_converter.py | 4 +- .../core/app/apps/test_pause_resume.py | 38 +- .../core/app/workflow/test_file_runtime.py | 6 +- .../core/app/workflow/test_node_factory.py | 4 +- .../datasource/test_datasource_manager.py | 4 +- .../entities/test_entities_model_entities.py | 2 +- api/tests/unit_tests/core/file/test_models.py | 40 +- .../unit_tests/core/helper/test_ssrf_proxy.py | 59 +++ .../test_model_provider_factory.py | 12 +- .../core/plugin/test_model_runtime_adapter.py | 2 +- .../core/plugin/utils/test_chunk_merger.py | 6 +- .../prompt/test_advanced_prompt_transform.py | 20 +- .../test_agent_history_prompt_transform.py | 2 +- .../core/prompt/test_prompt_transform.py | 2 +- .../core/rag/extractor/test_word_extractor.py | 39 +- .../test_human_input_form_repository_impl.py | 2 +- .../test_human_input_repository.py | 2 +- api/tests/unit_tests/core/test_file.py | 4 +- .../unit_tests/core/variables/test_segment.py | 36 +- .../variables/test_segment_type_validation.py | 2 +- .../graph_engine/test_mock_factory.py | 33 +- .../workflow/graph_engine/test_mock_nodes.py | 9 +- .../test_parallel_human_input_join_resume.py | 16 +- .../core/workflow/nodes/answer/test_answer.py | 18 +- .../nodes/datasource/test_datasource_node.py | 24 +- .../http_request/test_http_request_node.py | 10 +- .../human_input/test_email_delivery_config.py | 2 +- .../nodes/human_input/test_entities.py | 54 ++- .../test_human_input_form_filled_event.py | 35 +- .../test_iteration_child_engine_errors.py | 20 +- .../test_knowledge_index_node.py | 91 +++-- .../test_knowledge_retrieval_node.py | 64 ++-- .../workflow/nodes/list_operator/node_spec.py | 152 +++----- .../core/workflow/nodes/llm/test_llm_utils.py | 8 +- .../core/workflow/nodes/llm/test_node.py | 47 +-- .../template_transform_node_spec.py | 101 ++--- .../test_template_transform_node.py | 19 +- .../core/workflow/nodes/test_base_node.py | 65 ++-- .../nodes/test_document_extractor_node.py | 116 +++++- .../core/workflow/nodes/test_if_else.py | 76 ++-- .../core/workflow/nodes/test_list_operator.py | 32 +- .../nodes/test_start_node_json_object.py | 40 +- .../workflow/nodes/tool/test_tool_node.py | 20 +- .../trigger_plugin/test_trigger_event_node.py | 31 +- .../webhook/test_webhook_file_conversion.py | 13 +- .../nodes/webhook/test_webhook_node.py | 48 ++- .../core/workflow/test_human_input_adapter.py | 350 ++++++++++++++++++ .../core/workflow/test_human_input_compat.py | 184 --------- .../core/workflow/test_node_factory.py | 72 +++- .../core/workflow/test_node_runtime.py | 2 +- .../core/workflow/test_system_variable.py | 2 +- .../core/workflow/test_variable_pool.py | 14 +- .../workflow/test_workflow_entry_helpers.py | 18 +- .../factories/test_build_from_mapping.py | 2 +- .../factories/test_variable_factory.py | 52 +-- .../test_conversation_variable_fields.py | 47 +++ .../unit_tests/fields/test_file_fields.py | 2 +- .../models/test_file_input_compat.py | 149 ++++++++ api/tests/unit_tests/models/test_workflow.py | 50 +-- .../services/test_variable_truncator.py | 4 +- .../services/test_workflow_service.py | 21 +- .../workflow/test_draft_var_loader_simple.py | 8 +- .../test_workflow_draft_variable_service.py | 4 +- .../test_workflow_human_input_delivery.py | 39 +- api/uv.lock | 8 +- 134 files changed, 2154 insertions(+), 1134 deletions(-) rename api/core/workflow/{human_input_compat.py => human_input_adapter.py} (74%) create mode 100644 api/tests/unit_tests/core/workflow/test_human_input_adapter.py delete mode 100644 api/tests/unit_tests/core/workflow/test_human_input_compat.py create mode 100644 api/tests/unit_tests/fields/test_conversation_variable_fields.py create mode 100644 api/tests/unit_tests/models/test_file_input_compat.py diff --git a/api/controllers/console/app/conversation_variables.py b/api/controllers/console/app/conversation_variables.py index cead33d14f..9c8b095b9f 100644 --- a/api/controllers/console/app/conversation_variables.py +++ b/api/controllers/console/app/conversation_variables.py @@ -45,7 +45,7 @@ class ConversationVariableResponse(ResponseModel): def _normalize_value_type(cls, value: Any) -> str: exposed_type = getattr(value, "exposed_type", None) if callable(exposed_type): - return str(exposed_type().value) + return str(exposed_type()) if isinstance(value, str): return value try: diff --git a/api/controllers/console/app/workflow_draft_variable.py b/api/controllers/console/app/workflow_draft_variable.py index f6319573e0..e32ba5f66c 100644 --- a/api/controllers/console/app/workflow_draft_variable.py +++ b/api/controllers/console/app/workflow_draft_variable.py @@ -102,7 +102,7 @@ def _serialize_var_value(variable: WorkflowDraftVariable): def _serialize_variable_type(workflow_draft_var: WorkflowDraftVariable) -> str: value_type = workflow_draft_var.value_type - return value_type.exposed_type().value + return str(value_type.exposed_type()) class FullContentDict(TypedDict): @@ -122,7 +122,7 @@ def _serialize_full_content(variable: WorkflowDraftVariable) -> FullContentDict result: FullContentDict = { "size_bytes": variable_file.size, - "value_type": variable_file.value_type.exposed_type().value, + "value_type": str(variable_file.value_type.exposed_type()), "length": variable_file.length, "download_url": file_helpers.get_signed_file_url(variable_file.upload_file_id, as_attachment=True), } @@ -598,7 +598,7 @@ class EnvironmentVariableCollectionApi(Resource): "name": v.name, "description": v.description, "selector": v.selector, - "value_type": v.value_type.exposed_type().value, + "value_type": str(v.value_type.exposed_type()), "value": v.value, # Do not track edited for env vars. "edited": False, diff --git a/api/controllers/service_api/app/conversation.py b/api/controllers/service_api/app/conversation.py index c4353ca7b8..ca4b18cb5e 100644 --- a/api/controllers/service_api/app/conversation.py +++ b/api/controllers/service_api/app/conversation.py @@ -84,10 +84,10 @@ class ConversationVariableResponse(ResponseModel): def normalize_value_type(cls, value: Any) -> str: exposed_type = getattr(value, "exposed_type", None) if callable(exposed_type): - return str(exposed_type().value) + return str(exposed_type()) if isinstance(value, str): try: - return str(SegmentType(value).exposed_type().value) + return str(SegmentType(value).exposed_type()) except ValueError: return value try: diff --git a/api/core/agent/base_agent_runner.py b/api/core/agent/base_agent_runner.py index 790602ef5d..c22102c2ba 100644 --- a/api/core/agent/base_agent_runner.py +++ b/api/core/agent/base_agent_runner.py @@ -42,7 +42,7 @@ from graphon.model_runtime.entities import ( ) from graphon.model_runtime.entities.message_entities import ImagePromptMessageContent, PromptMessageContentUnionTypes from graphon.model_runtime.entities.model_entities import ModelFeature -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel from models.enums import CreatorUserRole from models.model import Conversation, Message, MessageAgentThought, MessageFile diff --git a/api/core/app/app_config/easy_ui_based_app/model_config/converter.py b/api/core/app/app_config/easy_ui_based_app/model_config/converter.py index dbd7527fc6..5df3df2b3e 100644 --- a/api/core/app/app_config/easy_ui_based_app/model_config/converter.py +++ b/api/core/app/app_config/easy_ui_based_app/model_config/converter.py @@ -7,7 +7,7 @@ from core.errors.error import ModelCurrentlyNotSupportError, ProviderTokenNotIni from core.plugin.impl.model_runtime_factory import create_plugin_provider_manager from graphon.model_runtime.entities.llm_entities import LLMMode from graphon.model_runtime.entities.model_entities import ModelPropertyKey, ModelType -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel class ModelConfigConverter: diff --git a/api/core/app/apps/agent_chat/app_runner.py b/api/core/app/apps/agent_chat/app_runner.py index 09ddce327e..cae0eee0df 100644 --- a/api/core/app/apps/agent_chat/app_runner.py +++ b/api/core/app/apps/agent_chat/app_runner.py @@ -18,7 +18,7 @@ from core.moderation.base import ModerationError from extensions.ext_database import db from graphon.model_runtime.entities.llm_entities import LLMMode from graphon.model_runtime.entities.model_entities import ModelFeature, ModelPropertyKey -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel from models.model import App, Conversation, Message logger = logging.getLogger(__name__) diff --git a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py index dfe6133cb6..e2e07ebaff 100644 --- a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py +++ b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py @@ -59,7 +59,7 @@ from graphon.model_runtime.entities.message_entities import ( AssistantPromptMessage, TextPromptMessageContent, ) -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel from libs.datetime_utils import naive_utc_now from models.model import AppMode, Conversation, Message, MessageAgentThought, MessageFile, UploadFile diff --git a/api/core/app/workflow/file_runtime.py b/api/core/app/workflow/file_runtime.py index 68e5e5f0c8..3a6f9d575a 100644 --- a/api/core/app/workflow/file_runtime.py +++ b/api/core/app/workflow/file_runtime.py @@ -12,13 +12,14 @@ from typing import TYPE_CHECKING, Literal from configs import dify_config from core.app.file_access import DatabaseFileAccessController, FileAccessControllerProtocol from core.db.session_factory import session_factory -from core.helper.ssrf_proxy import ssrf_proxy +from core.helper.ssrf_proxy import graphon_ssrf_proxy from core.tools.signature import sign_tool_file from core.workflow.file_reference import parse_file_reference from extensions.ext_storage import storage from graphon.file import FileTransferMethod -from graphon.file.protocols import HttpResponseProtocol, WorkflowFileRuntimeProtocol +from graphon.file.protocols import WorkflowFileRuntimeProtocol from graphon.file.runtime import set_workflow_file_runtime +from graphon.http.protocols import HttpResponseProtocol if TYPE_CHECKING: from graphon.file import File @@ -43,7 +44,7 @@ class DifyWorkflowFileRuntime(WorkflowFileRuntimeProtocol): return dify_config.MULTIMODAL_SEND_FORMAT def http_get(self, url: str, *, follow_redirects: bool = True) -> HttpResponseProtocol: - return ssrf_proxy.get(url, follow_redirects=follow_redirects) + return graphon_ssrf_proxy.get(url, follow_redirects=follow_redirects) def storage_load(self, path: str, *, stream: bool = False) -> bytes | Generator: return storage.load(path, stream=stream) diff --git a/api/core/app/workflow/layers/persistence.py b/api/core/app/workflow/layers/persistence.py index 87f005a250..d521304615 100644 --- a/api/core/app/workflow/layers/persistence.py +++ b/api/core/app/workflow/layers/persistence.py @@ -349,7 +349,7 @@ class WorkflowPersistenceLayer(GraphEngineLayer): execution.total_tokens = runtime_state.total_tokens execution.total_steps = runtime_state.node_run_steps execution.outputs = execution.outputs or runtime_state.outputs - execution.exceptions_count = runtime_state.exceptions_count + execution.exceptions_count = max(execution.exceptions_count, runtime_state.exceptions_count) def _update_node_execution( self, diff --git a/api/core/datasource/datasource_manager.py b/api/core/datasource/datasource_manager.py index dc831e5cac..f0dcb13b62 100644 --- a/api/core/datasource/datasource_manager.py +++ b/api/core/datasource/datasource_manager.py @@ -352,11 +352,11 @@ class DatasourceManager: raise ValueError(f"UploadFile not found for file_id={file_id}, tenant_id={tenant_id}") file_info = File( - id=upload_file.id, + file_id=upload_file.id, filename=upload_file.name, extension="." + upload_file.extension, mime_type=upload_file.mime_type, - type=FileType.CUSTOM, + file_type=FileType.CUSTOM, transfer_method=FileTransferMethod.LOCAL_FILE, remote_url=upload_file.source_url, reference=build_file_reference(record_id=str(upload_file.id)), diff --git a/api/core/entities/provider_configuration.py b/api/core/entities/provider_configuration.py index 6bbf163c9d..38b87e2cd1 100644 --- a/api/core/entities/provider_configuration.py +++ b/api/core/entities/provider_configuration.py @@ -31,7 +31,7 @@ from graphon.model_runtime.entities.provider_entities import ( FormType, ProviderEntity, ) -from graphon.model_runtime.model_providers.__base.ai_model import AIModel +from graphon.model_runtime.model_providers.base.ai_model import AIModel from graphon.model_runtime.model_providers.model_provider_factory import ModelProviderFactory from graphon.model_runtime.runtime import ModelRuntime from libs.datetime_utils import naive_utc_now @@ -363,7 +363,7 @@ class ProviderConfiguration(BaseModel): ) for key, value in validated_credentials.items(): - if key in provider_credential_secret_variables: + if key in provider_credential_secret_variables and isinstance(value, str): validated_credentials[key] = encrypter.encrypt_token(self.tenant_id, value) return validated_credentials @@ -912,7 +912,7 @@ class ProviderConfiguration(BaseModel): ) for key, value in validated_credentials.items(): - if key in provider_credential_secret_variables: + if key in provider_credential_secret_variables and isinstance(value, str): validated_credentials[key] = encrypter.encrypt_token(self.tenant_id, value) return validated_credentials diff --git a/api/core/helper/code_executor/template_transformer.py b/api/core/helper/code_executor/template_transformer.py index b96a9ce380..38864a1830 100644 --- a/api/core/helper/code_executor/template_transformer.py +++ b/api/core/helper/code_executor/template_transformer.py @@ -102,7 +102,7 @@ class TemplateTransformer(ABC): @classmethod def serialize_inputs(cls, inputs: Mapping[str, Any]) -> str: - inputs_json_str = dumps_with_segments(inputs, ensure_ascii=False).encode() + inputs_json_str = dumps_with_segments(inputs).encode() input_base64_encoded = b64encode(inputs_json_str).decode("utf-8") return input_base64_encoded diff --git a/api/core/helper/moderation.py b/api/core/helper/moderation.py index dc37a36943..f169f247cf 100644 --- a/api/core/helper/moderation.py +++ b/api/core/helper/moderation.py @@ -8,7 +8,7 @@ from core.plugin.impl.model_runtime_factory import create_plugin_model_provider_ from extensions.ext_hosting_provider import hosting_configuration from graphon.model_runtime.entities.model_entities import ModelType from graphon.model_runtime.errors.invoke import InvokeBadRequestError -from graphon.model_runtime.model_providers.__base.moderation_model import ModerationModel +from graphon.model_runtime.model_providers.base.moderation_model import ModerationModel from models.provider import ProviderType logger = logging.getLogger(__name__) diff --git a/api/core/helper/ssrf_proxy.py b/api/core/helper/ssrf_proxy.py index e38592bb7b..91e92712b7 100644 --- a/api/core/helper/ssrf_proxy.py +++ b/api/core/helper/ssrf_proxy.py @@ -12,6 +12,7 @@ from pydantic import TypeAdapter, ValidationError from configs import dify_config from core.helper.http_client_pooling import get_pooled_http_client from core.tools.errors import ToolSSRFError +from graphon.http.response import HttpResponse logger = logging.getLogger(__name__) @@ -267,4 +268,47 @@ class SSRFProxy: return patch(url=url, max_retries=max_retries, **kwargs) +def _to_graphon_http_response(response: httpx.Response) -> HttpResponse: + """Convert an ``httpx`` response into Graphon's transport-agnostic wrapper.""" + return HttpResponse( + status_code=response.status_code, + headers=dict(response.headers), + content=response.content, + url=str(response.url) if response.url else None, + reason_phrase=response.reason_phrase, + fallback_text=response.text, + ) + + +class GraphonSSRFProxy: + """Adapter exposing SSRF helpers behind Graphon's ``HttpClientProtocol``.""" + + @property + def max_retries_exceeded_error(self) -> type[Exception]: + return max_retries_exceeded_error + + @property + def request_error(self) -> type[Exception]: + return request_error + + def get(self, url: str, max_retries: int = SSRF_DEFAULT_MAX_RETRIES, **kwargs: Any) -> HttpResponse: + return _to_graphon_http_response(get(url=url, max_retries=max_retries, **kwargs)) + + def head(self, url: str, max_retries: int = SSRF_DEFAULT_MAX_RETRIES, **kwargs: Any) -> HttpResponse: + return _to_graphon_http_response(head(url=url, max_retries=max_retries, **kwargs)) + + def post(self, url: str, max_retries: int = SSRF_DEFAULT_MAX_RETRIES, **kwargs: Any) -> HttpResponse: + return _to_graphon_http_response(post(url=url, max_retries=max_retries, **kwargs)) + + def put(self, url: str, max_retries: int = SSRF_DEFAULT_MAX_RETRIES, **kwargs: Any) -> HttpResponse: + return _to_graphon_http_response(put(url=url, max_retries=max_retries, **kwargs)) + + def delete(self, url: str, max_retries: int = SSRF_DEFAULT_MAX_RETRIES, **kwargs: Any) -> HttpResponse: + return _to_graphon_http_response(delete(url=url, max_retries=max_retries, **kwargs)) + + def patch(self, url: str, max_retries: int = SSRF_DEFAULT_MAX_RETRIES, **kwargs: Any) -> HttpResponse: + return _to_graphon_http_response(patch(url=url, max_retries=max_retries, **kwargs)) + + ssrf_proxy = SSRFProxy() +graphon_ssrf_proxy = GraphonSSRFProxy() diff --git a/api/core/model_manager.py b/api/core/model_manager.py index d8d8dfedd8..86d0e3baaa 100644 --- a/api/core/model_manager.py +++ b/api/core/model_manager.py @@ -1,6 +1,6 @@ import logging from collections.abc import Callable, Generator, Iterable, Mapping, Sequence -from typing import IO, Any, Literal, Optional, Union, cast, overload +from typing import IO, Any, Literal, Optional, ParamSpec, TypeVar, Union, cast, overload from configs import dify_config from core.entities import PluginCredentialType @@ -18,15 +18,17 @@ from graphon.model_runtime.entities.model_entities import AIModelEntity, ModelFe from graphon.model_runtime.entities.rerank_entities import MultimodalRerankInput, RerankResult from graphon.model_runtime.entities.text_embedding_entities import EmbeddingResult from graphon.model_runtime.errors.invoke import InvokeAuthorizationError, InvokeConnectionError, InvokeRateLimitError -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from graphon.model_runtime.model_providers.__base.moderation_model import ModerationModel -from graphon.model_runtime.model_providers.__base.rerank_model import RerankModel -from graphon.model_runtime.model_providers.__base.speech2text_model import Speech2TextModel -from graphon.model_runtime.model_providers.__base.text_embedding_model import TextEmbeddingModel -from graphon.model_runtime.model_providers.__base.tts_model import TTSModel +from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel +from graphon.model_runtime.model_providers.base.moderation_model import ModerationModel +from graphon.model_runtime.model_providers.base.rerank_model import RerankModel +from graphon.model_runtime.model_providers.base.speech2text_model import Speech2TextModel +from graphon.model_runtime.model_providers.base.text_embedding_model import TextEmbeddingModel +from graphon.model_runtime.model_providers.base.tts_model import TTSModel from models.provider import ProviderType logger = logging.getLogger(__name__) +P = ParamSpec("P") +R = TypeVar("R") class ModelInstance: @@ -168,7 +170,7 @@ class ModelInstance: return cast( Union[LLMResult, Generator], self._round_robin_invoke( - function=self.model_type_instance.invoke, + self.model_type_instance.invoke, model=self.model_name, credentials=self.credentials, prompt_messages=list(prompt_messages), @@ -193,7 +195,7 @@ class ModelInstance: if not isinstance(self.model_type_instance, LargeLanguageModel): raise Exception("Model type instance is not LargeLanguageModel") return self._round_robin_invoke( - function=self.model_type_instance.get_num_tokens, + self.model_type_instance.get_num_tokens, model=self.model_name, credentials=self.credentials, prompt_messages=list(prompt_messages), @@ -213,7 +215,7 @@ class ModelInstance: if not isinstance(self.model_type_instance, TextEmbeddingModel): raise Exception("Model type instance is not TextEmbeddingModel") return self._round_robin_invoke( - function=self.model_type_instance.invoke, + self.model_type_instance.invoke, model=self.model_name, credentials=self.credentials, texts=texts, @@ -235,7 +237,7 @@ class ModelInstance: if not isinstance(self.model_type_instance, TextEmbeddingModel): raise Exception("Model type instance is not TextEmbeddingModel") return self._round_robin_invoke( - function=self.model_type_instance.invoke, + self.model_type_instance.invoke, model=self.model_name, credentials=self.credentials, multimodel_documents=multimodel_documents, @@ -252,7 +254,7 @@ class ModelInstance: if not isinstance(self.model_type_instance, TextEmbeddingModel): raise Exception("Model type instance is not TextEmbeddingModel") return self._round_robin_invoke( - function=self.model_type_instance.get_num_tokens, + self.model_type_instance.get_num_tokens, model=self.model_name, credentials=self.credentials, texts=texts, @@ -277,7 +279,7 @@ class ModelInstance: if not isinstance(self.model_type_instance, RerankModel): raise Exception("Model type instance is not RerankModel") return self._round_robin_invoke( - function=self.model_type_instance.invoke, + self.model_type_instance.invoke, model=self.model_name, credentials=self.credentials, query=query, @@ -305,7 +307,7 @@ class ModelInstance: if not isinstance(self.model_type_instance, RerankModel): raise Exception("Model type instance is not RerankModel") return self._round_robin_invoke( - function=self.model_type_instance.invoke_multimodal_rerank, + self.model_type_instance.invoke_multimodal_rerank, model=self.model_name, credentials=self.credentials, query=query, @@ -324,7 +326,7 @@ class ModelInstance: if not isinstance(self.model_type_instance, ModerationModel): raise Exception("Model type instance is not ModerationModel") return self._round_robin_invoke( - function=self.model_type_instance.invoke, + self.model_type_instance.invoke, model=self.model_name, credentials=self.credentials, text=text, @@ -340,7 +342,7 @@ class ModelInstance: if not isinstance(self.model_type_instance, Speech2TextModel): raise Exception("Model type instance is not Speech2TextModel") return self._round_robin_invoke( - function=self.model_type_instance.invoke, + self.model_type_instance.invoke, model=self.model_name, credentials=self.credentials, file=file, @@ -357,14 +359,14 @@ class ModelInstance: if not isinstance(self.model_type_instance, TTSModel): raise Exception("Model type instance is not TTSModel") return self._round_robin_invoke( - function=self.model_type_instance.invoke, + self.model_type_instance.invoke, model=self.model_name, credentials=self.credentials, content_text=content_text, voice=voice, ) - def _round_robin_invoke[**P, R](self, function: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R: + def _round_robin_invoke(self, function: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R: """ Round-robin invoke :param function: function to invoke diff --git a/api/core/plugin/impl/model_runtime.py b/api/core/plugin/impl/model_runtime.py index e3fba4ef3a..4e66d58b5e 100644 --- a/api/core/plugin/impl/model_runtime.py +++ b/api/core/plugin/impl/model_runtime.py @@ -66,15 +66,15 @@ class PluginModelRuntime(ModelRuntime): if not provider_schema.icon_small: raise ValueError(f"Provider {provider} does not have small icon.") file_name = ( - provider_schema.icon_small.zh_Hans if lang.lower() == "zh_hans" else provider_schema.icon_small.en_US + provider_schema.icon_small.zh_hans if lang.lower() == "zh_hans" else provider_schema.icon_small.en_us ) elif icon_type.lower() == "icon_small_dark": if not provider_schema.icon_small_dark: raise ValueError(f"Provider {provider} does not have small dark icon.") file_name = ( - provider_schema.icon_small_dark.zh_Hans + provider_schema.icon_small_dark.zh_hans if lang.lower() == "zh_hans" - else provider_schema.icon_small_dark.en_US + else provider_schema.icon_small_dark.en_us ) else: raise ValueError(f"Unsupported icon type: {icon_type}.") diff --git a/api/core/prompt/agent_history_prompt_transform.py b/api/core/prompt/agent_history_prompt_transform.py index 8f1d51f08a..7c6280fe93 100644 --- a/api/core/prompt/agent_history_prompt_transform.py +++ b/api/core/prompt/agent_history_prompt_transform.py @@ -10,7 +10,7 @@ from graphon.model_runtime.entities.message_entities import ( SystemPromptMessage, UserPromptMessage, ) -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel class AgentHistoryPromptTransform(PromptTransform): diff --git a/api/core/rag/embedding/cached_embedding.py b/api/core/rag/embedding/cached_embedding.py index 4926f44f16..a9995778f7 100644 --- a/api/core/rag/embedding/cached_embedding.py +++ b/api/core/rag/embedding/cached_embedding.py @@ -14,7 +14,7 @@ from core.rag.embedding.embedding_base import Embeddings from extensions.ext_database import db from extensions.ext_redis import redis_client from graphon.model_runtime.entities.model_entities import ModelPropertyKey -from graphon.model_runtime.model_providers.__base.text_embedding_model import TextEmbeddingModel +from graphon.model_runtime.model_providers.base.text_embedding_model import TextEmbeddingModel from libs import helper from models.dataset import Embedding diff --git a/api/core/rag/extractor/word_extractor.py b/api/core/rag/extractor/word_extractor.py index 052fca930d..0330a43b28 100644 --- a/api/core/rag/extractor/word_extractor.py +++ b/api/core/rag/extractor/word_extractor.py @@ -3,6 +3,7 @@ Supports local file paths and remote URLs (downloaded via `core.helper.ssrf_proxy`). """ +import inspect import logging import mimetypes import os @@ -36,8 +37,11 @@ class WordExtractor(BaseExtractor): file_path: Path to the file to load. """ + _closed: bool + def __init__(self, file_path: str, tenant_id: str, user_id: str): """Initialize with file path.""" + self._closed = False self.file_path = file_path self.tenant_id = tenant_id self.user_id = user_id @@ -65,9 +69,27 @@ class WordExtractor(BaseExtractor): elif not os.path.isfile(self.file_path): raise ValueError(f"File path {self.file_path} is not a valid file or url") + def close(self) -> None: + """Best-effort cleanup for downloaded temporary files.""" + if getattr(self, "_closed", False): + return + + self._closed = True + temp_file = getattr(self, "temp_file", None) + if temp_file is None: + return + + try: + close_result = temp_file.close() + if inspect.isawaitable(close_result): + close_awaitable = getattr(close_result, "close", None) + if callable(close_awaitable): + close_awaitable() + except Exception: + logger.debug("Failed to cleanup downloaded word temp file", exc_info=True) + def __del__(self): - if hasattr(self, "temp_file"): - self.temp_file.close() + self.close() def extract(self) -> list[Document]: """Load given path as single page.""" diff --git a/api/core/rag/index_processor/processor/paragraph_index_processor.py b/api/core/rag/index_processor/processor/paragraph_index_processor.py index f8242efe31..7ffa9afafd 100644 --- a/api/core/rag/index_processor/processor/paragraph_index_processor.py +++ b/api/core/rag/index_processor/processor/paragraph_index_processor.py @@ -609,11 +609,11 @@ class ParagraphIndexProcessor(BaseIndexProcessor): try: # Create File object directly (similar to DatasetRetrieval) file_obj = File( - id=upload_file.id, + file_id=upload_file.id, filename=upload_file.name, extension="." + upload_file.extension, mime_type=upload_file.mime_type, - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.LOCAL_FILE, remote_url=upload_file.source_url, reference=build_file_reference( diff --git a/api/core/rag/retrieval/dataset_retrieval.py b/api/core/rag/retrieval/dataset_retrieval.py index 1453fe020b..5631b3a921 100644 --- a/api/core/rag/retrieval/dataset_retrieval.py +++ b/api/core/rag/retrieval/dataset_retrieval.py @@ -68,7 +68,7 @@ from graphon.file import File, FileTransferMethod, FileType from graphon.model_runtime.entities.llm_entities import LLMMode, LLMResult, LLMUsage from graphon.model_runtime.entities.message_entities import PromptMessage, PromptMessageRole, PromptMessageTool from graphon.model_runtime.entities.model_entities import ModelFeature, ModelType -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel from libs.helper import parse_uuid_str_or_none from libs.json_in_md_parser import parse_and_check_json_markdown from models import UploadFile @@ -517,11 +517,11 @@ class DatasetRetrieval: if attachments_with_bindings: for _, upload_file in attachments_with_bindings: attachment_info = File( - id=upload_file.id, + file_id=upload_file.id, filename=upload_file.name, extension="." + upload_file.extension, mime_type=upload_file.mime_type, - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.LOCAL_FILE, remote_url=upload_file.source_url, reference=build_file_reference( diff --git a/api/core/rag/splitter/fixed_text_splitter.py b/api/core/rag/splitter/fixed_text_splitter.py index 2581c354dd..66b375dad1 100644 --- a/api/core/rag/splitter/fixed_text_splitter.py +++ b/api/core/rag/splitter/fixed_text_splitter.py @@ -9,7 +9,7 @@ from typing import Any, Literal from core.model_manager import ModelInstance from core.rag.splitter.text_splitter import RecursiveCharacterTextSplitter -from graphon.model_runtime.model_providers.__base.tokenizers.gpt2_tokenizer import GPT2Tokenizer +from graphon.model_runtime.model_providers.base.tokenizers.gpt2_tokenizer import GPT2Tokenizer class EnhanceRecursiveCharacterTextSplitter(RecursiveCharacterTextSplitter): diff --git a/api/core/repositories/human_input_repository.py b/api/core/repositories/human_input_repository.py index 02625e242f..740d727e26 100644 --- a/api/core/repositories/human_input_repository.py +++ b/api/core/repositories/human_input_repository.py @@ -8,7 +8,7 @@ from sqlalchemy import select from sqlalchemy.orm import Session, selectinload from core.db.session_factory import session_factory -from core.workflow.human_input_compat import ( +from core.workflow.human_input_adapter import ( BoundRecipient, DeliveryChannelConfig, EmailDeliveryMethod, diff --git a/api/core/tools/tool_file_manager.py b/api/core/tools/tool_file_manager.py index b3424cd9a5..c87e8a3ae0 100644 --- a/api/core/tools/tool_file_manager.py +++ b/api/core/tools/tool_file_manager.py @@ -28,7 +28,7 @@ class ToolFileManager: def _build_graph_file_reference(tool_file: ToolFile) -> File: extension = guess_extension(tool_file.mimetype) or ".bin" return File( - type=get_file_type_by_mime_type(tool_file.mimetype), + file_type=get_file_type_by_mime_type(tool_file.mimetype), transfer_method=FileTransferMethod.TOOL_FILE, remote_url=tool_file.original_url, reference=build_file_reference(record_id=str(tool_file.id)), diff --git a/api/core/tools/tool_manager.py b/api/core/tools/tool_manager.py index f4588904d3..87cf6d7085 100644 --- a/api/core/tools/tool_manager.py +++ b/api/core/tools/tool_manager.py @@ -1082,7 +1082,12 @@ class ToolManager: continue tool_input = ToolNodeData.ToolInput.model_validate(tool_configurations.get(parameter.name, {})) if tool_input.type == "variable": - variable = variable_pool.get(tool_input.value) + variable_selector = tool_input.value + if not isinstance(variable_selector, list) or not all( + isinstance(selector_part, str) for selector_part in variable_selector + ): + raise ToolParameterError("Variable tool input must be a variable selector") + variable = variable_pool.get(variable_selector) if variable is None: raise ToolParameterError(f"Variable {tool_input.value} does not exist") parameter_value = variable.value diff --git a/api/core/tools/utils/model_invocation_utils.py b/api/core/tools/utils/model_invocation_utils.py index 9e1d41cb39..a3623d4ecd 100644 --- a/api/core/tools/utils/model_invocation_utils.py +++ b/api/core/tools/utils/model_invocation_utils.py @@ -21,7 +21,7 @@ from graphon.model_runtime.errors.invoke import ( InvokeRateLimitError, InvokeServerUnavailableError, ) -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel from graphon.model_runtime.utils.encoders import jsonable_encoder from models.tools import ToolModelInvoke diff --git a/api/core/tools/workflow_as_tool/tool.py b/api/core/tools/workflow_as_tool/tool.py index 52ab605963..cd8c6352b5 100644 --- a/api/core/tools/workflow_as_tool/tool.py +++ b/api/core/tools/workflow_as_tool/tool.py @@ -357,7 +357,10 @@ class WorkflowTool(Tool): def _update_file_mapping(self, file_dict: dict[str, Any]) -> dict[str, Any]: file_id = resolve_file_record_id(file_dict.get("reference") or file_dict.get("related_id")) - transfer_method = FileTransferMethod.value_of(file_dict.get("transfer_method")) + transfer_method_value = file_dict.get("transfer_method") + if not isinstance(transfer_method_value, str): + raise ValueError("Workflow file mapping is missing a valid transfer_method") + transfer_method = FileTransferMethod.value_of(transfer_method_value) match transfer_method: case FileTransferMethod.TOOL_FILE: file_dict["tool_file_id"] = file_id diff --git a/api/core/workflow/human_input_compat.py b/api/core/workflow/human_input_adapter.py similarity index 74% rename from api/core/workflow/human_input_compat.py rename to api/core/workflow/human_input_adapter.py index 75a0a0c202..4b765e6aea 100644 --- a/api/core/workflow/human_input_compat.py +++ b/api/core/workflow/human_input_adapter.py @@ -1,8 +1,8 @@ -"""Workflow-layer adapters for legacy human-input payload keys. +"""Workflow-to-Graphon adapters for persisted node payloads. -Stored workflow graphs and editor payloads may still use Dify-specific human -input recipient keys. Normalize them here before handing configs to -`graphon` so graph-owned models only see graph-neutral field names. +Stored workflow graphs and editor payloads still contain a small set of +Dify-owned field spellings and value shapes. Adapt them here before handing the +payload to Graphon so Graphon-owned models only see current contracts. """ from __future__ import annotations @@ -185,7 +185,7 @@ def _copy_mapping(value: object) -> dict[str, Any] | None: return None -def normalize_human_input_node_data_for_graph(node_data: Mapping[str, Any] | BaseModel) -> dict[str, Any]: +def adapt_human_input_node_data_for_graph(node_data: Mapping[str, Any] | BaseModel) -> dict[str, Any]: normalized = _copy_mapping(node_data) if normalized is None: raise TypeError(f"human-input node data must be a mapping, got {type(node_data).__name__}") @@ -215,7 +215,7 @@ def normalize_human_input_node_data_for_graph(node_data: Mapping[str, Any] | Bas def parse_human_input_delivery_methods(node_data: Mapping[str, Any] | BaseModel) -> list[DeliveryChannelConfig]: - normalized = normalize_human_input_node_data_for_graph(node_data) + normalized = adapt_human_input_node_data_for_graph(node_data) raw_delivery_methods = normalized.get("delivery_methods") if not isinstance(raw_delivery_methods, list): return [] @@ -229,17 +229,20 @@ def is_human_input_webapp_enabled(node_data: Mapping[str, Any] | BaseModel) -> b return False -def normalize_node_data_for_graph(node_data: Mapping[str, Any] | BaseModel) -> dict[str, Any]: +def adapt_node_data_for_graph(node_data: Mapping[str, Any] | BaseModel) -> dict[str, Any]: normalized = _copy_mapping(node_data) if normalized is None: raise TypeError(f"node data must be a mapping, got {type(node_data).__name__}") - if normalized.get("type") != BuiltinNodeTypes.HUMAN_INPUT: - return normalized - return normalize_human_input_node_data_for_graph(normalized) + node_type = normalized.get("type") + if node_type == BuiltinNodeTypes.HUMAN_INPUT: + return adapt_human_input_node_data_for_graph(normalized) + if node_type == BuiltinNodeTypes.TOOL: + return _adapt_tool_node_data_for_graph(normalized) + return normalized -def normalize_node_config_for_graph(node_config: Mapping[str, Any] | BaseModel) -> dict[str, Any]: +def adapt_node_config_for_graph(node_config: Mapping[str, Any] | BaseModel) -> dict[str, Any]: normalized = _copy_mapping(node_config) if normalized is None: raise TypeError(f"node config must be a mapping, got {type(node_config).__name__}") @@ -248,10 +251,65 @@ def normalize_node_config_for_graph(node_config: Mapping[str, Any] | BaseModel) if data_mapping is None: return normalized - normalized["data"] = normalize_node_data_for_graph(data_mapping) + normalized["data"] = adapt_node_data_for_graph(data_mapping) return normalized +def _adapt_tool_node_data_for_graph(node_data: Mapping[str, Any]) -> dict[str, Any]: + normalized = dict(node_data) + + raw_tool_configurations = normalized.get("tool_configurations") + if not isinstance(raw_tool_configurations, Mapping): + return normalized + + existing_tool_parameters = normalized.get("tool_parameters") + normalized_tool_parameters = dict(existing_tool_parameters) if isinstance(existing_tool_parameters, Mapping) else {} + normalized_tool_configurations: dict[str, Any] = {} + found_legacy_tool_inputs = False + + for name, value in raw_tool_configurations.items(): + if not isinstance(value, Mapping): + normalized_tool_configurations[name] = value + continue + + input_type = value.get("type") + input_value = value.get("value") + if input_type not in {"mixed", "variable", "constant"}: + normalized_tool_configurations[name] = value + continue + + found_legacy_tool_inputs = True + normalized_tool_parameters.setdefault(name, dict(value)) + + flattened_value = _flatten_legacy_tool_configuration_value( + input_type=input_type, + input_value=input_value, + ) + if flattened_value is not None: + normalized_tool_configurations[name] = flattened_value + + if not found_legacy_tool_inputs: + return normalized + + normalized["tool_parameters"] = normalized_tool_parameters + normalized["tool_configurations"] = normalized_tool_configurations + return normalized + + +def _flatten_legacy_tool_configuration_value(*, input_type: Any, input_value: Any) -> str | int | float | bool | None: + if input_type in {"mixed", "constant"} and isinstance(input_value, str | int | float | bool): + return input_value + + if ( + input_type == "variable" + and isinstance(input_value, list) + and all(isinstance(item, str) for item in input_value) + ): + return "{{#" + ".".join(input_value) + "#}}" + + return None + + def _normalize_email_recipients(recipients: Mapping[str, Any]) -> dict[str, Any]: normalized = dict(recipients) @@ -291,9 +349,9 @@ __all__ = [ "MemberRecipient", "WebAppDeliveryMethod", "_WebAppDeliveryConfig", + "adapt_human_input_node_data_for_graph", + "adapt_node_config_for_graph", + "adapt_node_data_for_graph", "is_human_input_webapp_enabled", - "normalize_human_input_node_data_for_graph", - "normalize_node_config_for_graph", - "normalize_node_data_for_graph", "parse_human_input_delivery_methods", ] diff --git a/api/core/workflow/node_factory.py b/api/core/workflow/node_factory.py index 351da3444f..de4eae1b22 100644 --- a/api/core/workflow/node_factory.py +++ b/api/core/workflow/node_factory.py @@ -15,12 +15,12 @@ from core.helper.code_executor.code_executor import ( CodeExecutionError, CodeExecutor, ) -from core.helper.ssrf_proxy import ssrf_proxy +from core.helper.ssrf_proxy import graphon_ssrf_proxy from core.memory.token_buffer_memory import TokenBufferMemory from core.model_manager import ModelInstance from core.prompt.entities.advanced_prompt_entities import MemoryConfig from core.trigger.constants import TRIGGER_NODE_TYPES -from core.workflow.human_input_compat import normalize_node_config_for_graph +from core.workflow.human_input_adapter import adapt_node_config_for_graph from core.workflow.node_runtime import ( DifyFileReferenceFactory, DifyHumanInputNodeRuntime, @@ -46,7 +46,7 @@ from graphon.enums import BuiltinNodeTypes, NodeType from graphon.file.file_manager import file_manager from graphon.graph.graph import NodeFactory from graphon.model_runtime.memory import PromptMessageMemory -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel from graphon.nodes.base.node import Node from graphon.nodes.code.code_node import WorkflowCodeExecutor from graphon.nodes.code.entities import CodeLanguage @@ -121,6 +121,7 @@ def get_node_type_classes_mapping() -> Mapping[NodeType, Mapping[str, type[Node] def resolve_workflow_node_class(*, node_type: NodeType, node_version: str) -> type[Node]: + """Resolve the production node class for the requested type/version.""" node_mapping = get_node_type_classes_mapping().get(node_type) if not node_mapping: raise ValueError(f"No class mapping found for node type: {node_type}") @@ -297,7 +298,7 @@ class DifyNodeFactory(NodeFactory): ) self._jinja2_template_renderer = CodeExecutorJinja2TemplateRenderer() self._template_transform_max_output_length = dify_config.TEMPLATE_TRANSFORM_MAX_LENGTH - self._http_request_http_client = ssrf_proxy + self._http_request_http_client = graphon_ssrf_proxy self._bound_tool_file_manager_factory = lambda: DifyToolFileManager( self._dify_context, conversation_id_getter=self._conversation_id, @@ -364,10 +365,14 @@ class DifyNodeFactory(NodeFactory): (including pydantic ValidationError, which subclasses ValueError), if node type is unknown, or if no implementation exists for the resolved version """ - typed_node_config = NodeConfigDictAdapter.validate_python(normalize_node_config_for_graph(node_config)) + typed_node_config = NodeConfigDictAdapter.validate_python(adapt_node_config_for_graph(node_config)) node_id = typed_node_config["id"] node_data = typed_node_config["data"] node_class = self._resolve_node_class(node_type=node_data.type, node_version=str(node_data.version)) + # Graph configs are initially validated against permissive shared node data. + # Re-validate using the resolved node class so workflow-local node schemas + # stay explicit and constructors receive the concrete typed payload. + resolved_node_data = self._validate_resolved_node_data(node_class, node_data) node_type = node_data.type node_init_kwargs_factories: Mapping[NodeType, Callable[[], dict[str, object]]] = { BuiltinNodeTypes.CODE: lambda: { @@ -391,7 +396,7 @@ class DifyNodeFactory(NodeFactory): }, BuiltinNodeTypes.LLM: lambda: self._build_llm_compatible_node_init_kwargs( node_class=node_class, - node_data=node_data, + node_data=resolved_node_data, wrap_model_instance=True, include_http_client=True, include_llm_file_saver=True, @@ -405,7 +410,7 @@ class DifyNodeFactory(NodeFactory): }, BuiltinNodeTypes.QUESTION_CLASSIFIER: lambda: self._build_llm_compatible_node_init_kwargs( node_class=node_class, - node_data=node_data, + node_data=resolved_node_data, wrap_model_instance=True, include_http_client=True, include_llm_file_saver=True, @@ -415,7 +420,7 @@ class DifyNodeFactory(NodeFactory): ), BuiltinNodeTypes.PARAMETER_EXTRACTOR: lambda: self._build_llm_compatible_node_init_kwargs( node_class=node_class, - node_data=node_data, + node_data=resolved_node_data, wrap_model_instance=True, include_http_client=False, include_llm_file_saver=False, @@ -436,8 +441,8 @@ class DifyNodeFactory(NodeFactory): } node_init_kwargs = node_init_kwargs_factories.get(node_type, lambda: {})() return node_class( - id=node_id, - config=typed_node_config, + node_id=node_id, + config=resolved_node_data, graph_init_params=self.graph_init_params, graph_runtime_state=self.graph_runtime_state, **node_init_kwargs, @@ -448,7 +453,10 @@ class DifyNodeFactory(NodeFactory): """ Re-validate the permissive graph payload with the concrete NodeData model declared by the resolved node class. """ - return node_class.validate_node_data(node_data) + validate_node_data = getattr(node_class, "validate_node_data", None) + if callable(validate_node_data): + return cast("BaseNodeData", validate_node_data(node_data)) + return node_data @staticmethod def _resolve_node_class(*, node_type: NodeType, node_version: str) -> type[Node]: diff --git a/api/core/workflow/node_runtime.py b/api/core/workflow/node_runtime.py index 2e632e56f0..b8725853c4 100644 --- a/api/core/workflow/node_runtime.py +++ b/api/core/workflow/node_runtime.py @@ -2,7 +2,7 @@ from __future__ import annotations from collections.abc import Callable, Generator, Mapping, Sequence from dataclasses import dataclass -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, Any, Literal, cast, overload from sqlalchemy import select from sqlalchemy.orm import Session @@ -41,7 +41,7 @@ from graphon.model_runtime.entities.llm_entities import ( ) from graphon.model_runtime.entities.message_entities import PromptMessage, PromptMessageTool from graphon.model_runtime.entities.model_entities import AIModelEntity -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel from graphon.nodes.human_input.entities import HumanInputNodeData from graphon.nodes.llm.runtime_protocols import ( PreparedLLMProtocol, @@ -64,7 +64,7 @@ from models.dataset import SegmentAttachmentBinding from models.model import UploadFile from services.tools.builtin_tools_manage_service import BuiltinToolManageService -from .human_input_compat import ( +from .human_input_adapter import ( BoundRecipient, DeliveryChannelConfig, DeliveryMethodType, @@ -173,6 +173,28 @@ class DifyPreparedLLM(PreparedLLMProtocol): def get_llm_num_tokens(self, prompt_messages: Sequence[PromptMessage]) -> int: return self._model_instance.get_llm_num_tokens(prompt_messages) + @overload + def invoke_llm( + self, + *, + prompt_messages: Sequence[PromptMessage], + model_parameters: Mapping[str, Any], + tools: Sequence[PromptMessageTool] | None, + stop: Sequence[str] | None, + stream: Literal[False], + ) -> LLMResult: ... + + @overload + def invoke_llm( + self, + *, + prompt_messages: Sequence[PromptMessage], + model_parameters: Mapping[str, Any], + tools: Sequence[PromptMessageTool] | None, + stop: Sequence[str] | None, + stream: Literal[True], + ) -> Generator[LLMResultChunk, None, None]: ... + def invoke_llm( self, *, @@ -190,6 +212,28 @@ class DifyPreparedLLM(PreparedLLMProtocol): stream=stream, ) + @overload + def invoke_llm_with_structured_output( + self, + *, + prompt_messages: Sequence[PromptMessage], + json_schema: Mapping[str, Any], + model_parameters: Mapping[str, Any], + stop: Sequence[str] | None, + stream: Literal[False], + ) -> LLMResultWithStructuredOutput: ... + + @overload + def invoke_llm_with_structured_output( + self, + *, + prompt_messages: Sequence[PromptMessage], + json_schema: Mapping[str, Any], + model_parameters: Mapping[str, Any], + stop: Sequence[str] | None, + stream: Literal[True], + ) -> Generator[LLMResultChunkWithStructuredOutput, None, None]: ... + def invoke_llm_with_structured_output( self, *, diff --git a/api/core/workflow/nodes/agent/agent_node.py b/api/core/workflow/nodes/agent/agent_node.py index 7b000101b0..68a24e86b1 100644 --- a/api/core/workflow/nodes/agent/agent_node.py +++ b/api/core/workflow/nodes/agent/agent_node.py @@ -5,7 +5,6 @@ from typing import TYPE_CHECKING, Any from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY, DifyRunContext from core.workflow.system_variables import SystemVariableKey, get_system_text -from graphon.entities.graph_config import NodeConfigDict from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionStatus from graphon.node_events import NodeEventBase, NodeRunResult, StreamCompletedEvent from graphon.nodes.base.node import Node @@ -35,18 +34,18 @@ class AgentNode(Node[AgentNodeData]): def __init__( self, - id: str, - config: NodeConfigDict, + node_id: str, + config: AgentNodeData, + *, graph_init_params: GraphInitParams, graph_runtime_state: GraphRuntimeState, - *, strategy_resolver: AgentStrategyResolver, presentation_provider: AgentStrategyPresentationProvider, runtime_support: AgentRuntimeSupport, message_transformer: AgentMessageTransformer, ) -> None: super().__init__( - id=id, + node_id=node_id, config=config, graph_init_params=graph_init_params, graph_runtime_state=graph_runtime_state, diff --git a/api/core/workflow/nodes/datasource/datasource_node.py b/api/core/workflow/nodes/datasource/datasource_node.py index e4f6b3b470..f3006c4242 100644 --- a/api/core/workflow/nodes/datasource/datasource_node.py +++ b/api/core/workflow/nodes/datasource/datasource_node.py @@ -7,7 +7,6 @@ from core.datasource.entities.datasource_entities import DatasourceProviderType from core.plugin.impl.exc import PluginDaemonClientSideError from core.workflow.file_reference import resolve_file_record_id from core.workflow.system_variables import SystemVariableKey, get_system_segment -from graphon.entities.graph_config import NodeConfigDict from graphon.enums import ( BuiltinNodeTypes, NodeExecutionType, @@ -36,13 +35,14 @@ class DatasourceNode(Node[DatasourceNodeData]): def __init__( self, - id: str, - config: NodeConfigDict, + node_id: str, + config: DatasourceNodeData, + *, graph_init_params: "GraphInitParams", graph_runtime_state: "GraphRuntimeState", - ): + ) -> None: super().__init__( - id=id, + node_id=node_id, config=config, graph_init_params=graph_init_params, graph_runtime_state=graph_runtime_state, diff --git a/api/core/workflow/nodes/knowledge_index/knowledge_index_node.py b/api/core/workflow/nodes/knowledge_index/knowledge_index_node.py index d5cab05dbe..9c1b7ab2c4 100644 --- a/api/core/workflow/nodes/knowledge_index/knowledge_index_node.py +++ b/api/core/workflow/nodes/knowledge_index/knowledge_index_node.py @@ -7,7 +7,6 @@ from core.rag.index_processor.index_processor_base import SummaryIndexSettingDic from core.rag.summary_index.summary_index import SummaryIndex from core.workflow.nodes.knowledge_index import KNOWLEDGE_INDEX_NODE_TYPE from core.workflow.system_variables import SystemVariableKey, get_system_segment, get_system_text -from graphon.entities.graph_config import NodeConfigDict from graphon.enums import NodeExecutionType, WorkflowNodeExecutionStatus from graphon.node_events import NodeRunResult from graphon.nodes.base.node import Node @@ -32,12 +31,18 @@ class KnowledgeIndexNode(Node[KnowledgeIndexNodeData]): def __init__( self, - id: str, - config: NodeConfigDict, + node_id: str, + config: KnowledgeIndexNodeData, + *, graph_init_params: "GraphInitParams", graph_runtime_state: "GraphRuntimeState", ) -> None: - super().__init__(id, config, graph_init_params, graph_runtime_state) + super().__init__( + node_id=node_id, + config=config, + graph_init_params=graph_init_params, + graph_runtime_state=graph_runtime_state, + ) self.index_processor = IndexProcessor() self.summary_index_service = SummaryIndex() diff --git a/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py b/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py index 47ad14b499..25f73e446d 100644 --- a/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py +++ b/api/core/workflow/nodes/knowledge_retrieval/knowledge_retrieval_node.py @@ -14,7 +14,6 @@ from core.rag.data_post_processor.data_post_processor import RerankingModelDict, from core.rag.retrieval.dataset_retrieval import DatasetRetrieval from core.workflow.file_reference import parse_file_reference from graphon.entities import GraphInitParams -from graphon.entities.graph_config import NodeConfigDict from graphon.enums import ( BuiltinNodeTypes, WorkflowNodeExecutionMetadataKey, @@ -50,6 +49,18 @@ if TYPE_CHECKING: logger = logging.getLogger(__name__) +def _normalize_metadata_filter_scalar(value: object) -> str | int | float | None: + if value is None or isinstance(value, (str, float)): + return value + if isinstance(value, int) and not isinstance(value, bool): + return value + return str(value) + + +def _normalize_metadata_filter_sequence_item(value: object) -> str: + return value if isinstance(value, str) else str(value) + + class KnowledgeRetrievalNode(LLMUsageTrackingMixin, Node[KnowledgeRetrievalNodeData]): node_type = BuiltinNodeTypes.KNOWLEDGE_RETRIEVAL @@ -59,13 +70,14 @@ class KnowledgeRetrievalNode(LLMUsageTrackingMixin, Node[KnowledgeRetrievalNodeD def __init__( self, - id: str, - config: NodeConfigDict, + node_id: str, + config: KnowledgeRetrievalNodeData, + *, graph_init_params: "GraphInitParams", graph_runtime_state: "GraphRuntimeState", - ): + ) -> None: super().__init__( - id=id, + node_id=node_id, config=config, graph_init_params=graph_init_params, graph_runtime_state=graph_runtime_state, @@ -282,18 +294,21 @@ class KnowledgeRetrievalNode(LLMUsageTrackingMixin, Node[KnowledgeRetrievalNodeD resolved_conditions: list[Condition] = [] for cond in conditions.conditions or []: value = cond.value + resolved_value: str | Sequence[str] | int | float | None if isinstance(value, str): segment_group = variable_pool.convert_template(value) if len(segment_group.value) == 1: - resolved_value = segment_group.value[0].to_object() + resolved_value = _normalize_metadata_filter_scalar(segment_group.value[0].to_object()) else: resolved_value = segment_group.text elif isinstance(value, Sequence) and all(isinstance(v, str) for v in value): - resolved_values = [] - for v in value: # type: ignore + resolved_values: list[str] = [] + for v in value: segment_group = variable_pool.convert_template(v) if len(segment_group.value) == 1: - resolved_values.append(segment_group.value[0].to_object()) + resolved_values.append( + _normalize_metadata_filter_sequence_item(segment_group.value[0].to_object()) + ) else: resolved_values.append(segment_group.text) resolved_value = resolved_values diff --git a/api/factories/file_factory/builders.py b/api/factories/file_factory/builders.py index ce1fa441c2..1d2ad4d445 100644 --- a/api/factories/file_factory/builders.py +++ b/api/factories/file_factory/builders.py @@ -148,11 +148,11 @@ def _build_from_local_file( ) return File( - id=mapping.get("id"), + file_id=mapping.get("id"), filename=row.name, extension="." + row.extension, mime_type=row.mime_type, - type=file_type, + file_type=file_type, transfer_method=transfer_method, remote_url=row.source_url, reference=build_file_reference(record_id=str(row.id)), @@ -196,11 +196,11 @@ def _build_from_remote_url( ) return File( - id=mapping.get("id"), + file_id=mapping.get("id"), filename=upload_file.name, extension="." + upload_file.extension, mime_type=upload_file.mime_type, - type=file_type, + file_type=file_type, transfer_method=transfer_method, remote_url=helpers.get_signed_file_url(upload_file_id=str(upload_file_id)), reference=build_file_reference(record_id=str(upload_file.id)), @@ -222,9 +222,9 @@ def _build_from_remote_url( ) return File( - id=mapping.get("id"), + file_id=mapping.get("id"), filename=filename, - type=file_type, + file_type=file_type, transfer_method=transfer_method, remote_url=url, mime_type=mime_type, @@ -263,9 +263,9 @@ def _build_from_tool_file( ) return File( - id=mapping.get("id"), + file_id=mapping.get("id"), filename=tool_file.name, - type=file_type, + file_type=file_type, transfer_method=transfer_method, remote_url=tool_file.original_url, reference=build_file_reference(record_id=str(tool_file.id)), @@ -306,9 +306,9 @@ def _build_from_datasource_file( ) return File( - id=mapping.get("datasource_file_id"), + file_id=mapping.get("datasource_file_id"), filename=datasource_file.name, - type=file_type, + file_type=file_type, transfer_method=FileTransferMethod.TOOL_FILE, remote_url=datasource_file.source_url, reference=build_file_reference(record_id=str(datasource_file.id)), diff --git a/api/fields/_value_type_serializer.py b/api/fields/_value_type_serializer.py index b5acbbbcb4..d518114777 100644 --- a/api/fields/_value_type_serializer.py +++ b/api/fields/_value_type_serializer.py @@ -10,9 +10,9 @@ class _VarTypedDict(TypedDict, total=False): def serialize_value_type(v: _VarTypedDict | Segment) -> str: if isinstance(v, Segment): - return v.value_type.exposed_type().value + return str(v.value_type.exposed_type()) else: value_type = v.get("value_type") if value_type is None: raise ValueError("value_type is required but not provided") - return value_type.exposed_type().value + return str(value_type.exposed_type()) diff --git a/api/fields/conversation_variable_fields.py b/api/fields/conversation_variable_fields.py index cf4a71d545..e4219ba1ee 100644 --- a/api/fields/conversation_variable_fields.py +++ b/api/fields/conversation_variable_fields.py @@ -57,10 +57,10 @@ class ConversationVariableResponse(ResponseModel): def _normalize_value_type(cls, value: Any) -> str: exposed_type = getattr(value, "exposed_type", None) if callable(exposed_type): - return str(exposed_type().value) + return str(exposed_type()) if isinstance(value, str): try: - return str(SegmentType(value).exposed_type().value) + return str(SegmentType(value).exposed_type()) except ValueError: return value try: diff --git a/api/fields/workflow_fields.py b/api/fields/workflow_fields.py index f9b5e98936..6e947858ba 100644 --- a/api/fields/workflow_fields.py +++ b/api/fields/workflow_fields.py @@ -26,7 +26,7 @@ class EnvironmentVariableField(fields.Raw): "id": value.id, "name": value.name, "value": value.value, - "value_type": value.value_type.exposed_type().value, + "value_type": str(value.value_type.exposed_type()), "description": value.description, } if isinstance(value, dict): diff --git a/api/models/human_input.py b/api/models/human_input.py index b4c7a634b6..7447d3efcb 100644 --- a/api/models/human_input.py +++ b/api/models/human_input.py @@ -6,7 +6,7 @@ import sqlalchemy as sa from pydantic import BaseModel, Field from sqlalchemy.orm import Mapped, mapped_column, relationship -from core.workflow.human_input_compat import DeliveryMethodType +from core.workflow.human_input_adapter import DeliveryMethodType from graphon.nodes.human_input.enums import HumanInputFormKind, HumanInputFormStatus from libs.helper import generate_string diff --git a/api/models/utils/file_input_compat.py b/api/models/utils/file_input_compat.py index a2dc8f6157..77dcbd13d4 100644 --- a/api/models/utils/file_input_compat.py +++ b/api/models/utils/file_input_compat.py @@ -5,7 +5,8 @@ from functools import lru_cache from typing import Any from core.workflow.file_reference import parse_file_reference -from graphon.file import File, FileTransferMethod +from graphon.file import File, FileTransferMethod, FileType +from graphon.file.constants import FILE_MODEL_IDENTITY, maybe_file_object @lru_cache(maxsize=1) @@ -43,6 +44,124 @@ def resolve_file_mapping_tenant_id( return tenant_resolver() +def build_file_from_mapping_without_lookup(*, file_mapping: Mapping[str, Any]) -> File: + """Build a graph `File` directly from serialized metadata.""" + + def _coerce_file_type(value: Any) -> FileType: + if isinstance(value, FileType): + return value + if isinstance(value, str): + return FileType.value_of(value) + raise ValueError("file type is required in file mapping") + + mapping = dict(file_mapping) + transfer_method_value = mapping.get("transfer_method") + if isinstance(transfer_method_value, FileTransferMethod): + transfer_method = transfer_method_value + elif isinstance(transfer_method_value, str): + transfer_method = FileTransferMethod.value_of(transfer_method_value) + else: + raise ValueError("transfer_method is required in file mapping") + + file_id = mapping.get("file_id") + if not isinstance(file_id, str) or not file_id: + legacy_id = mapping.get("id") + file_id = legacy_id if isinstance(legacy_id, str) and legacy_id else None + + related_id = resolve_file_record_id(mapping) + if related_id is None: + raw_related_id = mapping.get("related_id") + related_id = raw_related_id if isinstance(raw_related_id, str) and raw_related_id else None + + remote_url = mapping.get("remote_url") + if not isinstance(remote_url, str) or not remote_url: + url = mapping.get("url") + remote_url = url if isinstance(url, str) and url else None + + reference = mapping.get("reference") + if not isinstance(reference, str) or not reference: + reference = None + + filename = mapping.get("filename") + if not isinstance(filename, str): + filename = None + + extension = mapping.get("extension") + if not isinstance(extension, str): + extension = None + + mime_type = mapping.get("mime_type") + if not isinstance(mime_type, str): + mime_type = None + + size = mapping.get("size", -1) + if not isinstance(size, int): + size = -1 + + storage_key = mapping.get("storage_key") + if not isinstance(storage_key, str): + storage_key = None + + tenant_id = mapping.get("tenant_id") + if not isinstance(tenant_id, str): + tenant_id = None + + dify_model_identity = mapping.get("dify_model_identity") + if not isinstance(dify_model_identity, str): + dify_model_identity = FILE_MODEL_IDENTITY + + tool_file_id = mapping.get("tool_file_id") + if not isinstance(tool_file_id, str): + tool_file_id = None + + upload_file_id = mapping.get("upload_file_id") + if not isinstance(upload_file_id, str): + upload_file_id = None + + datasource_file_id = mapping.get("datasource_file_id") + if not isinstance(datasource_file_id, str): + datasource_file_id = None + + return File( + file_id=file_id, + tenant_id=tenant_id, + file_type=_coerce_file_type(mapping.get("file_type", mapping.get("type"))), + transfer_method=transfer_method, + remote_url=remote_url, + reference=reference, + related_id=related_id, + filename=filename, + extension=extension, + mime_type=mime_type, + size=size, + storage_key=storage_key, + dify_model_identity=dify_model_identity, + url=remote_url, + tool_file_id=tool_file_id, + upload_file_id=upload_file_id, + datasource_file_id=datasource_file_id, + ) + + +def rebuild_serialized_graph_files_without_lookup(value: Any) -> Any: + """Recursively rebuild serialized graph file payloads into `File` objects. + + `graphon` 0.2.2 no longer accepts legacy serialized file mappings via + `model_validate_json()`. Dify keeps this recovery path at the model boundary + so historical JSON blobs remain readable without reintroducing global graph + patches or test-local coercion. + """ + if isinstance(value, list): + return [rebuild_serialized_graph_files_without_lookup(item) for item in value] + + if isinstance(value, dict): + if maybe_file_object(value): + return build_file_from_mapping_without_lookup(file_mapping=value) + return {key: rebuild_serialized_graph_files_without_lookup(item) for key, item in value.items()} + + return value + + def build_file_from_stored_mapping( *, file_mapping: Mapping[str, Any], @@ -76,12 +195,7 @@ def build_file_from_stored_mapping( pass if transfer_method == FileTransferMethod.REMOTE_URL and record_id is None: - remote_url = mapping.get("remote_url") - if not isinstance(remote_url, str) or not remote_url: - url = mapping.get("url") - if isinstance(url, str) and url: - mapping["remote_url"] = url - return File.model_validate(mapping) + return build_file_from_mapping_without_lookup(file_mapping=mapping) return file_factory.build_from_mapping( mapping=mapping, diff --git a/api/models/workflow.py b/api/models/workflow.py index dfda03c2ee..d127244b0f 100644 --- a/api/models/workflow.py +++ b/api/models/workflow.py @@ -24,7 +24,7 @@ from sqlalchemy.orm import Mapped, mapped_column from typing_extensions import deprecated from core.trigger.constants import TRIGGER_PLUGIN_NODE_TYPE -from core.workflow.human_input_compat import normalize_node_config_for_graph +from core.workflow.human_input_adapter import adapt_node_config_for_graph from core.workflow.variable_prefixes import ( CONVERSATION_VARIABLE_NODE_ID, SYSTEM_VARIABLE_NODE_ID, @@ -64,7 +64,10 @@ from .base import Base, DefaultFieldsDCMixin, TypeBase from .engine import db from .enums import CreatorUserRole, DraftVariableType, ExecutionOffLoadType, WorkflowRunTriggeredFrom from .types import EnumText, LongText, StringUUID -from .utils.file_input_compat import build_file_from_stored_mapping +from .utils.file_input_compat import ( + build_file_from_mapping_without_lookup, + build_file_from_stored_mapping, +) logger = logging.getLogger(__name__) @@ -290,7 +293,7 @@ class Workflow(Base): # bug node_config: dict[str, Any] = next(filter(lambda node: node["id"] == node_id, nodes)) except StopIteration: raise NodeNotFoundError(node_id) - return NodeConfigDictAdapter.validate_python(normalize_node_config_for_graph(node_config)) + return NodeConfigDictAdapter.validate_python(adapt_node_config_for_graph(node_config)) @staticmethod def get_node_type_from_node_config(node_config: NodeConfigDict) -> NodeType: @@ -1688,7 +1691,7 @@ class WorkflowDraftVariable(Base): return cast(Any, value) normalized_file = dict(value) normalized_file.pop("tenant_id", None) - return File.model_validate(normalized_file) + return build_file_from_mapping_without_lookup(file_mapping=normalized_file) elif isinstance(value, list) and value: value_list = cast(list[Any], value) first: Any = value_list[0] @@ -1698,7 +1701,7 @@ class WorkflowDraftVariable(Base): for item in value_list: normalized_file = dict(cast(dict[str, Any], item)) normalized_file.pop("tenant_id", None) - file_list.append(File.model_validate(normalized_file)) + file_list.append(build_file_from_mapping_without_lookup(file_mapping=normalized_file)) return cast(Any, file_list) else: return cast(Any, value) diff --git a/api/providers/trace/trace-tencent/src/dify_trace_tencent/tencent_trace.py b/api/providers/trace/trace-tencent/src/dify_trace_tencent/tencent_trace.py index cfcf6b307e..a8c480e4a5 100644 --- a/api/providers/trace/trace-tencent/src/dify_trace_tencent/tencent_trace.py +++ b/api/providers/trace/trace-tencent/src/dify_trace_tencent/tencent_trace.py @@ -1,7 +1,6 @@ -""" -Tencent APM tracing implementation with separated concerns -""" +"""Tencent APM tracing with idempotent client cleanup.""" +import inspect import logging from sqlalchemy import select @@ -38,10 +37,18 @@ class TencentDataTrace(BaseTraceInstance): """ Tencent APM trace implementation with single responsibility principle. Acts as a coordinator that delegates specific tasks to specialized classes. + + The instance owns a long-lived ``TencentTraceClient``. Cleanup may happen + explicitly in tests or implicitly during garbage collection, so shutdown + must be safe to call multiple times. """ + trace_client: TencentTraceClient + _closed: bool + def __init__(self, tencent_config: TencentConfig): super().__init__(tencent_config) + self._closed = False self.trace_client = TencentTraceClient( service_name=tencent_config.service_name, endpoint=tencent_config.endpoint, @@ -513,10 +520,25 @@ class TencentDataTrace(BaseTraceInstance): except Exception: logger.debug("[Tencent APM] Failed to record message trace duration") - def __del__(self): - """Ensure proper cleanup on garbage collection.""" + def close(self) -> None: + """Synchronously and idempotently shutdown the underlying trace client.""" + if getattr(self, "_closed", False): + return + + self._closed = True + trace_client = getattr(self, "trace_client", None) + if trace_client is None: + return + try: - if hasattr(self, "trace_client"): - self.trace_client.shutdown() + shutdown_result = trace_client.shutdown() + if inspect.isawaitable(shutdown_result): + close_awaitable = getattr(shutdown_result, "close", None) + if callable(close_awaitable): + close_awaitable() except Exception: logger.exception("[Tencent APM] Failed to shutdown trace client during cleanup") + + def __del__(self): + """Ensure best-effort cleanup on garbage collection without retrying shutdown.""" + self.close() diff --git a/api/providers/trace/trace-tencent/tests/unit_tests/tencent_trace/test_tencent_trace.py b/api/providers/trace/trace-tencent/tests/unit_tests/tencent_trace/test_tencent_trace.py index a91a0aa558..54524b09ca 100644 --- a/api/providers/trace/trace-tencent/tests/unit_tests/tencent_trace/test_tencent_trace.py +++ b/api/providers/trace/trace-tencent/tests/unit_tests/tencent_trace/test_tencent_trace.py @@ -1,5 +1,7 @@ +import gc import logging -from unittest.mock import MagicMock, patch +import warnings +from unittest.mock import AsyncMock, MagicMock, patch import pytest from dify_trace_tencent.config import TencentConfig @@ -632,13 +634,38 @@ class TestTencentDataTrace: with patch("dify_trace_tencent.tencent_trace.logger.debug") as mock_log: tencent_data_trace._record_message_trace_duration(trace_info) - def test_del(self, tencent_data_trace): + def test_close(self, tencent_data_trace): client = tencent_data_trace.trace_client - tencent_data_trace.__del__() + tencent_data_trace.close() client.shutdown.assert_called_once() - def test_del_exception(self, tencent_data_trace): + def test_close_is_idempotent(self, tencent_data_trace): + client = tencent_data_trace.trace_client + + tencent_data_trace.close() + tencent_data_trace.close() + + client.shutdown.assert_called_once() + + def test_close_exception(self, tencent_data_trace): tencent_data_trace.trace_client.shutdown.side_effect = Exception("error") with patch("dify_trace_tencent.tencent_trace.logger.exception") as mock_log: - tencent_data_trace.__del__() + tencent_data_trace.close() mock_log.assert_called_once_with("[Tencent APM] Failed to shutdown trace client during cleanup") + + def test_close_handles_async_shutdown_mock(self, tencent_data_trace): + shutdown = AsyncMock() + tencent_data_trace.trace_client.shutdown = shutdown + + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + tencent_data_trace.close() + gc.collect() + + shutdown.assert_called_once() + assert not [ + warning + for warning in caught + if issubclass(warning.category, RuntimeWarning) + and "AsyncMockMixin._execute_mock_call" in str(warning.message) + ] diff --git a/api/pyproject.toml b/api/pyproject.toml index 12b8b3d782..8f6ee796ab 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -44,7 +44,7 @@ dependencies = [ # Emerging: newer and fast-moving, use compatible pins "fastopenapi[flask]~=0.7.0", - "graphon~=0.1.2", + "graphon~=0.2.2", "httpx-sse~=0.4.0", "json-repair~=0.59.2", ] diff --git a/api/services/app_service.py b/api/services/app_service.py index afd98e2975..038c59633a 100644 --- a/api/services/app_service.py +++ b/api/services/app_service.py @@ -16,7 +16,7 @@ from core.tools.utils.configuration import ToolParameterConfigurationManager from events.app_event import app_was_created, app_was_deleted, app_was_updated from extensions.ext_database import db from graphon.model_runtime.entities.model_entities import ModelPropertyKey, ModelType -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel from libs.datetime_utils import naive_utc_now from libs.login import current_user from models import Account diff --git a/api/services/dataset_service.py b/api/services/dataset_service.py index e6f5f80a6d..894cb05687 100644 --- a/api/services/dataset_service.py +++ b/api/services/dataset_service.py @@ -30,7 +30,7 @@ from extensions.ext_database import db from extensions.ext_redis import redis_client from graphon.file import helpers as file_helpers from graphon.model_runtime.entities.model_entities import ModelFeature, ModelType -from graphon.model_runtime.model_providers.__base.text_embedding_model import TextEmbeddingModel +from graphon.model_runtime.model_providers.base.text_embedding_model import TextEmbeddingModel from libs import helper from libs.datetime_utils import naive_utc_now from libs.login import current_user diff --git a/api/services/human_input_delivery_test_service.py b/api/services/human_input_delivery_test_service.py index 68ef67dec1..8b4983e5f7 100644 --- a/api/services/human_input_delivery_test_service.py +++ b/api/services/human_input_delivery_test_service.py @@ -8,7 +8,7 @@ from sqlalchemy import Engine, select from sqlalchemy.orm import sessionmaker from configs import dify_config -from core.workflow.human_input_compat import ( +from core.workflow.human_input_adapter import ( DeliveryChannelConfig, EmailDeliveryConfig, EmailDeliveryMethod, diff --git a/api/services/rag_pipeline/rag_pipeline.py b/api/services/rag_pipeline/rag_pipeline.py index 968600d1bc..9db6682e10 100644 --- a/api/services/rag_pipeline/rag_pipeline.py +++ b/api/services/rag_pipeline/rag_pipeline.py @@ -476,7 +476,7 @@ class RagPipelineService: :param filters: filter by node config parameters. :return: """ - node_type_enum = NodeType(node_type) + node_type_enum: NodeType = node_type node_mapping = get_node_type_classes_mapping() # return default block config diff --git a/api/services/variable_truncator.py b/api/services/variable_truncator.py index c96050ce13..1529c2b98f 100644 --- a/api/services/variable_truncator.py +++ b/api/services/variable_truncator.py @@ -169,7 +169,7 @@ class VariableTruncator(BaseTruncator): return TruncationResult(StringSegment(value=fallback_result.value), True) # Apply final fallback - convert to JSON string and truncate - json_str = dumps_with_segments(result.value, ensure_ascii=False) + json_str = dumps_with_segments(result.value) if len(json_str) > self._max_size_bytes: json_str = json_str[: self._max_size_bytes] + "..." return TruncationResult(result=StringSegment(value=json_str), truncated=True) diff --git a/api/services/workflow_draft_variable_service.py b/api/services/workflow_draft_variable_service.py index 5ec00ee336..96f936ff9b 100644 --- a/api/services/workflow_draft_variable_service.py +++ b/api/services/workflow_draft_variable_service.py @@ -146,7 +146,7 @@ class DraftVarLoader(VariableLoader): variable = segment_to_variable( segment=segment, selector=draft_var.get_selector(), - id=draft_var.id, + variable_id=draft_var.id, name=draft_var.name, description=draft_var.description, ) @@ -180,7 +180,7 @@ class DraftVarLoader(VariableLoader): variable = segment_to_variable( segment=segment, selector=draft_var.get_selector(), - id=draft_var.id, + variable_id=draft_var.id, name=draft_var.name, description=draft_var.description, ) @@ -191,7 +191,7 @@ class DraftVarLoader(VariableLoader): variable = segment_to_variable( segment=segment, selector=draft_var.get_selector(), - id=draft_var.id, + variable_id=draft_var.id, name=draft_var.name, description=draft_var.description, ) @@ -1067,7 +1067,7 @@ class DraftVariableSaver: filename = f"{self._generate_filename(name)}.txt" else: # For other types, store as JSON - original_content_serialized = dumps_with_segments(value_seg.value, ensure_ascii=False) + original_content_serialized = dumps_with_segments(value_seg.value) content_type = "application/json" filename = f"{self._generate_filename(name)}.json" diff --git a/api/services/workflow_service.py b/api/services/workflow_service.py index d71223314e..d4b9095ce5 100644 --- a/api/services/workflow_service.py +++ b/api/services/workflow_service.py @@ -18,9 +18,9 @@ from core.plugin.impl.model_runtime_factory import create_plugin_model_assembly, from core.repositories import DifyCoreRepositoryFactory from core.repositories.human_input_repository import FormCreateParams, HumanInputFormRepositoryImpl from core.trigger.constants import is_trigger_node_type -from core.workflow.human_input_compat import ( +from core.workflow.human_input_adapter import ( DeliveryChannelConfig, - normalize_human_input_node_data_for_graph, + adapt_human_input_node_data_for_graph, parse_human_input_delivery_methods, ) from core.workflow.node_factory import ( @@ -791,7 +791,7 @@ class WorkflowService: :param filters: filter by node config parameters. :return: """ - node_type_enum = NodeType(node_type) + node_type_enum: NodeType = node_type node_mapping = get_node_type_classes_mapping() # return default block config @@ -1096,7 +1096,7 @@ class WorkflowService: raise ValueError("Node type must be human-input.") node_data = HumanInputNodeData.model_validate( - normalize_human_input_node_data_for_graph(node_config["data"]), + adapt_human_input_node_data_for_graph(node_config["data"]), from_attributes=True, ) delivery_method = self._resolve_human_input_delivery_method( @@ -1237,9 +1237,10 @@ class WorkflowService: variable_pool=variable_pool, start_at=time.perf_counter(), ) + node_data = HumanInputNode.validate_node_data(adapt_human_input_node_data_for_graph(node_config["data"])) node = HumanInputNode( - id=node_config["id"], - config=node_config, + node_id=node_config["id"], + config=node_data, graph_init_params=graph_init_params, graph_runtime_state=graph_runtime_state, runtime=DifyHumanInputNodeRuntime(run_context), @@ -1529,7 +1530,7 @@ class WorkflowService: from graphon.nodes.human_input.entities import HumanInputNodeData try: - HumanInputNodeData.model_validate(normalize_human_input_node_data_for_graph(node_data)) + HumanInputNodeData.model_validate(adapt_human_input_node_data_for_graph(node_data)) except Exception as e: raise ValueError(f"Invalid HumanInput node data: {str(e)}") diff --git a/api/tasks/mail_human_input_delivery_task.py b/api/tasks/mail_human_input_delivery_task.py index f8ae3f4b6e..2a60be7762 100644 --- a/api/tasks/mail_human_input_delivery_task.py +++ b/api/tasks/mail_human_input_delivery_task.py @@ -11,7 +11,7 @@ from sqlalchemy.orm import Session, sessionmaker from configs import dify_config from core.app.layers.pause_state_persist_layer import WorkflowResumptionContext -from core.workflow.human_input_compat import EmailDeliveryConfig, EmailDeliveryMethod +from core.workflow.human_input_adapter import EmailDeliveryConfig, EmailDeliveryMethod from extensions.ext_database import db from extensions.ext_mail import mail from graphon.runtime import GraphRuntimeState, VariablePool diff --git a/api/tests/integration_tests/core/workflow/nodes/datasource/test_datasource_node_integration.py b/api/tests/integration_tests/core/workflow/nodes/datasource/test_datasource_node_integration.py index b5318aaa2b..2392084c36 100644 --- a/api/tests/integration_tests/core/workflow/nodes/datasource/test_datasource_node_integration.py +++ b/api/tests/integration_tests/core/workflow/nodes/datasource/test_datasource_node_integration.py @@ -1,5 +1,6 @@ from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY from core.workflow.nodes.datasource.datasource_node import DatasourceNode +from core.workflow.nodes.datasource.entities import DatasourceNodeData from graphon.enums import WorkflowNodeExecutionStatus from graphon.node_events import NodeRunResult, StreamCompletedEvent @@ -69,19 +70,16 @@ def test_node_integration_minimal_stream(mocker): mocker.patch("core.workflow.nodes.datasource.datasource_node.DatasourceManager", new=_Mgr) node = DatasourceNode( - id="n", - config={ - "id": "n", - "data": { - "type": "datasource", - "version": "1", - "title": "Datasource", - "provider_type": "plugin", - "provider_name": "p", - "plugin_id": "plug", - "datasource_name": "ds", - }, - }, + node_id="n", + config=DatasourceNodeData( + type="datasource", + version="1", + title="Datasource", + provider_type="plugin", + provider_name="p", + plugin_id="plug", + datasource_name="ds", + ), graph_init_params=_GP(), graph_runtime_state=_GS(vp), ) diff --git a/api/tests/integration_tests/workflow/nodes/test_code.py b/api/tests/integration_tests/workflow/nodes/test_code.py index e3476c292b..aaa6092993 100644 --- a/api/tests/integration_tests/workflow/nodes/test_code.py +++ b/api/tests/integration_tests/workflow/nodes/test_code.py @@ -11,6 +11,7 @@ from graphon.enums import WorkflowNodeExecutionStatus from graphon.graph import Graph from graphon.node_events import NodeRunResult from graphon.nodes.code.code_node import CodeNode +from graphon.nodes.code.entities import CodeNodeData from graphon.nodes.code.limits import CodeNodeLimits from graphon.runtime import GraphRuntimeState, VariablePool from tests.workflow_test_utils import build_test_graph_init_params @@ -64,8 +65,8 @@ def init_code_node(code_config: dict): graph = Graph.init(graph_config=graph_config, node_factory=node_factory, root_node_id="start") node = CodeNode( - id=str(uuid.uuid4()), - config=code_config, + node_id=str(uuid.uuid4()), + config=CodeNodeData.model_validate(code_config["data"]), graph_init_params=init_params, graph_runtime_state=graph_runtime_state, code_executor=node_factory._code_executor, diff --git a/api/tests/integration_tests/workflow/nodes/test_http.py b/api/tests/integration_tests/workflow/nodes/test_http.py index aa6cf1e021..b9f7b9575b 100644 --- a/api/tests/integration_tests/workflow/nodes/test_http.py +++ b/api/tests/integration_tests/workflow/nodes/test_http.py @@ -14,7 +14,7 @@ from core.workflow.system_variables import build_system_variables from graphon.enums import WorkflowNodeExecutionStatus from graphon.file.file_manager import file_manager from graphon.graph import Graph -from graphon.nodes.http_request import HttpRequestNode, HttpRequestNodeConfig +from graphon.nodes.http_request import HttpRequestNode, HttpRequestNodeConfig, HttpRequestNodeData from graphon.runtime import GraphRuntimeState, VariablePool from tests.workflow_test_utils import build_test_graph_init_params @@ -75,8 +75,8 @@ def init_http_node(config: dict): graph = Graph.init(graph_config=graph_config, node_factory=node_factory, root_node_id="start") node = HttpRequestNode( - id=str(uuid.uuid4()), - config=config, + node_id=str(uuid.uuid4()), + config=HttpRequestNodeData.model_validate(config["data"]), graph_init_params=init_params, graph_runtime_state=graph_runtime_state, http_request_config=HTTP_REQUEST_CONFIG, @@ -723,8 +723,8 @@ def test_nested_object_variable_selector(setup_http_mock): graph = Graph.init(graph_config=graph_config, node_factory=node_factory, root_node_id="start") node = HttpRequestNode( - id=str(uuid.uuid4()), - config=graph_config["nodes"][1], + node_id=str(uuid.uuid4()), + config=HttpRequestNodeData.model_validate(graph_config["nodes"][1]["data"]), graph_init_params=init_params, graph_runtime_state=graph_runtime_state, http_request_config=HTTP_REQUEST_CONFIG, diff --git a/api/tests/integration_tests/workflow/nodes/test_llm.py b/api/tests/integration_tests/workflow/nodes/test_llm.py index fa5d63cfbf..3eead70163 100644 --- a/api/tests/integration_tests/workflow/nodes/test_llm.py +++ b/api/tests/integration_tests/workflow/nodes/test_llm.py @@ -11,6 +11,7 @@ from core.workflow.system_variables import build_system_variables from extensions.ext_database import db from graphon.enums import WorkflowNodeExecutionStatus from graphon.node_events import StreamCompletedEvent +from graphon.nodes.llm.entities import LLMNodeData from graphon.nodes.llm.file_saver import LLMFileSaver from graphon.nodes.llm.node import LLMNode from graphon.nodes.llm.protocols import CredentialsProvider, ModelFactory @@ -75,8 +76,8 @@ def init_llm_node(config: dict) -> LLMNode: llm_file_saver = MagicMock(spec=LLMFileSaver) node = LLMNode( - id=str(uuid.uuid4()), - config=config, + node_id=str(uuid.uuid4()), + config=LLMNodeData.model_validate(config["data"]), graph_init_params=init_params, graph_runtime_state=graph_runtime_state, credentials_provider=MagicMock(spec=CredentialsProvider), diff --git a/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py b/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py index 52886855b8..f2eabb86c3 100644 --- a/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py +++ b/api/tests/integration_tests/workflow/nodes/test_parameter_extractor.py @@ -11,6 +11,7 @@ from extensions.ext_database import db from graphon.enums import WorkflowNodeExecutionStatus from graphon.model_runtime.entities import AssistantPromptMessage, UserPromptMessage from graphon.nodes.llm.protocols import CredentialsProvider, ModelFactory +from graphon.nodes.parameter_extractor.entities import ParameterExtractorNodeData from graphon.nodes.parameter_extractor.parameter_extractor_node import ParameterExtractorNode from graphon.runtime import GraphRuntimeState, VariablePool from tests.integration_tests.workflow.nodes.__mock.model import get_mocked_fetch_model_instance @@ -69,8 +70,8 @@ def init_parameter_extractor_node(config: dict, memory=None): graph_runtime_state = GraphRuntimeState(variable_pool=variable_pool, start_at=time.perf_counter()) node = ParameterExtractorNode( - id=str(uuid.uuid4()), - config=config, + node_id=str(uuid.uuid4()), + config=ParameterExtractorNodeData.model_validate(config["data"]), graph_init_params=init_params, graph_runtime_state=graph_runtime_state, credentials_provider=MagicMock(spec=CredentialsProvider), diff --git a/api/tests/integration_tests/workflow/nodes/test_template_transform.py b/api/tests/integration_tests/workflow/nodes/test_template_transform.py index 9e3e1a47e3..e2e0723fb8 100644 --- a/api/tests/integration_tests/workflow/nodes/test_template_transform.py +++ b/api/tests/integration_tests/workflow/nodes/test_template_transform.py @@ -6,6 +6,7 @@ from core.workflow.node_factory import DifyNodeFactory from core.workflow.system_variables import build_system_variables from graphon.enums import WorkflowNodeExecutionStatus from graphon.graph import Graph +from graphon.nodes.template_transform.entities import TemplateTransformNodeData from graphon.nodes.template_transform.template_transform_node import TemplateTransformNode from graphon.runtime import GraphRuntimeState, VariablePool from graphon.template_rendering import TemplateRenderError @@ -86,8 +87,8 @@ def test_execute_template_transform(): assert graph is not None node = TemplateTransformNode( - id=str(uuid.uuid4()), - config=config, + node_id=str(uuid.uuid4()), + config=TemplateTransformNodeData.model_validate(config["data"]), graph_init_params=init_params, graph_runtime_state=graph_runtime_state, jinja2_template_renderer=_SimpleJinja2Renderer(), diff --git a/api/tests/integration_tests/workflow/nodes/test_tool.py b/api/tests/integration_tests/workflow/nodes/test_tool.py index f9ec51ee10..a8e9422c1e 100644 --- a/api/tests/integration_tests/workflow/nodes/test_tool.py +++ b/api/tests/integration_tests/workflow/nodes/test_tool.py @@ -11,6 +11,7 @@ from graphon.enums import WorkflowNodeExecutionStatus from graphon.graph import Graph from graphon.node_events import StreamCompletedEvent from graphon.nodes.protocols import ToolFileManagerProtocol +from graphon.nodes.tool.entities import ToolNodeData from graphon.nodes.tool.tool_node import ToolNode from graphon.runtime import GraphRuntimeState, VariablePool from tests.workflow_test_utils import build_test_graph_init_params @@ -60,8 +61,8 @@ def init_tool_node(config: dict): tool_file_manager_factory = MagicMock(spec=ToolFileManagerProtocol) node = ToolNode( - id=str(uuid.uuid4()), - config=config, + node_id=str(uuid.uuid4()), + config=ToolNodeData.model_validate(config["data"]), graph_init_params=init_params, graph_runtime_state=graph_runtime_state, tool_file_manager_factory=tool_file_manager_factory, diff --git a/api/tests/test_containers_integration_tests/core/repositories/test_human_input_form_repository_impl.py b/api/tests/test_containers_integration_tests/core/repositories/test_human_input_form_repository_impl.py index 14d5740072..6524d6ce61 100644 --- a/api/tests/test_containers_integration_tests/core/repositories/test_human_input_form_repository_impl.py +++ b/api/tests/test_containers_integration_tests/core/repositories/test_human_input_form_repository_impl.py @@ -8,7 +8,7 @@ from sqlalchemy import Engine, select from sqlalchemy.orm import Session from core.repositories.human_input_repository import FormCreateParams, HumanInputFormRepositoryImpl -from core.workflow.human_input_compat import ( +from core.workflow.human_input_adapter import ( DeliveryChannelConfig, EmailDeliveryConfig, EmailDeliveryMethod, diff --git a/api/tests/test_containers_integration_tests/core/workflow/test_human_input_resume_node_execution.py b/api/tests/test_containers_integration_tests/core/workflow/test_human_input_resume_node_execution.py index da4f8847d6..5aed230cd4 100644 --- a/api/tests/test_containers_integration_tests/core/workflow/test_human_input_resume_node_execution.py +++ b/api/tests/test_containers_integration_tests/core/workflow/test_human_input_resume_node_execution.py @@ -101,8 +101,8 @@ def _build_graph( start_data = StartNodeData(title="start", variables=[]) start_node = StartNode( - id="start", - config={"id": "start", "data": start_data.model_dump()}, + node_id="start", + config=start_data, graph_init_params=params, graph_runtime_state=runtime_state, ) @@ -116,8 +116,8 @@ def _build_graph( ], ) human_node = HumanInputNode( - id="human", - config={"id": "human", "data": human_data.model_dump()}, + node_id="human", + config=human_data, graph_init_params=params, graph_runtime_state=runtime_state, form_repository=form_repository, @@ -130,8 +130,8 @@ def _build_graph( desc=None, ) end_node = EndNode( - id="end", - config={"id": "end", "data": end_data.model_dump()}, + node_id="end", + config=end_data, graph_init_params=params, graph_runtime_state=runtime_state, ) diff --git a/api/tests/test_containers_integration_tests/factories/test_storage_key_loader.py b/api/tests/test_containers_integration_tests/factories/test_storage_key_loader.py index 2e207ddc67..35e41035df 100644 --- a/api/tests/test_containers_integration_tests/factories/test_storage_key_loader.py +++ b/api/tests/test_containers_integration_tests/factories/test_storage_key_loader.py @@ -123,9 +123,9 @@ class TestStorageKeyLoader(unittest.TestCase): file_related_id = related_id return File( - id=str(uuid4()), # Generate new UUID for File.id + file_id=str(uuid4()), # Generate new UUID for File.id tenant_id=tenant_id, - type=FileType.DOCUMENT, + file_type=FileType.DOCUMENT, transfer_method=transfer_method, related_id=file_related_id, remote_url=remote_url, diff --git a/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_execution_extra_content_repository.py b/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_execution_extra_content_repository.py index aaf9a85d60..54b7afc018 100644 --- a/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_execution_extra_content_repository.py +++ b/api/tests/test_containers_integration_tests/repositories/test_sqlalchemy_execution_extra_content_repository.py @@ -271,7 +271,7 @@ def _create_recipient( def _create_delivery(session: Session, *, form_id: str) -> HumanInputDelivery: - from core.workflow.human_input_compat import DeliveryMethodType + from core.workflow.human_input_adapter import DeliveryMethodType from models.human_input import ConsoleDeliveryPayload delivery = HumanInputDelivery( diff --git a/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test.py b/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test.py index 18c5320d0a..80f9083e81 100644 --- a/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test.py +++ b/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test.py @@ -4,7 +4,7 @@ from unittest.mock import MagicMock import pytest -from core.workflow.human_input_compat import ( +from core.workflow.human_input_adapter import ( EmailDeliveryConfig, EmailDeliveryMethod, EmailRecipients, diff --git a/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test_service.py b/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test_service.py index 21a54e909e..ed75363f3b 100644 --- a/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test_service.py +++ b/api/tests/test_containers_integration_tests/services/test_human_input_delivery_test_service.py @@ -8,7 +8,7 @@ import pytest from sqlalchemy.engine import Engine from configs import dify_config -from core.workflow.human_input_compat import ( +from core.workflow.human_input_adapter import ( EmailDeliveryConfig, EmailDeliveryMethod, EmailRecipients, diff --git a/api/tests/test_containers_integration_tests/tasks/test_mail_human_input_delivery_task.py b/api/tests/test_containers_integration_tests/tasks/test_mail_human_input_delivery_task.py index 328bdbf055..95a867dbb5 100644 --- a/api/tests/test_containers_integration_tests/tasks/test_mail_human_input_delivery_task.py +++ b/api/tests/test_containers_integration_tests/tasks/test_mail_human_input_delivery_task.py @@ -10,7 +10,7 @@ from core.app.app_config.entities import WorkflowUIBasedAppConfig from core.app.entities.app_invoke_entities import InvokeFrom, WorkflowAppGenerateEntity from core.app.layers.pause_state_persist_layer import WorkflowResumptionContext from core.repositories.human_input_repository import FormCreateParams, HumanInputFormRepositoryImpl -from core.workflow.human_input_compat import ( +from core.workflow.human_input_adapter import ( EmailDeliveryConfig, EmailDeliveryMethod, EmailRecipients, diff --git a/api/tests/unit_tests/controllers/console/app/test_workflow.py b/api/tests/unit_tests/controllers/console/app/test_workflow.py index 6ff3b19362..e91c0a0597 100644 --- a/api/tests/unit_tests/controllers/console/app/test_workflow.py +++ b/api/tests/unit_tests/controllers/console/app/test_workflow.py @@ -31,7 +31,7 @@ def test_parse_file_with_config(monkeypatch: pytest.MonkeyPatch) -> None: file_list = [ File( tenant_id="t1", - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.REMOTE_URL, remote_url="http://u", ) diff --git a/api/tests/unit_tests/controllers/console/app/workflow_draft_variables_test.py b/api/tests/unit_tests/controllers/console/app/workflow_draft_variables_test.py index b19a1740eb..22b80b748e 100644 --- a/api/tests/unit_tests/controllers/console/app/workflow_draft_variables_test.py +++ b/api/tests/unit_tests/controllers/console/app/workflow_draft_variables_test.py @@ -314,8 +314,8 @@ def test_workflow_file_variable_with_signed_url(): # Create a File object with LOCAL_FILE transfer method (which generates signed URLs) test_file = File( - id="test_file_id", - type=FileType.IMAGE, + file_id="test_file_id", + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.LOCAL_FILE, related_id="test_upload_file_id", filename="test.jpg", @@ -370,8 +370,8 @@ def test_workflow_file_variable_remote_url(): # Create a File object with REMOTE_URL transfer method test_file = File( - id="test_file_id", - type=FileType.IMAGE, + file_id="test_file_id", + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.REMOTE_URL, remote_url="https://example.com/test.jpg", filename="test.jpg", diff --git a/api/tests/unit_tests/controllers/service_api/app/test_conversation.py b/api/tests/unit_tests/controllers/service_api/app/test_conversation.py index 14c35a9ed5..4fb8ecf784 100644 --- a/api/tests/unit_tests/controllers/service_api/app/test_conversation.py +++ b/api/tests/unit_tests/controllers/service_api/app/test_conversation.py @@ -37,6 +37,8 @@ from controllers.service_api.app.conversation import ( ConversationVariableUpdatePayload, ) from controllers.service_api.app.error import NotChatAppError +from fields._value_type_serializer import serialize_value_type +from graphon.variables import StringSegment from graphon.variables.types import SegmentType from models.model import App, AppMode, EndUser from services.conversation_service import ConversationService @@ -284,6 +286,32 @@ class TestConversationVariableResponseModels: assert response.created_at == int(created_at.timestamp()) assert response.updated_at == int(created_at.timestamp()) + def test_variable_response_normalizes_string_value_type_alias(self): + response = ConversationVariableResponse.model_validate( + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "foo", + "value_type": SegmentType.INTEGER.value, + } + ) + + assert response.value_type == "number" + + def test_variable_response_normalizes_callable_exposed_type(self): + response = ConversationVariableResponse.model_validate( + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "foo", + "value_type": SimpleNamespace(exposed_type=lambda: SegmentType.STRING.exposed_type()), + } + ) + + assert response.value_type == "string" + + def test_serialize_value_type_supports_segments_and_mappings(self): + assert serialize_value_type(StringSegment(value="hello")) == "string" + assert serialize_value_type({"value_type": SegmentType.INTEGER}) == "number" + def test_variable_pagination_response(self): response = ConversationVariableInfiniteScrollPaginationResponse.model_validate( { diff --git a/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter.py b/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter.py index 3ab63aed25..dd6cd0e919 100644 --- a/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter.py +++ b/api/tests/unit_tests/core/app/apps/common/test_workflow_response_converter.py @@ -11,8 +11,8 @@ class TestWorkflowResponseConverterFetchFilesFromVariableValue: def create_test_file(self, file_id: str = "test_file_1") -> File: """Create a test File object""" return File( - id=file_id, - type=FileType.DOCUMENT, + file_id=file_id, + file_type=FileType.DOCUMENT, transfer_method=FileTransferMethod.LOCAL_FILE, related_id="related_123", filename=f"{file_id}.txt", diff --git a/api/tests/unit_tests/core/app/apps/test_pause_resume.py b/api/tests/unit_tests/core/app/apps/test_pause_resume.py index a04a7b7576..6104b8d6ca 100644 --- a/api/tests/unit_tests/core/app/apps/test_pause_resume.py +++ b/api/tests/unit_tests/core/app/apps/test_pause_resume.py @@ -7,11 +7,11 @@ import graphon.nodes.human_input.entities # noqa: F401 from core.app.apps.advanced_chat import app_generator as adv_app_gen_module from core.app.apps.workflow import app_generator as wf_app_gen_module from core.app.entities.app_invoke_entities import InvokeFrom +from core.workflow import node_factory as node_factory_module from core.workflow.node_factory import DifyNodeFactory from core.workflow.system_variables import build_system_variables from graphon.entities import WorkflowStartReason from graphon.entities.base_node_data import BaseNodeData, RetryConfig -from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter from graphon.entities.pause_reason import SchedulingPause from graphon.enums import BuiltinNodeTypes, NodeType, WorkflowNodeExecutionStatus from graphon.graph import Graph @@ -55,8 +55,21 @@ class _StubToolNode(Node[_StubToolNodeData]): def version(cls) -> str: return "1" - def init_node_data(self, data): - self._node_data = _StubToolNodeData.model_validate(data) + def __init__( + self, + node_id: str, + config: _StubToolNodeData, + *, + graph_init_params, + graph_runtime_state, + **_kwargs: Any, + ) -> None: + super().__init__( + node_id=node_id, + config=config, + graph_init_params=graph_init_params, + graph_runtime_state=graph_runtime_state, + ) def _get_error_strategy(self): return self._node_data.error_strategy @@ -89,21 +102,14 @@ class _StubToolNode(Node[_StubToolNodeData]): def _patch_tool_node(mocker): - original_create_node = DifyNodeFactory.create_node + original_resolve_node_class = node_factory_module.resolve_workflow_node_class - def _patched_create_node(self, node_config: dict[str, object] | NodeConfigDict) -> Node: - typed_node_config = NodeConfigDictAdapter.validate_python(node_config) - node_data = typed_node_config["data"] - if node_data.type == BuiltinNodeTypes.TOOL: - return _StubToolNode( - id=str(typed_node_config["id"]), - config=typed_node_config, - graph_init_params=self.graph_init_params, - graph_runtime_state=self.graph_runtime_state, - ) - return original_create_node(self, typed_node_config) + def _patched_resolve_node_class(*, node_type: NodeType, node_version: str) -> type[Node]: + if node_type == BuiltinNodeTypes.TOOL: + return _StubToolNode + return original_resolve_node_class(node_type=node_type, node_version=node_version) - mocker.patch.object(DifyNodeFactory, "create_node", _patched_create_node) + mocker.patch.object(node_factory_module, "resolve_workflow_node_class", side_effect=_patched_resolve_node_class) def _node_data(node_type: NodeType, data: BaseNodeData) -> dict[str, object]: diff --git a/api/tests/unit_tests/core/app/workflow/test_file_runtime.py b/api/tests/unit_tests/core/app/workflow/test_file_runtime.py index cddd03f4b0..701863b927 100644 --- a/api/tests/unit_tests/core/app/workflow/test_file_runtime.py +++ b/api/tests/unit_tests/core/app/workflow/test_file_runtime.py @@ -26,8 +26,8 @@ def _build_file( extension: str | None = None, ) -> File: return File( - id="file-id", - type=FileType.IMAGE, + file_id="file-id", + file_type=FileType.IMAGE, transfer_method=transfer_method, reference=reference, remote_url=remote_url, @@ -351,7 +351,7 @@ def test_runtime_helper_wrappers_delegate_to_config_and_io(monkeypatch: pytest.M assert runtime.multimodal_send_format == "url" - with patch.object(file_runtime.ssrf_proxy, "get", return_value="response") as mock_get: + with patch.object(file_runtime.graphon_ssrf_proxy, "get", return_value="response") as mock_get: assert runtime.http_get("http://example", follow_redirects=False) == "response" mock_get.assert_called_once_with("http://example", follow_redirects=False) diff --git a/api/tests/unit_tests/core/app/workflow/test_node_factory.py b/api/tests/unit_tests/core/app/workflow/test_node_factory.py index c4bfb23272..30a068f4c5 100644 --- a/api/tests/unit_tests/core/app/workflow/test_node_factory.py +++ b/api/tests/unit_tests/core/app/workflow/test_node_factory.py @@ -8,8 +8,8 @@ from graphon.enums import BuiltinNodeTypes class DummyNode: - def __init__(self, *, id, config, graph_init_params, graph_runtime_state, **kwargs): - self.id = id + def __init__(self, *, node_id, config, graph_init_params, graph_runtime_state, **kwargs): + self.id = node_id self.config = config self.graph_init_params = graph_init_params self.graph_runtime_state = graph_runtime_state diff --git a/api/tests/unit_tests/core/datasource/test_datasource_manager.py b/api/tests/unit_tests/core/datasource/test_datasource_manager.py index 81315d2508..deeac49bbc 100644 --- a/api/tests/unit_tests/core/datasource/test_datasource_manager.py +++ b/api/tests/unit_tests/core/datasource/test_datasource_manager.py @@ -430,7 +430,7 @@ def test_stream_node_events_builds_file_and_variables_from_messages(mocker): mocker.patch("core.datasource.datasource_manager.session_factory.create_session", return_value=_Session()) mocker.patch("core.datasource.datasource_manager.get_file_type_by_mime_type", return_value=FileType.IMAGE) built = File( - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.TOOL_FILE, related_id="tool_file_1", extension=".png", @@ -530,7 +530,7 @@ def test_stream_node_events_online_drive_sets_variable_pool_file_and_outputs(moc mocker.patch.object(DatasourceManager, "stream_online_results", return_value=_gen_messages_text_only("ignored")) file_in = File( - type=FileType.DOCUMENT, + file_type=FileType.DOCUMENT, transfer_method=FileTransferMethod.TOOL_FILE, related_id="tf", extension=".pdf", diff --git a/api/tests/unit_tests/core/entities/test_entities_model_entities.py b/api/tests/unit_tests/core/entities/test_entities_model_entities.py index a0b2820157..aeca2e3afd 100644 --- a/api/tests/unit_tests/core/entities/test_entities_model_entities.py +++ b/api/tests/unit_tests/core/entities/test_entities_model_entities.py @@ -46,7 +46,7 @@ def test_simple_model_provider_entity_maps_from_provider_entity() -> None: # Assert assert simple_provider.provider == "openai" - assert simple_provider.label.en_US == "OpenAI" + assert simple_provider.label.en_us == "OpenAI" assert simple_provider.supported_model_types == [ModelType.LLM] diff --git a/api/tests/unit_tests/core/file/test_models.py b/api/tests/unit_tests/core/file/test_models.py index bb6e40e224..8cb0938575 100644 --- a/api/tests/unit_tests/core/file/test_models.py +++ b/api/tests/unit_tests/core/file/test_models.py @@ -3,9 +3,9 @@ from graphon.file import File, FileTransferMethod, FileType def test_file(): file = File( - id="test-file", + file_id="test-file", tenant_id="test-tenant-id", - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.TOOL_FILE, related_id="test-related-id", filename="image.png", @@ -25,27 +25,21 @@ def test_file(): assert file.size == 67 -def test_file_model_validate_accepts_legacy_tenant_id(): - data = { - "id": "test-file", - "tenant_id": "test-tenant-id", - "type": "image", - "transfer_method": "tool_file", - "related_id": "test-related-id", - "filename": "image.png", - "extension": ".png", - "mime_type": "image/png", - "size": 67, - "storage_key": "test-storage-key", - "url": "https://example.com/image.png", - # Extra legacy fields - "tool_file_id": "tool-file-123", - "upload_file_id": "upload-file-456", - "datasource_file_id": "datasource-file-789", - } +def test_file_constructor_accepts_legacy_tenant_id(): + file = File( + file_id="test-file", + tenant_id="test-tenant-id", + file_type=FileType.IMAGE, + transfer_method=FileTransferMethod.TOOL_FILE, + tool_file_id="tool-file-123", + filename="image.png", + extension=".png", + mime_type="image/png", + size=67, + storage_key="test-storage-key", + url="https://example.com/image.png", + ) - file = File.model_validate(data) - - assert file.related_id == "test-related-id" + assert file.related_id == "tool-file-123" assert file.storage_key == "test-storage-key" assert "tenant_id" not in file.model_dump() diff --git a/api/tests/unit_tests/core/helper/test_ssrf_proxy.py b/api/tests/unit_tests/core/helper/test_ssrf_proxy.py index 3b5c5e6597..d9fed9ae2a 100644 --- a/api/tests/unit_tests/core/helper/test_ssrf_proxy.py +++ b/api/tests/unit_tests/core/helper/test_ssrf_proxy.py @@ -1,11 +1,17 @@ from unittest.mock import MagicMock, patch +import httpx import pytest from core.helper.ssrf_proxy import ( SSRF_DEFAULT_MAX_RETRIES, + SSRFProxy, _get_user_provided_host_header, + _to_graphon_http_response, + graphon_ssrf_proxy, make_request, + max_retries_exceeded_error, + request_error, ) @@ -174,3 +180,56 @@ class TestFollowRedirectsParameter: call_kwargs = mock_client.request.call_args.kwargs assert call_kwargs.get("follow_redirects") is True + + +def test_to_graphon_http_response_preserves_httpx_response_fields() -> None: + response = httpx.Response( + 201, + headers={"X-Test": "1"}, + content=b"payload", + request=httpx.Request("GET", "https://example.com/resource"), + ) + + wrapped = _to_graphon_http_response(response) + + assert wrapped.status_code == 201 + assert wrapped.headers == {"x-test": "1", "content-length": "7"} + assert wrapped.content == b"payload" + assert wrapped.url == "https://example.com/resource" + assert wrapped.reason_phrase == "Created" + assert wrapped.text == "payload" + + +def test_ssrf_proxy_exposes_expected_error_types() -> None: + proxy = SSRFProxy() + + assert proxy.max_retries_exceeded_error is max_retries_exceeded_error + assert proxy.request_error is request_error + assert graphon_ssrf_proxy.max_retries_exceeded_error is max_retries_exceeded_error + assert graphon_ssrf_proxy.request_error is request_error + + +@pytest.mark.parametrize("method_name", ["get", "head", "post", "put", "delete", "patch"]) +def test_graphon_ssrf_proxy_wraps_module_requests(method_name: str) -> None: + response = httpx.Response( + 200, + headers={"X-Test": "1"}, + content=b"ok", + request=httpx.Request("GET", "https://example.com/resource"), + ) + + with patch(f"core.helper.ssrf_proxy.{method_name}", return_value=response) as mock_method: + wrapped = getattr(graphon_ssrf_proxy, method_name)( + "https://example.com/resource", + max_retries=3, + headers={"X-Test": "1"}, + ) + + mock_method.assert_called_once_with( + url="https://example.com/resource", + max_retries=3, + headers={"X-Test": "1"}, + ) + assert wrapped.status_code == 200 + assert wrapped.url == "https://example.com/resource" + assert wrapped.content == b"ok" diff --git a/api/tests/unit_tests/core/model_runtime/test_model_provider_factory.py b/api/tests/unit_tests/core/model_runtime/test_model_provider_factory.py index 249ecb5006..c4fd970562 100644 --- a/api/tests/unit_tests/core/model_runtime/test_model_provider_factory.py +++ b/api/tests/unit_tests/core/model_runtime/test_model_provider_factory.py @@ -13,12 +13,12 @@ from graphon.model_runtime.entities.provider_entities import ( ProviderCredentialSchema, ProviderEntity, ) -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel -from graphon.model_runtime.model_providers.__base.moderation_model import ModerationModel -from graphon.model_runtime.model_providers.__base.rerank_model import RerankModel -from graphon.model_runtime.model_providers.__base.speech2text_model import Speech2TextModel -from graphon.model_runtime.model_providers.__base.text_embedding_model import TextEmbeddingModel -from graphon.model_runtime.model_providers.__base.tts_model import TTSModel +from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel +from graphon.model_runtime.model_providers.base.moderation_model import ModerationModel +from graphon.model_runtime.model_providers.base.rerank_model import RerankModel +from graphon.model_runtime.model_providers.base.speech2text_model import Speech2TextModel +from graphon.model_runtime.model_providers.base.text_embedding_model import TextEmbeddingModel +from graphon.model_runtime.model_providers.base.tts_model import TTSModel from graphon.model_runtime.model_providers.model_provider_factory import ModelProviderFactory diff --git a/api/tests/unit_tests/core/plugin/test_model_runtime_adapter.py b/api/tests/unit_tests/core/plugin/test_model_runtime_adapter.py index 68aa130518..88bf555594 100644 --- a/api/tests/unit_tests/core/plugin/test_model_runtime_adapter.py +++ b/api/tests/unit_tests/core/plugin/test_model_runtime_adapter.py @@ -56,7 +56,7 @@ class TestPluginModelRuntime: assert len(providers) == 1 assert providers[0].provider == "langgenius/openai/openai" assert providers[0].provider_name == "openai" - assert providers[0].label.en_US == "OpenAI" + assert providers[0].label.en_us == "OpenAI" client.fetch_model_providers.assert_called_once_with("tenant") def test_fetch_model_providers_only_exposes_short_name_for_canonical_provider(self) -> None: diff --git a/api/tests/unit_tests/core/plugin/utils/test_chunk_merger.py b/api/tests/unit_tests/core/plugin/utils/test_chunk_merger.py index d49b6e4b71..00a4207786 100644 --- a/api/tests/unit_tests/core/plugin/utils/test_chunk_merger.py +++ b/api/tests/unit_tests/core/plugin/utils/test_chunk_merger.py @@ -466,7 +466,7 @@ class TestConverter: def test_convert_parameters_to_plugin_format_with_single_file_and_selector(self): file_param = File( tenant_id="tenant-1", - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.REMOTE_URL, remote_url="https://example.com/file.png", storage_key="", @@ -499,14 +499,14 @@ class TestConverter: def test_convert_parameters_to_plugin_format_with_lists_and_passthrough_values(self): file_one = File( tenant_id="tenant-1", - type=FileType.DOCUMENT, + file_type=FileType.DOCUMENT, transfer_method=FileTransferMethod.REMOTE_URL, remote_url="https://example.com/a.txt", storage_key="", ) file_two = File( tenant_id="tenant-1", - type=FileType.DOCUMENT, + file_type=FileType.DOCUMENT, transfer_method=FileTransferMethod.REMOTE_URL, remote_url="https://example.com/b.txt", storage_key="", diff --git a/api/tests/unit_tests/core/prompt/test_advanced_prompt_transform.py b/api/tests/unit_tests/core/prompt/test_advanced_prompt_transform.py index 395d392127..e536c0831f 100644 --- a/api/tests/unit_tests/core/prompt/test_advanced_prompt_transform.py +++ b/api/tests/unit_tests/core/prompt/test_advanced_prompt_transform.py @@ -134,9 +134,9 @@ def test__get_chat_model_prompt_messages_with_files_no_memory(get_chat_model_arg files = [ File( - id="file1", + file_id="file1", tenant_id="tenant1", - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.REMOTE_URL, remote_url="https://example.com/image1.jpg", storage_key="", @@ -245,9 +245,9 @@ def test_completion_prompt_jinja2_with_files(): completion_template = CompletionModelPromptTemplate(text="Hi {{name}}", edition_type="jinja2") file = File( - id="file1", + file_id="file1", tenant_id="tenant1", - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.REMOTE_URL, remote_url="https://example.com/image.jpg", storage_key="", @@ -379,9 +379,9 @@ def test_chat_prompt_memory_with_files_and_query(): memory = MagicMock(spec=TokenBufferMemory) prompt_template = [ChatModelMessage(text="sys", role=PromptMessageRole.SYSTEM)] file = File( - id="file1", + file_id="file1", tenant_id="tenant1", - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.REMOTE_URL, remote_url="https://example.com/image.jpg", storage_key="", @@ -413,9 +413,9 @@ def test_chat_prompt_files_without_query_updates_last_user_or_appends_new(): transform = AdvancedPromptTransform() model_config_mock = MagicMock(spec=ModelConfigEntity) file = File( - id="file1", + file_id="file1", tenant_id="tenant1", - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.REMOTE_URL, remote_url="https://example.com/image.jpg", storage_key="", @@ -463,9 +463,9 @@ def test_chat_prompt_files_with_query_branch(): transform = AdvancedPromptTransform() model_config_mock = MagicMock(spec=ModelConfigEntity) file = File( - id="file1", + file_id="file1", tenant_id="tenant1", - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.REMOTE_URL, remote_url="https://example.com/image.jpg", storage_key="", diff --git a/api/tests/unit_tests/core/prompt/test_agent_history_prompt_transform.py b/api/tests/unit_tests/core/prompt/test_agent_history_prompt_transform.py index 803afa54d7..28966242d8 100644 --- a/api/tests/unit_tests/core/prompt/test_agent_history_prompt_transform.py +++ b/api/tests/unit_tests/core/prompt/test_agent_history_prompt_transform.py @@ -12,7 +12,7 @@ from graphon.model_runtime.entities.message_entities import ( ToolPromptMessage, UserPromptMessage, ) -from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel from models.model import Conversation diff --git a/api/tests/unit_tests/core/prompt/test_prompt_transform.py b/api/tests/unit_tests/core/prompt/test_prompt_transform.py index 9f9ea33695..5308c8e7b3 100644 --- a/api/tests/unit_tests/core/prompt/test_prompt_transform.py +++ b/api/tests/unit_tests/core/prompt/test_prompt_transform.py @@ -11,7 +11,7 @@ from graphon.model_runtime.entities.model_entities import ModelPropertyKey # from graphon.model_runtime.entities.message_entities import UserPromptMessage # from graphon.model_runtime.entities.model_entities import AIModelEntity, ModelPropertyKey, ParameterRule # from graphon.model_runtime.entities.provider_entities import ProviderEntity -# from graphon.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel +# from graphon.model_runtime.model_providers.base.large_language_model import LargeLanguageModel # from core.prompt.prompt_transform import PromptTransform diff --git a/api/tests/unit_tests/core/rag/extractor/test_word_extractor.py b/api/tests/unit_tests/core/rag/extractor/test_word_extractor.py index 64eb89590a..0220fb6d4a 100644 --- a/api/tests/unit_tests/core/rag/extractor/test_word_extractor.py +++ b/api/tests/unit_tests/core/rag/extractor/test_word_extractor.py @@ -1,12 +1,14 @@ """Primarily used for testing merged cell scenarios""" +import gc import io import os import tempfile +import warnings from collections import UserDict from pathlib import Path from types import SimpleNamespace -from unittest.mock import MagicMock +from unittest.mock import AsyncMock, MagicMock import pytest from docx import Document @@ -354,15 +356,46 @@ def test_init_expands_home_path_and_invalid_local_path(monkeypatch, tmp_path): WordExtractor("not-a-file", "tenant", "user") -def test_del_closes_temp_file(): +def test_close_closes_temp_file(): extractor = object.__new__(WordExtractor) + extractor._closed = False extractor.temp_file = MagicMock() - WordExtractor.__del__(extractor) + extractor.close() extractor.temp_file.close.assert_called_once() +def test_close_is_idempotent(): + extractor = object.__new__(WordExtractor) + extractor._closed = False + extractor.temp_file = MagicMock() + + extractor.close() + extractor.close() + + extractor.temp_file.close.assert_called_once() + + +def test_close_handles_async_close_mock(): + extractor = object.__new__(WordExtractor) + extractor._closed = False + extractor.temp_file = MagicMock() + extractor.temp_file.close = AsyncMock() + + with warnings.catch_warnings(record=True) as caught: + warnings.simplefilter("always") + extractor.close() + gc.collect() + + extractor.temp_file.close.assert_called_once() + assert not [ + warning + for warning in caught + if issubclass(warning.category, RuntimeWarning) and "AsyncMockMixin._execute_mock_call" in str(warning.message) + ] + + def test_extract_images_handles_invalid_external_cases(monkeypatch): class FakeTargetRef: def __contains__(self, item): diff --git a/api/tests/unit_tests/core/repositories/test_human_input_form_repository_impl.py b/api/tests/unit_tests/core/repositories/test_human_input_form_repository_impl.py index 8be1ac318c..18ae9fafc8 100644 --- a/api/tests/unit_tests/core/repositories/test_human_input_form_repository_impl.py +++ b/api/tests/unit_tests/core/repositories/test_human_input_form_repository_impl.py @@ -14,7 +14,7 @@ from core.repositories.human_input_repository import ( HumanInputFormSubmissionRepository, _WorkspaceMemberInfo, ) -from core.workflow.human_input_compat import ( +from core.workflow.human_input_adapter import ( EmailDeliveryConfig, EmailDeliveryMethod, EmailRecipients, diff --git a/api/tests/unit_tests/core/repositories/test_human_input_repository.py b/api/tests/unit_tests/core/repositories/test_human_input_repository.py index 1297a95df1..4248782d93 100644 --- a/api/tests/unit_tests/core/repositories/test_human_input_repository.py +++ b/api/tests/unit_tests/core/repositories/test_human_input_repository.py @@ -21,7 +21,7 @@ from core.repositories.human_input_repository import ( _InvalidTimeoutStatusError, _WorkspaceMemberInfo, ) -from core.workflow.human_input_compat import ( +from core.workflow.human_input_adapter import ( EmailDeliveryConfig, EmailDeliveryMethod, EmailRecipients, diff --git a/api/tests/unit_tests/core/test_file.py b/api/tests/unit_tests/core/test_file.py index f17927f16b..eab0176f41 100644 --- a/api/tests/unit_tests/core/test_file.py +++ b/api/tests/unit_tests/core/test_file.py @@ -6,9 +6,9 @@ from models.workflow import Workflow def test_file_to_dict(): file = File( - id="file1", + file_id="file1", tenant_id="tenant1", - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.REMOTE_URL, remote_url="https://example.com/image1.jpg", storage_key="storage_key", diff --git a/api/tests/unit_tests/core/variables/test_segment.py b/api/tests/unit_tests/core/variables/test_segment.py index 72052c8c05..9e07ea1b6d 100644 --- a/api/tests/unit_tests/core/variables/test_segment.py +++ b/api/tests/unit_tests/core/variables/test_segment.py @@ -1,8 +1,9 @@ import dataclasses +from typing import Annotated import orjson import pytest -from pydantic import BaseModel +from pydantic import BaseModel, Discriminator, Tag from core.helper import encrypter from core.workflow.system_variables import build_bootstrap_variables, build_system_variables @@ -12,17 +13,18 @@ from graphon.runtime import VariablePool from graphon.variables.segment_group import SegmentGroup from graphon.variables.segments import ( ArrayAnySegment, + ArrayBooleanSegment, ArrayFileSegment, ArrayNumberSegment, ArrayObjectSegment, ArrayStringSegment, + BooleanSegment, FileSegment, FloatSegment, IntegerSegment, NoneSegment, ObjectSegment, Segment, - SegmentUnion, StringSegment, get_segment_discriminator, ) @@ -47,6 +49,26 @@ from graphon.variables.variables import ( StringVariable, Variable, ) +from models.utils.file_input_compat import rebuild_serialized_graph_files_without_lookup + +type SegmentUnion = Annotated[ + ( + Annotated[NoneSegment, Tag(SegmentType.NONE)] + | Annotated[StringSegment, Tag(SegmentType.STRING)] + | Annotated[FloatSegment, Tag(SegmentType.FLOAT)] + | Annotated[IntegerSegment, Tag(SegmentType.INTEGER)] + | Annotated[ObjectSegment, Tag(SegmentType.OBJECT)] + | Annotated[FileSegment, Tag(SegmentType.FILE)] + | Annotated[BooleanSegment, Tag(SegmentType.BOOLEAN)] + | Annotated[ArrayAnySegment, Tag(SegmentType.ARRAY_ANY)] + | Annotated[ArrayStringSegment, Tag(SegmentType.ARRAY_STRING)] + | Annotated[ArrayNumberSegment, Tag(SegmentType.ARRAY_NUMBER)] + | Annotated[ArrayObjectSegment, Tag(SegmentType.ARRAY_OBJECT)] + | Annotated[ArrayFileSegment, Tag(SegmentType.ARRAY_FILE)] + | Annotated[ArrayBooleanSegment, Tag(SegmentType.ARRAY_BOOLEAN)] + ), + Discriminator(get_segment_discriminator), +] def _build_variable_pool( @@ -123,7 +145,7 @@ def create_test_file( ) -> File: """Factory function to create File objects for testing""" return File( - type=file_type, + file_type=file_type, transfer_method=transfer_method, filename=filename, extension=extension, @@ -160,7 +182,7 @@ class TestSegmentDumpAndLoad: assert restored == model def test_all_segments_serialization(self): - """Test serialization/deserialization of all segment types""" + """Test file-aware segment serialization through Dify's model boundary.""" # Create one instance of each segment type test_file = create_test_file() @@ -181,7 +203,7 @@ class TestSegmentDumpAndLoad: # Test serialization and deserialization model = _Segments(segments=all_segments) json_str = model.model_dump_json() - loaded = _Segments.model_validate_json(json_str) + loaded = _Segments.model_validate(rebuild_serialized_graph_files_without_lookup(orjson.loads(json_str))) # Verify all segments are preserved assert len(loaded.segments) == len(all_segments) @@ -202,7 +224,7 @@ class TestSegmentDumpAndLoad: assert loaded_segment.value == original.value def test_all_variables_serialization(self): - """Test serialization/deserialization of all variable types""" + """Test file-aware variable serialization through Dify's model boundary.""" # Create one instance of each variable type test_file = create_test_file() @@ -223,7 +245,7 @@ class TestSegmentDumpAndLoad: # Test serialization and deserialization model = _Variables(variables=all_variables) json_str = model.model_dump_json() - loaded = _Variables.model_validate_json(json_str) + loaded = _Variables.model_validate(rebuild_serialized_graph_files_without_lookup(orjson.loads(json_str))) # Verify all variables are preserved assert len(loaded.variables) == len(all_variables) diff --git a/api/tests/unit_tests/core/variables/test_segment_type_validation.py b/api/tests/unit_tests/core/variables/test_segment_type_validation.py index 94e788edb2..317fe99d37 100644 --- a/api/tests/unit_tests/core/variables/test_segment_type_validation.py +++ b/api/tests/unit_tests/core/variables/test_segment_type_validation.py @@ -35,7 +35,7 @@ def create_test_file( """Factory function to create File objects for testing.""" return File( tenant_id="test-tenant", - type=file_type, + file_type=file_type, transfer_method=transfer_method, filename=filename, extension=extension, diff --git a/api/tests/unit_tests/core/workflow/graph_engine/test_mock_factory.py b/api/tests/unit_tests/core/workflow/graph_engine/test_mock_factory.py index 76b2984a4b..9f3e3b00b9 100644 --- a/api/tests/unit_tests/core/workflow/graph_engine/test_mock_factory.py +++ b/api/tests/unit_tests/core/workflow/graph_engine/test_mock_factory.py @@ -1,12 +1,13 @@ -""" -Mock node factory for testing workflows with third-party service dependencies. +"""Mock node factory for third-party-service workflow tests. -This module provides a MockNodeFactory that automatically detects and mocks nodes -requiring external services (LLM, Agent, Tool, Knowledge Retrieval, HTTP Request). +The factory follows the same config adaptation path as production +`DifyNodeFactory.create_node()`, but swaps selected node classes for mock +implementations before instantiation. """ from typing import TYPE_CHECKING, Any +from core.workflow.human_input_adapter import adapt_node_config_for_graph from core.workflow.node_factory import DifyNodeFactory from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter from graphon.enums import BuiltinNodeTypes, NodeType @@ -82,20 +83,20 @@ class MockNodeFactory(DifyNodeFactory): :param node_config: Node configuration dictionary :return: Node instance (real or mocked) """ - typed_node_config = NodeConfigDictAdapter.validate_python(node_config) + typed_node_config = NodeConfigDictAdapter.validate_python(adapt_node_config_for_graph(node_config)) + node_id = typed_node_config["id"] node_data = typed_node_config["data"] node_type = node_data.type # Check if this node type should be mocked if node_type in self._mock_node_types: - node_id = typed_node_config["id"] - # Create mock node instance mock_class = self._mock_node_types[node_type] + resolved_node_data = self._validate_resolved_node_data(mock_class, node_data) if node_type == BuiltinNodeTypes.CODE: mock_instance = mock_class( - id=node_id, - config=typed_node_config, + node_id=node_id, + config=resolved_node_data, graph_init_params=self.graph_init_params, graph_runtime_state=self.graph_runtime_state, mock_config=self.mock_config, @@ -104,8 +105,8 @@ class MockNodeFactory(DifyNodeFactory): ) elif node_type == BuiltinNodeTypes.HTTP_REQUEST: mock_instance = mock_class( - id=node_id, - config=typed_node_config, + node_id=node_id, + config=resolved_node_data, graph_init_params=self.graph_init_params, graph_runtime_state=self.graph_runtime_state, mock_config=self.mock_config, @@ -120,8 +121,8 @@ class MockNodeFactory(DifyNodeFactory): BuiltinNodeTypes.PARAMETER_EXTRACTOR, }: mock_instance = mock_class( - id=node_id, - config=typed_node_config, + node_id=node_id, + config=resolved_node_data, graph_init_params=self.graph_init_params, graph_runtime_state=self.graph_runtime_state, mock_config=self.mock_config, @@ -130,8 +131,8 @@ class MockNodeFactory(DifyNodeFactory): ) else: mock_instance = mock_class( - id=node_id, - config=typed_node_config, + node_id=node_id, + config=resolved_node_data, graph_init_params=self.graph_init_params, graph_runtime_state=self.graph_runtime_state, mock_config=self.mock_config, @@ -140,7 +141,7 @@ class MockNodeFactory(DifyNodeFactory): return mock_instance # For non-mocked node types, use parent implementation - return super().create_node(typed_node_config) + return super().create_node(node_config) def should_mock_node(self, node_type: NodeType) -> bool: """ diff --git a/api/tests/unit_tests/core/workflow/graph_engine/test_mock_nodes.py b/api/tests/unit_tests/core/workflow/graph_engine/test_mock_nodes.py index 971b9b2bbf..f9819c47ec 100644 --- a/api/tests/unit_tests/core/workflow/graph_engine/test_mock_nodes.py +++ b/api/tests/unit_tests/core/workflow/graph_engine/test_mock_nodes.py @@ -55,13 +55,14 @@ class MockNodeMixin: def __init__( self, - id: str, - config: Mapping[str, Any], + node_id: str, + config: Any, + *, graph_init_params: "GraphInitParams", graph_runtime_state: "GraphRuntimeState", mock_config: Optional["MockConfig"] = None, **kwargs: Any, - ): + ) -> None: if isinstance(self, (LLMNode, QuestionClassifierNode, ParameterExtractorNode)): kwargs.setdefault("credentials_provider", MagicMock(spec=CredentialsProvider)) kwargs.setdefault("model_factory", MagicMock(spec=ModelFactory)) @@ -96,7 +97,7 @@ class MockNodeMixin: kwargs.setdefault("message_transformer", MagicMock()) super().__init__( - id=id, + node_id=node_id, config=config, graph_init_params=graph_init_params, graph_runtime_state=graph_runtime_state, diff --git a/api/tests/unit_tests/core/workflow/graph_engine/test_parallel_human_input_join_resume.py b/api/tests/unit_tests/core/workflow/graph_engine/test_parallel_human_input_join_resume.py index 55a329eba9..75bc6d05f7 100644 --- a/api/tests/unit_tests/core/workflow/graph_engine/test_parallel_human_input_join_resume.py +++ b/api/tests/unit_tests/core/workflow/graph_engine/test_parallel_human_input_join_resume.py @@ -139,8 +139,8 @@ def _build_graph(runtime_state: GraphRuntimeState, repo: HumanInputFormRepositor start_config = {"id": "start", "data": StartNodeData(title="Start", variables=[]).model_dump()} start_node = StartNode( - id=start_config["id"], - config=start_config, + node_id=start_config["id"], + config=StartNodeData(title="Start", variables=[]), graph_init_params=graph_init_params, graph_runtime_state=runtime_state, ) @@ -154,8 +154,8 @@ def _build_graph(runtime_state: GraphRuntimeState, repo: HumanInputFormRepositor human_a_config = {"id": "human_a", "data": human_data.model_dump()} human_a = HumanInputNode( - id=human_a_config["id"], - config=human_a_config, + node_id=human_a_config["id"], + config=human_data, graph_init_params=graph_init_params, graph_runtime_state=runtime_state, form_repository=repo, @@ -164,8 +164,8 @@ def _build_graph(runtime_state: GraphRuntimeState, repo: HumanInputFormRepositor human_b_config = {"id": "human_b", "data": human_data.model_dump()} human_b = HumanInputNode( - id=human_b_config["id"], - config=human_b_config, + node_id=human_b_config["id"], + config=human_data, graph_init_params=graph_init_params, graph_runtime_state=runtime_state, form_repository=repo, @@ -182,8 +182,8 @@ def _build_graph(runtime_state: GraphRuntimeState, repo: HumanInputFormRepositor ) end_config = {"id": "end", "data": end_data.model_dump()} end_node = EndNode( - id=end_config["id"], - config=end_config, + node_id=end_config["id"], + config=end_data, graph_init_params=graph_init_params, graph_runtime_state=runtime_state, ) diff --git a/api/tests/unit_tests/core/workflow/nodes/answer/test_answer.py b/api/tests/unit_tests/core/workflow/nodes/answer/test_answer.py index 9c0ad25b58..76b4cd1ef4 100644 --- a/api/tests/unit_tests/core/workflow/nodes/answer/test_answer.py +++ b/api/tests/unit_tests/core/workflow/nodes/answer/test_answer.py @@ -9,6 +9,7 @@ from extensions.ext_database import db from graphon.enums import WorkflowNodeExecutionStatus from graphon.graph import Graph from graphon.nodes.answer.answer_node import AnswerNode +from graphon.nodes.answer.entities import AnswerNodeData from graphon.runtime import GraphRuntimeState, VariablePool from tests.workflow_test_utils import build_test_graph_init_params @@ -66,20 +67,15 @@ def test_execute_answer(): graph = Graph.init(graph_config=graph_config, node_factory=node_factory, root_node_id="start") - node_config = { - "id": "answer", - "data": { - "title": "123", - "type": "answer", - "answer": "Today's weather is {{#start.weather#}}\n{{#llm.text#}}\n{{img}}\nFin.", - }, - } - node = AnswerNode( - id=str(uuid.uuid4()), + node_id=str(uuid.uuid4()), graph_init_params=init_params, graph_runtime_state=graph_runtime_state, - config=node_config, + config=AnswerNodeData( + title="123", + type="answer", + answer="Today's weather is {{#start.weather#}}\n{{#llm.text#}}\n{{img}}\nFin.", + ), ) # Mock db.session.close() diff --git a/api/tests/unit_tests/core/workflow/nodes/datasource/test_datasource_node.py b/api/tests/unit_tests/core/workflow/nodes/datasource/test_datasource_node.py index 9cceadde49..d7ef781732 100644 --- a/api/tests/unit_tests/core/workflow/nodes/datasource/test_datasource_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/datasource/test_datasource_node.py @@ -1,5 +1,6 @@ from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY from core.workflow.nodes.datasource.datasource_node import DatasourceNode +from core.workflow.nodes.datasource.entities import DatasourceNodeData from graphon.enums import WorkflowNodeExecutionStatus from graphon.node_events import NodeRunResult, StreamChunkEvent, StreamCompletedEvent @@ -77,19 +78,16 @@ def test_datasource_node_delegates_to_manager_stream(mocker): mocker.patch("core.workflow.nodes.datasource.datasource_node.DatasourceManager", new=_Mgr) node = DatasourceNode( - id="n", - config={ - "id": "n", - "data": { - "type": "datasource", - "version": "1", - "title": "Datasource", - "provider_type": "plugin", - "provider_name": "p", - "plugin_id": "plug", - "datasource_name": "ds", - }, - }, + node_id="n", + config=DatasourceNodeData( + type="datasource", + version="1", + title="Datasource", + provider_type="plugin", + provider_name="p", + plugin_id="plug", + datasource_name="ds", + ), graph_init_params=gp, graph_runtime_state=gs, ) diff --git a/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_node.py b/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_node.py index a3cadc0681..2e89a2da3c 100644 --- a/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/http_request/test_http_request_node.py @@ -12,7 +12,7 @@ from core.workflow.system_variables import build_system_variables from graphon.enums import WorkflowNodeExecutionStatus from graphon.file.file_manager import file_manager from graphon.nodes.http_request import HTTP_REQUEST_CONFIG_FILTER_KEY, HttpRequestNode, HttpRequestNodeConfig -from graphon.nodes.http_request.entities import HttpRequestNodeTimeout, Response +from graphon.nodes.http_request.entities import HttpRequestNodeData, HttpRequestNodeTimeout, Response from graphon.runtime import GraphRuntimeState, VariablePool from tests.workflow_test_utils import build_test_graph_init_params @@ -66,8 +66,8 @@ def test_get_default_config_uses_injected_http_request_config(): assert default_config["retry_config"]["max_retries"] == 7 -def test_get_default_config_with_malformed_http_request_config_raises_value_error(): - with pytest.raises(ValueError, match="http_request_config must be an HttpRequestNodeConfig instance"): +def test_get_default_config_with_malformed_http_request_config_raises_type_error(): + with pytest.raises(TypeError, match="http_request_config must be an HttpRequestNodeConfig instance"): HttpRequestNode.get_default_config(filters={HTTP_REQUEST_CONFIG_FILTER_KEY: "invalid"}) @@ -114,8 +114,8 @@ def _build_http_node( start_at=time.perf_counter(), ) return HttpRequestNode( - id="http-node", - config=node_config, + node_id="http-node", + config=HttpRequestNodeData.model_validate(node_data), graph_init_params=graph_init_params, graph_runtime_state=graph_runtime_state, http_request_config=HTTP_REQUEST_CONFIG, diff --git a/api/tests/unit_tests/core/workflow/nodes/human_input/test_email_delivery_config.py b/api/tests/unit_tests/core/workflow/nodes/human_input/test_email_delivery_config.py index 1d6a4da7c4..07430498e5 100644 --- a/api/tests/unit_tests/core/workflow/nodes/human_input/test_email_delivery_config.py +++ b/api/tests/unit_tests/core/workflow/nodes/human_input/test_email_delivery_config.py @@ -1,4 +1,4 @@ -from core.workflow.human_input_compat import EmailDeliveryConfig, EmailRecipients +from core.workflow.human_input_adapter import EmailDeliveryConfig, EmailRecipients from graphon.runtime import VariablePool diff --git a/api/tests/unit_tests/core/workflow/nodes/human_input/test_entities.py b/api/tests/unit_tests/core/workflow/nodes/human_input/test_entities.py index c0e21d0bf7..0659984c76 100644 --- a/api/tests/unit_tests/core/workflow/nodes/human_input/test_entities.py +++ b/api/tests/unit_tests/core/workflow/nodes/human_input/test_entities.py @@ -19,7 +19,7 @@ from core.repositories.human_input_repository import ( HumanInputFormRecipientEntity, HumanInputFormRepository, ) -from core.workflow.human_input_compat import ( +from core.workflow.human_input_adapter import ( DeliveryMethodType, EmailDeliveryConfig, EmailDeliveryMethod, @@ -136,6 +136,26 @@ class InMemoryHumanInputFormRepository(HumanInputFormRepository): entity.status_value = HumanInputFormStatus.SUBMITTED +def _build_human_input_node( + *, + node_id: str, + node_data: HumanInputNodeData | Mapping[str, Any], + graph_init_params: GraphInitParams, + graph_runtime_state: GraphRuntimeState, + runtime: DifyHumanInputNodeRuntime, +) -> HumanInputNode: + typed_node_data = ( + node_data if isinstance(node_data, HumanInputNodeData) else HumanInputNodeData.model_validate(node_data) + ) + return HumanInputNode( + node_id=node_id, + config=typed_node_data, + graph_init_params=graph_init_params, + graph_runtime_state=graph_runtime_state, + runtime=runtime, + ) + + class TestDeliveryMethod: """Test DeliveryMethod entity.""" @@ -239,7 +259,7 @@ class TestUserAction: data[field_name] = value with pytest.raises(ValidationError) as exc_info: - UserAction(**data) + UserAction.model_validate(data) errors = exc_info.value.errors() assert any(error["loc"] == (field_name,) and error["type"] == "string_too_long" for error in errors) @@ -465,9 +485,9 @@ class TestHumanInputNodeVariableResolution: runtime = DifyHumanInputNodeRuntime(graph_init_params.run_context) runtime._build_form_repository = MagicMock(return_value=mock_repo) # type: ignore[attr-defined] - node = HumanInputNode( - id=config["id"], - config=config, + node = _build_human_input_node( + node_id=config["id"], + node_data=config["data"], graph_init_params=graph_init_params, graph_runtime_state=runtime_state, runtime=runtime, @@ -530,9 +550,9 @@ class TestHumanInputNodeVariableResolution: runtime = DifyHumanInputNodeRuntime(graph_init_params.run_context) runtime._build_form_repository = MagicMock(return_value=mock_repo) # type: ignore[attr-defined] - node = HumanInputNode( - id=config["id"], - config=config, + node = _build_human_input_node( + node_id=config["id"], + node_data=config["data"], graph_init_params=graph_init_params, graph_runtime_state=runtime_state, runtime=runtime, @@ -595,9 +615,9 @@ class TestHumanInputNodeVariableResolution: runtime = DifyHumanInputNodeRuntime(graph_init_params.run_context) runtime._build_form_repository = MagicMock(return_value=mock_repo) # type: ignore[attr-defined] - node = HumanInputNode( - id=config["id"], - config=config, + node = _build_human_input_node( + node_id=config["id"], + node_data=config["data"], graph_init_params=graph_init_params, graph_runtime_state=runtime_state, runtime=runtime, @@ -671,9 +691,9 @@ class TestHumanInputNodeVariableResolution: runtime = DifyHumanInputNodeRuntime(graph_init_params.run_context) runtime._build_form_repository = MagicMock(return_value=mock_repo) # type: ignore[attr-defined] - node = HumanInputNode( - id=config["id"], - config=config, + node = _build_human_input_node( + node_id=config["id"], + node_data=config["data"], graph_init_params=graph_init_params, graph_runtime_state=runtime_state, runtime=runtime, @@ -770,9 +790,9 @@ class TestHumanInputNodeRenderedContent: form_repository = InMemoryHumanInputFormRepository() runtime = DifyHumanInputNodeRuntime(graph_init_params.run_context) runtime._build_form_repository = MagicMock(return_value=form_repository) # type: ignore[attr-defined] - node = HumanInputNode( - id=config["id"], - config=config, + node = _build_human_input_node( + node_id=config["id"], + node_data=config["data"], graph_init_params=graph_init_params, graph_runtime_state=runtime_state, runtime=runtime, diff --git a/api/tests/unit_tests/core/workflow/nodes/human_input/test_human_input_form_filled_event.py b/api/tests/unit_tests/core/workflow/nodes/human_input/test_human_input_form_filled_event.py index bc98028d5b..4a9438b14f 100644 --- a/api/tests/unit_tests/core/workflow/nodes/human_input/test_human_input_form_filled_event.py +++ b/api/tests/unit_tests/core/workflow/nodes/human_input/test_human_input_form_filled_event.py @@ -11,6 +11,7 @@ from graphon.graph_events import ( NodeRunHumanInputFormTimeoutEvent, NodeRunStartedEvent, ) +from graphon.nodes.human_input.entities import HumanInputNodeData from graphon.nodes.human_input.enums import HumanInputFormStatus from graphon.nodes.human_input.human_input_node import HumanInputNode from graphon.runtime import GraphRuntimeState, VariablePool @@ -25,6 +26,28 @@ class _FakeFormRepository: return self._form +def _create_human_input_node( + *, + config: dict, + graph_init_params: GraphInitParams, + graph_runtime_state: GraphRuntimeState, + repo: _FakeFormRepository, +) -> HumanInputNode: + node_data = ( + config["data"] + if isinstance(config["data"], HumanInputNodeData) + else HumanInputNodeData.model_validate(config["data"]) + ) + return HumanInputNode( + node_id=config["id"], + config=node_data, + graph_init_params=graph_init_params, + graph_runtime_state=graph_runtime_state, + form_repository=repo, + runtime=DifyHumanInputNodeRuntime(graph_init_params.run_context), + ) + + def _build_node(form_content: str = "Please enter your name:\n\n{{#$output.name#}}") -> HumanInputNode: system_variables = default_system_variables() graph_runtime_state = GraphRuntimeState( @@ -80,13 +103,11 @@ def _build_node(form_content: str = "Please enter your name:\n\n{{#$output.name# ) repo = _FakeFormRepository(fake_form) - return HumanInputNode( - id="node-1", + return _create_human_input_node( config=config, graph_init_params=graph_init_params, graph_runtime_state=graph_runtime_state, - form_repository=repo, - runtime=DifyHumanInputNodeRuntime(graph_init_params.run_context), + repo=repo, ) @@ -145,13 +166,11 @@ def _build_timeout_node() -> HumanInputNode: ) repo = _FakeFormRepository(fake_form) - return HumanInputNode( - id="node-1", + return _create_human_input_node( config=config, graph_init_params=graph_init_params, graph_runtime_state=graph_runtime_state, - form_repository=repo, - runtime=DifyHumanInputNodeRuntime(graph_init_params.run_context), + repo=repo, ) diff --git a/api/tests/unit_tests/core/workflow/nodes/iteration/test_iteration_child_engine_errors.py b/api/tests/unit_tests/core/workflow/nodes/iteration/test_iteration_child_engine_errors.py index 82cc734274..8ffce39cd6 100644 --- a/api/tests/unit_tests/core/workflow/nodes/iteration/test_iteration_child_engine_errors.py +++ b/api/tests/unit_tests/core/workflow/nodes/iteration/test_iteration_child_engine_errors.py @@ -5,6 +5,7 @@ import pytest from core.workflow.system_variables import default_system_variables from graphon.entities import GraphInitParams +from graphon.nodes.iteration.entities import IterationNodeData from graphon.nodes.iteration.exc import IterationGraphNotFoundError from graphon.nodes.iteration.iteration_node import IterationNode from graphon.runtime import ( @@ -44,17 +45,14 @@ def _build_iteration_node( ) -> IterationNode: init_params = build_test_graph_init_params(graph_config=graph_config) return IterationNode( - id="iteration-node", - config={ - "id": "iteration-node", - "data": { - "type": "iteration", - "title": "Iteration", - "iterator_selector": ["start", "items"], - "output_selector": ["iteration-node", "output"], - "start_node_id": start_node_id, - }, - }, + node_id="iteration-node", + config=IterationNodeData( + type="iteration", + title="Iteration", + iterator_selector=["start", "items"], + output_selector=["iteration-node", "output"], + start_node_id=start_node_id, + ), graph_init_params=init_params, graph_runtime_state=runtime_state, ) diff --git a/api/tests/unit_tests/core/workflow/nodes/knowledge_index/test_knowledge_index_node.py b/api/tests/unit_tests/core/workflow/nodes/knowledge_index/test_knowledge_index_node.py index a6fca1bfb4..f254fc3d09 100644 --- a/api/tests/unit_tests/core/workflow/nodes/knowledge_index/test_knowledge_index_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/knowledge_index/test_knowledge_index_node.py @@ -93,6 +93,25 @@ def sample_chunks(): } +def _build_node( + *, + node_id: str, + node_data: KnowledgeIndexNodeData | dict[str, object], + graph_init_params, + graph_runtime_state, +) -> KnowledgeIndexNode: + return KnowledgeIndexNode( + node_id=node_id, + config=( + node_data + if isinstance(node_data, KnowledgeIndexNodeData) + else KnowledgeIndexNodeData.model_validate(node_data) + ), + graph_init_params=graph_init_params, + graph_runtime_state=graph_runtime_state, + ) + + class TestKnowledgeIndexNode: """ Test suite for KnowledgeIndexNode. @@ -115,9 +134,9 @@ class TestKnowledgeIndexNode: } # Act - node = KnowledgeIndexNode( - id=node_id, - config=config, + node = _build_node( + node_id=node_id, + node_data=config["data"], graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -143,9 +162,9 @@ class TestKnowledgeIndexNode: "data": sample_node_data.model_dump(), } - node = KnowledgeIndexNode( - id=node_id, - config=config, + node = _build_node( + node_id=node_id, + node_data=config["data"], graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -176,9 +195,9 @@ class TestKnowledgeIndexNode: "data": sample_node_data.model_dump(), } - node = KnowledgeIndexNode( - id=node_id, - config=config, + node = _build_node( + node_id=node_id, + node_data=config["data"], graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -212,9 +231,9 @@ class TestKnowledgeIndexNode: "data": sample_node_data.model_dump(), } - node = KnowledgeIndexNode( - id=node_id, - config=config, + node = _build_node( + node_id=node_id, + node_data=config["data"], graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -269,9 +288,9 @@ class TestKnowledgeIndexNode: "data": sample_node_data.model_dump(), } - node = KnowledgeIndexNode( - id=node_id, - config=config, + node = _build_node( + node_id=node_id, + node_data=config["data"], graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -332,9 +351,9 @@ class TestKnowledgeIndexNode: "data": sample_node_data.model_dump(), } - node = KnowledgeIndexNode( - id=node_id, - config=config, + node = _build_node( + node_id=node_id, + node_data=config["data"], graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -383,9 +402,9 @@ class TestKnowledgeIndexNode: "data": sample_node_data.model_dump(), } - node = KnowledgeIndexNode( - id=node_id, - config=config, + node = _build_node( + node_id=node_id, + node_data=config["data"], graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -440,9 +459,9 @@ class TestKnowledgeIndexNode: "data": sample_node_data.model_dump(), } - node = KnowledgeIndexNode( - id=node_id, - config=config, + node = _build_node( + node_id=node_id, + node_data=config["data"], graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -498,9 +517,9 @@ class TestKnowledgeIndexNode: "data": sample_node_data.model_dump(), } - node = KnowledgeIndexNode( - id=node_id, - config=config, + node = _build_node( + node_id=node_id, + node_data=config["data"], graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -536,9 +555,9 @@ class TestKnowledgeIndexNode: "data": sample_node_data.model_dump(), } - node = KnowledgeIndexNode( - id=node_id, - config=config, + node = _build_node( + node_id=node_id, + node_data=config["data"], graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -583,9 +602,9 @@ class TestKnowledgeIndexNode: "data": sample_node_data.model_dump(), } - node = KnowledgeIndexNode( - id=node_id, - config=config, + node = _build_node( + node_id=node_id, + node_data=config["data"], graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -623,9 +642,9 @@ class TestInvokeKnowledgeIndex: "data": sample_node_data.model_dump(), } - node = KnowledgeIndexNode( - id=node_id, - config=config, + node = _build_node( + node_id=node_id, + node_data=config["data"], graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) diff --git a/api/tests/unit_tests/core/workflow/nodes/knowledge_retrieval/test_knowledge_retrieval_node.py b/api/tests/unit_tests/core/workflow/nodes/knowledge_retrieval/test_knowledge_retrieval_node.py index 45e8ae7d20..e923ee761b 100644 --- a/api/tests/unit_tests/core/workflow/nodes/knowledge_retrieval/test_knowledge_retrieval_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/knowledge_retrieval/test_knowledge_retrieval_node.py @@ -14,7 +14,11 @@ from core.workflow.nodes.knowledge_retrieval.entities import ( SingleRetrievalConfig, ) from core.workflow.nodes.knowledge_retrieval.exc import RateLimitExceededError -from core.workflow.nodes.knowledge_retrieval.knowledge_retrieval_node import KnowledgeRetrievalNode +from core.workflow.nodes.knowledge_retrieval.knowledge_retrieval_node import ( + KnowledgeRetrievalNode, + _normalize_metadata_filter_scalar, + _normalize_metadata_filter_sequence_item, +) from core.workflow.nodes.knowledge_retrieval.retrieval import RAGRetrievalProtocol, Source from core.workflow.system_variables import build_system_variables from graphon.enums import WorkflowNodeExecutionStatus @@ -85,6 +89,12 @@ def sample_node_data(): ) +def test_metadata_filter_normalizers_preserve_numeric_scalars_and_stringify_other_values() -> None: + assert _normalize_metadata_filter_scalar(3) == 3 + assert _normalize_metadata_filter_scalar(True) == "True" + assert _normalize_metadata_filter_sequence_item(4) == "4" + + class TestKnowledgeRetrievalNode: """ Test suite for KnowledgeRetrievalNode. @@ -106,8 +116,8 @@ class TestKnowledgeRetrievalNode: # Act node = KnowledgeRetrievalNode( - id=node_id, - config=config, + node_id=node_id, + config=KnowledgeRetrievalNodeData.model_validate(config["data"]), graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -135,8 +145,8 @@ class TestKnowledgeRetrievalNode: } node = KnowledgeRetrievalNode( - id=node_id, - config=config, + node_id=node_id, + config=KnowledgeRetrievalNodeData.model_validate(config["data"]), graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -194,8 +204,8 @@ class TestKnowledgeRetrievalNode: mock_rag_retrieval.llm_usage = LLMUsage.empty_usage() node = KnowledgeRetrievalNode( - id=node_id, - config=config, + node_id=node_id, + config=KnowledgeRetrievalNodeData.model_validate(config["data"]), graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -238,8 +248,8 @@ class TestKnowledgeRetrievalNode: mock_rag_retrieval.llm_usage = LLMUsage.empty_usage() node = KnowledgeRetrievalNode( - id=node_id, - config=config, + node_id=node_id, + config=KnowledgeRetrievalNodeData.model_validate(config["data"]), graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -274,8 +284,8 @@ class TestKnowledgeRetrievalNode: } node = KnowledgeRetrievalNode( - id=node_id, - config=config, + node_id=node_id, + config=KnowledgeRetrievalNodeData.model_validate(config["data"]), graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -309,8 +319,8 @@ class TestKnowledgeRetrievalNode: } node = KnowledgeRetrievalNode( - id=node_id, - config=config, + node_id=node_id, + config=KnowledgeRetrievalNodeData.model_validate(config["data"]), graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -350,8 +360,8 @@ class TestKnowledgeRetrievalNode: mock_rag_retrieval.llm_usage = LLMUsage.empty_usage() node = KnowledgeRetrievalNode( - id=node_id, - config=config, + node_id=node_id, + config=KnowledgeRetrievalNodeData.model_validate(config["data"]), graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -389,8 +399,8 @@ class TestKnowledgeRetrievalNode: mock_rag_retrieval.llm_usage = LLMUsage.empty_usage() node = KnowledgeRetrievalNode( - id=node_id, - config=config, + node_id=node_id, + config=KnowledgeRetrievalNodeData.model_validate(config["data"]), graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -470,8 +480,8 @@ class TestFetchDatasetRetriever: config = {"id": node_id, "data": node_data.model_dump()} node = KnowledgeRetrievalNode( - id=node_id, - config=config, + node_id=node_id, + config=KnowledgeRetrievalNodeData.model_validate(config["data"]), graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -507,8 +517,8 @@ class TestFetchDatasetRetriever: } node = KnowledgeRetrievalNode( - id=node_id, - config=config, + node_id=node_id, + config=KnowledgeRetrievalNodeData.model_validate(config["data"]), graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -562,8 +572,8 @@ class TestFetchDatasetRetriever: } node = KnowledgeRetrievalNode( - id=node_id, - config=config, + node_id=node_id, + config=KnowledgeRetrievalNodeData.model_validate(config["data"]), graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -610,8 +620,8 @@ class TestFetchDatasetRetriever: mock_graph_runtime_state.variable_pool.add(["start", "query"], StringSegment(value="readme")) node = KnowledgeRetrievalNode( - id=node_id, - config=config, + node_id=node_id, + config=KnowledgeRetrievalNodeData.model_validate(config["data"]), graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -671,8 +681,8 @@ class TestFetchDatasetRetriever: node_id = str(uuid.uuid4()) config = {"id": node_id, "data": node_data.model_dump()} node = KnowledgeRetrievalNode( - id=node_id, - config=config, + node_id=node_id, + config=KnowledgeRetrievalNodeData.model_validate(config["data"]), graph_init_params=mock_graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) diff --git a/api/tests/unit_tests/core/workflow/nodes/list_operator/node_spec.py b/api/tests/unit_tests/core/workflow/nodes/list_operator/node_spec.py index eca34f05be..388654f279 100644 --- a/api/tests/unit_tests/core/workflow/nodes/list_operator/node_spec.py +++ b/api/tests/unit_tests/core/workflow/nodes/list_operator/node_spec.py @@ -1,3 +1,4 @@ +from types import SimpleNamespace from unittest.mock import MagicMock import pytest @@ -5,6 +6,7 @@ import pytest from core.app.entities.app_invoke_entities import DIFY_RUN_CONTEXT_KEY from graphon.entities import GraphInitParams from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionStatus +from graphon.nodes.list_operator.entities import ListOperatorNodeData from graphon.nodes.list_operator.node import ListOperatorNode from graphon.runtime import GraphRuntimeState from graphon.variables import ArrayNumberSegment, ArrayStringSegment @@ -13,11 +15,28 @@ from graphon.variables import ArrayNumberSegment, ArrayStringSegment class TestListOperatorNode: """Comprehensive tests for ListOperatorNode.""" + @staticmethod + def _build_node(*, config, graph_init_params, graph_runtime_state): + return ListOperatorNode( + node_id="test", + config=config if isinstance(config, ListOperatorNodeData) else ListOperatorNodeData.model_validate(config), + graph_init_params=graph_init_params, + graph_runtime_state=graph_runtime_state, + ) + + @staticmethod + def _filter_by(comparison_operator: str, value: str) -> dict[str, object]: + return { + "enabled": True, + "conditions": [{"comparison_operator": comparison_operator, "value": value}], + } + @pytest.fixture def mock_graph_runtime_state(self): """Create mock GraphRuntimeState.""" mock_state = MagicMock(spec=GraphRuntimeState) mock_variable_pool = MagicMock() + mock_variable_pool.convert_template.side_effect = lambda value: SimpleNamespace(text=value) mock_state.variable_pool = mock_variable_pool return mock_state @@ -45,9 +64,8 @@ class TestListOperatorNode: def _create_node(config, mock_variable): mock_graph_runtime_state.variable_pool.get.return_value = mock_variable - return ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + return self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -64,9 +82,8 @@ class TestListOperatorNode: "limit": {"enabled": False}, } - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -109,9 +126,8 @@ class TestListOperatorNode: mock_var = ArrayStringSegment(value=[]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -128,11 +144,7 @@ class TestListOperatorNode: config = { "title": "Test", "variable": ["sys", "items"], - "filter_by": { - "enabled": True, - "condition": "contains", - "value": "app", - }, + "filter_by": self._filter_by("contains", "app"), "order_by": {"enabled": False}, "limit": {"enabled": False}, } @@ -140,9 +152,8 @@ class TestListOperatorNode: mock_var = ArrayStringSegment(value=["apple", "banana", "pineapple", "cherry"]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -157,11 +168,7 @@ class TestListOperatorNode: config = { "title": "Test", "variable": ["sys", "items"], - "filter_by": { - "enabled": True, - "condition": "not contains", - "value": "app", - }, + "filter_by": self._filter_by("not contains", "app"), "order_by": {"enabled": False}, "limit": {"enabled": False}, } @@ -169,9 +176,8 @@ class TestListOperatorNode: mock_var = ArrayStringSegment(value=["apple", "banana", "pineapple", "cherry"]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -186,11 +192,7 @@ class TestListOperatorNode: config = { "title": "Test", "variable": ["sys", "numbers"], - "filter_by": { - "enabled": True, - "condition": ">", - "value": "5", - }, + "filter_by": self._filter_by(">", "5"), "order_by": {"enabled": False}, "limit": {"enabled": False}, } @@ -198,9 +200,8 @@ class TestListOperatorNode: mock_var = ArrayNumberSegment(value=[1, 3, 5, 7, 9, 11]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -226,9 +227,8 @@ class TestListOperatorNode: mock_var = ArrayStringSegment(value=["cherry", "apple", "banana"]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -254,9 +254,8 @@ class TestListOperatorNode: mock_var = ArrayStringSegment(value=["cherry", "apple", "banana"]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -282,9 +281,8 @@ class TestListOperatorNode: mock_var = ArrayStringSegment(value=["apple", "banana", "cherry", "date"]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -299,11 +297,7 @@ class TestListOperatorNode: config = { "title": "Test", "variable": ["sys", "numbers"], - "filter_by": { - "enabled": True, - "condition": ">", - "value": "3", - }, + "filter_by": self._filter_by(">", "3"), "order_by": { "enabled": True, "value": "desc", @@ -317,9 +311,8 @@ class TestListOperatorNode: mock_var = ArrayNumberSegment(value=[1, 2, 3, 4, 5, 6, 7, 8, 9]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -341,9 +334,8 @@ class TestListOperatorNode: mock_graph_runtime_state.variable_pool.get.return_value = None - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -366,9 +358,8 @@ class TestListOperatorNode: mock_var = ArrayStringSegment(value=["first", "middle", "last"]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -384,11 +375,7 @@ class TestListOperatorNode: config = { "title": "Test", "variable": ["sys", "items"], - "filter_by": { - "enabled": True, - "condition": "start with", - "value": "app", - }, + "filter_by": self._filter_by("start with", "app"), "order_by": {"enabled": False}, "limit": {"enabled": False}, } @@ -396,9 +383,8 @@ class TestListOperatorNode: mock_var = ArrayStringSegment(value=["apple", "application", "banana", "apricot"]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -413,11 +399,7 @@ class TestListOperatorNode: config = { "title": "Test", "variable": ["sys", "items"], - "filter_by": { - "enabled": True, - "condition": "end with", - "value": "le", - }, + "filter_by": self._filter_by("end with", "le"), "order_by": {"enabled": False}, "limit": {"enabled": False}, } @@ -425,9 +407,8 @@ class TestListOperatorNode: mock_var = ArrayStringSegment(value=["apple", "banana", "pineapple", "table"]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -442,11 +423,7 @@ class TestListOperatorNode: config = { "title": "Test", "variable": ["sys", "numbers"], - "filter_by": { - "enabled": True, - "condition": "=", - "value": "5", - }, + "filter_by": self._filter_by("=", "5"), "order_by": {"enabled": False}, "limit": {"enabled": False}, } @@ -454,9 +431,8 @@ class TestListOperatorNode: mock_var = ArrayNumberSegment(value=[1, 3, 5, 5, 7, 9]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -471,11 +447,7 @@ class TestListOperatorNode: config = { "title": "Test", "variable": ["sys", "numbers"], - "filter_by": { - "enabled": True, - "condition": "≠", - "value": "5", - }, + "filter_by": self._filter_by("≠", "5"), "order_by": {"enabled": False}, "limit": {"enabled": False}, } @@ -483,9 +455,8 @@ class TestListOperatorNode: mock_var = ArrayNumberSegment(value=[1, 3, 5, 7, 9]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) @@ -511,9 +482,8 @@ class TestListOperatorNode: mock_var = ArrayNumberSegment(value=[9, 3, 7, 1, 5]) mock_graph_runtime_state.variable_pool.get.return_value = mock_var - node = ListOperatorNode( - id="test", - config={"id": "test", "data": config}, + node = self._build_node( + config=config, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, ) diff --git a/api/tests/unit_tests/core/workflow/nodes/llm/test_llm_utils.py b/api/tests/unit_tests/core/workflow/nodes/llm/test_llm_utils.py index 4186bbdc93..212ad07bd3 100644 --- a/api/tests/unit_tests/core/workflow/nodes/llm/test_llm_utils.py +++ b/api/tests/unit_tests/core/workflow/nodes/llm/test_llm_utils.py @@ -71,8 +71,8 @@ def _build_image_file( mime_type: str = "image/png", ) -> File: return File( - id=file_id, - type=FileType.IMAGE, + file_id=file_id, + file_type=FileType.IMAGE, filename=f"{file_id}{extension}", transfer_method=FileTransferMethod.REMOTE_URL, remote_url=remote_url, @@ -95,6 +95,8 @@ def variable_pool() -> VariablePool: def _fetch_prompt_messages_with_mocked_content(content): variable_pool = VariablePool.empty() model_instance = mock.MagicMock(spec=ModelInstance) + model_schema = mock.MagicMock() + model_schema.supports_prompt_content_type.side_effect = lambda content_type: content_type == "text" prompt_template = [ LLMNodeChatModelMessage( text="You are a classifier.", @@ -106,7 +108,7 @@ def _fetch_prompt_messages_with_mocked_content(content): with ( mock.patch( "graphon.nodes.llm.llm_utils.fetch_model_schema", - return_value=mock.MagicMock(features=[]), + return_value=model_schema, ), mock.patch( "graphon.nodes.llm.llm_utils.handle_list_messages", diff --git a/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py b/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py index b1f81b6c48..c707cf28cd 100644 --- a/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/llm/test_node.py @@ -140,8 +140,8 @@ def _build_image_file( mime_type: str = "image/png", ) -> File: return File( - id=file_id, - type=FileType.IMAGE, + file_id=file_id, + file_type=FileType.IMAGE, filename=f"{file_id}{extension}", transfer_method=FileTransferMethod.REMOTE_URL, remote_url=remote_url, @@ -205,14 +205,10 @@ def llm_node( mock_credentials_provider = mock.MagicMock(spec=CredentialsProvider) mock_model_factory = mock.MagicMock(spec=ModelFactory) mock_prompt_message_serializer = mock.MagicMock(spec=PromptMessageSerializerProtocol) - node_config = { - "id": "1", - "data": llm_node_data.model_dump(), - } http_client = mock.MagicMock() node = LLMNode( - id="1", - config=node_config, + node_id="1", + config=llm_node_data, graph_init_params=graph_init_params, graph_runtime_state=graph_runtime_state, credentials_provider=mock_credentials_provider, @@ -403,8 +399,8 @@ def test_dify_model_access_adapters_call_managers(): def test_fetch_files_with_file_segment(): file = File( - id="1", - type=FileType.IMAGE, + file_id="1", + file_type=FileType.IMAGE, filename="test.jpg", transfer_method=FileTransferMethod.LOCAL_FILE, related_id="1", @@ -420,16 +416,16 @@ def test_fetch_files_with_file_segment(): def test_fetch_files_with_array_file_segment(): files = [ File( - id="1", - type=FileType.IMAGE, + file_id="1", + file_type=FileType.IMAGE, filename="test1.jpg", transfer_method=FileTransferMethod.LOCAL_FILE, related_id="1", storage_key="", ), File( - id="2", - type=FileType.IMAGE, + file_id="2", + file_type=FileType.IMAGE, filename="test2.jpg", transfer_method=FileTransferMethod.LOCAL_FILE, related_id="2", @@ -1174,14 +1170,10 @@ def llm_node_for_multimodal(llm_node_data, graph_init_params, graph_runtime_stat mock_credentials_provider = mock.MagicMock(spec=CredentialsProvider) mock_model_factory = mock.MagicMock(spec=ModelFactory) mock_prompt_message_serializer = mock.MagicMock(spec=PromptMessageSerializerProtocol) - node_config = { - "id": "1", - "data": llm_node_data.model_dump(), - } http_client = mock.MagicMock() node = LLMNode( - id="1", - config=node_config, + node_id="1", + config=llm_node_data, graph_init_params=graph_init_params, graph_runtime_state=graph_runtime_state, credentials_provider=mock_credentials_provider, @@ -1203,8 +1195,8 @@ class TestLLMNodeSaveMultiModalImageOutput: mime_type="image/png", ) mock_file = File( - id=str(uuid.uuid4()), - type=FileType.IMAGE, + file_id=str(uuid.uuid4()), + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.TOOL_FILE, related_id=str(uuid.uuid4()), filename="test-file.png", @@ -1233,8 +1225,8 @@ class TestLLMNodeSaveMultiModalImageOutput: mime_type="image/jpg", ) mock_file = File( - id=str(uuid.uuid4()), - type=FileType.IMAGE, + file_id=str(uuid.uuid4()), + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.TOOL_FILE, related_id=str(uuid.uuid4()), filename="test-file.png", @@ -1291,8 +1283,8 @@ class TestSaveMultimodalOutputAndConvertResultToMarkdown: image_b64_data = base64.b64encode(image_raw_data).decode() mock_saved_file = File( - id=str(uuid.uuid4()), - type=FileType.IMAGE, + file_id=str(uuid.uuid4()), + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.TOOL_FILE, filename="test.png", extension=".png", @@ -1457,7 +1449,6 @@ def test_invoke_llm_dispatches_to_expected_model_method(structured_output_enable file_saver=file_saver, file_outputs=[], node_id="node-1", - node_type=LLMNode.node_type, reasoning_format="separated", ) ) @@ -1514,7 +1505,6 @@ def test_handle_invoke_result_streaming_collects_text_metrics_and_structured_out file_saver=mock.MagicMock(spec=LLMFileSaver), file_outputs=[], node_id="node-1", - node_type=LLMNode.node_type, model_instance=_build_prepared_llm_mock(), reasoning_format="separated", request_start_time=1.0, @@ -1552,7 +1542,6 @@ def test_handle_invoke_result_wraps_structured_output_parse_errors(): file_saver=mock.MagicMock(spec=LLMFileSaver), file_outputs=[], node_id="node-1", - node_type=LLMNode.node_type, model_instance=model_instance, ) ) diff --git a/api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py b/api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py index bc44ececd8..892f6cc586 100644 --- a/api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py +++ b/api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py @@ -13,6 +13,28 @@ from graphon.template_rendering import TemplateRenderError from tests.workflow_test_utils import build_test_graph_init_params +def _build_template_transform_node( + *, + node_data, + graph_init_params, + graph_runtime_state, + node_id: str = "test_node", + **kwargs, +) -> TemplateTransformNode: + typed_node_data = ( + node_data + if isinstance(node_data, TemplateTransformNodeData) + else TemplateTransformNodeData.model_validate(node_data) + ) + return TemplateTransformNode( + node_id=node_id, + config=typed_node_data, + graph_init_params=graph_init_params, + graph_runtime_state=graph_runtime_state, + **kwargs, + ) + + class TestTemplateTransformNode: """Comprehensive test suite for TemplateTransformNode.""" @@ -59,9 +81,8 @@ class TestTemplateTransformNode: def test_node_initialization(self, basic_node_data, mock_graph_runtime_state, graph_init_params): """Test that TemplateTransformNode initializes correctly.""" mock_renderer = MagicMock() - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": basic_node_data}, + node = _build_template_transform_node( + node_data=basic_node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -75,9 +96,8 @@ class TestTemplateTransformNode: def test_get_title(self, basic_node_data, mock_graph_runtime_state, graph_init_params): """Test _get_title method.""" mock_renderer = MagicMock() - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": basic_node_data}, + node = _build_template_transform_node( + node_data=basic_node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -88,9 +108,8 @@ class TestTemplateTransformNode: def test_get_description(self, basic_node_data, mock_graph_runtime_state, graph_init_params): """Test _get_description method.""" mock_renderer = MagicMock() - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": basic_node_data}, + node = _build_template_transform_node( + node_data=basic_node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -108,9 +127,8 @@ class TestTemplateTransformNode: } mock_renderer = MagicMock() - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": node_data}, + node = _build_template_transform_node( + node_data=node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -143,9 +161,8 @@ class TestTemplateTransformNode: mock_renderer = MagicMock() with pytest.raises(ValueError, match="max_output_length must be a positive integer"): - TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": basic_node_data}, + _build_template_transform_node( + node_data=basic_node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -170,9 +187,8 @@ class TestTemplateTransformNode: mock_renderer = MagicMock() mock_renderer.render_template.return_value = "Hello Alice, you are 30 years old!" - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": basic_node_data}, + node = _build_template_transform_node( + node_data=basic_node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -198,9 +214,8 @@ class TestTemplateTransformNode: mock_renderer = MagicMock() mock_renderer.render_template.return_value = "Value: " - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": node_data}, + node = _build_template_transform_node( + node_data=node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -218,9 +233,8 @@ class TestTemplateTransformNode: mock_renderer = MagicMock() mock_renderer.render_template.side_effect = TemplateRenderError("Template syntax error") - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": basic_node_data}, + node = _build_template_transform_node( + node_data=basic_node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -238,9 +252,8 @@ class TestTemplateTransformNode: mock_renderer = MagicMock() mock_renderer.render_template.return_value = "This is a very long output that exceeds the limit" - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": basic_node_data}, + node = _build_template_transform_node( + node_data=basic_node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -260,9 +273,8 @@ class TestTemplateTransformNode: mock_renderer = MagicMock() mock_renderer.render_template.return_value = "1234567890" - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": basic_node_data}, + node = _build_template_transform_node( + node_data=basic_node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -302,9 +314,8 @@ class TestTemplateTransformNode: mock_renderer = MagicMock() mock_renderer.render_template.return_value = "apple, banana, orange (Total: 3)" - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": node_data}, + node = _build_template_transform_node( + node_data=node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -375,8 +386,8 @@ class TestTemplateTransformNode: ) assert mapping == { - "node_123.var1": ["sys", "input1"], - "node_123.empty_selector": [], + "node_123.var1": ("sys", "input1"), + "node_123.empty_selector": (), } def test_extract_variable_selector_to_variable_mapping_ignores_invalid_entries(self): @@ -409,9 +420,8 @@ class TestTemplateTransformNode: mock_renderer = MagicMock() mock_renderer.render_template.return_value = "This is a static message." - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": node_data}, + node = _build_template_transform_node( + node_data=node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -448,9 +458,8 @@ class TestTemplateTransformNode: mock_renderer = MagicMock() mock_renderer.render_template.return_value = "Total: $31.5" - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": node_data}, + node = _build_template_transform_node( + node_data=node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -477,9 +486,8 @@ class TestTemplateTransformNode: mock_renderer = MagicMock() mock_renderer.render_template.return_value = "Name: John Doe, Email: john@example.com" - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": node_data}, + node = _build_template_transform_node( + node_data=node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, @@ -507,9 +515,8 @@ class TestTemplateTransformNode: mock_renderer = MagicMock() mock_renderer.render_template.return_value = "Tags: #python #ai #workflow " - node = TemplateTransformNode( - id="test_node", - config={"id": "test_node", "data": node_data}, + node = _build_template_transform_node( + node_data=node_data, graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=mock_renderer, diff --git a/api/tests/unit_tests/core/workflow/nodes/template_transform/test_template_transform_node.py b/api/tests/unit_tests/core/workflow/nodes/template_transform/test_template_transform_node.py index 636237e56e..a846efbb43 100644 --- a/api/tests/unit_tests/core/workflow/nodes/template_transform/test_template_transform_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/template_transform/test_template_transform_node.py @@ -4,6 +4,7 @@ import pytest from core.app.entities.app_invoke_entities import InvokeFrom, UserFrom from graphon.nodes.base.entities import VariableSelector +from graphon.nodes.template_transform.entities import TemplateTransformNodeData from graphon.nodes.template_transform.template_transform_node import ( DEFAULT_TEMPLATE_TRANSFORM_MAX_OUTPUT_LENGTH, TemplateTransformNode, @@ -37,15 +38,13 @@ def mock_graph_runtime_state(): def test_node_uses_default_max_output_length_when_not_overridden(graph_init_params, mock_graph_runtime_state): node = TemplateTransformNode( - id="test_node", - config={ - "id": "test_node", - "data": { - "title": "Template Transform", - "variables": [], - "template": "hello", - }, - }, + node_id="test_node", + config=TemplateTransformNodeData( + title="Template Transform", + type="template-transform", + variables=[], + template="hello", + ), graph_init_params=graph_init_params, graph_runtime_state=mock_graph_runtime_state, jinja2_template_renderer=MagicMock(), @@ -70,5 +69,5 @@ def test_extract_variable_selector_to_variable_mapping_accepts_mixed_valid_entri assert mapping == { "node_123.validated": ["sys", "input1"], - "node_123.raw": ["sys", "input2"], + "node_123.raw": ("sys", "input2"), } diff --git a/api/tests/unit_tests/core/workflow/nodes/test_base_node.py b/api/tests/unit_tests/core/workflow/nodes/test_base_node.py index 0522dd9d14..364408ead6 100644 --- a/api/tests/unit_tests/core/workflow/nodes/test_base_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/test_base_node.py @@ -7,7 +7,6 @@ from core.workflow.node_runtime import resolve_dify_run_context from core.workflow.system_variables import build_system_variables from graphon.entities import GraphInitParams from graphon.entities.base_node_data import BaseNodeData -from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter from graphon.enums import BuiltinNodeTypes from graphon.nodes.base.node import Node from graphon.runtime import GraphRuntimeState, VariablePool @@ -42,17 +41,19 @@ def _build_context(graph_config: Mapping[str, object]) -> tuple[GraphInitParams, return init_params, runtime_state -def _build_node_config() -> NodeConfigDict: - return NodeConfigDictAdapter.validate_python( - { - "id": "node-1", - "data": { - "type": BuiltinNodeTypes.ANSWER, - "title": "Sample", - "foo": "bar", - }, - } - ) +def _build_node_config() -> dict[str, object]: + return { + "id": "node-1", + "data": _SampleNodeData( + type=BuiltinNodeTypes.ANSWER, + title="Sample", + foo="bar", + ), + } + + +def _build_node_data() -> _SampleNodeData: + return _build_node_config()["data"] # type: ignore[return-value] def test_node_hydrates_data_during_initialization(): @@ -60,8 +61,8 @@ def test_node_hydrates_data_during_initialization(): init_params, runtime_state = _build_context(graph_config) node = _SampleNode( - id="node-1", - config=_build_node_config(), + node_id="node-1", + config=_build_node_data(), graph_init_params=init_params, graph_runtime_state=runtime_state, ) @@ -86,8 +87,8 @@ def test_node_accepts_invoke_from_enum(): ) node = _SampleNode( - id="node-1", - config=_build_node_config(), + node_id="node-1", + config=_build_node_data(), graph_init_params=init_params, graph_runtime_state=runtime_state, ) @@ -117,13 +118,7 @@ def test_missing_generic_argument_raises_type_error(): def test_base_node_data_keeps_dict_style_access_compatibility(): - node_data = _SampleNodeData.model_validate( - { - "type": BuiltinNodeTypes.ANSWER, - "title": "Sample", - "foo": "bar", - } - ) + node_data = _SampleNodeData(type=BuiltinNodeTypes.ANSWER, title="Sample", foo="bar") assert node_data["foo"] == "bar" assert node_data.get("foo") == "bar" @@ -133,21 +128,19 @@ def test_base_node_data_keeps_dict_style_access_compatibility(): def test_node_hydration_preserves_compatibility_extra_fields(): graph_config: dict[str, object] = {} init_params, runtime_state = _build_context(graph_config) - node_config = NodeConfigDictAdapter.validate_python( - { - "id": "node-1", - "data": { - "type": BuiltinNodeTypes.ANSWER, - "title": "Sample", - "foo": "bar", - "compat_flag": True, - }, - } - ) + node_config = { + "id": "node-1", + "data": _SampleNodeData( + type=BuiltinNodeTypes.ANSWER, + title="Sample", + foo="bar", + compat_flag=True, + ), + } node = _SampleNode( - id="node-1", - config=node_config, + node_id="node-1", + config=node_config["data"], graph_init_params=init_params, graph_runtime_state=runtime_state, ) diff --git a/api/tests/unit_tests/core/workflow/nodes/test_document_extractor_node.py b/api/tests/unit_tests/core/workflow/nodes/test_document_extractor_node.py index 87ec2d5bce..dd75b32593 100644 --- a/api/tests/unit_tests/core/workflow/nodes/test_document_extractor_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/test_document_extractor_node.py @@ -11,14 +11,16 @@ from graphon.enums import BuiltinNodeTypes, WorkflowNodeExecutionStatus from graphon.file import File, FileTransferMethod from graphon.node_events import NodeRunResult from graphon.nodes.document_extractor import DocumentExtractorNode, DocumentExtractorNodeData +from graphon.nodes.document_extractor.exc import TextExtractionError, UnsupportedFileTypeError from graphon.nodes.document_extractor.node import ( _extract_text_from_docx, _extract_text_from_excel, + _extract_text_from_file, _extract_text_from_pdf, _extract_text_from_plain_text, _normalize_docx_zip, ) -from graphon.variables import ArrayFileSegment +from graphon.variables import ArrayFileSegment, FileSegment from graphon.variables.segments import ArrayStringSegment from graphon.variables.variables import StringVariable from tests.workflow_test_utils import build_test_graph_init_params @@ -44,11 +46,10 @@ def document_extractor_node(graph_init_params): title="Test Document Extractor", variable_selector=["node_id", "variable_name"], ) - node_config = {"id": "test_node_id", "data": node_data.model_dump()} http_client = Mock() node = DocumentExtractorNode( - id="test_node_id", - config=node_config, + node_id="test_node_id", + config=node_data, graph_init_params=graph_init_params, graph_runtime_state=Mock(), http_client=http_client, @@ -341,7 +342,7 @@ def test_extract_text_from_excel_sheet_parse_error(mock_excel_file): # Mock ExcelFile mock_excel_instance = Mock() mock_excel_instance.sheet_names = ["GoodSheet", "BadSheet"] - mock_excel_instance.parse.side_effect = [df, Exception("Parse error")] + mock_excel_instance.parse.side_effect = [df, TypeError("Parse error")] mock_excel_file.return_value = mock_excel_instance file_content = b"fake_excel_mixed_content" @@ -386,7 +387,7 @@ def test_extract_text_from_excel_all_sheets_fail(mock_excel_file): # Mock ExcelFile mock_excel_instance = Mock() mock_excel_instance.sheet_names = ["BadSheet1", "BadSheet2"] - mock_excel_instance.parse.side_effect = [Exception("Error 1"), Exception("Error 2")] + mock_excel_instance.parse.side_effect = [TypeError("Error 1"), TypeError("Error 2")] mock_excel_file.return_value = mock_excel_instance file_content = b"fake_excel_all_bad_sheets" @@ -397,6 +398,12 @@ def test_extract_text_from_excel_all_sheets_fail(mock_excel_file): assert mock_excel_instance.parse.call_count == 2 +@patch("pandas.ExcelFile", side_effect=RuntimeError("broken workbook")) +def test_extract_text_from_excel_wraps_workbook_open_errors(mock_excel_file): + with pytest.raises(TextExtractionError, match="Failed to extract text from Excel file: broken workbook"): + _extract_text_from_excel(b"broken") + + @patch("pandas.ExcelFile") def test_extract_text_from_excel_numeric_type_column(mock_excel_file): """Test extracting text from Excel file with numeric column names.""" @@ -420,6 +427,103 @@ def test_extract_text_from_excel_numeric_type_column(mock_excel_file): assert expected_manual == result +@pytest.mark.parametrize( + ("extension", "mime_type"), + [ + (".xlsx", "text/plain"), + (None, "application/vnd.ms-excel"), + ], +) +def test_extract_text_from_file_routes_excel_inputs(document_extractor_node, extension, mime_type): + file = Mock(spec=File) + file.extension = extension + file.mime_type = mime_type + + with ( + patch( + "graphon.nodes.document_extractor.node._download_file_content", + return_value=b"excel", + ), + patch( + "graphon.nodes.document_extractor.node._extract_text_from_excel", + return_value="excel text", + ) as mock_extract, + ): + result = _extract_text_from_file( + document_extractor_node.http_client, + file, + unstructured_api_config=document_extractor_node._unstructured_api_config, + ) + + assert result == "excel text" + mock_extract.assert_called_once_with(b"excel") + + +def test_extract_text_from_file_rejects_missing_extension_and_mime_type(document_extractor_node): + file = Mock(spec=File) + file.extension = None + file.mime_type = None + + with patch( + "graphon.nodes.document_extractor.node._download_file_content", + return_value=b"unknown", + ): + with pytest.raises(UnsupportedFileTypeError, match="Unable to determine file type"): + _extract_text_from_file( + document_extractor_node.http_client, + file, + unstructured_api_config=document_extractor_node._unstructured_api_config, + ) + + +def test_run_list_file_extraction_error_returns_failed(document_extractor_node, mock_graph_runtime_state): + document_extractor_node.graph_runtime_state = mock_graph_runtime_state + file_list = Mock(spec=ArrayFileSegment) + file_list.value = [Mock(spec=File)] + mock_graph_runtime_state.variable_pool.get.return_value = file_list + + with patch( + "graphon.nodes.document_extractor.node._extract_text_from_file", + side_effect=TextExtractionError("bad file"), + ): + result = document_extractor_node._run() + + assert result.status == WorkflowNodeExecutionStatus.FAILED + assert result.error == "bad file" + + +def test_run_single_file_segment_extraction_error_returns_failed(document_extractor_node, mock_graph_runtime_state): + document_extractor_node.graph_runtime_state = mock_graph_runtime_state + file_segment = Mock(spec=FileSegment) + file_segment.value = Mock(spec=File) + mock_graph_runtime_state.variable_pool.get.return_value = file_segment + + with patch( + "graphon.nodes.document_extractor.node._extract_text_from_file", + side_effect=TextExtractionError("single file failed"), + ): + result = document_extractor_node._run() + + assert result.status == WorkflowNodeExecutionStatus.FAILED + assert result.error == "single file failed" + + +def test_run_single_file_segment_returns_string_output(document_extractor_node, mock_graph_runtime_state): + document_extractor_node.graph_runtime_state = mock_graph_runtime_state + file_segment = Mock(spec=FileSegment) + file_segment.value = Mock(spec=File) + mock_graph_runtime_state.variable_pool.get.return_value = file_segment + + with patch( + "graphon.nodes.document_extractor.node._extract_text_from_file", + return_value="single file text", + ): + result = document_extractor_node._run() + + assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED + assert result.outputs == {"text": "single file text"} + + def _make_docx_zip(use_backslash: bool) -> bytes: """Helper to build a minimal in-memory DOCX zip. diff --git a/api/tests/unit_tests/core/workflow/nodes/test_if_else.py b/api/tests/unit_tests/core/workflow/nodes/test_if_else.py index 782750e02e..aa9a1360b0 100644 --- a/api/tests/unit_tests/core/workflow/nodes/test_if_else.py +++ b/api/tests/unit_tests/core/workflow/nodes/test_if_else.py @@ -19,6 +19,20 @@ from graphon.variables import ArrayFileSegment from tests.workflow_test_utils import build_test_graph_init_params +def _build_if_else_node( + *, + node_data: IfElseNodeData | dict[str, object], + init_params, + graph_runtime_state, +) -> IfElseNode: + return IfElseNode( + node_id=str(uuid.uuid4()), + graph_init_params=init_params, + graph_runtime_state=graph_runtime_state, + config=node_data if isinstance(node_data, IfElseNodeData) else IfElseNodeData.model_validate(node_data), + ) + + def test_execute_if_else_result_true(): graph_config = {"edges": [], "nodes": [{"data": {"type": "start", "title": "Start"}, "id": "start"}]} @@ -61,9 +75,8 @@ def test_execute_if_else_result_true(): ) graph = Graph.init(graph_config=graph_config, node_factory=node_factory, root_node_id="start") - node_config = { - "id": "if-else", - "data": { + node = _build_if_else_node( + node_data={ "title": "123", "type": "if-else", "logical_operator": "and", @@ -104,13 +117,8 @@ def test_execute_if_else_result_true(): {"comparison_operator": "not null", "variable_selector": ["start", "not_null"]}, ], }, - } - - node = IfElseNode( - id=str(uuid.uuid4()), - graph_init_params=init_params, + init_params=init_params, graph_runtime_state=graph_runtime_state, - config=node_config, ) # Mock db.session.close() @@ -155,9 +163,8 @@ def test_execute_if_else_result_false(): ) graph = Graph.init(graph_config=graph_config, node_factory=node_factory, root_node_id="start") - node_config = { - "id": "if-else", - "data": { + node = _build_if_else_node( + node_data={ "title": "123", "type": "if-else", "logical_operator": "or", @@ -174,13 +181,8 @@ def test_execute_if_else_result_false(): }, ], }, - } - - node = IfElseNode( - id=str(uuid.uuid4()), - graph_init_params=init_params, + init_params=init_params, graph_runtime_state=graph_runtime_state, - config=node_config, ) # Mock db.session.close() @@ -222,11 +224,6 @@ def test_array_file_contains_file_name(): ], ) - node_config = { - "id": "if-else", - "data": node_data.model_dump(), - } - # Create properly configured mock for graph_init_params graph_init_params = Mock() graph_init_params.workflow_id = "test_workflow" @@ -242,17 +239,12 @@ def test_array_file_contains_file_name(): } } - node = IfElseNode( - id=str(uuid.uuid4()), - graph_init_params=graph_init_params, - graph_runtime_state=Mock(), - config=node_config, - ) + node = _build_if_else_node(node_data=node_data, init_params=graph_init_params, graph_runtime_state=Mock()) node.graph_runtime_state.variable_pool.get.return_value = ArrayFileSegment( value=[ File( - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.LOCAL_FILE, related_id="1", filename="ab", @@ -334,11 +326,10 @@ def test_execute_if_else_boolean_conditions(condition: Condition): "logical_operator": "and", "conditions": [condition.model_dump()], } - node = IfElseNode( - id=str(uuid.uuid4()), - graph_init_params=init_params, + node = _build_if_else_node( + node_data=node_data, + init_params=init_params, graph_runtime_state=graph_runtime_state, - config={"id": "if-else", "data": node_data}, ) # Mock db.session.close() @@ -400,14 +391,10 @@ def test_execute_if_else_boolean_false_conditions(): ], } - node = IfElseNode( - id=str(uuid.uuid4()), - graph_init_params=init_params, + node = _build_if_else_node( + node_data=node_data, + init_params=init_params, graph_runtime_state=graph_runtime_state, - config={ - "id": "if-else", - "data": node_data, - }, ) # Mock db.session.close() @@ -472,11 +459,10 @@ def test_execute_if_else_boolean_cases_structure(): } ], } - node = IfElseNode( - id=str(uuid.uuid4()), - graph_init_params=init_params, + node = _build_if_else_node( + node_data=node_data, + init_params=init_params, graph_runtime_state=graph_runtime_state, - config={"id": "if-else", "data": node_data}, ) # Mock db.session.close() diff --git a/api/tests/unit_tests/core/workflow/nodes/test_list_operator.py b/api/tests/unit_tests/core/workflow/nodes/test_list_operator.py index b217e4e8e7..465a4c0ff4 100644 --- a/api/tests/unit_tests/core/workflow/nodes/test_list_operator.py +++ b/api/tests/unit_tests/core/workflow/nodes/test_list_operator.py @@ -19,6 +19,15 @@ from graphon.nodes.list_operator.node import ListOperatorNode, _get_file_extract from graphon.variables import ArrayFileSegment +def _build_list_operator_node(node_data: ListOperatorNodeData, graph_init_params) -> ListOperatorNode: + return ListOperatorNode( + node_id="test_node_id", + config=node_data, + graph_init_params=graph_init_params, + graph_runtime_state=MagicMock(), + ) + + @pytest.fixture def list_operator_node(): config = { @@ -35,10 +44,6 @@ def list_operator_node(): "title": "Test Title", } node_data = ListOperatorNodeData.model_validate(config) - node_config = { - "id": "test_node_id", - "data": node_data.model_dump(), - } # Create properly configured mock for graph_init_params graph_init_params = MagicMock() graph_init_params.workflow_id = "test_workflow" @@ -54,12 +59,7 @@ def list_operator_node(): } } - node = ListOperatorNode( - id="test_node_id", - config=node_config, - graph_init_params=graph_init_params, - graph_runtime_state=MagicMock(), - ) + node = _build_list_operator_node(node_data, graph_init_params) node.graph_runtime_state = MagicMock() node.graph_runtime_state.variable_pool = MagicMock() return node @@ -70,28 +70,28 @@ def test_filter_files_by_type(list_operator_node): files = [ File( filename="image1.jpg", - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.LOCAL_FILE, related_id="related1", storage_key="", ), File( filename="document1.pdf", - type=FileType.DOCUMENT, + file_type=FileType.DOCUMENT, transfer_method=FileTransferMethod.LOCAL_FILE, related_id="related2", storage_key="", ), File( filename="image2.png", - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.LOCAL_FILE, related_id="related3", storage_key="", ), File( filename="audio1.mp3", - type=FileType.AUDIO, + file_type=FileType.AUDIO, transfer_method=FileTransferMethod.LOCAL_FILE, related_id="related4", storage_key="", @@ -136,7 +136,7 @@ def test_filter_files_by_type(list_operator_node): def test_get_file_extract_string_func(): # Create a File object file = File( - type=FileType.DOCUMENT, + file_type=FileType.DOCUMENT, transfer_method=FileTransferMethod.LOCAL_FILE, filename="test_file.txt", extension=".txt", @@ -156,7 +156,7 @@ def test_get_file_extract_string_func(): # Test with empty values empty_file = File( - type=FileType.DOCUMENT, + file_type=FileType.DOCUMENT, transfer_method=FileTransferMethod.LOCAL_FILE, filename=None, extension=None, diff --git a/api/tests/unit_tests/core/workflow/nodes/test_start_node_json_object.py b/api/tests/unit_tests/core/workflow/nodes/test_start_node_json_object.py index 543f9878de..5655f80737 100644 --- a/api/tests/unit_tests/core/workflow/nodes/test_start_node_json_object.py +++ b/api/tests/unit_tests/core/workflow/nodes/test_start_node_json_object.py @@ -22,10 +22,7 @@ def make_start_node(user_inputs, variables): inputs=user_inputs, ) - config = { - "id": "start", - "data": StartNodeData(title="Start", variables=variables).model_dump(), - } + node_data = StartNodeData(title="Start", variables=variables) graph_runtime_state = GraphRuntimeState( variable_pool=variable_pool, @@ -33,8 +30,8 @@ def make_start_node(user_inputs, variables): ) return StartNode( - id="start", - config=config, + node_id="start", + config=node_data, graph_init_params=build_test_graph_init_params( workflow_id="wf", graph_config={}, @@ -109,7 +106,7 @@ def test_json_object_invalid_json_string(): node = make_start_node(user_inputs, variables) - with pytest.raises(ValueError, match="JSON object for 'profile' must be an object"): + with pytest.raises(TypeError, match="JSON object for 'profile' must be an object"): node._run() @@ -248,25 +245,22 @@ def test_start_node_outputs_full_variable_pool_snapshot(): inputs={"profile": {"age": 20, "name": "Tom"}}, ) - config = { - "id": "start", - "data": StartNodeData( - title="Start", - variables=[ - VariableEntity( - variable="profile", - label="profile", - type=VariableEntityType.JSON_OBJECT, - required=True, - ) - ], - ).model_dump(), - } + node_data = StartNodeData( + title="Start", + variables=[ + VariableEntity( + variable="profile", + label="profile", + type=VariableEntityType.JSON_OBJECT, + required=True, + ) + ], + ) graph_runtime_state = GraphRuntimeState(variable_pool=variable_pool, start_at=time.perf_counter()) node = StartNode( - id="start", - config=config, + node_id="start", + config=node_data, graph_init_params=build_test_graph_init_params( workflow_id="wf", graph_config={}, diff --git a/api/tests/unit_tests/core/workflow/nodes/tool/test_tool_node.py b/api/tests/unit_tests/core/workflow/nodes/tool/test_tool_node.py index c806181340..284af68319 100644 --- a/api/tests/unit_tests/core/workflow/nodes/tool/test_tool_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/tool/test_tool_node.py @@ -13,6 +13,7 @@ from core.workflow.system_variables import build_system_variables from graphon.file import File, FileTransferMethod, FileType from graphon.model_runtime.entities.llm_entities import LLMUsage from graphon.node_events import StreamChunkEvent, StreamCompletedEvent +from graphon.nodes.tool.entities import ToolNodeData from graphon.nodes.tool_runtime_entities import ToolRuntimeHandle, ToolRuntimeMessage from graphon.runtime import GraphRuntimeState, VariablePool from graphon.variables.segments import ArrayFileSegment @@ -108,8 +109,8 @@ def tool_node(monkeypatch) -> ToolNode: runtime = _StubToolRuntime() node = ToolNode( - id="node-instance", - config=config, + node_id="node-instance", + config=ToolNodeData.model_validate(config["data"]), graph_init_params=init_params, graph_runtime_state=graph_runtime_state, tool_file_manager_factory=tool_file_manager_factory, @@ -118,13 +119,13 @@ def tool_node(monkeypatch) -> ToolNode: return node -def _collect_events(generator: Generator) -> tuple[list[Any], LLMUsage]: +def _collect_events(generator: Generator) -> list[Any]: events: list[Any] = [] try: while True: events.append(next(generator)) - except StopIteration as stop: - return events, stop.value + except StopIteration: + return events def _run_transform(tool_node: ToolNode, message: ToolRuntimeMessage) -> tuple[list[Any], LLMUsage]: @@ -135,12 +136,15 @@ def _run_transform(tool_node: ToolNode, message: ToolRuntimeMessage) -> tuple[li node_id=tool_node._node_id, tool_runtime=ToolRuntimeHandle(raw=object()), ) - return _collect_events(generator) + events = _collect_events(generator) + completed_events = [event for event in events if isinstance(event, StreamCompletedEvent)] + assert completed_events + return events, completed_events[-1].node_run_result.llm_usage def test_link_messages_with_file_populate_files_output(tool_node: ToolNode): file_obj = File( - type=FileType.DOCUMENT, + file_type=FileType.DOCUMENT, transfer_method=FileTransferMethod.TOOL_FILE, related_id="file-id", filename="demo.pdf", @@ -195,7 +199,7 @@ def test_plain_link_messages_remain_links(tool_node: ToolNode): def test_image_link_messages_use_tool_file_id_metadata(tool_node: ToolNode): file_obj = File( - type=FileType.DOCUMENT, + file_type=FileType.DOCUMENT, transfer_method=FileTransferMethod.TOOL_FILE, related_id="file-id", filename="demo.pdf", diff --git a/api/tests/unit_tests/core/workflow/nodes/trigger_plugin/test_trigger_event_node.py b/api/tests/unit_tests/core/workflow/nodes/trigger_plugin/test_trigger_event_node.py index c8ddc53284..e3b5e3b591 100644 --- a/api/tests/unit_tests/core/workflow/nodes/trigger_plugin/test_trigger_event_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/trigger_plugin/test_trigger_event_node.py @@ -1,10 +1,10 @@ from collections.abc import Mapping from core.trigger.constants import TRIGGER_PLUGIN_NODE_TYPE +from core.workflow.nodes.trigger_plugin.entities import TriggerEventNodeData from core.workflow.nodes.trigger_plugin.trigger_event_node import TriggerEventNode from core.workflow.system_variables import build_system_variables from graphon.entities import GraphInitParams -from graphon.entities.graph_config import NodeConfigDict, NodeConfigDictAdapter from graphon.enums import WorkflowNodeExecutionMetadataKey, WorkflowNodeExecutionStatus from graphon.runtime import GraphRuntimeState from tests.workflow_test_utils import build_test_graph_init_params, build_test_variable_pool @@ -27,29 +27,24 @@ def _build_context(graph_config: Mapping[str, object]) -> tuple[GraphInitParams, return init_params, runtime_state -def _build_node_config() -> NodeConfigDict: - return NodeConfigDictAdapter.validate_python( - { - "id": "node-1", - "data": { - "type": TRIGGER_PLUGIN_NODE_TYPE, - "title": "Trigger Event", - "plugin_id": "plugin-id", - "provider_id": "provider-id", - "event_name": "event-name", - "subscription_id": "subscription-id", - "plugin_unique_identifier": "plugin-unique-identifier", - "event_parameters": {}, - }, - } +def _build_node_data() -> TriggerEventNodeData: + return TriggerEventNodeData( + type=TRIGGER_PLUGIN_NODE_TYPE, + title="Trigger Event", + plugin_id="plugin-id", + provider_id="provider-id", + event_name="event-name", + subscription_id="subscription-id", + plugin_unique_identifier="plugin-unique-identifier", + event_parameters={}, ) def test_trigger_event_node_run_populates_trigger_info_metadata() -> None: init_params, runtime_state = _build_context(graph_config={}) node = TriggerEventNode( - id="node-1", - config=_build_node_config(), + node_id="node-1", + config=_build_node_data(), graph_init_params=init_params, graph_runtime_state=runtime_state, ) diff --git a/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_file_conversion.py b/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_file_conversion.py index 1bbc12b23f..07d03bec05 100644 --- a/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_file_conversion.py +++ b/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_file_conversion.py @@ -30,11 +30,6 @@ def create_webhook_node( tenant_id: str = "test-tenant", ) -> TriggerWebhookNode: """Helper function to create a webhook node with proper initialization.""" - node_config = { - "id": "webhook-node-1", - "data": webhook_data.model_dump(), - } - graph_init_params = GraphInitParams( workflow_id="test-workflow", graph_config={}, @@ -56,8 +51,8 @@ def create_webhook_node( ) node = TriggerWebhookNode( - id="webhook-node-1", - config=node_config, + node_id="webhook-node-1", + config=webhook_data, graph_init_params=graph_init_params, graph_runtime_state=runtime_state, ) @@ -66,10 +61,6 @@ def create_webhook_node( runtime_state.app_config = Mock() runtime_state.app_config.tenant_id = tenant_id - # Provide compatibility alias expected by node implementation - # Some nodes reference `self.node_id`; expose it as an alias to `self.id` for tests - node.node_id = node.id - return node diff --git a/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_node.py b/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_node.py index 427afa96ec..b839490d3c 100644 --- a/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_node.py +++ b/api/tests/unit_tests/core/workflow/nodes/webhook/test_webhook_node.py @@ -24,11 +24,6 @@ from tests.workflow_test_utils import build_test_variable_pool def create_webhook_node(webhook_data: WebhookData, variable_pool: VariablePool) -> TriggerWebhookNode: """Helper function to create a webhook node with proper initialization.""" - node_config = { - "id": "1", - "data": webhook_data.model_dump(), - } - graph_init_params = GraphInitParams( workflow_id="1", graph_config={}, @@ -48,8 +43,8 @@ def create_webhook_node(webhook_data: WebhookData, variable_pool: VariablePool) start_at=0, ) node = TriggerWebhookNode( - id="1", - config=node_config, + node_id="1", + config=webhook_data, graph_init_params=graph_init_params, graph_runtime_state=runtime_state, ) @@ -57,9 +52,6 @@ def create_webhook_node(webhook_data: WebhookData, variable_pool: VariablePool) # Provide tenant_id for conversion path runtime_state.app_config = type("_AppCfg", (), {"tenant_id": "1"})() - # Compatibility alias for some nodes referencing `self.node_id` - node.node_id = node.id - return node @@ -225,7 +217,7 @@ def test_webhook_node_run_with_file_params(): """Test webhook node execution with file parameter extraction.""" # Create mock file objects file1 = File( - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.LOCAL_FILE, related_id="file1", filename="image.jpg", @@ -234,7 +226,7 @@ def test_webhook_node_run_with_file_params(): ) file2 = File( - type=FileType.DOCUMENT, + file_type=FileType.DOCUMENT, transfer_method=FileTransferMethod.LOCAL_FILE, related_id="file2", filename="document.pdf", @@ -269,8 +261,19 @@ def test_webhook_node_run_with_file_params(): # Mock the node's file reference boundary to avoid DB-dependent validation on upload_file_id with patch.object(node._file_reference_factory, "build_from_mapping") as mock_file_factory: - def _to_file(*, mapping): - return File.model_validate(mapping) + def _to_file(*, mapping: dict[str, Any]) -> File: + return File( + file_id=mapping.get("id"), + file_type=FileType(mapping["type"]), + transfer_method=FileTransferMethod(mapping["transfer_method"]), + related_id=mapping.get("related_id"), + filename=mapping.get("filename"), + extension=mapping.get("extension"), + mime_type=mapping.get("mime_type"), + size=mapping.get("size", -1), + storage_key=mapping.get("storage_key", ""), + remote_url=mapping.get("url"), + ) mock_file_factory.side_effect = _to_file result = node._run() @@ -284,7 +287,7 @@ def test_webhook_node_run_with_file_params(): def test_webhook_node_run_mixed_parameters(): """Test webhook node execution with mixed parameter types.""" file_obj = File( - type=FileType.IMAGE, + file_type=FileType.IMAGE, transfer_method=FileTransferMethod.LOCAL_FILE, related_id="file1", filename="test.jpg", @@ -317,8 +320,19 @@ def test_webhook_node_run_mixed_parameters(): # Mock the node's file reference boundary to avoid DB-dependent validation on upload_file_id with patch.object(node._file_reference_factory, "build_from_mapping") as mock_file_factory: - def _to_file(*, mapping): - return File.model_validate(mapping) + def _to_file(*, mapping: dict[str, Any]) -> File: + return File( + file_id=mapping.get("id"), + file_type=FileType(mapping["type"]), + transfer_method=FileTransferMethod(mapping["transfer_method"]), + related_id=mapping.get("related_id"), + filename=mapping.get("filename"), + extension=mapping.get("extension"), + mime_type=mapping.get("mime_type"), + size=mapping.get("size", -1), + storage_key=mapping.get("storage_key", ""), + remote_url=mapping.get("url"), + ) mock_file_factory.side_effect = _to_file result = node._run() diff --git a/api/tests/unit_tests/core/workflow/test_human_input_adapter.py b/api/tests/unit_tests/core/workflow/test_human_input_adapter.py new file mode 100644 index 0000000000..8b5fceeb37 --- /dev/null +++ b/api/tests/unit_tests/core/workflow/test_human_input_adapter.py @@ -0,0 +1,350 @@ +from types import SimpleNamespace + +import pytest +from pydantic import BaseModel + +from core.workflow.human_input_adapter import ( + DeliveryMethodType, + EmailDeliveryConfig, + EmailDeliveryMethod, + EmailRecipients, + WebAppDeliveryMethod, + _WebAppDeliveryConfig, + adapt_human_input_node_data_for_graph, + adapt_node_config_for_graph, + adapt_node_data_for_graph, + is_human_input_webapp_enabled, + parse_human_input_delivery_methods, +) +from graphon.enums import BuiltinNodeTypes +from graphon.nodes.base.variable_template_parser import VariableTemplateParser + + +def test_email_delivery_config_helpers_render_and_sanitize_text() -> None: + variable_pool = SimpleNamespace( + convert_template=lambda body: SimpleNamespace(text=body.replace("{{#node.value#}}", "42")) + ) + + rendered = EmailDeliveryConfig.render_body_template( + body="Open {{#url#}} and use {{#node.value#}}", + url="https://example.com", + variable_pool=variable_pool, + ) + sanitized = EmailDeliveryConfig.sanitize_subject("Hello\r\n Team") + html = EmailDeliveryConfig.render_markdown_body( + "**Hello** [mail](mailto:test@example.com)" + ) + + assert rendered == "Open https://example.com and use 42" + assert sanitized == "Hello alert(1) Team" + assert "Hello" in html + assert " Team") - html = EmailDeliveryConfig.render_markdown_body( - "**Hello** [mail](mailto:test@example.com)" - ) - - assert rendered == "Open https://example.com and use 42" - assert sanitized == "Hello alert(1) Team" - assert "Hello" in html - assert "