From 04ad68de70232a1c3cd3fe6a340e8b5fca5f1682 Mon Sep 17 00:00:00 2001 From: lif <1835304752@qq.com> Date: Tue, 23 Dec 2025 09:45:47 +0800 Subject: [PATCH 01/15] fix(chat): reset scroll state when switching conversations (#29984) --- web/app/components/base/chat/chat/index.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/web/app/components/base/chat/chat/index.tsx b/web/app/components/base/chat/chat/index.tsx index 19c7b0da52..9864dda6ae 100644 --- a/web/app/components/base/chat/chat/index.tsx +++ b/web/app/components/base/chat/chat/index.tsx @@ -222,11 +222,16 @@ const Chat: FC = ({ return () => container.removeEventListener('scroll', setUserScrolled) }, []) - // Reset user scroll state when a new chat starts (length <= 1) + // Reset user scroll state when conversation changes or a new chat starts + // Track the first message ID to detect conversation switches (fixes #29820) + const prevFirstMessageIdRef = useRef(undefined) useEffect(() => { - if (chatList.length <= 1) + const firstMessageId = chatList[0]?.id + // Reset when: new chat (length <= 1) OR conversation switched (first message ID changed) + if (chatList.length <= 1 || (firstMessageId && prevFirstMessageIdRef.current !== firstMessageId)) userScrolledRef.current = false - }, [chatList.length]) + prevFirstMessageIdRef.current = firstMessageId + }, [chatList]) useEffect(() => { if (!sidebarCollapseState) From 52ba2a1df95b6ac6467880809e5d0022b8c774b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Tue, 23 Dec 2025 11:25:05 +0800 Subject: [PATCH 02/15] fix: invite team member display issue (#30011) --- .../header/account-setting/members-page/invite-modal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/header/account-setting/members-page/invite-modal/index.tsx b/web/app/components/header/account-setting/members-page/invite-modal/index.tsx index 14654c1196..ae18e23097 100644 --- a/web/app/components/header/account-setting/members-page/invite-modal/index.tsx +++ b/web/app/components/header/account-setting/members-page/invite-modal/index.tsx @@ -116,7 +116,7 @@ const InviteModal = ({ inputClassName='bg-transparent' onChange={setEmails} getLabel={(email, index, removeEmail) => -
+
{email}
removeEmail(index)}> × From d005689d0a8bf31f0787c4c0bd1affe675713c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Tue, 23 Dec 2025 12:26:52 +0800 Subject: [PATCH 03/15] chore: remove unused login call from activation flow (#30017) --- api/controllers/console/auth/activate.py | 9 ++-- .../console/auth/test_account_activation.py | 49 ++----------------- 2 files changed, 8 insertions(+), 50 deletions(-) diff --git a/api/controllers/console/auth/activate.py b/api/controllers/console/auth/activate.py index 6834656a7f..fe70d930fb 100644 --- a/api/controllers/console/auth/activate.py +++ b/api/controllers/console/auth/activate.py @@ -7,9 +7,9 @@ from controllers.console import console_ns from controllers.console.error import AlreadyActivateError from extensions.ext_database import db from libs.datetime_utils import naive_utc_now -from libs.helper import EmailStr, extract_remote_ip, timezone +from libs.helper import EmailStr, timezone from models import AccountStatus -from services.account_service import AccountService, RegisterService +from services.account_service import RegisterService DEFAULT_REF_TEMPLATE_SWAGGER_2_0 = "#/definitions/{model}" @@ -93,7 +93,6 @@ class ActivateApi(Resource): "ActivationResponse", { "result": fields.String(description="Operation result"), - "data": fields.Raw(description="Login token data"), }, ), ) @@ -117,6 +116,4 @@ class ActivateApi(Resource): account.initialized_at = naive_utc_now() db.session.commit() - token_pair = AccountService.login(account, ip_address=extract_remote_ip(request)) - - return {"result": "success", "data": token_pair.model_dump()} + return {"result": "success"} diff --git a/api/tests/unit_tests/controllers/console/auth/test_account_activation.py b/api/tests/unit_tests/controllers/console/auth/test_account_activation.py index 4192fb2ca7..da21e0e358 100644 --- a/api/tests/unit_tests/controllers/console/auth/test_account_activation.py +++ b/api/tests/unit_tests/controllers/console/auth/test_account_activation.py @@ -163,34 +163,17 @@ class TestActivateApi: "account": mock_account, } - @pytest.fixture - def mock_token_pair(self): - """Create mock token pair object.""" - token_pair = MagicMock() - token_pair.access_token = "access_token" - token_pair.refresh_token = "refresh_token" - token_pair.csrf_token = "csrf_token" - token_pair.model_dump.return_value = { - "access_token": "access_token", - "refresh_token": "refresh_token", - "csrf_token": "csrf_token", - } - return token_pair - @patch("controllers.console.auth.activate.RegisterService.get_invitation_if_token_valid") @patch("controllers.console.auth.activate.RegisterService.revoke_token") @patch("controllers.console.auth.activate.db") - @patch("controllers.console.auth.activate.AccountService.login") def test_successful_account_activation( self, - mock_login, mock_db, mock_revoke_token, mock_get_invitation, app, mock_invitation, mock_account, - mock_token_pair, ): """ Test successful account activation. @@ -198,12 +181,10 @@ class TestActivateApi: Verifies that: - Account is activated with user preferences - Account status is set to ACTIVE - - User is logged in after activation - Invitation token is revoked """ # Arrange mock_get_invitation.return_value = mock_invitation - mock_login.return_value = mock_token_pair # Act with app.test_request_context( @@ -230,7 +211,6 @@ class TestActivateApi: assert mock_account.initialized_at is not None mock_revoke_token.assert_called_once_with("workspace-123", "invitee@example.com", "valid_token") mock_db.session.commit.assert_called_once() - mock_login.assert_called_once() @patch("controllers.console.auth.activate.RegisterService.get_invitation_if_token_valid") def test_activation_with_invalid_token(self, mock_get_invitation, app): @@ -264,17 +244,14 @@ class TestActivateApi: @patch("controllers.console.auth.activate.RegisterService.get_invitation_if_token_valid") @patch("controllers.console.auth.activate.RegisterService.revoke_token") @patch("controllers.console.auth.activate.db") - @patch("controllers.console.auth.activate.AccountService.login") def test_activation_sets_interface_theme( self, - mock_login, mock_db, mock_revoke_token, mock_get_invitation, app, mock_invitation, mock_account, - mock_token_pair, ): """ Test that activation sets default interface theme. @@ -284,7 +261,6 @@ class TestActivateApi: """ # Arrange mock_get_invitation.return_value = mock_invitation - mock_login.return_value = mock_token_pair # Act with app.test_request_context( @@ -317,17 +293,14 @@ class TestActivateApi: @patch("controllers.console.auth.activate.RegisterService.get_invitation_if_token_valid") @patch("controllers.console.auth.activate.RegisterService.revoke_token") @patch("controllers.console.auth.activate.db") - @patch("controllers.console.auth.activate.AccountService.login") def test_activation_with_different_locales( self, - mock_login, mock_db, mock_revoke_token, mock_get_invitation, app, mock_invitation, mock_account, - mock_token_pair, language, timezone, ): @@ -341,7 +314,6 @@ class TestActivateApi: """ # Arrange mock_get_invitation.return_value = mock_invitation - mock_login.return_value = mock_token_pair # Act with app.test_request_context( @@ -367,27 +339,23 @@ class TestActivateApi: @patch("controllers.console.auth.activate.RegisterService.get_invitation_if_token_valid") @patch("controllers.console.auth.activate.RegisterService.revoke_token") @patch("controllers.console.auth.activate.db") - @patch("controllers.console.auth.activate.AccountService.login") - def test_activation_returns_token_data( + def test_activation_returns_success_response( self, - mock_login, mock_db, mock_revoke_token, mock_get_invitation, app, mock_invitation, - mock_token_pair, ): """ - Test that activation returns authentication tokens. + Test that activation returns a success response without authentication tokens. Verifies that: - - Token pair is returned in response - - All token types are included (access, refresh, csrf) + - Response contains a success result + - No token data is returned """ # Arrange mock_get_invitation.return_value = mock_invitation - mock_login.return_value = mock_token_pair # Act with app.test_request_context( @@ -406,24 +374,18 @@ class TestActivateApi: response = api.post() # Assert - assert "data" in response - assert response["data"]["access_token"] == "access_token" - assert response["data"]["refresh_token"] == "refresh_token" - assert response["data"]["csrf_token"] == "csrf_token" + assert response == {"result": "success"} @patch("controllers.console.auth.activate.RegisterService.get_invitation_if_token_valid") @patch("controllers.console.auth.activate.RegisterService.revoke_token") @patch("controllers.console.auth.activate.db") - @patch("controllers.console.auth.activate.AccountService.login") def test_activation_without_workspace_id( self, - mock_login, mock_db, mock_revoke_token, mock_get_invitation, app, mock_invitation, - mock_token_pair, ): """ Test account activation without workspace_id. @@ -434,7 +396,6 @@ class TestActivateApi: """ # Arrange mock_get_invitation.return_value = mock_invitation - mock_login.return_value = mock_token_pair # Act with app.test_request_context( From 9701a2994b7beee1bdc3f7531a62dfc3b094a54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Tue, 23 Dec 2025 14:05:21 +0800 Subject: [PATCH 04/15] chore: Translate stray Chinese comment to English (#30024) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- api/core/rag/datasource/vdb/oracle/oraclevector.py | 3 ++- api/core/rag/datasource/vdb/pyvastbase/vastbase_vector.py | 2 +- api/core/rag/index_processor/index_processor_base.py | 2 +- web/app/components/workflow/nodes/code/code-parser.ts | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/api/core/rag/datasource/vdb/oracle/oraclevector.py b/api/core/rag/datasource/vdb/oracle/oraclevector.py index d82ab89a34..cb05c22b55 100644 --- a/api/core/rag/datasource/vdb/oracle/oraclevector.py +++ b/api/core/rag/datasource/vdb/oracle/oraclevector.py @@ -289,7 +289,8 @@ class OracleVector(BaseVector): words = pseg.cut(query) current_entity = "" for word, pos in words: - if pos in {"nr", "Ng", "eng", "nz", "n", "ORG", "v"}: # nr: 人名,ns: 地名,nt: 机构名 + # `nr`: Person, `ns`: Location, `nt`: Organization + if pos in {"nr", "Ng", "eng", "nz", "n", "ORG", "v"}: current_entity += word else: if current_entity: diff --git a/api/core/rag/datasource/vdb/pyvastbase/vastbase_vector.py b/api/core/rag/datasource/vdb/pyvastbase/vastbase_vector.py index 86b6ace3f6..d080e8da58 100644 --- a/api/core/rag/datasource/vdb/pyvastbase/vastbase_vector.py +++ b/api/core/rag/datasource/vdb/pyvastbase/vastbase_vector.py @@ -213,7 +213,7 @@ class VastbaseVector(BaseVector): with self._get_cursor() as cur: cur.execute(SQL_CREATE_TABLE.format(table_name=self.table_name, dimension=dimension)) - # Vastbase 支持的向量维度取值范围为 [1,16000] + # Vastbase supports vector dimensions in the range [1, 16,000] if dimension <= 16000: cur.execute(SQL_CREATE_INDEX.format(table_name=self.table_name)) redis_client.set(collection_exist_cache_key, 1, ex=3600) diff --git a/api/core/rag/index_processor/index_processor_base.py b/api/core/rag/index_processor/index_processor_base.py index 8a28eb477a..e36b54eedd 100644 --- a/api/core/rag/index_processor/index_processor_base.py +++ b/api/core/rag/index_processor/index_processor_base.py @@ -231,7 +231,7 @@ class BaseIndexProcessor(ABC): if not filename: parsed_url = urlparse(image_url) - # unquote 处理 URL 中的中文 + # Decode percent-encoded characters in the URL path. path = unquote(parsed_url.path) filename = os.path.basename(path) diff --git a/web/app/components/workflow/nodes/code/code-parser.ts b/web/app/components/workflow/nodes/code/code-parser.ts index 86447a06e5..7550e62e96 100644 --- a/web/app/components/workflow/nodes/code/code-parser.ts +++ b/web/app/components/workflow/nodes/code/code-parser.ts @@ -31,7 +31,7 @@ export const extractReturnType = (code: string, language: CodeLanguage): OutputV if (returnIndex === -1) return {} - // return から始まる部分文字列を取得 + // Extract the substring starting with 'return'. const codeAfterReturn = codeWithoutComments.slice(returnIndex) let bracketCount = 0 From f2842da3972043e804d5426595c35883c8484b1a Mon Sep 17 00:00:00 2001 From: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Date: Tue, 23 Dec 2025 16:58:55 +0800 Subject: [PATCH 05/15] chore(web): new lint setup (#30020) Co-authored-by: yyh --- .github/workflows/autofix.yml | 21 - .gitignore | 11 - web/.gitignore | 10 + web/.oxlintrc.json | 144 - web/.storybook/preview.tsx | 2 +- web/.storybook/utils/form-story-wrapper.tsx | 9 +- web/.vscode/launch.json | 28 +- web/.vscode/settings.example.json | 66 +- web/__mocks__/provider-context.ts | 4 +- web/__tests__/check-i18n.test.ts | 8 +- web/__tests__/description-validation.test.tsx | 12 +- .../document-detail-navigation-fix.test.tsx | 29 +- web/__tests__/document-list-sorting.test.tsx | 12 +- web/__tests__/embedded-user-id-auth.test.tsx | 4 +- web/__tests__/embedded-user-id-store.test.tsx | 6 +- .../goto-anything/command-selector.test.tsx | 6 +- .../goto-anything/match-action.test.ts | 8 +- .../goto-anything/scope-command-tags.test.tsx | 17 +- .../search-error-handling.test.ts | 2 +- .../slash-command-modes.test.tsx | 8 +- web/__tests__/i18n-upload-features.test.ts | 28 +- web/__tests__/navigation-utils.test.ts | 56 +- web/__tests__/real-browser-flicker.test.tsx | 71 +- web/__tests__/unified-tags-logic.test.ts | 12 +- .../workflow-onboarding-integration.test.tsx | 32 +- .../workflow-parallel-limit.test.tsx | 15 +- web/__tests__/xss-prevention.test.tsx | 2 +- .../[appId]/develop/page.tsx | 4 +- .../(appDetailLayout)/[appId]/layout-main.tsx | 55 +- .../[appId]/overview/card-view.tsx | 44 +- .../[appId]/overview/chart-view.tsx | 62 +- .../overview/long-time-range-picker.tsx | 15 +- .../[appId]/overview/page.tsx | 2 +- .../time-range-picker/date-picker.tsx | 20 +- .../overview/time-range-picker/index.tsx | 27 +- .../time-range-picker/range-selector.tsx | 24 +- .../svg-attribute-error-reproduction.spec.tsx | 4 +- .../overview/tracing/config-button.tsx | 12 +- .../[appId]/overview/tracing/config-popup.tsx | 102 +- .../[appId]/overview/tracing/field.tsx | 13 +- .../[appId]/overview/tracing/panel.tsx | 60 +- .../tracing/provider-config-modal.tsx | 806 +++--- .../overview/tracing/provider-panel.tsx | 30 +- .../[appId]/overview/tracing/tracing-icon.tsx | 4 +- .../[appId]/workflow/page.tsx | 2 +- .../app/(appDetailLayout)/layout.tsx | 2 +- .../documents/[documentId]/page.tsx | 2 +- .../documents/[documentId]/settings/page.tsx | 2 +- .../[datasetId]/layout-main.tsx | 29 +- .../[datasetId]/pipeline/page.tsx | 2 +- .../[datasetId]/settings/page.tsx | 10 +- web/app/(commonLayout)/datasets/layout.tsx | 6 +- .../(commonLayout)/education-apply/page.tsx | 8 +- web/app/(commonLayout)/layout.tsx | 18 +- web/app/(commonLayout)/plugins/page.tsx | 4 +- web/app/(commonLayout)/tools/page.tsx | 1 + .../components/authenticated-layout.tsx | 56 +- web/app/(shareLayout)/components/splash.tsx | 35 +- .../webapp-reset-password/check-code/page.tsx | 72 +- .../webapp-reset-password/layout.tsx | 47 +- .../webapp-reset-password/page.tsx | 74 +- .../set-password/page.tsx | 61 +- .../webapp-signin/check-code/page.tsx | 91 +- .../components/external-member-sso-auth.tsx | 18 +- .../components/mail-and-code-auth.tsx | 33 +- .../components/mail-and-password-auth.tsx | 138 +- .../webapp-signin/components/sso-auth.tsx | 11 +- .../(shareLayout)/webapp-signin/layout.tsx | 38 +- .../webapp-signin/normalForm.tsx | 243 +- web/app/(shareLayout)/webapp-signin/page.tsx | 44 +- .../account-page/AvatarWithEdit.tsx | 54 +- .../account-page/email-change-modal.tsx | 118 +- .../(commonLayout)/account-page/index.tsx | 143 +- web/app/account/(commonLayout)/avatar.tsx | 38 +- .../delete-account/components/check-email.tsx | 49 +- .../delete-account/components/feed-back.tsx | 47 +- .../components/verify-email.tsx | 53 +- .../(commonLayout)/delete-account/index.tsx | 39 +- web/app/account/(commonLayout)/header.tsx | 36 +- web/app/account/(commonLayout)/layout.tsx | 14 +- web/app/account/(commonLayout)/page.tsx | 10 +- web/app/account/oauth/authorize/layout.tsx | 53 +- web/app/account/oauth/authorize/page.tsx | 80 +- web/app/activate/activateForm.tsx | 11 +- web/app/activate/page.tsx | 16 +- web/app/components/app-sidebar/app-info.tsx | 150 +- .../components/app-sidebar/app-operations.tsx | 43 +- .../app-sidebar/app-sidebar-dropdown.tsx | 54 +- web/app/components/app-sidebar/basic.tsx | 123 +- .../app-sidebar/dataset-info/dropdown.tsx | 40 +- .../app-sidebar/dataset-info/index.spec.tsx | 16 +- .../app-sidebar/dataset-info/index.tsx | 30 +- .../app-sidebar/dataset-info/menu-item.tsx | 8 +- .../app-sidebar/dataset-info/menu.tsx | 14 +- .../app-sidebar/dataset-sidebar-dropdown.tsx | 64 +- web/app/components/app-sidebar/index.tsx | 30 +- .../components/app-sidebar/navLink.spec.tsx | 6 +- web/app/components/app-sidebar/navLink.tsx | 33 +- .../sidebar-animation-issues.spec.tsx | 25 +- .../text-squeeze-fix-verification.spec.tsx | 11 +- .../components/app-sidebar/toggle-button.tsx | 26 +- .../edit-item/index.spec.tsx | 8 +- .../add-annotation-modal/edit-item/index.tsx | 12 +- .../add-annotation-modal/index.spec.tsx | 24 +- .../annotation/add-annotation-modal/index.tsx | 27 +- .../app/annotation/batch-action.spec.tsx | 4 +- .../app/annotation/batch-action.tsx | 27 +- .../csv-downloader.spec.tsx | 6 +- .../csv-downloader.tsx | 46 +- .../csv-uploader.spec.tsx | 5 +- .../csv-uploader.tsx | 34 +- .../batch-add-annotation-modal/index.spec.tsx | 12 +- .../batch-add-annotation-modal/index.tsx | 26 +- .../index.spec.tsx | 10 +- .../index.tsx | 2 +- .../edit-annotation-modal/edit-item/index.tsx | 131 +- .../edit-annotation-modal/index.spec.tsx | 3 +- .../edit-annotation-modal/index.tsx | 44 +- .../app/annotation/empty-element.spec.tsx | 2 +- .../app/annotation/empty-element.tsx | 19 +- .../components/app/annotation/filter.spec.tsx | 5 +- web/app/components/app/annotation/filter.tsx | 4 +- .../app/annotation/header-opts/index.spec.tsx | 35 +- .../app/annotation/header-opts/index.tsx | 80 +- .../components/app/annotation/index.spec.tsx | 15 +- web/app/components/app/annotation/index.tsx | 119 +- .../components/app/annotation/list.spec.tsx | 6 +- web/app/components/app/annotation/list.tsx | 58 +- .../index.spec.tsx | 10 +- .../hit-history-no-data.tsx | 8 +- .../view-annotation-modal/index.spec.tsx | 13 +- .../view-annotation-modal/index.tsx | 164 +- .../access-control-dialog.tsx | 6 +- .../access-control-item.tsx | 26 +- .../access-control.spec.tsx | 18 +- .../add-member-or-group-pop.tsx | 209 +- .../app/app-access-control/index.tsx | 94 +- .../specific-groups-or-members.tsx | 134 +- .../app/app-publisher/features-wrapper.tsx | 17 +- .../components/app/app-publisher/index.tsx | 434 +-- .../publish-with-multiple-model.tsx | 39 +- .../app/app-publisher/suggested-action.tsx | 14 +- .../app/app-publisher/version-info-modal.tsx | 87 +- .../base/feature-panel/index.tsx | 10 +- .../configuration/base/group-name/index.tsx | 10 +- .../base/operation-btn/index.spec.tsx | 12 +- .../base/operation-btn/index.tsx | 15 +- .../base/var-highlight/index.tsx | 4 +- .../cannot-query-dataset.spec.tsx | 6 +- .../warning-mask/cannot-query-dataset.tsx | 12 +- .../warning-mask/formatting-changed.spec.tsx | 6 +- .../base/warning-mask/formatting-changed.tsx | 10 +- .../warning-mask/has-not-set-api.spec.tsx | 8 +- .../base/warning-mask/has-not-set-api.tsx | 9 +- .../base/warning-mask/index.spec.tsx | 4 +- .../configuration/base/warning-mask/index.tsx | 11 +- .../config-prompt/advanced-prompt-input.tsx | 136 +- .../confirm-add-var/index.spec.tsx | 2 +- .../config-prompt/confirm-add-var/index.tsx | 28 +- .../conversation-history/edit-modal.spec.tsx | 6 +- .../conversation-history/edit-modal.tsx | 21 +- .../history-panel.spec.tsx | 2 +- .../conversation-history/history-panel.tsx | 45 +- .../config-prompt/index.spec.tsx | 10 +- .../app/configuration/config-prompt/index.tsx | 80 +- .../message-type-selector.spec.tsx | 4 +- .../config-prompt/message-type-selector.tsx | 30 +- .../prompt-editor-height-resize-wrap.spec.tsx | 2 +- .../prompt-editor-height-resize-wrap.tsx | 14 +- .../config-prompt/simple-prompt-input.tsx | 62 +- .../config-var/config-modal/config.ts | 4 +- .../config-var/config-modal/field.tsx | 12 +- .../config-var/config-modal/index.tsx | 102 +- .../config-var/config-modal/type-select.tsx | 26 +- .../config-var/config-select/index.spec.tsx | 6 +- .../config-var/config-select/index.tsx | 25 +- .../config-var/config-string/index.spec.tsx | 3 +- .../app/configuration/config-var/index.tsx | 65 +- .../config-var/input-type-icon.tsx | 2 +- .../configuration/config-var/modal-foot.tsx | 4 +- .../select-type-item/index.spec.tsx | 8 +- .../config-var/select-type-item/index.tsx | 12 +- .../config-var/select-var-type.tsx | 32 +- .../app/configuration/config-var/var-item.tsx | 36 +- .../config-vision/index.spec.tsx | 10 +- .../app/configuration/config-vision/index.tsx | 36 +- .../config-vision/param-config-content.tsx | 44 +- .../config-vision/param-config.tsx | 14 +- .../config/agent-setting-button.spec.tsx | 6 +- .../config/agent-setting-button.tsx | 10 +- .../config/agent/agent-setting/index.spec.tsx | 22 +- .../config/agent/agent-setting/index.tsx | 70 +- .../agent/agent-setting/item-panel.spec.tsx | 4 +- .../config/agent/agent-setting/item-panel.tsx | 13 +- .../config/agent/agent-tools/index.spec.tsx | 51 +- .../config/agent/agent-tools/index.tsx | 170 +- .../setting-built-in-tool.spec.tsx | 23 +- .../agent-tools/setting-built-in-tool.tsx | 120 +- .../config/agent/prompt-editor.tsx | 37 +- .../assistant-type-picker/index.spec.tsx | 6 +- .../config/assistant-type-picker/index.tsx | 60 +- .../config/automatic/automatic-btn.tsx | 10 +- .../config/automatic/get-automatic-res.tsx | 147 +- .../config/automatic/idea-output.tsx | 20 +- .../instruction-editor-in-workflow.tsx | 8 +- .../config/automatic/instruction-editor.tsx | 54 +- .../automatic/prompt-res-in-workflow.tsx | 6 +- .../config/automatic/prompt-res.tsx | 6 +- .../config/automatic/prompt-toast.tsx | 12 +- .../config/automatic/res-placeholder.tsx | 8 +- .../configuration/config/automatic/result.tsx | 89 +- .../config/automatic/version-selector.tsx | 36 +- .../code-generator/get-code-generator-res.tsx | 87 +- .../config/config-audio.spec.tsx | 6 +- .../app/configuration/config/config-audio.tsx | 34 +- .../config/config-document.spec.tsx | 6 +- .../configuration/config/config-document.tsx | 34 +- .../app/configuration/config/index.spec.tsx | 8 +- .../app/configuration/config/index.tsx | 20 +- .../configuration/ctrl-btn-group/index.tsx | 4 +- .../dataset-config/card-item/index.spec.tsx | 14 +- .../dataset-config/card-item/index.tsx | 63 +- .../dataset-config/context-var/index.spec.tsx | 9 +- .../dataset-config/context-var/index.tsx | 20 +- .../context-var/var-picker.spec.tsx | 10 +- .../dataset-config/context-var/var-picker.tsx | 78 +- .../dataset-config/index.spec.tsx | 29 +- .../configuration/dataset-config/index.tsx | 105 +- .../params-config/config-content.spec.tsx | 20 +- .../params-config/config-content.tsx | 108 +- .../params-config/index.spec.tsx | 28 +- .../dataset-config/params-config/index.tsx | 48 +- .../params-config/weighted-score.tsx | 18 +- .../dataset-config/select-dataset/index.tsx | 55 +- .../settings-modal/index.spec.tsx | 17 +- .../dataset-config/settings-modal/index.tsx | 139 +- .../settings-modal/retrieval-section.spec.tsx | 11 +- .../settings-modal/retrieval-section.tsx | 88 +- .../configuration/debug/chat-user-input.tsx | 26 +- .../debug-with-multiple-model/chat-item.tsx | 36 +- .../debug-with-multiple-model/context.tsx | 5 +- .../debug-with-multiple-model/debug-item.tsx | 55 +- .../debug-with-multiple-model/index.spec.tsx | 37 +- .../debug/debug-with-multiple-model/index.tsx | 26 +- .../model-parameter-trigger.tsx | 37 +- .../text-generation-item.tsx | 18 +- .../debug-with-single-model/index.spec.tsx | 18 +- .../debug/debug-with-single-model/index.tsx | 38 +- .../app/configuration/debug/hooks.tsx | 20 +- .../app/configuration/debug/index.tsx | 125 +- .../hooks/use-advanced-prompt-config.ts | 18 +- .../components/app/configuration/index.tsx | 235 +- .../prompt-value-panel/index.tsx | 75 +- .../tools/external-data-tool-modal.tsx | 87 +- .../app/configuration/tools/index.tsx | 77 +- .../create-app-dialog/app-card/index.spec.tsx | 6 +- .../app/create-app-dialog/app-card/index.tsx | 37 +- .../app/create-app-dialog/app-list/index.tsx | 149 +- .../create-app-dialog/app-list/sidebar.tsx | 55 +- .../app/create-app-dialog/index.spec.tsx | 76 +- .../app/create-app-dialog/index.tsx | 15 +- .../components/app/create-app-modal/index.tsx | 432 +-- .../dsl-confirm-modal.tsx | 26 +- .../app/create-from-dsl-modal/index.tsx | 90 +- .../app/create-from-dsl-modal/uploader.tsx | 38 +- .../app/duplicate-modal/index.spec.tsx | 8 +- .../components/app/duplicate-modal/index.tsx | 68 +- .../components/app/log-annotation/index.tsx | 20 +- web/app/components/app/log/empty-element.tsx | 47 +- web/app/components/app/log/filter.tsx | 27 +- web/app/components/app/log/index.tsx | 74 +- web/app/components/app/log/list.tsx | 369 +-- web/app/components/app/log/model-info.tsx | 41 +- web/app/components/app/log/var-panel.tsx | 36 +- .../overview/__tests__/toggle-logic.test.ts | 2 +- .../apikey-info-panel.test-utils.tsx | 22 +- .../app/overview/apikey-info-panel/index.tsx | 50 +- web/app/components/app/overview/app-card.tsx | 263 +- web/app/components/app/overview/app-chart.tsx | 258 +- .../app/overview/customize/index.spec.tsx | 2 +- .../app/overview/customize/index.tsx | 152 +- .../app/overview/embedded/index.tsx | 31 +- .../app/overview/settings/index.tsx | 157 +- .../components/app/overview/trigger-card.tsx | 23 +- web/app/components/app/store.ts | 4 +- .../app/switch-app-modal/index.spec.tsx | 15 +- .../components/app/switch-app-modal/index.tsx | 100 +- .../app/text-generate/item/index.tsx | 166 +- .../app/text-generate/item/result-tab.tsx | 14 +- .../app/text-generate/saved-items/index.tsx | 85 +- .../saved-items/no-data/index.tsx | 23 +- .../app/type-selector/index.spec.tsx | 4 +- .../components/app/type-selector/index.tsx | 137 +- .../app/workflow-log/detail.spec.tsx | 6 +- .../components/app/workflow-log/detail.tsx | 29 +- .../app/workflow-log/filter.spec.tsx | 8 +- .../components/app/workflow-log/filter.tsx | 30 +- .../app/workflow-log/index.spec.tsx | 20 +- web/app/components/app/workflow-log/index.tsx | 65 +- .../components/app/workflow-log/list.spec.tsx | 10 +- web/app/components/app/workflow-log/list.tsx | 139 +- .../workflow-log/trigger-by-display.spec.tsx | 8 +- .../app/workflow-log/trigger-by-display.tsx | 24 +- web/app/components/apps/app-card.spec.tsx | 124 +- web/app/components/apps/app-card.tsx | 205 +- web/app/components/apps/empty.spec.tsx | 2 +- web/app/components/apps/empty.tsx | 6 +- web/app/components/apps/footer.spec.tsx | 2 +- web/app/components/apps/footer.tsx | 30 +- .../apps/hooks/use-apps-query-state.spec.ts | 6 +- .../apps/hooks/use-apps-query-state.ts | 3 +- web/app/components/apps/index.spec.tsx | 8 +- web/app/components/apps/index.tsx | 10 +- web/app/components/apps/list.spec.tsx | 18 +- web/app/components/apps/list.tsx | 100 +- web/app/components/apps/new-app-card.spec.tsx | 34 +- web/app/components/apps/new-app-card.tsx | 23 +- .../base/action-button/index.spec.tsx | 26 +- .../components/base/action-button/index.tsx | 8 +- .../base/agent-log-modal/detail.tsx | 34 +- .../base/agent-log-modal/index.stories.tsx | 8 +- .../components/base/agent-log-modal/index.tsx | 18 +- .../base/agent-log-modal/iteration.tsx | 14 +- .../base/agent-log-modal/result.tsx | 70 +- .../base/agent-log-modal/tool-call.tsx | 29 +- .../base/agent-log-modal/tracing.tsx | 4 +- .../base/amplitude/AmplitudeProvider.tsx | 2 +- web/app/components/base/answer-icon/index.tsx | 35 +- .../base/app-icon-picker/ImageInput.tsx | 49 +- .../base/app-icon-picker/index.stories.tsx | 3 +- .../components/base/app-icon-picker/index.tsx | 108 +- .../components/base/app-icon-picker/utils.ts | 2 +- .../components/base/app-icon/index.spec.tsx | 32 +- web/app/components/base/app-icon/index.tsx | 27 +- web/app/components/base/app-unavailable.tsx | 12 +- .../base/audio-btn/audio.player.manager.ts | 1 + .../base/audio-btn/index.stories.tsx | 2 +- web/app/components/base/audio-btn/index.tsx | 27 +- .../base/audio-gallery/AudioPlayer.tsx | 26 +- .../components/base/audio-gallery/index.tsx | 3 +- .../base/auto-height-textarea/index.tsx | 51 +- .../components/base/avatar/index.stories.tsx | 5 +- web/app/components/base/avatar/index.tsx | 2 +- web/app/components/base/badge.tsx | 2 +- web/app/components/base/badge/index.tsx | 16 +- web/app/components/base/block-input/index.tsx | 45 +- web/app/components/base/button/add-button.tsx | 4 +- web/app/components/base/button/index.spec.tsx | 56 +- web/app/components/base/button/index.tsx | 10 +- .../components/base/button/sync-button.tsx | 10 +- .../base/chat/__tests__/utils.spec.ts | 4 +- .../chat/chat-with-history/chat-wrapper.tsx | 93 +- .../base/chat/chat-with-history/context.tsx | 12 +- .../chat-with-history/header-in-mobile.tsx | 52 +- .../chat/chat-with-history/header/index.tsx | 44 +- .../header/mobile-operation-dropdown.tsx | 22 +- .../chat-with-history/header/operation.tsx | 18 +- .../base/chat/chat-with-history/hooks.tsx | 44 +- .../base/chat/chat-with-history/index.tsx | 27 +- .../chat-with-history/inputs-form/content.tsx | 30 +- .../chat-with-history/inputs-form/index.tsx | 38 +- .../inputs-form/view-form-dropdown.tsx | 24 +- .../chat/chat-with-history/sidebar/index.tsx | 67 +- .../chat/chat-with-history/sidebar/item.tsx | 8 +- .../chat/chat-with-history/sidebar/list.tsx | 6 +- .../chat-with-history/sidebar/operation.tsx | 26 +- .../sidebar/rename-modal.tsx | 13 +- .../base/chat/chat/answer/agent-content.tsx | 6 +- .../base/chat/chat/answer/basic-content.tsx | 2 +- .../base/chat/chat/answer/index.stories.tsx | 36 +- .../base/chat/chat/answer/index.tsx | 44 +- .../components/base/chat/chat/answer/more.tsx | 14 +- .../base/chat/chat/answer/operation.tsx | 217 +- .../chat/chat/answer/suggested-questions.tsx | 9 +- .../base/chat/chat/answer/tool-detail.tsx | 28 +- .../chat/chat/answer/workflow-process.tsx | 37 +- .../base/chat/chat/chat-input-area/index.tsx | 45 +- .../chat/chat/chat-input-area/operation.tsx | 36 +- .../base/chat/chat/check-input-forms-hooks.ts | 2 +- .../base/chat/chat/citation/index.tsx | 22 +- .../base/chat/chat/citation/popup.tsx | 79 +- .../chat/chat/citation/progress-tooltip.tsx | 16 +- .../base/chat/chat/citation/tooltip.tsx | 12 +- .../base/chat/chat/content-switch.tsx | 11 +- web/app/components/base/chat/chat/context.tsx | 8 +- web/app/components/base/chat/chat/hooks.ts | 62 +- web/app/components/base/chat/chat/index.tsx | 59 +- .../base/chat/chat/loading-anim/index.tsx | 2 +- .../components/base/chat/chat/log/index.tsx | 6 +- .../base/chat/chat/question.stories.tsx | 10 +- .../components/base/chat/chat/question.tsx | 104 +- .../base/chat/chat/thought/index.tsx | 4 +- .../components/base/chat/chat/try-to-ask.tsx | 16 +- web/app/components/base/chat/chat/type.ts | 6 +- web/app/components/base/chat/chat/utils.ts | 4 +- .../chat/embedded-chatbot/chat-wrapper.tsx | 83 +- .../base/chat/embedded-chatbot/context.tsx | 8 +- .../chat/embedded-chatbot/header/index.tsx | 65 +- .../base/chat/embedded-chatbot/hooks.tsx | 43 +- .../base/chat/embedded-chatbot/index.tsx | 114 +- .../embedded-chatbot/inputs-form/content.tsx | 32 +- .../embedded-chatbot/inputs-form/index.tsx | 38 +- .../inputs-form/view-form-dropdown.tsx | 22 +- web/app/components/base/chat/types.ts | 12 +- web/app/components/base/chat/utils.ts | 16 +- .../components/base/checkbox-list/index.tsx | 145 +- .../checkbox/assets/indeterminate-icon.tsx | 4 +- .../base/checkbox/index.stories.tsx | 9 +- web/app/components/base/checkbox/index.tsx | 2 +- .../components/base/chip/index.stories.tsx | 15 +- web/app/components/base/chip/index.tsx | 42 +- web/app/components/base/confirm/index.tsx | 27 +- .../components/base/content-dialog/index.tsx | 15 +- .../components/base/copy-feedback/index.tsx | 17 +- web/app/components/base/copy-icon/index.tsx | 15 +- .../components/base/corner-label/index.tsx | 6 +- .../calendar/days-of-week.tsx | 4 +- .../date-and-time-picker/calendar/index.tsx | 30 +- .../date-and-time-picker/calendar/item.tsx | 8 +- .../common/option-list-item.tsx | 3 +- .../date-picker/footer.tsx | 35 +- .../date-picker/header.tsx | 30 +- .../date-picker/index.tsx | 192 +- .../base/date-and-time-picker/hooks.ts | 4 +- .../date-and-time-picker/index.stories.tsx | 16 +- .../time-picker/footer.tsx | 19 +- .../time-picker/header.tsx | 4 +- .../time-picker/index.spec.tsx | 35 +- .../time-picker/index.tsx | 107 +- .../time-picker/options.tsx | 13 +- .../base/date-and-time-picker/types.ts | 2 +- .../date-and-time-picker/utils/dayjs.spec.ts | 31 +- .../base/date-and-time-picker/utils/dayjs.ts | 20 +- .../year-and-month-picker/footer.tsx | 10 +- .../year-and-month-picker/header.tsx | 14 +- .../year-and-month-picker/options.tsx | 11 +- web/app/components/base/dialog/index.tsx | 15 +- web/app/components/base/divider/index.tsx | 31 +- .../base/drawer-plus/index.stories.tsx | 6 +- web/app/components/base/drawer-plus/index.tsx | 20 +- web/app/components/base/drawer/index.spec.tsx | 8 +- .../components/base/drawer/index.stories.tsx | 6 +- web/app/components/base/drawer/index.tsx | 76 +- .../base/dropdown/index.stories.tsx | 9 +- web/app/components/base/dropdown/index.tsx | 38 +- .../base/emoji-picker/Inner.stories.tsx | 2 +- .../components/base/emoji-picker/Inner.tsx | 218 +- .../base/emoji-picker/index.stories.tsx | 2 +- .../components/base/emoji-picker/index.tsx | 71 +- .../base/encrypted-bottom/index.tsx | 11 +- .../components/base/error-boundary/index.tsx | 56 +- web/app/components/base/features/context.tsx | 8 +- web/app/components/base/features/hooks.ts | 2 +- .../base/features/index.stories.tsx | 2 +- .../annotation-ctrl-button.tsx | 14 +- .../annotation-reply/config-param-modal.tsx | 50 +- .../annotation-reply/config-param.tsx | 8 +- .../annotation-reply/index.tsx | 59 +- .../score-slider/base-slider/index.tsx | 40 +- .../annotation-reply/score-slider/index.tsx | 8 +- .../annotation-reply/use-annotation-config.ts | 10 +- .../features/new-feature-panel/citation.tsx | 16 +- .../conversation-opener/index.tsx | 30 +- .../conversation-opener/modal.tsx | 79 +- .../new-feature-panel/dialog-wrapper.tsx | 10 +- .../new-feature-panel/feature-bar.tsx | 64 +- .../new-feature-panel/feature-card.tsx | 14 +- .../new-feature-panel/file-upload/index.tsx | 42 +- .../file-upload/setting-content.tsx | 28 +- .../file-upload/setting-modal.tsx | 13 +- .../features/new-feature-panel/follow-up.tsx | 16 +- .../new-feature-panel/image-upload/index.tsx | 50 +- .../base/features/new-feature-panel/index.tsx | 61 +- .../moderation/form-generation.tsx | 18 +- .../new-feature-panel/moderation/index.tsx | 42 +- .../moderation/moderation-content.tsx | 32 +- .../moderation/moderation-setting-modal.tsx | 117 +- .../new-feature-panel/more-like-this.tsx | 16 +- .../new-feature-panel/speech-to-text.tsx | 16 +- .../text-to-speech/index.tsx | 50 +- .../text-to-speech/param-config-content.tsx | 90 +- .../text-to-speech/voice-settings.tsx | 8 +- web/app/components/base/features/store.ts | 2 +- web/app/components/base/features/types.ts | 2 +- .../base/file-icon/index.stories.tsx | 5 +- web/app/components/base/file-icon/index.tsx | 2 +- .../base/file-thumb/image-render.tsx | 4 +- web/app/components/base/file-thumb/index.tsx | 34 +- .../base/file-uploader/audio-preview.tsx | 10 +- .../file-from-link-or-local/index.tsx | 45 +- .../base/file-uploader/file-input.tsx | 8 +- .../base/file-uploader/file-list-in-log.tsx | 38 +- .../base/file-uploader/file-list.stories.tsx | 4 +- .../base/file-uploader/file-type-icon.tsx | 6 +- .../file-uploader-in-attachment/file-item.tsx | 82 +- .../index.stories.tsx | 8 +- .../file-uploader-in-attachment/index.tsx | 32 +- .../file-image-item.tsx | 38 +- .../file-uploader-in-chat-input/file-item.tsx | 48 +- .../file-uploader-in-chat-input/file-list.tsx | 8 +- .../index.stories.tsx | 8 +- .../file-uploader-in-chat-input/index.tsx | 16 +- .../components/base/file-uploader/hooks.ts | 32 +- .../components/base/file-uploader/index.ts | 4 +- .../base/file-uploader/pdf-preview.tsx | 49 +- .../components/base/file-uploader/store.tsx | 8 +- .../base/file-uploader/utils.spec.ts | 8 +- .../components/base/file-uploader/utils.ts | 11 +- .../base/file-uploader/video-preview.tsx | 10 +- .../float-right-container/index.stories.tsx | 2 +- .../base/float-right-container/index.tsx | 2 +- .../base/form/components/base/base-field.tsx | 58 +- .../base/form/components/base/base-form.tsx | 43 +- .../base/form/components/base/index.tsx | 2 +- .../base/form/components/field/checkbox.tsx | 8 +- .../form/components/field/custom-select.tsx | 4 +- .../base/form/components/field/file-types.tsx | 10 +- .../form/components/field/file-uploader.tsx | 12 +- .../field/input-type-select/hooks.tsx | 6 +- .../field/input-type-select/index.tsx | 10 +- .../field/input-type-select/option.tsx | 6 +- .../field/input-type-select/trigger.tsx | 30 +- .../field/mixed-variable-text-input/index.tsx | 2 +- .../mixed-variable-text-input/placeholder.tsx | 19 +- .../form/components/field/number-input.tsx | 10 +- .../form/components/field/number-slider.tsx | 8 +- .../base/form/components/field/options.tsx | 10 +- .../base/form/components/field/select.tsx | 4 +- .../base/form/components/field/text-area.tsx | 10 +- .../base/form/components/field/text.tsx | 9 +- .../form/components/field/upload-method.tsx | 12 +- .../field/variable-or-constant-input.tsx | 20 +- .../components/field/variable-selector.tsx | 12 +- .../base/form/components/form/actions.tsx | 6 +- .../components/base/form/components/label.tsx | 14 +- .../base/form/form-scenarios/auth/index.tsx | 6 +- .../base/form/form-scenarios/base/field.tsx | 10 +- .../base/form/form-scenarios/base/index.tsx | 6 +- .../base/form/form-scenarios/base/types.ts | 6 +- .../base/form/form-scenarios/base/utils.ts | 3 +- .../form-scenarios/demo/contact-fields.tsx | 18 +- .../base/form/form-scenarios/demo/index.tsx | 14 +- .../form/form-scenarios/input-field/field.tsx | 7 +- .../form/form-scenarios/input-field/types.ts | 4 +- .../form/form-scenarios/input-field/utils.ts | 3 +- .../form/form-scenarios/node-panel/field.tsx | 7 +- .../base/form/hooks/use-check-validated.ts | 4 +- .../base/form/hooks/use-get-form-values.ts | 4 +- .../base/form/hooks/use-get-validators.ts | 4 +- .../components/base/form/index.stories.tsx | 15 +- web/app/components/base/form/index.tsx | 20 +- web/app/components/base/form/types.ts | 8 +- .../base/fullscreen-modal/index.tsx | 35 +- web/app/components/base/ga/index.tsx | 12 +- web/app/components/base/grid-mask/index.tsx | 4 +- .../components/base/icons/IconBase.spec.tsx | 2 +- web/app/components/base/icons/IconBase.tsx | 4 +- .../base/icons/icon-gallery.stories.tsx | 17 +- web/app/components/base/icons/script.mjs | 6 +- .../icons/src/image/llm/BaichuanTextCn.tsx | 2 +- .../base/icons/src/image/llm/Minimax.tsx | 2 +- .../base/icons/src/image/llm/MinimaxText.tsx | 2 +- .../base/icons/src/image/llm/Tongyi.tsx | 2 +- .../base/icons/src/image/llm/TongyiText.tsx | 2 +- .../base/icons/src/image/llm/TongyiTextCn.tsx | 2 +- .../base/icons/src/image/llm/Wxyy.tsx | 2 +- .../base/icons/src/image/llm/WxyyText.tsx | 2 +- .../base/icons/src/image/llm/WxyyTextCn.tsx | 2 +- .../base/icons/src/image/llm/index.ts | 10 +- .../base/icons/src/public/avatar/Robot.json | 180 +- .../base/icons/src/public/avatar/Robot.tsx | 8 +- .../base/icons/src/public/avatar/User.json | 174 +- .../base/icons/src/public/avatar/User.tsx | 8 +- .../icons/src/public/billing/ArCube1.json | 54 +- .../base/icons/src/public/billing/ArCube1.tsx | 8 +- .../icons/src/public/billing/Asterisk.json | 72 +- .../icons/src/public/billing/Asterisk.tsx | 8 +- .../public/billing/AwsMarketplaceDark.json | 354 +-- .../src/public/billing/AwsMarketplaceDark.tsx | 8 +- .../public/billing/AwsMarketplaceLight.json | 354 +-- .../public/billing/AwsMarketplaceLight.tsx | 8 +- .../base/icons/src/public/billing/Azure.json | 382 +-- .../base/icons/src/public/billing/Azure.tsx | 8 +- .../icons/src/public/billing/Buildings.json | 74 +- .../icons/src/public/billing/Buildings.tsx | 8 +- .../icons/src/public/billing/Diamond.json | 74 +- .../base/icons/src/public/billing/Diamond.tsx | 8 +- .../icons/src/public/billing/GoogleCloud.json | 128 +- .../icons/src/public/billing/GoogleCloud.tsx | 8 +- .../base/icons/src/public/billing/Group2.json | 54 +- .../base/icons/src/public/billing/Group2.tsx | 8 +- .../icons/src/public/billing/Keyframe.json | 52 +- .../icons/src/public/billing/Keyframe.tsx | 8 +- .../icons/src/public/billing/Sparkles.json | 186 +- .../icons/src/public/billing/Sparkles.tsx | 8 +- .../src/public/billing/SparklesSoft.json | 68 +- .../icons/src/public/billing/SparklesSoft.tsx | 8 +- .../base/icons/src/public/billing/index.ts | 2 +- .../base/icons/src/public/common/D.json | 246 +- .../base/icons/src/public/common/D.tsx | 8 +- .../public/common/DiagonalDividingLine.json | 52 +- .../public/common/DiagonalDividingLine.tsx | 8 +- .../base/icons/src/public/common/Dify.json | 120 +- .../base/icons/src/public/common/Dify.tsx | 8 +- .../base/icons/src/public/common/Gdpr.json | 676 ++--- .../base/icons/src/public/common/Gdpr.tsx | 8 +- .../base/icons/src/public/common/Github.json | 68 +- .../base/icons/src/public/common/Github.tsx | 8 +- .../icons/src/public/common/Highlight.json | 130 +- .../icons/src/public/common/Highlight.tsx | 8 +- .../base/icons/src/public/common/Iso.json | 238 +- .../base/icons/src/public/common/Iso.tsx | 8 +- .../base/icons/src/public/common/Line3.json | 52 +- .../base/icons/src/public/common/Line3.tsx | 8 +- .../base/icons/src/public/common/Lock.json | 72 +- .../base/icons/src/public/common/Lock.tsx | 8 +- .../src/public/common/MessageChatSquare.json | 70 +- .../src/public/common/MessageChatSquare.tsx | 8 +- .../src/public/common/MultiPathRetrieval.json | 302 +- .../src/public/common/MultiPathRetrieval.tsx | 8 +- .../src/public/common/NTo1Retrieval.json | 288 +- .../icons/src/public/common/NTo1Retrieval.tsx | 8 +- .../base/icons/src/public/common/Notion.json | 162 +- .../base/icons/src/public/common/Notion.tsx | 8 +- .../base/icons/src/public/common/Soc2.json | 1872 ++++++------ .../base/icons/src/public/common/Soc2.tsx | 8 +- .../icons/src/public/common/SparklesSoft.json | 90 +- .../icons/src/public/common/SparklesSoft.tsx | 8 +- .../src/public/common/SparklesSoftAccent.json | 68 +- .../src/public/common/SparklesSoftAccent.tsx | 8 +- .../base/icons/src/public/common/index.ts | 4 +- .../icons/src/public/education/Triangle.json | 50 +- .../icons/src/public/education/Triangle.tsx | 8 +- .../base/icons/src/public/files/Csv.json | 358 +-- .../base/icons/src/public/files/Csv.tsx | 8 +- .../base/icons/src/public/files/Doc.json | 334 +-- .../base/icons/src/public/files/Doc.tsx | 8 +- .../base/icons/src/public/files/Docx.json | 352 +-- .../base/icons/src/public/files/Docx.tsx | 8 +- .../base/icons/src/public/files/Html.json | 352 +-- .../base/icons/src/public/files/Html.tsx | 8 +- .../base/icons/src/public/files/Json.json | 352 +-- .../base/icons/src/public/files/Json.tsx | 8 +- .../base/icons/src/public/files/Md.json | 284 +- .../base/icons/src/public/files/Md.tsx | 8 +- .../base/icons/src/public/files/Pdf.json | 334 +-- .../base/icons/src/public/files/Pdf.tsx | 8 +- .../base/icons/src/public/files/Txt.json | 356 +-- .../base/icons/src/public/files/Txt.tsx | 8 +- .../base/icons/src/public/files/Unknown.json | 394 +-- .../base/icons/src/public/files/Unknown.tsx | 8 +- .../base/icons/src/public/files/Xlsx.json | 286 +- .../base/icons/src/public/files/Xlsx.tsx | 8 +- .../base/icons/src/public/files/Yaml.json | 358 +-- .../base/icons/src/public/files/Yaml.tsx | 8 +- .../base/icons/src/public/knowledge/File.json | 70 +- .../base/icons/src/public/knowledge/File.tsx | 8 +- .../knowledge/OptionCardEffectBlue.json | 176 +- .../public/knowledge/OptionCardEffectBlue.tsx | 8 +- .../knowledge/OptionCardEffectBlueLight.json | 176 +- .../knowledge/OptionCardEffectBlueLight.tsx | 8 +- .../knowledge/OptionCardEffectOrange.json | 176 +- .../knowledge/OptionCardEffectOrange.tsx | 8 +- .../knowledge/OptionCardEffectPurple.json | 176 +- .../knowledge/OptionCardEffectPurple.tsx | 8 +- .../knowledge/OptionCardEffectTeal.json | 174 +- .../public/knowledge/OptionCardEffectTeal.tsx | 8 +- .../src/public/knowledge/SelectionMod.json | 228 +- .../src/public/knowledge/SelectionMod.tsx | 8 +- .../src/public/knowledge/Watercrawl.json | 372 +-- .../icons/src/public/knowledge/Watercrawl.tsx | 8 +- .../dataset-card/ExternalKnowledgeBase.json | 470 +-- .../dataset-card/ExternalKnowledgeBase.tsx | 8 +- .../knowledge/dataset-card/General.json | 910 +++--- .../public/knowledge/dataset-card/General.tsx | 8 +- .../public/knowledge/dataset-card/Graph.json | 2112 +++++++------- .../public/knowledge/dataset-card/Graph.tsx | 8 +- .../knowledge/dataset-card/ParentChild.json | 692 ++--- .../knowledge/dataset-card/ParentChild.tsx | 8 +- .../src/public/knowledge/dataset-card/Qa.json | 470 +-- .../src/public/knowledge/dataset-card/Qa.tsx | 8 +- .../base/icons/src/public/knowledge/index.ts | 2 +- .../knowledge/online-drive/BucketsBlue.json | 1178 ++++---- .../knowledge/online-drive/BucketsBlue.tsx | 8 +- .../knowledge/online-drive/BucketsGray.json | 1178 ++++---- .../knowledge/online-drive/BucketsGray.tsx | 8 +- .../public/knowledge/online-drive/Folder.json | 2176 +++++++------- .../public/knowledge/online-drive/Folder.tsx | 8 +- .../base/icons/src/public/llm/Anthropic.json | 70 +- .../base/icons/src/public/llm/Anthropic.tsx | 8 +- .../icons/src/public/llm/AnthropicDark.json | 2088 +++++++------- .../icons/src/public/llm/AnthropicDark.tsx | 8 +- .../icons/src/public/llm/AnthropicLight.json | 2088 +++++++------- .../icons/src/public/llm/AnthropicLight.tsx | 8 +- .../icons/src/public/llm/AnthropicText.json | 1074 +++---- .../icons/src/public/llm/AnthropicText.tsx | 8 +- .../src/public/llm/AzureOpenaiService.json | 144 +- .../src/public/llm/AzureOpenaiService.tsx | 8 +- .../public/llm/AzureOpenaiServiceText.json | 468 +-- .../src/public/llm/AzureOpenaiServiceText.tsx | 8 +- .../base/icons/src/public/llm/Azureai.json | 356 +-- .../base/icons/src/public/llm/Azureai.tsx | 8 +- .../icons/src/public/llm/AzureaiText.json | 482 ++-- .../base/icons/src/public/llm/AzureaiText.tsx | 8 +- .../base/icons/src/public/llm/Baichuan.json | 148 +- .../base/icons/src/public/llm/Baichuan.tsx | 8 +- .../icons/src/public/llm/BaichuanText.json | 308 +- .../icons/src/public/llm/BaichuanText.tsx | 8 +- .../base/icons/src/public/llm/Chatglm.json | 140 +- .../base/icons/src/public/llm/Chatglm.tsx | 8 +- .../icons/src/public/llm/ChatglmText.json | 266 +- .../base/icons/src/public/llm/ChatglmText.tsx | 8 +- .../base/icons/src/public/llm/Cohere.json | 220 +- .../base/icons/src/public/llm/Cohere.tsx | 8 +- .../base/icons/src/public/llm/CohereText.json | 176 +- .../base/icons/src/public/llm/CohereText.tsx | 8 +- .../base/icons/src/public/llm/Gpt3.json | 98 +- .../base/icons/src/public/llm/Gpt3.tsx | 8 +- .../base/icons/src/public/llm/Gpt4.json | 98 +- .../base/icons/src/public/llm/Gpt4.tsx | 8 +- .../icons/src/public/llm/Huggingface.json | 312 +- .../base/icons/src/public/llm/Huggingface.tsx | 8 +- .../icons/src/public/llm/HuggingfaceText.json | 640 ++--- .../icons/src/public/llm/HuggingfaceText.tsx | 8 +- .../src/public/llm/HuggingfaceTextHub.json | 696 ++--- .../src/public/llm/HuggingfaceTextHub.tsx | 8 +- .../icons/src/public/llm/IflytekSpark.json | 84 +- .../icons/src/public/llm/IflytekSpark.tsx | 8 +- .../src/public/llm/IflytekSparkText.json | 370 +-- .../icons/src/public/llm/IflytekSparkText.tsx | 8 +- .../src/public/llm/IflytekSparkTextCn.json | 192 +- .../src/public/llm/IflytekSparkTextCn.tsx | 8 +- .../base/icons/src/public/llm/Jina.json | 66 +- .../base/icons/src/public/llm/Jina.tsx | 8 +- .../base/icons/src/public/llm/JinaText.json | 160 +- .../base/icons/src/public/llm/JinaText.tsx | 8 +- .../base/icons/src/public/llm/Localai.json | 210 +- .../base/icons/src/public/llm/Localai.tsx | 8 +- .../icons/src/public/llm/LocalaiText.json | 336 +-- .../base/icons/src/public/llm/LocalaiText.tsx | 8 +- .../base/icons/src/public/llm/Microsoft.json | 148 +- .../base/icons/src/public/llm/Microsoft.tsx | 8 +- .../icons/src/public/llm/OpenaiBlack.json | 70 +- .../base/icons/src/public/llm/OpenaiBlack.tsx | 8 +- .../base/icons/src/public/llm/OpenaiBlue.json | 70 +- .../base/icons/src/public/llm/OpenaiBlue.tsx | 8 +- .../icons/src/public/llm/OpenaiGreen.json | 70 +- .../base/icons/src/public/llm/OpenaiGreen.tsx | 8 +- .../base/icons/src/public/llm/OpenaiTeal.json | 70 +- .../base/icons/src/public/llm/OpenaiTeal.tsx | 8 +- .../base/icons/src/public/llm/OpenaiText.json | 150 +- .../base/icons/src/public/llm/OpenaiText.tsx | 8 +- .../src/public/llm/OpenaiTransparent.json | 48 +- .../src/public/llm/OpenaiTransparent.tsx | 8 +- .../icons/src/public/llm/OpenaiViolet.json | 70 +- .../icons/src/public/llm/OpenaiViolet.tsx | 8 +- .../icons/src/public/llm/OpenaiYellow.json | 70 +- .../icons/src/public/llm/OpenaiYellow.tsx | 8 +- .../base/icons/src/public/llm/Openllm.json | 162 +- .../base/icons/src/public/llm/Openllm.tsx | 8 +- .../icons/src/public/llm/OpenllmText.json | 282 +- .../base/icons/src/public/llm/OpenllmText.tsx | 8 +- .../base/icons/src/public/llm/Replicate.json | 74 +- .../base/icons/src/public/llm/Replicate.tsx | 8 +- .../icons/src/public/llm/ReplicateText.json | 228 +- .../icons/src/public/llm/ReplicateText.tsx | 8 +- .../src/public/llm/XorbitsInference.json | 348 +-- .../icons/src/public/llm/XorbitsInference.tsx | 8 +- .../src/public/llm/XorbitsInferenceText.json | 654 ++--- .../src/public/llm/XorbitsInferenceText.tsx | 8 +- .../base/icons/src/public/llm/Zhipuai.json | 102 +- .../base/icons/src/public/llm/Zhipuai.tsx | 8 +- .../icons/src/public/llm/ZhipuaiText.json | 84 +- .../base/icons/src/public/llm/ZhipuaiText.tsx | 8 +- .../icons/src/public/llm/ZhipuaiTextCn.json | 120 +- .../icons/src/public/llm/ZhipuaiTextCn.tsx | 8 +- .../base/icons/src/public/llm/index.ts | 36 +- .../base/icons/src/public/model/Checked.json | 54 +- .../base/icons/src/public/model/Checked.tsx | 8 +- .../src/public/other/DefaultToolIcon.json | 158 +- .../src/public/other/DefaultToolIcon.tsx | 8 +- .../icons/src/public/other/Icon3Dots.json | 54 +- .../base/icons/src/public/other/Icon3Dots.tsx | 8 +- .../icons/src/public/other/Message3Fill.json | 342 +-- .../icons/src/public/other/Message3Fill.tsx | 8 +- .../icons/src/public/other/RowStruct.json | 108 +- .../base/icons/src/public/other/RowStruct.tsx | 8 +- .../base/icons/src/public/other/index.ts | 2 +- .../base/icons/src/public/plugins/Google.json | 102 +- .../base/icons/src/public/plugins/Google.tsx | 8 +- .../icons/src/public/plugins/PartnerDark.json | 890 +++--- .../icons/src/public/plugins/PartnerDark.tsx | 8 +- .../src/public/plugins/PartnerLight.json | 888 +++--- .../icons/src/public/plugins/PartnerLight.tsx | 8 +- .../src/public/plugins/VerifiedDark.json | 910 +++--- .../icons/src/public/plugins/VerifiedDark.tsx | 8 +- .../src/public/plugins/VerifiedLight.json | 908 +++--- .../src/public/plugins/VerifiedLight.tsx | 8 +- .../icons/src/public/plugins/WebReader.json | 74 +- .../icons/src/public/plugins/WebReader.tsx | 8 +- .../icons/src/public/plugins/Wikipedia.json | 48 +- .../icons/src/public/plugins/Wikipedia.tsx | 8 +- .../icons/src/public/thought/DataSet.json | 124 +- .../base/icons/src/public/thought/DataSet.tsx | 8 +- .../icons/src/public/thought/Loading.json | 124 +- .../base/icons/src/public/thought/Loading.tsx | 8 +- .../base/icons/src/public/thought/Search.json | 124 +- .../base/icons/src/public/thought/Search.tsx | 8 +- .../icons/src/public/thought/ThoughtList.json | 162 +- .../icons/src/public/thought/ThoughtList.tsx | 8 +- .../icons/src/public/thought/WebReader.json | 124 +- .../icons/src/public/thought/WebReader.tsx | 8 +- .../icons/src/public/tracing/AliyunIcon.json | 254 +- .../icons/src/public/tracing/AliyunIcon.tsx | 8 +- .../src/public/tracing/AliyunIconBig.json | 152 +- .../src/public/tracing/AliyunIconBig.tsx | 8 +- .../icons/src/public/tracing/ArizeIcon.json | 240 +- .../icons/src/public/tracing/ArizeIcon.tsx | 8 +- .../src/public/tracing/ArizeIconBig.json | 240 +- .../icons/src/public/tracing/ArizeIconBig.tsx | 8 +- .../src/public/tracing/DatabricksIcon.json | 266 +- .../src/public/tracing/DatabricksIcon.tsx | 8 +- .../src/public/tracing/DatabricksIconBig.json | 266 +- .../src/public/tracing/DatabricksIconBig.tsx | 8 +- .../src/public/tracing/LangfuseIcon.json | 468 +-- .../icons/src/public/tracing/LangfuseIcon.tsx | 8 +- .../src/public/tracing/LangfuseIconBig.json | 468 +-- .../src/public/tracing/LangfuseIconBig.tsx | 8 +- .../src/public/tracing/LangsmithIcon.json | 372 +-- .../src/public/tracing/LangsmithIcon.tsx | 8 +- .../src/public/tracing/LangsmithIconBig.json | 372 +-- .../src/public/tracing/LangsmithIconBig.tsx | 8 +- .../icons/src/public/tracing/MlflowIcon.json | 212 +- .../icons/src/public/tracing/MlflowIcon.tsx | 8 +- .../src/public/tracing/MlflowIconBig.json | 212 +- .../src/public/tracing/MlflowIconBig.tsx | 8 +- .../icons/src/public/tracing/OpikIcon.json | 322 +-- .../icons/src/public/tracing/OpikIcon.tsx | 8 +- .../icons/src/public/tracing/OpikIconBig.json | 320 +-- .../icons/src/public/tracing/OpikIconBig.tsx | 8 +- .../icons/src/public/tracing/PhoenixIcon.json | 1702 +++++------ .../icons/src/public/tracing/PhoenixIcon.tsx | 8 +- .../src/public/tracing/PhoenixIconBig.json | 1702 +++++------ .../src/public/tracing/PhoenixIconBig.tsx | 8 +- .../icons/src/public/tracing/TencentIcon.tsx | 8 +- .../src/public/tracing/TencentIconBig.tsx | 8 +- .../icons/src/public/tracing/TracingIcon.json | 90 +- .../icons/src/public/tracing/TracingIcon.tsx | 8 +- .../icons/src/public/tracing/WeaveIcon.json | 554 ++-- .../icons/src/public/tracing/WeaveIcon.tsx | 8 +- .../src/public/tracing/WeaveIconBig.json | 554 ++-- .../icons/src/public/tracing/WeaveIconBig.tsx | 8 +- .../base/icons/src/public/tracing/index.ts | 22 +- .../icons/src/vender/features/Citations.json | 48 +- .../icons/src/vender/features/Citations.tsx | 8 +- .../vender/features/ContentModeration.json | 52 +- .../src/vender/features/ContentModeration.tsx | 8 +- .../icons/src/vender/features/Document.json | 42 +- .../icons/src/vender/features/Document.tsx | 8 +- .../src/vender/features/FolderUpload.json | 48 +- .../src/vender/features/FolderUpload.tsx | 8 +- .../src/vender/features/LoveMessage.json | 48 +- .../icons/src/vender/features/LoveMessage.tsx | 8 +- .../src/vender/features/MessageFast.json | 52 +- .../icons/src/vender/features/MessageFast.tsx | 8 +- .../src/vender/features/Microphone01.json | 70 +- .../src/vender/features/Microphone01.tsx | 8 +- .../src/vender/features/TextToAudio.json | 150 +- .../icons/src/vender/features/TextToAudio.tsx | 8 +- .../src/vender/features/VirtualAssistant.json | 66 +- .../src/vender/features/VirtualAssistant.tsx | 8 +- .../icons/src/vender/features/Vision.json | 52 +- .../base/icons/src/vender/features/Vision.tsx | 8 +- .../icons/src/vender/knowledge/AddChunks.json | 138 +- .../icons/src/vender/knowledge/AddChunks.tsx | 8 +- .../src/vender/knowledge/ApiAggregate.json | 48 +- .../src/vender/knowledge/ApiAggregate.tsx | 8 +- .../src/vender/knowledge/ArrowShape.json | 50 +- .../icons/src/vender/knowledge/ArrowShape.tsx | 8 +- .../icons/src/vender/knowledge/Chunk.json | 228 +- .../base/icons/src/vender/knowledge/Chunk.tsx | 8 +- .../icons/src/vender/knowledge/Collapse.json | 120 +- .../icons/src/vender/knowledge/Collapse.tsx | 8 +- .../icons/src/vender/knowledge/Divider.json | 54 +- .../icons/src/vender/knowledge/Divider.tsx | 8 +- .../icons/src/vender/knowledge/Economic.json | 106 +- .../icons/src/vender/knowledge/Economic.tsx | 8 +- .../src/vender/knowledge/FullTextSearch.json | 108 +- .../src/vender/knowledge/FullTextSearch.tsx | 8 +- .../src/vender/knowledge/GeneralChunk.json | 198 +- .../src/vender/knowledge/GeneralChunk.tsx | 8 +- .../src/vender/knowledge/HighQuality.json | 68 +- .../src/vender/knowledge/HighQuality.tsx | 8 +- .../src/vender/knowledge/HybridSearch.json | 228 +- .../src/vender/knowledge/HybridSearch.tsx | 8 +- .../vender/knowledge/ParentChildChunk.json | 120 +- .../src/vender/knowledge/ParentChildChunk.tsx | 8 +- .../vender/knowledge/QuestionAndAnswer.json | 72 +- .../vender/knowledge/QuestionAndAnswer.tsx | 8 +- .../src/vender/knowledge/SearchMenu.json | 150 +- .../icons/src/vender/knowledge/SearchMenu.tsx | 8 +- .../src/vender/knowledge/VectorSearch.json | 228 +- .../src/vender/knowledge/VectorSearch.tsx | 8 +- .../line/alertsAndFeedback/AlertTriangle.json | 74 +- .../line/alertsAndFeedback/AlertTriangle.tsx | 8 +- .../line/alertsAndFeedback/ThumbsDown.json | 128 +- .../line/alertsAndFeedback/ThumbsDown.tsx | 8 +- .../line/alertsAndFeedback/ThumbsUp.json | 128 +- .../line/alertsAndFeedback/ThumbsUp.tsx | 8 +- .../line/alertsAndFeedback/Warning.json | 48 +- .../vender/line/alertsAndFeedback/Warning.tsx | 8 +- .../vender/line/arrows/ArrowNarrowLeft.json | 54 +- .../vender/line/arrows/ArrowNarrowLeft.tsx | 8 +- .../src/vender/line/arrows/ArrowUpRight.json | 74 +- .../src/vender/line/arrows/ArrowUpRight.tsx | 8 +- .../vender/line/arrows/ChevronDownDouble.json | 74 +- .../vender/line/arrows/ChevronDownDouble.tsx | 8 +- .../src/vender/line/arrows/ChevronRight.json | 74 +- .../src/vender/line/arrows/ChevronRight.tsx | 8 +- .../line/arrows/ChevronSelectorVertical.json | 54 +- .../line/arrows/ChevronSelectorVertical.tsx | 8 +- .../icons/src/vender/line/arrows/IconR.json | 48 +- .../icons/src/vender/line/arrows/IconR.tsx | 8 +- .../src/vender/line/arrows/RefreshCcw01.json | 54 +- .../src/vender/line/arrows/RefreshCcw01.tsx | 8 +- .../src/vender/line/arrows/RefreshCw05.json | 54 +- .../src/vender/line/arrows/RefreshCw05.tsx | 8 +- .../src/vender/line/arrows/ReverseLeft.json | 74 +- .../src/vender/line/arrows/ReverseLeft.tsx | 8 +- .../icons/src/vender/line/arrows/index.ts | 2 +- .../src/vender/line/communication/AiText.json | 74 +- .../src/vender/line/communication/AiText.tsx | 8 +- .../vender/line/communication/ChatBot.json | 182 +- .../src/vender/line/communication/ChatBot.tsx | 8 +- .../line/communication/ChatBotSlim.json | 132 +- .../vender/line/communication/ChatBotSlim.tsx | 8 +- .../vender/line/communication/CuteRobot.json | 74 +- .../vender/line/communication/CuteRobot.tsx | 8 +- .../communication/MessageCheckRemove.json | 74 +- .../line/communication/MessageCheckRemove.tsx | 8 +- .../line/communication/MessageFastPlus.json | 54 +- .../line/communication/MessageFastPlus.tsx | 8 +- .../src/vender/line/communication/index.ts | 2 +- .../line/development/ArtificialBrain.json | 54 +- .../line/development/ArtificialBrain.tsx | 8 +- .../line/development/BarChartSquare02.json | 74 +- .../line/development/BarChartSquare02.tsx | 8 +- .../vender/line/development/BracketsX.json | 54 +- .../src/vender/line/development/BracketsX.tsx | 8 +- .../vender/line/development/CodeBrowser.json | 74 +- .../vender/line/development/CodeBrowser.tsx | 8 +- .../vender/line/development/Container.json | 54 +- .../src/vender/line/development/Container.tsx | 8 +- .../vender/line/development/Database01.json | 54 +- .../vender/line/development/Database01.tsx | 8 +- .../vender/line/development/Database03.json | 54 +- .../vender/line/development/Database03.tsx | 8 +- .../vender/line/development/FileHeart02.json | 100 +- .../vender/line/development/FileHeart02.tsx | 8 +- .../vender/line/development/GitBranch01.json | 74 +- .../vender/line/development/GitBranch01.tsx | 8 +- .../line/development/PromptEngineering.json | 126 +- .../line/development/PromptEngineering.tsx | 8 +- .../line/development/PuzzlePiece01.json | 128 +- .../vender/line/development/PuzzlePiece01.tsx | 8 +- .../line/development/TerminalSquare.json | 74 +- .../line/development/TerminalSquare.tsx | 8 +- .../src/vender/line/development/Variable.json | 120 +- .../src/vender/line/development/Variable.tsx | 8 +- .../src/vender/line/development/Webhooks.json | 174 +- .../src/vender/line/development/Webhooks.tsx | 8 +- .../src/vender/line/editor/AlignLeft.json | 74 +- .../src/vender/line/editor/AlignLeft.tsx | 8 +- .../src/vender/line/editor/BezierCurve03.json | 72 +- .../src/vender/line/editor/BezierCurve03.tsx | 8 +- .../src/vender/line/editor/Collapse.json | 120 +- .../icons/src/vender/line/editor/Collapse.tsx | 8 +- .../icons/src/vender/line/editor/Colors.json | 74 +- .../icons/src/vender/line/editor/Colors.tsx | 8 +- .../vender/line/editor/ImageIndentLeft.json | 74 +- .../vender/line/editor/ImageIndentLeft.tsx | 8 +- .../src/vender/line/editor/LeftIndent02.json | 54 +- .../src/vender/line/editor/LeftIndent02.tsx | 8 +- .../vender/line/editor/LetterSpacing01.json | 74 +- .../vender/line/editor/LetterSpacing01.tsx | 8 +- .../src/vender/line/editor/TypeSquare.json | 72 +- .../src/vender/line/editor/TypeSquare.tsx | 8 +- .../src/vender/line/education/BookOpen01.json | 94 +- .../src/vender/line/education/BookOpen01.tsx | 8 +- .../icons/src/vender/line/files/Copy.json | 54 +- .../base/icons/src/vender/line/files/Copy.tsx | 8 +- .../src/vender/line/files/CopyCheck.json | 54 +- .../icons/src/vender/line/files/CopyCheck.tsx | 8 +- .../icons/src/vender/line/files/File02.json | 74 +- .../icons/src/vender/line/files/File02.tsx | 8 +- .../src/vender/line/files/FileArrow01.json | 74 +- .../src/vender/line/files/FileArrow01.tsx | 8 +- .../src/vender/line/files/FileCheck02.json | 74 +- .../src/vender/line/files/FileCheck02.tsx | 8 +- .../src/vender/line/files/FileDownload02.json | 54 +- .../src/vender/line/files/FileDownload02.tsx | 8 +- .../src/vender/line/files/FilePlus01.json | 74 +- .../src/vender/line/files/FilePlus01.tsx | 8 +- .../src/vender/line/files/FilePlus02.json | 54 +- .../src/vender/line/files/FilePlus02.tsx | 8 +- .../icons/src/vender/line/files/FileText.json | 74 +- .../icons/src/vender/line/files/FileText.tsx | 8 +- .../src/vender/line/files/FileUpload.json | 100 +- .../src/vender/line/files/FileUpload.tsx | 8 +- .../icons/src/vender/line/files/Folder.json | 74 +- .../icons/src/vender/line/files/Folder.tsx | 8 +- .../base/icons/src/vender/line/files/index.ts | 2 +- .../line/financeAndECommerce/Balance.json | 54 +- .../line/financeAndECommerce/Balance.tsx | 8 +- .../financeAndECommerce/CoinsStacked01.json | 74 +- .../financeAndECommerce/CoinsStacked01.tsx | 8 +- .../line/financeAndECommerce/GoldCoin.json | 236 +- .../line/financeAndECommerce/GoldCoin.tsx | 8 +- .../line/financeAndECommerce/ReceiptList.json | 54 +- .../line/financeAndECommerce/ReceiptList.tsx | 8 +- .../line/financeAndECommerce/Tag01.json | 128 +- .../vender/line/financeAndECommerce/Tag01.tsx | 8 +- .../line/financeAndECommerce/Tag03.json | 74 +- .../vender/line/financeAndECommerce/Tag03.tsx | 8 +- .../icons/src/vender/line/general/AtSign.json | 128 +- .../icons/src/vender/line/general/AtSign.tsx | 8 +- .../src/vender/line/general/Bookmark.json | 54 +- .../src/vender/line/general/Bookmark.tsx | 8 +- .../icons/src/vender/line/general/Check.json | 74 +- .../icons/src/vender/line/general/Check.tsx | 8 +- .../src/vender/line/general/CheckDone01.json | 74 +- .../src/vender/line/general/CheckDone01.tsx | 8 +- .../vender/line/general/ChecklistSquare.json | 68 +- .../vender/line/general/ChecklistSquare.tsx | 8 +- .../vender/line/general/CodeAssistant.json | 102 +- .../src/vender/line/general/CodeAssistant.tsx | 8 +- .../src/vender/line/general/DotsGrid.json | 264 +- .../src/vender/line/general/DotsGrid.tsx | 8 +- .../icons/src/vender/line/general/Edit02.json | 128 +- .../icons/src/vender/line/general/Edit02.tsx | 8 +- .../icons/src/vender/line/general/Edit04.json | 54 +- .../icons/src/vender/line/general/Edit04.tsx | 8 +- .../icons/src/vender/line/general/Edit05.json | 128 +- .../icons/src/vender/line/general/Edit05.tsx | 8 +- .../icons/src/vender/line/general/Hash02.json | 72 +- .../icons/src/vender/line/general/Hash02.tsx | 8 +- .../src/vender/line/general/InfoCircle.json | 128 +- .../src/vender/line/general/InfoCircle.tsx | 8 +- .../icons/src/vender/line/general/Link03.json | 110 +- .../icons/src/vender/line/general/Link03.tsx | 8 +- .../vender/line/general/LinkExternal02.json | 72 +- .../vender/line/general/LinkExternal02.tsx | 8 +- .../src/vender/line/general/LogIn04.json | 102 +- .../icons/src/vender/line/general/LogIn04.tsx | 8 +- .../src/vender/line/general/LogOut01.json | 74 +- .../src/vender/line/general/LogOut01.tsx | 8 +- .../src/vender/line/general/LogOut04.json | 102 +- .../src/vender/line/general/LogOut04.tsx | 8 +- .../src/vender/line/general/MagicEdit.json | 106 +- .../src/vender/line/general/MagicEdit.tsx | 8 +- .../icons/src/vender/line/general/Menu01.json | 74 +- .../icons/src/vender/line/general/Menu01.tsx | 8 +- .../icons/src/vender/line/general/Pin01.json | 74 +- .../icons/src/vender/line/general/Pin01.tsx | 8 +- .../icons/src/vender/line/general/Pin02.json | 54 +- .../icons/src/vender/line/general/Pin02.tsx | 8 +- .../icons/src/vender/line/general/Plus02.json | 74 +- .../icons/src/vender/line/general/Plus02.tsx | 8 +- .../src/vender/line/general/Refresh.json | 42 +- .../icons/src/vender/line/general/Refresh.tsx | 8 +- .../src/vender/line/general/SearchMenu.json | 150 +- .../src/vender/line/general/SearchMenu.tsx | 8 +- .../src/vender/line/general/Settings01.json | 168 +- .../src/vender/line/general/Settings01.tsx | 8 +- .../src/vender/line/general/Settings04.json | 74 +- .../src/vender/line/general/Settings04.tsx | 8 +- .../src/vender/line/general/Target04.json | 126 +- .../src/vender/line/general/Target04.tsx | 8 +- .../src/vender/line/general/Upload03.json | 128 +- .../src/vender/line/general/Upload03.tsx | 8 +- .../vender/line/general/UploadCloud01.json | 80 +- .../src/vender/line/general/UploadCloud01.tsx | 8 +- .../base/icons/src/vender/line/general/X.json | 74 +- .../base/icons/src/vender/line/general/X.tsx | 8 +- .../icons/src/vender/line/general/index.ts | 2 +- .../src/vender/line/images/ImagePlus.json | 74 +- .../src/vender/line/images/ImagePlus.tsx | 8 +- .../src/vender/line/layout/AlignLeft01.json | 74 +- .../src/vender/line/layout/AlignLeft01.tsx | 8 +- .../src/vender/line/layout/AlignRight01.json | 74 +- .../src/vender/line/layout/AlignRight01.tsx | 8 +- .../icons/src/vender/line/layout/Grid01.json | 162 +- .../icons/src/vender/line/layout/Grid01.tsx | 8 +- .../src/vender/line/layout/LayoutGrid02.json | 54 +- .../src/vender/line/layout/LayoutGrid02.tsx | 8 +- .../line/mediaAndDevices/Microphone01.json | 74 +- .../line/mediaAndDevices/Microphone01.tsx | 8 +- .../line/mediaAndDevices/PlayCircle.json | 168 +- .../line/mediaAndDevices/PlayCircle.tsx | 8 +- .../vender/line/mediaAndDevices/SlidersH.json | 54 +- .../vender/line/mediaAndDevices/SlidersH.tsx | 8 +- .../vender/line/mediaAndDevices/Speaker.json | 220 +- .../vender/line/mediaAndDevices/Speaker.tsx | 8 +- .../src/vender/line/mediaAndDevices/Stop.json | 128 +- .../src/vender/line/mediaAndDevices/Stop.tsx | 8 +- .../line/mediaAndDevices/StopCircle.json | 114 +- .../line/mediaAndDevices/StopCircle.tsx | 8 +- .../src/vender/line/mediaAndDevices/index.ts | 2 +- .../icons/src/vender/line/others/BubbleX.json | 110 +- .../icons/src/vender/line/others/BubbleX.tsx | 8 +- .../icons/src/vender/line/others/Colors.json | 128 +- .../icons/src/vender/line/others/Colors.tsx | 8 +- .../src/vender/line/others/DragHandle.json | 72 +- .../src/vender/line/others/DragHandle.tsx | 8 +- .../icons/src/vender/line/others/Env.json | 176 +- .../base/icons/src/vender/line/others/Env.tsx | 8 +- .../vender/line/others/GlobalVariable.json | 52 +- .../src/vender/line/others/GlobalVariable.tsx | 8 +- .../src/vender/line/others/Icon3Dots.json | 74 +- .../src/vender/line/others/Icon3Dots.tsx | 8 +- .../src/vender/line/others/LongArrowLeft.json | 50 +- .../src/vender/line/others/LongArrowLeft.tsx | 8 +- .../vender/line/others/LongArrowRight.json | 50 +- .../src/vender/line/others/LongArrowRight.tsx | 8 +- .../src/vender/line/others/SearchMenu.json | 150 +- .../src/vender/line/others/SearchMenu.tsx | 8 +- .../icons/src/vender/line/others/Tools.json | 234 +- .../icons/src/vender/line/others/Tools.tsx | 8 +- .../src/vender/line/shapes/CubeOutline.json | 192 +- .../src/vender/line/shapes/CubeOutline.tsx | 8 +- .../vender/line/time/ClockFastForward.json | 54 +- .../src/vender/line/time/ClockFastForward.tsx | 8 +- .../icons/src/vender/line/time/ClockPlay.json | 128 +- .../icons/src/vender/line/time/ClockPlay.tsx | 8 +- .../src/vender/line/time/ClockPlaySlim.json | 74 +- .../src/vender/line/time/ClockPlaySlim.tsx | 8 +- .../src/vender/line/time/ClockRefresh.json | 120 +- .../src/vender/line/time/ClockRefresh.tsx | 8 +- .../base/icons/src/vender/line/time/index.ts | 2 +- .../icons/src/vender/line/users/User01.json | 74 +- .../icons/src/vender/line/users/User01.tsx | 8 +- .../icons/src/vender/line/users/Users01.json | 74 +- .../icons/src/vender/line/users/Users01.tsx | 8 +- .../src/vender/line/weather/Stars02.json | 54 +- .../icons/src/vender/line/weather/Stars02.tsx | 8 +- .../icons/src/vender/other/AnthropicText.json | 1074 +++---- .../icons/src/vender/other/AnthropicText.tsx | 8 +- .../icons/src/vender/other/Generator.json | 70 +- .../base/icons/src/vender/other/Generator.tsx | 8 +- .../base/icons/src/vender/other/Group.json | 128 +- .../base/icons/src/vender/other/Group.tsx | 8 +- .../src/vender/other/HourglassShape.json | 50 +- .../icons/src/vender/other/HourglassShape.tsx | 8 +- .../base/icons/src/vender/other/Mcp.json | 66 +- .../base/icons/src/vender/other/Mcp.tsx | 8 +- .../src/vender/other/NoToolPlaceholder.json | 554 ++-- .../src/vender/other/NoToolPlaceholder.tsx | 8 +- .../base/icons/src/vender/other/Openai.json | 156 +- .../base/icons/src/vender/other/Openai.tsx | 8 +- .../icons/src/vender/other/ReplayLine.json | 68 +- .../icons/src/vender/other/ReplayLine.tsx | 8 +- .../src/vender/other/SquareChecklist.json | 48 +- .../src/vender/other/SquareChecklist.tsx | 8 +- .../icons/src/vender/pipeline/InputField.json | 124 +- .../icons/src/vender/pipeline/InputField.tsx | 8 +- .../src/vender/pipeline/PipelineFill.json | 156 +- .../src/vender/pipeline/PipelineFill.tsx | 8 +- .../src/vender/pipeline/PipelineLine.json | 68 +- .../src/vender/pipeline/PipelineLine.tsx | 8 +- .../src/vender/plugin/BoxSparkleFill.json | 128 +- .../src/vender/plugin/BoxSparkleFill.tsx | 8 +- .../icons/src/vender/plugin/LeftCorner.json | 50 +- .../icons/src/vender/plugin/LeftCorner.tsx | 8 +- .../base/icons/src/vender/plugin/Trigger.json | 142 +- .../base/icons/src/vender/plugin/Trigger.tsx | 8 +- .../solid/FinanceAndECommerce/GoldCoin.json | 48 +- .../solid/FinanceAndECommerce/GoldCoin.tsx | 8 +- .../solid/FinanceAndECommerce/Scales02.json | 92 +- .../solid/FinanceAndECommerce/Scales02.tsx | 8 +- .../alertsAndFeedback/AlertTriangle.json | 72 +- .../solid/alertsAndFeedback/AlertTriangle.tsx | 8 +- .../solid/arrows/ArrowDownDoubleLine.json | 48 +- .../solid/arrows/ArrowDownDoubleLine.tsx | 8 +- .../solid/arrows/ArrowDownRoundFill.json | 50 +- .../solid/arrows/ArrowDownRoundFill.tsx | 8 +- .../solid/arrows/ArrowUpDoubleLine.json | 48 +- .../vender/solid/arrows/ArrowUpDoubleLine.tsx | 8 +- .../src/vender/solid/arrows/ChevronDown.json | 74 +- .../src/vender/solid/arrows/ChevronDown.tsx | 8 +- .../src/vender/solid/arrows/HighPriority.json | 102 +- .../src/vender/solid/arrows/HighPriority.tsx | 8 +- .../vender/solid/communication/AiText.json | 102 +- .../src/vender/solid/communication/AiText.tsx | 8 +- .../solid/communication/BubbleTextMod.json | 52 +- .../solid/communication/BubbleTextMod.tsx | 8 +- .../vender/solid/communication/ChatBot.json | 112 +- .../vender/solid/communication/ChatBot.tsx | 8 +- .../vender/solid/communication/CuteRobot.json | 72 +- .../vender/solid/communication/CuteRobot.tsx | 8 +- .../vender/solid/communication/EditList.json | 102 +- .../vender/solid/communication/EditList.tsx | 8 +- .../solid/communication/ListSparkle.json | 102 +- .../solid/communication/ListSparkle.tsx | 8 +- .../src/vender/solid/communication/Logic.json | 102 +- .../src/vender/solid/communication/Logic.tsx | 8 +- .../communication/MessageDotsCircle.json | 72 +- .../solid/communication/MessageDotsCircle.tsx | 8 +- .../solid/communication/MessageFast.json | 52 +- .../solid/communication/MessageFast.tsx | 8 +- .../communication/MessageHeartCircle.json | 72 +- .../communication/MessageHeartCircle.tsx | 8 +- .../communication/MessageSmileSquare.json | 72 +- .../communication/MessageSmileSquare.tsx | 8 +- .../vender/solid/communication/Send03.json | 68 +- .../src/vender/solid/communication/Send03.tsx | 8 +- .../solid/development/ApiConnection.json | 102 +- .../solid/development/ApiConnection.tsx | 8 +- .../solid/development/ApiConnectionMod.json | 72 +- .../solid/development/ApiConnectionMod.tsx | 8 +- .../solid/development/BarChartSquare02.json | 72 +- .../solid/development/BarChartSquare02.tsx | 8 +- .../vender/solid/development/Container.json | 84 +- .../vender/solid/development/Container.tsx | 8 +- .../vender/solid/development/Database02.json | 88 +- .../vender/solid/development/Database02.tsx | 8 +- .../vender/solid/development/Database03.json | 52 +- .../vender/solid/development/Database03.tsx | 8 +- .../vender/solid/development/FileHeart02.json | 96 +- .../vender/solid/development/FileHeart02.tsx | 8 +- .../solid/development/PatternRecognition.json | 192 +- .../solid/development/PatternRecognition.tsx | 8 +- .../solid/development/PromptEngineering.json | 102 +- .../solid/development/PromptEngineering.tsx | 8 +- .../solid/development/PuzzlePiece01.json | 72 +- .../solid/development/PuzzlePiece01.tsx | 8 +- .../vender/solid/development/Semantic.json | 102 +- .../src/vender/solid/development/Semantic.tsx | 8 +- .../solid/development/TerminalSquare.json | 72 +- .../solid/development/TerminalSquare.tsx | 8 +- .../vender/solid/development/Variable02.json | 120 +- .../vender/solid/development/Variable02.tsx | 8 +- .../src/vender/solid/development/index.ts | 2 +- .../src/vender/solid/editor/Brush01.json | 66 +- .../icons/src/vender/solid/editor/Brush01.tsx | 8 +- .../src/vender/solid/editor/Citations.json | 68 +- .../src/vender/solid/editor/Citations.tsx | 8 +- .../icons/src/vender/solid/editor/Colors.json | 120 +- .../icons/src/vender/solid/editor/Colors.tsx | 8 +- .../src/vender/solid/editor/Paragraph.json | 84 +- .../src/vender/solid/editor/Paragraph.tsx | 8 +- .../src/vender/solid/editor/TypeSquare.json | 52 +- .../src/vender/solid/editor/TypeSquare.tsx | 8 +- .../src/vender/solid/education/Beaker02.json | 72 +- .../src/vender/solid/education/Beaker02.tsx | 8 +- .../vender/solid/education/BubbleText.json | 72 +- .../src/vender/solid/education/BubbleText.tsx | 8 +- .../src/vender/solid/education/Heart02.json | 48 +- .../src/vender/solid/education/Heart02.tsx | 8 +- .../src/vender/solid/education/Unblur.json | 300 +- .../src/vender/solid/education/Unblur.tsx | 8 +- .../icons/src/vender/solid/files/File05.json | 106 +- .../icons/src/vender/solid/files/File05.tsx | 8 +- .../src/vender/solid/files/FileSearch02.json | 110 +- .../src/vender/solid/files/FileSearch02.tsx | 8 +- .../icons/src/vender/solid/files/FileZip.json | 90 +- .../icons/src/vender/solid/files/FileZip.tsx | 8 +- .../icons/src/vender/solid/files/Folder.json | 72 +- .../icons/src/vender/solid/files/Folder.tsx | 8 +- .../vender/solid/general/AnswerTriangle.json | 50 +- .../vender/solid/general/AnswerTriangle.tsx | 8 +- .../solid/general/ArrowDownRoundFill.json | 68 +- .../solid/general/ArrowDownRoundFill.tsx | 8 +- .../src/vender/solid/general/CheckCircle.json | 72 +- .../src/vender/solid/general/CheckCircle.tsx | 8 +- .../src/vender/solid/general/CheckDone01.json | 70 +- .../src/vender/solid/general/CheckDone01.tsx | 8 +- .../src/vender/solid/general/Download02.json | 54 +- .../src/vender/solid/general/Download02.tsx | 8 +- .../src/vender/solid/general/Edit03.json | 110 +- .../icons/src/vender/solid/general/Edit03.tsx | 8 +- .../src/vender/solid/general/Edit04.json | 74 +- .../icons/src/vender/solid/general/Edit04.tsx | 8 +- .../icons/src/vender/solid/general/Eye.json | 70 +- .../icons/src/vender/solid/general/Eye.tsx | 8 +- .../src/vender/solid/general/Github.json | 68 +- .../icons/src/vender/solid/general/Github.tsx | 8 +- .../solid/general/MessageClockCircle.json | 68 +- .../solid/general/MessageClockCircle.tsx | 8 +- .../src/vender/solid/general/PlusCircle.json | 72 +- .../src/vender/solid/general/PlusCircle.tsx | 8 +- .../solid/general/QuestionTriangle.json | 86 +- .../vender/solid/general/QuestionTriangle.tsx | 8 +- .../src/vender/solid/general/SearchMd.json | 72 +- .../src/vender/solid/general/SearchMd.tsx | 8 +- .../src/vender/solid/general/Target04.json | 88 +- .../src/vender/solid/general/Target04.tsx | 8 +- .../src/vender/solid/general/Tool03.json | 120 +- .../icons/src/vender/solid/general/Tool03.tsx | 8 +- .../src/vender/solid/general/XCircle.json | 54 +- .../src/vender/solid/general/XCircle.tsx | 8 +- .../src/vender/solid/general/ZapFast.json | 154 +- .../src/vender/solid/general/ZapFast.tsx | 8 +- .../src/vender/solid/general/ZapNarrow.json | 72 +- .../src/vender/solid/general/ZapNarrow.tsx | 8 +- .../icons/src/vender/solid/layout/Grid01.json | 154 +- .../icons/src/vender/solid/layout/Grid01.tsx | 8 +- .../mediaAndDevices/AudioSupportIcon.json | 48 +- .../mediaAndDevices/AudioSupportIcon.tsx | 8 +- .../mediaAndDevices/DocumentSupportIcon.json | 48 +- .../mediaAndDevices/DocumentSupportIcon.tsx | 8 +- .../solid/mediaAndDevices/MagicBox.json | 124 +- .../vender/solid/mediaAndDevices/MagicBox.tsx | 8 +- .../solid/mediaAndDevices/MagicEyes.json | 72 +- .../solid/mediaAndDevices/MagicEyes.tsx | 8 +- .../solid/mediaAndDevices/MagicWand.json | 142 +- .../solid/mediaAndDevices/MagicWand.tsx | 8 +- .../solid/mediaAndDevices/Microphone01.json | 106 +- .../solid/mediaAndDevices/Microphone01.tsx | 8 +- .../vender/solid/mediaAndDevices/Play.json | 72 +- .../src/vender/solid/mediaAndDevices/Play.tsx | 8 +- .../vender/solid/mediaAndDevices/Robot.json | 72 +- .../vender/solid/mediaAndDevices/Robot.tsx | 8 +- .../solid/mediaAndDevices/Sliders02.json | 150 +- .../solid/mediaAndDevices/Sliders02.tsx | 8 +- .../vender/solid/mediaAndDevices/Speaker.json | 220 +- .../vender/solid/mediaAndDevices/Speaker.tsx | 8 +- .../solid/mediaAndDevices/StopCircle.json | 72 +- .../solid/mediaAndDevices/StopCircle.tsx | 8 +- .../mediaAndDevices/VideoSupportIcon.json | 48 +- .../mediaAndDevices/VideoSupportIcon.tsx | 8 +- .../src/vender/solid/security/Lock01.json | 72 +- .../src/vender/solid/security/Lock01.tsx | 8 +- .../icons/src/vender/solid/shapes/Corner.json | 50 +- .../icons/src/vender/solid/shapes/Corner.tsx | 8 +- .../icons/src/vender/solid/shapes/Star04.json | 68 +- .../icons/src/vender/solid/shapes/Star04.tsx | 8 +- .../icons/src/vender/solid/shapes/Star06.json | 120 +- .../icons/src/vender/solid/shapes/Star06.tsx | 8 +- .../icons/src/vender/solid/users/User01.json | 110 +- .../icons/src/vender/solid/users/User01.tsx | 8 +- .../src/vender/solid/users/UserEdit02.json | 180 +- .../src/vender/solid/users/UserEdit02.tsx | 8 +- .../icons/src/vender/solid/users/Users01.json | 154 +- .../icons/src/vender/solid/users/Users01.tsx | 8 +- .../src/vender/solid/users/UsersPlus.json | 150 +- .../src/vender/solid/users/UsersPlus.tsx | 8 +- .../src/vender/system/AutoUpdateLine.json | 70 +- .../src/vender/system/AutoUpdateLine.tsx | 8 +- .../base/icons/src/vender/workflow/Agent.json | 102 +- .../base/icons/src/vender/workflow/Agent.tsx | 8 +- .../icons/src/vender/workflow/Answer.json | 72 +- .../base/icons/src/vender/workflow/Answer.tsx | 8 +- .../src/vender/workflow/ApiAggregate.json | 48 +- .../src/vender/workflow/ApiAggregate.tsx | 8 +- .../icons/src/vender/workflow/Assigner.json | 132 +- .../icons/src/vender/workflow/Assigner.tsx | 8 +- .../icons/src/vender/workflow/Asterisk.json | 48 +- .../icons/src/vender/workflow/Asterisk.tsx | 8 +- .../vender/workflow/CalendarCheckLine.json | 48 +- .../src/vender/workflow/CalendarCheckLine.tsx | 8 +- .../base/icons/src/vender/workflow/Code.json | 72 +- .../base/icons/src/vender/workflow/Code.tsx | 8 +- .../icons/src/vender/workflow/Datasource.json | 48 +- .../icons/src/vender/workflow/Datasource.tsx | 8 +- .../src/vender/workflow/DocsExtractor.json | 124 +- .../src/vender/workflow/DocsExtractor.tsx | 8 +- .../base/icons/src/vender/workflow/End.json | 72 +- .../base/icons/src/vender/workflow/End.tsx | 8 +- .../base/icons/src/vender/workflow/Home.json | 72 +- .../base/icons/src/vender/workflow/Home.tsx | 8 +- .../base/icons/src/vender/workflow/Http.json | 138 +- .../base/icons/src/vender/workflow/Http.tsx | 8 +- .../icons/src/vender/workflow/IfElse.json | 72 +- .../base/icons/src/vender/workflow/IfElse.tsx | 8 +- .../icons/src/vender/workflow/Iteration.json | 68 +- .../icons/src/vender/workflow/Iteration.tsx | 8 +- .../src/vender/workflow/IterationStart.json | 68 +- .../src/vender/workflow/IterationStart.tsx | 8 +- .../base/icons/src/vender/workflow/Jinja.json | 192 +- .../base/icons/src/vender/workflow/Jinja.tsx | 8 +- .../src/vender/workflow/KnowledgeBase.json | 68 +- .../src/vender/workflow/KnowledgeBase.tsx | 8 +- .../vender/workflow/KnowledgeRetrieval.json | 72 +- .../vender/workflow/KnowledgeRetrieval.tsx | 8 +- .../icons/src/vender/workflow/ListFilter.json | 72 +- .../icons/src/vender/workflow/ListFilter.tsx | 8 +- .../base/icons/src/vender/workflow/Llm.json | 72 +- .../base/icons/src/vender/workflow/Llm.tsx | 8 +- .../base/icons/src/vender/workflow/Loop.json | 72 +- .../base/icons/src/vender/workflow/Loop.tsx | 8 +- .../icons/src/vender/workflow/LoopEnd.json | 72 +- .../icons/src/vender/workflow/LoopEnd.tsx | 8 +- .../vender/workflow/ParameterExtractor.json | 528 ++-- .../vender/workflow/ParameterExtractor.tsx | 8 +- .../vender/workflow/QuestionClassifier.json | 72 +- .../vender/workflow/QuestionClassifier.tsx | 8 +- .../icons/src/vender/workflow/Schedule.json | 88 +- .../icons/src/vender/workflow/Schedule.tsx | 8 +- .../vender/workflow/TemplatingTransform.json | 304 +- .../vender/workflow/TemplatingTransform.tsx | 8 +- .../icons/src/vender/workflow/TriggerAll.json | 142 +- .../icons/src/vender/workflow/TriggerAll.tsx | 8 +- .../icons/src/vender/workflow/VariableX.json | 72 +- .../icons/src/vender/workflow/VariableX.tsx | 8 +- .../src/vender/workflow/WebhookLine.json | 48 +- .../icons/src/vender/workflow/WebhookLine.tsx | 8 +- .../src/vender/workflow/WindowCursor.json | 120 +- .../src/vender/workflow/WindowCursor.tsx | 8 +- .../base/icons/src/vender/workflow/index.ts | 4 +- web/app/components/base/icons/utils.spec.ts | 2 +- web/app/components/base/icons/utils.ts | 24 +- .../components/base/image-gallery/index.tsx | 32 +- .../base/image-uploader/audio-preview.tsx | 10 +- .../image-uploader/chat-image-uploader.tsx | 14 +- .../components/base/image-uploader/hooks.ts | 12 +- .../base/image-uploader/image-link-input.tsx | 10 +- .../image-uploader/image-list.stories.tsx | 6 +- .../base/image-uploader/image-list.tsx | 23 +- .../base/image-uploader/image-preview.tsx | 69 +- .../text-generation-image-uploader.tsx | 30 +- .../base/image-uploader/uploader.tsx | 10 +- .../base/image-uploader/video-preview.tsx | 10 +- .../base/inline-delete-confirm/index.spec.tsx | 20 +- .../inline-delete-confirm/index.stories.tsx | 2 +- .../base/input-number/index.stories.tsx | 44 +- .../components/base/input-number/index.tsx | 88 +- .../base/input-with-copy/index.spec.tsx | 2 +- .../components/base/input-with-copy/index.tsx | 24 +- web/app/components/base/input/index.spec.tsx | 2 +- .../components/base/input/index.stories.tsx | 7 +- web/app/components/base/input/index.tsx | 15 +- .../base/linked-apps-panel/index.stories.tsx | 2 +- .../base/linked-apps-panel/index.tsx | 20 +- .../base/list-empty/horizontal-line.tsx | 8 +- web/app/components/base/list-empty/index.tsx | 25 +- .../base/list-empty/vertical-line.tsx | 8 +- .../components/base/loading/index.spec.tsx | 4 +- web/app/components/base/loading/index.tsx | 7 +- web/app/components/base/logo/dify-logo.tsx | 5 +- .../components/base/logo/index.stories.tsx | 10 +- .../base/logo/logo-embedded-chat-avatar.tsx | 2 +- .../base/logo/logo-embedded-chat-header.tsx | 24 +- web/app/components/base/logo/logo-site.tsx | 4 +- .../base/markdown-blocks/button.tsx | 37 +- .../base/markdown-blocks/code-block.tsx | 79 +- .../components/base/markdown-blocks/form.tsx | 27 +- .../components/base/markdown-blocks/index.ts | 20 +- .../components/base/markdown-blocks/link.tsx | 2 +- .../base/markdown-blocks/plugin-img.tsx | 4 +- .../base/markdown-blocks/plugin-paragraph.tsx | 4 +- .../base/markdown-blocks/pre-code.tsx | 3 +- .../markdown-blocks/think-block.stories.tsx | 2 +- .../base/markdown-blocks/think-block.tsx | 5 +- .../components/base/markdown-blocks/utils.ts | 3 +- .../base/markdown/error-boundary.tsx | 11 +- web/app/components/base/markdown/index.tsx | 6 +- .../base/markdown/markdown-utils.ts | 3 +- .../base/markdown/react-markdown-wrapper.tsx | 6 +- web/app/components/base/mermaid/index.tsx | 28 +- web/app/components/base/mermaid/utils.ts | 18 +- .../base/message-log-modal/index.stories.tsx | 8 +- .../base/message-log-modal/index.tsx | 32 +- .../components/base/modal-like-wrap/index.tsx | 33 +- web/app/components/base/modal/index.tsx | 60 +- web/app/components/base/modal/modal.tsx | 30 +- .../base/new-audio-button/index.stories.tsx | 2 +- .../base/new-audio-button/index.tsx | 10 +- web/app/components/base/node-status/index.tsx | 14 +- .../base/notion-connector/index.tsx | 20 +- web/app/components/base/notion-icon/index.tsx | 6 +- .../base/notion-page-selector/base.tsx | 68 +- .../credential-selector/index.tsx | 34 +- .../notion-page-selector/index.stories.tsx | 9 +- .../page-selector/index.tsx | 47 +- .../search-input/index.tsx | 10 +- web/app/components/base/pagination/hook.ts | 2 +- web/app/components/base/pagination/index.tsx | 58 +- .../components/base/pagination/pagination.tsx | 10 +- web/app/components/base/pagination/type.ts | 4 +- .../base/param-item/index.stories.tsx | 4 +- web/app/components/base/param-item/index.tsx | 32 +- .../base/param-item/score-threshold-item.tsx | 2 +- .../components/base/param-item/top-k-item.tsx | 2 +- .../components/base/popover/index.stories.tsx | 4 +- web/app/components/base/popover/index.tsx | 36 +- .../base/portal-to-follow-elem/index.spec.tsx | 28 +- .../portal-to-follow-elem/index.stories.tsx | 2 +- .../base/portal-to-follow-elem/index.tsx | 9 +- .../components/base/premium-badge/index.tsx | 9 +- .../progress-bar/progress-circle.stories.tsx | 3 +- .../base/prompt-editor/constants.tsx | 5 +- .../components/base/prompt-editor/hooks.ts | 43 +- .../base/prompt-editor/index.stories.tsx | 24 +- .../components/base/prompt-editor/index.tsx | 134 +- .../plugins/component-picker-block/hooks.tsx | 77 +- .../plugins/component-picker-block/index.tsx | 62 +- .../component-picker-block/prompt-option.tsx | 5 +- .../variable-option.tsx | 9 +- .../plugins/context-block/component.tsx | 55 +- .../context-block-replacement-block.tsx | 12 +- .../plugins/context-block/index.tsx | 18 +- .../plugins/context-block/node.tsx | 4 +- .../plugins/current-block/component.tsx | 13 +- .../current-block-replacement-block.tsx | 12 +- .../plugins/current-block/index.tsx | 16 +- .../plugins/current-block/node.tsx | 4 +- .../plugins/error-message-block/component.tsx | 11 +- .../error-message-block-replacement-block.tsx | 12 +- .../plugins/error-message-block/index.tsx | 16 +- .../plugins/history-block/component.tsx | 46 +- .../history-block-replacement-block.tsx | 14 +- .../plugins/history-block/index.tsx | 18 +- .../plugins/history-block/node.tsx | 4 +- .../plugins/last-run-block/component.tsx | 11 +- .../plugins/last-run-block/index.tsx | 16 +- .../last-run-block-replacement-block.tsx | 12 +- .../plugins/on-blur-or-focus-block.tsx | 6 +- .../prompt-editor/plugins/placeholder.tsx | 5 +- .../plugins/query-block/component.tsx | 10 +- .../plugins/query-block/index.tsx | 14 +- .../query-block-replacement-block.tsx | 12 +- .../prompt-editor/plugins/update-block.tsx | 4 +- .../plugins/variable-block/index.tsx | 6 +- .../plugins/variable-value-block/index.tsx | 4 +- .../workflow-variable-block/component.tsx | 51 +- .../plugins/workflow-variable-block/index.tsx | 16 +- .../plugins/workflow-variable-block/node.tsx | 7 +- ...kflow-variable-block-replacement-block.tsx | 18 +- .../components/base/prompt-editor/types.ts | 4 +- .../components/base/prompt-editor/utils.ts | 6 +- .../components/base/prompt-log-modal/card.tsx | 16 +- .../base/prompt-log-modal/index.stories.tsx | 6 +- .../base/prompt-log-modal/index.tsx | 28 +- web/app/components/base/qrcode/index.tsx | 20 +- .../base/radio-card/index.stories.tsx | 79 +- web/app/components/base/radio-card/index.tsx | 20 +- .../base/radio-card/simple/index.tsx | 10 +- .../base/radio/component/group/index.tsx | 2 +- .../base/radio/component/radio/index.tsx | 27 +- .../components/base/radio/index.stories.tsx | 37 +- web/app/components/base/radio/index.tsx | 2 +- web/app/components/base/radio/ui.tsx | 3 +- .../base/search-input/index.stories.tsx | 188 +- .../components/base/search-input/index.tsx | 9 +- .../base/segmented-control/index.spec.tsx | 16 +- .../base/segmented-control/index.stories.tsx | 4 +- .../base/segmented-control/index.tsx | 19 +- web/app/components/base/select/custom.tsx | 77 +- .../components/base/select/index.stories.tsx | 45 +- web/app/components/base/select/index.tsx | 229 +- .../components/base/select/locale-signin.tsx | 29 +- web/app/components/base/select/locale.tsx | 29 +- web/app/components/base/select/pure.tsx | 36 +- .../base/simple-pie-chart/index.tsx | 8 +- .../components/base/slider/index.stories.tsx | 105 +- web/app/components/base/slider/index.tsx | 24 +- .../components/base/sort/index.stories.tsx | 4 +- web/app/components/base/sort/index.tsx | 39 +- .../components/base/spinner/index.spec.tsx | 2 +- .../components/base/spinner/index.stories.tsx | 4 +- web/app/components/base/spinner/index.tsx | 4 +- web/app/components/base/svg-gallery/index.tsx | 27 +- web/app/components/base/svg/index.stories.tsx | 4 +- web/app/components/base/svg/index.tsx | 4 +- .../components/base/switch/index.stories.tsx | 16 +- web/app/components/base/switch/index.tsx | 16 +- .../base/tab-header/index.stories.tsx | 6 +- web/app/components/base/tab-header/index.tsx | 6 +- .../base/tab-slider-new/index.stories.tsx | 2 +- .../base/tab-slider-plain/index.tsx | 2 +- web/app/components/base/tab-slider/index.tsx | 20 +- .../base/tag-input/index.stories.tsx | 30 +- web/app/components/base/tag-input/index.tsx | 14 +- .../components/base/tag-management/filter.tsx | 93 +- .../base/tag-management/index.stories.tsx | 10 +- .../components/base/tag-management/index.tsx | 22 +- .../components/base/tag-management/panel.tsx | 70 +- .../base/tag-management/selector.tsx | 21 +- .../components/base/tag-management/store.ts | 2 +- .../base/tag-management/tag-item-editor.tsx | 37 +- .../base/tag-management/tag-remove-modal.tsx | 30 +- .../base/tag-management/trigger.tsx | 56 +- web/app/components/base/tag/index.tsx | 8 +- .../components/base/text-generation/hooks.ts | 3 +- .../components/base/text-generation/types.ts | 3 +- .../base/textarea/index.stories.tsx | 22 +- web/app/components/base/textarea/index.tsx | 3 +- web/app/components/base/theme-selector.tsx | 73 +- web/app/components/base/theme-switcher.tsx | 14 +- .../timezone-label/__tests__/index.test.tsx | 5 +- web/app/components/base/toast/index.spec.tsx | 22 +- web/app/components/base/toast/index.tsx | 107 +- web/app/components/base/tooltip/content.tsx | 8 +- .../components/base/tooltip/index.spec.tsx | 34 +- web/app/components/base/tooltip/index.tsx | 32 +- .../base/video-gallery/VideoPlayer.tsx | 20 +- .../components/base/video-gallery/index.tsx | 3 +- .../base/voice-input/index.stories.tsx | 317 +- web/app/components/base/voice-input/index.tsx | 32 +- web/app/components/base/voice-input/utils.ts | 4 +- .../base/with-input-validation/index.spec.tsx | 14 +- .../with-input-validation/index.stories.tsx | 46 +- .../base/with-input-validation/index.tsx | 2 +- web/app/components/base/zendesk/index.tsx | 8 +- web/app/components/base/zendesk/utils.ts | 6 +- .../billing/annotation-full/index.spec.tsx | 4 +- .../billing/annotation-full/index.tsx | 20 +- .../billing/annotation-full/modal.spec.tsx | 10 +- .../billing/annotation-full/modal.tsx | 24 +- .../billing/annotation-full/usage.tsx | 2 +- .../billing/apps-full-in-dialog/index.tsx | 37 +- .../components/billing/billing-page/index.tsx | 26 +- web/app/components/billing/config.ts | 4 +- .../billing/header-billing-btn/index.tsx | 6 +- .../billing/partner-stack/index.tsx | 2 +- .../billing/partner-stack/use-ps-info.ts | 10 +- .../billing/plan-upgrade-modal/index.spec.tsx | 4 +- .../billing/plan-upgrade-modal/index.tsx | 26 +- .../billing/plan/assets/enterprise.tsx | 164 +- .../billing/plan/assets/index.spec.tsx | 6 +- .../components/billing/plan/assets/index.tsx | 6 +- .../billing/plan/assets/professional.tsx | 164 +- .../billing/plan/assets/sandbox.tsx | 164 +- .../components/billing/plan/assets/team.tsx | 164 +- web/app/components/billing/plan/index.tsx | 72 +- .../billing/pricing/assets/cloud.tsx | 26 +- .../billing/pricing/assets/community.tsx | 170 +- .../pricing/assets/enterprise-noise.tsx | 20 +- .../billing/pricing/assets/enterprise.tsx | 170 +- .../billing/pricing/assets/index.tsx | 16 +- .../billing/pricing/assets/noise-bottom.tsx | 20 +- .../billing/pricing/assets/noise-top.tsx | 20 +- .../billing/pricing/assets/premium-noise.tsx | 20 +- .../billing/pricing/assets/premium.tsx | 170 +- .../billing/pricing/assets/professional.tsx | 170 +- .../billing/pricing/assets/sandbox.tsx | 170 +- .../billing/pricing/assets/self-hosted.tsx | 26 +- .../billing/pricing/assets/team.tsx | 170 +- web/app/components/billing/pricing/footer.tsx | 27 +- web/app/components/billing/pricing/header.tsx | 24 +- web/app/components/billing/pricing/index.tsx | 24 +- .../billing/pricing/plan-switcher/index.tsx | 18 +- .../plan-switcher/plan-range-switcher.tsx | 6 +- .../billing/pricing/plan-switcher/tab.tsx | 2 +- .../plans/cloud-plan-item/button.spec.tsx | 8 +- .../pricing/plans/cloud-plan-item/button.tsx | 12 +- .../plans/cloud-plan-item/index.spec.tsx | 20 +- .../pricing/plans/cloud-plan-item/index.tsx | 52 +- .../plans/cloud-plan-item/list/index.spec.tsx | 10 +- .../plans/cloud-plan-item/list/index.tsx | 17 +- .../cloud-plan-item/list/item/index.spec.tsx | 2 +- .../plans/cloud-plan-item/list/item/index.tsx | 4 +- .../cloud-plan-item/list/item/tooltip.tsx | 10 +- .../billing/pricing/plans/index.spec.tsx | 21 +- .../billing/pricing/plans/index.tsx | 43 +- .../self-hosted-plan-item/button.spec.tsx | 10 +- .../plans/self-hosted-plan-item/button.tsx | 19 +- .../self-hosted-plan-item/index.spec.tsx | 20 +- .../plans/self-hosted-plan-item/index.tsx | 44 +- .../self-hosted-plan-item/list/index.spec.tsx | 6 +- .../self-hosted-plan-item/list/index.tsx | 13 +- .../self-hosted-plan-item/list/item.spec.tsx | 4 +- .../plans/self-hosted-plan-item/list/item.tsx | 10 +- .../billing/priority-label/index.tsx | 22 +- .../components/billing/progress-bar/index.tsx | 2 +- .../trigger-events-limit-modal/index.tsx | 4 +- web/app/components/billing/type.ts | 6 +- .../components/billing/upgrade-btn/index.tsx | 10 +- .../billing/usage-info/apps-info.tsx | 6 +- .../components/billing/usage-info/index.tsx | 38 +- .../billing/usage-info/vector-space-info.tsx | 8 +- web/app/components/billing/utils/index.ts | 2 +- .../billing/vector-space-full/index.tsx | 14 +- .../custom/custom-page/index.spec.tsx | 8 +- .../components/custom/custom-page/index.tsx | 22 +- .../custom/custom-web-app-brand/index.tsx | 180 +- web/app/components/datasets/chunk.tsx | 64 +- .../datasets/common/check-rerank-model.ts | 9 +- .../datasets/common/chunking-mode-label.tsx | 6 +- .../datasets/common/credential-icon.tsx | 6 +- .../datasets/common/document-file-icon.tsx | 4 +- .../common/document-picker/document-list.tsx | 12 +- .../common/document-picker/index.spec.tsx | 15 +- .../datasets/common/document-picker/index.tsx | 73 +- .../preview-document-picker.spec.tsx | 9 +- .../preview-document-picker.tsx | 51 +- .../auto-disabled-document.tsx | 7 +- .../index-failed.tsx | 10 +- .../status-with-action.tsx | 15 +- .../index.tsx | 14 +- .../datasets/common/image-list/index.tsx | 9 +- .../datasets/common/image-list/more.tsx | 10 +- .../datasets/common/image-previewer/index.tsx | 63 +- .../common/image-uploader/hooks/use-upload.ts | 23 +- .../image-uploader-in-chunk/image-input.tsx | 24 +- .../image-uploader-in-chunk/image-item.tsx | 36 +- .../image-uploader-in-chunk/index.tsx | 19 +- .../image-input.tsx | 28 +- .../image-item.tsx | 36 +- .../index.tsx | 26 +- .../datasets/common/image-uploader/store.tsx | 6 +- .../datasets/common/image-uploader/utils.ts | 2 +- .../retrieval-method-config/index.spec.tsx | 10 +- .../common/retrieval-method-config/index.tsx | 92 +- .../common/retrieval-method-info/index.tsx | 38 +- .../common/retrieval-param-config/index.tsx | 83 +- .../dsl-confirm-modal.tsx | 26 +- .../create-from-dsl-modal/header.tsx | 6 +- .../create-from-dsl-modal/index.tsx | 56 +- .../create-from-dsl-modal/tab/index.tsx | 4 +- .../create-from-dsl-modal/tab/item.tsx | 2 +- .../create-from-dsl-modal/uploader.tsx | 45 +- .../datasets/create-from-pipeline/footer.tsx | 18 +- .../datasets/create-from-pipeline/header.tsx | 18 +- .../datasets/create-from-pipeline/index.tsx | 8 +- .../list/built-in-pipeline-list.tsx | 14 +- .../create-from-pipeline/list/create-card.tsx | 20 +- .../list/customized-list.tsx | 10 +- .../create-from-pipeline/list/index.tsx | 2 +- .../list/template-card/actions.tsx | 38 +- .../list/template-card/content.tsx | 24 +- .../details/chunk-structure-card.tsx | 21 +- .../list/template-card/details/hooks.tsx | 11 +- .../list/template-card/details/index.tsx | 60 +- .../list/template-card/edit-pipeline-info.tsx | 47 +- .../list/template-card/index.tsx | 31 +- .../list/template-card/operations.tsx | 22 +- .../create/embedding-process/index.tsx | 202 +- .../index.spec.tsx | 4 +- .../empty-dataset-creation-modal/index.tsx | 20 +- .../create/file-preview/index.spec.tsx | 9 +- .../datasets/create/file-preview/index.tsx | 20 +- .../datasets/create/file-uploader/index.tsx | 70 +- web/app/components/datasets/create/icons.ts | 6 +- .../components/datasets/create/index.spec.tsx | 12 +- web/app/components/datasets/create/index.tsx | 27 +- .../create/notion-page-preview/index.spec.tsx | 7 +- .../create/notion-page-preview/index.tsx | 18 +- .../datasets/create/step-one/index.tsx | 78 +- .../datasets/create/step-one/upgrade-card.tsx | 18 +- .../datasets/create/step-three/index.spec.tsx | 2 +- .../datasets/create/step-three/index.tsx | 60 +- .../datasets/create/step-two/index.tsx | 887 +++--- .../datasets/create/step-two/inputs.tsx | 127 +- .../step-two/language-select/index.spec.tsx | 4 +- .../create/step-two/language-select/index.tsx | 32 +- .../datasets/create/step-two/option-card.tsx | 56 +- .../step-two/preview-item/index.spec.tsx | 2 +- .../create/step-two/preview-item/index.tsx | 24 +- .../datasets/create/stepper/index.spec.tsx | 6 +- .../datasets/create/stepper/index.tsx | 35 +- .../datasets/create/stepper/step.tsx | 36 +- .../create/stop-embedding-modal/index.tsx | 12 +- .../datasets/create/top-bar/index.spec.tsx | 5 +- .../datasets/create/top-bar/index.tsx | 42 +- .../datasets/create/website/base.spec.tsx | 6 +- .../website/base/checkbox-with-label.tsx | 6 +- .../website/base/crawled-result-item.tsx | 20 +- .../create/website/base/crawled-result.tsx | 14 +- .../datasets/create/website/base/crawling.tsx | 17 +- .../create/website/base/error-message.tsx | 10 +- .../datasets/create/website/base/field.tsx | 17 +- .../datasets/create/website/base/header.tsx | 35 +- .../datasets/create/website/base/input.tsx | 4 +- .../create/website/base/options-wrap.tsx | 17 +- .../create/website/base/url-input.tsx | 8 +- .../create/website/firecrawl/index.tsx | 61 +- .../create/website/firecrawl/options.tsx | 24 +- .../datasets/create/website/index.tsx | 110 +- .../website/jina-reader/base/url-input.tsx | 10 +- .../create/website/jina-reader/index.spec.tsx | 7 +- .../create/website/jina-reader/index.tsx | 61 +- .../create/website/jina-reader/options.tsx | 16 +- .../datasets/create/website/no-data.tsx | 60 +- .../datasets/create/website/preview.tsx | 14 +- .../create/website/watercrawl/index.spec.tsx | 13 +- .../create/website/watercrawl/index.tsx | 61 +- .../create/website/watercrawl/options.tsx | 28 +- .../actions/index.spec.tsx | 2 +- .../create-from-pipeline/actions/index.tsx | 42 +- .../data-source-options/datasource-icon.tsx | 5 +- .../data-source-options/hooks.tsx | 9 +- .../data-source-options/index.spec.tsx | 17 +- .../data-source-options/index.tsx | 8 +- .../data-source-options/option-card.tsx | 4 +- .../base/credential-selector/index.spec.tsx | 7 +- .../base/credential-selector/index.tsx | 12 +- .../base/credential-selector/item.tsx | 8 +- .../base/credential-selector/list.tsx | 2 +- .../base/credential-selector/trigger.tsx | 10 +- .../data-source/base/header.spec.tsx | 5 +- .../data-source/base/header.tsx | 36 +- .../data-source/local-file/index.tsx | 92 +- .../online-documents/index.spec.tsx | 10 +- .../data-source/online-documents/index.tsx | 81 +- .../page-selector/index.spec.tsx | 4 +- .../online-documents/page-selector/index.tsx | 8 +- .../online-documents/page-selector/item.tsx | 73 +- .../online-documents/page-selector/utils.ts | 2 +- .../data-source/online-documents/title.tsx | 2 +- .../online-drive/connect/index.spec.tsx | 2 +- .../online-drive/connect/index.tsx | 22 +- .../file-list/header/breadcrumbs/bucket.tsx | 12 +- .../file-list/header/breadcrumbs/drive.tsx | 6 +- .../breadcrumbs/dropdown/index.spec.tsx | 11 +- .../header/breadcrumbs/dropdown/index.tsx | 12 +- .../header/breadcrumbs/dropdown/item.tsx | 2 +- .../header/breadcrumbs/dropdown/menu.tsx | 2 +- .../file-list/header/breadcrumbs/index.tsx | 12 +- .../file-list/header/breadcrumbs/item.tsx | 4 +- .../online-drive/file-list/header/index.tsx | 8 +- .../online-drive/file-list/index.spec.tsx | 10 +- .../online-drive/file-list/index.tsx | 6 +- .../file-list/list/empty-folder.tsx | 4 +- .../file-list/list/empty-search-result.tsx | 18 +- .../online-drive/file-list/list/file-icon.tsx | 6 +- .../file-list/list/index.spec.tsx | 61 +- .../online-drive/file-list/list/index.tsx | 26 +- .../online-drive/file-list/list/item.tsx | 42 +- .../data-source/online-drive/header.tsx | 28 +- .../data-source/online-drive/index.spec.tsx | 16 +- .../data-source/online-drive/index.tsx | 43 +- .../data-source/online-drive/utils.ts | 10 +- .../data-source/store/index.ts | 12 +- .../base/checkbox-with-label.tsx | 6 +- .../base/crawled-result-item.tsx | 49 +- .../website-crawl/base/crawled-result.tsx | 16 +- .../website-crawl/base/crawling.tsx | 30 +- .../website-crawl/base/error-message.tsx | 15 +- .../website-crawl/base/index.spec.tsx | 7 +- .../website-crawl/base/options/index.spec.tsx | 16 +- .../website-crawl/base/options/index.tsx | 36 +- .../data-source/website-crawl/index.spec.tsx | 12 +- .../data-source/website-crawl/index.tsx | 52 +- .../documents/create-from-pipeline/hooks.ts | 11 +- .../documents/create-from-pipeline/index.tsx | 89 +- .../create-from-pipeline/left-header.tsx | 30 +- .../preview/chunk-preview.spec.tsx | 14 +- .../preview/chunk-preview.tsx | 242 +- .../preview/file-preview.spec.tsx | 2 +- .../preview/file-preview.tsx | 34 +- .../create-from-pipeline/preview/loading.tsx | 72 +- .../preview/online-document-preview.spec.tsx | 7 +- .../preview/online-document-preview.tsx | 36 +- .../preview/web-preview.spec.tsx | 2 +- .../preview/web-preview.tsx | 28 +- .../process-documents/actions.tsx | 18 +- .../process-documents/components.spec.tsx | 10 +- .../process-documents/form.tsx | 10 +- .../process-documents/header.tsx | 20 +- .../process-documents/hooks.ts | 2 +- .../process-documents/index.spec.tsx | 6 +- .../process-documents/index.tsx | 8 +- .../embedding-process/index.spec.tsx | 11 +- .../processing/embedding-process/index.tsx | 106 +- .../embedding-process/rule-detail.spec.tsx | 13 +- .../embedding-process/rule-detail.tsx | 31 +- .../processing/index.spec.tsx | 9 +- .../create-from-pipeline/processing/index.tsx | 32 +- .../create-from-pipeline/step-indicator.tsx | 4 +- .../detail/batch-modal/csv-downloader.tsx | 70 +- .../detail/batch-modal/csv-uploader.tsx | 44 +- .../documents/detail/batch-modal/index.tsx | 22 +- .../detail/completed/child-segment-detail.tsx | 43 +- .../detail/completed/child-segment-list.tsx | 145 +- .../completed/common/action-buttons.tsx | 51 +- .../detail/completed/common/add-another.tsx | 11 +- .../detail/completed/common/batch-action.tsx | 67 +- .../detail/completed/common/chunk-content.tsx | 40 +- .../documents/detail/completed/common/dot.tsx | 2 +- .../detail/completed/common/drawer.tsx | 42 +- .../detail/completed/common/empty.tsx | 43 +- .../completed/common/full-screen-drawer.tsx | 9 +- .../detail/completed/common/keywords.tsx | 26 +- .../completed/common/regeneration-modal.tsx | 45 +- .../completed/common/segment-index-tag.tsx | 3 +- .../documents/detail/completed/common/tag.tsx | 6 +- .../detail/completed/display-toggle.tsx | 19 +- .../documents/detail/completed/index.tsx | 211 +- .../detail/completed/new-child-segment.tsx | 86 +- .../completed/segment-card/chunk-content.tsx | 26 +- .../completed/segment-card/index.spec.tsx | 21 +- .../detail/completed/segment-card/index.tsx | 204 +- .../detail/completed/segment-detail.tsx | 65 +- .../detail/completed/segment-list.tsx | 35 +- .../skeleton/full-doc-list-skeleton.tsx | 12 +- .../skeleton/general-list-skeleton.tsx | 64 +- .../skeleton/paragraph-list-skeleton.tsx | 66 +- .../skeleton/parent-chunk-card-skeleton.tsx | 34 +- .../detail/completed/status-item.tsx | 11 +- .../documents/detail/document-title.tsx | 2 +- .../documents/detail/embedding/index.tsx | 147 +- .../detail/embedding/skeleton/index.tsx | 58 +- .../datasets/documents/detail/index.tsx | 111 +- .../documents/detail/metadata/index.tsx | 312 +- .../datasets/documents/detail/new-segment.tsx | 79 +- .../documents/detail/segment-add/index.tsx | 78 +- .../detail/settings/document-settings.tsx | 36 +- .../settings/pipeline-settings/index.spec.tsx | 14 +- .../settings/pipeline-settings/index.tsx | 34 +- .../pipeline-settings/left-header.tsx | 20 +- .../process-documents/actions.tsx | 6 +- .../process-documents/index.spec.tsx | 10 +- .../process-documents/index.tsx | 8 +- .../hooks/use-document-list-query-state.ts | 5 +- .../components/datasets/documents/index.tsx | 209 +- .../components/datasets/documents/list.tsx | 300 +- .../datasets/documents/operations.tsx | 290 +- .../datasets/documents/rename-modal.tsx | 16 +- .../documents/status-item/index.spec.tsx | 16 +- .../datasets/documents/status-item/index.tsx | 107 +- .../external-api/external-api-modal/Form.tsx | 18 +- .../external-api/external-api-modal/index.tsx | 95 +- .../external-api/external-api-panel/index.tsx | 57 +- .../external-knowledge-api-card/index.tsx | 34 +- .../connector/index.spec.tsx | 4 +- .../connector/index.tsx | 6 +- .../create/ExternalApiSelect.tsx | 36 +- .../create/ExternalApiSelection.tsx | 55 +- .../create/InfoPanel.tsx | 24 +- .../create/KnowledgeBaseInfo.tsx | 22 +- .../create/RetrievalSettings.tsx | 23 +- .../create/index.spec.tsx | 4 +- .../external-knowledge-base/create/index.tsx | 59 +- .../components/datasets/extra-info/index.tsx | 8 +- .../datasets/extra-info/service-api/card.tsx | 74 +- .../datasets/extra-info/service-api/index.tsx | 17 +- .../datasets/extra-info/statistics.tsx | 42 +- .../formatted-text/flavours/edit-slice.tsx | 72 +- .../formatted-text/flavours/preview-slice.tsx | 25 +- .../formatted-text/flavours/shared.tsx | 55 +- .../datasets/formatted-text/formatted.tsx | 12 +- .../components/child-chunks-item.tsx | 11 +- .../components/chunk-detail-modal.tsx | 61 +- .../hit-testing/components/empty-records.tsx | 12 +- .../datasets/hit-testing/components/mask.tsx | 3 +- .../components/query-input/index.tsx | 97 +- .../components/query-input/textarea.tsx | 42 +- .../hit-testing/components/records.tsx | 42 +- .../components/result-item-external.tsx | 24 +- .../components/result-item-footer.tsx | 4 +- .../components/result-item-meta.tsx | 10 +- .../hit-testing/components/result-item.tsx | 49 +- .../datasets/hit-testing/components/score.tsx | 7 +- .../components/datasets/hit-testing/index.tsx | 111 +- .../modify-external-retrieval-modal.tsx | 30 +- .../hit-testing/modify-retrieval-modal.tsx | 70 +- .../datasets/list/dataset-card/index.tsx | 121 +- .../list/dataset-card/operation-item.tsx | 8 +- .../datasets/list/dataset-card/operations.tsx | 12 +- .../datasets/list/dataset-footer/index.tsx | 15 +- web/app/components/datasets/list/datasets.tsx | 8 +- web/app/components/datasets/list/index.tsx | 46 +- .../datasets/list/new-dataset-card/index.tsx | 18 +- .../datasets/list/new-dataset-card/option.tsx | 8 +- .../datasets/metadata/add-metadata-button.tsx | 12 +- .../datasets/metadata/base/date-picker.tsx | 16 +- .../metadata/edit-metadata-batch/add-row.tsx | 10 +- .../metadata/edit-metadata-batch/edit-row.tsx | 44 +- .../edit-metadata-batch/edited-beacon.tsx | 32 +- .../edit-metadata-batch/input-combined.tsx | 10 +- .../input-has-set-multiple-value.tsx | 10 +- .../metadata/edit-metadata-batch/label.tsx | 7 +- .../metadata/edit-metadata-batch/modal.tsx | 83 +- .../hooks/use-batch-edit-document-metadata.ts | 11 +- .../hooks/use-edit-dataset-metadata.ts | 15 +- .../metadata/hooks/use-metadata-document.ts | 17 +- .../metadata-dataset/create-content.tsx | 30 +- .../create-metadata-modal.tsx | 10 +- .../dataset-metadata-drawer.tsx | 87 +- .../metadata/metadata-dataset/field.tsx | 4 +- .../select-metadata-modal.tsx | 54 +- .../metadata-dataset/select-metadata.tsx | 41 +- .../metadata/metadata-document/field.tsx | 6 +- .../metadata/metadata-document/index.tsx | 108 +- .../metadata/metadata-document/info-group.tsx | 61 +- .../metadata/metadata-document/no-data.tsx | 14 +- .../datasets/metadata/utils/get-icon.ts | 2 +- .../datasets/no-linked-apps-panel.tsx | 17 +- .../components/datasets/preview/container.tsx | 28 +- .../components/datasets/preview/header.tsx | 18 +- .../datasets/rename-modal/index.tsx | 44 +- .../settings/chunk-structure/hooks.tsx | 11 +- .../settings/chunk-structure/index.tsx | 6 +- .../datasets/settings/form/index.tsx | 313 +- .../datasets/settings/index-method/index.tsx | 26 +- .../settings/index-method/keyword-number.tsx | 22 +- .../datasets/settings/option-card.tsx | 32 +- .../settings/permission-selector/index.tsx | 83 +- .../permission-selector/member-item.tsx | 18 +- .../permission-selector/permission-item.tsx | 8 +- .../datasets/settings/utils/index.tsx | 3 +- web/app/components/develop/ApiServer.tsx | 17 +- web/app/components/develop/code.tsx | 40 +- web/app/components/develop/doc.tsx | 170 +- web/app/components/develop/index.tsx | 14 +- web/app/components/develop/md.tsx | 11 +- .../develop/secret-key/input-copy.tsx | 23 +- .../develop/secret-key/secret-key-button.tsx | 10 +- .../secret-key/secret-key-generate.tsx | 20 +- .../develop/secret-key/secret-key-modal.tsx | 62 +- web/app/components/develop/tag.tsx | 4 +- .../explore/app-card/index.spec.tsx | 7 +- web/app/components/explore/app-card/index.tsx | 50 +- web/app/components/explore/app-list/index.tsx | 48 +- web/app/components/explore/category.tsx | 8 +- .../explore/create-app-modal/index.spec.tsx | 66 +- .../explore/create-app-modal/index.tsx | 109 +- web/app/components/explore/index.tsx | 12 +- .../explore/installed-app/index.spec.tsx | 20 +- .../explore/installed-app/index.tsx | 73 +- .../explore/item-operation/index.tsx | 22 +- .../explore/sidebar/app-nav-item/index.tsx | 28 +- web/app/components/explore/sidebar/index.tsx | 28 +- .../components/goto-anything/actions/app.tsx | 15 +- .../actions/commands/account.tsx | 6 +- .../actions/commands/community.tsx | 6 +- .../goto-anything/actions/commands/docs.tsx | 10 +- .../goto-anything/actions/commands/forum.tsx | 6 +- .../goto-anything/actions/commands/index.ts | 18 +- .../actions/commands/language.tsx | 4 +- .../actions/commands/registry.ts | 2 +- .../goto-anything/actions/commands/slash.tsx | 23 +- .../goto-anything/actions/commands/theme.tsx | 16 +- .../goto-anything/actions/commands/zen.tsx | 8 +- .../components/goto-anything/actions/index.ts | 12 +- .../goto-anything/actions/knowledge.tsx | 7 +- .../goto-anything/actions/plugin.tsx | 4 +- .../components/goto-anything/actions/types.ts | 6 +- .../goto-anything/command-selector.spec.tsx | 16 +- .../goto-anything/command-selector.tsx | 70 +- .../components/goto-anything/context.spec.tsx | 10 +- web/app/components/goto-anything/context.tsx | 2 +- .../components/goto-anything/index.spec.tsx | 4 +- web/app/components/goto-anything/index.tsx | 307 +- .../components/header/account-about/index.tsx | 87 +- .../header/account-dropdown/compliance.tsx | 202 +- .../header/account-dropdown/index.tsx | 218 +- .../header/account-dropdown/support.tsx | 184 +- .../workplace-selector/index.tsx | 41 +- .../Integrations-page/index.tsx | 22 +- .../api-based-extension-page/empty.tsx | 17 +- .../api-based-extension-page/index.tsx | 12 +- .../api-based-extension-page/item.tsx | 28 +- .../api-based-extension-page/modal.tsx | 47 +- .../api-based-extension-page/selector.tsx | 72 +- .../header/account-setting/collapse/index.tsx | 10 +- .../data-source-page-new/card.tsx | 48 +- .../data-source-page-new/configure.tsx | 56 +- .../data-source-page-new/hooks/index.ts | 2 +- .../hooks/use-data-source-auth-update.ts | 3 +- .../data-source-page-new/index.tsx | 8 +- .../install-from-marketplace.tsx | 50 +- .../data-source-page-new/item.tsx | 36 +- .../data-source-page-new/operator.tsx | 84 +- .../data-source-notion/index.tsx | 17 +- .../data-source-notion/operate/index.tsx | 36 +- .../config-firecrawl-modal.tsx | 62 +- .../config-jina-reader-modal.tsx | 58 +- .../config-watercrawl-modal.tsx | 62 +- .../data-source-website/index.tsx | 39 +- .../data-source-page/panel/config-item.tsx | 34 +- .../data-source-page/panel/index.tsx | 86 +- .../header/account-setting/index.tsx | 76 +- .../key-validator/KeyInput.tsx | 13 +- .../account-setting/key-validator/Operate.tsx | 46 +- .../key-validator/ValidateStatus.tsx | 13 +- .../account-setting/key-validator/hooks.ts | 9 +- .../account-setting/key-validator/index.tsx | 23 +- .../account-setting/language-page/index.tsx | 12 +- .../edit-workspace-modal/index.tsx | 38 +- .../account-setting/members-page/index.tsx | 153 +- .../members-page/invite-modal/index.tsx | 71 +- .../invite-modal/role-selector.tsx | 102 +- .../members-page/invited-modal/index.tsx | 100 +- .../invited-modal/invitation-link.tsx | 14 +- .../members-page/operation/index.tsx | 41 +- .../operation/transfer-ownership.tsx | 10 +- .../transfer-ownership-modal/index.tsx | 88 +- .../member-selector.tsx | 44 +- .../header/account-setting/menu-dialog.tsx | 11 +- .../model-provider-page/declarations.ts | 10 +- .../model-provider-page/hooks.spec.ts | 2 +- .../model-provider-page/hooks.ts | 54 +- .../model-provider-page/index.tsx | 59 +- .../install-from-marketplace.tsx | 52 +- .../add-credential-in-load-balancing.tsx | 33 +- .../model-auth/add-custom-model.tsx | 63 +- .../model-auth/authorized/authorized-item.tsx | 18 +- .../model-auth/authorized/credential-item.tsx | 39 +- .../model-auth/authorized/index.tsx | 75 +- .../model-auth/config-model.tsx | 18 +- .../model-auth/config-provider.tsx | 28 +- .../model-auth/credential-selector.tsx | 38 +- .../model-auth/hooks/index.ts | 6 +- .../model-auth/hooks/use-auth-service.ts | 6 +- .../model-auth/hooks/use-auth.ts | 24 +- .../model-auth/hooks/use-credential-data.ts | 4 +- .../model-auth/hooks/use-credential-status.ts | 2 +- .../hooks/use-model-form-schemas.ts | 6 +- .../model-provider-page/model-auth/index.tsx | 8 +- .../manage-custom-model-credentials.tsx | 28 +- .../switch-credential-in-load-balancing.tsx | 52 +- .../model-provider-page/model-badge/index.tsx | 3 +- .../model-provider-page/model-icon/index.tsx | 21 +- .../model-provider-page/model-modal/Form.tsx | 141 +- .../model-modal/Input.test.tsx | 2 +- .../model-provider-page/model-modal/Input.tsx | 6 +- .../model-provider-page/model-modal/index.tsx | 144 +- .../model-provider-page/model-name/index.tsx | 14 +- .../agent-model-trigger.tsx | 155 +- .../configuration-button.tsx | 5 +- .../model-parameter-modal/index.tsx | 86 +- .../model-parameter-modal/parameter-item.tsx | 88 +- .../presets-parameter.tsx | 12 +- .../status-indicators.tsx | 76 +- .../model-parameter-modal/trigger.tsx | 52 +- .../deprecated-model-trigger.tsx | 12 +- .../model-selector/empty-trigger.tsx | 20 +- .../model-selector/feature-icon.tsx | 38 +- .../model-selector/index.tsx | 18 +- .../model-selector/model-trigger.tsx | 32 +- .../model-selector/popup-item.tsx | 63 +- .../model-selector/popup.tsx | 55 +- .../provider-added-card/add-model-button.tsx | 2 +- .../provider-added-card/cooldown-timer.tsx | 10 +- .../provider-added-card/credential-panel.tsx | 31 +- .../provider-added-card/index.tsx | 70 +- .../provider-added-card/model-list-item.tsx | 67 +- .../provider-added-card/model-list.tsx | 40 +- .../model-load-balancing-configs.tsx | 103 +- .../model-load-balancing-modal.tsx | 231 +- .../provider-added-card/priority-selector.tsx | 33 +- .../provider-added-card/priority-use-tip.tsx | 4 +- .../provider-added-card/quota-panel.tsx | 14 +- .../provider-icon/index.tsx | 22 +- .../system-model-selector/index.tsx | 106 +- .../model-provider-page/utils.ts | 14 +- .../plugin-page/SerpapiPlugin.tsx | 14 +- .../account-setting/plugin-page/index.tsx | 23 +- .../account-setting/plugin-page/utils.ts | 2 +- web/app/components/header/app-back/index.tsx | 8 +- web/app/components/header/app-nav/index.tsx | 24 +- .../components/header/app-selector/index.tsx | 79 +- .../components/header/dataset-nav/index.tsx | 25 +- web/app/components/header/env-nav/index.tsx | 15 +- .../components/header/explore-nav/index.tsx | 20 +- .../header/github-star/index.spec.tsx | 4 +- .../components/header/github-star/index.tsx | 4 +- web/app/components/header/header-wrapper.tsx | 10 +- web/app/components/header/index.tsx | 68 +- web/app/components/header/indicator/index.tsx | 6 +- .../components/header/license-env/index.tsx | 28 +- .../components/header/maintenance-notice.tsx | 12 +- web/app/components/header/nav/index.tsx | 23 +- .../header/nav/nav-selector/index.tsx | 90 +- .../components/header/plan-badge/index.tsx | 75 +- .../header/plugins-nav/downloading-icon.tsx | 6 +- .../components/header/plugins-nav/index.tsx | 34 +- web/app/components/header/tools-nav/index.tsx | 21 +- web/app/components/i18n-server.tsx | 4 +- web/app/components/i18n.tsx | 11 +- .../plugins/base/badges/icon-with-tooltip.tsx | 9 +- .../plugins/base/badges/partner.tsx | 2 +- .../plugins/base/badges/verified.tsx | 2 +- .../plugins/base/deprecation-notice.tsx | 29 +- .../plugins/base/key-value-item.tsx | 16 +- .../plugins/card/base/card-icon.tsx | 24 +- .../plugins/card/base/corner-mark.tsx | 2 +- .../plugins/card/base/download-count.tsx | 2 +- .../components/plugins/card/base/org-info.tsx | 5 +- .../plugins/card/base/placeholder.tsx | 25 +- .../components/plugins/card/base/title.tsx | 2 +- web/app/components/plugins/card/index.tsx | 34 +- .../install-plugin/base/check-task-status.ts | 4 +- .../plugins/install-plugin/base/installed.tsx | 24 +- .../install-plugin/base/loading-error.tsx | 29 +- .../plugins/install-plugin/base/loading.tsx | 10 +- .../plugins/install-plugin/base/version.tsx | 20 +- .../plugins/install-plugin/hooks.ts | 18 +- .../hooks/use-check-installed.tsx | 7 +- .../hooks/use-install-plugin-limit.tsx | 6 +- .../hooks/use-refresh-plugin-list.tsx | 16 +- .../install-plugin/install-bundle/index.tsx | 14 +- .../install-bundle/item/github-item.tsx | 9 +- .../install-bundle/item/loaded-item.tsx | 13 +- .../install-bundle/item/marketplace-item.tsx | 7 +- .../install-bundle/item/package-item.tsx | 7 +- .../install-bundle/ready-to-install.tsx | 4 +- .../install-bundle/steps/install-multi.tsx | 38 +- .../install-bundle/steps/install.tsx | 62 +- .../install-bundle/steps/installed.tsx | 22 +- .../install-from-github/index.tsx | 134 +- .../install-from-github/steps/loaded.tsx | 52 +- .../steps/selectPackage.tsx | 56 +- .../install-from-github/steps/setURL.tsx | 30 +- .../install-from-local-package/index.tsx | 64 +- .../ready-to-install.tsx | 14 +- .../steps/install.tsx | 62 +- .../steps/uploading.tsx | 29 +- .../install-from-marketplace/index.tsx | 90 +- .../steps/install.tsx | 62 +- .../plugins/marketplace/context.tsx | 20 +- .../plugins/marketplace/description/index.tsx | 8 +- .../plugins/marketplace/empty/index.tsx | 25 +- .../plugins/marketplace/empty/line.tsx | 24 +- .../components/plugins/marketplace/hooks.ts | 32 +- .../components/plugins/marketplace/index.tsx | 10 +- .../plugins/marketplace/list/card-wrapper.tsx | 64 +- .../plugins/marketplace/list/index.tsx | 9 +- .../marketplace/list/list-with-collection.tsx | 23 +- .../plugins/marketplace/list/list-wrapper.tsx | 22 +- .../marketplace/plugin-type-switch.tsx | 21 +- .../plugins/marketplace/search-box/index.tsx | 40 +- .../search-box/search-box-wrapper.tsx | 4 +- .../marketplace/search-box/tags-filter.tsx | 22 +- .../search-box/trigger/marketplace.tsx | 21 +- .../search-box/trigger/tool-selector.tsx | 17 +- .../marketplace/sort-dropdown/index.tsx | 20 +- .../sticky-search-and-switch-wrapper.tsx | 4 +- .../components/plugins/marketplace/utils.ts | 6 +- .../authorize/add-api-key-button.tsx | 8 +- .../authorize/add-oauth-button.tsx | 69 +- .../plugin-auth/authorize/api-key-modal.tsx | 28 +- .../plugins/plugin-auth/authorize/index.tsx | 20 +- .../authorize/oauth-client-settings.tsx | 40 +- .../authorized-in-data-source-node.tsx | 8 +- .../plugin-auth/authorized-in-node.tsx | 18 +- .../plugins/plugin-auth/authorized/index.tsx | 92 +- .../plugins/plugin-auth/authorized/item.tsx | 58 +- .../plugin-auth/hooks/use-credential.ts | 5 +- .../plugins/plugin-auth/hooks/use-get-api.ts | 6 +- .../hooks/use-plugin-auth-action.ts | 2 +- .../plugin-auth/hooks/use-plugin-auth.ts | 4 +- .../components/plugins/plugin-auth/index.tsx | 16 +- .../plugin-auth/plugin-auth-in-agent.tsx | 23 +- .../plugin-auth-in-datasource-node.tsx | 12 +- .../plugins/plugin-auth/plugin-auth.tsx | 4 +- .../plugin-detail-panel/action-list.tsx | 10 +- .../agent-strategy-list.tsx | 10 +- .../app-selector/app-inputs-form.tsx | 16 +- .../app-selector/app-inputs-panel.tsx | 24 +- .../app-selector/app-picker.tsx | 48 +- .../app-selector/app-trigger.tsx | 17 +- .../app-selector/index.tsx | 37 +- .../datasource-action-list.tsx | 12 +- .../plugin-detail-panel/detail-header.tsx | 192 +- .../plugin-detail-panel/endpoint-card.tsx | 64 +- .../plugin-detail-panel/endpoint-list.tsx | 54 +- .../plugin-detail-panel/endpoint-modal.tsx | 51 +- .../plugins/plugin-detail-panel/index.tsx | 37 +- .../plugin-detail-panel/model-list.tsx | 14 +- .../model-selector/index.tsx | 104 +- .../model-selector/llm-params-panel.tsx | 16 +- .../model-selector/tts-params-panel.tsx | 14 +- .../multiple-tool-selector/index.tsx | 55 +- .../operation-dropdown.tsx | 40 +- .../plugins/plugin-detail-panel/store.ts | 2 +- .../plugin-detail-panel/strategy-detail.tsx | 82 +- .../plugin-detail-panel/strategy-item.tsx | 8 +- .../subscription-list/create/common-modal.tsx | 225 +- .../subscription-list/create/index.tsx | 224 +- .../subscription-list/create/oauth-client.tsx | 88 +- .../subscription-list/delete-confirm.tsx | 49 +- .../subscription-list/index.tsx | 4 +- .../subscription-list/list-view.tsx | 12 +- .../subscription-list/log-viewer.tsx | 69 +- .../subscription-list/selector-entry.tsx | 68 +- .../subscription-list/selector-view.tsx | 57 +- .../subscription-list/subscription-card.tsx | 28 +- .../tool-selector/index.tsx | 134 +- .../tool-selector/reasoning-config-form.tsx | 129 +- .../tool-selector/schema-modal.tsx | 27 +- .../tool-selector/tool-credentials-form.tsx | 80 +- .../tool-selector/tool-item.tsx | 69 +- .../tool-selector/tool-trigger.tsx | 17 +- .../trigger/event-detail-drawer.tsx | 100 +- .../trigger/event-list.tsx | 21 +- .../components/plugins/plugin-item/action.tsx | 46 +- .../components/plugins/plugin-item/index.tsx | 147 +- .../plugins/plugin-mutation-model/index.tsx | 26 +- .../plugins/plugin-page/context.tsx | 8 +- .../plugins/plugin-page/debug-info.tsx | 45 +- .../plugins/plugin-page/empty/index.tsx | 79 +- .../filter-management/category-filter.tsx | 39 +- .../plugin-page/filter-management/index.tsx | 8 +- .../filter-management/search-box.tsx | 7 +- .../plugin-page/filter-management/store.ts | 2 +- .../filter-management/tag-filter.tsx | 39 +- .../components/plugins/plugin-page/index.tsx | 97 +- .../plugin-page/install-plugin-dropdown.tsx | 58 +- .../plugins/plugin-page/list/index.tsx | 6 +- .../plugins/plugin-page/plugin-info.tsx | 8 +- .../plugins/plugin-page/plugin-tasks/hooks.ts | 2 +- .../plugin-page/plugin-tasks/index.tsx | 130 +- .../plugins/plugin-page/plugins-panel.tsx | 52 +- .../plugin-page/use-reference-setting.ts | 10 +- .../plugins/plugin-page/use-uploader.ts | 16 +- web/app/components/plugins/provider-card.tsx | 48 +- .../plugins/readme-panel/entrance.tsx | 14 +- .../components/plugins/readme-panel/index.tsx | 61 +- .../components/plugins/readme-panel/store.ts | 12 +- .../auto-update-setting/config.ts | 1 + .../auto-update-setting/index.tsx | 60 +- .../no-data-placeholder.tsx | 12 +- .../no-plugin-selected.tsx | 4 +- .../auto-update-setting/plugins-picker.tsx | 42 +- .../auto-update-setting/plugins-selected.tsx | 13 +- .../auto-update-setting/strategy-picker.tsx | 34 +- .../auto-update-setting/tool-item.tsx | 24 +- .../auto-update-setting/tool-picker.tsx | 32 +- .../auto-update-setting/utils.ts | 5 +- .../plugins/reference-setting-modal/label.tsx | 4 +- .../plugins/reference-setting-modal/modal.tsx | 32 +- web/app/components/plugins/types.ts | 38 +- .../update-plugin/downgrade-warning.tsx | 14 +- .../plugins/update-plugin/from-github.tsx | 2 +- .../update-plugin/from-market-place.tsx | 38 +- .../plugins/update-plugin/index.tsx | 2 +- .../update-plugin/plugin-version-picker.tsx | 31 +- .../components/chunk-card-list/chunk-card.tsx | 25 +- .../components/chunk-card-list/index.tsx | 7 +- .../components/chunk-card-list/q-a-item.tsx | 6 +- .../rag-pipeline/components/conversion.tsx | 40 +- .../rag-pipeline/components/panel/index.tsx | 4 +- .../input-field/editor/form/hidden-fields.tsx | 2 +- .../panel/input-field/editor/form/hooks.ts | 22 +- .../panel/input-field/editor/form/index.tsx | 32 +- .../panel/input-field/editor/form/schema.ts | 8 +- .../editor/form/show-all-settings.tsx | 20 +- .../panel/input-field/editor/index.tsx | 22 +- .../panel/input-field/editor/utils.ts | 2 +- .../input-field/field-list/field-item.tsx | 75 +- .../field-list/field-list-container.tsx | 10 +- .../panel/input-field/field-list/hooks.ts | 24 +- .../panel/input-field/field-list/index.tsx | 18 +- .../panel/input-field/field-list/types.ts | 4 +- .../panel/input-field/footer-tip.tsx | 8 +- .../components/panel/input-field/hooks.ts | 5 +- .../components/panel/input-field/index.tsx | 66 +- .../label-right-content/datasource.tsx | 12 +- .../label-right-content/global-inputs.tsx | 8 +- .../panel/input-field/preview/data-source.tsx | 12 +- .../panel/input-field/preview/form.tsx | 6 +- .../panel/input-field/preview/index.tsx | 24 +- .../input-field/preview/process-documents.tsx | 4 +- .../components/panel/test-run/header.tsx | 14 +- .../components/panel/test-run/index.tsx | 22 +- .../test-run/preparation/actions/index.tsx | 8 +- .../preparation/data-source-options/index.tsx | 4 +- .../data-source-options/option-card.tsx | 10 +- .../document-processing/actions.tsx | 12 +- .../preparation/document-processing/index.tsx | 6 +- .../document-processing/options.tsx | 14 +- .../test-run/preparation/footer-tips.tsx | 2 +- .../panel/test-run/preparation/hooks.ts | 8 +- .../panel/test-run/preparation/index.tsx | 39 +- .../test-run/preparation/step-indicator.tsx | 14 +- .../panel/test-run/result/index.tsx | 14 +- .../test-run/result/result-preview/index.tsx | 24 +- .../test-run/result/result-preview/utils.ts | 5 +- .../panel/test-run/result/tabs/index.tsx | 10 +- .../panel/test-run/result/tabs/tab.tsx | 4 +- .../publish-as-knowledge-pipeline-modal.tsx | 52 +- .../rag-pipeline/components/publish-toast.tsx | 28 +- .../components/rag-pipeline-children.tsx | 14 +- .../components/rag-pipeline-header/index.tsx | 4 +- .../input-field-button.tsx | 12 +- .../rag-pipeline-header/publisher/index.tsx | 14 +- .../rag-pipeline-header/publisher/popup.tsx | 166 +- .../rag-pipeline-header/run-mode.tsx | 32 +- .../components/rag-pipeline-main.tsx | 8 +- .../rag-pipeline/components/screenshot.tsx | 8 +- .../components/update-dsl-modal.tsx | 108 +- .../components/rag-pipeline/hooks/index.ts | 10 +- .../components/rag-pipeline/hooks/use-DSL.ts | 8 +- .../hooks/use-available-nodes-meta-data.ts | 10 +- .../rag-pipeline/hooks/use-configs-map.ts | 2 +- .../hooks/use-input-field-panel.ts | 2 +- .../rag-pipeline/hooks/use-input-fields.ts | 5 +- .../hooks/use-nodes-sync-draft.ts | 10 +- .../rag-pipeline/hooks/use-pipeline-config.ts | 8 +- .../rag-pipeline/hooks/use-pipeline-init.ts | 6 +- .../hooks/use-pipeline-refresh-draft.ts | 4 +- .../rag-pipeline/hooks/use-pipeline-run.ts | 19 +- .../hooks/use-pipeline-start-run.tsx | 2 +- .../hooks/use-pipeline-template.ts | 4 +- .../rag-pipeline/hooks/use-pipeline.tsx | 7 +- .../hooks/use-rag-pipeline-search.tsx | 99 +- web/app/components/rag-pipeline/index.tsx | 12 +- .../components/rag-pipeline/store/index.ts | 8 +- .../components/rag-pipeline/utils/nodes.ts | 12 +- web/app/components/react-scan.tsx | 2 +- web/app/components/sentry-initializer.tsx | 2 +- .../share/text-generation/index.tsx | 202 +- .../share/text-generation/info-modal.tsx | 25 +- .../share/text-generation/menu-dropdown.tsx | 46 +- .../text-generation/no-data/index.spec.tsx | 2 +- .../share/text-generation/no-data/index.tsx | 8 +- .../share/text-generation/result/content.tsx | 11 +- .../share/text-generation/result/header.tsx | 32 +- .../share/text-generation/result/index.tsx | 79 +- .../run-batch/csv-download/index.spec.tsx | 6 +- .../run-batch/csv-download/index.tsx | 28 +- .../run-batch/csv-reader/index.spec.tsx | 8 +- .../run-batch/csv-reader/index.tsx | 32 +- .../text-generation/run-batch/index.spec.tsx | 8 +- .../share/text-generation/run-batch/index.tsx | 15 +- .../run-batch/res-download/index.spec.tsx | 2 +- .../run-batch/res-download/index.tsx | 10 +- .../text-generation/run-once/index.spec.tsx | 8 +- .../share/text-generation/run-once/index.tsx | 234 +- web/app/components/signin/countdown.tsx | 21 +- web/app/components/splash.tsx | 2 +- web/app/components/swr-initializer.tsx | 29 +- .../config-credentials.tsx | 85 +- .../get-schema.tsx | 48 +- .../edit-custom-collection-modal/index.tsx | 175 +- .../edit-custom-collection-modal/test-api.tsx | 73 +- web/app/components/tools/labels/filter.tsx | 70 +- web/app/components/tools/labels/selector.tsx | 49 +- web/app/components/tools/labels/store.ts | 2 +- web/app/components/tools/marketplace/hooks.ts | 4 +- .../tools/marketplace/index.spec.tsx | 17 +- .../components/tools/marketplace/index.tsx | 26 +- web/app/components/tools/mcp/create-card.tsx | 34 +- .../components/tools/mcp/detail/content.tsx | 112 +- .../tools/mcp/detail/list-loading.tsx | 40 +- .../tools/mcp/detail/operation-dropdown.tsx | 22 +- .../tools/mcp/detail/provider-detail.tsx | 6 +- .../components/tools/mcp/detail/tool-item.tsx | 41 +- .../components/tools/mcp/headers-input.tsx | 61 +- web/app/components/tools/mcp/index.tsx | 11 +- .../components/tools/mcp/mcp-server-modal.tsx | 51 +- .../tools/mcp/mcp-server-param-item.tsx | 17 +- .../components/tools/mcp/mcp-service-card.tsx | 117 +- web/app/components/tools/mcp/modal.tsx | 153 +- .../components/tools/mcp/provider-card.tsx | 48 +- web/app/components/tools/provider-list.tsx | 56 +- .../tools/provider/custom-create-card.tsx | 39 +- web/app/components/tools/provider/detail.tsx | 128 +- web/app/components/tools/provider/empty.tsx | 15 +- .../components/tools/provider/tool-item.tsx | 10 +- .../setting/build-in/config-credentials.tsx | 111 +- .../components/tools/utils/to-form-schema.ts | 12 +- .../tools/workflow-tool/configure-button.tsx | 107 +- .../confirm-modal/index.spec.tsx | 2 +- .../workflow-tool/confirm-modal/index.tsx | 28 +- .../components/tools/workflow-tool/index.tsx | 195 +- .../tools/workflow-tool/method-selector.tsx | 49 +- .../tools/workflow-tool/utils.test.ts | 2 +- .../components/workflow-children.tsx | 27 +- .../chat-variable-trigger.spec.tsx | 2 +- .../workflow-header/features-trigger.spec.tsx | 8 +- .../workflow-header/features-trigger.tsx | 54 +- .../components/workflow-header/index.spec.tsx | 10 +- .../components/workflow-header/index.tsx | 8 +- .../workflow-app/components/workflow-main.tsx | 6 +- .../workflow-onboarding-modal/index.spec.tsx | 4 +- .../workflow-onboarding-modal/index.tsx | 12 +- .../start-node-option.spec.tsx | 2 +- .../start-node-option.tsx | 5 +- .../start-node-selection-panel.spec.tsx | 4 +- .../start-node-selection-panel.tsx | 17 +- .../components/workflow-panel.tsx | 8 +- .../components/workflow-app/hooks/index.ts | 20 +- .../components/workflow-app/hooks/use-DSL.ts | 8 +- .../hooks/use-available-nodes-meta-data.ts | 28 +- .../workflow-app/hooks/use-configs-map.ts | 2 +- .../hooks/use-nodes-sync-draft.ts | 10 +- .../workflow-app/hooks/use-workflow-init.ts | 14 +- .../hooks/use-workflow-refresh-draft.ts | 4 +- .../workflow-app/hooks/use-workflow-run.ts | 44 +- .../hooks/use-workflow-start-run.tsx | 6 +- .../hooks/use-workflow-template.ts | 10 +- web/app/components/workflow-app/index.tsx | 46 +- .../__tests__/trigger-status-sync.test.tsx | 25 +- web/app/components/workflow/block-icon.tsx | 46 +- .../block-selector/all-start-blocks.tsx | 78 +- .../workflow/block-selector/all-tools.tsx | 83 +- .../workflow/block-selector/blocks.tsx | 43 +- .../workflow/block-selector/data-sources.tsx | 30 +- .../block-selector/featured-tools.tsx | 89 +- .../block-selector/featured-triggers.tsx | 86 +- .../workflow/block-selector/hooks.ts | 3 +- .../workflow/block-selector/index-bar.tsx | 4 +- .../workflow/block-selector/index.tsx | 4 +- .../workflow/block-selector/main.tsx | 64 +- .../market-place-plugin/action.tsx | 25 +- .../market-place-plugin/item.tsx | 26 +- .../market-place-plugin/list.tsx | 32 +- .../rag-tool-recommendations/index.tsx | 48 +- .../rag-tool-recommendations/list.tsx | 56 +- .../uninstalled-item.tsx | 24 +- .../workflow/block-selector/start-blocks.tsx | 51 +- .../workflow/block-selector/tabs.tsx | 34 +- .../workflow/block-selector/tool-picker.tsx | 44 +- .../block-selector/tool/action-item.tsx | 35 +- .../tool/tool-list-flat-view/list.tsx | 12 +- .../tool/tool-list-tree-view/item.tsx | 11 +- .../tool/tool-list-tree-view/list.tsx | 10 +- .../workflow/block-selector/tool/tool.tsx | 51 +- .../workflow/block-selector/tools.tsx | 67 +- .../trigger-plugin/action-item.tsx | 32 +- .../block-selector/trigger-plugin/item.tsx | 26 +- .../block-selector/trigger-plugin/list.tsx | 6 +- .../workflow/block-selector/types.ts | 10 +- .../use-check-vertical-scrollbar.ts | 3 +- .../block-selector/use-sticky-scroll.ts | 2 +- .../workflow/block-selector/utils.ts | 2 +- .../block-selector/view-type-select.tsx | 24 +- .../workflow/candidate-node-main.tsx | 18 +- .../components/workflow/candidate-node.tsx | 2 +- web/app/components/workflow/constants.ts | 61 +- web/app/components/workflow/constants/node.ts | 34 +- web/app/components/workflow/context.tsx | 4 +- .../workflow/custom-connection-line.tsx | 8 +- .../custom-edge-linear-gradient-render.tsx | 6 +- web/app/components/workflow/custom-edge.tsx | 27 +- .../datasets-detail-store/provider.tsx | 14 +- .../workflow/datasets-detail-store/store.ts | 4 +- .../workflow/dsl-export-confirm-modal.tsx | 50 +- web/app/components/workflow/features.tsx | 11 +- .../workflow/header/chat-variable-button.tsx | 4 +- .../components/workflow/header/checklist.tsx | 93 +- .../workflow/header/editing-title.tsx | 10 +- .../components/workflow/header/env-button.tsx | 6 +- .../header/global-variable-button.tsx | 6 +- .../workflow/header/header-in-normal.tsx | 30 +- .../workflow/header/header-in-restoring.tsx | 32 +- .../header/header-in-view-history.tsx | 22 +- web/app/components/workflow/header/index.tsx | 14 +- .../workflow/header/restoring-title.tsx | 12 +- .../workflow/header/run-and-history.tsx | 16 +- .../components/workflow/header/run-mode.tsx | 83 +- .../workflow/header/running-title.tsx | 10 +- .../header/scroll-to-selected-node-button.tsx | 11 +- .../workflow/header/test-run-menu.tsx | 23 +- .../components/workflow/header/undo-redo.tsx | 54 +- .../header/version-history-button.tsx | 49 +- .../workflow/header/view-history.tsx | 88 +- .../workflow/header/view-workflow-history.tsx | 226 +- .../components/workflow/help-line/index.tsx | 10 +- .../workflow/hooks-store/provider.tsx | 2 +- .../components/workflow/hooks-store/store.ts | 32 +- web/app/components/workflow/hooks/index.ts | 34 +- .../hooks/use-auto-generate-webhook-url.ts | 2 +- .../workflow/hooks/use-available-blocks.ts | 3 +- .../workflow/hooks/use-checklist.ts | 82 +- .../workflow/hooks/use-config-vision.ts | 6 +- .../hooks/use-dynamic-test-run-options.tsx | 53 +- .../use-edges-interactions-without-sync.ts | 2 +- .../workflow/hooks/use-edges-interactions.ts | 12 +- .../hooks/use-fetch-workflow-inspect-vars.ts | 27 +- .../components/workflow/hooks/use-helpline.ts | 4 +- .../hooks/use-inspect-vars-crud-common.ts | 21 +- .../workflow/hooks/use-inspect-vars-crud.ts | 10 +- .../workflow/hooks/use-node-data-update.ts | 6 +- .../hooks/use-node-plugin-installation.ts | 11 +- .../hooks/use-nodes-available-var-list.ts | 4 +- .../use-nodes-interactions-without-sync.ts | 4 +- .../workflow/hooks/use-nodes-interactions.ts | 300 +- .../workflow/hooks/use-nodes-layout.ts | 16 +- .../workflow/hooks/use-nodes-meta-data.ts | 8 +- .../workflow/hooks/use-nodes-sync-draft.ts | 2 +- .../hooks/use-selection-interactions.ts | 10 +- .../workflow/hooks/use-shortcuts.ts | 14 +- .../workflow/hooks/use-tool-icon.ts | 18 +- .../workflow/hooks/use-workflow-history.ts | 9 +- .../hooks/use-workflow-interactions.ts | 33 +- .../hooks/use-workflow-run-event/index.ts | 16 +- .../use-workflow-agent-log.ts | 4 +- .../use-workflow-failed.ts | 2 +- .../use-workflow-finished.ts | 6 +- .../use-workflow-node-finished.ts | 8 +- .../use-workflow-node-iteration-finished.ts | 6 +- .../use-workflow-node-iteration-next.ts | 4 +- .../use-workflow-node-iteration-started.ts | 12 +- .../use-workflow-node-loop-finished.ts | 4 +- .../use-workflow-node-loop-next.ts | 4 +- .../use-workflow-node-loop-started.ts | 8 +- .../use-workflow-node-retry.ts | 6 +- .../use-workflow-node-started.ts | 10 +- .../use-workflow-started.ts | 6 +- .../use-workflow-text-chunk.ts | 4 +- .../use-workflow-text-replace.ts | 4 +- .../workflow/hooks/use-workflow-search.tsx | 122 +- .../workflow/hooks/use-workflow-variables.ts | 18 +- .../components/workflow/hooks/use-workflow.ts | 48 +- web/app/components/workflow/index.tsx | 119 +- .../components/workflow/node-contextmenu.tsx | 10 +- .../nodes/_base/components/add-button.tsx | 10 +- .../add-variable-popup-with-position.tsx | 16 +- .../_base/components/add-variable-popup.tsx | 12 +- .../components/agent-strategy-selector.tsx | 293 +- .../nodes/_base/components/agent-strategy.tsx | 277 +- .../components/before-run-form/bool-input.tsx | 10 +- .../components/before-run-form/form-item.tsx | 161 +- .../_base/components/before-run-form/form.tsx | 14 +- .../components/before-run-form/index.tsx | 41 +- .../components/before-run-form/panel-wrap.tsx | 27 +- .../components/code-generator-button.tsx | 19 +- .../components/collapse/field-collapse.tsx | 6 +- .../nodes/_base/components/collapse/index.tsx | 6 +- .../nodes/_base/components/config-vision.tsx | 50 +- .../nodes/_base/components/editor/base.tsx | 54 +- .../code-editor/editor-support-vars.tsx | 14 +- .../components/editor/code-editor/index.tsx | 73 +- .../_base/components/editor/text-editor.tsx | 4 +- .../components/error-handle/default-value.tsx | 24 +- .../error-handle/error-handle-on-node.tsx | 20 +- .../error-handle/error-handle-on-panel.tsx | 33 +- .../error-handle/error-handle-tip.tsx | 13 +- .../error-handle-type-selector.tsx | 33 +- .../error-handle/fail-branch-card.tsx | 16 +- .../_base/components/error-handle/hooks.ts | 12 +- .../_base/components/error-handle/utils.ts | 2 +- .../workflow/nodes/_base/components/field.tsx | 21 +- .../nodes/_base/components/file-type-item.tsx | 54 +- .../_base/components/file-upload-setting.tsx | 44 +- .../_base/components/form-input-boolean.tsx | 10 +- .../_base/components/form-input-item.tsx | 138 +- .../components/form-input-type-switch.tsx | 8 +- .../workflow/nodes/_base/components/group.tsx | 22 +- .../nodes/_base/components/help-link.tsx | 12 +- .../nodes/_base/components/info-panel.tsx | 6 +- .../_base/components/input-field/add.tsx | 2 +- .../components/input-number-with-slider.tsx | 8 +- .../components/input-support-select-var.tsx | 24 +- .../_base/components/input-var-type-icon.tsx | 2 +- .../components/install-plugin-button.tsx | 34 +- .../components/layout/box-group-field.tsx | 2 +- .../_base/components/layout/box-group.tsx | 8 +- .../nodes/_base/components/layout/box.tsx | 3 +- .../_base/components/layout/field-title.tsx | 6 +- .../nodes/_base/components/layout/field.tsx | 2 +- .../_base/components/layout/group-field.tsx | 2 +- .../nodes/_base/components/layout/group.tsx | 3 +- .../nodes/_base/components/layout/index.tsx | 8 +- .../components/list-no-data-placeholder.tsx | 2 +- .../mcp-tool-not-support-tooltip.tsx | 12 +- .../nodes/_base/components/memory-config.tsx | 53 +- .../mixed-variable-text-input/index.tsx | 12 +- .../mixed-variable-text-input/placeholder.tsx | 19 +- .../nodes/_base/components/next-step/add.tsx | 26 +- .../_base/components/next-step/container.tsx | 9 +- .../_base/components/next-step/index.tsx | 26 +- .../nodes/_base/components/next-step/item.tsx | 20 +- .../nodes/_base/components/next-step/line.tsx | 16 +- .../_base/components/next-step/operator.tsx | 38 +- .../nodes/_base/components/node-control.tsx | 39 +- .../nodes/_base/components/node-handle.tsx | 32 +- .../nodes/_base/components/node-resizer.tsx | 15 +- .../nodes/_base/components/option-card.tsx | 26 +- .../nodes/_base/components/output-vars.tsx | 16 +- .../panel-operator/change-block.tsx | 16 +- .../_base/components/panel-operator/index.tsx | 14 +- .../panel-operator/panel-operator-popup.tsx | 63 +- .../nodes/_base/components/prompt/editor.tsx | 243 +- .../readonly-input-with-select-var.tsx | 31 +- .../nodes/_base/components/remove-button.tsx | 6 +- .../nodes/_base/components/retry/hooks.ts | 4 +- .../_base/components/retry/retry-on-node.tsx | 23 +- .../_base/components/retry/retry-on-panel.tsx | 50 +- .../nodes/_base/components/selector.tsx | 42 +- .../nodes/_base/components/setting-item.tsx | 25 +- .../components/support-var-input/index.tsx | 24 +- .../components/switch-plugin-version.tsx | 157 +- .../components/title-description-input.tsx | 2 +- .../_base/components/toggle-expand-btn.tsx | 4 +- .../nodes/_base/components/variable-tag.tsx | 10 +- .../variable/assigned-var-reference-popup.tsx | 35 +- .../components/variable/constant-field.tsx | 14 +- .../variable/manage-input-field.tsx | 16 +- .../components/variable/match-schema-type.ts | 14 +- .../object-child-tree-panel/picker/field.tsx | 28 +- .../object-child-tree-panel/picker/index.tsx | 20 +- .../object-child-tree-panel/show/field.tsx | 30 +- .../object-child-tree-panel/show/index.tsx | 6 +- .../tree-indent-line.tsx | 4 +- .../components/variable/output-var-list.tsx | 26 +- .../variable/use-match-schema-type.ts | 5 +- .../nodes/_base/components/variable/utils.ts | 316 +- .../variable/var-full-path-panel.tsx | 18 +- .../_base/components/variable/var-list.tsx | 45 +- .../variable/var-reference-picker.tsx | 397 +-- .../variable/var-reference-popup.tsx | 93 +- .../variable/var-reference-vars.tsx | 187 +- .../components/variable/var-type-picker.tsx | 27 +- .../variable-label/base/variable-icon.tsx | 2 +- .../variable-label/base/variable-label.tsx | 22 +- .../variable-label/base/variable-name.tsx | 2 +- .../base/variable-node-label.tsx | 8 +- .../variable/variable-label/hooks.ts | 8 +- .../variable/variable-label/index.tsx | 4 +- .../variable-icon-with-color.tsx | 6 +- .../variable-label-in-editor.tsx | 4 +- .../variable-label/variable-label-in-node.tsx | 4 +- .../variable-label-in-select.tsx | 2 +- .../variable-label/variable-label-in-text.tsx | 4 +- .../_base/components/workflow-panel/index.tsx | 142 +- .../workflow-panel/last-run/index.tsx | 27 +- .../workflow-panel/last-run/no-data.tsx | 18 +- .../workflow-panel/last-run/use-last-run.ts | 58 +- .../_base/components/workflow-panel/tab.tsx | 6 +- .../workflow-panel/trigger-subscription.tsx | 22 +- .../_base/hooks/use-available-var-list.ts | 6 +- .../nodes/_base/hooks/use-node-crud.ts | 3 +- .../nodes/_base/hooks/use-node-help-link.ts | 2 +- .../nodes/_base/hooks/use-one-step-run.ts | 79 +- .../nodes/_base/hooks/use-output-var-list.ts | 22 +- .../nodes/_base/hooks/use-toggle-expend.ts | 7 +- .../nodes/_base/hooks/use-var-list.ts | 4 +- .../components/workflow/nodes/_base/node.tsx | 118 +- .../nodes/agent/components/model-bar.tsx | 78 +- .../nodes/agent/components/tool-icon.tsx | 104 +- .../workflow/nodes/agent/default.ts | 9 +- .../components/workflow/nodes/agent/node.tsx | 117 +- .../components/workflow/nodes/agent/panel.tsx | 183 +- .../components/workflow/nodes/agent/types.ts | 2 +- .../workflow/nodes/agent/use-config.ts | 24 +- .../nodes/agent/use-single-run-form-params.ts | 14 +- .../workflow/nodes/answer/default.ts | 2 +- .../components/workflow/nodes/answer/node.tsx | 22 +- .../workflow/nodes/answer/panel.tsx | 9 +- .../workflow/nodes/answer/use-config.ts | 10 +- .../components/operation-selector.tsx | 68 +- .../assigner/components/var-list/index.tsx | 75 +- .../components/var-list/use-var-list.ts | 4 +- .../workflow/nodes/assigner/default.ts | 8 +- .../workflow/nodes/assigner/hooks.ts | 10 +- .../workflow/nodes/assigner/node.tsx | 27 +- .../workflow/nodes/assigner/panel.tsx | 24 +- .../workflow/nodes/assigner/use-config.ts | 20 +- .../assigner/use-single-run-form-params.ts | 12 +- .../workflow/nodes/code/code-parser.spec.ts | 42 +- .../workflow/nodes/code/code-parser.ts | 4 +- .../components/workflow/nodes/code/default.ts | 7 +- .../workflow/nodes/code/dependency-picker.tsx | 42 +- .../components/workflow/nodes/code/node.tsx | 2 +- .../components/workflow/nodes/code/panel.tsx | 51 +- .../components/workflow/nodes/code/types.ts | 2 +- .../workflow/nodes/code/use-config.ts | 20 +- .../nodes/code/use-single-run-form-params.ts | 6 +- .../components/workflow/nodes/components.ts | 76 +- .../nodes/data-source-empty/default.ts | 2 +- .../workflow/nodes/data-source-empty/hooks.ts | 9 +- .../nodes/data-source-empty/index.tsx | 23 +- .../nodes/data-source/before-run-form.tsx | 18 +- .../workflow/nodes/data-source/default.ts | 22 +- .../data-source/hooks/use-before-run-form.ts | 21 +- .../nodes/data-source/hooks/use-config.ts | 14 +- .../workflow/nodes/data-source/node.tsx | 18 +- .../workflow/nodes/data-source/panel.tsx | 32 +- .../workflow/nodes/data-source/types.ts | 5 +- .../workflow/nodes/data-source/utils.ts | 2 +- .../nodes/document-extractor/default.ts | 5 +- .../nodes/document-extractor/node.tsx | 13 +- .../nodes/document-extractor/panel.tsx | 27 +- .../nodes/document-extractor/use-config.ts | 10 +- .../use-single-run-form-params.ts | 8 +- .../components/workflow/nodes/end/default.ts | 2 +- .../components/workflow/nodes/end/node.tsx | 6 +- .../components/workflow/nodes/end/panel.tsx | 14 +- .../workflow/nodes/end/use-config.ts | 5 +- .../nodes/http/components/api-input.tsx | 32 +- .../http/components/authorization/index.tsx | 38 +- .../components/authorization/radio-group.tsx | 2 +- .../nodes/http/components/curl-panel.tsx | 22 +- .../nodes/http/components/edit-body/index.tsx | 44 +- .../components/key-value/bulk-edit/index.tsx | 18 +- .../nodes/http/components/key-value/index.tsx | 22 +- .../key-value/key-value-edit/index.tsx | 12 +- .../key-value/key-value-edit/input-item.tsx | 63 +- .../key-value/key-value-edit/item.tsx | 101 +- .../nodes/http/components/timeout/index.tsx | 6 +- .../components/workflow/nodes/http/default.ts | 13 +- .../nodes/http/hooks/use-key-value-list.ts | 4 +- .../components/workflow/nodes/http/node.tsx | 15 +- .../components/workflow/nodes/http/panel.tsx | 71 +- .../workflow/nodes/http/use-config.ts | 21 +- .../nodes/http/use-single-run-form-params.ts | 6 +- .../components/workflow/nodes/http/utils.ts | 3 +- .../if-else/components/condition-add.tsx | 24 +- .../components/condition-files-list-value.tsx | 50 +- .../condition-list/condition-input.tsx | 8 +- .../condition-list/condition-item.tsx | 148 +- .../condition-list/condition-operator.tsx | 23 +- .../condition-list/condition-var-selector.tsx | 8 +- .../components/condition-list/index.tsx | 34 +- .../components/condition-number-input.tsx | 65 +- .../if-else/components/condition-value.tsx | 42 +- .../if-else/components/condition-wrap.tsx | 106 +- .../workflow/nodes/if-else/default.ts | 13 +- .../workflow/nodes/if-else/node.tsx | 74 +- .../workflow/nodes/if-else/panel.tsx | 30 +- .../workflow/nodes/if-else/use-config.ts | 24 +- .../if-else/use-is-var-file-attribute.ts | 6 +- .../if-else/use-single-run-form-params.ts | 10 +- .../workflow/nodes/if-else/utils.ts | 13 +- web/app/components/workflow/nodes/index.tsx | 8 +- .../workflow/nodes/iteration-start/default.ts | 2 +- .../workflow/nodes/iteration-start/index.tsx | 20 +- .../workflow/nodes/iteration/add-block.tsx | 39 +- .../workflow/nodes/iteration/default.ts | 5 +- .../workflow/nodes/iteration/node.tsx | 21 +- .../workflow/nodes/iteration/panel.tsx | 71 +- .../workflow/nodes/iteration/use-config.ts | 26 +- .../nodes/iteration/use-interactions.ts | 20 +- .../iteration/use-single-run-form-params.ts | 18 +- .../components/chunk-structure/hooks.tsx | 5 +- .../components/chunk-structure/index.tsx | 20 +- .../chunk-structure/instruction/index.tsx | 32 +- .../chunk-structure/instruction/line.tsx | 24 +- .../components/chunk-structure/selector.tsx | 23 +- .../components/embedding-model.tsx | 8 +- .../components/index-method.tsx | 43 +- .../knowledge-base/components/option-card.tsx | 27 +- .../components/retrieval-setting/hooks.tsx | 24 +- .../components/retrieval-setting/index.tsx | 27 +- .../reranking-model-selector.tsx | 8 +- .../search-method-option.tsx | 48 +- .../top-k-and-score-threshold.tsx | 26 +- .../components/retrieval-setting/type.ts | 2 +- .../workflow/nodes/knowledge-base/default.ts | 4 +- .../nodes/knowledge-base/hooks/use-config.ts | 26 +- .../workflow/nodes/knowledge-base/node.tsx | 18 +- .../workflow/nodes/knowledge-base/panel.tsx | 47 +- .../workflow/nodes/knowledge-base/types.ts | 12 +- .../use-single-run-form-params.ts | 12 +- .../components/add-dataset.tsx | 8 +- .../components/dataset-item.tsx | 71 +- .../components/dataset-list.tsx | 41 +- .../components/metadata/add-condition.tsx | 40 +- .../condition-common-variable-selector.tsx | 38 +- .../condition-list/condition-date.tsx | 14 +- .../condition-list/condition-item.tsx | 70 +- .../condition-list/condition-number.tsx | 22 +- .../condition-list/condition-operator.tsx | 32 +- .../condition-list/condition-string.tsx | 18 +- .../condition-list/condition-value-method.tsx | 20 +- .../condition-variable-selector.tsx | 26 +- .../metadata/condition-list/index.tsx | 17 +- .../metadata/condition-list/utils.ts | 13 +- .../metadata/metadata-filter/index.tsx | 32 +- .../metadata-filter-selector.tsx | 30 +- .../components/metadata/metadata-icon.tsx | 2 +- .../components/metadata/metadata-panel.tsx | 20 +- .../components/metadata/metadata-trigger.tsx | 18 +- .../components/retrieval-config.tsx | 52 +- .../nodes/knowledge-retrieval/default.ts | 7 +- .../nodes/knowledge-retrieval/hooks.ts | 4 +- .../nodes/knowledge-retrieval/node.tsx | 18 +- .../nodes/knowledge-retrieval/panel.tsx | 34 +- .../nodes/knowledge-retrieval/types.ts | 8 +- .../nodes/knowledge-retrieval/use-config.ts | 63 +- .../use-single-run-form-params.ts | 12 +- .../nodes/knowledge-retrieval/utils.ts | 12 +- .../components/extract-input.tsx | 14 +- .../components/filter-condition.tsx | 44 +- .../list-operator/components/limit-config.tsx | 30 +- .../components/sub-variable-picker.tsx | 46 +- .../workflow/nodes/list-operator/default.ts | 10 +- .../workflow/nodes/list-operator/node.tsx | 13 +- .../workflow/nodes/list-operator/panel.tsx | 131 +- .../nodes/list-operator/use-config.ts | 14 +- .../llm/components/config-prompt-item.tsx | 44 +- .../nodes/llm/components/config-prompt.tsx | 201 +- .../json-schema-config-modal/code-editor.tsx | 36 +- .../error-message.tsx | 9 +- .../json-schema-config-modal/index.tsx | 7 +- .../json-importer.tsx | 43 +- .../json-schema-config.tsx | 73 +- .../json-schema-generator/assets/index.tsx | 4 +- .../generated-result.tsx | 47 +- .../json-schema-generator/index.tsx | 39 +- .../json-schema-generator/prompt-editor.tsx | 46 +- .../schema-editor.tsx | 11 +- .../visual-editor/add-field.tsx | 18 +- .../visual-editor/card.tsx | 15 +- .../visual-editor/context.tsx | 4 +- .../visual-editor/edit-card/actions.tsx | 24 +- .../edit-card/advanced-actions.tsx | 25 +- .../edit-card/advanced-options.tsx | 25 +- .../edit-card/auto-width-input.tsx | 6 +- .../visual-editor/edit-card/index.tsx | 86 +- .../edit-card/required-switch.tsx | 10 +- .../visual-editor/edit-card/type-selector.tsx | 40 +- .../visual-editor/hooks.ts | 30 +- .../visual-editor/index.tsx | 4 +- .../visual-editor/schema-node.tsx | 92 +- .../visual-editor/store.ts | 2 +- .../llm/components/prompt-generator-btn.tsx | 23 +- .../components/reasoning-format-config.tsx | 8 +- .../llm/components/resolution-picker.tsx | 6 +- .../nodes/llm/components/structure-output.tsx | 50 +- .../components/workflow/nodes/llm/default.ts | 16 +- .../components/workflow/nodes/llm/node.tsx | 8 +- .../components/workflow/nodes/llm/panel.tsx | 130 +- .../workflow/nodes/llm/use-config.ts | 36 +- .../nodes/llm/use-single-run-form-params.ts | 28 +- .../components/workflow/nodes/llm/utils.ts | 32 +- .../workflow/nodes/loop-end/default.ts | 4 +- .../workflow/nodes/loop-start/default.ts | 2 +- .../workflow/nodes/loop-start/index.tsx | 20 +- .../workflow/nodes/loop/add-block.tsx | 41 +- .../nodes/loop/components/condition-add.tsx | 24 +- .../components/condition-files-list-value.tsx | 48 +- .../condition-list/condition-input.tsx | 8 +- .../condition-list/condition-item.tsx | 144 +- .../condition-list/condition-operator.tsx | 23 +- .../condition-list/condition-var-selector.tsx | 8 +- .../loop/components/condition-list/index.tsx | 34 +- .../components/condition-number-input.tsx | 65 +- .../nodes/loop/components/condition-value.tsx | 30 +- .../nodes/loop/components/condition-wrap.tsx | 71 +- .../loop/components/loop-variables/empty.tsx | 2 +- .../components/loop-variables/form-item.tsx | 40 +- .../loop/components/loop-variables/index.tsx | 4 +- .../loop/components/loop-variables/item.tsx | 32 +- .../components/workflow/nodes/loop/default.ts | 19 +- .../workflow/nodes/loop/insert-block.tsx | 10 +- .../components/workflow/nodes/loop/node.tsx | 17 +- .../components/workflow/nodes/loop/panel.tsx | 40 +- .../workflow/nodes/loop/use-config.ts | 34 +- .../workflow/nodes/loop/use-interactions.ts | 16 +- .../nodes/loop/use-is-var-file-attribute.ts | 2 +- .../nodes/loop/use-single-run-form-params.ts | 12 +- .../components/workflow/nodes/loop/utils.ts | 13 +- .../extract-parameter/import-from-tool.tsx | 27 +- .../components/extract-parameter/item.tsx | 33 +- .../components/extract-parameter/list.tsx | 12 +- .../components/extract-parameter/update.tsx | 51 +- .../components/reasoning-mode-picker.tsx | 8 +- .../nodes/parameter-extractor/default.ts | 8 +- .../nodes/parameter-extractor/node.tsx | 8 +- .../nodes/parameter-extractor/panel.tsx | 132 +- .../nodes/parameter-extractor/use-config.ts | 28 +- .../use-single-run-form-params.ts | 22 +- .../components/advanced-setting.tsx | 25 +- .../components/class-item.tsx | 8 +- .../components/class-list.tsx | 43 +- .../nodes/question-classifier/default.ts | 4 +- .../nodes/question-classifier/node.tsx | 53 +- .../nodes/question-classifier/panel.tsx | 34 +- .../nodes/question-classifier/use-config.ts | 29 +- .../use-single-run-form-params.ts | 22 +- .../nodes/start/components/var-item.tsx | 85 +- .../nodes/start/components/var-list.tsx | 38 +- .../workflow/nodes/start/default.ts | 2 +- .../components/workflow/nodes/start/node.tsx | 25 +- .../components/workflow/nodes/start/panel.tsx | 44 +- .../workflow/nodes/start/use-config.ts | 26 +- .../nodes/start/use-single-run-form-params.ts | 12 +- .../nodes/template-transform/default.ts | 5 +- .../nodes/template-transform/node.tsx | 2 +- .../nodes/template-transform/panel.tsx | 45 +- .../nodes/template-transform/use-config.ts | 12 +- .../use-single-run-form-params.ts | 6 +- .../nodes/tool/components/copy-id.tsx | 16 +- .../nodes/tool/components/input-var-list.tsx | 42 +- .../mixed-variable-text-input/index.tsx | 14 +- .../mixed-variable-text-input/placeholder.tsx | 19 +- .../nodes/tool/components/tool-form/index.tsx | 8 +- .../nodes/tool/components/tool-form/item.tsx | 46 +- .../components/workflow/nodes/tool/default.ts | 24 +- .../components/workflow/nodes/tool/node.tsx | 32 +- .../nodes/tool/output-schema-utils.ts | 7 +- .../components/workflow/nodes/tool/panel.tsx | 36 +- .../components/workflow/nodes/tool/types.ts | 2 +- .../workflow/nodes/tool/use-config.ts | 33 +- .../nodes/tool/use-get-data-for-check-more.ts | 2 +- .../nodes/tool/use-single-run-form-params.ts | 18 +- .../components/trigger-form/index.tsx | 6 +- .../components/trigger-form/item.tsx | 48 +- .../workflow/nodes/trigger-plugin/default.ts | 23 +- .../hooks/use-trigger-auth-flow.ts | 5 +- .../workflow/nodes/trigger-plugin/node.tsx | 10 +- .../workflow/nodes/trigger-plugin/panel.tsx | 46 +- .../workflow/nodes/trigger-plugin/types.ts | 4 +- .../nodes/trigger-plugin/use-check-params.ts | 6 +- .../nodes/trigger-plugin/use-config.ts | 26 +- .../trigger-plugin/utils/form-helpers.ts | 4 +- .../components/frequency-selector.tsx | 2 +- .../components/mode-switcher.tsx | 4 +- .../components/mode-toggle.tsx | 2 +- .../components/monthly-days-selector.tsx | 28 +- .../components/next-execution-times.tsx | 2 +- .../nodes/trigger-schedule/default.ts | 19 +- .../workflow/nodes/trigger-schedule/node.tsx | 4 +- .../workflow/nodes/trigger-schedule/panel.tsx | 86 +- .../nodes/trigger-schedule/use-config.ts | 4 +- .../utils/execution-time-calculator.ts | 32 +- .../utils/integration.spec.ts | 13 +- .../components/generic-table.tsx | 34 +- .../components/header-table.tsx | 4 +- .../components/parameter-table.tsx | 12 +- .../workflow/nodes/trigger-webhook/default.ts | 4 +- .../workflow/nodes/trigger-webhook/node.tsx | 2 +- .../workflow/nodes/trigger-webhook/panel.tsx | 32 +- .../workflow/nodes/trigger-webhook/types.ts | 2 +- .../nodes/trigger-webhook/use-config.ts | 19 +- .../trigger-webhook/utils/raw-variable.ts | 3 +- .../utils/render-output-vars.tsx | 17 +- .../components/add-variable/index.tsx | 35 +- .../components/node-group-item.tsx | 38 +- .../components/node-variable-item.tsx | 41 +- .../components/var-group-item.tsx | 129 +- .../components/var-list/index.tsx | 20 +- .../components/var-list/use-var-list.ts | 4 +- .../nodes/variable-assigner/default.ts | 7 +- .../workflow/nodes/variable-assigner/hooks.ts | 30 +- .../workflow/nodes/variable-assigner/node.tsx | 8 +- .../nodes/variable-assigner/panel.tsx | 104 +- .../nodes/variable-assigner/use-config.ts | 18 +- .../use-single-run-form-params.ts | 12 +- .../workflow/note-node/constants.ts | 2 +- .../components/workflow/note-node/hooks.ts | 4 +- .../components/workflow/note-node/index.tsx | 29 +- .../note-node/note-editor/context.tsx | 12 +- .../workflow/note-node/note-editor/editor.tsx | 30 +- .../plugins/format-detector-plugin/hooks.ts | 20 +- .../plugins/link-editor-plugin/component.tsx | 50 +- .../plugins/link-editor-plugin/hooks.ts | 26 +- .../plugins/link-editor-plugin/index.tsx | 2 +- .../note-editor/toolbar/color-picker.tsx | 24 +- .../note-node/note-editor/toolbar/command.tsx | 14 +- .../note-node/note-editor/toolbar/divider.tsx | 2 +- .../toolbar/font-size-selector.tsx | 21 +- .../note-node/note-editor/toolbar/hooks.ts | 30 +- .../note-node/note-editor/toolbar/index.tsx | 24 +- .../note-editor/toolbar/operator.tsx | 32 +- .../workflow/note-node/note-editor/utils.ts | 4 +- .../workflow/operator/add-block.tsx | 39 +- .../components/workflow/operator/control.tsx | 36 +- web/app/components/workflow/operator/hooks.ts | 8 +- .../components/workflow/operator/index.tsx | 22 +- .../workflow/operator/more-actions.tsx | 54 +- .../workflow/operator/tip-popup.tsx | 12 +- .../workflow/operator/zoom-in-out.tsx | 55 +- .../components/workflow/panel-contextmenu.tsx | 30 +- .../workflow/panel/chat-record/index.tsx | 42 +- .../workflow/panel/chat-record/user-input.tsx | 8 +- .../components/array-bool-list.tsx | 16 +- .../components/array-value-list.tsx | 16 +- .../components/bool-value.tsx | 12 +- .../components/object-value-item.tsx | 20 +- .../components/object-value-list.tsx | 10 +- .../components/variable-item.tsx | 33 +- .../components/variable-modal-trigger.tsx | 19 +- .../components/variable-modal.tsx | 100 +- .../components/variable-type-select.tsx | 34 +- .../panel/chat-variable-panel/index.tsx | 88 +- .../panel/debug-and-preview/chat-wrapper.tsx | 52 +- .../conversation-variable-modal.tsx | 93 +- .../panel/debug-and-preview/empty.tsx | 8 +- .../workflow/panel/debug-and-preview/hooks.ts | 38 +- .../panel/debug-and-preview/index.tsx | 53 +- .../panel/debug-and-preview/user-input.tsx | 10 +- .../workflow/panel/env-panel/env-item.tsx | 43 +- .../workflow/panel/env-panel/index.tsx | 36 +- .../panel/env-panel/variable-modal.tsx | 134 +- .../panel/env-panel/variable-trigger.tsx | 19 +- .../panel/global-variable-panel/index.tsx | 57 +- .../panel/global-variable-panel/item.tsx | 25 +- web/app/components/workflow/panel/index.tsx | 11 +- .../workflow/panel/inputs-panel.tsx | 38 +- web/app/components/workflow/panel/record.tsx | 10 +- .../context-menu/index.tsx | 29 +- .../context-menu/menu-item.tsx | 6 +- .../context-menu/use-context-menu.ts | 26 +- .../delete-confirm-modal.tsx | 41 +- .../panel/version-history-panel/empty.tsx | 31 +- .../filter/filter-item.tsx | 11 +- .../filter/filter-switch.tsx | 13 +- .../version-history-panel/filter/index.tsx | 23 +- .../panel/version-history-panel/index.tsx | 140 +- .../version-history-panel/loading/index.tsx | 10 +- .../version-history-panel/loading/item.tsx | 17 +- .../restore-confirm-modal.tsx | 41 +- .../version-history-item.tsx | 38 +- .../workflow/panel/workflow-preview.tsx | 71 +- .../workflow/plugin-dependency/hooks.ts | 4 +- .../workflow/plugin-dependency/index.tsx | 2 +- .../workflow/plugin-dependency/store.ts | 2 +- .../workflow/run/agent-log/agent-log-item.tsx | 51 +- .../run/agent-log/agent-log-nav-more.tsx | 18 +- .../workflow/run/agent-log/agent-log-nav.tsx | 48 +- .../run/agent-log/agent-log-trigger.tsx | 16 +- .../run/agent-log/agent-result-panel.tsx | 39 +- web/app/components/workflow/run/hooks.ts | 12 +- web/app/components/workflow/run/index.tsx | 38 +- .../iteration-log/iteration-log-trigger.tsx | 37 +- .../iteration-log/iteration-result-panel.tsx | 51 +- .../run/loop-log/loop-log-trigger.tsx | 30 +- .../run/loop-log/loop-result-panel.tsx | 55 +- .../workflow/run/loop-result-panel.tsx | 57 +- web/app/components/workflow/run/meta.tsx | 54 +- web/app/components/workflow/run/node.tsx | 82 +- .../components/workflow/run/output-panel.tsx | 28 +- .../components/workflow/run/result-panel.tsx | 38 +- .../components/workflow/run/result-text.tsx | 28 +- .../run/retry-log/retry-log-trigger.tsx | 14 +- .../run/retry-log/retry-result-panel.tsx | 14 +- .../workflow/run/special-result-panel.tsx | 11 +- .../workflow/run/status-container.tsx | 6 +- web/app/components/workflow/run/status.tsx | 57 +- .../components/workflow/run/tracing-panel.tsx | 24 +- .../run/utils/format-log/agent/index.spec.ts | 4 +- .../run/utils/format-log/agent/index.ts | 2 +- .../utils/format-log/graph-to-log-struct.ts | 50 +- .../workflow/run/utils/format-log/index.ts | 6 +- .../utils/format-log/iteration/index.spec.ts | 4 +- .../run/utils/format-log/iteration/index.ts | 5 +- .../run/utils/format-log/loop/index.spec.ts | 6 +- .../run/utils/format-log/loop/index.ts | 5 +- .../run/utils/format-log/parallel/index.ts | 12 +- .../run/utils/format-log/retry/index.spec.ts | 4 +- .../workflow/selection-contextmenu.tsx | 81 +- .../components/workflow/shortcuts-name.tsx | 5 +- .../components/workflow/simple-node/index.tsx | 49 +- .../store/__tests__/trigger-status.test.ts | 2 +- web/app/components/workflow/store/index.ts | 2 +- .../workflow/debug/inspect-vars-slice.ts | 16 +- .../workflow/store/workflow/index.ts | 86 +- .../workflow/store/workflow/node-slice.ts | 8 +- .../store/workflow/workflow-draft-slice.ts | 4 +- .../workflow/store/workflow/workflow-slice.ts | 8 +- .../workflow/syncing-data-modal.tsx | 2 +- web/app/components/workflow/types.ts | 25 +- .../components/workflow/update-dsl-modal.tsx | 142 +- .../components/workflow/utils/data-source.ts | 2 +- .../components/workflow/utils/elk-layout.ts | 14 +- .../workflow/utils/gen-node-meta-data.ts | 2 +- web/app/components/workflow/utils/index.ts | 16 +- .../workflow/utils/node-navigation.ts | 4 +- web/app/components/workflow/utils/node.ts | 16 +- web/app/components/workflow/utils/tool.ts | 6 +- web/app/components/workflow/utils/variable.ts | 6 +- .../workflow/utils/workflow-entry.ts | 6 +- .../workflow/utils/workflow-init.spec.ts | 4 +- .../workflow/utils/workflow-init.ts | 40 +- web/app/components/workflow/utils/workflow.ts | 16 +- .../variable-inspect/display-content.tsx | 95 +- .../workflow/variable-inspect/empty.tsx | 21 +- .../workflow/variable-inspect/group.tsx | 45 +- .../workflow/variable-inspect/index.tsx | 13 +- .../variable-inspect/large-data-alert.tsx | 12 +- .../workflow/variable-inspect/left.tsx | 28 +- .../workflow/variable-inspect/listening.tsx | 52 +- .../workflow/variable-inspect/panel.tsx | 62 +- .../workflow/variable-inspect/right.tsx | 144 +- .../workflow/variable-inspect/trigger.tsx | 41 +- .../variable-inspect/value-content.tsx | 112 +- .../workflow/workflow-history-store.tsx | 13 +- .../components/custom-edge.tsx | 11 +- .../components/error-handle-on-node.tsx | 16 +- .../components/node-handle.tsx | 12 +- .../components/nodes/base.tsx | 46 +- .../components/nodes/constants.ts | 2 +- .../components/nodes/if-else/node.tsx | 72 +- .../components/nodes/index.tsx | 2 +- .../nodes/iteration-start/index.tsx | 14 +- .../components/nodes/iteration/node.tsx | 11 +- .../components/nodes/loop-start/index.tsx | 14 +- .../components/nodes/loop/hooks.ts | 6 +- .../components/nodes/loop/node.tsx | 8 +- .../nodes/question-classifier/node.tsx | 14 +- .../components/note-node/index.tsx | 17 +- .../components/zoom-in-out.tsx | 40 +- .../workflow/workflow-preview/index.tsx | 66 +- web/app/dev-preview/page.tsx | 4 +- .../education-apply/education-apply-page.tsx | 98 +- .../education-apply/expire-notice-modal.tsx | 72 +- web/app/education-apply/hooks.ts | 25 +- web/app/education-apply/role-selector.tsx | 4 +- web/app/education-apply/search-input.tsx | 14 +- web/app/education-apply/user-info.tsx | 26 +- .../education-apply/verify-state-modal.tsx | 38 +- .../forgot-password/ChangePasswordForm.tsx | 45 +- .../forgot-password/ForgotPasswordForm.tsx | 88 +- web/app/forgot-password/page.tsx | 22 +- web/app/init/InitPasswordPopup.tsx | 60 +- web/app/init/page.tsx | 2 +- web/app/install/installForm.tsx | 209 +- web/app/install/page.tsx | 16 +- web/app/layout.tsx | 28 +- web/app/page.tsx | 4 +- .../repos/[owner]/[repo]/releases/route.ts | 7 +- web/app/reset-password/check-code/page.tsx | 72 +- web/app/reset-password/layout.tsx | 49 +- web/app/reset-password/page.tsx | 74 +- web/app/reset-password/set-password/page.tsx | 61 +- web/app/routePrefixHandle.tsx | 4 +- web/app/signin/_header.tsx | 34 +- web/app/signin/check-code/page.tsx | 91 +- .../signin/components/mail-and-code-auth.tsx | 34 +- .../components/mail-and-password-auth.tsx | 146 +- web/app/signin/components/social-auth.tsx | 76 +- web/app/signin/components/sso-auth.tsx | 6 +- web/app/signin/invite-settings/page.tsx | 208 +- web/app/signin/layout.tsx | 38 +- web/app/signin/normal-form.tsx | 286 +- web/app/signin/one-more-step.tsx | 50 +- web/app/signin/page.tsx | 4 +- web/app/signin/split.tsx | 3 +- web/app/signin/utils/post-login-redirect.ts | 7 +- web/app/signup/check-code/page.tsx | 70 +- web/app/signup/components/input-mail.tsx | 128 +- web/app/signup/layout.tsx | 34 +- web/app/signup/page.tsx | 6 +- web/app/signup/set-password/page.tsx | 41 +- web/config/index.spec.ts | 17 +- web/config/index.ts | 32 +- web/context/access-control-store.ts | 4 +- web/context/app-context.tsx | 23 +- web/context/dataset-detail.ts | 6 +- web/context/datasets-context.tsx | 2 +- web/context/debug-configuration.ts | 17 +- web/context/event-emitter.tsx | 4 +- web/context/explore-context.ts | 2 +- .../external-knowledge-api-context.tsx | 2 +- web/context/global-public-context.tsx | 12 +- .../hooks/use-trigger-events-limit-modal.ts | 5 +- web/context/i18n.ts | 4 +- web/context/mitt-context.tsx | 2 +- web/context/modal-context.test.tsx | 6 +- web/context/modal-context.tsx | 67 +- web/context/provider-context-mock.spec.tsx | 4 +- web/context/provider-context.tsx | 44 +- web/context/query-client.tsx | 10 +- web/context/web-app-context.tsx | 20 +- web/context/workspace-context.tsx | 5 +- web/eslint.config.mjs | 225 +- web/hooks/use-app-favicon.ts | 12 +- web/hooks/use-document-title.spec.ts | 4 +- web/hooks/use-document-title.ts | 4 +- web/hooks/use-format-time-from-now.spec.ts | 31 +- web/hooks/use-format-time-from-now.ts | 2 +- web/hooks/use-import-dsl.ts | 22 +- web/hooks/use-metadata.ts | 5 +- web/hooks/use-mitt.ts | 16 +- web/hooks/use-moderate.ts | 2 +- web/hooks/use-pay.tsx | 8 +- web/hooks/use-tab-searchparams.spec.ts | 6 +- web/hooks/use-theme.ts | 2 +- web/hooks/use-timestamp.ts | 4 +- web/i18n-config/README.md | 3 +- web/i18n-config/auto-gen-i18n.js | 23 +- web/i18n-config/check-i18n-sync.js | 9 +- web/i18n-config/check-i18n.js | 41 +- web/i18n-config/generate-i18n-types.js | 27 +- web/i18n-config/i18next-config.ts | 9 +- web/i18n-config/index.ts | 11 +- web/i18n-config/language.ts | 1 + web/i18n-config/server.ts | 8 +- web/models/access-control.ts | 4 +- web/models/app.ts | 12 +- web/models/common.ts | 2 +- web/models/datasets.ts | 14 +- web/models/debug.ts | 11 +- web/models/explore.ts | 1 + web/models/log.ts | 6 +- web/models/pipeline.ts | 10 +- web/next.config.js | 44 +- web/package.json | 78 +- web/pnpm-lock.yaml | 1130 ++++---- web/scripts/copy-and-start.mjs | 2 +- web/scripts/generate-icons.js | 41 +- web/scripts/optimize-standalone.js | 124 +- web/service/_tools_util.spec.ts | 8 +- web/service/access-control.ts | 12 +- web/service/annotation.ts | 10 +- web/service/apps.ts | 66 +- web/service/base.ts | 30 +- web/service/billing.ts | 2 +- web/service/common.ts | 162 +- web/service/datasets.ts | 50 +- web/service/debug.ts | 10 +- web/service/demo/index.tsx | 12 +- web/service/explore.ts | 4 +- web/service/fetch.ts | 10 +- web/service/knowledge/use-create-dataset.ts | 10 +- web/service/knowledge/use-dataset.ts | 24 +- web/service/knowledge/use-document.ts | 16 +- web/service/knowledge/use-hit-testing.ts | 4 +- web/service/knowledge/use-import.ts | 2 +- web/service/knowledge/use-metadata.spec.tsx | 4 +- web/service/knowledge/use-metadata.ts | 8 +- web/service/knowledge/use-segment.ts | 28 +- web/service/log.ts | 18 +- web/service/plugins.ts | 15 +- web/service/share.ts | 27 +- web/service/sso.ts | 4 +- web/service/tag.ts | 2 +- web/service/tools.ts | 4 +- web/service/use-apps.ts | 6 +- web/service/use-base.ts | 3 +- web/service/use-billing.ts | 2 +- web/service/use-common.ts | 46 +- web/service/use-datasource.ts | 11 +- web/service/use-education.ts | 6 +- web/service/use-endpoints.ts | 5 +- web/service/use-explore.ts | 4 +- web/service/use-flow.ts | 2 +- web/service/use-models.ts | 12 +- web/service/use-oauth.ts | 2 +- web/service/use-pipeline.ts | 24 +- web/service/use-plugins-auth.ts | 13 +- web/service/use-plugins.ts | 66 +- web/service/use-strategy.ts | 4 +- web/service/use-tools.ts | 23 +- web/service/use-triggers.ts | 10 +- web/service/use-workflow.ts | 11 +- web/service/utils.spec.ts | 2 +- web/service/workflow-payload.ts | 6 +- web/service/workflow.ts | 10 +- web/tailwind.config.js | 1 + web/testing/analyze-component.js | 184 +- web/testing/testing.md | 27 +- web/tsconfig.json | 34 +- web/types/app.ts | 23 +- web/types/feature.ts | 4 +- web/types/i18n.d.ts | 80 +- web/types/react-18-input-autosize.d.ts | 2 +- web/types/workflow.ts | 36 +- web/utils/app-redirection.spec.ts | 22 +- web/utils/classnames.spec.ts | 20 +- web/utils/classnames.ts | 3 +- web/utils/completion-params.spec.ts | 42 +- web/utils/completion-params.ts | 4 +- web/utils/context.spec.ts | 6 +- web/utils/context.ts | 6 +- web/utils/emoji.spec.ts | 2 +- web/utils/emoji.ts | 2 +- web/utils/encryption.ts | 6 +- web/utils/format.spec.ts | 38 +- web/utils/format.ts | 7 +- web/utils/get-icon.spec.ts | 36 +- web/utils/index.spec.ts | 2 +- web/utils/mcp.spec.ts | 28 +- web/utils/model-config.spec.ts | 4 +- web/utils/model-config.ts | 4 +- web/utils/navigation.spec.ts | 62 +- web/utils/permission.spec.ts | 18 +- web/utils/time.spec.ts | 26 +- web/utils/time.ts | 5 +- web/utils/timezone.json | 2544 ++++++++--------- web/utils/tool-call.spec.ts | 20 +- web/utils/tool-call.ts | 3 +- web/utils/urlValidation.ts | 3 +- web/utils/var.spec.ts | 2 +- web/utils/var.ts | 6 +- web/utils/zod.spec.ts | 2 +- web/vitest.config.ts | 2 +- web/vitest.setup.ts | 2 +- 3356 files changed, 85046 insertions(+), 81278 deletions(-) delete mode 100644 web/.oxlintrc.json diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index bafac7bd13..dbced47988 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -68,25 +68,4 @@ jobs: run: | uvx --python 3.13 mdformat . --exclude ".claude/skills/**/SKILL.md" - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - package_json_file: web/package.json - run_install: false - - - name: Setup NodeJS - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: pnpm - cache-dependency-path: ./web/pnpm-lock.yaml - - - name: Web dependencies - working-directory: ./web - run: pnpm install --frozen-lockfile - - - name: oxlint - working-directory: ./web - run: pnpm exec oxlint --config .oxlintrc.json --fix . - - uses: autofix-ci/action@635ffb0c9798bd160680f18fd73371e355b85f27 diff --git a/.gitignore b/.gitignore index 5ad728c3da..4e9a0eaa23 100644 --- a/.gitignore +++ b/.gitignore @@ -139,7 +139,6 @@ pyrightconfig.json .idea/' .DS_Store -web/.vscode/settings.json # Intellij IDEA Files .idea/* @@ -205,7 +204,6 @@ sdks/python-client/dify_client.egg-info !.vscode/launch.json.template !.vscode/README.md api/.vscode -web/.vscode # vscode Code History Extension .history @@ -220,15 +218,6 @@ plugins.jsonl # mise mise.toml -# Next.js build output -.next/ - -# PWA generated files -web/public/sw.js -web/public/sw.js.map -web/public/workbox-*.js -web/public/workbox-*.js.map -web/public/fallback-*.js # AI Assistant .roo/ diff --git a/web/.gitignore b/web/.gitignore index 048c5f6485..9de3dc83f9 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -54,3 +54,13 @@ package-lock.json # mise mise.toml + +# PWA generated files +public/sw.js +public/sw.js.map +public/workbox-*.js +public/workbox-*.js.map +public/fallback-*.js + +.vscode/settings.json +.vscode/mcp.json diff --git a/web/.oxlintrc.json b/web/.oxlintrc.json deleted file mode 100644 index 57eddd34fb..0000000000 --- a/web/.oxlintrc.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "plugins": [ - "unicorn", - "typescript", - "oxc" - ], - "categories": {}, - "rules": { - "for-direction": "error", - "no-async-promise-executor": "error", - "no-caller": "error", - "no-class-assign": "error", - "no-compare-neg-zero": "error", - "no-cond-assign": "warn", - "no-const-assign": "warn", - "no-constant-binary-expression": "error", - "no-constant-condition": "warn", - "no-control-regex": "warn", - "no-debugger": "warn", - "no-delete-var": "warn", - "no-dupe-class-members": "warn", - "no-dupe-else-if": "warn", - "no-dupe-keys": "warn", - "no-duplicate-case": "warn", - "no-empty-character-class": "warn", - "no-empty-pattern": "warn", - "no-empty-static-block": "warn", - "no-eval": "warn", - "no-ex-assign": "warn", - "no-extra-boolean-cast": "warn", - "no-func-assign": "warn", - "no-global-assign": "warn", - "no-import-assign": "warn", - "no-invalid-regexp": "warn", - "no-irregular-whitespace": "warn", - "no-loss-of-precision": "warn", - "no-new-native-nonconstructor": "warn", - "no-nonoctal-decimal-escape": "warn", - "no-obj-calls": "warn", - "no-self-assign": "warn", - "no-setter-return": "warn", - "no-shadow-restricted-names": "warn", - "no-sparse-arrays": "warn", - "no-this-before-super": "warn", - "no-unassigned-vars": "warn", - "no-unsafe-finally": "warn", - "no-unsafe-negation": "warn", - "no-unsafe-optional-chaining": "error", - "no-unused-labels": "warn", - "no-unused-private-class-members": "warn", - "no-unused-vars": "warn", - "no-useless-backreference": "warn", - "no-useless-catch": "error", - "no-useless-escape": "warn", - "no-useless-rename": "warn", - "no-with": "warn", - "require-yield": "warn", - "use-isnan": "warn", - "valid-typeof": "warn", - "oxc/bad-array-method-on-arguments": "warn", - "oxc/bad-char-at-comparison": "warn", - "oxc/bad-comparison-sequence": "warn", - "oxc/bad-min-max-func": "warn", - "oxc/bad-object-literal-comparison": "warn", - "oxc/bad-replace-all-arg": "warn", - "oxc/const-comparisons": "warn", - "oxc/double-comparisons": "warn", - "oxc/erasing-op": "warn", - "oxc/missing-throw": "warn", - "oxc/number-arg-out-of-range": "warn", - "oxc/only-used-in-recursion": "warn", - "oxc/uninvoked-array-callback": "warn", - "typescript/await-thenable": "warn", - "typescript/no-array-delete": "warn", - "typescript/no-base-to-string": "warn", - "typescript/no-confusing-void-expression": "warn", - "typescript/no-duplicate-enum-values": "warn", - "typescript/no-duplicate-type-constituents": "warn", - "typescript/no-extra-non-null-assertion": "warn", - "typescript/no-floating-promises": "warn", - "typescript/no-for-in-array": "warn", - "typescript/no-implied-eval": "warn", - "typescript/no-meaningless-void-operator": "warn", - "typescript/no-misused-new": "warn", - "typescript/no-misused-spread": "warn", - "typescript/no-non-null-asserted-optional-chain": "warn", - "typescript/no-redundant-type-constituents": "warn", - "typescript/no-this-alias": "warn", - "typescript/no-unnecessary-parameter-property-assignment": "warn", - "typescript/no-unsafe-declaration-merging": "warn", - "typescript/no-unsafe-unary-minus": "warn", - "typescript/no-useless-empty-export": "warn", - "typescript/no-wrapper-object-types": "warn", - "typescript/prefer-as-const": "warn", - "typescript/require-array-sort-compare": "warn", - "typescript/restrict-template-expressions": "warn", - "typescript/triple-slash-reference": "warn", - "typescript/unbound-method": "warn", - "unicorn/no-await-in-promise-methods": "warn", - "unicorn/no-empty-file": "warn", - "unicorn/no-invalid-fetch-options": "warn", - "unicorn/no-invalid-remove-event-listener": "warn", - "unicorn/no-new-array": "warn", - "unicorn/no-single-promise-in-promise-methods": "warn", - "unicorn/no-thenable": "warn", - "unicorn/no-unnecessary-await": "warn", - "unicorn/no-useless-fallback-in-spread": "warn", - "unicorn/no-useless-length-check": "warn", - "unicorn/no-useless-spread": "warn", - "unicorn/prefer-set-size": "warn", - "unicorn/prefer-string-starts-ends-with": "warn" - }, - "settings": { - "jsx-a11y": { - "polymorphicPropName": null, - "components": {}, - "attributes": {} - }, - "next": { - "rootDir": [] - }, - "react": { - "formComponents": [], - "linkComponents": [] - }, - "jsdoc": { - "ignorePrivate": false, - "ignoreInternal": false, - "ignoreReplacesDocs": true, - "overrideReplacesDocs": true, - "augmentsExtendsReplacesDocs": false, - "implementsReplacesDocs": false, - "exemptDestructuredRootsFromChecks": false, - "tagNamePreference": {} - } - }, - "env": { - "builtin": true - }, - "globals": {}, - "ignorePatterns": [ - "**/*.js" - ] -} \ No newline at end of file diff --git a/web/.storybook/preview.tsx b/web/.storybook/preview.tsx index 1f5726de34..37c636cc75 100644 --- a/web/.storybook/preview.tsx +++ b/web/.storybook/preview.tsx @@ -1,8 +1,8 @@ import type { Preview } from '@storybook/react' import { withThemeByDataAttribute } from '@storybook/addon-themes' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' -import I18N from '../app/components/i18n' import { ToastProvider } from '../app/components/base/toast' +import I18N from '../app/components/i18n' import '../app/styles/globals.css' import '../app/styles/markdown.scss' diff --git a/web/.storybook/utils/form-story-wrapper.tsx b/web/.storybook/utils/form-story-wrapper.tsx index 689c3a20ff..90349a0325 100644 --- a/web/.storybook/utils/form-story-wrapper.tsx +++ b/web/.storybook/utils/form-story-wrapper.tsx @@ -1,6 +1,6 @@ -import { useState } from 'react' import type { ReactNode } from 'react' import { useStore } from '@tanstack/react-form' +import { useState } from 'react' import { useAppForm } from '@/app/components/base/form' type UseAppFormOptions = Parameters[0] @@ -49,7 +49,12 @@ export const FormStoryWrapper = ({
@@ -254,7 +277,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => { }) describe('Page Refresh Scenario Simulation', () => { - test('simulates complete page loading process with dark theme', async () => { + it('simulates complete page loading process with dark theme', async () => { // Setup: User previously selected dark mode setupMockEnvironment('dark') @@ -286,7 +309,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => { console.log('State change detection: Initial -> Final') }) - test('handles light theme correctly', async () => { + it('handles light theme correctly', async () => { setupMockEnvironment('light') render( @@ -302,7 +325,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => { expect(screen.getByTestId('visual-appearance')).toHaveTextContent('Appearance: light') }) - test('handles system theme with dark preference', async () => { + it('handles system theme with dark preference', async () => { setupMockEnvironment('system', true) // system theme, dark preference render( @@ -318,7 +341,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => { expect(screen.getByTestId('visual-appearance')).toHaveTextContent('Appearance: dark') }) - test('handles system theme with light preference', async () => { + it('handles system theme with light preference', async () => { setupMockEnvironment('system', false) // system theme, light preference render( @@ -334,7 +357,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => { expect(screen.getByTestId('visual-appearance')).toHaveTextContent('Appearance: light') }) - test('handles no stored theme (defaults to system)', async () => { + it('handles no stored theme (defaults to system)', async () => { setupMockEnvironment(null, false) // no stored theme, system prefers light render( @@ -348,10 +371,10 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => { }) }) - test('measures timing window of style changes', async () => { + it('measures timing window of style changes', async () => { setupMockEnvironment('dark') - const timingData: Array<{ phase: string; timestamp: number; styles: any }> = [] + const timingData: Array<{ phase: string, timestamp: number, styles: any }> = [] const TimingPageComponent = createTimingPageComponent(timingData) render( @@ -384,10 +407,10 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => { }) describe('CSS Application Timing Tests', () => { - test('checks CSS class changes causing flicker', async () => { + it('checks CSS class changes causing flicker', async () => { setupMockEnvironment('dark') - const cssStates: Array<{ className: string; timestamp: number }> = [] + const cssStates: Array<{ className: string, timestamp: number }> = [] const CSSTestComponent = createCSSTestComponent(cssStates) render( @@ -420,7 +443,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => { }) describe('Edge Cases and Error Handling', () => { - test('handles localStorage access errors gracefully', async () => { + it('handles localStorage access errors gracefully', async () => { setupMockEnvironment(null) const mockStorage = { @@ -457,7 +480,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => { } }) - test('handles invalid theme values in localStorage', async () => { + it('handles invalid theme values in localStorage', async () => { setupMockEnvironment('invalid-theme-value') render( @@ -477,8 +500,8 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => { }) describe('Performance and Regression Tests', () => { - test('verifies ThemeProvider position fix reduces initialization delay', async () => { - const performanceMarks: Array<{ event: string; timestamp: number }> = [] + it('verifies ThemeProvider position fix reduces initialization delay', async () => { + const performanceMarks: Array<{ event: string, timestamp: number }> = [] setupMockEnvironment('dark') @@ -507,7 +530,7 @@ describe('Real Browser Environment Dark Mode Flicker Test', () => { }) describe('Solution Requirements Definition', () => { - test('defines technical requirements to eliminate flicker', () => { + it('defines technical requirements to eliminate flicker', () => { const technicalRequirements = { ssrConsistency: 'SSR and CSR must render identical initial styles', synchronousDetection: 'Theme detection must complete synchronously before first render', diff --git a/web/__tests__/unified-tags-logic.test.ts b/web/__tests__/unified-tags-logic.test.ts index ec73a6a268..d4bbd7f4ff 100644 --- a/web/__tests__/unified-tags-logic.test.ts +++ b/web/__tests__/unified-tags-logic.test.ts @@ -14,8 +14,8 @@ describe('Unified Tags Editing - Pure Logic Tests', () => { // This is the valueNotChanged logic from TagSelector component const valueNotChanged = currentValue.length === newSelectedTagIDs.length - && currentValue.every(v => newSelectedTagIDs.includes(v)) - && newSelectedTagIDs.every(v => currentValue.includes(v)) + && currentValue.every(v => newSelectedTagIDs.includes(v)) + && newSelectedTagIDs.every(v => currentValue.includes(v)) expect(valueNotChanged).toBe(false) }) @@ -26,8 +26,8 @@ describe('Unified Tags Editing - Pure Logic Tests', () => { const valueNotChanged = currentValue.length === newSelectedTagIDs.length - && currentValue.every(v => newSelectedTagIDs.includes(v)) - && newSelectedTagIDs.every(v => currentValue.includes(v)) + && currentValue.every(v => newSelectedTagIDs.includes(v)) + && newSelectedTagIDs.every(v => currentValue.includes(v)) expect(valueNotChanged).toBe(true) }) @@ -70,7 +70,7 @@ describe('Unified Tags Editing - Pure Logic Tests', () => { }) describe('Fallback Logic (from layout-main.tsx)', () => { - type Tag = { id: string; name: string } + type Tag = { id: string, name: string } type AppDetail = { tags: Tag[] } type FallbackResult = { tags?: Tag[] } | null // no-op @@ -316,7 +316,7 @@ describe('Unified Tags Editing - Pure Logic Tests', () => { ] // Filter out invalid entries - const validTags = mixedData.filter((tag): tag is { id: string; name: string; type: string; binding_count: number } => + const validTags = mixedData.filter((tag): tag is { id: string, name: string, type: string, binding_count: number } => tag != null && typeof tag === 'object' && 'id' in tag diff --git a/web/__tests__/workflow-onboarding-integration.test.tsx b/web/__tests__/workflow-onboarding-integration.test.tsx index e4db04148b..a991115dfb 100644 --- a/web/__tests__/workflow-onboarding-integration.test.tsx +++ b/web/__tests__/workflow-onboarding-integration.test.tsx @@ -1,6 +1,6 @@ import type { Mock } from 'vitest' -import { BlockEnum } from '@/app/components/workflow/types' import { useWorkflowStore } from '@/app/components/workflow/store' +import { BlockEnum } from '@/app/components/workflow/types' // Type for mocked store type MockWorkflowStore = { @@ -103,9 +103,9 @@ describe('Workflow Onboarding Integration Logic', () => { // Simulate the validation logic from use-nodes-sync-draft.ts const isValidStartNode = mockNode.data.type === BlockEnum.Start - || mockNode.data.type === BlockEnum.TriggerSchedule - || mockNode.data.type === BlockEnum.TriggerWebhook - || mockNode.data.type === BlockEnum.TriggerPlugin + || mockNode.data.type === BlockEnum.TriggerSchedule + || mockNode.data.type === BlockEnum.TriggerWebhook + || mockNode.data.type === BlockEnum.TriggerPlugin expect(isValidStartNode).toBe(true) }) @@ -117,9 +117,9 @@ describe('Workflow Onboarding Integration Logic', () => { } const isValidStartNode = mockNode.data.type === BlockEnum.Start - || mockNode.data.type === BlockEnum.TriggerSchedule - || mockNode.data.type === BlockEnum.TriggerWebhook - || mockNode.data.type === BlockEnum.TriggerPlugin + || mockNode.data.type === BlockEnum.TriggerSchedule + || mockNode.data.type === BlockEnum.TriggerWebhook + || mockNode.data.type === BlockEnum.TriggerPlugin expect(isValidStartNode).toBe(true) }) @@ -131,9 +131,9 @@ describe('Workflow Onboarding Integration Logic', () => { } const isValidStartNode = mockNode.data.type === BlockEnum.Start - || mockNode.data.type === BlockEnum.TriggerSchedule - || mockNode.data.type === BlockEnum.TriggerWebhook - || mockNode.data.type === BlockEnum.TriggerPlugin + || mockNode.data.type === BlockEnum.TriggerSchedule + || mockNode.data.type === BlockEnum.TriggerWebhook + || mockNode.data.type === BlockEnum.TriggerPlugin expect(isValidStartNode).toBe(true) }) @@ -145,9 +145,9 @@ describe('Workflow Onboarding Integration Logic', () => { } const isValidStartNode = mockNode.data.type === BlockEnum.Start - || mockNode.data.type === BlockEnum.TriggerSchedule - || mockNode.data.type === BlockEnum.TriggerWebhook - || mockNode.data.type === BlockEnum.TriggerPlugin + || mockNode.data.type === BlockEnum.TriggerSchedule + || mockNode.data.type === BlockEnum.TriggerWebhook + || mockNode.data.type === BlockEnum.TriggerPlugin expect(isValidStartNode).toBe(true) }) @@ -159,9 +159,9 @@ describe('Workflow Onboarding Integration Logic', () => { } const isValidStartNode = mockNode.data.type === BlockEnum.Start - || mockNode.data.type === BlockEnum.TriggerSchedule - || mockNode.data.type === BlockEnum.TriggerWebhook - || mockNode.data.type === BlockEnum.TriggerPlugin + || mockNode.data.type === BlockEnum.TriggerSchedule + || mockNode.data.type === BlockEnum.TriggerWebhook + || mockNode.data.type === BlockEnum.TriggerPlugin expect(isValidStartNode).toBe(false) }) diff --git a/web/__tests__/workflow-parallel-limit.test.tsx b/web/__tests__/workflow-parallel-limit.test.tsx index 8d845794da..d2afb30882 100644 --- a/web/__tests__/workflow-parallel-limit.test.tsx +++ b/web/__tests__/workflow-parallel-limit.test.tsx @@ -35,11 +35,16 @@ function restoreEnvironment() { vi.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string) => { - if (key.includes('MaxParallelismTitle')) return 'Max Parallelism' - if (key.includes('MaxParallelismDesc')) return 'Maximum number of parallel executions' - if (key.includes('parallelMode')) return 'Parallel Mode' - if (key.includes('parallelPanelDesc')) return 'Enable parallel execution' - if (key.includes('errorResponseMethod')) return 'Error Response Method' + if (key.includes('MaxParallelismTitle')) + return 'Max Parallelism' + if (key.includes('MaxParallelismDesc')) + return 'Maximum number of parallel executions' + if (key.includes('parallelMode')) + return 'Parallel Mode' + if (key.includes('parallelPanelDesc')) + return 'Enable parallel execution' + if (key.includes('errorResponseMethod')) + return 'Error Response Method' return key }, }), diff --git a/web/__tests__/xss-prevention.test.tsx b/web/__tests__/xss-prevention.test.tsx index 235a28af51..b236f9ed3e 100644 --- a/web/__tests__/xss-prevention.test.tsx +++ b/web/__tests__/xss-prevention.test.tsx @@ -5,8 +5,8 @@ * components have been properly fixed by replacing dangerouslySetInnerHTML with safe React rendering. */ -import React from 'react' import { cleanup, render } from '@testing-library/react' +import React from 'react' import BlockInput from '../app/components/base/block-input' import SupportVarInput from '../app/components/workflow/nodes/_base/components/support-var-input' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx index 11335b270c..ba04eae64a 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx @@ -1,9 +1,9 @@ -import React from 'react' import type { Locale } from '@/i18n-config' +import React from 'react' import DevelopMain from '@/app/components/develop' export type IDevelopProps = { - params: Promise<{ locale: Locale; appId: string }> + params: Promise<{ locale: Locale, appId: string }> } const Develop = async (props: IDevelopProps) => { diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx index d5e3c61932..41bf4c3acf 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx @@ -1,8 +1,7 @@ 'use client' import type { FC } from 'react' -import { useUnmount } from 'ahooks' -import React, { useCallback, useEffect, useState } from 'react' -import { usePathname, useRouter } from 'next/navigation' +import type { NavIcon } from '@/app/components/app-sidebar/navLink' +import type { App } from '@/types/app' import { RiDashboard2Fill, RiDashboard2Line, @@ -13,21 +12,23 @@ import { RiTerminalWindowFill, RiTerminalWindowLine, } from '@remixicon/react' +import { useUnmount } from 'ahooks' +import dynamic from 'next/dynamic' +import { usePathname, useRouter } from 'next/navigation' +import React, { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useShallow } from 'zustand/react/shallow' -import s from './style.module.css' -import { cn } from '@/utils/classnames' -import { useStore } from '@/app/components/app/store' import AppSideBar from '@/app/components/app-sidebar' -import type { NavIcon } from '@/app/components/app-sidebar/navLink' -import { fetchAppDetailDirect } from '@/service/apps' -import { useAppContext } from '@/context/app-context' +import { useStore } from '@/app/components/app/store' import Loading from '@/app/components/base/loading' -import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' -import { type App, AppModeEnum } from '@/types/app' -import useDocumentTitle from '@/hooks/use-document-title' import { useStore as useTagStore } from '@/app/components/base/tag-management/store' -import dynamic from 'next/dynamic' +import { useAppContext } from '@/context/app-context' +import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' +import useDocumentTitle from '@/hooks/use-document-title' +import { fetchAppDetailDirect } from '@/service/apps' +import { AppModeEnum } from '@/types/app' +import { cn } from '@/utils/classnames' +import s from './style.module.css' const TagManagementModal = dynamic(() => import('@/app/components/base/tag-management'), { ssr: false, @@ -68,11 +69,11 @@ const AppDetailLayout: FC = (props) => { const navConfig = [ ...(isCurrentWorkspaceEditor ? [{ - name: t('common.appMenus.promptEng'), - href: `/app/${appId}/${(mode === AppModeEnum.WORKFLOW || mode === AppModeEnum.ADVANCED_CHAT) ? 'workflow' : 'configuration'}`, - icon: RiTerminalWindowLine, - selectedIcon: RiTerminalWindowFill, - }] + name: t('common.appMenus.promptEng'), + href: `/app/${appId}/${(mode === AppModeEnum.WORKFLOW || mode === AppModeEnum.ADVANCED_CHAT) ? 'workflow' : 'configuration'}`, + icon: RiTerminalWindowLine, + selectedIcon: RiTerminalWindowFill, + }] : [] ), { @@ -83,13 +84,13 @@ const AppDetailLayout: FC = (props) => { }, ...(isCurrentWorkspaceEditor ? [{ - name: mode !== AppModeEnum.WORKFLOW - ? t('common.appMenus.logAndAnn') - : t('common.appMenus.logs'), - href: `/app/${appId}/logs`, - icon: RiFileList3Line, - selectedIcon: RiFileList3Fill, - }] + name: mode !== AppModeEnum.WORKFLOW + ? t('common.appMenus.logAndAnn') + : t('common.appMenus.logs'), + href: `/app/${appId}/logs`, + icon: RiFileList3Line, + selectedIcon: RiFileList3Fill, + }] : [] ), { @@ -156,7 +157,7 @@ const AppDetailLayout: FC = (props) => { if (!appDetail) { return ( -
+
) @@ -173,7 +174,7 @@ const AppDetailLayout: FC = (props) => { {children}
{showTagManagementModal && ( - + )} ) 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 fb431c5ac8..f9110c9c1b 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx @@ -1,30 +1,30 @@ 'use client' import type { FC } from 'react' +import type { IAppCardProps } from '@/app/components/app/overview/app-card' +import type { BlockEnum } from '@/app/components/workflow/types' +import type { UpdateAppSiteCodeResponse } from '@/models/app' +import type { App } from '@/types/app' import React, { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import AppCard from '@/app/components/app/overview/app-card' -import Loading from '@/app/components/base/loading' -import MCPServiceCard from '@/app/components/tools/mcp/mcp-service-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 { ToastContext } from '@/app/components/base/toast' +import MCPServiceCard from '@/app/components/tools/mcp/mcp-service-card' +import { isTriggerNode } from '@/app/components/workflow/types' +import { NEED_REFRESH_APP_LIST_KEY } from '@/config' +import { useDocLink } from '@/context/i18n' import { fetchAppDetail, updateAppSiteAccessToken, updateAppSiteConfig, updateAppSiteStatus, } from '@/service/apps' -import type { App } from '@/types/app' -import { AppModeEnum } from '@/types/app' -import type { UpdateAppSiteCodeResponse } from '@/models/app' -import { asyncRunSafe } from '@/utils' -import { NEED_REFRESH_APP_LIST_KEY } from '@/config' -import type { IAppCardProps } from '@/app/components/app/overview/app-card' -import { useStore as useAppStore } from '@/app/components/app/store' import { useAppWorkflow } from '@/service/use-workflow' -import type { BlockEnum } from '@/app/components/workflow/types' -import { isTriggerNode } from '@/app/components/workflow/types' -import { useDocLink } from '@/context/i18n' +import { AppModeEnum } from '@/types/app' +import { asyncRunSafe } from '@/utils' export type ICardViewProps = { appId: string @@ -59,12 +59,12 @@ const CardView: FC = ({ appId, isInPanel, className }) => { const triggerDocUrl = docLink('/guides/workflow/node/start') const buildTriggerModeMessage = useCallback((featureName: string) => ( -
-
+
+
{t('appOverview.overview.disableTooltip.triggerMode', { feature: featureName })}
{ event.stopPropagation() window.open(triggerDocUrl, '_blank') @@ -185,12 +185,14 @@ const CardView: FC = ({ appId, isInPanel, className }) => { ) - const triggerCardNode = showTriggerCard ? ( - - ) : null + const triggerCardNode = showTriggerCard + ? ( + + ) + : null return (
diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chart-view.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chart-view.tsx index 64cd2fbd28..9304b45284 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chart-view.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chart-view.tsx @@ -1,15 +1,15 @@ 'use client' -import React, { useState } from 'react' +import type { PeriodParams } from '@/app/components/app/overview/app-chart' import dayjs from 'dayjs' import quarterOfYear from 'dayjs/plugin/quarterOfYear' +import React, { useState } from 'react' import { useTranslation } from 'react-i18next' -import type { PeriodParams } from '@/app/components/app/overview/app-chart' +import { TIME_PERIOD_MAPPING as LONG_TIME_PERIOD_MAPPING } from '@/app/components/app/log/filter' import { AvgResponseTime, AvgSessionInteractions, AvgUserInteractions, ConversationsChart, CostChart, EndUsersChart, MessagesChart, TokenPerSecond, UserSatisfactionRate, WorkflowCostChart, WorkflowDailyTerminalsChart, WorkflowMessagesChart } from '@/app/components/app/overview/app-chart' import { useStore as useAppStore } from '@/app/components/app/store' -import TimeRangePicker from './time-range-picker' -import { TIME_PERIOD_MAPPING as LONG_TIME_PERIOD_MAPPING } from '@/app/components/app/log/filter' import { IS_CLOUD_EDITION } from '@/config' import LongTimeRangePicker from './long-time-range-picker' +import TimeRangePicker from './time-range-picker' dayjs.extend(quarterOfYear) @@ -43,63 +43,65 @@ export default function ChartView({ appId, headerRight }: IChartViewProps) { return (
-
-
{t('common.appMenus.overview')}
-
- {IS_CLOUD_EDITION ? ( - - ) : ( - - )} +
+
{t('common.appMenus.overview')}
+
+ {IS_CLOUD_EDITION + ? ( + + ) + : ( + + )} {headerRight}
{!isWorkflow && ( -
+
)} {!isWorkflow && ( -
+
{isChatApp ? ( - - ) + + ) : ( - - )} + + )}
)} {!isWorkflow && ( -
+
)} {!isWorkflow && isChatApp && ( -
+
)} {isWorkflow && ( -
+
)} {isWorkflow && ( -
+
diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/long-time-range-picker.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/long-time-range-picker.tsx index cad4d41a0e..4d59f4a67f 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/long-time-range-picker.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/long-time-range-picker.tsx @@ -1,13 +1,14 @@ 'use client' -import type { PeriodParams } from '@/app/components/app/overview/app-chart' import type { FC } from 'react' -import React from 'react' +import type { PeriodParams } from '@/app/components/app/overview/app-chart' import type { Item } from '@/app/components/base/select' -import { SimpleSelect } from '@/app/components/base/select' -import { useTranslation } from 'react-i18next' import dayjs from 'dayjs' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { SimpleSelect } from '@/app/components/base/select' + type Props = { - periodMapping: { [key: string]: { value: number; name: string } } + periodMapping: { [key: string]: { value: number, name: string } } onSelect: (payload: PeriodParams) => void queryDateFormat: string } @@ -53,10 +54,10 @@ const LongTimeRangePicker: FC = ({ return ( ({ value: k, name: t(`appLog.filter.period.${v.name}`) }))} - className='mt-0 !w-40' + className="mt-0 !w-40" notClearable={true} onSelect={handleSelect} - defaultValue={'2'} + defaultValue="2" /> ) } diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx index bc07a799e4..e7ee4eb203 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx @@ -1,7 +1,7 @@ import React from 'react' +import ApikeyInfoPanel from '@/app/components/app/overview/apikey-info-panel' import ChartView from './chart-view' import TracingPanel from './tracing/panel' -import ApikeyInfoPanel from '@/app/components/app/overview/apikey-info-panel' export type IDevelopProps = { params: Promise<{ appId: string }> diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/date-picker.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/date-picker.tsx index dda5dff2b9..bc8bf58354 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/date-picker.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/date-picker.tsx @@ -1,15 +1,15 @@ 'use client' -import { RiCalendarLine } from '@remixicon/react' import type { Dayjs } from 'dayjs' import type { FC } from 'react' +import type { TriggerProps } from '@/app/components/base/date-and-time-picker/types' +import { RiCalendarLine } from '@remixicon/react' +import dayjs from 'dayjs' +import { noop } from 'lodash-es' import React, { useCallback } from 'react' +import Picker from '@/app/components/base/date-and-time-picker/date-picker' +import { useI18N } from '@/context/i18n' import { cn } from '@/utils/classnames' import { formatToLocalTime } from '@/utils/format' -import { useI18N } from '@/context/i18n' -import Picker from '@/app/components/base/date-and-time-picker/date-picker' -import type { TriggerProps } from '@/app/components/base/date-and-time-picker/types' -import { noop } from 'lodash-es' -import dayjs from 'dayjs' type Props = { start: Dayjs @@ -50,9 +50,9 @@ const DatePicker: FC = ({ }, [availableEndDate, start]) return ( -
-
- +
+
+
= ({ noConfirm getIsDateDisabled={startDateDisabled} /> - - + - void queryDateFormat: string } @@ -44,9 +44,12 @@ const TimeRangePicker: FC = ({ const handleDateChange = useCallback((type: 'start' | 'end') => { return (date?: Dayjs) => { - if (!date) return - if (type === 'start' && date.isSame(start)) return - if (type === 'end' && date.isSame(end)) return + if (!date) + return + if (type === 'start' && date.isSame(start)) + return + if (type === 'end' && date.isSame(end)) + return if (type === 'start') setStart(date) else @@ -67,13 +70,13 @@ const TimeRangePicker: FC = ({ }, [start, end, onSelect, locale, queryDateFormat]) return ( -
+
- + void } @@ -41,13 +41,13 @@ const RangeSelector: FC = ({ const renderTrigger = useCallback((item: Item | null, isOpen: boolean) => { return (
-
{isCustomRange ? t('appLog.filter.period.custom') : item?.name}
+
{isCustomRange ? t('appLog.filter.period.custom') : item?.name}
) }, [isCustomRange]) - const renderOption = useCallback(({ item, selected }: { item: Item; selected: boolean }) => { + const renderOption = useCallback(({ item, selected }: { item: Item, selected: boolean }) => { return ( <> {selected && ( @@ -66,14 +66,14 @@ const RangeSelector: FC = ({ return ( ({ ...v, name: t(`appLog.filter.period.${v.name}`) }))} - className='mt-0 !w-40' + className="mt-0 !w-40" notClearable={true} onSelect={handleSelectRange} defaultValue={0} - wrapperClassName='h-8' - optionWrapClassName='w-[200px] translate-x-[-24px]' + wrapperClassName="h-8" + optionWrapClassName="w-[200px] translate-x-[-24px]" renderTrigger={renderTrigger} - optionClassName='flex items-center py-0 pl-7 pr-2 h-8' + optionClassName="flex items-center py-0 pl-7 pr-2 h-8" renderOption={renderOption} /> ) diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/__tests__/svg-attribute-error-reproduction.spec.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/__tests__/svg-attribute-error-reproduction.spec.tsx index f93bef526f..a08a1476d7 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/__tests__/svg-attribute-error-reproduction.spec.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/__tests__/svg-attribute-error-reproduction.spec.tsx @@ -1,8 +1,8 @@ -import React from 'react' import { render } from '@testing-library/react' +import React from 'react' import { OpikIconBig } from '@/app/components/base/icons/src/public/tracing' -import { normalizeAttrs } from '@/app/components/base/icons/utils' import iconData from '@/app/components/base/icons/src/public/tracing/OpikIconBig.json' +import { normalizeAttrs } from '@/app/components/base/icons/utils' describe('SVG Attribute Error Reproduction', () => { // Capture console errors diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx index 17c919bf22..8dbb410f8c 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx @@ -1,15 +1,15 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useRef, useState } from 'react' - import type { PopupProps } from './config-popup' -import ConfigPopup from './config-popup' -import { cn } from '@/utils/classnames' + +import React, { useCallback, useRef, useState } from 'react' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' +import { cn } from '@/utils/classnames' +import ConfigPopup from './config-popup' type Props = { readOnly: boolean @@ -42,7 +42,7 @@ const ConfigBtn: FC = ({ @@ -50,7 +50,7 @@ const ConfigBtn: FC = ({ {children}
- + 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 767ccb8c59..3cbfcb4315 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 @@ -1,18 +1,18 @@ 'use client' import type { FC, JSX } from 'react' +import type { AliyunConfig, ArizeConfig, DatabricksConfig, LangFuseConfig, LangSmithConfig, MLflowConfig, OpikConfig, PhoenixConfig, TencentConfig, WeaveConfig } from './type' +import { useBoolean } from 'ahooks' import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useBoolean } from 'ahooks' -import TracingIcon from './tracing-icon' -import ProviderPanel from './provider-panel' -import type { AliyunConfig, ArizeConfig, DatabricksConfig, LangFuseConfig, LangSmithConfig, MLflowConfig, OpikConfig, PhoenixConfig, TencentConfig, WeaveConfig } from './type' -import { TracingProvider } from './type' -import ProviderConfigModal from './provider-config-modal' -import Indicator from '@/app/components/header/indicator' +import Divider from '@/app/components/base/divider' import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' -import Divider from '@/app/components/base/divider' +import Indicator from '@/app/components/header/indicator' import { cn } from '@/utils/classnames' +import ProviderConfigModal from './provider-config-modal' +import ProviderPanel from './provider-panel' +import TracingIcon from './tracing-icon' +import { TracingProvider } from './type' const I18N_PREFIX = 'app.tracing' @@ -92,7 +92,7 @@ const ConfigPopup: FC = ({ const switchContent = ( = ({ } return ( -
-
-
- -
{t(`${I18N_PREFIX}.tracing`)}
+
+
+
+ +
{t(`${I18N_PREFIX}.tracing`)}
-
+
{t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)} @@ -337,53 +337,53 @@ const ConfigPopup: FC = ({ <> {providerAllNotConfigured ? ( - - {switchContent} - - ) + + {switchContent} + + ) : switchContent} )}
-
+
{t(`${I18N_PREFIX}.tracingDescription`)}
- -
+ +
{(providerAllConfigured || providerAllNotConfigured) ? ( - <> -
{t(`${I18N_PREFIX}.configProviderTitle.${providerAllConfigured ? 'configured' : 'notConfigured'}`)}
-
- {langfusePanel} - {langSmithPanel} - {opikPanel} - {mlflowPanel} - {databricksPanel} - {weavePanel} - {arizePanel} - {phoenixPanel} - {aliyunPanel} - {tencentPanel} -
- - ) + <> +
{t(`${I18N_PREFIX}.configProviderTitle.${providerAllConfigured ? 'configured' : 'notConfigured'}`)}
+
+ {langfusePanel} + {langSmithPanel} + {opikPanel} + {mlflowPanel} + {databricksPanel} + {weavePanel} + {arizePanel} + {phoenixPanel} + {aliyunPanel} + {tencentPanel} +
+ + ) : ( - <> -
{t(`${I18N_PREFIX}.configProviderTitle.configured`)}
-
- {configuredProviderPanel()} -
-
{t(`${I18N_PREFIX}.configProviderTitle.moreProvider`)}
-
- {moreProviderPanel()} -
- - )} + <> +
{t(`${I18N_PREFIX}.configProviderTitle.configured`)}
+
+ {configuredProviderPanel()} +
+
{t(`${I18N_PREFIX}.configProviderTitle.moreProvider`)}
+
+ {moreProviderPanel()} +
+ + )}
{isShowConfigModal && ( diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx index e170159e35..a827a2b80c 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx @@ -1,8 +1,8 @@ 'use client' import type { FC } from 'react' import React from 'react' -import { cn } from '@/utils/classnames' import Input from '@/app/components/base/input' +import { cn } from '@/utils/classnames' type Props = { className?: string @@ -25,14 +25,17 @@ const Field: FC = ({ }) => { return (
-
-
{label}
- {isRequired && *} +
+
+ {label} + {' '} +
+ {isRequired && *}
onChange(e.target.value)} - className='h-9' + className="h-9" placeholder={placeholder} />
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 319ff3f423..1ea89012e3 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx @@ -1,26 +1,26 @@ 'use client' import type { FC } from 'react' -import React, { useEffect, useState } from 'react' +import type { AliyunConfig, ArizeConfig, DatabricksConfig, LangFuseConfig, LangSmithConfig, MLflowConfig, OpikConfig, PhoenixConfig, TencentConfig, WeaveConfig } from './type' +import type { TracingStatus } from '@/models/app' import { RiArrowDownDoubleLine, RiEqualizer2Line, } from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import { usePathname } from 'next/navigation' import { useBoolean } from 'ahooks' -import type { AliyunConfig, ArizeConfig, DatabricksConfig, LangFuseConfig, LangSmithConfig, MLflowConfig, OpikConfig, PhoenixConfig, TencentConfig, WeaveConfig } from './type' -import { TracingProvider } from './type' -import TracingIcon from './tracing-icon' -import ConfigButton from './config-button' -import { cn } from '@/utils/classnames' -import { AliyunIcon, ArizeIcon, DatabricksIcon, LangfuseIcon, LangsmithIcon, MlflowIcon, OpikIcon, PhoenixIcon, TencentIcon, WeaveIcon } from '@/app/components/base/icons/src/public/tracing' -import Indicator from '@/app/components/header/indicator' -import { fetchTracingConfig as doFetchTracingConfig, fetchTracingStatus, updateTracingStatus } from '@/service/apps' -import type { TracingStatus } from '@/models/app' -import Toast from '@/app/components/base/toast' -import { useAppContext } from '@/context/app-context' -import Loading from '@/app/components/base/loading' +import { usePathname } from 'next/navigation' +import React, { useEffect, useState } from 'react' +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/toast' +import Indicator from '@/app/components/header/indicator' +import { useAppContext } from '@/context/app-context' +import { fetchTracingConfig as doFetchTracingConfig, fetchTracingStatus, updateTracingStatus } from '@/service/apps' +import { cn } from '@/utils/classnames' +import ConfigButton from './config-button' +import TracingIcon from './tracing-icon' +import { TracingProvider } from './type' const I18N_PREFIX = 'app.tracing' @@ -215,8 +215,8 @@ const Panel: FC = () => { if (!isLoaded) { return ( -
-
+
+
@@ -252,14 +252,14 @@ const Panel: FC = () => { 'flex cursor-pointer select-none items-center rounded-xl border-l-[0.5px] border-t border-effects-highlight bg-background-default-dodge p-2 shadow-xs hover:border-effects-highlight-lightmode-off hover:bg-background-default-lighter', )} > - -
{t(`${I18N_PREFIX}.title`)}
-
- + +
{t(`${I18N_PREFIX}.title`)}
+
+
- -
- + +
+
@@ -291,17 +291,17 @@ const Panel: FC = () => { 'flex cursor-pointer select-none items-center rounded-xl border-l-[0.5px] border-t border-effects-highlight bg-background-default-dodge p-2 shadow-xs hover:border-effects-highlight-lightmode-off hover:bg-background-default-lighter', )} > -
+
-
+
{t(`${I18N_PREFIX}.${enabled ? 'enabled' : 'disabled'}`)}
- {InUseProviderIcon && } -
- + {InUseProviderIcon && } +
+
- +
)} 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 7cf479f5a8..254ca4e01a 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,23 +1,23 @@ 'use client' import type { FC } from 'react' +import type { AliyunConfig, ArizeConfig, DatabricksConfig, LangFuseConfig, LangSmithConfig, MLflowConfig, OpikConfig, PhoenixConfig, TencentConfig, WeaveConfig } from './type' +import { useBoolean } from 'ahooks' import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useBoolean } from 'ahooks' -import Field from './field' -import type { AliyunConfig, ArizeConfig, DatabricksConfig, LangFuseConfig, LangSmithConfig, MLflowConfig, OpikConfig, PhoenixConfig, TencentConfig, WeaveConfig } from './type' -import { TracingProvider } from './type' -import { docURL } from './config' +import Button from '@/app/components/base/button' +import Confirm from '@/app/components/base/confirm' +import Divider from '@/app/components/base/divider' +import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general' +import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security' import { PortalToFollowElem, PortalToFollowElemContent, } from '@/app/components/base/portal-to-follow-elem' -import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security' -import Button from '@/app/components/base/button' -import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general' -import Confirm from '@/app/components/base/confirm' -import { addTracingConfig, removeTracingConfig, updateTracingConfig } from '@/service/apps' import Toast from '@/app/components/base/toast' -import Divider from '@/app/components/base/divider' +import { addTracingConfig, removeTracingConfig, updateTracingConfig } from '@/service/apps' +import { docURL } from './config' +import Field from './field' +import { TracingProvider } from './type' type Props = { appId: string @@ -295,403 +295,407 @@ const ProviderConfigModal: FC = ({ <> {!isShowRemoveConfirm ? ( - - -
-
-
-
-
{t(`${I18N_PREFIX}.title`)}{t(`app.tracing.${type}.title`)}
-
- -
- {type === TracingProvider.arize && ( - <> - - - - - - )} - {type === TracingProvider.phoenix && ( - <> - - - - - )} - {type === TracingProvider.aliyun && ( - <> - - - - - )} - {type === TracingProvider.tencent && ( - <> - - - - - )} - {type === TracingProvider.weave && ( - <> - - - - - - - )} - {type === TracingProvider.langSmith && ( - <> - - - - - )} - {type === TracingProvider.langfuse && ( - <> - - - - - )} - {type === TracingProvider.opik && ( - <> - - - - - - )} - {type === TracingProvider.mlflow && ( - <> - - - - - - )} - {type === TracingProvider.databricks && ( - <> - - - - - - - )} -
-
- - {t(`${I18N_PREFIX}.viewDocsLink`, { key: t(`app.tracing.${type}.title`) })} - - -
- {isEdit && ( - <> - - - - )} - - + + +
+
+
+
+
+ {t(`${I18N_PREFIX}.title`)} + {t(`app.tracing.${type}.title`)} +
+
+ {type === TracingProvider.arize && ( + <> + + + + + + )} + {type === TracingProvider.phoenix && ( + <> + + + + + )} + {type === TracingProvider.aliyun && ( + <> + + + + + )} + {type === TracingProvider.tencent && ( + <> + + + + + )} + {type === TracingProvider.weave && ( + <> + + + + + + + )} + {type === TracingProvider.langSmith && ( + <> + + + + + )} + {type === TracingProvider.langfuse && ( + <> + + + + + )} + {type === TracingProvider.opik && ( + <> + + + + + + )} + {type === TracingProvider.mlflow && ( + <> + + + + + + )} + {type === TracingProvider.databricks && ( + <> + + + + + + + )} +
+
+ + {t(`${I18N_PREFIX}.viewDocsLink`, { key: t(`app.tracing.${type}.title`) })} + + +
+ {isEdit && ( + <> + + + + )} + + +
+ +
-
-
-
- - {t('common.modelProvider.encrypted.front')} - - PKCS1_OAEP - - {t('common.modelProvider.encrypted.back')} +
+
+ + {t('common.modelProvider.encrypted.front')} + + PKCS1_OAEP + + {t('common.modelProvider.encrypted.back')} +
-
-
-
- ) + + + ) : ( - - )} + + )} ) } diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx index 0779689c76..aebdf70b55 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx @@ -1,14 +1,14 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' import { RiEqualizer2Line, } from '@remixicon/react' +import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { TracingProvider } from './type' -import { cn } from '@/utils/classnames' import { AliyunIconBig, ArizeIconBig, DatabricksIconBig, LangfuseIconBig, LangsmithIconBig, MlflowIconBig, OpikIconBig, PhoenixIconBig, TencentIconBig, WeaveIconBig } from '@/app/components/base/icons/src/public/tracing' import { Eye as View } from '@/app/components/base/icons/src/vender/solid/general' +import { cn } from '@/utils/classnames' +import { TracingProvider } from './type' const I18N_PREFIX = 'app.tracing' @@ -78,30 +78,30 @@ const ProviderPanel: FC = ({ )} onClick={handleChosen} > -
-
- - {isChosen &&
{t(`${I18N_PREFIX}.inUse`)}
} +
+
+ + {isChosen &&
{t(`${I18N_PREFIX}.inUse`)}
}
{!readOnly && ( -
+
{hasConfigured && ( -
- -
{t(`${I18N_PREFIX}.view`)}
+
+ +
{t(`${I18N_PREFIX}.view`)}
)}
- -
{t(`${I18N_PREFIX}.config`)}
+ +
{t(`${I18N_PREFIX}.config`)}
)}
-
+
{t(`${I18N_PREFIX}.${type}.description`)}
diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx index aeca1cd3ab..aa8567548d 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx @@ -1,8 +1,8 @@ 'use client' import type { FC } from 'react' import React from 'react' -import { cn } from '@/utils/classnames' import { TracingIcon as Icon } from '@/app/components/base/icons/src/public/tracing' +import { cn } from '@/utils/classnames' type Props = { className?: string @@ -21,7 +21,7 @@ const TracingIcon: FC = ({ const sizeClass = sizeClassMap[size] return (
- +
) } diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx index 15da0bbed2..6c9386a44e 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx @@ -2,7 +2,7 @@ import WorkflowApp from '@/app/components/workflow-app' const Page = () => { return ( -
+
) diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/layout.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/layout.tsx index 126bf45842..f41babeb4e 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/layout.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/layout.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useEffect } from 'react' import { useRouter } from 'next/navigation' +import React, { useEffect } from 'react' import { useTranslation } from 'react-i18next' import { useAppContext } from '@/context/app-context' import useDocumentTitle from '@/hooks/use-document-title' diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx index 1db6b667ad..ed6365c890 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx @@ -2,7 +2,7 @@ import React from 'react' import MainDetail from '@/app/components/datasets/documents/detail' export type IDocumentDetailProps = { - params: Promise<{ datasetId: string; documentId: string }> + params: Promise<{ datasetId: string, documentId: string }> } const DocumentDetail = async (props: IDocumentDetailProps) => { diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx index 539521410c..e8576d4a4b 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx @@ -2,7 +2,7 @@ import React from 'react' import Settings from '@/app/components/datasets/documents/detail/settings' export type IProps = { - params: Promise<{ datasetId: string; documentId: string }> + params: Promise<{ datasetId: string, documentId: string }> } const DocumentSettings = async (props: IProps) => { diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx index 3581587b54..6caecc4826 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx @@ -1,9 +1,6 @@ 'use client' -import type { FC } from 'react' -import React, { useEffect, useMemo, useState } from 'react' -import { usePathname } from 'next/navigation' -import { useTranslation } from 'react-i18next' import type { RemixiconComponentType } from '@remixicon/react' +import type { FC } from 'react' import { RiEqualizer2Fill, RiEqualizer2Line, @@ -12,17 +9,20 @@ import { RiFocus2Fill, RiFocus2Line, } from '@remixicon/react' +import { usePathname } from 'next/navigation' +import React, { useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' import AppSideBar from '@/app/components/app-sidebar' -import Loading from '@/app/components/base/loading' -import DatasetDetailContext from '@/context/dataset-detail' -import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import { useStore } from '@/app/components/app/store' -import { useAppContext } from '@/context/app-context' import { PipelineFill, PipelineLine } from '@/app/components/base/icons/src/vender/pipeline' -import { useDatasetDetail, useDatasetRelatedApps } from '@/service/knowledge/use-dataset' -import useDocumentTitle from '@/hooks/use-document-title' +import Loading from '@/app/components/base/loading' import ExtraInfo from '@/app/components/datasets/extra-info' +import { useAppContext } from '@/context/app-context' +import DatasetDetailContext from '@/context/dataset-detail' import { useEventEmitterContextContext } from '@/context/event-emitter' +import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' +import useDocumentTitle from '@/hooks/use-document-title' +import { useDatasetDetail, useDatasetRelatedApps } from '@/service/knowledge/use-dataset' import { cn } from '@/utils/classnames' export type IAppDetailLayoutProps = { @@ -115,7 +115,7 @@ const DatasetDetailLayout: FC = (props) => { }, [isMobile, setAppSidebarExpand]) if (!datasetRes && !error) - return + return return (
= (props) => { indexingTechnique: datasetRes?.indexing_technique, dataset: datasetRes, mutateDatasetRes, - }}> + }} + > {!hideSideBar && ( = (props) => { ? mode => : undefined } - iconType='dataset' + iconType="dataset" /> )} -
{children}
+
{children}
) diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/pipeline/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/pipeline/page.tsx index 9a18021cc0..f3028cbad0 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/pipeline/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/pipeline/page.tsx @@ -3,7 +3,7 @@ import RagPipeline from '@/app/components/rag-pipeline' const PipelinePage = () => { return ( -
+
) diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx index 5469a5f472..59540a1854 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx @@ -1,16 +1,16 @@ import React from 'react' -import { getLocaleOnServer, useTranslation as translate } from '@/i18n-config/server' import Form from '@/app/components/datasets/settings/form' +import { getLocaleOnServer, useTranslation as translate } from '@/i18n-config/server' const Settings = async () => { const locale = await getLocaleOnServer() const { t } = await translate(locale, 'dataset-settings') return ( -
-
-
{t('title')}
-
{t('desc')}
+
+
+
{t('title')}
+
{t('desc')}
diff --git a/web/app/(commonLayout)/datasets/layout.tsx b/web/app/(commonLayout)/datasets/layout.tsx index 5f97d853ef..fda4d3c803 100644 --- a/web/app/(commonLayout)/datasets/layout.tsx +++ b/web/app/(commonLayout)/datasets/layout.tsx @@ -1,11 +1,11 @@ 'use client' +import { useRouter } from 'next/navigation' +import { useEffect } from 'react' import Loading from '@/app/components/base/loading' import { useAppContext } from '@/context/app-context' import { ExternalApiPanelProvider } from '@/context/external-api-panel-context' import { ExternalKnowledgeApiProvider } from '@/context/external-knowledge-api-context' -import { useRouter } from 'next/navigation' -import { useEffect } from 'react' export default function DatasetsLayout({ children }: { children: React.ReactNode }) { const { isCurrentWorkspaceEditor, isCurrentWorkspaceDatasetOperator, currentWorkspace, isLoadingCurrentWorkspace } = useAppContext() @@ -19,7 +19,7 @@ export default function DatasetsLayout({ children }: { children: React.ReactNode }, [isCurrentWorkspaceEditor, isCurrentWorkspaceDatasetOperator, isLoadingCurrentWorkspace, currentWorkspace, router]) if (isLoadingCurrentWorkspace || !(isCurrentWorkspaceEditor || isCurrentWorkspaceDatasetOperator)) - return + return return ( diff --git a/web/app/(commonLayout)/education-apply/page.tsx b/web/app/(commonLayout)/education-apply/page.tsx index 5dd3c35519..fce6fe1d5d 100644 --- a/web/app/(commonLayout)/education-apply/page.tsx +++ b/web/app/(commonLayout)/education-apply/page.tsx @@ -1,13 +1,13 @@ 'use client' -import { - useEffect, - useMemo, -} from 'react' import { useRouter, useSearchParams, } from 'next/navigation' +import { + useEffect, + useMemo, +} from 'react' import EducationApplyPage from '@/app/education-apply/education-apply-page' import { useProviderContext } from '@/context/provider-context' diff --git a/web/app/(commonLayout)/layout.tsx b/web/app/(commonLayout)/layout.tsx index 60c2a98700..a759506bf0 100644 --- a/web/app/(commonLayout)/layout.tsx +++ b/web/app/(commonLayout)/layout.tsx @@ -1,18 +1,18 @@ -import React from 'react' import type { ReactNode } from 'react' +import React from 'react' +import AmplitudeProvider from '@/app/components/base/amplitude' +import GA, { GaType } from '@/app/components/base/ga' +import Zendesk from '@/app/components/base/zendesk' +import GotoAnything from '@/app/components/goto-anything' +import Header from '@/app/components/header' +import HeaderWrapper from '@/app/components/header/header-wrapper' +import ReadmePanel from '@/app/components/plugins/readme-panel' import SwrInitializer from '@/app/components/swr-initializer' import { AppContextProvider } from '@/context/app-context' -import GA, { GaType } from '@/app/components/base/ga' -import AmplitudeProvider from '@/app/components/base/amplitude' -import HeaderWrapper from '@/app/components/header/header-wrapper' -import Header from '@/app/components/header' import { EventEmitterContextProvider } from '@/context/event-emitter' -import { ProviderContextProvider } from '@/context/provider-context' import { ModalContextProvider } from '@/context/modal-context' -import GotoAnything from '@/app/components/goto-anything' -import Zendesk from '@/app/components/base/zendesk' +import { ProviderContextProvider } from '@/context/provider-context' import PartnerStack from '../components/billing/partner-stack' -import ReadmePanel from '@/app/components/plugins/readme-panel' import Splash from '../components/splash' const Layout = ({ children }: { children: ReactNode }) => { diff --git a/web/app/(commonLayout)/plugins/page.tsx b/web/app/(commonLayout)/plugins/page.tsx index ad61b16ba2..2df9cf23c4 100644 --- a/web/app/(commonLayout)/plugins/page.tsx +++ b/web/app/(commonLayout)/plugins/page.tsx @@ -1,6 +1,6 @@ +import Marketplace from '@/app/components/plugins/marketplace' import PluginPage from '@/app/components/plugins/plugin-page' import PluginsPanel from '@/app/components/plugins/plugin-page/plugins-panel' -import Marketplace from '@/app/components/plugins/marketplace' import { getLocaleOnServer } from '@/i18n-config/server' const PluginList = async () => { @@ -8,7 +8,7 @@ const PluginList = async () => { return ( } - marketplace={} + marketplace={} /> ) } diff --git a/web/app/(commonLayout)/tools/page.tsx b/web/app/(commonLayout)/tools/page.tsx index dc7e189c43..3ea50e70ef 100644 --- a/web/app/(commonLayout)/tools/page.tsx +++ b/web/app/(commonLayout)/tools/page.tsx @@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next' import ToolProviderList from '@/app/components/tools/provider-list' import { useAppContext } from '@/context/app-context' import useDocumentTitle from '@/hooks/use-document-title' + const ToolsList: FC = () => { const router = useRouter() const { isCurrentWorkspaceDatasetOperator } = useAppContext() diff --git a/web/app/(shareLayout)/components/authenticated-layout.tsx b/web/app/(shareLayout)/components/authenticated-layout.tsx index 2185606a6d..5f436429d3 100644 --- a/web/app/(shareLayout)/components/authenticated-layout.tsx +++ b/web/app/(shareLayout)/components/authenticated-layout.tsx @@ -1,14 +1,14 @@ 'use client' +import { usePathname, useRouter, useSearchParams } from 'next/navigation' +import React, { useCallback, useEffect } from 'react' +import { useTranslation } from 'react-i18next' import AppUnavailable from '@/app/components/base/app-unavailable' import Loading from '@/app/components/base/loading' import { useWebAppStore } from '@/context/web-app-context' import { useGetUserCanAccessApp } from '@/service/access-control' import { useGetWebAppInfo, useGetWebAppMeta, useGetWebAppParams } from '@/service/use-share' import { webAppLogout } from '@/service/webapp-auth' -import { usePathname, useRouter, useSearchParams } from 'next/navigation' -import React, { useCallback, useEffect } from 'react' -import { useTranslation } from 'react-i18next' const AuthenticatedLayout = ({ children }: { children: React.ReactNode }) => { const { t } = useTranslation() @@ -49,35 +49,47 @@ const AuthenticatedLayout = ({ children }: { children: React.ReactNode }) => { }, [getSigninUrl, router, webAppLogout, shareCode]) if (appInfoError) { - return
- -
+ return ( +
+ +
+ ) } if (appParamsError) { - return
- -
+ return ( +
+ +
+ ) } if (appMetaError) { - return
- -
+ return ( +
+ +
+ ) } if (useCanAccessAppError) { - return
- -
+ return ( +
+ +
+ ) } if (userCanAccessApp && !userCanAccessApp.result) { - return
- - {t('common.userProfile.logout')} -
+ return ( +
+ + {t('common.userProfile.logout')} +
+ ) } if (isFetchingAppInfo || isFetchingAppParams || isFetchingAppMeta) { - return
- -
+ return ( +
+ +
+ ) } return <>{children} } diff --git a/web/app/(shareLayout)/components/splash.tsx b/web/app/(shareLayout)/components/splash.tsx index 9315e79d20..b8ea1b2e56 100644 --- a/web/app/(shareLayout)/components/splash.tsx +++ b/web/app/(shareLayout)/components/splash.tsx @@ -1,15 +1,13 @@ 'use client' import type { FC, PropsWithChildren } from 'react' -import { useEffect, useState } from 'react' -import { useCallback } from 'react' -import { useWebAppStore } from '@/context/web-app-context' import { useRouter, useSearchParams } from 'next/navigation' -import AppUnavailable from '@/app/components/base/app-unavailable' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { webAppLoginStatus, webAppLogout } from '@/service/webapp-auth' -import { fetchAccessToken } from '@/service/share' +import AppUnavailable from '@/app/components/base/app-unavailable' import Loading from '@/app/components/base/loading' -import { setWebAppAccessToken, setWebAppPassport } from '@/service/webapp-auth' +import { useWebAppStore } from '@/context/web-app-context' +import { fetchAccessToken } from '@/service/share' +import { setWebAppAccessToken, setWebAppPassport, webAppLoginStatus, webAppLogout } from '@/service/webapp-auth' const Splash: FC = ({ children }) => { const { t } = useTranslation() @@ -42,7 +40,7 @@ const Splash: FC = ({ children }) => { return } - if(tokenFromUrl) + if (tokenFromUrl) setWebAppAccessToken(tokenFromUrl) const redirectOrFinish = () => { @@ -90,19 +88,24 @@ const Splash: FC = ({ children }) => { message, webAppAccessMode, tokenFromUrl, - embeddedUserId]) + embeddedUserId, + ]) if (message) { - return
- - {code === '403' ? t('common.userProfile.logout') : t('share.login.backToHome')} -
+ return ( +
+ + {code === '403' ? t('common.userProfile.logout') : t('share.login.backToHome')} +
+ ) } if (isLoading) { - return
- -
+ return ( +
+ +
+ ) } return <>{children} } 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 d1d92d12df..9ce058340c 100644 --- a/web/app/(shareLayout)/webapp-reset-password/check-code/page.tsx +++ b/web/app/(shareLayout)/webapp-reset-password/check-code/page.tsx @@ -1,15 +1,15 @@ 'use client' import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import { useState } from 'react' import { useRouter, useSearchParams } from 'next/navigation' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' -import Countdown from '@/app/components/signin/countdown' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' -import { sendWebAppResetPasswordCode, verifyWebAppResetPasswordCode } from '@/service/common' +import Countdown from '@/app/components/signin/countdown' import I18NContext from '@/context/i18n' +import { sendWebAppResetPasswordCode, verifyWebAppResetPasswordCode } from '@/service/common' export default function CheckCode() { const { t } = useTranslation() @@ -63,37 +63,39 @@ export default function CheckCode() { catch (error) { console.error(error) } } - return
-
- -
-
-

{t('login.checkCode.checkYourEmail')}

-

- - {t('login.checkCode.tipsPrefix')} - {email} - -
- {t('login.checkCode.validTime')} -

-
- - - - - setVerifyCode(e.target.value)} maxLength={6} className='mt-1' placeholder={t('login.checkCode.verificationCodePlaceholder') || ''} /> - - - -
-
-
-
router.back()} className='flex h-9 cursor-pointer items-center justify-center text-text-tertiary'> -
- + return ( +
+
+ +
+
+

{t('login.checkCode.checkYourEmail')}

+

+ + {t('login.checkCode.tipsPrefix')} + {email} + +
+ {t('login.checkCode.validTime')} +

+
+ +
+ + + setVerifyCode(e.target.value)} maxLength={6} className="mt-1" placeholder={t('login.checkCode.verificationCodePlaceholder') || ''} /> + + + +
+
+
+
router.back()} className="flex h-9 cursor-pointer items-center justify-center text-text-tertiary"> +
+ +
+ {t('login.back')}
- {t('login.back')}
-
+ ) } diff --git a/web/app/(shareLayout)/webapp-reset-password/layout.tsx b/web/app/(shareLayout)/webapp-reset-password/layout.tsx index 13073b0e6a..a71317c8b9 100644 --- a/web/app/(shareLayout)/webapp-reset-password/layout.tsx +++ b/web/app/(shareLayout)/webapp-reset-password/layout.tsx @@ -1,30 +1,39 @@ 'use client' import Header from '@/app/signin/_header' -import { cn } from '@/utils/classnames' import { useGlobalPublicStore } from '@/context/global-public-context' +import { cn } from '@/utils/classnames' export default function SignInLayout({ children }: any) { const { systemFeatures } = useGlobalPublicStore() - return <> -
-
-
-
-
- {children} + return ( + <> +
+
+
+
+
+ {children} +
+ {!systemFeatures.branding.enabled && ( +
+ © + {' '} + {new Date().getFullYear()} + {' '} + LangGenius, Inc. All rights reserved. +
+ )}
- {!systemFeatures.branding.enabled &&
- © {new Date().getFullYear()} LangGenius, Inc. All rights reserved. -
}
-
- + + ) } diff --git a/web/app/(shareLayout)/webapp-reset-password/page.tsx b/web/app/(shareLayout)/webapp-reset-password/page.tsx index 96cd4c5805..92c39eb729 100644 --- a/web/app/(shareLayout)/webapp-reset-password/page.tsx +++ b/web/app/(shareLayout)/webapp-reset-password/page.tsx @@ -1,19 +1,19 @@ 'use client' -import Link from 'next/link' import { RiArrowLeftLine, RiLockPasswordLine } from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import { useState } from 'react' +import { noop } from 'lodash-es' +import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' -import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown' -import { emailRegex } from '@/config' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' -import { sendResetPasswordCode } from '@/service/common' +import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown' +import { emailRegex } from '@/config' import I18NContext from '@/context/i18n' -import { noop } from 'lodash-es' import useDocumentTitle from '@/hooks/use-document-title' +import { sendResetPasswordCode } from '@/service/common' export default function CheckCode() { const { t } = useTranslation() @@ -68,37 +68,39 @@ export default function CheckCode() { } } - return
-
- -
-
-

{t('login.resetPassword')}

-

- {t('login.resetPasswordDesc')} -

-
+ return ( +
+
+ +
+
+

{t('login.resetPassword')}

+

+ {t('login.resetPasswordDesc')} +

+
-
- -
- -
- setEmail(e.target.value)} /> -
-
- + + +
+ +
+ setEmail(e.target.value)} /> +
+
+ +
+ +
+
- -
-
+ +
+ +
+ {t('login.backToLogin')} +
- -
- -
- {t('login.backToLogin')} - -
+ ) } 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 843f10e039..bfb71d9c6f 100644 --- a/web/app/(shareLayout)/webapp-reset-password/set-password/page.tsx +++ b/web/app/(shareLayout)/webapp-reset-password/set-password/page.tsx @@ -1,15 +1,15 @@ 'use client' -import { useCallback, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { useRouter, useSearchParams } from 'next/navigation' -import { cn } from '@/utils/classnames' import { RiCheckboxCircleFill } from '@remixicon/react' import { useCountDown } from 'ahooks' +import { useRouter, useSearchParams } from 'next/navigation' +import { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' -import { changeWebAppPasswordWithToken } from '@/service/common' -import Toast from '@/app/components/base/toast' import Input from '@/app/components/base/input' +import Toast from '@/app/components/base/toast' import { validPassword } from '@/config' +import { changeWebAppPasswordWithToken } from '@/service/common' +import { cn } from '@/utils/classnames' const ChangePasswordForm = () => { const { t } = useTranslation() @@ -86,14 +86,15 @@ const ChangePasswordForm = () => { 'px-6', 'md:px-[108px]', ) - }> + } + > {!showSuccess && ( -
+

{t('login.changePassword')}

-

+

{t('login.changePasswordTip')}

@@ -101,13 +102,14 @@ const ChangePasswordForm = () => {
{/* Password */} -
+
-
+
setPassword(e.target.value)} placeholder={t('login.passwordPlaceholder') || ''} @@ -116,21 +118,21 @@ const ChangePasswordForm = () => {
-
{t('login.error.passwordInvalid')}
+
{t('login.error.passwordInvalid')}
{/* Confirm Password */} -
+
-
+
{
+
)} diff --git a/web/app/(shareLayout)/webapp-signin/check-code/page.tsx b/web/app/(shareLayout)/webapp-signin/check-code/page.tsx index 00876f97e9..ee7fc22bea 100644 --- a/web/app/(shareLayout)/webapp-signin/check-code/page.tsx +++ b/web/app/(shareLayout)/webapp-signin/check-code/page.tsx @@ -1,18 +1,19 @@ 'use client' +import type { FormEvent } from 'react' import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import { type FormEvent, useCallback, useEffect, useRef, useState } from 'react' import { useRouter, useSearchParams } from 'next/navigation' +import { useCallback, useEffect, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' -import Countdown from '@/app/components/signin/countdown' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' -import { sendWebAppEMailLoginCode, webAppEmailLoginWithCode } from '@/service/common' +import Countdown from '@/app/components/signin/countdown' import I18NContext from '@/context/i18n' -import { setWebAppAccessToken, setWebAppPassport } from '@/service/webapp-auth' -import { fetchAccessToken } from '@/service/share' import { useWebAppStore } from '@/context/web-app-context' +import { sendWebAppEMailLoginCode, webAppEmailLoginWithCode } from '@/service/common' +import { fetchAccessToken } from '@/service/share' +import { setWebAppAccessToken, setWebAppPassport } from '@/service/webapp-auth' export default function CheckCode() { const { t } = useTranslation() @@ -101,44 +102,46 @@ export default function CheckCode() { catch (error) { console.error(error) } } - return
-
- -
-
-

{t('login.checkCode.checkYourEmail')}

-

- - {t('login.checkCode.tipsPrefix')} - {email} - -
- {t('login.checkCode.validTime')} -

-
- -
- - setVerifyCode(e.target.value)} - maxLength={6} - className='mt-1' - placeholder={t('login.checkCode.verificationCodePlaceholder') || ''} - /> - - - -
-
-
-
router.back()} className='flex h-9 cursor-pointer items-center justify-center text-text-tertiary'> -
- + return ( +
+
+ +
+
+

{t('login.checkCode.checkYourEmail')}

+

+ + {t('login.checkCode.tipsPrefix')} + {email} + +
+ {t('login.checkCode.validTime')} +

+
+ +
+ + setVerifyCode(e.target.value)} + maxLength={6} + className="mt-1" + placeholder={t('login.checkCode.verificationCodePlaceholder') || ''} + /> + + + +
+
+
+
router.back()} className="flex h-9 cursor-pointer items-center justify-center text-text-tertiary"> +
+ +
+ {t('login.back')}
- {t('login.back')}
-
+ ) } 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 612a9677a6..091493812f 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,12 +1,12 @@ 'use client' import { useRouter, useSearchParams } from 'next/navigation' import React, { useCallback, useEffect } from 'react' -import Toast from '@/app/components/base/toast' -import { fetchWebOAuth2SSOUrl, fetchWebOIDCSSOUrl, fetchWebSAMLSSOUrl } from '@/service/share' -import { useGlobalPublicStore } from '@/context/global-public-context' -import { SSOProtocol } from '@/types/feature' -import Loading from '@/app/components/base/loading' import AppUnavailable from '@/app/components/base/app-unavailable' +import Loading from '@/app/components/base/loading' +import Toast from '@/app/components/base/toast' +import { useGlobalPublicStore } from '@/context/global-public-context' +import { fetchWebOAuth2SSOUrl, fetchWebOIDCSSOUrl, fetchWebSAMLSSOUrl } from '@/service/share' +import { SSOProtocol } from '@/types/feature' const ExternalMemberSSOAuth = () => { const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) @@ -68,9 +68,11 @@ const ExternalMemberSSOAuth = () => { }, [handleSSOLogin]) if (!systemFeatures.webapp_auth.sso_config.protocol) { - return
- -
+ return ( +
+ +
+ ) } return ( 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 107442761a..9020858347 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,15 +1,15 @@ +import { noop } from 'lodash-es' +import { useRouter, useSearchParams } from 'next/navigation' import { useState } from 'react' import { useTranslation } from 'react-i18next' -import { useRouter, useSearchParams } from 'next/navigation' import { useContext } from 'use-context-selector' -import Input from '@/app/components/base/input' import Button from '@/app/components/base/button' -import { emailRegex } from '@/config' +import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' -import { sendWebAppEMailLoginCode } from '@/service/common' import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown' +import { emailRegex } from '@/config' import I18NContext from '@/context/i18n' -import { noop } from 'lodash-es' +import { sendWebAppEMailLoginCode } from '@/service/common' export default function MailAndCodeAuth() { const { t } = useTranslation() @@ -52,17 +52,18 @@ export default function MailAndCodeAuth() { } } - return (
- -
- -
- setEmail(e.target.value)} /> + return ( + + +
+ +
+ setEmail(e.target.value)} /> +
+
+ +
-
- -
-
- + ) } 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 0136445ac9..67e4bf7af2 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,17 +1,17 @@ 'use client' +import { noop } from 'lodash-es' import Link from 'next/link' +import { useRouter, useSearchParams } from 'next/navigation' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useRouter, useSearchParams } from 'next/navigation' import { useContext } from 'use-context-selector' import Button from '@/app/components/base/button' +import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' import { emailRegex } from '@/config' -import { webAppLogin } from '@/service/common' -import Input from '@/app/components/base/input' import I18NContext from '@/context/i18n' import { useWebAppStore } from '@/context/web-app-context' -import { noop } from 'lodash-es' +import { webAppLogin } from '@/service/common' import { fetchAccessToken } from '@/service/share' import { setWebAppAccessToken, setWebAppPassport } from '@/service/webapp-auth' @@ -107,70 +107,74 @@ export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAut } } - return
-
- -
- setEmail(e.target.value)} - id="email" - type="email" - autoComplete="email" - placeholder={t('login.emailPlaceholder') || ''} - tabIndex={1} - /> -
-
- -
- -
- setPassword(e.target.value)} - id="password" - onKeyDown={(e) => { - if (e.key === 'Enter') - handleEmailPasswordLogin() - }} - type={showPassword ? 'text' : 'password'} - autoComplete="current-password" - placeholder={t('login.passwordPlaceholder') || ''} - tabIndex={2} - /> -
- + return ( + +
+ +
+ setEmail(e.target.value)} + id="email" + type="email" + autoComplete="email" + placeholder={t('login.emailPlaceholder') || ''} + tabIndex={1} + />
-
-
- -
- +
+ +
+ setPassword(e.target.value)} + id="password" + onKeyDown={(e) => { + if (e.key === 'Enter') + handleEmailPasswordLogin() + }} + type={showPassword ? 'text' : 'password'} + autoComplete="current-password" + placeholder={t('login.passwordPlaceholder') || ''} + tabIndex={2} + /> +
+ +
+
+
+ +
+ +
+ + ) } diff --git a/web/app/(shareLayout)/webapp-signin/components/sso-auth.tsx b/web/app/(shareLayout)/webapp-signin/components/sso-auth.tsx index bcba572644..472952c2c8 100644 --- a/web/app/(shareLayout)/webapp-signin/components/sso-auth.tsx +++ b/web/app/(shareLayout)/webapp-signin/components/sso-auth.tsx @@ -1,14 +1,13 @@ 'use client' -import { useRouter, useSearchParams } from 'next/navigation' import type { FC } from 'react' -import { useCallback } from 'react' -import { useState } from 'react' +import { useRouter, useSearchParams } from 'next/navigation' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security' import Toast from '@/app/components/base/toast' -import Button from '@/app/components/base/button' -import { SSOProtocol } from '@/types/feature' import { fetchMembersOAuth2SSOUrl, fetchMembersOIDCSSOUrl, fetchMembersSAMLSSOUrl } from '@/service/share' +import { SSOProtocol } from '@/types/feature' type SSOAuthProps = { protocol: SSOProtocol | '' @@ -82,7 +81,7 @@ const SSOAuth: FC = ({ disabled={isLoading} className="w-full" > - + {t('login.withSSO')} ) diff --git a/web/app/(shareLayout)/webapp-signin/layout.tsx b/web/app/(shareLayout)/webapp-signin/layout.tsx index c75f925d40..dd4510a541 100644 --- a/web/app/(shareLayout)/webapp-signin/layout.tsx +++ b/web/app/(shareLayout)/webapp-signin/layout.tsx @@ -1,28 +1,36 @@ 'use client' -import { cn } from '@/utils/classnames' -import { useGlobalPublicStore } from '@/context/global-public-context' -import useDocumentTitle from '@/hooks/use-document-title' import type { PropsWithChildren } from 'react' import { useTranslation } from 'react-i18next' +import { useGlobalPublicStore } from '@/context/global-public-context' +import useDocumentTitle from '@/hooks/use-document-title' +import { cn } from '@/utils/classnames' export default function SignInLayout({ children }: PropsWithChildren) { const { t } = useTranslation() const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) useDocumentTitle(t('login.webapp.login')) - return <> -
-
- {/*
*/} -
-
- {children} + return ( + <> +
+
+ {/*
*/} +
+
+ {children} +
+ {systemFeatures.branding.enabled === false && ( +
+ © + {' '} + {new Date().getFullYear()} + {' '} + LangGenius, Inc. All rights reserved. +
+ )}
- {systemFeatures.branding.enabled === false &&
- © {new Date().getFullYear()} LangGenius, Inc. All rights reserved. -
}
-
- + + ) } diff --git a/web/app/(shareLayout)/webapp-signin/normalForm.tsx b/web/app/(shareLayout)/webapp-signin/normalForm.tsx index a14bfcd737..2aaa267962 100644 --- a/web/app/(shareLayout)/webapp-signin/normalForm.tsx +++ b/web/app/(shareLayout)/webapp-signin/normalForm.tsx @@ -1,16 +1,16 @@ 'use client' +import { RiContractLine, RiDoorLockLine, RiErrorWarningFill } from '@remixicon/react' +import Link from 'next/link' import React, { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import Link from 'next/link' -import { RiContractLine, RiDoorLockLine, RiErrorWarningFill } from '@remixicon/react' import Loading from '@/app/components/base/loading' +import { IS_CE_EDITION } from '@/config' +import { useGlobalPublicStore } from '@/context/global-public-context' +import { LicenseStatus } from '@/types/feature' +import { cn } from '@/utils/classnames' import MailAndCodeAuth from './components/mail-and-code-auth' import MailAndPasswordAuth from './components/mail-and-password-auth' import SSOAuth from './components/sso-auth' -import { cn } from '@/utils/classnames' -import { LicenseStatus } from '@/types/feature' -import { IS_CE_EDITION } from '@/config' -import { useGlobalPublicStore } from '@/context/global-public-context' const NormalForm = () => { const { t } = useTranslation() @@ -37,57 +37,66 @@ const NormalForm = () => { init() }, [init]) if (isLoading) { - return
- -
+ return ( +
+ +
+ ) } if (systemFeatures.license?.status === LicenseStatus.LOST) { - return
-
-
-
- - + return ( +
+
+
+
+ + +
+

{t('login.licenseLost')}

+

{t('login.licenseLostTip')}

-

{t('login.licenseLost')}

-

{t('login.licenseLostTip')}

-
+ ) } if (systemFeatures.license?.status === LicenseStatus.EXPIRED) { - return
-
-
-
- - + return ( +
+
+
+
+ + +
+

{t('login.licenseExpired')}

+

{t('login.licenseExpiredTip')}

-

{t('login.licenseExpired')}

-

{t('login.licenseExpiredTip')}

-
+ ) } if (systemFeatures.license?.status === LicenseStatus.INACTIVE) { - return
-
-
-
- - + return ( +
+
+
+
+ + +
+

{t('login.licenseInactive')}

+

{t('login.licenseInactiveTip')}

-

{t('login.licenseInactive')}

-

{t('login.licenseInactiveTip')}

-
+ ) } return ( @@ -95,78 +104,106 @@ const NormalForm = () => {

{systemFeatures.branding.enabled ? t('login.pageTitleForE') : t('login.pageTitle')}

-

{t('login.welcome')}

+

{t('login.welcome')}

- {systemFeatures.sso_enforced_for_signin &&
- -
} + {systemFeatures.sso_enforced_for_signin && ( +
+ +
+ )}
- {showORLine &&
- -
- {t('login.or')} -
-
} - { - (systemFeatures.enable_email_code_login || systemFeatures.enable_email_password_login) && <> - {systemFeatures.enable_email_code_login && authType === 'code' && <> - - {systemFeatures.enable_email_password_login &&
{ updateAuthType('password') }}> - {t('login.usePassword')} -
} - } - {systemFeatures.enable_email_password_login && authType === 'password' && <> - - {systemFeatures.enable_email_code_login &&
{ updateAuthType('code') }}> - {t('login.useVerificationCode')} -
} - } - - } - {allMethodsAreDisabled && <> -
-
- -
-

{t('login.noLoginMethod')}

-

{t('login.noLoginMethodTip')}

-
-
+ {showORLine && ( +
+
+ {t('login.or')}
- } - {!systemFeatures.branding.enabled && <> -
- {t('login.tosDesc')} + )} + { + (systemFeatures.enable_email_code_login || systemFeatures.enable_email_password_login) && ( + <> + {systemFeatures.enable_email_code_login && authType === 'code' && ( + <> + + {systemFeatures.enable_email_password_login && ( +
{ updateAuthType('password') }}> + {t('login.usePassword')} +
+ )} + + )} + {systemFeatures.enable_email_password_login && authType === 'password' && ( + <> + + {systemFeatures.enable_email_code_login && ( +
{ updateAuthType('code') }}> + {t('login.useVerificationCode')} +
+ )} + + )} + + ) + } + {allMethodsAreDisabled && ( + <> +
+
+ +
+

{t('login.noLoginMethod')}

+

{t('login.noLoginMethodTip')}

+
+
+ +
+ + )} + {!systemFeatures.branding.enabled && ( + <> +
+ {t('login.tosDesc')}   - {t('login.tos')} + + {t('login.tos')} +  &  - {t('login.pp')} -
- {IS_CE_EDITION &&
- {t('login.goToInit')} + + {t('login.pp')} + +
+ {IS_CE_EDITION && ( +
+ {t('login.goToInit')}   - {t('login.setAdminAccount')} -
} - } + + {t('login.setAdminAccount')} + +
+ )} + + )}
diff --git a/web/app/(shareLayout)/webapp-signin/page.tsx b/web/app/(shareLayout)/webapp-signin/page.tsx index 2ffa19c0c9..ca380c9398 100644 --- a/web/app/(shareLayout)/webapp-signin/page.tsx +++ b/web/app/(shareLayout)/webapp-signin/page.tsx @@ -1,15 +1,15 @@ 'use client' -import { useRouter, useSearchParams } from 'next/navigation' import type { FC } from 'react' +import { useRouter, useSearchParams } from 'next/navigation' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { useGlobalPublicStore } from '@/context/global-public-context' import AppUnavailable from '@/app/components/base/app-unavailable' -import NormalForm from './normalForm' -import { AccessMode } from '@/models/access-control' -import ExternalMemberSsoAuth from './components/external-member-sso-auth' +import { useGlobalPublicStore } from '@/context/global-public-context' import { useWebAppStore } from '@/context/web-app-context' +import { AccessMode } from '@/models/access-control' import { webAppLogout } from '@/service/webapp-auth' +import ExternalMemberSsoAuth from './components/external-member-sso-auth' +import NormalForm from './normalForm' const WebSSOForm: FC = () => { const { t } = useTranslation() @@ -34,29 +34,37 @@ const WebSSOForm: FC = () => { }, [getSigninUrl, router, webAppLogout, shareCode]) if (!redirectUrl) { - return
- -
+ return ( +
+ +
+ ) } if (!systemFeatures.webapp_auth.enabled) { - return
-

{t('login.webapp.disabled')}

-
+ return ( +
+

{t('login.webapp.disabled')}

+
+ ) } if (webAppAccessMode && (webAppAccessMode === AccessMode.ORGANIZATION || webAppAccessMode === AccessMode.SPECIFIC_GROUPS_MEMBERS)) { - return
- -
+ return ( +
+ +
+ ) } if (webAppAccessMode && webAppAccessMode === AccessMode.EXTERNAL_MEMBERS) return - return
- - {t('share.login.backToHome')} -
+ return ( +
+ + {t('share.login.backToHome')} +
+ ) } export default React.memo(WebSSOForm) diff --git a/web/app/account/(commonLayout)/account-page/AvatarWithEdit.tsx b/web/app/account/(commonLayout)/account-page/AvatarWithEdit.tsx index f3dbc9421c..dd254435bb 100644 --- a/web/app/account/(commonLayout)/account-page/AvatarWithEdit.tsx +++ b/web/app/account/(commonLayout)/account-page/AvatarWithEdit.tsx @@ -1,23 +1,25 @@ 'use client' 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/avatar' +import type { ImageFile } from '@/types/app' +import { RiDeleteBin5Line, RiPencilLine } from '@remixicon/react' import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' -import { RiDeleteBin5Line, RiPencilLine } from '@remixicon/react' -import { updateUserProfile } from '@/service/common' -import { ToastContext } from '@/app/components/base/toast' -import ImageInput, { type OnImageInput } from '@/app/components/base/app-icon-picker/ImageInput' -import Modal from '@/app/components/base/modal' -import Divider from '@/app/components/base/divider' -import Button from '@/app/components/base/button' -import Avatar, { type AvatarProps } from '@/app/components/base/avatar' -import { useLocalFileUploader } from '@/app/components/base/image-uploader/hooks' -import type { ImageFile } from '@/types/app' +import ImageInput from '@/app/components/base/app-icon-picker/ImageInput' import getCroppedImg from '@/app/components/base/app-icon-picker/utils' +import Avatar from '@/app/components/base/avatar' +import Button from '@/app/components/base/button' +import Divider from '@/app/components/base/divider' +import { useLocalFileUploader } from '@/app/components/base/image-uploader/hooks' +import Modal from '@/app/components/base/modal' +import { ToastContext } from '@/app/components/base/toast' import { DISABLE_UPLOAD_IMAGE_AS_ICON } from '@/config' +import { updateUserProfile } from '@/service/common' -type InputImageInfo = { file: File } | { tempUrl: string; croppedAreaPixels: Area; fileName: string } +type InputImageInfo = { file: File } | { tempUrl: string, croppedAreaPixels: Area, fileName: string } type AvatarWithEditProps = AvatarProps & { onSave?: () => void } const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => { @@ -116,15 +118,17 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => { setHoverArea(isRight ? 'right' : 'left') }} > - {hoverArea === 'right' && !onAvatarError ? ( - - - - ) : ( - - - - )} + {hoverArea === 'right' && !onAvatarError + ? ( + + + + ) + : ( + + + + )}
@@ -135,15 +139,15 @@ const AvatarWithEdit = ({ onSave, ...props }: AvatarWithEditProps) => { isShow={isShowAvatarPicker} onClose={() => setIsShowAvatarPicker(false)} > - - + + -
- -
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 d04cd18557..83e667a1f3 100644 --- a/web/app/account/(commonLayout)/account-page/email-change-modal.tsx +++ b/web/app/account/(commonLayout)/account-page/email-change-modal.tsx @@ -1,22 +1,22 @@ +import type { ResponseError } from '@/service/fetch' +import { RiCloseLine } from '@remixicon/react' +import { noop } from 'lodash-es' +import { useRouter } from 'next/navigation' import React, { useState } from 'react' import { Trans, useTranslation } from 'react-i18next' -import { useRouter } from 'next/navigation' import { useContext } from 'use-context-selector' -import { ToastContext } from '@/app/components/base/toast' -import { RiCloseLine } from '@remixicon/react' -import Modal from '@/app/components/base/modal' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' +import Modal from '@/app/components/base/modal' +import { ToastContext } from '@/app/components/base/toast' import { checkEmailExisted, resetEmail, sendVerifyCode, verifyEmail, } from '@/service/common' -import { noop } from 'lodash-es' -import { asyncRunSafe } from '@/utils' -import type { ResponseError } from '@/service/fetch' import { useLogout } from '@/service/use-common' +import { asyncRunSafe } from '@/utils' type Props = { show: boolean @@ -116,7 +116,7 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => { } const isValidEmail = (email: string): boolean => { - const rfc5322emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ + const rfc5322emailRegex = /^[\w.!#$%&'*+/=?^`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i return rfc5322emailRegex.test(email) && email.length <= 254 } @@ -201,35 +201,35 @@ const EmailChangeModal = ({ onClose, email, show }: Props) => { -
- +
+
{step === STEP.start && ( <> -
{t('common.account.changeEmail.title')}
-
-
{t('common.account.changeEmail.authTip')}
-
+
{t('common.account.changeEmail.title')}
+
+
{t('common.account.changeEmail.authTip')}
+
}} + components={{ email: }} values={{ email }} />
-
-
+
+
-
+
{t('common.account.changeEmail.resendTip')} {time > 0 && ( {t('common.account.changeEmail.resendCount', { count: time })} )} {!time && ( - {t('common.account.changeEmail.resend')} + {t('common.account.changeEmail.resend')} )}
)} {step === STEP.newEmail && ( <> -
{t('common.account.changeEmail.newEmail')}
-
-
{t('common.account.changeEmail.content3')}
+
{t('common.account.changeEmail.newEmail')}
+
+
{t('common.account.changeEmail.content3')}
-
-
{t('common.account.changeEmail.emailLabel')}
+
+
{t('common.account.changeEmail.emailLabel')}
handleNewEmailValueChange(e.target.value)} destructive={newEmailExited || unAvailableEmail} /> {newEmailExited && ( -
{t('common.account.changeEmail.existingEmail')}
+
{t('common.account.changeEmail.existingEmail')}
)} {unAvailableEmail && ( -
{t('common.account.changeEmail.unAvailableEmail')}
+
{t('common.account.changeEmail.unAvailableEmail')}
)}
-
+
-
+
{t('common.account.changeEmail.resendTip')} {time > 0 && ( {t('common.account.changeEmail.resendCount', { count: time })} )} {!time && ( - {t('common.account.changeEmail.resend')} + {t('common.account.changeEmail.resend')} )}
diff --git a/web/app/account/(commonLayout)/account-page/index.tsx b/web/app/account/(commonLayout)/account-page/index.tsx index 15a03b428a..baa9759ffe 100644 --- a/web/app/account/(commonLayout)/account-page/index.tsx +++ b/web/app/account/(commonLayout)/account-page/index.tsx @@ -1,30 +1,29 @@ 'use client' -import { useState } from 'react' -import { useTranslation } from 'react-i18next' +import type { IItem } from '@/app/components/header/account-setting/collapse' +import type { App } from '@/types/app' import { RiGraduationCapFill, } from '@remixicon/react' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' -import DeleteAccount from '../delete-account' -import AvatarWithEdit from './AvatarWithEdit' -import Collapse from '@/app/components/header/account-setting/collapse' -import type { IItem } from '@/app/components/header/account-setting/collapse' -import Modal from '@/app/components/base/modal' -import Button from '@/app/components/base/button' -import { updateUserProfile } from '@/service/common' -import { useAppContext } from '@/context/app-context' -import { useProviderContext } from '@/context/provider-context' -import { ToastContext } from '@/app/components/base/toast' import AppIcon from '@/app/components/base/app-icon' -import { IS_CE_EDITION } from '@/config' +import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' +import Modal from '@/app/components/base/modal' import PremiumBadge from '@/app/components/base/premium-badge' +import { ToastContext } from '@/app/components/base/toast' +import Collapse from '@/app/components/header/account-setting/collapse' +import { IS_CE_EDITION, validPassword } from '@/config' +import { useAppContext } from '@/context/app-context' import { useGlobalPublicStore } from '@/context/global-public-context' -import EmailChangeModal from './email-change-modal' -import { validPassword } from '@/config' - -import type { App } from '@/types/app' +import { useProviderContext } from '@/context/provider-context' +import { updateUserProfile } from '@/service/common' import { useAppList } from '@/service/use-apps' +import DeleteAccount from '../delete-account' + +import AvatarWithEdit from './AvatarWithEdit' +import EmailChangeModal from './email-change-modal' const titleClassName = ` system-sm-semibold text-text-secondary @@ -129,60 +128,60 @@ export default function AccountPage() { const renderAppItem = (item: IItem) => { const { icon, icon_background, icon_type, icon_url } = item as any return ( -
-
+
+
-
{item.name}
+
{item.name}
) } return ( <> -
-

{t('common.account.myAccount')}

+
+

{t('common.account.myAccount')}

-
+
-
-

+

+

{userProfile.name} {isEducationAccount && ( - - - EDU + + + EDU )}

-

{userProfile.email}

+

{userProfile.email}

-
+
{t('common.account.name')}
-
-
- {userProfile.name} +
+
+ {userProfile.name}
-
+
{t('common.operation.edit')}
-
+
{t('common.account.email')}
-
-
- {userProfile.email} +
+
+ {userProfile.email}
{systemFeatures.enable_change_email && ( -
setShowUpdateEmail(true)}> +
setShowUpdateEmail(true)}> {t('common.operation.change')}
)} @@ -190,17 +189,17 @@ export default function AccountPage() {
{ systemFeatures.enable_email_password_login && ( -
+
-
{t('common.account.password')}
-
{t('common.account.passwordTip')}
+
{t('common.account.password')}
+
{t('common.account.passwordTip')}
) } -
-
+
+
{t('common.account.langGeniusAccount')}
{t('common.account.langGeniusAccountTip')}
{!!apps.length && ( @@ -208,29 +207,30 @@ export default function AccountPage() { title={`${t('common.account.showAppLength', { length: apps.length })}`} items={apps.map((app: App) => ({ ...app, key: app.id, name: app.name }))} renderItem={renderAppItem} - wrapperClassName='mt-2' + wrapperClassName="mt-2" /> )} - {!IS_CE_EDITION && } + {!IS_CE_EDITION && }
{ editNameModalVisible && ( setEditNameModalVisible(false)} - className='!w-[420px] !p-6' + className="!w-[420px] !p-6" > -
{t('common.account.editName')}
+
{t('common.account.editName')}
{t('common.account.name')}
- setEditName(e.target.value)} /> -
- +
+
)} -
+
{userProfile.is_password_set ? t('common.account.newPassword') : t('common.account.password')}
-
+
-
{t('common.account.confirmPassword')}
-
+
{t('common.account.confirmPassword')}
+
-
- +
+ - -
- + return ( + <> +
+ {t('common.account.deleteTip')} +
+
+ {t('common.account.deletePrivacyLinkTip')} + {t('common.account.deletePrivacyLink')} +
+ + { + setUserInputEmail(e.target.value) + }} + /> +
+ + +
+ + ) } 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 64a378d2fe..4a1b41cb20 100644 --- a/web/app/account/(commonLayout)/delete-account/components/feed-back.tsx +++ b/web/app/account/(commonLayout)/delete-account/components/feed-back.tsx @@ -1,14 +1,14 @@ 'use client' -import { useTranslation } from 'react-i18next' -import { useCallback, useState } from 'react' import { useRouter } from 'next/navigation' -import { useDeleteAccountFeedback } from '../state' -import { useAppContext } from '@/context/app-context' +import { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import CustomDialog from '@/app/components/base/dialog' import Textarea from '@/app/components/base/textarea' import Toast from '@/app/components/base/toast' +import { useAppContext } from '@/context/app-context' import { useLogout } from '@/service/use-common' +import { useDeleteAccountFeedback } from '../state' type DeleteAccountProps = { onCancel: () => void @@ -46,20 +46,27 @@ export default function FeedBack(props: DeleteAccountProps) { props.onCancel() handleSuccess() }, [handleSuccess, props]) - return - - + > +
{latestParams.length > 0 && (
-
-
{t('tools.mcp.server.modal.parameters')}
- +
+
{t('tools.mcp.server.modal.parameters')}
+
-
{t('tools.mcp.server.modal.parametersTip')}
-
+
{t('tools.mcp.server.modal.parametersTip')}
+
{latestParams.map(paramItem => ( )}
-
- +
+
diff --git a/web/app/components/tools/mcp/mcp-server-param-item.tsx b/web/app/components/tools/mcp/mcp-server-param-item.tsx index a48d1b92b0..3d99cc5bad 100644 --- a/web/app/components/tools/mcp/mcp-server-param-item.tsx +++ b/web/app/components/tools/mcp/mcp-server-param-item.tsx @@ -17,19 +17,20 @@ const MCPServerParamItem = ({ const { t } = useTranslation() return ( -
-
-
{data.label}
-
·
-
{data.variable}
-
{data.type}
+
+
+
{data.label}
+
·
+
{data.variable}
+
{data.type}
+ > +
) } diff --git a/web/app/components/tools/mcp/mcp-service-card.tsx b/web/app/components/tools/mcp/mcp-service-card.tsx index 006ef44ad3..521e93222b 100644 --- a/web/app/components/tools/mcp/mcp-service-card.tsx +++ b/web/app/components/tools/mcp/mcp-service-card.tsx @@ -1,32 +1,33 @@ 'use client' +import type { AppDetailResponse } from '@/models/app' +import type { AppSSO } from '@/types/app' +import { RiEditLine, RiLoopLeftLine } from '@remixicon/react' import React, { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { RiEditLine, RiLoopLeftLine } from '@remixicon/react' +import Button from '@/app/components/base/button' +import Confirm from '@/app/components/base/confirm' +import CopyFeedback from '@/app/components/base/copy-feedback' +import Divider from '@/app/components/base/divider' import { Mcp, } from '@/app/components/base/icons/src/vender/other' -import Button from '@/app/components/base/button' -import Tooltip from '@/app/components/base/tooltip' import Switch from '@/app/components/base/switch' -import Divider from '@/app/components/base/divider' -import CopyFeedback from '@/app/components/base/copy-feedback' -import Confirm from '@/app/components/base/confirm' -import type { AppDetailResponse } from '@/models/app' -import { useAppContext } from '@/context/app-context' -import { AppModeEnum, type AppSSO } from '@/types/app' +import Tooltip from '@/app/components/base/tooltip' import Indicator from '@/app/components/header/indicator' import MCPServerModal from '@/app/components/tools/mcp/mcp-server-modal' -import { useAppWorkflow } from '@/service/use-workflow' +import { BlockEnum } from '@/app/components/workflow/types' +import { useAppContext } from '@/context/app-context' +import { useDocLink } from '@/context/i18n' +import { fetchAppDetail } from '@/service/apps' import { useInvalidateMCPServerDetail, useMCPServerDetail, useRefreshMCPServerCode, useUpdateMCPServer, } from '@/service/use-tools' -import { BlockEnum } from '@/app/components/workflow/types' +import { useAppWorkflow } from '@/service/use-workflow' +import { AppModeEnum } from '@/types/app' import { cn } from '@/utils/classnames' -import { fetchAppDetail } from '@/service/apps' -import { useDocLink } from '@/context/i18n' export type IAppCardProps = { appInfo: AppDetailResponse & Partial @@ -54,7 +55,7 @@ function MCPServiceCard({ const { data: currentWorkflow } = useAppWorkflow(isAdvancedApp ? appId : '') const [basicAppConfig, setBasicAppConfig] = useState({}) const basicAppInputForm = useMemo(() => { - if(!isBasicApp || !basicAppConfig?.user_input_form) + if (!isBasicApp || !basicAppConfig?.user_input_form) return [] return basicAppConfig.user_input_form.map((item: any) => { const type = Object.keys(item)[0] @@ -65,7 +66,7 @@ function MCPServiceCard({ }) }, [basicAppConfig.user_input_form, isBasicApp]) useEffect(() => { - if(isBasicApp && appId) { + if (isBasicApp && appId) { (async () => { const res = await fetchAppDetail({ url: '/apps', id: appId }) setBasicAppConfig(res?.model_config || {}) @@ -89,7 +90,7 @@ function MCPServiceCard({ const [activated, setActivated] = useState(serverActivated) const latestParams = useMemo(() => { - if(isAdvancedApp) { + if (isAdvancedApp) { if (!currentWorkflow?.graph) return [] const startNode = currentWorkflow?.graph.nodes.find(node => node.data.type === BlockEnum.Start) as any @@ -150,21 +151,23 @@ function MCPServiceCard({
{triggerModeDisabled && ( - triggerModeMessage ? ( - - - - ) : + triggerModeMessage + ? ( + + + + ) + : )}
-
-
-
- +
+
+
+
@@ -172,7 +175,7 @@ function MCPServiceCard({
-
+
{serverActivated @@ -182,23 +185,29 @@ function MCPServiceCard({
-
- {t('appOverview.overview.appInfo.enableTooltip.description')} -
-
window.open(docLink('/guides/workflow/node/user-input'), '_blank')} - > - {t('appOverview.overview.appInfo.enableTooltip.learnMore')} -
- - ) : triggerModeMessage || '' - ) : '' + toggleDisabled + ? ( + appUnpublished + ? ( + t('tools.mcp.server.publishTip') + ) + : missingStartNode + ? ( + <> +
+ {t('appOverview.overview.appInfo.enableTooltip.description')} +
+
window.open(docLink('/guides/workflow/node/user-input'), '_blank')} + > + {t('appOverview.overview.appInfo.enableTooltip.learnMore')} +
+ + ) + : triggerModeMessage || '' + ) + : '' } position="right" popupClassName="w-58 max-w-60 rounded-xl bg-components-panel-bg px-3.5 py-3 shadow-lg" @@ -210,7 +219,7 @@ function MCPServiceCard({
{!isMinimalState && ( -
+
{t('tools.mcp.server.url')}
@@ -224,7 +233,7 @@ function MCPServiceCard({ <> {isCurrentWorkspaceManager && ( @@ -235,7 +244,7 @@ function MCPServiceCard({ className="cursor-pointer rounded-md p-1 hover:bg-state-base-hover" onClick={() => setShowConfirmDelete(true)} > - +
)} @@ -246,11 +255,11 @@ function MCPServiceCard({ )}
{!isMinimalState && ( -
+
+
+
- {showAppIconPicker && { - setAppIcon(payload) - setShowAppIconPicker(false) - }} - onClose={() => { - setAppIcon(getIcon(data)) - setShowAppIconPicker(false) - }} - />} + {showAppIconPicker && ( + { + setAppIcon(payload) + setShowAppIconPicker(false) + }} + onClose={() => { + setAppIcon(getIcon(data)) + setShowAppIconPicker(false) + }} + /> + )} ) diff --git a/web/app/components/tools/mcp/provider-card.tsx b/web/app/components/tools/mcp/provider-card.tsx index 831a1122ed..a7b092e0c0 100644 --- a/web/app/components/tools/mcp/provider-card.tsx +++ b/web/app/components/tools/mcp/provider-card.tsx @@ -1,18 +1,18 @@ 'use client' -import { useCallback, useState } from 'react' -import { useBoolean } from 'ahooks' -import { useTranslation } from 'react-i18next' -import { useAppContext } from '@/context/app-context' +import type { ToolWithProvider } from '../../workflow/types' import { RiHammerFill } from '@remixicon/react' +import { useBoolean } from 'ahooks' +import { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Confirm from '@/app/components/base/confirm' import Indicator from '@/app/components/header/indicator' import Icon from '@/app/components/plugins/card/base/card-icon' +import { useAppContext } from '@/context/app-context' import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' -import type { ToolWithProvider } from '../../workflow/types' -import Confirm from '@/app/components/base/confirm' -import MCPModal from './modal' -import OperationDropdown from './detail/operation-dropdown' import { useDeleteMCP, useUpdateMCP } from '@/service/use-tools' import { cn } from '@/utils/classnames' +import OperationDropdown from './detail/operation-dropdown' +import MCPModal from './modal' type Props = { currentProvider?: ToolWithProvider @@ -82,34 +82,34 @@ const MCPCard = ({ currentProvider?.id === data.id && 'border-components-option-card-option-selected-border bg-components-card-bg-alt', )} > -
-
+
+
-
-
{data.name}
-
{data.server_identifier}
+
+
{data.name}
+
{data.server_identifier}
-
-
-
- +
+
+
+ {data.tools.length > 0 && ( -
{t('tools.mcp.toolsCount', { count: data.tools.length })}
+
{t('tools.mcp.toolsCount', { count: data.tools.length })}
)} {!data.tools.length && ( -
{t('tools.mcp.noTools')}
+
{t('tools.mcp.noTools')}
)}
/
{`${t('tools.mcp.updateTime')} ${formatTimeFromNow(data.updated_at! * 1000)}`}
- {data.is_team_authorization && data.tools.length > 0 && } + {data.is_team_authorization && data.tools.length > 0 && } {(!data.is_team_authorization || !data.tools.length) && ( -
+
{t('tools.mcp.noConfigured')} - +
)}
@@ -135,11 +135,11 @@ const MCPCard = ({ {t('tools.mcp.deleteConfirmTitle', { mcp: data.name })}
- } + )} onCancel={hideDeleteConfirm} onConfirm={handleDelete} isLoading={deleting} diff --git a/web/app/components/tools/provider-list.tsx b/web/app/components/tools/provider-list.tsx index 567cc94450..648ecb9802 100644 --- a/web/app/components/tools/provider-list.tsx +++ b/web/app/components/tools/provider-list.tsx @@ -1,27 +1,27 @@ 'use client' +import type { Collection } from './types' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import type { Collection } from './types' -import Marketplace from './marketplace' -import { cn } from '@/utils/classnames' -import { useTabSearchParams } from '@/hooks/use-tab-searchparams' -import TabSliderNew from '@/app/components/base/tab-slider-new' -import LabelFilter from '@/app/components/tools/labels/filter' import Input from '@/app/components/base/input' -import ProviderDetail from '@/app/components/tools/provider/detail' -import Empty from '@/app/components/plugins/marketplace/empty' -import CustomCreateCard from '@/app/components/tools/provider/custom-create-card' -import WorkflowToolEmpty from '@/app/components/tools/provider/empty' +import TabSliderNew from '@/app/components/base/tab-slider-new' import Card from '@/app/components/plugins/card' import CardMoreInfo from '@/app/components/plugins/card/card-more-info' -import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel' -import MCPList from './mcp' -import { useAllToolProviders } from '@/service/use-tools' -import { useCheckInstalled, useInvalidateInstalledPluginList } from '@/service/use-plugins' -import { useGlobalPublicStore } from '@/context/global-public-context' -import { ToolTypeEnum } from '../workflow/block-selector/types' -import { useMarketplace } from './marketplace/hooks' import { useTags } from '@/app/components/plugins/hooks' +import Empty from '@/app/components/plugins/marketplace/empty' +import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel' +import LabelFilter from '@/app/components/tools/labels/filter' +import CustomCreateCard from '@/app/components/tools/provider/custom-create-card' +import ProviderDetail from '@/app/components/tools/provider/detail' +import WorkflowToolEmpty from '@/app/components/tools/provider/empty' +import { useGlobalPublicStore } from '@/context/global-public-context' +import { useTabSearchParams } from '@/hooks/use-tab-searchparams' +import { useCheckInstalled, useInvalidateInstalledPluginList } from '@/service/use-plugins' +import { useAllToolProviders } from '@/service/use-tools' +import { cn } from '@/utils/classnames' +import { ToolTypeEnum } from '../workflow/block-selector/types' +import Marketplace from './marketplace' +import { useMarketplace } from './marketplace/hooks' +import MCPList from './mcp' const getToolType = (type: string) => { switch (type) { @@ -125,15 +125,16 @@ const ProviderList = () => { return ( <> -
+
+ )} + > { @@ -143,14 +144,14 @@ const ProviderList = () => { }} options={options} /> -
+
{activeTab !== 'mcp' && ( )} handleKeywordsChange(e.target.value)} onClear={() => handleKeywordsChange('')} @@ -161,7 +162,8 @@ const ProviderList = () => {
+ )} + > {activeTab === 'api' && } {filteredCollectionList.map(collection => (
{ org: collection.plugin_id ? collection.plugin_id.split('/')[0] : '', name: collection.plugin_id ? collection.plugin_id.split('/')[1] : collection.name, } as any} - footer={ + footer={( getTagLabel(label)) || []} /> - } + )} />
))} - {!filteredCollectionList.length && activeTab === 'workflow' &&
} + {!filteredCollectionList.length && activeTab === 'workflow' &&
}
)} {!filteredCollectionList.length && activeTab === 'builtin' && ( - + )}
{enable_marketplace && activeTab === 'builtin' && ( diff --git a/web/app/components/tools/provider/custom-create-card.tsx b/web/app/components/tools/provider/custom-create-card.tsx index dbb7026aba..ba0c9e6449 100644 --- a/web/app/components/tools/provider/custom-create-card.tsx +++ b/web/app/components/tools/provider/custom-create-card.tsx @@ -1,20 +1,19 @@ 'use client' -import { useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' +import type { CustomCollectionBackend } from '../types' import { RiAddCircleFill, RiArrowRightUpLine, RiBookOpenLine, } from '@remixicon/react' -import type { CustomCollectionBackend } from '../types' -import I18n from '@/context/i18n' -import { getLanguage } from '@/i18n-config/language' -import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal' -import { createCustomCollection } from '@/service/tools' +import { useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { useContext } from 'use-context-selector' import Toast from '@/app/components/base/toast' +import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal' import { useAppContext } from '@/context/app-context' -import { useDocLink } from '@/context/i18n' +import I18n, { useDocLink } from '@/context/i18n' +import { getLanguage } from '@/i18n-config/language' +import { createCustomCollection } from '@/service/tools' type Props = { onRefreshData: () => void @@ -47,20 +46,20 @@ const Contribute = ({ onRefreshData }: Props) => { return ( <> {isCurrentWorkspaceManager && ( -
-
setIsShowEditCustomCollectionModal(true)}> -
-
- +
+
setIsShowEditCustomCollectionModal(true)}> +
+
+
-
{t('tools.createCustomTool')}
+
{t('tools.createCustomTool')}
- diff --git a/web/app/components/tools/provider/detail.tsx b/web/app/components/tools/provider/detail.tsx index d310dde41b..e881dd3997 100644 --- a/web/app/components/tools/provider/detail.tsx +++ b/web/app/components/tools/provider/detail.tsx @@ -1,32 +1,33 @@ 'use client' -import React, { useCallback, useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' +import type { Collection, CustomCollectionBackend, Tool, WorkflowToolProviderRequest, WorkflowToolProviderResponse } from '../types' import { RiCloseLine, } from '@remixicon/react' -import { AuthHeaderPrefix, AuthType, CollectionType } from '../types' -import { basePath } from '@/utils/var' -import type { Collection, CustomCollectionBackend, Tool, WorkflowToolProviderRequest, WorkflowToolProviderResponse } from '../types' -import ToolItem from './tool-item' -import { cn } from '@/utils/classnames' -import I18n from '@/context/i18n' -import { getLanguage } from '@/i18n-config/language' -import Confirm from '@/app/components/base/confirm' -import Button from '@/app/components/base/button' -import Indicator from '@/app/components/header/indicator' -import { LinkExternal02, Settings01 } from '@/app/components/base/icons/src/vender/line/general' -import Icon from '@/app/components/plugins/card/base/card-icon' -import Title from '@/app/components/plugins/card/base/title' -import OrgInfo from '@/app/components/plugins/card/base/org-info' -import Description from '@/app/components/plugins/card/base/description' -import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials' -import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal' -import WorkflowToolModal from '@/app/components/tools/workflow-tool' -import Toast from '@/app/components/base/toast' -import Drawer from '@/app/components/base/drawer' +import React, { useCallback, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { useContext } from 'use-context-selector' import ActionButton from '@/app/components/base/action-button' +import Button from '@/app/components/base/button' +import Confirm from '@/app/components/base/confirm' +import Drawer from '@/app/components/base/drawer' +import { LinkExternal02, Settings01 } from '@/app/components/base/icons/src/vender/line/general' +import Loading from '@/app/components/base/loading' +import Toast from '@/app/components/base/toast' +import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import Indicator from '@/app/components/header/indicator' +import Icon from '@/app/components/plugins/card/base/card-icon' +import Description from '@/app/components/plugins/card/base/description' +import OrgInfo from '@/app/components/plugins/card/base/org-info' +import Title from '@/app/components/plugins/card/base/title' +import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal' +import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials' +import WorkflowToolModal from '@/app/components/tools/workflow-tool' +import { useAppContext } from '@/context/app-context' +import I18n from '@/context/i18n' +import { useModalContext } from '@/context/modal-context' +import { useProviderContext } from '@/context/provider-context' +import { getLanguage } from '@/i18n-config/language' import { deleteWorkflowTool, fetchBuiltInToolList, @@ -40,12 +41,11 @@ import { updateBuiltInToolCredential, updateCustomCollection, } from '@/service/tools' -import { useModalContext } from '@/context/modal-context' -import { useProviderContext } from '@/context/provider-context' -import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import Loading from '@/app/components/base/loading' -import { useAppContext } from '@/context/app-context' import { useInvalidateAllWorkflowTools } from '@/service/use-tools' +import { cn } from '@/utils/classnames' +import { basePath } from '@/utils/var' +import { AuthHeaderPrefix, AuthType, CollectionType } from '../types' +import ToolItem from './tool-item' type Props = { collection: Collection @@ -236,25 +236,25 @@ const ProviderDetail = ({ positionCenter={false} panelClassName={cn('mb-2 mr-2 mt-[64px] !w-[420px] !max-w-[420px] justify-start rounded-2xl border-[0.5px] border-components-panel-border !bg-components-panel-bg !p-0 shadow-xl')} > -
+
-
+
</div> - <div className='mb-1 mt-0.5 flex h-4 items-center justify-between'> + <div className="mb-1 mt-0.5 flex h-4 items-center justify-between"> <OrgInfo - packageNameClassName='w-auto' + packageNameClassName="w-auto" orgName={collection.author} packageName={collection.name} /> </div> </div> - <div className='flex gap-1'> + <div className="flex gap-1"> <ActionButton onClick={onHide}> - <RiCloseLine className='h-4 w-4' /> + <RiCloseLine className="h-4 w-4" /> </ActionButton> </div> </div> @@ -262,25 +262,25 @@ const ProviderDetail = ({ {!!collection.description[language] && ( <Description text={collection.description[language]} descriptionLineRows={2}></Description> )} - <div className='flex gap-1 border-b-[0.5px] border-divider-subtle'> + <div className="flex gap-1 border-b-[0.5px] border-divider-subtle"> {collection.type === CollectionType.custom && !isDetailLoading && ( <Button className={cn('my-3 w-full shrink-0')} onClick={() => setIsShowEditCustomCollectionModal(true)} > - <Settings01 className='mr-1 h-4 w-4 text-text-tertiary' /> - <div className='system-sm-medium text-text-secondary'>{t('tools.createTool.editAction')}</div> + <Settings01 className="mr-1 h-4 w-4 text-text-tertiary" /> + <div className="system-sm-medium text-text-secondary">{t('tools.createTool.editAction')}</div> </Button> )} {collection.type === CollectionType.workflow && !isDetailLoading && customCollection && ( <> <Button - variant='primary' + variant="primary" className={cn('my-3 w-[183px] shrink-0')} > - <a className='flex items-center' href={`${basePath}/app/${(customCollection as WorkflowToolProviderResponse).workflow_app_id}/workflow`} rel='noreferrer' target='_blank'> - <div className='system-sm-medium'>{t('tools.openInStudio')}</div> - <LinkExternal02 className='ml-1 h-4 w-4' /> + <a className="flex items-center" href={`${basePath}/app/${(customCollection as WorkflowToolProviderResponse).workflow_app_id}/workflow`} rel="noreferrer" target="_blank"> + <div className="system-sm-medium">{t('tools.openInStudio')}</div> + <LinkExternal02 className="ml-1 h-4 w-4" /> </a> </Button> <Button @@ -288,30 +288,30 @@ const ProviderDetail = ({ onClick={() => setIsShowEditWorkflowToolModal(true)} disabled={!isCurrentWorkspaceManager} > - <div className='system-sm-medium text-text-secondary'>{t('tools.createTool.editAction')}</div> + <div className="system-sm-medium text-text-secondary">{t('tools.createTool.editAction')}</div> </Button> </> )} </div> - <div className='flex min-h-0 flex-1 flex-col pt-3'> - {isDetailLoading && <div className='flex h-[200px]'><Loading type='app' /></div>} + <div className="flex min-h-0 flex-1 flex-col pt-3"> + {isDetailLoading && <div className="flex h-[200px]"><Loading type="app" /></div>} {!isDetailLoading && ( <> <div className="shrink-0"> {(collection.type === CollectionType.builtIn || collection.type === CollectionType.model) && isAuthed && ( - <div className='system-sm-semibold-uppercase mb-1 flex h-6 items-center justify-between text-text-secondary'> + <div className="system-sm-semibold-uppercase mb-1 flex h-6 items-center justify-between text-text-secondary"> {t('plugin.detailPanel.actionNum', { num: toolList.length, action: toolList.length > 1 ? 'actions' : 'action' })} {needAuth && ( <Button - variant='secondary' - size='small' + variant="secondary" + size="small" onClick={() => { if (collection.type === CollectionType.builtIn || collection.type === CollectionType.model) showSettingAuthModal() }} disabled={!isCurrentWorkspaceManager} > - <Indicator className='mr-2' color={'green'} /> + <Indicator className="mr-2" color="green" /> {t('tools.auth.authorized')} </Button> )} @@ -319,13 +319,13 @@ const ProviderDetail = ({ )} {(collection.type === CollectionType.builtIn || collection.type === CollectionType.model) && needAuth && !isAuthed && ( <> - <div className='system-sm-semibold-uppercase text-text-secondary'> - <span className=''>{t('tools.includeToolNum', { num: toolList.length, action: toolList.length > 1 ? 'actions' : 'action' }).toLocaleUpperCase()}</span> - <span className='px-1'>·</span> - <span className='text-util-colors-orange-orange-600'>{t('tools.auth.setup').toLocaleUpperCase()}</span> + <div className="system-sm-semibold-uppercase text-text-secondary"> + <span className="">{t('tools.includeToolNum', { num: toolList.length, action: toolList.length > 1 ? 'actions' : 'action' }).toLocaleUpperCase()}</span> + <span className="px-1">·</span> + <span className="text-util-colors-orange-orange-600">{t('tools.auth.setup').toLocaleUpperCase()}</span> </div> <Button - variant='primary' + variant="primary" className={cn('my-3 w-full shrink-0')} onClick={() => { if (collection.type === CollectionType.builtIn || collection.type === CollectionType.model) @@ -338,17 +338,17 @@ const ProviderDetail = ({ </> )} {(collection.type === CollectionType.custom) && ( - <div className='system-sm-semibold-uppercase text-text-secondary'> - <span className=''>{t('tools.includeToolNum', { num: toolList.length, action: toolList.length > 1 ? 'actions' : 'action' }).toLocaleUpperCase()}</span> + <div className="system-sm-semibold-uppercase text-text-secondary"> + <span className="">{t('tools.includeToolNum', { num: toolList.length, action: toolList.length > 1 ? 'actions' : 'action' }).toLocaleUpperCase()}</span> </div> )} {(collection.type === CollectionType.workflow) && ( - <div className='system-sm-semibold-uppercase text-text-secondary'> - <span className=''>{t('tools.createTool.toolInput.title').toLocaleUpperCase()}</span> + <div className="system-sm-semibold-uppercase text-text-secondary"> + <span className="">{t('tools.createTool.toolInput.title').toLocaleUpperCase()}</span> </div> )} </div> - <div className='mt-1 flex-1 overflow-y-auto py-2'> + <div className="mt-1 flex-1 overflow-y-auto py-2"> {collection.type !== CollectionType.workflow && toolList.map(tool => ( <ToolItem key={tool.name} @@ -360,13 +360,13 @@ const ProviderDetail = ({ /> ))} {collection.type === CollectionType.workflow && (customCollection as WorkflowToolProviderResponse)?.tool?.parameters.map(item => ( - <div key={item.name} className='mb-1 py-1'> - <div className='mb-1 flex items-center gap-2'> - <span className='code-sm-semibold text-text-secondary'>{item.name}</span> - <span className='system-xs-regular text-text-tertiary'>{item.type}</span> - <span className='system-xs-medium text-text-warning-secondary'>{item.required ? t('tools.createTool.toolInput.required') : ''}</span> + <div key={item.name} className="mb-1 py-1"> + <div className="mb-1 flex items-center gap-2"> + <span className="code-sm-semibold text-text-secondary">{item.name}</span> + <span className="system-xs-regular text-text-tertiary">{item.type}</span> + <span className="system-xs-medium text-text-warning-secondary">{item.required ? t('tools.createTool.toolInput.required') : ''}</span> </div> - <div className='system-xs-regular text-text-tertiary'>{item.llm_description}</div> + <div className="system-xs-regular text-text-tertiary">{item.llm_description}</div> </div> ))} </div> diff --git a/web/app/components/tools/provider/empty.tsx b/web/app/components/tools/provider/empty.tsx index bbd0f6fec1..7e916ba62f 100644 --- a/web/app/components/tools/provider/empty.tsx +++ b/web/app/components/tools/provider/empty.tsx @@ -1,11 +1,12 @@ 'use client' -import { useTranslation } from 'react-i18next' -import { ToolTypeEnum } from '../../workflow/block-selector/types' import { RiArrowRightUpLine } from '@remixicon/react' import Link from 'next/link' +import { useTranslation } from 'react-i18next' +import useTheme from '@/hooks/use-theme' import { cn } from '@/utils/classnames' import { NoToolPlaceholder } from '../../base/icons/src/vender/other' -import useTheme from '@/hooks/use-theme' +import { ToolTypeEnum } from '../../workflow/block-selector/types' + type Props = { type?: ToolTypeEnum isAgent?: boolean @@ -35,14 +36,16 @@ const Empty = ({ const hasTitle = t(`tools.addToolModal.${renderType}.title`) !== `tools.addToolModal.${renderType}.title` return ( - <div className='flex flex-col items-center justify-center'> + <div className="flex flex-col items-center justify-center"> <NoToolPlaceholder className={theme === 'dark' ? 'invert' : ''} /> - <div className='mb-1 mt-2 text-[13px] font-medium leading-[18px] text-text-primary'> + <div className="mb-1 mt-2 text-[13px] font-medium leading-[18px] text-text-primary"> {hasTitle ? t(`tools.addToolModal.${renderType}.title`) : 'No tools available'} </div> {(!isAgent && hasTitle) && ( <Comp className={cn('flex items-center text-[13px] leading-[18px] text-text-tertiary', hasLink && 'cursor-pointer hover:text-text-accent')} {...linkProps}> - {t(`tools.addToolModal.${renderType}.tip`)} {hasLink && <RiArrowRightUpLine className='ml-0.5 h-3 w-3' />} + {t(`tools.addToolModal.${renderType}.tip`)} + {' '} + {hasLink && <RiArrowRightUpLine className="ml-0.5 h-3 w-3" />} </Comp> )} </div> diff --git a/web/app/components/tools/provider/tool-item.tsx b/web/app/components/tools/provider/tool-item.tsx index 7edf1c61f1..3248e3c024 100644 --- a/web/app/components/tools/provider/tool-item.tsx +++ b/web/app/components/tools/provider/tool-item.tsx @@ -1,11 +1,11 @@ 'use client' +import type { Collection, Tool } from '../types' import React, { useState } from 'react' import { useContext } from 'use-context-selector' -import type { Collection, Tool } from '../types' -import { cn } from '@/utils/classnames' +import SettingBuiltInTool from '@/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool' import I18n from '@/context/i18n' import { getLanguage } from '@/i18n-config/language' -import SettingBuiltInTool from '@/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool' +import { cn } from '@/utils/classnames' type Props = { disabled?: boolean @@ -32,8 +32,8 @@ const ToolItem = ({ className={cn('bg-components-panel-item-bg cursor-pointer rounded-xl border-[0.5px] border-components-panel-border-subtle px-4 py-3 shadow-xs hover:bg-components-panel-on-panel-item-bg-hover', disabled && '!cursor-not-allowed opacity-50')} onClick={() => !disabled && setShowDetail(true)} > - <div className='system-md-semibold pb-0.5 text-text-secondary'>{tool.label[language]}</div> - <div className='system-xs-regular line-clamp-2 text-text-tertiary' title={tool.description[language]}>{tool.description[language]}</div> + <div className="system-md-semibold pb-0.5 text-text-secondary">{tool.label[language]}</div> + <div className="system-xs-regular line-clamp-2 text-text-tertiary" title={tool.description[language]}>{tool.description[language]}</div> </div> {showDetail && ( <SettingBuiltInTool diff --git a/web/app/components/tools/setting/build-in/config-credentials.tsx b/web/app/components/tools/setting/build-in/config-credentials.tsx index 5effeaa47d..783d4a6476 100644 --- a/web/app/components/tools/setting/build-in/config-credentials.tsx +++ b/web/app/components/tools/setting/build-in/config-credentials.tsx @@ -1,19 +1,19 @@ 'use client' import type { FC } from 'react' +import type { Collection } from '../../types' +import { noop } from 'lodash-es' import React, { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { addDefaultValue, toolCredentialToFormSchemas } from '../../utils/to-form-schema' -import type { Collection } from '../../types' -import { cn } from '@/utils/classnames' -import Drawer from '@/app/components/base/drawer-plus' import Button from '@/app/components/base/button' -import Toast from '@/app/components/base/toast' -import { fetchBuiltInToolCredential, fetchBuiltInToolCredentialSchema } from '@/service/tools' -import Loading from '@/app/components/base/loading' -import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form' +import Drawer from '@/app/components/base/drawer-plus' import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general' +import Loading from '@/app/components/base/loading' +import Toast from '@/app/components/base/toast' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' -import { noop } from 'lodash-es' +import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form' +import { fetchBuiltInToolCredential, fetchBuiltInToolCredentialSchema } from '@/service/tools' +import { cn } from '@/utils/classnames' +import { addDefaultValue, toolCredentialToFormSchemas } from '../../utils/to-form-schema' type Props = { collection: Collection @@ -72,56 +72,57 @@ const ConfigCredential: FC<Props> = ({ onHide={onCancel} title={t('tools.auth.setupModalTitle') as string} titleDescription={t('tools.auth.setupModalTitleDescription') as string} - panelClassName='mt-[64px] mb-2 !w-[420px] border-components-panel-border' - maxWidthClassName='!max-w-[420px]' - height='calc(100vh - 64px)' - contentClassName='!bg-components-panel-bg' - headerClassName='!border-b-divider-subtle' - body={ - - <div className='h-full px-6 py-3'> + panelClassName="mt-[64px] mb-2 !w-[420px] border-components-panel-border" + maxWidthClassName="!max-w-[420px]" + height="calc(100vh - 64px)" + contentClassName="!bg-components-panel-bg" + headerClassName="!border-b-divider-subtle" + body={( + <div className="h-full px-6 py-3"> {!credentialSchema - ? <Loading type='app' /> + ? <Loading type="app" /> : ( - <> - <Form - value={tempCredential} - onChange={(v) => { - setTempCredential(v) - }} - formSchemas={credentialSchema} - isEditMode={true} - showOnVariableMap={{}} - validating={false} - inputClassName='!bg-components-input-bg-normal' - fieldMoreInfo={item => item.url - ? (<a - href={item.url} - target='_blank' rel='noopener noreferrer' - className='inline-flex items-center text-xs text-text-accent' - > - {t('tools.howToGet')} - <LinkExternal02 className='ml-1 h-3 w-3' /> - </a>) - : null} - /> - <div className={cn((collection.is_team_authorization && !isHideRemoveBtn) ? 'justify-between' : 'justify-end', 'mt-2 flex ')} > - { - (collection.is_team_authorization && !isHideRemoveBtn) && ( - <Button onClick={onRemove}>{t('common.operation.remove')}</Button> - ) - } - <div className='flex space-x-2'> - <Button onClick={onCancel}>{t('common.operation.cancel')}</Button> - <Button loading={isLoading || isSaving} disabled={isLoading || isSaving} variant='primary' onClick={handleSave}>{t('common.operation.save')}</Button> + <> + <Form + value={tempCredential} + onChange={(v) => { + setTempCredential(v) + }} + formSchemas={credentialSchema} + isEditMode={true} + showOnVariableMap={{}} + validating={false} + inputClassName="!bg-components-input-bg-normal" + fieldMoreInfo={item => item.url + ? ( + <a + href={item.url} + target="_blank" + rel="noopener noreferrer" + className="inline-flex items-center text-xs text-text-accent" + > + {t('tools.howToGet')} + <LinkExternal02 className="ml-1 h-3 w-3" /> + </a> + ) + : null} + /> + <div className={cn((collection.is_team_authorization && !isHideRemoveBtn) ? 'justify-between' : 'justify-end', 'mt-2 flex ')}> + { + (collection.is_team_authorization && !isHideRemoveBtn) && ( + <Button onClick={onRemove}>{t('common.operation.remove')}</Button> + ) + } + <div className="flex space-x-2"> + <Button onClick={onCancel}>{t('common.operation.cancel')}</Button> + <Button loading={isLoading || isSaving} disabled={isLoading || isSaving} variant="primary" onClick={handleSave}>{t('common.operation.save')}</Button> + </div> </div> - </div> - </> - ) - } + </> + )} - </div > - } + </div> + )} isShowMask={true} clickOutsideNotOpen={false} /> diff --git a/web/app/components/tools/utils/to-form-schema.ts b/web/app/components/tools/utils/to-form-schema.ts index 69f5dd5f2f..e3d1f660fd 100644 --- a/web/app/components/tools/utils/to-form-schema.ts +++ b/web/app/components/tools/utils/to-form-schema.ts @@ -1,7 +1,7 @@ -import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' import type { TriggerEventParameter } from '../../plugins/types' import type { ToolCredential, ToolParameter } from '../types' +import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' export const toType = (type: string) => { switch (type) { @@ -76,7 +76,7 @@ export const toolCredentialToFormSchemas = (parameters: ToolCredential[]) => { return formSchemas } -export const addDefaultValue = (value: Record<string, any>, formSchemas: { variable: string; type: string; default?: any }[]) => { +export const addDefaultValue = (value: Record<string, any>, formSchemas: { variable: string, type: string, default?: any }[]) => { const newValues = { ...value } formSchemas.forEach((formSchema) => { const itemValue = value[formSchema.variable] @@ -122,7 +122,7 @@ const correctInitialData = (type: string, target: any, defaultValue: any) => { return target } -export const generateFormValue = (value: Record<string, any>, formSchemas: { variable: string; default?: any; type: string }[], isReasoning = false) => { +export const generateFormValue = (value: Record<string, any>, formSchemas: { variable: string, default?: any, type: string }[], isReasoning = false) => { const newValues = {} as any formSchemas.forEach((formSchema) => { const itemValue = value[formSchema.variable] @@ -162,7 +162,7 @@ export const getStructureValue = (value: Record<string, any>) => { return newValue } -export const getConfiguredValue = (value: Record<string, any>, formSchemas: { variable: string; type: string; default?: any }[]) => { +export const getConfiguredValue = (value: Record<string, any>, formSchemas: { variable: string, type: string, default?: any }[]) => { const newValues = { ...value } formSchemas.forEach((formSchema) => { const itemValue = value[formSchema.variable] @@ -187,7 +187,7 @@ const getVarKindType = (type: FormTypeEnum) => { return VarKindType.mixed } -export const generateAgentToolValue = (value: Record<string, any>, formSchemas: { variable: string; default?: any; type: string }[], isReasoning = false) => { +export const generateAgentToolValue = (value: Record<string, any>, formSchemas: { variable: string, default?: any, type: string }[], isReasoning = false) => { const newValues = {} as any if (!isReasoning) { formSchemas.forEach((formSchema) => { diff --git a/web/app/components/tools/workflow-tool/configure-button.tsx b/web/app/components/tools/workflow-tool/configure-button.tsx index 0feee28abf..0d28576de5 100644 --- a/web/app/components/tools/workflow-tool/configure-button.tsx +++ b/web/app/components/tools/workflow-tool/configure-button.tsx @@ -1,21 +1,21 @@ 'use client' -import React, { useCallback, useEffect, useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { useRouter } from 'next/navigation' -import { RiArrowRightUpLine, RiHammerLine } from '@remixicon/react' -import Divider from '../../base/divider' -import { cn } from '@/utils/classnames' -import Button from '@/app/components/base/button' -import Indicator from '@/app/components/header/indicator' -import WorkflowToolModal from '@/app/components/tools/workflow-tool' -import Loading from '@/app/components/base/loading' -import Toast from '@/app/components/base/toast' -import { createWorkflowToolProvider, fetchWorkflowToolDetailByAppID, saveWorkflowToolProvider } from '@/service/tools' import type { Emoji, WorkflowToolProviderOutputParameter, WorkflowToolProviderParameter, WorkflowToolProviderRequest, WorkflowToolProviderResponse } from '@/app/components/tools/types' import type { InputVar, Variable } from '@/app/components/workflow/types' import type { PublishWorkflowParams } from '@/types/workflow' +import { RiArrowRightUpLine, RiHammerLine } from '@remixicon/react' +import { useRouter } from 'next/navigation' +import React, { useCallback, useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' +import Loading from '@/app/components/base/loading' +import Toast from '@/app/components/base/toast' +import Indicator from '@/app/components/header/indicator' +import WorkflowToolModal from '@/app/components/tools/workflow-tool' import { useAppContext } from '@/context/app-context' +import { createWorkflowToolProvider, fetchWorkflowToolDetailByAppID, saveWorkflowToolProvider } from '@/service/tools' import { useInvalidateAllWorkflowTools } from '@/service/use-tools' +import { cn } from '@/utils/classnames' +import Divider from '../../base/divider' type Props = { disabled: boolean @@ -132,11 +132,11 @@ const WorkflowToolConfigureButton = ({ privacy_policy: detail?.privacy_policy || '', ...(published ? { - workflow_tool_id: detail?.workflow_tool_id, - } + workflow_tool_id: detail?.workflow_tool_id, + } : { - workflow_app_id: workflowAppId, - }), + workflow_app_id: workflowAppId, + }), } }, [detail, published, workflowAppId, icon, name, description, inputs]) @@ -197,75 +197,76 @@ const WorkflowToolConfigureButton = ({ return ( <> - <Divider type='horizontal' className='h-px bg-divider-subtle' /> + <Divider type="horizontal" className="h-px bg-divider-subtle" /> {(!published || !isLoading) && ( <div className={cn( 'group rounded-lg bg-background-section-burn transition-colors', disabled || !isCurrentWorkspaceManager ? 'cursor-not-allowed opacity-60 shadow-xs' : 'cursor-pointer', !disabled && !published && isCurrentWorkspaceManager && 'hover:bg-state-accent-hover', - )}> + )} + > {isCurrentWorkspaceManager ? ( - <div - className='flex items-center justify-start gap-2 p-2 pl-2.5' - onClick={() => !disabled && !published && setShowModal(true)} - > - <RiHammerLine className={cn('relative h-4 w-4 text-text-secondary', !disabled && !published && 'group-hover:text-text-accent')} /> <div - title={t('workflow.common.workflowAsTool') || ''} - className={cn('system-sm-medium shrink grow basis-0 truncate text-text-secondary', !disabled && !published && 'group-hover:text-text-accent')} + className="flex items-center justify-start gap-2 p-2 pl-2.5" + onClick={() => !disabled && !published && setShowModal(true)} > - {t('workflow.common.workflowAsTool')} + <RiHammerLine className={cn('relative h-4 w-4 text-text-secondary', !disabled && !published && 'group-hover:text-text-accent')} /> + <div + title={t('workflow.common.workflowAsTool') || ''} + className={cn('system-sm-medium shrink grow basis-0 truncate text-text-secondary', !disabled && !published && 'group-hover:text-text-accent')} + > + {t('workflow.common.workflowAsTool')} + </div> + {!published && ( + <span className="system-2xs-medium-uppercase shrink-0 rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-1 py-0.5 text-text-tertiary"> + {t('workflow.common.configureRequired')} + </span> + )} </div> - {!published && ( - <span className='system-2xs-medium-uppercase shrink-0 rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-1 py-0.5 text-text-tertiary'> - {t('workflow.common.configureRequired')} - </span> - )} - </div> - ) + ) : ( - <div - className='flex items-center justify-start gap-2 p-2 pl-2.5' - > - <RiHammerLine className='h-4 w-4 text-text-tertiary' /> <div - title={t('workflow.common.workflowAsTool') || ''} - className='system-sm-medium shrink grow basis-0 truncate text-text-tertiary' + className="flex items-center justify-start gap-2 p-2 pl-2.5" > - {t('workflow.common.workflowAsTool')} + <RiHammerLine className="h-4 w-4 text-text-tertiary" /> + <div + title={t('workflow.common.workflowAsTool') || ''} + className="system-sm-medium shrink grow basis-0 truncate text-text-tertiary" + > + {t('workflow.common.workflowAsTool')} + </div> </div> - </div> - )} + )} {disabledReason && ( - <div className='mt-1 px-2.5 pb-2 text-xs leading-[18px] text-text-tertiary'> + <div className="mt-1 px-2.5 pb-2 text-xs leading-[18px] text-text-tertiary"> {disabledReason} </div> )} {published && ( - <div className='border-t-[0.5px] border-divider-regular px-2.5 py-2'> - <div className='flex justify-between gap-x-2'> + <div className="border-t-[0.5px] border-divider-regular px-2.5 py-2"> + <div className="flex justify-between gap-x-2"> <Button - size='small' - className='w-[140px]' + size="small" + className="w-[140px]" onClick={() => setShowModal(true)} disabled={!isCurrentWorkspaceManager || disabled} > {t('workflow.common.configure')} - {outdated && <Indicator className='ml-1' color={'yellow'} />} + {outdated && <Indicator className="ml-1" color="yellow" />} </Button> <Button - size='small' - className='w-[140px]' + size="small" + className="w-[140px]" onClick={() => router.push('/tools?category=workflow')} disabled={disabled} > {t('workflow.common.manageInTools')} - <RiArrowRightUpLine className='ml-1 h-4 w-4' /> + <RiArrowRightUpLine className="ml-1 h-4 w-4" /> </Button> </div> {outdated && ( - <div className='mt-1 text-xs leading-[18px] text-text-warning'> + <div className="mt-1 text-xs leading-[18px] text-text-warning"> {t('workflow.common.workflowAsToolTip')} </div> )} @@ -273,7 +274,7 @@ const WorkflowToolConfigureButton = ({ )} </div> )} - {published && isLoading && <div className='pt-2'><Loading type='app' /></div>} + {published && isLoading && <div className="pt-2"><Loading type="app" /></div>} {showModal && ( <WorkflowToolModal isAdd={!published} diff --git a/web/app/components/tools/workflow-tool/confirm-modal/index.spec.tsx b/web/app/components/tools/workflow-tool/confirm-modal/index.spec.tsx index 064a4d3cda..972ac8c882 100644 --- a/web/app/components/tools/workflow-tool/confirm-modal/index.spec.tsx +++ b/web/app/components/tools/workflow-tool/confirm-modal/index.spec.tsx @@ -1,6 +1,6 @@ -import React from 'react' import { act, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' +import React from 'react' import ConfirmModal from './index' // Test utilities diff --git a/web/app/components/tools/workflow-tool/confirm-modal/index.tsx b/web/app/components/tools/workflow-tool/confirm-modal/index.tsx index e76ad4add4..f90774cb32 100644 --- a/web/app/components/tools/workflow-tool/confirm-modal/index.tsx +++ b/web/app/components/tools/workflow-tool/confirm-modal/index.tsx @@ -1,12 +1,12 @@ 'use client' -import { useTranslation } from 'react-i18next' import { RiCloseLine } from '@remixicon/react' -import { cn } from '@/utils/classnames' -import Button from '@/app/components/base/button' -import Modal from '@/app/components/base/modal' -import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' import { noop } from 'lodash-es' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' +import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' +import Modal from '@/app/components/base/modal' +import { cn } from '@/utils/classnames' type ConfirmModalProps = { show: boolean @@ -23,19 +23,19 @@ const ConfirmModal = ({ show, onConfirm, onClose }: ConfirmModalProps) => { isShow={show} onClose={noop} > - <div className='absolute right-4 top-4 cursor-pointer p-2' onClick={onClose}> - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <div className="absolute right-4 top-4 cursor-pointer p-2" onClick={onClose}> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> - <div className='h-12 w-12 rounded-xl border-[0.5px] border-divider-regular bg-background-section p-3 shadow-xl'> - <AlertTriangle className='h-6 w-6 text-[rgb(247,144,9)]' /> + <div className="h-12 w-12 rounded-xl border-[0.5px] border-divider-regular bg-background-section p-3 shadow-xl"> + <AlertTriangle className="h-6 w-6 text-[rgb(247,144,9)]" /> </div> - <div className='relative mt-3 text-xl font-semibold leading-[30px] text-text-primary'>{t('tools.createTool.confirmTitle')}</div> - <div className='my-1 text-sm leading-5 text-text-tertiary'> + <div className="relative mt-3 text-xl font-semibold leading-[30px] text-text-primary">{t('tools.createTool.confirmTitle')}</div> + <div className="my-1 text-sm leading-5 text-text-tertiary"> {t('tools.createTool.confirmTip')} </div> - <div className='flex items-center justify-end pt-6'> - <div className='flex items-center'> - <Button className='mr-2' onClick={onClose}>{t('common.operation.cancel')}</Button> + <div className="flex items-center justify-end pt-6"> + <div className="flex items-center"> + <Button className="mr-2" onClick={onClose}>{t('common.operation.cancel')}</Button> <Button variant="warning" onClick={onConfirm}>{t('common.operation.confirm')}</Button> </div> </div> diff --git a/web/app/components/tools/workflow-tool/index.tsx b/web/app/components/tools/workflow-tool/index.tsx index 3d70f1f424..af226e7071 100644 --- a/web/app/components/tools/workflow-tool/index.tsx +++ b/web/app/components/tools/workflow-tool/index.tsx @@ -1,24 +1,24 @@ 'use client' import type { FC } from 'react' +import type { Emoji, WorkflowToolProviderOutputParameter, WorkflowToolProviderParameter, WorkflowToolProviderRequest } from '../types' +import { RiErrorWarningLine } from '@remixicon/react' +import { produce } from 'immer' import React, { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { produce } from 'immer' -import type { Emoji, WorkflowToolProviderOutputParameter, WorkflowToolProviderParameter, WorkflowToolProviderRequest } from '../types' -import { buildWorkflowOutputParameters } from './utils' -import { cn } from '@/utils/classnames' +import AppIcon from '@/app/components/base/app-icon' +import Button from '@/app/components/base/button' import Drawer from '@/app/components/base/drawer-plus' +import EmojiPicker from '@/app/components/base/emoji-picker' import Input from '@/app/components/base/input' import Textarea from '@/app/components/base/textarea' -import Button from '@/app/components/base/button' import Toast from '@/app/components/base/toast' -import EmojiPicker from '@/app/components/base/emoji-picker' -import AppIcon from '@/app/components/base/app-icon' -import MethodSelector from '@/app/components/tools/workflow-tool/method-selector' +import Tooltip from '@/app/components/base/tooltip' import LabelSelector from '@/app/components/tools/labels/selector' import ConfirmModal from '@/app/components/tools/workflow-tool/confirm-modal' -import Tooltip from '@/app/components/base/tooltip' +import MethodSelector from '@/app/components/tools/workflow-tool/method-selector' import { VarType } from '@/app/components/workflow/types' -import { RiErrorWarningLine } from '@remixicon/react' +import { cn } from '@/utils/classnames' +import { buildWorkflowOutputParameters } from './utils' type Props = { isAdd?: boolean @@ -152,20 +152,24 @@ const WorkflowToolAsModal: FC<Props> = ({ isShow onHide={onHide} title={t('workflow.common.workflowAsTool')!} - panelClassName='mt-2 !w-[640px]' - maxWidthClassName='!max-w-[640px]' - height='calc(100vh - 16px)' - headerClassName='!border-b-divider' - body={ - <div className='flex h-full flex-col'> - <div className='h-0 grow space-y-4 overflow-y-auto px-6 py-3'> + panelClassName="mt-2 !w-[640px]" + maxWidthClassName="!max-w-[640px]" + height="calc(100vh - 16px)" + headerClassName="!border-b-divider" + body={( + <div className="flex h-full flex-col"> + <div className="h-0 grow space-y-4 overflow-y-auto px-6 py-3"> {/* name & icon */} <div> - <div className='system-sm-medium py-2 text-text-primary'>{t('tools.createTool.name')} <span className='ml-1 text-red-500'>*</span></div> - <div className='flex items-center justify-between gap-3'> - <AppIcon size='large' onClick={() => { setShowEmojiPicker(true) }} className='cursor-pointer' iconType='emoji' icon={emoji.content} background={emoji.background} /> + <div className="system-sm-medium py-2 text-text-primary"> + {t('tools.createTool.name')} + {' '} + <span className="ml-1 text-red-500">*</span> + </div> + <div className="flex items-center justify-between gap-3"> + <AppIcon size="large" onClick={() => { setShowEmojiPicker(true) }} className="cursor-pointer" iconType="emoji" icon={emoji.content} background={emoji.background} /> <Input - className='h-10 grow' + className="h-10 grow" placeholder={t('tools.createTool.toolNamePlaceHolder')!} value={label} onChange={e => setLabel(e.target.value)} @@ -174,29 +178,31 @@ const WorkflowToolAsModal: FC<Props> = ({ </div> {/* name for tool call */} <div> - <div className='system-sm-medium flex items-center py-2 text-text-primary'> - {t('tools.createTool.nameForToolCall')} <span className='ml-1 text-red-500'>*</span> + <div className="system-sm-medium flex items-center py-2 text-text-primary"> + {t('tools.createTool.nameForToolCall')} + {' '} + <span className="ml-1 text-red-500">*</span> <Tooltip - popupContent={ - <div className='w-[180px]'> + popupContent={( + <div className="w-[180px]"> {t('tools.createTool.nameForToolCallPlaceHolder')} </div> - } + )} /> </div> <Input - className='h-10' + className="h-10" placeholder={t('tools.createTool.nameForToolCallPlaceHolder')!} value={name} onChange={e => setName(e.target.value)} /> {!isNameValid(name) && ( - <div className='text-xs leading-[18px] text-red-500'>{t('tools.createTool.nameForToolCallTip')}</div> + <div className="text-xs leading-[18px] text-red-500">{t('tools.createTool.nameForToolCallTip')}</div> )} </div> {/* description */} <div> - <div className='system-sm-medium py-2 text-text-primary'>{t('tools.createTool.description')}</div> + <div className="system-sm-medium py-2 text-text-primary">{t('tools.createTool.description')}</div> <Textarea placeholder={t('tools.createTool.descriptionPlaceholder') || ''} value={description} @@ -205,11 +211,11 @@ const WorkflowToolAsModal: FC<Props> = ({ </div> {/* Tool Input */} <div> - <div className='system-sm-medium py-2 text-text-primary'>{t('tools.createTool.toolInput.title')}</div> - <div className='w-full overflow-x-auto rounded-lg border border-divider-regular'> - <table className='w-full text-xs font-normal leading-[18px] text-text-secondary'> - <thead className='uppercase text-text-tertiary'> - <tr className='border-b border-divider-regular'> + <div className="system-sm-medium py-2 text-text-primary">{t('tools.createTool.toolInput.title')}</div> + <div className="w-full overflow-x-auto rounded-lg border border-divider-regular"> + <table className="w-full text-xs font-normal leading-[18px] text-text-secondary"> + <thead className="uppercase text-text-tertiary"> + <tr className="border-b border-divider-regular"> <th className="w-[156px] p-2 pl-3 font-medium">{t('tools.createTool.toolInput.name')}</th> <th className="w-[102px] p-2 pl-3 font-medium">{t('tools.createTool.toolInput.method')}</th> <th className="p-2 pl-3 font-medium">{t('tools.createTool.toolInput.description')}</th> @@ -217,21 +223,22 @@ const WorkflowToolAsModal: FC<Props> = ({ </thead> <tbody> {parameters.map((item, index) => ( - <tr key={index} className='border-b border-divider-regular last:border-0'> + <tr key={index} className="border-b border-divider-regular last:border-0"> <td className="max-w-[156px] p-2 pl-3"> - <div className='text-[13px] leading-[18px]'> - <div title={item.name} className='flex'> - <span className='truncate font-medium text-text-primary'>{item.name}</span> - <span className='shrink-0 pl-1 text-xs leading-[18px] text-[#ec4a0a]'>{item.required ? t('tools.createTool.toolInput.required') : ''}</span> + <div className="text-[13px] leading-[18px]"> + <div title={item.name} className="flex"> + <span className="truncate font-medium text-text-primary">{item.name}</span> + <span className="shrink-0 pl-1 text-xs leading-[18px] text-[#ec4a0a]">{item.required ? t('tools.createTool.toolInput.required') : ''}</span> </div> - <div className='text-text-tertiary'>{item.type}</div> + <div className="text-text-tertiary">{item.type}</div> </div> </td> <td> {item.name === '__image' && ( <div className={cn( 'flex h-9 min-h-[56px] cursor-default items-center gap-1 bg-transparent px-3 py-2', - )}> + )} + > <div className={cn('grow truncate text-[13px] leading-[18px] text-text-secondary')}> {t('tools.createTool.toolInput.methodParameter')} </div> @@ -243,8 +250,8 @@ const WorkflowToolAsModal: FC<Props> = ({ </td> <td className="w-[236px] p-2 pl-3 text-text-tertiary"> <input - type='text' - className='w-full appearance-none bg-transparent text-[13px] font-normal leading-[18px] text-text-secondary caret-primary-600 outline-none placeholder:text-text-quaternary' + type="text" + className="w-full appearance-none bg-transparent text-[13px] font-normal leading-[18px] text-text-secondary caret-primary-600 outline-none placeholder:text-text-quaternary" placeholder={t('tools.createTool.toolInput.descriptionPlaceholder')!} value={item.description} onChange={e => handleParameterChange('description', e.target.value, index)} @@ -258,42 +265,44 @@ const WorkflowToolAsModal: FC<Props> = ({ </div> {/* Tool Output */} <div> - <div className='system-sm-medium py-2 text-text-primary'>{t('tools.createTool.toolOutput.title')}</div> - <div className='w-full overflow-x-auto rounded-lg border border-divider-regular'> - <table className='w-full text-xs font-normal leading-[18px] text-text-secondary'> - <thead className='uppercase text-text-tertiary'> - <tr className='border-b border-divider-regular'> + <div className="system-sm-medium py-2 text-text-primary">{t('tools.createTool.toolOutput.title')}</div> + <div className="w-full overflow-x-auto rounded-lg border border-divider-regular"> + <table className="w-full text-xs font-normal leading-[18px] text-text-secondary"> + <thead className="uppercase text-text-tertiary"> + <tr className="border-b border-divider-regular"> <th className="w-[156px] p-2 pl-3 font-medium">{t('tools.createTool.name')}</th> <th className="p-2 pl-3 font-medium">{t('tools.createTool.toolOutput.description')}</th> </tr> </thead> <tbody> {[...reservedOutputParameters, ...outputParameters].map((item, index) => ( - <tr key={index} className='border-b border-divider-regular last:border-0'> + <tr key={index} className="border-b border-divider-regular last:border-0"> <td className="max-w-[156px] p-2 pl-3"> - <div className='text-[13px] leading-[18px]'> - <div title={item.name} className='flex items-center'> - <span className='truncate font-medium text-text-primary'>{item.name}</span> - <span className='shrink-0 pl-1 text-xs leading-[18px] text-[#ec4a0a]'>{item.reserved ? t('tools.createTool.toolOutput.reserved') : ''}</span> + <div className="text-[13px] leading-[18px]"> + <div title={item.name} className="flex items-center"> + <span className="truncate font-medium text-text-primary">{item.name}</span> + <span className="shrink-0 pl-1 text-xs leading-[18px] text-[#ec4a0a]">{item.reserved ? t('tools.createTool.toolOutput.reserved') : ''}</span> { - !item.reserved && isOutputParameterReserved(item.name) ? ( - <Tooltip - popupContent={ - <div className='w-[180px]'> - {t('tools.createTool.toolOutput.reservedParameterDuplicateTip')} - </div> - } - > - <RiErrorWarningLine className='h-3 w-3 text-text-warning-secondary' /> - </Tooltip> - ) : null + !item.reserved && isOutputParameterReserved(item.name) + ? ( + <Tooltip + popupContent={( + <div className="w-[180px]"> + {t('tools.createTool.toolOutput.reservedParameterDuplicateTip')} + </div> + )} + > + <RiErrorWarningLine className="h-3 w-3 text-text-warning-secondary" /> + </Tooltip> + ) + : null } </div> - <div className='text-text-tertiary'>{item.type}</div> + <div className="text-text-tertiary">{item.type}</div> </div> </td> <td className="w-[236px] p-2 pl-3 text-text-tertiary"> - <span className='text-[13px] font-normal leading-[18px] text-text-secondary'>{item.description}</span> + <span className="text-[13px] font-normal leading-[18px] text-text-secondary">{item.description}</span> </td> </tr> ))} @@ -303,47 +312,55 @@ const WorkflowToolAsModal: FC<Props> = ({ </div> {/* Tags */} <div> - <div className='system-sm-medium py-2 text-text-primary'>{t('tools.createTool.toolInput.label')}</div> + <div className="system-sm-medium py-2 text-text-primary">{t('tools.createTool.toolInput.label')}</div> <LabelSelector value={labels} onChange={handleLabelSelect} /> </div> {/* Privacy Policy */} <div> - <div className='system-sm-medium py-2 text-text-primary'>{t('tools.createTool.privacyPolicy')}</div> + <div className="system-sm-medium py-2 text-text-primary">{t('tools.createTool.privacyPolicy')}</div> <Input - className='h-10' + className="h-10" value={privacyPolicy} onChange={e => setPrivacyPolicy(e.target.value)} - placeholder={t('tools.createTool.privacyPolicyPlaceholder') || ''} /> + placeholder={t('tools.createTool.privacyPolicyPlaceholder') || ''} + /> </div> </div> - <div className={cn((!isAdd && onRemove) ? 'justify-between' : 'justify-end', 'mt-2 flex shrink-0 rounded-b-[10px] border-t border-divider-regular bg-background-section-burn px-6 py-4')} > + <div className={cn((!isAdd && onRemove) ? 'justify-between' : 'justify-end', 'mt-2 flex shrink-0 rounded-b-[10px] border-t border-divider-regular bg-background-section-burn px-6 py-4')}> {!isAdd && onRemove && ( - <Button variant='warning' onClick={onRemove}>{t('common.operation.delete')}</Button> + <Button variant="warning" onClick={onRemove}>{t('common.operation.delete')}</Button> )} - <div className='flex space-x-2 '> + <div className="flex space-x-2 "> <Button onClick={onHide}>{t('common.operation.cancel')}</Button> - <Button variant='primary' onClick={() => { - if (isAdd) - onConfirm() - else - setShowModal(true) - }}>{t('common.operation.save')}</Button> + <Button + variant="primary" + onClick={() => { + if (isAdd) + onConfirm() + else + setShowModal(true) + }} + > + {t('common.operation.save')} + </Button> </div> </div> </div> - } + )} isShowMask={true} clickOutsideNotOpen={true} /> - {showEmojiPicker && <EmojiPicker - onSelect={(icon, icon_background) => { - setEmoji({ content: icon, background: icon_background }) - setShowEmojiPicker(false) - }} - onClose={() => { - setShowEmojiPicker(false) - }} - />} + {showEmojiPicker && ( + <EmojiPicker + onSelect={(icon, icon_background) => { + setEmoji({ content: icon, background: icon_background }) + setShowEmojiPicker(false) + }} + onClose={() => { + setShowEmojiPicker(false) + }} + /> + )} {showModal && ( <ConfirmModal show={showModal} diff --git a/web/app/components/tools/workflow-tool/method-selector.tsx b/web/app/components/tools/workflow-tool/method-selector.tsx index 03eb651ba3..c5a259ef43 100644 --- a/web/app/components/tools/workflow-tool/method-selector.tsx +++ b/web/app/components/tools/workflow-tool/method-selector.tsx @@ -1,14 +1,14 @@ import type { FC } from 'react' +import { RiArrowDownSLine } from '@remixicon/react' import { useState } from 'react' import { useTranslation } from 'react-i18next' -import { RiArrowDownSLine } from '@remixicon/react' -import { cn } from '@/utils/classnames' +import { Check } from '@/app/components/base/icons/src/vender/line/general' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Check } from '@/app/components/base/icons/src/vender/line/general' +import { cn } from '@/utils/classnames' type MethodSelectorProps = { value?: string @@ -25,46 +25,47 @@ const MethodSelector: FC<MethodSelectorProps> = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-start' + placement="bottom-start" offset={4} > - <div className='relative'> + <div className="relative"> <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)} - className='block' + className="block" > <div className={cn( 'flex h-9 min-h-[56px] cursor-pointer items-center gap-1 bg-transparent px-3 py-2 hover:bg-background-section-burn', open && '!bg-background-section-burn hover:bg-background-section-burn', - )}> + )} + > <div className={cn('grow truncate text-[13px] leading-[18px] text-text-secondary')}> {value === 'llm' ? t('tools.createTool.toolInput.methodParameter') : t('tools.createTool.toolInput.methodSetting')} </div> - <div className='ml-1 shrink-0 text-text-secondary opacity-60'> - <RiArrowDownSLine className='h-4 w-4' /> + <div className="ml-1 shrink-0 text-text-secondary opacity-60"> + <RiArrowDownSLine className="h-4 w-4" /> </div> </div> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1040]'> - <div className='relative w-[320px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-sm'> - <div className='p-1'> - <div className='cursor-pointer rounded-lg py-2.5 pl-3 pr-2 hover:bg-components-panel-on-panel-item-bg-hover' onClick={() => onChange('llm')}> - <div className='item-center flex gap-1'> - <div className='h-4 w-4 shrink-0'> - {value === 'llm' && <Check className='h-4 w-4 shrink-0 text-text-accent' />} + <PortalToFollowElemContent className="z-[1040]"> + <div className="relative w-[320px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-sm"> + <div className="p-1"> + <div className="cursor-pointer rounded-lg py-2.5 pl-3 pr-2 hover:bg-components-panel-on-panel-item-bg-hover" onClick={() => onChange('llm')}> + <div className="item-center flex gap-1"> + <div className="h-4 w-4 shrink-0"> + {value === 'llm' && <Check className="h-4 w-4 shrink-0 text-text-accent" />} </div> - <div className='text-[13px] font-medium leading-[18px] text-text-secondary'>{t('tools.createTool.toolInput.methodParameter')}</div> + <div className="text-[13px] font-medium leading-[18px] text-text-secondary">{t('tools.createTool.toolInput.methodParameter')}</div> </div> - <div className='pl-5 text-[13px] leading-[18px] text-text-tertiary'>{t('tools.createTool.toolInput.methodParameterTip')}</div> + <div className="pl-5 text-[13px] leading-[18px] text-text-tertiary">{t('tools.createTool.toolInput.methodParameterTip')}</div> </div> - <div className='cursor-pointer rounded-lg py-2.5 pl-3 pr-2 hover:bg-components-panel-on-panel-item-bg-hover' onClick={() => onChange('form')}> - <div className='item-center flex gap-1'> - <div className='h-4 w-4 shrink-0'> - {value === 'form' && <Check className='h-4 w-4 shrink-0 text-text-accent' />} + <div className="cursor-pointer rounded-lg py-2.5 pl-3 pr-2 hover:bg-components-panel-on-panel-item-bg-hover" onClick={() => onChange('form')}> + <div className="item-center flex gap-1"> + <div className="h-4 w-4 shrink-0"> + {value === 'form' && <Check className="h-4 w-4 shrink-0 text-text-accent" />} </div> - <div className='text-[13px] font-medium leading-[18px] text-text-secondary'>{t('tools.createTool.toolInput.methodSetting')}</div> + <div className="text-[13px] font-medium leading-[18px] text-text-secondary">{t('tools.createTool.toolInput.methodSetting')}</div> </div> - <div className='pl-5 text-[13px] leading-[18px] text-text-tertiary'>{t('tools.createTool.toolInput.methodSettingTip')}</div> + <div className="pl-5 text-[13px] leading-[18px] text-text-tertiary">{t('tools.createTool.toolInput.methodSettingTip')}</div> </div> </div> </div> diff --git a/web/app/components/tools/workflow-tool/utils.test.ts b/web/app/components/tools/workflow-tool/utils.test.ts index fef8c05489..bc2dc98c19 100644 --- a/web/app/components/tools/workflow-tool/utils.test.ts +++ b/web/app/components/tools/workflow-tool/utils.test.ts @@ -1,5 +1,5 @@ -import { VarType } from '@/app/components/workflow/types' import type { WorkflowToolProviderOutputParameter, WorkflowToolProviderOutputSchema } from '../types' +import { VarType } from '@/app/components/workflow/types' import { buildWorkflowOutputParameters } from './utils' describe('buildWorkflowOutputParameters', () => { diff --git a/web/app/components/workflow-app/components/workflow-children.tsx b/web/app/components/workflow-app/components/workflow-children.tsx index 1c8ed0cdf9..2634e8da2a 100644 --- a/web/app/components/workflow-app/components/workflow-children.tsx +++ b/web/app/components/workflow-app/components/workflow-children.tsx @@ -1,32 +1,31 @@ +import type { + PluginDefaultValue, + TriggerDefaultValue, +} from '@/app/components/workflow/block-selector/types' +import type { EnvironmentVariable } from '@/app/components/workflow/types' +import dynamic from 'next/dynamic' import { memo, useCallback, useState, } from 'react' -import type { EnvironmentVariable } from '@/app/components/workflow/types' -import { DSL_EXPORT_CHECK } from '@/app/components/workflow/constants' -import { START_INITIAL_POSITION } from '@/app/components/workflow/constants' -import { generateNewNode } from '@/app/components/workflow/utils' -import { useStore } from '@/app/components/workflow/store' import { useStoreApi } from 'reactflow' -import PluginDependency from '../../workflow/plugin-dependency' +import { DSL_EXPORT_CHECK, START_INITIAL_POSITION } from '@/app/components/workflow/constants' import { useAutoGenerateWebhookUrl, useDSL, usePanelInteractions, } from '@/app/components/workflow/hooks' import { useNodesSyncDraft } from '@/app/components/workflow/hooks/use-nodes-sync-draft' +import { useStore } from '@/app/components/workflow/store' +import { BlockEnum } from '@/app/components/workflow/types' +import { generateNewNode } from '@/app/components/workflow/utils' import { useEventEmitterContextContext } from '@/context/event-emitter' +import PluginDependency from '../../workflow/plugin-dependency' +import { useAvailableNodesMetaData } from '../hooks' +import { useAutoOnboarding } from '../hooks/use-auto-onboarding' import WorkflowHeader from './workflow-header' import WorkflowPanel from './workflow-panel' -import dynamic from 'next/dynamic' -import { BlockEnum } from '@/app/components/workflow/types' -import type { - PluginDefaultValue, - TriggerDefaultValue, -} from '@/app/components/workflow/block-selector/types' -import { useAutoOnboarding } from '../hooks/use-auto-onboarding' -import { useAvailableNodesMetaData } from '../hooks' const Features = dynamic(() => import('@/app/components/workflow/features'), { ssr: false, diff --git a/web/app/components/workflow-app/components/workflow-header/chat-variable-trigger.spec.tsx b/web/app/components/workflow-app/components/workflow-header/chat-variable-trigger.spec.tsx index 33115a2577..c73a4fb1da 100644 --- a/web/app/components/workflow-app/components/workflow-header/chat-variable-trigger.spec.tsx +++ b/web/app/components/workflow-app/components/workflow-header/chat-variable-trigger.spec.tsx @@ -17,7 +17,7 @@ vi.mock('../../hooks', () => ({ vi.mock('@/app/components/workflow/header/chat-variable-button', () => ({ __esModule: true, default: ({ disabled }: { disabled: boolean }) => ( - <button data-testid='chat-variable-button' type='button' disabled={disabled}> + <button data-testid="chat-variable-button" type="button" disabled={disabled}> ChatVariableButton </button> ), diff --git a/web/app/components/workflow-app/components/workflow-header/features-trigger.spec.tsx b/web/app/components/workflow-app/components/workflow-header/features-trigger.spec.tsx index 9f7b5c9129..5f17b0885c 100644 --- a/web/app/components/workflow-app/components/workflow-header/features-trigger.spec.tsx +++ b/web/app/components/workflow-app/components/workflow-header/features-trigger.spec.tsx @@ -1,9 +1,9 @@ import type { ReactElement } from 'react' +import type { AppPublisherProps } from '@/app/components/app/app-publisher' import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { Plan } from '@/app/components/billing/type' -import type { AppPublisherProps } from '@/app/components/app/app-publisher' import { ToastContext } from '@/app/components/base/toast' +import { Plan } from '@/app/components/billing/type' import { BlockEnum, InputVarType } from '@/app/components/workflow/types' import FeaturesTrigger from './features-trigger' @@ -96,7 +96,7 @@ vi.mock('@/app/components/app/app-publisher', () => ({ const inputs = props.inputs ?? [] return ( <div - data-testid='app-publisher' + data-testid="app-publisher" data-disabled={String(Boolean(props.disabled))} data-publish-disabled={String(Boolean(props.publishDisabled))} data-start-node-limit-exceeded={String(Boolean(props.startNodeLimitExceeded))} @@ -147,7 +147,7 @@ vi.mock('@/hooks/use-theme', () => ({ vi.mock('@/app/components/app/store', () => ({ __esModule: true, - useStore: (selector: (state: { appDetail?: { id: string }; setAppDetail: typeof mockSetAppDetail }) => unknown) => mockUseAppStoreSelector(selector), + useStore: (selector: (state: { appDetail?: { id: string }, setAppDetail: typeof mockSetAppDetail }) => unknown) => mockUseAppStoreSelector(selector), })) const createProviderContext = ({ diff --git a/web/app/components/workflow-app/components/workflow-header/features-trigger.tsx b/web/app/components/workflow-app/components/workflow-header/features-trigger.tsx index cba66996e8..1df6f10195 100644 --- a/web/app/components/workflow-app/components/workflow-header/features-trigger.tsx +++ b/web/app/components/workflow-app/components/workflow-header/features-trigger.tsx @@ -1,48 +1,48 @@ +import type { EndNodeType } from '@/app/components/workflow/nodes/end/types' +import type { StartNodeType } from '@/app/components/workflow/nodes/start/types' +import type { + CommonEdgeType, + Node, +} from '@/app/components/workflow/types' +import type { PublishWorkflowParams } from '@/types/workflow' +import { RiApps2AddLine } from '@remixicon/react' import { memo, useCallback, useMemo, } from 'react' -import { useEdges } from 'reactflow' -import { RiApps2AddLine } from '@remixicon/react' import { useTranslation } from 'react-i18next' -import { - useStore, - useWorkflowStore, -} from '@/app/components/workflow/store' +import { useEdges } from 'reactflow' +import AppPublisher from '@/app/components/app/app-publisher' +import { useStore as useAppStore } from '@/app/components/app/store' +import Button from '@/app/components/base/button' +import { useFeatures } from '@/app/components/base/features/hooks' +import { useToastContext } from '@/app/components/base/toast' +import { Plan } from '@/app/components/billing/type' import { useChecklist, useChecklistBeforePublish, + useIsChatMode, useNodesReadOnly, useNodesSyncDraft, // useWorkflowRunValidation, } from '@/app/components/workflow/hooks' -import Button from '@/app/components/base/button' -import AppPublisher from '@/app/components/app/app-publisher' -import { useFeatures } from '@/app/components/base/features/hooks' -import type { - CommonEdgeType, - Node, -} from '@/app/components/workflow/types' +import { + useStore, + useWorkflowStore, +} from '@/app/components/workflow/store' +import useNodes from '@/app/components/workflow/store/workflow/use-nodes' import { BlockEnum, InputVarType, isTriggerNode, } from '@/app/components/workflow/types' -import { useToastContext } from '@/app/components/base/toast' -import { useInvalidateAppWorkflow, usePublishWorkflow, useResetWorkflowVersionHistory } from '@/service/use-workflow' -import { useInvalidateAppTriggers } from '@/service/use-tools' -import type { PublishWorkflowParams } from '@/types/workflow' -import { fetchAppDetail } from '@/service/apps' -import { useStore as useAppStore } from '@/app/components/app/store' -import useTheme from '@/hooks/use-theme' -import { cn } from '@/utils/classnames' -import { useIsChatMode } from '@/app/components/workflow/hooks' -import type { StartNodeType } from '@/app/components/workflow/nodes/start/types' -import type { EndNodeType } from '@/app/components/workflow/nodes/end/types' import { useProviderContext } from '@/context/provider-context' -import { Plan } from '@/app/components/billing/type' -import useNodes from '@/app/components/workflow/store/workflow/use-nodes' +import useTheme from '@/hooks/use-theme' +import { fetchAppDetail } from '@/service/apps' +import { useInvalidateAppTriggers } from '@/service/use-tools' +import { useInvalidateAppWorkflow, usePublishWorkflow, useResetWorkflowVersionHistory } from '@/service/use-workflow' +import { cn } from '@/utils/classnames' const FeaturesTrigger = () => { const { t } = useTranslation() @@ -193,7 +193,7 @@ const FeaturesTrigger = () => { )} onClick={handleShowFeatures} > - <RiApps2AddLine className='mr-1 h-4 w-4 text-components-button-secondary-text' /> + <RiApps2AddLine className="mr-1 h-4 w-4 text-components-button-secondary-text" /> {t('workflow.common.features')} </Button> )} diff --git a/web/app/components/workflow-app/components/workflow-header/index.spec.tsx b/web/app/components/workflow-app/components/workflow-header/index.spec.tsx index fce8e0d724..8eee878cd9 100644 --- a/web/app/components/workflow-app/components/workflow-header/index.spec.tsx +++ b/web/app/components/workflow-app/components/workflow-header/index.spec.tsx @@ -1,7 +1,7 @@ -import { render, screen } from '@testing-library/react' -import type { App } from '@/types/app' -import { AppModeEnum } from '@/types/app' import type { HeaderProps } from '@/app/components/workflow/header' +import type { App } from '@/types/app' +import { render, screen } from '@testing-library/react' +import { AppModeEnum } from '@/types/app' import WorkflowHeader from './index' const mockUseAppStoreSelector = vi.fn() @@ -13,7 +13,7 @@ let appDetail: App vi.mock('@/app/components/app/store', () => ({ __esModule: true, - useStore: (selector: (state: { appDetail?: App; setCurrentLogItem: typeof mockSetCurrentLogItem; setShowMessageLogModal: typeof mockSetShowMessageLogModal }) => unknown) => mockUseAppStoreSelector(selector), + useStore: (selector: (state: { appDetail?: App, setCurrentLogItem: typeof mockSetCurrentLogItem, setShowMessageLogModal: typeof mockSetShowMessageLogModal }) => unknown) => mockUseAppStoreSelector(selector), })) vi.mock('@/app/components/workflow/header', () => ({ @@ -24,7 +24,7 @@ vi.mock('@/app/components/workflow/header', () => ({ return ( <div - data-testid='workflow-header' + data-testid="workflow-header" data-show-run={String(Boolean(props.normal?.runAndHistoryProps?.showRunButton))} data-show-preview={String(Boolean(props.normal?.runAndHistoryProps?.showPreviewButton))} data-history-url={props.normal?.runAndHistoryProps?.viewHistoryProps?.historyUrl ?? ''} diff --git a/web/app/components/workflow-app/components/workflow-header/index.tsx b/web/app/components/workflow-app/components/workflow-header/index.tsx index c0b8a37b87..4acb721487 100644 --- a/web/app/components/workflow-app/components/workflow-header/index.tsx +++ b/web/app/components/workflow-app/components/workflow-header/index.tsx @@ -1,19 +1,19 @@ +import type { HeaderProps } from '@/app/components/workflow/header' import { memo, useCallback, useMemo, } from 'react' import { useShallow } from 'zustand/react/shallow' -import type { HeaderProps } from '@/app/components/workflow/header' -import Header from '@/app/components/workflow/header' import { useStore as useAppStore } from '@/app/components/app/store' +import Header from '@/app/components/workflow/header' +import { useResetWorkflowVersionHistory } from '@/service/use-workflow' import { fetchWorkflowRunHistory, } from '@/service/workflow' +import { useIsChatMode } from '../../hooks' import ChatVariableTrigger from './chat-variable-trigger' import FeaturesTrigger from './features-trigger' -import { useResetWorkflowVersionHistory } from '@/service/use-workflow' -import { useIsChatMode } from '../../hooks' const WorkflowHeader = () => { const { appDetail, setCurrentLogItem, setShowMessageLogModal } = useAppStore(useShallow(state => ({ diff --git a/web/app/components/workflow-app/components/workflow-main.tsx b/web/app/components/workflow-app/components/workflow-main.tsx index e90b2904c9..38a044f088 100644 --- a/web/app/components/workflow-app/components/workflow-main.tsx +++ b/web/app/components/workflow-app/components/workflow-main.tsx @@ -1,11 +1,11 @@ +import type { WorkflowProps } from '@/app/components/workflow' import { useCallback, useMemo, } from 'react' import { useFeaturesStore } from '@/app/components/base/features/hooks' import { WorkflowWithInnerContext } from '@/app/components/workflow' -import type { WorkflowProps } from '@/app/components/workflow' -import WorkflowChildren from './workflow-children' +import { useWorkflowStore } from '@/app/components/workflow/store' import { useAvailableNodesMetaData, useConfigsMap, @@ -18,7 +18,7 @@ import { useWorkflowRun, useWorkflowStartRun, } from '../hooks' -import { useWorkflowStore } from '@/app/components/workflow/store' +import WorkflowChildren from './workflow-children' type WorkflowMainProps = Pick<WorkflowProps, 'nodes' | 'edges' | 'viewport'> const WorkflowMain = ({ diff --git a/web/app/components/workflow-app/components/workflow-onboarding-modal/index.spec.tsx b/web/app/components/workflow-app/components/workflow-onboarding-modal/index.spec.tsx index b37451fa07..b7a2cefd1c 100644 --- a/web/app/components/workflow-app/components/workflow-onboarding-modal/index.spec.tsx +++ b/web/app/components/workflow-app/components/workflow-onboarding-modal/index.spec.tsx @@ -1,8 +1,8 @@ -import React from 'react' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import WorkflowOnboardingModal from './index' +import React from 'react' import { BlockEnum } from '@/app/components/workflow/types' +import WorkflowOnboardingModal from './index' // Mock Modal component vi.mock('@/app/components/base/modal', () => ({ diff --git a/web/app/components/workflow-app/components/workflow-onboarding-modal/index.tsx b/web/app/components/workflow-app/components/workflow-onboarding-modal/index.tsx index 747a232ca7..633bb59ef1 100644 --- a/web/app/components/workflow-app/components/workflow-onboarding-modal/index.tsx +++ b/web/app/components/workflow-app/components/workflow-onboarding-modal/index.tsx @@ -1,15 +1,15 @@ 'use client' import type { FC } from 'react' +import type { PluginDefaultValue } from '@/app/components/workflow/block-selector/types' import { useCallback, useEffect, } from 'react' import { useTranslation } from 'react-i18next' -import { BlockEnum } from '@/app/components/workflow/types' -import type { PluginDefaultValue } from '@/app/components/workflow/block-selector/types' import Modal from '@/app/components/base/modal' -import StartNodeSelectionPanel from './start-node-selection-panel' +import { BlockEnum } from '@/app/components/workflow/types' import { useDocLink } from '@/context/i18n' +import StartNodeSelectionPanel from './start-node-selection-panel' type WorkflowOnboardingModalProps = { isShow: boolean @@ -61,7 +61,8 @@ const WorkflowOnboardingModal: FC<WorkflowOnboardingModalProps> = ({ {t('workflow.onboarding.title')} </h3> <div className="body-xs-regular leading-4 text-text-tertiary"> - {t('workflow.onboarding.description')}{' '} + {t('workflow.onboarding.description')} + {' '} <a href={docLink('/guides/workflow/node/start')} target="_blank" @@ -69,7 +70,8 @@ const WorkflowOnboardingModal: FC<WorkflowOnboardingModalProps> = ({ className="hover:text-text-accent-hover cursor-pointer text-text-accent underline" > {t('workflow.onboarding.learnMore')} - </a>{' '} + </a> + {' '} {t('workflow.onboarding.aboutStartNode')} </div> </div> diff --git a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-option.spec.tsx b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-option.spec.tsx index e089e96a59..0fa5c9f13d 100644 --- a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-option.spec.tsx +++ b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-option.spec.tsx @@ -1,6 +1,6 @@ -import React from 'react' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' +import React from 'react' import StartNodeOption from './start-node-option' describe('StartNodeOption', () => { diff --git a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-option.tsx b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-option.tsx index 2cc54b39ca..77f2a842c9 100644 --- a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-option.tsx +++ b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-option.tsx @@ -35,7 +35,10 @@ const StartNodeOption: FC<StartNodeOptionProps> = ({ <h3 className="system-md-semi-bold text-text-primary"> {title} {subtitle && ( - <span className="system-md-regular text-text-quaternary"> {subtitle}</span> + <span className="system-md-regular text-text-quaternary"> + {' '} + {subtitle} + </span> )} </h3> </div> diff --git a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.spec.tsx b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.spec.tsx index a7e748deeb..8c59ec3b82 100644 --- a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.spec.tsx +++ b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.spec.tsx @@ -1,8 +1,8 @@ -import React from 'react' import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import StartNodeSelectionPanel from './start-node-selection-panel' +import React from 'react' import { BlockEnum } from '@/app/components/workflow/types' +import StartNodeSelectionPanel from './start-node-selection-panel' // Mock NodeSelector component vi.mock('@/app/components/workflow/block-selector', () => ({ diff --git a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.tsx b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.tsx index de934a13b2..e46fac86fc 100644 --- a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.tsx +++ b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.tsx @@ -1,14 +1,13 @@ 'use client' import type { FC } from 'react' +import type { PluginDefaultValue } from '@/app/components/workflow/block-selector/types' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import StartNodeOption from './start-node-option' +import { Home, TriggerAll } from '@/app/components/base/icons/src/vender/workflow' import NodeSelector from '@/app/components/workflow/block-selector' -import { Home } from '@/app/components/base/icons/src/vender/workflow' -import { TriggerAll } from '@/app/components/base/icons/src/vender/workflow' -import { BlockEnum } from '@/app/components/workflow/types' -import type { PluginDefaultValue } from '@/app/components/workflow/block-selector/types' import { TabsEnum } from '@/app/components/workflow/block-selector/types' +import { BlockEnum } from '@/app/components/workflow/types' +import StartNodeOption from './start-node-option' type StartNodeSelectionPanelProps = { onSelectUserInput: () => void @@ -34,11 +33,11 @@ const StartNodeSelectionPanel: FC<StartNodeSelectionPanelProps> = ({ return ( <div className="grid grid-cols-2 gap-4"> <StartNodeOption - icon={ + icon={( <div className="flex h-9 w-9 items-center justify-center rounded-[10px] border-[0.5px] border-transparent bg-util-colors-blue-brand-blue-brand-500 p-2"> <Home className="h-5 w-5 text-white" /> </div> - } + )} title={t('workflow.onboarding.userInputFull')} description={t('workflow.onboarding.userInputDescription')} onClick={onSelectUserInput} @@ -61,11 +60,11 @@ const StartNodeSelectionPanel: FC<StartNodeSelectionPanelProps> = ({ ]} trigger={() => ( <StartNodeOption - icon={ + icon={( <div className="flex h-9 w-9 items-center justify-center rounded-[10px] border-[0.5px] border-transparent bg-util-colors-blue-brand-blue-brand-500 p-2"> <TriggerAll className="h-5 w-5 text-white" /> </div> - } + )} title={t('workflow.onboarding.trigger')} description={t('workflow.onboarding.triggerDescription')} onClick={handleTriggerClick} diff --git a/web/app/components/workflow-app/components/workflow-panel.tsx b/web/app/components/workflow-app/components/workflow-panel.tsx index 6e0504710e..a1ed289f94 100644 --- a/web/app/components/workflow-app/components/workflow-panel.tsx +++ b/web/app/components/workflow-app/components/workflow-panel.tsx @@ -1,16 +1,16 @@ +import type { PanelProps } from '@/app/components/workflow/panel' +import dynamic from 'next/dynamic' import { memo, useMemo, } from 'react' import { useShallow } from 'zustand/react/shallow' +import { useStore as useAppStore } from '@/app/components/app/store' +import Panel from '@/app/components/workflow/panel' import { useStore } from '@/app/components/workflow/store' import { useIsChatMode, } from '../hooks' -import { useStore as useAppStore } from '@/app/components/app/store' -import type { PanelProps } from '@/app/components/workflow/panel' -import Panel from '@/app/components/workflow/panel' -import dynamic from 'next/dynamic' const MessageLogModal = dynamic(() => import('@/app/components/base/message-log-modal'), { ssr: false, diff --git a/web/app/components/workflow-app/hooks/index.ts b/web/app/components/workflow-app/hooks/index.ts index a9eb0f9b7b..2a8c806859 100644 --- a/web/app/components/workflow-app/hooks/index.ts +++ b/web/app/components/workflow-app/hooks/index.ts @@ -1,13 +1,13 @@ -export * from './use-workflow-init' -export * from './use-workflow-template' +export * from '../../workflow/hooks/use-fetch-workflow-inspect-vars' +export * from './use-available-nodes-meta-data' +export * from './use-configs-map' +export * from './use-DSL' +export * from './use-get-run-and-trace-url' +export * from './use-inspect-vars-crud' +export * from './use-is-chat-mode' export * from './use-nodes-sync-draft' +export * from './use-workflow-init' +export * from './use-workflow-refresh-draft' export * from './use-workflow-run' export * from './use-workflow-start-run' -export * from './use-is-chat-mode' -export * from './use-available-nodes-meta-data' -export * from './use-workflow-refresh-draft' -export * from './use-get-run-and-trace-url' -export * from './use-DSL' -export * from '../../workflow/hooks/use-fetch-workflow-inspect-vars' -export * from './use-inspect-vars-crud' -export * from './use-configs-map' +export * from './use-workflow-template' diff --git a/web/app/components/workflow-app/hooks/use-DSL.ts b/web/app/components/workflow-app/hooks/use-DSL.ts index 6787c06198..afa55f38c8 100644 --- a/web/app/components/workflow-app/hooks/use-DSL.ts +++ b/web/app/components/workflow-app/hooks/use-DSL.ts @@ -3,15 +3,15 @@ import { useState, } from 'react' import { useTranslation } from 'react-i18next' +import { useStore as useAppStore } from '@/app/components/app/store' +import { useToastContext } from '@/app/components/base/toast' import { DSL_EXPORT_CHECK, } from '@/app/components/workflow/constants' -import { useNodesSyncDraft } from './use-nodes-sync-draft' import { useEventEmitterContextContext } from '@/context/event-emitter' -import { fetchWorkflowDraft } from '@/service/workflow' import { exportAppConfig } from '@/service/apps' -import { useToastContext } from '@/app/components/base/toast' -import { useStore as useAppStore } from '@/app/components/app/store' +import { fetchWorkflowDraft } from '@/service/workflow' +import { useNodesSyncDraft } from './use-nodes-sync-draft' export const useDSL = () => { const { t } = useTranslation() diff --git a/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts b/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts index b95d4d47f7..24d862321d 100644 --- a/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts +++ b/web/app/components/workflow-app/hooks/use-available-nodes-meta-data.ts @@ -1,16 +1,16 @@ +import type { AvailableNodesMetaData } from '@/app/components/workflow/hooks-store/store' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { useDocLink } from '@/context/i18n' -import StartDefault from '@/app/components/workflow/nodes/start/default' -import TriggerWebhookDefault from '@/app/components/workflow/nodes/trigger-webhook/default' -import TriggerScheduleDefault from '@/app/components/workflow/nodes/trigger-schedule/default' -import TriggerPluginDefault from '@/app/components/workflow/nodes/trigger-plugin/default' -import EndDefault from '@/app/components/workflow/nodes/end/default' -import AnswerDefault from '@/app/components/workflow/nodes/answer/default' import { WORKFLOW_COMMON_NODES } from '@/app/components/workflow/constants/node' -import type { AvailableNodesMetaData } from '@/app/components/workflow/hooks-store/store' -import { useIsChatMode } from './use-is-chat-mode' +import AnswerDefault from '@/app/components/workflow/nodes/answer/default' +import EndDefault from '@/app/components/workflow/nodes/end/default' +import StartDefault from '@/app/components/workflow/nodes/start/default' +import TriggerPluginDefault from '@/app/components/workflow/nodes/trigger-plugin/default' +import TriggerScheduleDefault from '@/app/components/workflow/nodes/trigger-schedule/default' +import TriggerWebhookDefault from '@/app/components/workflow/nodes/trigger-webhook/default' import { BlockEnum } from '@/app/components/workflow/types' +import { useDocLink } from '@/context/i18n' +import { useIsChatMode } from './use-is-chat-mode' export const useAvailableNodesMetaData = () => { const { t } = useTranslation() @@ -32,11 +32,11 @@ export const useAvailableNodesMetaData = () => { isChatMode ? [AnswerDefault] : [ - EndDefault, - TriggerWebhookDefault, - TriggerScheduleDefault, - TriggerPluginDefault, - ] + EndDefault, + TriggerWebhookDefault, + TriggerScheduleDefault, + TriggerPluginDefault, + ] ), ], [isChatMode, startNodeMetaData]) diff --git a/web/app/components/workflow-app/hooks/use-configs-map.ts b/web/app/components/workflow-app/hooks/use-configs-map.ts index 5d41901924..b16a16fdc9 100644 --- a/web/app/components/workflow-app/hooks/use-configs-map.ts +++ b/web/app/components/workflow-app/hooks/use-configs-map.ts @@ -1,7 +1,7 @@ import { useMemo } from 'react' +import { useFeatures } from '@/app/components/base/features/hooks' import { useStore } from '@/app/components/workflow/store' import { FlowType } from '@/types/common' -import { useFeatures } from '@/app/components/base/features/hooks' export const useConfigsMap = () => { const appId = useStore(s => s.appId) diff --git a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts index 56d9021feb..5d394bab1e 100644 --- a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts +++ b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts @@ -1,12 +1,12 @@ -import { useCallback } from 'react' import { produce } from 'immer' +import { useCallback } from 'react' import { useStoreApi } from 'reactflow' -import { useWorkflowStore } from '@/app/components/workflow/store' -import { useNodesReadOnly } from '@/app/components/workflow/hooks/use-workflow' -import { useSerialAsyncCallback } from '@/app/components/workflow/hooks/use-serial-async-callback' -import { syncWorkflowDraft } from '@/service/workflow' import { useFeaturesStore } from '@/app/components/base/features/hooks' +import { useSerialAsyncCallback } from '@/app/components/workflow/hooks/use-serial-async-callback' +import { useNodesReadOnly } from '@/app/components/workflow/hooks/use-workflow' +import { useWorkflowStore } from '@/app/components/workflow/store' import { API_PREFIX } from '@/config' +import { syncWorkflowDraft } from '@/service/workflow' import { useWorkflowRefreshDraft } from '.' export const useNodesSyncDraft = () => { diff --git a/web/app/components/workflow-app/hooks/use-workflow-init.ts b/web/app/components/workflow-app/hooks/use-workflow-init.ts index a0a6cc22a1..8e976937b5 100644 --- a/web/app/components/workflow-app/hooks/use-workflow-init.ts +++ b/web/app/components/workflow-app/hooks/use-workflow-init.ts @@ -1,26 +1,26 @@ +import type { Edge, Node } from '@/app/components/workflow/types' +import type { FileUploadConfigResponse } from '@/models/common' +import type { FetchWorkflowDraftResponse } from '@/types/workflow' import { useCallback, useEffect, useState, } from 'react' +import { useStore as useAppStore } from '@/app/components/app/store' import { useStore, useWorkflowStore, } from '@/app/components/workflow/store' -import { useWorkflowTemplate } from './use-workflow-template' -import { useStore as useAppStore } from '@/app/components/app/store' +import { BlockEnum } from '@/app/components/workflow/types' +import { useWorkflowConfig } from '@/service/use-workflow' import { fetchNodesDefaultConfigs, fetchPublishedWorkflow, fetchWorkflowDraft, syncWorkflowDraft, } from '@/service/workflow' -import type { FetchWorkflowDraftResponse } from '@/types/workflow' -import { useWorkflowConfig } from '@/service/use-workflow' -import type { FileUploadConfigResponse } from '@/models/common' -import type { Edge, Node } from '@/app/components/workflow/types' -import { BlockEnum } from '@/app/components/workflow/types' import { AppModeEnum } from '@/types/app' +import { useWorkflowTemplate } from './use-workflow-template' const hasConnectedUserInput = (nodes: Node[] = [], edges: Edge[] = []): boolean => { const startNodeIds = nodes diff --git a/web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts b/web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts index 910ddd4a8d..fa4a44d894 100644 --- a/web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts +++ b/web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts @@ -1,8 +1,8 @@ +import type { WorkflowDataUpdater } from '@/app/components/workflow/types' import { useCallback } from 'react' +import { useWorkflowUpdate } from '@/app/components/workflow/hooks' import { useWorkflowStore } from '@/app/components/workflow/store' import { fetchWorkflowDraft } from '@/service/workflow' -import type { WorkflowDataUpdater } from '@/app/components/workflow/types' -import { useWorkflowUpdate } from '@/app/components/workflow/hooks' export const useWorkflowRefreshDraft = () => { const workflowStore = useWorkflowStore() diff --git a/web/app/components/workflow-app/hooks/use-workflow-run.ts b/web/app/components/workflow-app/hooks/use-workflow-run.ts index f051f73489..16c7bab6d4 100644 --- a/web/app/components/workflow-app/hooks/use-workflow-run.ts +++ b/web/app/components/workflow-app/hooks/use-workflow-run.ts @@ -1,35 +1,34 @@ +import type AudioPlayer from '@/app/components/base/audio-btn/audio' +import type { Node } from '@/app/components/workflow/types' +import type { IOtherOptions } from '@/service/base' +import type { VersionHistory } from '@/types/workflow' +import { produce } from 'immer' +import { noop } from 'lodash-es' +import { usePathname } from 'next/navigation' import { useCallback, useRef } from 'react' import { useReactFlow, useStoreApi, } from 'reactflow' -import { produce } from 'immer' import { v4 as uuidV4 } from 'uuid' -import { usePathname } from 'next/navigation' -import { useWorkflowStore } from '@/app/components/workflow/store' -import type { Node } from '@/app/components/workflow/types' -import { WorkflowRunningStatus } from '@/app/components/workflow/types' +import { useStore as useAppStore } from '@/app/components/app/store' +import { trackEvent } from '@/app/components/base/amplitude' +import { AudioPlayerManager } from '@/app/components/base/audio-btn/audio.player.manager' +import { useFeaturesStore } from '@/app/components/base/features/hooks' +import Toast from '@/app/components/base/toast' +import { TriggerType } from '@/app/components/workflow/header/test-run-menu' import { useWorkflowUpdate } from '@/app/components/workflow/hooks/use-workflow-interactions' import { useWorkflowRunEvent } from '@/app/components/workflow/hooks/use-workflow-run-event/use-workflow-run-event' -import { useStore as useAppStore } from '@/app/components/app/store' -import type { IOtherOptions } from '@/service/base' -import Toast from '@/app/components/base/toast' -import { handleStream, ssePost } from '@/service/base' -import { stopWorkflowRun } from '@/service/workflow' -import { useFeaturesStore } from '@/app/components/base/features/hooks' -import { AudioPlayerManager } from '@/app/components/base/audio-btn/audio.player.manager' -import type AudioPlayer from '@/app/components/base/audio-btn/audio' -import type { VersionHistory } from '@/types/workflow' -import { noop } from 'lodash-es' -import { useNodesSyncDraft } from './use-nodes-sync-draft' +import { useWorkflowStore } from '@/app/components/workflow/store' +import { WorkflowRunningStatus } from '@/app/components/workflow/types' +import { handleStream, post, ssePost } from '@/service/base' +import { ContentType } from '@/service/fetch' import { useInvalidAllLastRun } from '@/service/use-workflow' +import { stopWorkflowRun } from '@/service/workflow' +import { AppModeEnum } from '@/types/app' import { useSetWorkflowVarsWithValue } from '../../workflow/hooks/use-fetch-workflow-inspect-vars' import { useConfigsMap } from './use-configs-map' -import { post } from '@/service/base' -import { ContentType } from '@/service/fetch' -import { TriggerType } from '@/app/components/workflow/header/test-run-menu' -import { AppModeEnum } from '@/types/app' -import { trackEvent } from '@/app/components/base/amplitude' +import { useNodesSyncDraft } from './use-nodes-sync-draft' type HandleRunMode = TriggerType type HandleRunOptions = { @@ -668,8 +667,7 @@ export const useWorkflowRun = () => { }, }, ) - }, [store, doSyncWorkflowDraft, workflowStore, pathname, handleWorkflowStarted, handleWorkflowFinished, fetchInspectVars, invalidAllLastRun, handleWorkflowFailed, handleWorkflowNodeStarted, handleWorkflowNodeFinished, handleWorkflowNodeIterationStarted, handleWorkflowNodeIterationNext, handleWorkflowNodeIterationFinished, handleWorkflowNodeLoopStarted, handleWorkflowNodeLoopNext, handleWorkflowNodeLoopFinished, handleWorkflowNodeRetry, handleWorkflowAgentLog, handleWorkflowTextChunk, handleWorkflowTextReplace], - ) + }, [store, doSyncWorkflowDraft, workflowStore, pathname, handleWorkflowStarted, handleWorkflowFinished, fetchInspectVars, invalidAllLastRun, handleWorkflowFailed, handleWorkflowNodeStarted, handleWorkflowNodeFinished, handleWorkflowNodeIterationStarted, handleWorkflowNodeIterationNext, handleWorkflowNodeIterationFinished, handleWorkflowNodeLoopStarted, handleWorkflowNodeLoopNext, handleWorkflowNodeLoopFinished, handleWorkflowNodeRetry, handleWorkflowAgentLog, handleWorkflowTextChunk, handleWorkflowTextReplace]) const handleStopRun = useCallback((taskId: string) => { const setStoppedState = () => { diff --git a/web/app/components/workflow-app/hooks/use-workflow-start-run.tsx b/web/app/components/workflow-app/hooks/use-workflow-start-run.tsx index d2e3b3e3c9..9344a8d1ab 100644 --- a/web/app/components/workflow-app/hooks/use-workflow-start-run.tsx +++ b/web/app/components/workflow-app/hooks/use-workflow-start-run.tsx @@ -1,18 +1,18 @@ import { useCallback } from 'react' import { useStoreApi } from 'reactflow' +import { useFeaturesStore } from '@/app/components/base/features/hooks' +import { TriggerType } from '@/app/components/workflow/header/test-run-menu' +import { useWorkflowInteractions } from '@/app/components/workflow/hooks' import { useWorkflowStore } from '@/app/components/workflow/store' import { BlockEnum, WorkflowRunningStatus, } from '@/app/components/workflow/types' -import { useWorkflowInteractions } from '@/app/components/workflow/hooks' -import { useFeaturesStore } from '@/app/components/base/features/hooks' import { useIsChatMode, useNodesSyncDraft, useWorkflowRun, } from '.' -import { TriggerType } from '@/app/components/workflow/header/test-run-menu' export const useWorkflowStartRun = () => { const store = useStoreApi() diff --git a/web/app/components/workflow-app/hooks/use-workflow-template.ts b/web/app/components/workflow-app/hooks/use-workflow-template.ts index 5f3f9762f0..b48a68e1eb 100644 --- a/web/app/components/workflow-app/hooks/use-workflow-template.ts +++ b/web/app/components/workflow-app/hooks/use-workflow-template.ts @@ -1,14 +1,14 @@ +import type { StartNodeType } from '@/app/components/workflow/nodes/start/types' import { useTranslation } from 'react-i18next' -import { generateNewNode } from '@/app/components/workflow/utils' import { NODE_WIDTH_X_OFFSET, START_INITIAL_POSITION, } from '@/app/components/workflow/constants' -import { useIsChatMode } from './use-is-chat-mode' -import type { StartNodeType } from '@/app/components/workflow/nodes/start/types' -import startDefault from '@/app/components/workflow/nodes/start/default' -import llmDefault from '@/app/components/workflow/nodes/llm/default' import answerDefault from '@/app/components/workflow/nodes/answer/default' +import llmDefault from '@/app/components/workflow/nodes/llm/default' +import startDefault from '@/app/components/workflow/nodes/start/default' +import { generateNewNode } from '@/app/components/workflow/utils' +import { useIsChatMode } from './use-is-chat-mode' export const useWorkflowTemplate = () => { const isChatMode = useIsChatMode() diff --git a/web/app/components/workflow-app/index.tsx b/web/app/components/workflow-app/index.tsx index fcd247ef22..6a778ab6b8 100644 --- a/web/app/components/workflow-app/index.tsx +++ b/web/app/components/workflow-app/index.tsx @@ -1,40 +1,40 @@ 'use client' +import type { Features as FeaturesData } from '@/app/components/base/features/types' +import type { InjectWorkflowStoreSliceFn } from '@/app/components/workflow/store' +import { useSearchParams } from 'next/navigation' import { useEffect, useMemo, } from 'react' -import { - SupportUploadFileTypes, -} from '@/app/components/workflow/types' -import { - useWorkflowInit, -} from './hooks/use-workflow-init' -import { useAppTriggers } from '@/service/use-tools' -import { useTriggerStatusStore } from '@/app/components/workflow/store/trigger-status' import { useStore as useAppStore } from '@/app/components/app/store' -import { useWorkflowStore } from '@/app/components/workflow/store' -import { - initialEdges, - initialNodes, -} from '@/app/components/workflow/utils' -import Loading from '@/app/components/base/loading' import { FeaturesProvider } from '@/app/components/base/features' -import type { Features as FeaturesData } from '@/app/components/base/features/types' +import Loading from '@/app/components/base/loading' import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' -import { useAppContext } from '@/context/app-context' import WorkflowWithDefaultContext from '@/app/components/workflow' import { WorkflowContextProvider, } from '@/app/components/workflow/context' -import type { InjectWorkflowStoreSliceFn } from '@/app/components/workflow/store' -import { createWorkflowSlice } from './store/workflow/workflow-slice' -import WorkflowAppMain from './components/workflow-main' -import { useSearchParams } from 'next/navigation' - +import { useWorkflowStore } from '@/app/components/workflow/store' +import { useTriggerStatusStore } from '@/app/components/workflow/store/trigger-status' +import { + SupportUploadFileTypes, +} from '@/app/components/workflow/types' +import { + initialEdges, + initialNodes, +} from '@/app/components/workflow/utils' +import { useAppContext } from '@/context/app-context' import { fetchRunDetail } from '@/service/log' -import { useGetRunAndTraceUrl } from './hooks/use-get-run-and-trace-url' +import { useAppTriggers } from '@/service/use-tools' import { AppModeEnum } from '@/types/app' +import WorkflowAppMain from './components/workflow-main' + +import { useGetRunAndTraceUrl } from './hooks/use-get-run-and-trace-url' +import { + useWorkflowInit, +} from './hooks/use-workflow-init' +import { createWorkflowSlice } from './store/workflow/workflow-slice' const WorkflowAppWithAdditionalContext = () => { const { @@ -161,7 +161,7 @@ const WorkflowAppWithAdditionalContext = () => { if (!data || isLoading || isLoadingCurrentWorkspace || !currentWorkspace.id) { return ( - <div className='relative flex h-full w-full items-center justify-center'> + <div className="relative flex h-full w-full items-center justify-center"> <Loading /> </div> ) diff --git a/web/app/components/workflow/__tests__/trigger-status-sync.test.tsx b/web/app/components/workflow/__tests__/trigger-status-sync.test.tsx index 6295b3f5a9..6bf7e8f542 100644 --- a/web/app/components/workflow/__tests__/trigger-status-sync.test.tsx +++ b/web/app/components/workflow/__tests__/trigger-status-sync.test.tsx @@ -1,10 +1,10 @@ import type { MockedFunction } from 'vitest' -import React, { useCallback } from 'react' +import type { EntryNodeStatus } from '../store/trigger-status' +import type { BlockEnum } from '../types' import { act, render } from '@testing-library/react' +import React, { useCallback } from 'react' import { useTriggerStatusStore } from '../store/trigger-status' import { isTriggerNode } from '../types' -import type { BlockEnum } from '../types' -import type { EntryNodeStatus } from '../store/trigger-status' // Mock the isTriggerNode function while preserving BlockEnum vi.mock('../types', async importOriginal => ({ @@ -25,7 +25,9 @@ const TestTriggerNode: React.FC<{ return ( <div data-testid={`node-${nodeId}`} data-status={triggerStatus}> - Status: {triggerStatus} + Status: + {' '} + {triggerStatus} </div> ) } @@ -274,14 +276,14 @@ describe('Trigger Status Synchronization Integration', () => { nodeType: string }> = ({ nodeId, nodeType }) => { const triggerStatusSelector = useCallback((state: any) => - mockIsTriggerNode(nodeType as BlockEnum) ? (state.triggerStatuses[nodeId] || 'disabled') : 'enabled', - [nodeId, nodeType], - ) + mockIsTriggerNode(nodeType as BlockEnum) ? (state.triggerStatuses[nodeId] || 'disabled') : 'enabled', [nodeId, nodeType]) const triggerStatus = useTriggerStatusStore(triggerStatusSelector) return ( <div data-testid={`optimized-node-${nodeId}`} data-status={triggerStatus}> - Status: {triggerStatus} + Status: + {' '} + {triggerStatus} </div> ) } @@ -315,9 +317,10 @@ describe('Trigger Status Synchronization Integration', () => { mockIsTriggerNode.mockImplementation(nodeType => nodeType === 'trigger-webhook') const TestComponent: React.FC<{ nodeType: string }> = ({ nodeType }) => { - const triggerStatusSelector = useCallback((state: any) => - mockIsTriggerNode(nodeType as BlockEnum) ? (state.triggerStatuses['test-node'] || 'disabled') : 'enabled', - ['test-node', nodeType], // Dependencies should match implementation + const triggerStatusSelector = useCallback( + (state: any) => + mockIsTriggerNode(nodeType as BlockEnum) ? (state.triggerStatuses['test-node'] || 'disabled') : 'enabled', + ['test-node', nodeType], // Dependencies should match implementation ) const status = useTriggerStatusStore(triggerStatusSelector) return <div data-testid="test-component" data-status={status} /> diff --git a/web/app/components/workflow/block-icon.tsx b/web/app/components/workflow/block-icon.tsx index 3c66d07364..32b06d475f 100644 --- a/web/app/components/workflow/block-icon.tsx +++ b/web/app/components/workflow/block-icon.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import { memo } from 'react' -import { BlockEnum } from './types' +import AppIcon from '@/app/components/base/app-icon' import { Agent, Answer, @@ -26,14 +26,14 @@ import { VariableX, WebhookLine, } from '@/app/components/base/icons/src/vender/workflow' -import AppIcon from '@/app/components/base/app-icon' import { cn } from '@/utils/classnames' +import { BlockEnum } from './types' type BlockIconProps = { type: BlockEnum size?: string className?: string - toolIcon?: string | { content: string; background: string } + toolIcon?: string | { content: string, background: string } } const ICON_CONTAINER_CLASSNAME_SIZE_MAP: Record<string, string> = { xs: 'w-4 h-4 rounded-[5px] shadow-xs', @@ -125,15 +125,14 @@ const BlockIcon: FC<BlockIconProps> = ({ showDefaultIcon && ICON_CONTAINER_BG_COLOR_MAP[type], toolIcon && '!shadow-none', className, - )} + ) + } > { showDefaultIcon && ( - getIcon(type, - (type === BlockEnum.TriggerSchedule || type === BlockEnum.TriggerWebhook) - ? (size === 'xs' ? 'w-4 h-4' : 'w-4.5 h-4.5') - : (size === 'xs' ? 'w-3 h-3' : 'w-3.5 h-3.5'), - ) + getIcon(type, (type === BlockEnum.TriggerSchedule || type === BlockEnum.TriggerWebhook) + ? (size === 'xs' ? 'w-4 h-4' : 'w-4.5 h-4.5') + : (size === 'xs' ? 'w-3 h-3' : 'w-3.5 h-3.5')) ) } { @@ -142,21 +141,22 @@ const BlockIcon: FC<BlockIconProps> = ({ { typeof toolIcon === 'string' ? ( - <div - className='h-full w-full shrink-0 rounded-md bg-cover bg-center' - style={{ - backgroundImage: `url(${toolIcon})`, - }} - ></div> - ) + <div + className="h-full w-full shrink-0 rounded-md bg-cover bg-center" + style={{ + backgroundImage: `url(${toolIcon})`, + }} + > + </div> + ) : ( - <AppIcon - className='!h-full !w-full shrink-0' - size='tiny' - icon={toolIcon?.content} - background={toolIcon?.background} - /> - ) + <AppIcon + className="!h-full !w-full shrink-0" + size="tiny" + icon={toolIcon?.content} + background={toolIcon?.background} + /> + ) } </> ) diff --git a/web/app/components/workflow/block-selector/all-start-blocks.tsx b/web/app/components/workflow/block-selector/all-start-blocks.tsx index e073113c05..44d06a0084 100644 --- a/web/app/components/workflow/block-selector/all-start-blocks.tsx +++ b/web/app/components/workflow/block-selector/all-start-blocks.tsx @@ -1,4 +1,12 @@ 'use client' +import type { + RefObject, +} from 'react' +import type { BlockEnum, OnSelectBlock } from '../types' +import type { ListRef } from './market-place-plugin/list' +import type { TriggerDefaultValue, TriggerWithProvider } from './types' +import { RiArrowRightUpLine } from '@remixicon/react' +import Link from 'next/link' import { useCallback, useEffect, @@ -6,30 +14,23 @@ import { useRef, useState, } from 'react' -import type { - RefObject, -} from 'react' import { useTranslation } from 'react-i18next' -import type { BlockEnum, OnSelectBlock } from '../types' -import type { TriggerDefaultValue, TriggerWithProvider } from './types' +import Button from '@/app/components/base/button' +import Divider from '@/app/components/base/divider' +import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general' +import { useGlobalPublicStore } from '@/context/global-public-context' +import { useFeaturedTriggersRecommendations } from '@/service/use-plugins' +import { useAllTriggerPlugins, useInvalidateAllTriggerPlugins } from '@/service/use-triggers' +import { cn } from '@/utils/classnames' +import { getMarketplaceUrl } from '@/utils/var' +import { useMarketplacePlugins } from '../../plugins/marketplace/hooks' +import { PluginCategoryEnum } from '../../plugins/types' +import { BlockEnum as BlockEnumValue } from '../types' +import { ENTRY_NODE_TYPES } from './constants' +import FeaturedTriggers from './featured-triggers' +import PluginList from './market-place-plugin/list' import StartBlocks from './start-blocks' import TriggerPluginList from './trigger-plugin/list' -import { ENTRY_NODE_TYPES } from './constants' -import { cn } from '@/utils/classnames' -import Link from 'next/link' -import { RiArrowRightUpLine } from '@remixicon/react' -import { getMarketplaceUrl } from '@/utils/var' -import Button from '@/app/components/base/button' -import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general' -import { BlockEnum as BlockEnumValue } from '../types' -import FeaturedTriggers from './featured-triggers' -import Divider from '@/app/components/base/divider' -import { useGlobalPublicStore } from '@/context/global-public-context' -import { useAllTriggerPlugins, useInvalidateAllTriggerPlugins } from '@/service/use-triggers' -import { useFeaturedTriggersRecommendations } from '@/service/use-plugins' -import { PluginCategoryEnum } from '../../plugins/types' -import { useMarketplacePlugins } from '../../plugins/marketplace/hooks' -import PluginList, { type ListRef } from './market-place-plugin/list' const marketplaceFooterClassName = 'system-sm-medium z-10 flex h-8 flex-none cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg' @@ -114,7 +115,8 @@ const AllStartBlocks = ({ }, [enableTriggerPlugin, hasPluginContent]) useEffect(() => { - if (!enableTriggerPlugin || !enable_marketplace) return + if (!enableTriggerPlugin || !enable_marketplace) + return if (hasFilter) { fetchPlugins({ query: searchText, @@ -126,10 +128,10 @@ const AllStartBlocks = ({ return ( <div className={cn('min-w-[400px] max-w-[500px]', className)}> - <div className='flex max-h-[640px] flex-col'> + <div className="flex max-h-[640px] flex-col"> <div ref={wrapElemRef} - className='flex-1 overflow-y-auto' + className="flex-1 overflow-y-auto" onScroll={() => pluginRef.current?.handleScroll()} > <div className={cn(shouldShowEmptyState && 'hidden')}> @@ -144,14 +146,14 @@ const AllStartBlocks = ({ invalidateTriggers() }} /> - <div className='px-3'> - <Divider className='!h-px' /> + <div className="px-3"> + <Divider className="!h-px" /> </div> </> )} {shouldShowTriggerListTitle && ( - <div className='px-3 pb-1 pt-2'> - <span className='system-xs-medium text-text-primary'>{t('workflow.tabs.allTriggers')}</span> + <div className="px-3 pb-1 pt-2"> + <span className="system-xs-medium text-text-primary">{t('workflow.tabs.allTriggers')}</span> </div> )} <StartBlocks @@ -184,19 +186,19 @@ const AllStartBlocks = ({ </div> {shouldShowEmptyState && ( - <div className='flex h-full flex-col items-center justify-center gap-3 py-12 text-center'> - <SearchMenu className='h-8 w-8 text-text-quaternary' /> - <div className='text-sm font-medium text-text-secondary'> + <div className="flex h-full flex-col items-center justify-center gap-3 py-12 text-center"> + <SearchMenu className="h-8 w-8 text-text-quaternary" /> + <div className="text-sm font-medium text-text-secondary"> {t('workflow.tabs.noPluginsFound')} </div> <Link - href='https://github.com/langgenius/dify-plugins/issues/new?template=plugin_request.yaml' - target='_blank' + href="https://github.com/langgenius/dify-plugins/issues/new?template=plugin_request.yaml" + target="_blank" > <Button - size='small' - variant='secondary-accent' - className='h-6 cursor-pointer px-3 text-xs' + size="small" + variant="secondary-accent" + className="h-6 cursor-pointer px-3 text-xs" > {t('workflow.tabs.requestToCommunity')} </Button> @@ -210,10 +212,10 @@ const AllStartBlocks = ({ <Link className={marketplaceFooterClassName} href={getMarketplaceUrl('', { category: PluginCategoryEnum.trigger })} - target='_blank' + target="_blank" > <span>{t('plugin.findMoreInMarketplace')}</span> - <RiArrowRightUpLine className='ml-0.5 h-3 w-3' /> + <RiArrowRightUpLine className="ml-0.5 h-3 w-3" /> </Link> )} </div> diff --git a/web/app/components/workflow/block-selector/all-tools.tsx b/web/app/components/workflow/block-selector/all-tools.tsx index 8968a01557..060f8849d5 100644 --- a/web/app/components/workflow/block-selector/all-tools.tsx +++ b/web/app/components/workflow/block-selector/all-tools.tsx @@ -3,34 +3,34 @@ import type { RefObject, SetStateAction, } from 'react' -import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { useTranslation } from 'react-i18next' +import type { Plugin } from '../../plugins/types' import type { BlockEnum, ToolWithProvider, } from '../types' import type { ToolDefaultValue, ToolValue } from './types' -import { ToolTypeEnum } from './types' -import Tools from './tools' -import { useToolTabs } from './hooks' -import ViewTypeSelect, { ViewType } from './view-type-select' -import { cn } from '@/utils/classnames' -import Button from '@/app/components/base/button' -import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general' -import type { ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list' -import PluginList, { type ListProps } from '@/app/components/workflow/block-selector/market-place-plugin/list' -import type { Plugin } from '../../plugins/types' -import { PluginCategoryEnum } from '../../plugins/types' -import { useMarketplacePlugins } from '../../plugins/marketplace/hooks' -import { useGlobalPublicStore } from '@/context/global-public-context' -import RAGToolRecommendations from './rag-tool-recommendations' -import FeaturedTools from './featured-tools' -import Link from 'next/link' -import Divider from '@/app/components/base/divider' -import { RiArrowRightUpLine } from '@remixicon/react' -import { getMarketplaceUrl } from '@/utils/var' -import { useGetLanguage } from '@/context/i18n' +import type { ListProps, ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list' import type { OnSelectBlock } from '@/app/components/workflow/types' +import { RiArrowRightUpLine } from '@remixicon/react' +import Link from 'next/link' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' +import Divider from '@/app/components/base/divider' +import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general' +import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list' +import { useGlobalPublicStore } from '@/context/global-public-context' +import { useGetLanguage } from '@/context/i18n' +import { cn } from '@/utils/classnames' +import { getMarketplaceUrl } from '@/utils/var' +import { useMarketplacePlugins } from '../../plugins/marketplace/hooks' +import { PluginCategoryEnum } from '../../plugins/types' +import FeaturedTools from './featured-tools' +import { useToolTabs } from './hooks' +import RAGToolRecommendations from './rag-tool-recommendations' +import Tools from './tools' +import { ToolTypeEnum } from './types' +import ViewTypeSelect, { ViewType } from './view-type-select' const marketplaceFooterClassName = 'system-sm-medium z-10 flex h-8 flex-none cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg' @@ -172,7 +172,8 @@ const AllTools = ({ const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) useEffect(() => { - if (!enable_marketplace) return + if (!enable_marketplace) + return if (hasFilter) { fetchPlugins({ query: searchText, @@ -205,8 +206,8 @@ const AllTools = ({ return ( <div className={cn('max-w-[500px]', className)}> - <div className='flex items-center justify-between border-b border-divider-subtle px-3'> - <div className='flex h-8 items-center space-x-1'> + <div className="flex items-center justify-between border-b border-divider-subtle px-3"> + <div className="flex h-8 items-center space-x-1"> { tabs.map(tab => ( <div @@ -227,10 +228,10 @@ const AllTools = ({ <ViewTypeSelect viewType={activeView} onChange={setActiveView} /> )} </div> - <div className='flex max-h-[464px] flex-col'> + <div className="flex max-h-[464px] flex-col"> <div ref={wrapElemRef} - className='flex-1 overflow-y-auto' + className="flex-1 overflow-y-auto" onScroll={() => pluginRef.current?.handleScroll()} > <div className={cn(shouldShowEmptyState && 'hidden')}> @@ -254,15 +255,15 @@ const AllTools = ({ await onFeaturedInstallSuccess?.() }} /> - <div className='px-3'> - <Divider className='!h-px' /> + <div className="px-3"> + <Divider className="!h-px" /> </div> </> )} {hasToolsListContent && ( <> - <div className='px-3 pb-1 pt-2'> - <span className='system-xs-medium text-text-primary'>{t('tools.allTools')}</span> + <div className="px-3 pb-1 pt-2"> + <span className="system-xs-medium text-text-primary">{t('tools.allTools')}</span> </div> <Tools className={toolContentClassName} @@ -293,19 +294,19 @@ const AllTools = ({ </div> {shouldShowEmptyState && ( - <div className='flex h-full flex-col items-center justify-center gap-3 py-12 text-center'> - <SearchMenu className='h-8 w-8 text-text-quaternary' /> - <div className='text-sm font-medium text-text-secondary'> + <div className="flex h-full flex-col items-center justify-center gap-3 py-12 text-center"> + <SearchMenu className="h-8 w-8 text-text-quaternary" /> + <div className="text-sm font-medium text-text-secondary"> {t('workflow.tabs.noPluginsFound')} </div> <Link - href='https://github.com/langgenius/dify-plugins/issues/new?template=plugin_request.yaml' - target='_blank' + href="https://github.com/langgenius/dify-plugins/issues/new?template=plugin_request.yaml" + target="_blank" > <Button - size='small' - variant='secondary-accent' - className='h-6 cursor-pointer px-3 text-xs' + size="small" + variant="secondary-accent" + className="h-6 cursor-pointer px-3 text-xs" > {t('workflow.tabs.requestToCommunity')} </Button> @@ -317,10 +318,10 @@ const AllTools = ({ <Link className={marketplaceFooterClassName} href={getMarketplaceUrl('', { category: PluginCategoryEnum.tool })} - target='_blank' + target="_blank" > <span>{t('plugin.findMoreInMarketplace')}</span> - <RiArrowRightUpLine className='ml-0.5 h-3 w-3' /> + <RiArrowRightUpLine className="ml-0.5 h-3 w-3" /> </Link> )} </div> diff --git a/web/app/components/workflow/block-selector/blocks.tsx b/web/app/components/workflow/block-selector/blocks.tsx index 16256bf345..e626073a3a 100644 --- a/web/app/components/workflow/block-selector/blocks.tsx +++ b/web/app/components/workflow/block-selector/blocks.tsx @@ -1,18 +1,18 @@ +import type { NodeDefault } from '../types' +import { groupBy } from 'lodash-es' import { memo, useCallback, useMemo, } from 'react' -import { useStoreApi } from 'reactflow' import { useTranslation } from 'react-i18next' -import { groupBy } from 'lodash-es' +import { useStoreApi } from 'reactflow' +import Badge from '@/app/components/base/badge' +import Tooltip from '@/app/components/base/tooltip' import BlockIcon from '../block-icon' import { BlockEnum } from '../types' -import type { NodeDefault } from '../types' import { BLOCK_CLASSIFICATIONS } from './constants' import { useBlocks } from './hooks' -import Tooltip from '@/app/components/base/tooltip' -import Badge from '@/app/components/base/badge' type BlocksProps = { searchText: string @@ -50,9 +50,10 @@ const Blocks = ({ const list = (grouped[classification] || []).filter((block) => { // Filter out trigger types from Blocks tab if (block.metaData.type === BlockEnum.TriggerWebhook - || block.metaData.type === BlockEnum.TriggerSchedule - || block.metaData.type === BlockEnum.TriggerPlugin) + || block.metaData.type === BlockEnum.TriggerSchedule + || block.metaData.type === BlockEnum.TriggerPlugin) { return false + } return block.metaData.title.toLowerCase().includes(searchText.toLowerCase()) && availableBlocksTypes.includes(block.metaData.type) }) @@ -79,11 +80,11 @@ const Blocks = ({ return ( <div key={classification} - className='mb-1 last-of-type:mb-0' + className="mb-1 last-of-type:mb-0" > { classification !== '-' && !!filteredList.length && ( - <div className='flex h-[22px] items-start px-3 text-xs font-medium text-text-tertiary'> + <div className="flex h-[22px] items-start px-3 text-xs font-medium text-text-tertiary"> {t(`workflow.tabs.${classification}`)} </div> ) @@ -92,36 +93,36 @@ const Blocks = ({ filteredList.map(block => ( <Tooltip key={block.metaData.type} - position='right' - popupClassName='w-[200px] rounded-xl' + position="right" + popupClassName="w-[200px] rounded-xl" needsDelay={false} popupContent={( <div> <BlockIcon - size='md' - className='mb-2' + size="md" + className="mb-2" type={block.metaData.type} /> - <div className='system-md-medium mb-1 text-text-primary'>{block.metaData.title}</div> - <div className='system-xs-regular text-text-tertiary'>{block.metaData.description}</div> + <div className="system-md-medium mb-1 text-text-primary">{block.metaData.title}</div> + <div className="system-xs-regular text-text-tertiary">{block.metaData.description}</div> </div> )} > <div key={block.metaData.type} - className='flex h-8 w-full cursor-pointer items-center rounded-lg px-3 hover:bg-state-base-hover' + className="flex h-8 w-full cursor-pointer items-center rounded-lg px-3 hover:bg-state-base-hover" onClick={() => onSelect(block.metaData.type)} > <BlockIcon - className='mr-2 shrink-0' + className="mr-2 shrink-0" type={block.metaData.type} /> - <div className='grow text-sm text-text-secondary'>{block.metaData.title}</div> + <div className="grow text-sm text-text-secondary">{block.metaData.title}</div> { block.metaData.type === BlockEnum.LoopEnd && ( <Badge text={t('workflow.nodes.loop.loopNode')} - className='ml-2 shrink-0' + className="ml-2 shrink-0" /> ) } @@ -134,10 +135,10 @@ const Blocks = ({ }, [groups, onSelect, t, store]) return ( - <div className='max-h-[480px] max-w-[500px] overflow-y-auto p-1'> + <div className="max-h-[480px] max-w-[500px] overflow-y-auto p-1"> { isEmpty && ( - <div className='flex h-[22px] items-center px-3 text-xs font-medium text-text-tertiary'>{t('workflow.tabs.noResult')}</div> + <div className="flex h-[22px] items-center px-3 text-xs font-medium text-text-tertiary">{t('workflow.tabs.noResult')}</div> ) } { diff --git a/web/app/components/workflow/block-selector/data-sources.tsx b/web/app/components/workflow/block-selector/data-sources.tsx index c354208dee..5204f4e86c 100644 --- a/web/app/components/workflow/block-selector/data-sources.tsx +++ b/web/app/components/workflow/block-selector/data-sources.tsx @@ -1,24 +1,25 @@ +import type { + OnSelectBlock, + ToolWithProvider, +} from '../types' +import type { DataSourceDefaultValue, ToolDefaultValue } from './types' +import type { ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list' import { useCallback, useEffect, useMemo, useRef, } from 'react' -import { BlockEnum } from '../types' -import type { - OnSelectBlock, - ToolWithProvider, -} from '../types' -import type { DataSourceDefaultValue, ToolDefaultValue } from './types' -import Tools from './tools' -import { ViewType } from './view-type-select' -import { cn } from '@/utils/classnames' -import PluginList, { type ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list' +import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list' import { useGlobalPublicStore } from '@/context/global-public-context' -import { DEFAULT_FILE_EXTENSIONS_IN_LOCAL_FILE_DATA_SOURCE } from './constants' +import { useGetLanguage } from '@/context/i18n' +import { cn } from '@/utils/classnames' import { useMarketplacePlugins } from '../../plugins/marketplace/hooks' import { PluginCategoryEnum } from '../../plugins/types' -import { useGetLanguage } from '@/context/i18n' +import { BlockEnum } from '../types' +import { DEFAULT_FILE_EXTENSIONS_IN_LOCAL_FILE_DATA_SOURCE } from './constants' +import Tools from './tools' +import { ViewType } from './view-type-select' type AllToolsProps = { className?: string @@ -83,7 +84,8 @@ const DataSources = ({ } = useMarketplacePlugins() useEffect(() => { - if (!enable_marketplace) return + if (!enable_marketplace) + return if (searchText) { fetchPlugins({ query: searchText, @@ -96,7 +98,7 @@ const DataSources = ({ <div className={cn('w-[400px] min-w-0 max-w-full', className)}> <div ref={wrapElemRef} - className='max-h-[464px] overflow-y-auto overflow-x-hidden' + className="max-h-[464px] overflow-y-auto overflow-x-hidden" onScroll={pluginRef.current?.handleScroll} > <Tools diff --git a/web/app/components/workflow/block-selector/featured-tools.tsx b/web/app/components/workflow/block-selector/featured-tools.tsx index fe5c561362..1e6b976739 100644 --- a/web/app/components/workflow/block-selector/featured-tools.tsx +++ b/web/app/components/workflow/block-selector/featured-tools.tsx @@ -1,23 +1,24 @@ 'use client' -import { useEffect, useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { BlockEnum, type ToolWithProvider } from '../types' +import type { ToolWithProvider } from '../types' import type { ToolDefaultValue, ToolValue } from './types' import type { Plugin } from '@/app/components/plugins/types' -import { useGetLanguage } from '@/context/i18n' -import BlockIcon from '../block-icon' -import Tooltip from '@/app/components/base/tooltip' import { RiMoreLine } from '@remixicon/react' -import Loading from '@/app/components/base/loading' import Link from 'next/link' +import { useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { ArrowDownDoubleLine, ArrowDownRoundFill, ArrowUpDoubleLine } from '@/app/components/base/icons/src/vender/solid/arrows' +import Loading from '@/app/components/base/loading' +import Tooltip from '@/app/components/base/tooltip' +import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' +import Action from '@/app/components/workflow/block-selector/market-place-plugin/action' +import { useGetLanguage } from '@/context/i18n' +import { formatNumber } from '@/utils/format' import { getMarketplaceUrl } from '@/utils/var' +import BlockIcon from '../block-icon' +import { BlockEnum } from '../types' +import Tools from './tools' import { ToolTypeEnum } from './types' import { ViewType } from './view-type-select' -import Tools from './tools' -import { formatNumber } from '@/utils/format' -import Action from '@/app/components/workflow/block-selector/market-place-plugin/action' -import { ArrowDownDoubleLine, ArrowDownRoundFill, ArrowUpDoubleLine } from '@/app/components/base/icons/src/vender/solid/arrows' -import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' const MAX_RECOMMENDED_COUNT = 15 const INITIAL_VISIBLE_COUNT = 5 @@ -125,27 +126,27 @@ const FeaturedTools = ({ const showEmptyState = !isLoading && totalVisible === 0 return ( - <div className='px-3 pb-3 pt-2'> + <div className="px-3 pb-3 pt-2"> <button - type='button' - className='flex w-full items-center rounded-md px-0 py-1 text-left text-text-primary' + type="button" + className="flex w-full items-center rounded-md px-0 py-1 text-left text-text-primary" onClick={() => setIsCollapsed(prev => !prev)} > - <span className='system-xs-medium text-text-primary'>{t('workflow.tabs.featuredTools')}</span> + <span className="system-xs-medium text-text-primary">{t('workflow.tabs.featuredTools')}</span> <ArrowDownRoundFill className={`ml-0.5 h-4 w-4 text-text-tertiary transition-transform ${isCollapsed ? '-rotate-90' : 'rotate-0'}`} /> </button> {!isCollapsed && ( <> {isLoading && ( - <div className='py-3'> - <Loading type='app' /> + <div className="py-3"> + <Loading type="app" /> </div> )} {showEmptyState && ( - <p className='system-xs-regular py-2 text-text-tertiary'> - <Link className='text-text-accent' href={getMarketplaceUrl('', { category: 'tool' })} target='_blank' rel='noopener noreferrer'> + <p className="system-xs-regular py-2 text-text-tertiary"> + <Link className="text-text-accent" href={getMarketplaceUrl('', { category: 'tool' })} target="_blank" rel="noopener noreferrer"> {t('workflow.tabs.noFeaturedPlugins')} </Link> </p> @@ -155,7 +156,7 @@ const FeaturedTools = ({ <> {visibleInstalledProviders.length > 0 && ( <Tools - className='p-0' + className="p-0" tools={visibleInstalledProviders} onSelect={onSelect} canNotSelectMultiple @@ -168,7 +169,7 @@ const FeaturedTools = ({ )} {visibleUninstalledPlugins.length > 0 && ( - <div className='mt-1 flex flex-col gap-1'> + <div className="mt-1 flex flex-col gap-1"> {visibleUninstalledPlugins.map(plugin => ( <FeaturedToolUninstalledItem key={plugin.plugin_id} @@ -187,7 +188,7 @@ const FeaturedTools = ({ {!isLoading && totalVisible > 0 && canToggleVisibility && ( <div - className='group mt-1 flex cursor-pointer items-center gap-x-2 rounded-lg py-1 pl-3 pr-2 text-text-tertiary transition-colors hover:bg-state-base-hover hover:text-text-secondary' + className="group mt-1 flex cursor-pointer items-center gap-x-2 rounded-lg py-1 pl-3 pr-2 text-text-tertiary transition-colors hover:bg-state-base-hover hover:text-text-secondary" onClick={() => { setVisibleCount((count) => { if (count >= maxAvailable) @@ -197,15 +198,17 @@ const FeaturedTools = ({ }) }} > - <div className='flex items-center px-1 text-text-tertiary transition-colors group-hover:text-text-secondary'> - <RiMoreLine className='size-4 group-hover:hidden' /> - {isExpanded ? ( - <ArrowUpDoubleLine className='hidden size-4 group-hover:block' /> - ) : ( - <ArrowDownDoubleLine className='hidden size-4 group-hover:block' /> - )} + <div className="flex items-center px-1 text-text-tertiary transition-colors group-hover:text-text-secondary"> + <RiMoreLine className="size-4 group-hover:hidden" /> + {isExpanded + ? ( + <ArrowUpDoubleLine className="hidden size-4 group-hover:block" /> + ) + : ( + <ArrowDownDoubleLine className="hidden size-4 group-hover:block" /> + )} </div> - <div className='system-xs-regular'> + <div className="system-xs-regular"> {t(isExpanded ? 'workflow.tabs.showLessFeatured' : 'workflow.tabs.showMoreFeatured')} </div> </div> @@ -255,28 +258,28 @@ function FeaturedToolUninstalledItem({ return ( <> <Tooltip - position='right' + position="right" needsDelay={false} - popupClassName='!p-0 !px-3 !py-2.5 !w-[224px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !rounded-xl !shadow-lg' + popupClassName="!p-0 !px-3 !py-2.5 !w-[224px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !rounded-xl !shadow-lg" popupContent={( <div> - <BlockIcon size='md' className='mb-2' type={BlockEnum.Tool} toolIcon={plugin.icon} /> - <div className='mb-1 text-sm leading-5 text-text-primary'>{label}</div> - <div className='text-xs leading-[18px] text-text-secondary'>{description}</div> + <BlockIcon size="md" className="mb-2" type={BlockEnum.Tool} toolIcon={plugin.icon} /> + <div className="mb-1 text-sm leading-5 text-text-primary">{label}</div> + <div className="text-xs leading-[18px] text-text-secondary">{description}</div> </div> )} disabled={!description || isActionHovered || actionOpen || isInstallModalOpen} > <div - className='group flex h-8 w-full items-center rounded-lg pl-3 pr-1 hover:bg-state-base-hover' + className="group flex h-8 w-full items-center rounded-lg pl-3 pr-1 hover:bg-state-base-hover" > - <div className='flex h-full min-w-0 items-center'> + <div className="flex h-full min-w-0 items-center"> <BlockIcon type={BlockEnum.Tool} toolIcon={plugin.icon} /> - <div className='ml-2 min-w-0'> - <div className='system-sm-medium truncate text-text-secondary'>{label}</div> + <div className="ml-2 min-w-0"> + <div className="system-sm-medium truncate text-text-secondary">{label}</div> </div> </div> - <div className='ml-auto flex h-full items-center gap-1 pl-1'> + <div className="ml-auto flex h-full items-center gap-1 pl-1"> <span className={`system-xs-regular text-text-tertiary ${actionOpen ? 'hidden' : 'group-hover:hidden'}`}>{installCountLabel}</span> <div className={`system-xs-medium flex h-full items-center gap-1 text-components-button-secondary-accent-text [&_.action-btn]:h-6 [&_.action-btn]:min-h-0 [&_.action-btn]:w-6 [&_.action-btn]:rounded-lg [&_.action-btn]:p-0 ${actionOpen ? 'flex' : 'hidden group-hover:flex'}`} @@ -287,8 +290,8 @@ function FeaturedToolUninstalledItem({ }} > <button - type='button' - className='cursor-pointer rounded-md px-1.5 py-0.5 hover:bg-state-base-hover' + type="button" + className="cursor-pointer rounded-md px-1.5 py-0.5 hover:bg-state-base-hover" onClick={() => { setActionOpen(false) setIsInstallModalOpen(true) diff --git a/web/app/components/workflow/block-selector/featured-triggers.tsx b/web/app/components/workflow/block-selector/featured-triggers.tsx index 561ebc1784..c986a8abc0 100644 --- a/web/app/components/workflow/block-selector/featured-triggers.tsx +++ b/web/app/components/workflow/block-selector/featured-triggers.tsx @@ -1,21 +1,21 @@ 'use client' -import { useEffect, useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { BlockEnum } from '../types' import type { TriggerDefaultValue, TriggerWithProvider } from './types' import type { Plugin } from '@/app/components/plugins/types' -import { useGetLanguage } from '@/context/i18n' -import BlockIcon from '../block-icon' -import Tooltip from '@/app/components/base/tooltip' import { RiMoreLine } from '@remixicon/react' -import Loading from '@/app/components/base/loading' import Link from 'next/link' -import { getMarketplaceUrl } from '@/utils/var' -import TriggerPluginItem from './trigger-plugin/item' -import { formatNumber } from '@/utils/format' -import Action from '@/app/components/workflow/block-selector/market-place-plugin/action' +import { useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' import { ArrowDownDoubleLine, ArrowDownRoundFill, ArrowUpDoubleLine } from '@/app/components/base/icons/src/vender/solid/arrows' +import Loading from '@/app/components/base/loading' +import Tooltip from '@/app/components/base/tooltip' import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' +import Action from '@/app/components/workflow/block-selector/market-place-plugin/action' +import { useGetLanguage } from '@/context/i18n' +import { formatNumber } from '@/utils/format' +import { getMarketplaceUrl } from '@/utils/var' +import BlockIcon from '../block-icon' +import { BlockEnum } from '../types' +import TriggerPluginItem from './trigger-plugin/item' const MAX_RECOMMENDED_COUNT = 15 const INITIAL_VISIBLE_COUNT = 5 @@ -119,27 +119,27 @@ const FeaturedTriggers = ({ const showEmptyState = !isLoading && totalVisible === 0 return ( - <div className='px-3 pb-3 pt-2'> + <div className="px-3 pb-3 pt-2"> <button - type='button' - className='flex w-full items-center rounded-md px-0 py-1 text-left text-text-primary' + type="button" + className="flex w-full items-center rounded-md px-0 py-1 text-left text-text-primary" onClick={() => setIsCollapsed(prev => !prev)} > - <span className='system-xs-medium text-text-primary'>{t('workflow.tabs.featuredTools')}</span> + <span className="system-xs-medium text-text-primary">{t('workflow.tabs.featuredTools')}</span> <ArrowDownRoundFill className={`ml-0.5 h-4 w-4 text-text-tertiary transition-transform ${isCollapsed ? '-rotate-90' : 'rotate-0'}`} /> </button> {!isCollapsed && ( <> {isLoading && ( - <div className='py-3'> - <Loading type='app' /> + <div className="py-3"> + <Loading type="app" /> </div> )} {showEmptyState && ( - <p className='system-xs-regular py-2 text-text-tertiary'> - <Link className='text-text-accent' href={getMarketplaceUrl('', { category: 'trigger' })} target='_blank' rel='noopener noreferrer'> + <p className="system-xs-regular py-2 text-text-tertiary"> + <Link className="text-text-accent" href={getMarketplaceUrl('', { category: 'trigger' })} target="_blank" rel="noopener noreferrer"> {t('workflow.tabs.noFeaturedTriggers')} </Link> </p> @@ -148,7 +148,7 @@ const FeaturedTriggers = ({ {!showEmptyState && !isLoading && ( <> {visibleInstalledProviders.length > 0 && ( - <div className='mt-1'> + <div className="mt-1"> {visibleInstalledProviders.map(provider => ( <TriggerPluginItem key={provider.id} @@ -161,7 +161,7 @@ const FeaturedTriggers = ({ )} {visibleUninstalledPlugins.length > 0 && ( - <div className='mt-1 flex flex-col gap-1'> + <div className="mt-1 flex flex-col gap-1"> {visibleUninstalledPlugins.map(plugin => ( <FeaturedTriggerUninstalledItem key={plugin.plugin_id} @@ -180,7 +180,7 @@ const FeaturedTriggers = ({ {!isLoading && totalVisible > 0 && canToggleVisibility && ( <div - className='group mt-1 flex cursor-pointer items-center gap-x-2 rounded-lg py-1 pl-3 pr-2 text-text-tertiary transition-colors hover:bg-state-base-hover hover:text-text-secondary' + className="group mt-1 flex cursor-pointer items-center gap-x-2 rounded-lg py-1 pl-3 pr-2 text-text-tertiary transition-colors hover:bg-state-base-hover hover:text-text-secondary" onClick={() => { setVisibleCount((count) => { if (count >= maxAvailable) @@ -190,15 +190,17 @@ const FeaturedTriggers = ({ }) }} > - <div className='flex items-center px-1 text-text-tertiary transition-colors group-hover:text-text-secondary'> - <RiMoreLine className='size-4 group-hover:hidden' /> - {isExpanded ? ( - <ArrowUpDoubleLine className='hidden size-4 group-hover:block' /> - ) : ( - <ArrowDownDoubleLine className='hidden size-4 group-hover:block' /> - )} + <div className="flex items-center px-1 text-text-tertiary transition-colors group-hover:text-text-secondary"> + <RiMoreLine className="size-4 group-hover:hidden" /> + {isExpanded + ? ( + <ArrowUpDoubleLine className="hidden size-4 group-hover:block" /> + ) + : ( + <ArrowDownDoubleLine className="hidden size-4 group-hover:block" /> + )} </div> - <div className='system-xs-regular'> + <div className="system-xs-regular"> {t(isExpanded ? 'workflow.tabs.showLessFeatured' : 'workflow.tabs.showMoreFeatured')} </div> </div> @@ -248,28 +250,28 @@ function FeaturedTriggerUninstalledItem({ return ( <> <Tooltip - position='right' + position="right" needsDelay={false} - popupClassName='!p-0 !px-3 !py-2.5 !w-[224px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !rounded-xl !shadow-lg' + popupClassName="!p-0 !px-3 !py-2.5 !w-[224px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !rounded-xl !shadow-lg" popupContent={( <div> - <BlockIcon size='md' className='mb-2' type={BlockEnum.TriggerPlugin} toolIcon={plugin.icon} /> - <div className='mb-1 text-sm leading-5 text-text-primary'>{label}</div> - <div className='text-xs leading-[18px] text-text-secondary'>{description}</div> + <BlockIcon size="md" className="mb-2" type={BlockEnum.TriggerPlugin} toolIcon={plugin.icon} /> + <div className="mb-1 text-sm leading-5 text-text-primary">{label}</div> + <div className="text-xs leading-[18px] text-text-secondary">{description}</div> </div> )} disabled={!description || isActionHovered || actionOpen || isInstallModalOpen} > <div - className='group flex h-8 w-full items-center rounded-lg pl-3 pr-1 hover:bg-state-base-hover' + className="group flex h-8 w-full items-center rounded-lg pl-3 pr-1 hover:bg-state-base-hover" > - <div className='flex h-full min-w-0 items-center'> + <div className="flex h-full min-w-0 items-center"> <BlockIcon type={BlockEnum.TriggerPlugin} toolIcon={plugin.icon} /> - <div className='ml-2 min-w-0'> - <div className='system-sm-medium truncate text-text-secondary'>{label}</div> + <div className="ml-2 min-w-0"> + <div className="system-sm-medium truncate text-text-secondary">{label}</div> </div> </div> - <div className='ml-auto flex h-full items-center gap-1 pl-1'> + <div className="ml-auto flex h-full items-center gap-1 pl-1"> <span className={`system-xs-regular text-text-tertiary ${actionOpen ? 'hidden' : 'group-hover:hidden'}`}>{installCountLabel}</span> <div className={`system-xs-medium flex h-full items-center gap-1 text-components-button-secondary-accent-text [&_.action-btn]:h-6 [&_.action-btn]:min-h-0 [&_.action-btn]:w-6 [&_.action-btn]:rounded-lg [&_.action-btn]:p-0 ${actionOpen ? 'flex' : 'hidden group-hover:flex'}`} @@ -280,8 +282,8 @@ function FeaturedTriggerUninstalledItem({ }} > <button - type='button' - className='cursor-pointer rounded-md px-1.5 py-0.5 hover:bg-state-base-hover' + type="button" + className="cursor-pointer rounded-md px-1.5 py-0.5 hover:bg-state-base-hover" onClick={() => { setActionOpen(false) setIsInstallModalOpen(true) diff --git a/web/app/components/workflow/block-selector/hooks.ts b/web/app/components/workflow/block-selector/hooks.ts index e2dd14e16c..075a0b7d38 100644 --- a/web/app/components/workflow/block-selector/hooks.ts +++ b/web/app/components/workflow/block-selector/hooks.ts @@ -66,8 +66,7 @@ export const useTabs = ({ key: TabsEnum.Tools, name: t('workflow.tabs.tools'), show: !noTools, - }, - { + }, { key: TabsEnum.Start, name: t('workflow.tabs.start'), show: shouldShowStartTab, diff --git a/web/app/components/workflow/block-selector/index-bar.tsx b/web/app/components/workflow/block-selector/index-bar.tsx index f9a839a982..6b7a1af936 100644 --- a/web/app/components/workflow/block-selector/index-bar.tsx +++ b/web/app/components/workflow/block-selector/index-bar.tsx @@ -1,8 +1,8 @@ -import { pinyin } from 'pinyin-pro' import type { FC, RefObject } from 'react' import type { ToolWithProvider } from '../types' -import { CollectionType } from '../../tools/types' +import { pinyin } from 'pinyin-pro' import { cn } from '@/utils/classnames' +import { CollectionType } from '../../tools/types' export const CUSTOM_GROUP_NAME = '@@@custom@@@' export const WORKFLOW_GROUP_NAME = '@@@workflow@@@' diff --git a/web/app/components/workflow/block-selector/index.tsx b/web/app/components/workflow/block-selector/index.tsx index 9f7989265a..5b9d86d6d4 100644 --- a/web/app/components/workflow/block-selector/index.tsx +++ b/web/app/components/workflow/block-selector/index.tsx @@ -1,11 +1,11 @@ +import type { NodeSelectorProps } from './main' import { useMemo, } from 'react' -import type { NodeSelectorProps } from './main' -import NodeSelector from './main' import { useHooksStore } from '@/app/components/workflow/hooks-store/store' import { BlockEnum } from '@/app/components/workflow/types' import { useStore } from '../store' +import NodeSelector from './main' const NodeSelectorWrapper = (props: NodeSelectorProps) => { const availableNodesMetaData = useHooksStore(s => s.availableNodesMetaData) diff --git a/web/app/components/workflow/block-selector/main.tsx b/web/app/components/workflow/block-selector/main.tsx index 1fff8528e2..130ed0a740 100644 --- a/web/app/components/workflow/block-selector/main.tsx +++ b/web/app/components/workflow/block-selector/main.tsx @@ -1,7 +1,17 @@ +import type { + OffsetOptions, + Placement, +} from '@floating-ui/react' import type { FC, MouseEventHandler, } from 'react' +import type { + CommonNodeType, + NodeDefault, + OnSelectBlock, + ToolWithProvider, +} from '../types' import { memo, useCallback, @@ -9,31 +19,21 @@ import { useState, } from 'react' import { useTranslation } from 'react-i18next' -import useNodes from '@/app/components/workflow/store/workflow/use-nodes' -import type { - OffsetOptions, - Placement, -} from '@floating-ui/react' -import type { - CommonNodeType, - NodeDefault, - OnSelectBlock, - ToolWithProvider, -} from '../types' -import { BlockEnum, isTriggerNode } from '../types' -import Tabs from './tabs' -import { TabsEnum } from './types' -import { useTabs } from './hooks' +import { + Plus02, +} from '@/app/components/base/icons/src/vender/line/general' +import Input from '@/app/components/base/input' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import Input from '@/app/components/base/input' -import { - Plus02, -} from '@/app/components/base/icons/src/vender/line/general' import SearchBox from '@/app/components/plugins/marketplace/search-box' +import useNodes from '@/app/components/workflow/store/workflow/use-nodes' +import { BlockEnum, isTriggerNode } from '../types' +import { useTabs } from './hooks' +import Tabs from './tabs' +import { TabsEnum } from './types' export type NodeSelectorProps = { open?: boolean @@ -190,20 +190,20 @@ const NodeSelector: FC<NodeSelectorProps> = ({ trigger ? trigger(open) : ( - <div - className={` + <div + className={` z-10 flex h-4 w-4 cursor-pointer items-center justify-center rounded-full bg-components-button-primary-bg text-text-primary-on-surface hover:bg-components-button-primary-bg-hover ${triggerClassName?.(open)} `} - style={triggerStyle} - > - <Plus02 className='h-2.5 w-2.5' /> - </div> - ) + style={triggerStyle} + > + <Plus02 className="h-2.5 w-2.5" /> + </div> + ) } </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> + <PortalToFollowElemContent className="z-[1000]"> <div className={`rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg ${popupClassName}`}> <Tabs tabs={tabs} @@ -211,8 +211,8 @@ const NodeSelector: FC<NodeSelectorProps> = ({ blocks={blocks} allowStartNodeSelection={canSelectUserInput} onActiveTabChange={handleActiveTabChange} - filterElem={ - <div className='relative m-2' onClick={e => e.stopPropagation()}> + filterElem={( + <div className="relative m-2" onClick={e => e.stopPropagation()}> {activeTab === TabsEnum.Start && ( <SearchBox autoFocus @@ -221,7 +221,7 @@ const NodeSelector: FC<NodeSelectorProps> = ({ tags={tags} onTagsChange={setTags} placeholder={searchPlaceholder} - inputClassName='grow' + inputClassName="grow" /> )} {activeTab === TabsEnum.Blocks && ( @@ -254,11 +254,11 @@ const NodeSelector: FC<NodeSelectorProps> = ({ tags={tags} onTagsChange={setTags} placeholder={t('plugin.searchTools')!} - inputClassName='grow' + inputClassName="grow" /> )} </div> - } + )} onSelect={handleSelect} searchText={searchText} tags={tags} diff --git a/web/app/components/workflow/block-selector/market-place-plugin/action.tsx b/web/app/components/workflow/block-selector/market-place-plugin/action.tsx index 3d0cc7dfe7..a23ca32b50 100644 --- a/web/app/components/workflow/block-selector/market-place-plugin/action.tsx +++ b/web/app/components/workflow/block-selector/market-place-plugin/action.tsx @@ -1,9 +1,10 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { useTheme } from 'next-themes' -import { useTranslation } from 'react-i18next' import { RiMoreFill } from '@remixicon/react' +import { useQueryClient } from '@tanstack/react-query' +import { useTheme } from 'next-themes' +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' // import Button from '@/app/components/base/button' import { @@ -11,11 +12,10 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { cn } from '@/utils/classnames' import { useDownloadPlugin } from '@/service/use-plugins' +import { cn } from '@/utils/classnames' import { downloadFile } from '@/utils/format' import { getMarketplaceUrl } from '@/utils/var' -import { useQueryClient } from '@tanstack/react-query' type Props = { open: boolean @@ -53,7 +53,8 @@ const OperationDropdown: FC<Props> = ({ }), [author, name, version]) const { data: blob, isLoading } = useDownloadPlugin(downloadInfo, needDownload) const handleDownload = useCallback(() => { - if (isLoading) return + if (isLoading) + return queryClient.removeQueries({ queryKey: ['plugins', 'downloadPlugin', downloadInfo], exact: true, @@ -76,7 +77,7 @@ const OperationDropdown: FC<Props> = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-end' + placement="bottom-end" offset={{ mainAxis: 0, crossAxis: 0, @@ -84,13 +85,13 @@ const OperationDropdown: FC<Props> = ({ > <PortalToFollowElemTrigger onClick={handleTrigger}> <ActionButton className={cn(open && 'bg-state-base-hover')}> - <RiMoreFill className='h-4 w-4 text-components-button-secondary-accent-text' /> + <RiMoreFill className="h-4 w-4 text-components-button-secondary-accent-text" /> </ActionButton> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[9999]'> - <div className='min-w-[176px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'> - <div onClick={handleDownload} className='system-md-regular cursor-pointer rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'>{t('common.operation.download')}</div> - <a href={getMarketplaceUrl(`/plugins/${author}/${name}`, { theme })} target='_blank' className='system-md-regular block cursor-pointer rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover'>{t('common.operation.viewDetails')}</a> + <PortalToFollowElemContent className="z-[9999]"> + <div className="min-w-[176px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg"> + <div onClick={handleDownload} className="system-md-regular cursor-pointer rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover">{t('common.operation.download')}</div> + <a href={getMarketplaceUrl(`/plugins/${author}/${name}`, { theme })} target="_blank" className="system-md-regular block cursor-pointer rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover">{t('common.operation.viewDetails')}</a> </div> </PortalToFollowElemContent> </PortalToFollowElem> diff --git a/web/app/components/workflow/block-selector/market-place-plugin/item.tsx b/web/app/components/workflow/block-selector/market-place-plugin/item.tsx index 711bfadc7f..18d8629260 100644 --- a/web/app/components/workflow/block-selector/market-place-plugin/item.tsx +++ b/web/app/components/workflow/block-selector/market-place-plugin/item.tsx @@ -1,16 +1,16 @@ 'use client' import type { FC } from 'react' -import React from 'react' -import { useContext } from 'use-context-selector' -import { useTranslation } from 'react-i18next' -import Action from './action' import type { Plugin } from '@/app/components/plugins/types.ts' +import { useBoolean } from 'ahooks' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { useContext } from 'use-context-selector' import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' import I18n from '@/context/i18n' import { cn } from '@/utils/classnames' import { formatNumber } from '@/utils/format' -import { useBoolean } from 'ahooks' +import Action from './action' enum ActionType { install = 'install', @@ -36,16 +36,16 @@ const Item: FC<Props> = ({ }] = useBoolean(false) return ( - <div className='group/plugin flex rounded-lg py-1 pl-3 pr-1 hover:bg-state-base-hover'> + <div className="group/plugin flex rounded-lg py-1 pl-3 pr-1 hover:bg-state-base-hover"> <div - className='relative h-6 w-6 shrink-0 rounded-md border-[0.5px] border-components-panel-border-subtle bg-contain bg-center bg-no-repeat' + className="relative h-6 w-6 shrink-0 rounded-md border-[0.5px] border-components-panel-border-subtle bg-contain bg-center bg-no-repeat" style={{ backgroundImage: `url(${payload.icon})` }} /> - <div className='ml-2 flex w-0 grow'> - <div className='w-0 grow'> - <div className='system-sm-medium h-4 truncate leading-4 text-text-primary '>{getLocalizedText(payload.label)}</div> - <div className='system-xs-regular h-5 truncate leading-5 text-text-tertiary'>{getLocalizedText(payload.brief)}</div> - <div className='system-xs-regular flex space-x-1 text-text-tertiary'> + <div className="ml-2 flex w-0 grow"> + <div className="w-0 grow"> + <div className="system-sm-medium h-4 truncate leading-4 text-text-primary ">{getLocalizedText(payload.label)}</div> + <div className="system-xs-regular h-5 truncate leading-5 text-text-tertiary">{getLocalizedText(payload.brief)}</div> + <div className="system-xs-regular flex space-x-1 text-text-tertiary"> <div>{payload.org}</div> <div>·</div> <div>{t('plugin.install', { num: formatNumber(payload.install_count || 0) })}</div> @@ -54,7 +54,7 @@ const Item: FC<Props> = ({ {/* Action */} <div className={cn(!open ? 'hidden' : 'flex', 'system-xs-medium h-4 items-center space-x-1 text-components-button-secondary-accent-text group-hover/plugin:flex')}> <div - className='cursor-pointer rounded-md px-1.5 py-0.5 hover:bg-state-base-hover' + className="cursor-pointer rounded-md px-1.5 py-0.5 hover:bg-state-base-hover" onClick={showInstallModal} > {t('plugin.installAction')} diff --git a/web/app/components/workflow/block-selector/market-place-plugin/list.tsx b/web/app/components/workflow/block-selector/market-place-plugin/list.tsx index b2097c72cf..58724b4621 100644 --- a/web/app/components/workflow/block-selector/market-place-plugin/list.tsx +++ b/web/app/components/workflow/block-selector/market-place-plugin/list.tsx @@ -1,15 +1,15 @@ 'use client' -import { useEffect, useImperativeHandle, useMemo, useRef } from 'react' import type { RefObject } from 'react' -import { useTranslation } from 'react-i18next' -import useStickyScroll, { ScrollPosition } from '../use-sticky-scroll' -import Item from './item' import type { Plugin, PluginCategoryEnum } from '@/app/components/plugins/types' -import { cn } from '@/utils/classnames' -import Link from 'next/link' import { RiArrowRightUpLine, RiSearchLine } from '@remixicon/react' import { noop } from 'lodash-es' +import Link from 'next/link' +import { useEffect, useImperativeHandle, useMemo, useRef } from 'react' +import { useTranslation } from 'react-i18next' +import { cn } from '@/utils/classnames' import { getMarketplaceUrl } from '@/utils/var' +import useStickyScroll, { ScrollPosition } from '../use-sticky-scroll' +import Item from './item' export type ListProps = { wrapElemRef: React.RefObject<HTMLElement | null> @@ -79,12 +79,12 @@ const List = ({ return ( <Link - className='system-sm-medium sticky bottom-0 z-10 flex h-8 cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg' + className="system-sm-medium sticky bottom-0 z-10 flex h-8 cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg" href={getMarketplaceUrl('', { category })} - target='_blank' + target="_blank" > <span>{t('plugin.findMoreInMarketplace')}</span> - <RiArrowRightUpLine className='ml-0.5 h-3 w-3' /> + <RiArrowRightUpLine className="ml-0.5 h-3 w-3" /> </Link> ) } @@ -101,12 +101,12 @@ const List = ({ <span>{t('plugin.fromMarketplace')}</span> <Link href={urlWithSearchText} - target='_blank' - className='flex items-center text-text-accent-light-mode-only' + target="_blank" + className="flex items-center text-text-accent-light-mode-only" onClick={e => e.stopPropagation()} > <span>{t('plugin.searchInMarketplace')}</span> - <RiArrowRightUpLine className='ml-0.5 h-3 w-3' /> + <RiArrowRightUpLine className="ml-0.5 h-3 w-3" /> </Link> </div> )} @@ -119,14 +119,14 @@ const List = ({ /> ))} {hasRes && ( - <div className='mb-3 mt-2 flex items-center justify-center space-x-2'> + <div className="mb-3 mt-2 flex items-center justify-center space-x-2"> <div className="h-[2px] w-[90px] bg-gradient-to-l from-[rgba(16,24,40,0.08)] to-[rgba(255,255,255,0.01)]"></div> <Link href={urlWithSearchText} - target='_blank' - className='system-sm-medium flex h-4 shrink-0 items-center text-text-accent-light-mode-only' + target="_blank" + className="system-sm-medium flex h-4 shrink-0 items-center text-text-accent-light-mode-only" > - <RiSearchLine className='mr-0.5 h-3 w-3' /> + <RiSearchLine className="mr-0.5 h-3 w-3" /> <span>{t('plugin.searchInMarketplace')}</span> </Link> <div className="h-[2px] w-[90px] bg-gradient-to-l from-[rgba(255,255,255,0.01)] to-[rgba(16,24,40,0.08)]"></div> diff --git a/web/app/components/workflow/block-selector/rag-tool-recommendations/index.tsx b/web/app/components/workflow/block-selector/rag-tool-recommendations/index.tsx index 47b158b9b2..e9255917aa 100644 --- a/web/app/components/workflow/block-selector/rag-tool-recommendations/index.tsx +++ b/web/app/components/workflow/block-selector/rag-tool-recommendations/index.tsx @@ -1,17 +1,17 @@ 'use client' import type { Dispatch, SetStateAction } from 'react' +import type { ViewType } from '@/app/components/workflow/block-selector/view-type-select' +import type { OnSelectBlock } from '@/app/components/workflow/types' +import { RiMoreLine } from '@remixicon/react' +import Link from 'next/link' import React, { useCallback, useEffect, useMemo, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' -import type { OnSelectBlock } from '@/app/components/workflow/types' -import type { ViewType } from '@/app/components/workflow/block-selector/view-type-select' -import { RiMoreLine } from '@remixicon/react' -import Loading from '@/app/components/base/loading' -import Link from 'next/link' -import { getMarketplaceUrl } from '@/utils/var' -import { useRAGRecommendedPlugins } from '@/service/use-tools' -import List from './list' -import { getFormattedPlugin } from '@/app/components/plugins/marketplace/utils' import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/arrows' +import Loading from '@/app/components/base/loading' +import { getFormattedPlugin } from '@/app/components/plugins/marketplace/utils' +import { useRAGRecommendedPlugins } from '@/service/use-tools' +import { getMarketplaceUrl } from '@/utils/var' +import List from './list' type RAGToolRecommendationsProps = { viewType: ViewType @@ -75,33 +75,33 @@ const RAGToolRecommendations = ({ }, [onTagsChange]) return ( - <div className='flex flex-col p-1'> + <div className="flex flex-col p-1"> <button - type='button' - className='flex w-full items-center rounded-md px-3 pb-0.5 pt-1 text-left text-text-tertiary' + type="button" + className="flex w-full items-center rounded-md px-3 pb-0.5 pt-1 text-left text-text-tertiary" onClick={() => setIsCollapsed(prev => !prev)} > - <span className='system-xs-medium text-text-tertiary'>{t('pipeline.ragToolSuggestions.title')}</span> + <span className="system-xs-medium text-text-tertiary">{t('pipeline.ragToolSuggestions.title')}</span> <ArrowDownRoundFill className={`ml-1 h-4 w-4 text-text-tertiary transition-transform ${isCollapsed ? '-rotate-90' : 'rotate-0'}`} /> </button> {!isCollapsed && ( <> {/* For first time loading, show loading */} {isLoadingRAGRecommendedPlugins && ( - <div className='py-2'> - <Loading type='app' /> + <div className="py-2"> + <Loading type="app" /> </div> )} {!isFetchingRAGRecommendedPlugins && recommendedPlugins.length === 0 && unInstalledPlugins.length === 0 && ( - <p className='system-xs-regular px-3 py-1 text-text-tertiary'> + <p className="system-xs-regular px-3 py-1 text-text-tertiary"> <Trans - i18nKey='pipeline.ragToolSuggestions.noRecommendationPlugins' + i18nKey="pipeline.ragToolSuggestions.noRecommendationPlugins" components={{ CustomLink: ( <Link - className='text-text-accent' - target='_blank' - rel='noopener noreferrer' + className="text-text-accent" + target="_blank" + rel="noopener noreferrer" href={getMarketplaceUrl('', { tags: 'rag' })} /> ), @@ -118,13 +118,13 @@ const RAGToolRecommendations = ({ viewType={viewType} /> <div - className='flex cursor-pointer items-center gap-x-2 py-1 pl-3 pr-2' + className="flex cursor-pointer items-center gap-x-2 py-1 pl-3 pr-2" onClick={loadMore} > - <div className='px-1'> - <RiMoreLine className='size-4 text-text-tertiary' /> + <div className="px-1"> + <RiMoreLine className="size-4 text-text-tertiary" /> </div> - <div className='system-xs-regular text-text-tertiary'> + <div className="system-xs-regular text-text-tertiary"> {t('common.operation.more')} </div> </div> diff --git a/web/app/components/workflow/block-selector/rag-tool-recommendations/list.tsx b/web/app/components/workflow/block-selector/rag-tool-recommendations/list.tsx index 2012d03598..a3babd0953 100644 --- a/web/app/components/workflow/block-selector/rag-tool-recommendations/list.tsx +++ b/web/app/components/workflow/block-selector/rag-tool-recommendations/list.tsx @@ -1,15 +1,15 @@ -import { useCallback, useMemo, useRef } from 'react' import type { BlockEnum, ToolWithProvider } from '../../types' import type { ToolDefaultValue } from '../types' -import { ViewType } from '../view-type-select' -import { useGetLanguage } from '@/context/i18n' -import { groupItems } from '../index-bar' -import { cn } from '@/utils/classnames' -import ToolListTreeView from '../tool/tool-list-tree-view/list' -import ToolListFlatView from '../tool/tool-list-flat-view/list' -import UninstalledItem from './uninstalled-item' import type { Plugin } from '@/app/components/plugins/types' import type { OnSelectBlock } from '@/app/components/workflow/types' +import { useCallback, useMemo, useRef } from 'react' +import { useGetLanguage } from '@/context/i18n' +import { cn } from '@/utils/classnames' +import { groupItems } from '../index-bar' +import ToolListFlatView from '../tool/tool-list-flat-view/list' +import ToolListTreeView from '../tool/tool-list-tree-view/list' +import { ViewType } from '../view-type-select' +import UninstalledItem from './uninstalled-item' type ListProps = { onSelect: OnSelectBlock @@ -67,25 +67,27 @@ const List = ({ return ( <div className={cn('max-w-[100%] p-1', className)}> {!!tools.length && ( - isFlatView ? ( - <ToolListFlatView - toolRefs={toolRefs} - letters={letters} - payload={listViewToolData} - isShowLetterIndex={false} - hasSearchText={false} - onSelect={handleSelect} - canNotSelectMultiple - indexBar={null} - /> - ) : ( - <ToolListTreeView - payload={treeViewToolsData} - hasSearchText={false} - onSelect={handleSelect} - canNotSelectMultiple - /> - ) + isFlatView + ? ( + <ToolListFlatView + toolRefs={toolRefs} + letters={letters} + payload={listViewToolData} + isShowLetterIndex={false} + hasSearchText={false} + onSelect={handleSelect} + canNotSelectMultiple + indexBar={null} + /> + ) + : ( + <ToolListTreeView + payload={treeViewToolsData} + hasSearchText={false} + onSelect={handleSelect} + canNotSelectMultiple + /> + ) )} { unInstalledPlugins.map((item) => { diff --git a/web/app/components/workflow/block-selector/rag-tool-recommendations/uninstalled-item.tsx b/web/app/components/workflow/block-selector/rag-tool-recommendations/uninstalled-item.tsx index 98395ec25a..9a351c4eff 100644 --- a/web/app/components/workflow/block-selector/rag-tool-recommendations/uninstalled-item.tsx +++ b/web/app/components/workflow/block-selector/rag-tool-recommendations/uninstalled-item.tsx @@ -1,13 +1,13 @@ 'use client' -import React from 'react' -import { useContext } from 'use-context-selector' -import { useTranslation } from 'react-i18next' import type { Plugin } from '@/app/components/plugins/types' +import { useBoolean } from 'ahooks' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { useContext } from 'use-context-selector' import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' import I18n from '@/context/i18n' -import { useBoolean } from 'ahooks' -import { BlockEnum } from '../../types' import BlockIcon from '../../block-icon' +import { BlockEnum } from '../../types' type UninstalledItemProps = { payload: Plugin @@ -27,23 +27,23 @@ const UninstalledItem = ({ }] = useBoolean(false) return ( - <div className='flex h-8 items-center rounded-lg pl-3 pr-2 hover:bg-state-base-hover'> + <div className="flex h-8 items-center rounded-lg pl-3 pr-2 hover:bg-state-base-hover"> <BlockIcon - className='shrink-0' + className="shrink-0" type={BlockEnum.Tool} toolIcon={payload.icon} /> - <div className='ml-2 flex w-0 grow items-center'> - <div className='flex w-0 grow items-center gap-x-2'> - <span className='system-sm-regular truncate text-text-primary'> + <div className="ml-2 flex w-0 grow items-center"> + <div className="flex w-0 grow items-center gap-x-2"> + <span className="system-sm-regular truncate text-text-primary"> {getLocalizedText(payload.label)} </span> - <span className='system-xs-regular text-text-quaternary'> + <span className="system-xs-regular text-text-quaternary"> {payload.org} </span> </div> <div - className='system-xs-medium cursor-pointer pl-1.5 text-components-button-secondary-accent-text' + className="system-xs-medium cursor-pointer pl-1.5 text-components-button-secondary-accent-text" onClick={showInstallModal} > {t('plugin.installAction')} diff --git a/web/app/components/workflow/block-selector/start-blocks.tsx b/web/app/components/workflow/block-selector/start-blocks.tsx index 07404460be..5c4311b805 100644 --- a/web/app/components/workflow/block-selector/start-blocks.tsx +++ b/web/app/components/workflow/block-selector/start-blocks.tsx @@ -1,19 +1,19 @@ +import type { BlockEnum, CommonNodeType } from '../types' +import type { TriggerDefaultValue } from './types' import { memo, useCallback, useEffect, useMemo, } from 'react' -import useNodes from '@/app/components/workflow/store/workflow/use-nodes' import { useTranslation } from 'react-i18next' +import Tooltip from '@/app/components/base/tooltip' +import useNodes from '@/app/components/workflow/store/workflow/use-nodes' +import { useAvailableNodesMetaData } from '../../workflow-app/hooks' import BlockIcon from '../block-icon' -import type { BlockEnum, CommonNodeType } from '../types' import { BlockEnum as BlockEnumValues } from '../types' // import { useNodeMetaData } from '../hooks' import { START_BLOCKS } from './constants' -import type { TriggerDefaultValue } from './types' -import Tooltip from '@/app/components/base/tooltip' -import { useAvailableNodesMetaData } from '../../workflow-app/hooks' type StartBlocksProps = { searchText: string @@ -67,48 +67,49 @@ const StartBlocks = ({ onContentStateChange?.(!isEmpty) }, [isEmpty, onContentStateChange]) - const renderBlock = useCallback((block: { type: BlockEnum; title: string; description?: string }) => ( + const renderBlock = useCallback((block: { type: BlockEnum, title: string, description?: string }) => ( <Tooltip key={block.type} - position='right' - popupClassName='w-[224px] rounded-xl' + position="right" + popupClassName="w-[224px] rounded-xl" needsDelay={false} popupContent={( <div> <BlockIcon - size='md' - className='mb-2' + size="md" + className="mb-2" type={block.type} /> - <div className='system-md-medium mb-1 text-text-primary'> + <div className="system-md-medium mb-1 text-text-primary"> {block.type === BlockEnumValues.TriggerWebhook ? t('workflow.customWebhook') - : t(`workflow.blocks.${block.type}`) - } + : t(`workflow.blocks.${block.type}`)} </div> - <div className='system-xs-regular text-text-secondary'> + <div className="system-xs-regular text-text-secondary"> {t(`workflow.blocksAbout.${block.type}`)} </div> {(block.type === BlockEnumValues.TriggerWebhook || block.type === BlockEnumValues.TriggerSchedule) && ( - <div className='system-xs-regular mb-1 mt-1 text-text-tertiary'> - {t('tools.author')} {t('workflow.difyTeam')} + <div className="system-xs-regular mb-1 mt-1 text-text-tertiary"> + {t('tools.author')} + {' '} + {t('workflow.difyTeam')} </div> )} </div> )} > <div - className='flex h-8 w-full cursor-pointer items-center rounded-lg px-3 hover:bg-state-base-hover' + className="flex h-8 w-full cursor-pointer items-center rounded-lg px-3 hover:bg-state-base-hover" onClick={() => onSelect(block.type)} > <BlockIcon - className='mr-2 shrink-0' + className="mr-2 shrink-0" type={block.type} /> - <div className='flex w-0 grow items-center justify-between text-sm text-text-secondary'> - <span className='truncate'>{t(`workflow.blocks.${block.type}`)}</span> + <div className="flex w-0 grow items-center justify-between text-sm text-text-secondary"> + <span className="truncate">{t(`workflow.blocks.${block.type}`)}</span> {block.type === BlockEnumValues.Start && ( - <span className='system-xs-regular ml-2 shrink-0 text-text-quaternary'>{t('workflow.blocks.originalStartNode')}</span> + <span className="system-xs-regular ml-2 shrink-0 text-text-quaternary">{t('workflow.blocks.originalStartNode')}</span> )} </div> </div> @@ -119,14 +120,14 @@ const StartBlocks = ({ return null return ( - <div className='p-1'> - <div className='mb-1'> + <div className="p-1"> + <div className="mb-1"> {filteredBlocks.map((block, index) => ( <div key={block.type}> {renderBlock(block)} {block.type === BlockEnumValues.Start && index < filteredBlocks.length - 1 && ( - <div className='my-1 px-3'> - <div className='border-t border-divider-subtle' /> + <div className="my-1 px-3"> + <div className="border-t border-divider-subtle" /> </div> )} </div> diff --git a/web/app/components/workflow/block-selector/tabs.tsx b/web/app/components/workflow/block-selector/tabs.tsx index 0367208cfe..6b54122142 100644 --- a/web/app/components/workflow/block-selector/tabs.tsx +++ b/web/app/components/workflow/block-selector/tabs.tsx @@ -1,24 +1,24 @@ import type { Dispatch, FC, SetStateAction } from 'react' -import { memo, useEffect, useMemo } from 'react' -import { useTranslation } from 'react-i18next' -import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, useInvalidateAllBuiltInTools } from '@/service/use-tools' import type { BlockEnum, NodeDefault, OnSelectBlock, ToolWithProvider, } from '../types' -import { TabsEnum } from './types' -import Blocks from './blocks' +import { memo, useEffect, useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import Tooltip from '@/app/components/base/tooltip' +import { useGlobalPublicStore } from '@/context/global-public-context' +import { useFeaturedToolsRecommendations } from '@/service/use-plugins' +import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, useInvalidateAllBuiltInTools } from '@/service/use-tools' +import { cn } from '@/utils/classnames' +import { basePath } from '@/utils/var' +import { useWorkflowStore } from '../store' import AllStartBlocks from './all-start-blocks' import AllTools from './all-tools' +import Blocks from './blocks' import DataSources from './data-sources' -import { cn } from '@/utils/classnames' -import { useFeaturedToolsRecommendations } from '@/service/use-plugins' -import { useGlobalPublicStore } from '@/context/global-public-context' -import { useWorkflowStore } from '../store' -import { basePath } from '@/utils/var' -import Tooltip from '@/app/components/base/tooltip' +import { TabsEnum } from './types' export type TabsProps = { activeTab: TabsEnum @@ -129,7 +129,7 @@ const Tabs: FC<TabsProps> = ({ <div onClick={e => e.stopPropagation()}> { !noBlocks && ( - <div className='relative flex bg-background-section-burn pl-1 pt-1'> + <div className="relative flex bg-background-section-burn pl-1 pt-1"> { tabs.map((tab) => { const commonProps = { @@ -152,8 +152,8 @@ const Tabs: FC<TabsProps> = ({ return ( <Tooltip key={tab.key} - position='top' - popupClassName='max-w-[200px]' + position="top" + popupClassName="max-w-[200px]" popupContent={t('workflow.tabs.startDisabledTip')} > <div {...commonProps}> @@ -178,7 +178,7 @@ const Tabs: FC<TabsProps> = ({ {filterElem} { activeTab === TabsEnum.Start && (!noBlocks || forceShowStartContent) && ( - <div className='border-t border-divider-subtle'> + <div className="border-t border-divider-subtle"> <AllStartBlocks allowUserInputSelection={allowStartNodeSelection} searchText={searchText} @@ -191,7 +191,7 @@ const Tabs: FC<TabsProps> = ({ } { activeTab === TabsEnum.Blocks && !noBlocks && ( - <div className='border-t border-divider-subtle'> + <div className="border-t border-divider-subtle"> <Blocks searchText={searchText} onSelect={onSelect} @@ -203,7 +203,7 @@ const Tabs: FC<TabsProps> = ({ } { activeTab === TabsEnum.Sources && !!dataSources.length && ( - <div className='border-t border-divider-subtle'> + <div className="border-t border-divider-subtle"> <DataSources searchText={searchText} onSelect={onSelect} diff --git a/web/app/components/workflow/block-selector/tool-picker.tsx b/web/app/components/workflow/block-selector/tool-picker.tsx index c10496006d..cbb8d5a01e 100644 --- a/web/app/components/workflow/block-selector/tool-picker.tsx +++ b/web/app/components/workflow/block-selector/tool-picker.tsx @@ -1,28 +1,29 @@ 'use client' +import type { + OffsetOptions, + Placement, +} from '@floating-ui/react' import type { FC } from 'react' -import React from 'react' -import { useMemo, useState } from 'react' +import type { ToolDefaultValue, ToolValue } from './types' +import type { CustomCollectionBackend } from '@/app/components/tools/types' +import type { BlockEnum, OnSelectBlock } from '@/app/components/workflow/types' +import { useBoolean } from 'ahooks' +import React, { useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import type { - OffsetOptions, - Placement, -} from '@floating-ui/react' -import AllTools from '@/app/components/workflow/block-selector/all-tools' -import type { ToolDefaultValue, ToolValue } from './types' -import type { BlockEnum, OnSelectBlock } from '@/app/components/workflow/types' +import Toast from '@/app/components/base/toast' import SearchBox from '@/app/components/plugins/marketplace/search-box' -import { useTranslation } from 'react-i18next' -import { useBoolean } from 'ahooks' import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal' +import AllTools from '@/app/components/workflow/block-selector/all-tools' +import { useGlobalPublicStore } from '@/context/global-public-context' import { createCustomCollection, } from '@/service/tools' -import type { CustomCollectionBackend } from '@/app/components/tools/types' -import Toast from '@/app/components/base/toast' +import { useFeaturedToolsRecommendations } from '@/service/use-plugins' import { useAllBuiltInTools, useAllCustomTools, @@ -33,8 +34,6 @@ import { useInvalidateAllMCPTools, useInvalidateAllWorkflowTools, } from '@/service/use-tools' -import { useFeaturedToolsRecommendations } from '@/service/use-plugins' -import { useGlobalPublicStore } from '@/context/global-public-context' import { cn } from '@/utils/classnames' type Props = { @@ -119,7 +118,8 @@ const ToolPicker: FC<Props> = ({ const handleAddedCustomTool = invalidateCustomTools const handleTriggerClick = () => { - if (disabled) return + if (disabled) + return onShowChange(true) } @@ -149,7 +149,7 @@ const ToolPicker: FC<Props> = ({ if (isShowEditCollectionToolModal) { return ( <EditCustomToolModal - dialogClassName='bg-background-overlay' + dialogClassName="bg-background-overlay" payload={null} onHide={hideEditCustomCollectionModal} onAdd={doCreateCustomToolCollection} @@ -170,9 +170,9 @@ const ToolPicker: FC<Props> = ({ {trigger} </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> + <PortalToFollowElemContent className="z-[1000]"> <div className={cn('relative min-h-20 rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-sm', panelClassName)}> - <div className='p-2 pb-1'> + <div className="p-2 pb-1"> <SearchBox search={searchText} onSearchChange={setSearchText} @@ -182,12 +182,12 @@ const ToolPicker: FC<Props> = ({ supportAddCustomTool={supportAddCustomTool} onAddedCustomTool={handleAddedCustomTool} onShowAddCustomCollectionModal={showEditCustomCollectionModal} - inputClassName='grow' + inputClassName="grow" /> </div> <AllTools - className='mt-1' - toolContentClassName='max-w-[100%]' + className="mt-1" + toolContentClassName="max-w-[100%]" tags={tags} searchText={searchText} onSelect={handleSelect as OnSelectBlock} diff --git a/web/app/components/workflow/block-selector/tool/action-item.tsx b/web/app/components/workflow/block-selector/tool/action-item.tsx index 617a28ade2..60fac5e701 100644 --- a/web/app/components/workflow/block-selector/tool/action-item.tsx +++ b/web/app/components/workflow/block-selector/tool/action-item.tsx @@ -1,19 +1,19 @@ 'use client' import type { FC } from 'react' -import React, { useMemo } from 'react' import type { ToolWithProvider } from '../../types' -import { BlockEnum } from '../../types' import type { ToolDefaultValue } from '../types' -import Tooltip from '@/app/components/base/tooltip' import type { Tool } from '@/app/components/tools/types' -import { useGetLanguage } from '@/context/i18n' -import BlockIcon from '../../block-icon' -import { cn } from '@/utils/classnames' +import React, { useMemo } from 'react' import { useTranslation } from 'react-i18next' +import { trackEvent } from '@/app/components/base/amplitude' +import Tooltip from '@/app/components/base/tooltip' +import { useGetLanguage } from '@/context/i18n' import useTheme from '@/hooks/use-theme' import { Theme } from '@/types/app' +import { cn } from '@/utils/classnames' import { basePath } from '@/utils/var' -import { trackEvent } from '@/app/components/base/amplitude' +import BlockIcon from '../../block-icon' +import { BlockEnum } from '../../types' const normalizeProviderIcon = (icon?: ToolWithProvider['icon']) => { if (!icon) @@ -59,27 +59,28 @@ const ToolItem: FC<Props> = ({ return ( <Tooltip key={payload.name} - position='right' + position="right" needsDelay={false} - popupClassName='!p-0 !px-3 !py-2.5 !w-[200px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !rounded-xl !shadow-lg' + popupClassName="!p-0 !px-3 !py-2.5 !w-[200px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !rounded-xl !shadow-lg" popupContent={( <div> <BlockIcon - size='md' - className='mb-2' + size="md" + className="mb-2" type={BlockEnum.Tool} toolIcon={providerIcon} /> - <div className='mb-1 text-sm leading-5 text-text-primary'>{payload.label[language]}</div> - <div className='text-xs leading-[18px] text-text-secondary'>{payload.description[language]}</div> + <div className="mb-1 text-sm leading-5 text-text-primary">{payload.label[language]}</div> + <div className="text-xs leading-[18px] text-text-secondary">{payload.description[language]}</div> </div> )} > <div key={payload.name} - className='flex cursor-pointer items-center justify-between rounded-lg pl-[21px] pr-1 hover:bg-state-base-hover' + className="flex cursor-pointer items-center justify-between rounded-lg pl-[21px] pr-1 hover:bg-state-base-hover" onClick={() => { - if (disabled) return + if (disabled) + return const params: Record<string, string> = {} if (payload.parameters) { payload.parameters.forEach((item) => { @@ -113,10 +114,10 @@ const ToolItem: FC<Props> = ({ <span className={cn(disabled && 'opacity-30')}>{payload.label[language]}</span> </div> {isAdded && ( - <div className='system-xs-regular mr-4 text-text-tertiary'>{t('tools.addToolModal.added')}</div> + <div className="system-xs-regular mr-4 text-text-tertiary">{t('tools.addToolModal.added')}</div> )} </div> - </Tooltip > + </Tooltip> ) } export default React.memo(ToolItem) diff --git a/web/app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx b/web/app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx index 510d6f2f4b..54eb050e06 100644 --- a/web/app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx +++ b/web/app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx @@ -1,12 +1,10 @@ 'use client' import type { FC } from 'react' -import React from 'react' -import type { ToolWithProvider } from '../../../types' -import type { BlockEnum } from '../../../types' +import type { BlockEnum, ToolWithProvider } from '../../../types' import type { ToolDefaultValue, ToolValue } from '../../types' -import Tool from '../tool' +import React, { useMemo } from 'react' import { ViewType } from '../../view-type-select' -import { useMemo } from 'react' +import Tool from '../tool' type Props = { payload: ToolWithProvider[] @@ -45,8 +43,8 @@ const ToolViewFlatView: FC<Props> = ({ return res }, [payload, letters]) return ( - <div className='flex w-full'> - <div className='mr-1 grow'> + <div className="flex w-full"> + <div className="mr-1 grow"> {payload.map(tool => ( <div key={tool.id} diff --git a/web/app/components/workflow/block-selector/tool/tool-list-tree-view/item.tsx b/web/app/components/workflow/block-selector/tool/tool-list-tree-view/item.tsx index a2833646f3..64a376e394 100644 --- a/web/app/components/workflow/block-selector/tool/tool-list-tree-view/item.tsx +++ b/web/app/components/workflow/block-selector/tool/tool-list-tree-view/item.tsx @@ -1,11 +1,10 @@ 'use client' import type { FC } from 'react' -import React from 'react' -import type { ToolWithProvider } from '../../../types' -import Tool from '../tool' -import type { BlockEnum } from '../../../types' -import { ViewType } from '../../view-type-select' +import type { BlockEnum, ToolWithProvider } from '../../../types' import type { ToolDefaultValue, ToolValue } from '../../types' +import React from 'react' +import { ViewType } from '../../view-type-select' +import Tool from '../tool' type Props = { groupName: string @@ -30,7 +29,7 @@ const Item: FC<Props> = ({ }) => { return ( <div> - <div className='flex h-[22px] items-center px-3 text-xs font-medium text-text-tertiary'> + <div className="flex h-[22px] items-center px-3 text-xs font-medium text-text-tertiary"> {groupName} </div> <div> diff --git a/web/app/components/workflow/block-selector/tool/tool-list-tree-view/list.tsx b/web/app/components/workflow/block-selector/tool/tool-list-tree-view/list.tsx index 162b816069..0f790ab036 100644 --- a/web/app/components/workflow/block-selector/tool/tool-list-tree-view/list.tsx +++ b/web/app/components/workflow/block-selector/tool/tool-list-tree-view/list.tsx @@ -1,12 +1,11 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' -import type { ToolWithProvider } from '../../../types' -import type { BlockEnum } from '../../../types' +import type { BlockEnum, ToolWithProvider } from '../../../types' import type { ToolDefaultValue, ToolValue } from '../../types' -import Item from './item' +import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { AGENT_GROUP_NAME, CUSTOM_GROUP_NAME, WORKFLOW_GROUP_NAME } from '../../index-bar' +import Item from './item' type Props = { payload: Record<string, ToolWithProvider[]> @@ -41,7 +40,8 @@ const ToolListTreeView: FC<Props> = ({ return name }, [t]) - if (!payload) return null + if (!payload) + return null return ( <div> diff --git a/web/app/components/workflow/block-selector/tool/tool.tsx b/web/app/components/workflow/block-selector/tool/tool.tsx index 622b06734b..366059b311 100644 --- a/web/app/components/workflow/block-selector/tool/tool.tsx +++ b/web/app/components/workflow/block-selector/tool/tool.tsx @@ -1,24 +1,24 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useEffect, useMemo, useRef } from 'react' -import { cn } from '@/utils/classnames' -import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react' -import { useGetLanguage } from '@/context/i18n' import type { Tool as ToolType } from '../../../tools/types' -import { CollectionType } from '../../../tools/types' import type { ToolWithProvider } from '../../types' -import { BlockEnum } from '../../types' import type { ToolDefaultValue, ToolValue } from '../types' -import { ViewType } from '../view-type-select' -import ActionItem from './action-item' -import BlockIcon from '../../block-icon' -import { useTranslation } from 'react-i18next' +import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react' import { useHover } from 'ahooks' +import React, { useCallback, useEffect, useMemo, useRef } from 'react' +import { useTranslation } from 'react-i18next' +import { Mcp } from '@/app/components/base/icons/src/vender/other' +import { useGetLanguage } from '@/context/i18n' import useTheme from '@/hooks/use-theme' import { Theme } from '@/types/app' -import McpToolNotSupportTooltip from '../../nodes/_base/components/mcp-tool-not-support-tooltip' -import { Mcp } from '@/app/components/base/icons/src/vender/other' +import { cn } from '@/utils/classnames' import { basePath } from '@/utils/var' +import { CollectionType } from '../../../tools/types' +import BlockIcon from '../../block-icon' +import McpToolNotSupportTooltip from '../../nodes/_base/components/mcp-tool-not-support-tooltip' +import { BlockEnum } from '../../types' +import { ViewType } from '../view-type-select' +import ActionItem from './action-item' const normalizeProviderIcon = (icon?: ToolWithProvider['icon']) => { if (!icon) @@ -78,7 +78,8 @@ const Tool: FC<Props> = ({ return normalizedIcon }, [theme, normalizedIcon, normalizedIconDark]) const getIsDisabled = useCallback((tool: ToolType) => { - if (!selectedTools || !selectedTools.length) return false + if (!selectedTools || !selectedTools.length) + return false return selectedTools.some(selectedTool => (selectedTool.provider_name === payload.name || selectedTool.provider_name === payload.id) && selectedTool.tool_name === tool.name) }, [payload.id, payload.name, selectedTools]) @@ -89,7 +90,7 @@ const Tool: FC<Props> = ({ const notShowProviderSelectInfo = useMemo(() => { if (isAllSelected) { return ( - <span className='system-xs-regular text-text-tertiary'> + <span className="system-xs-regular text-text-tertiary"> {t('tools.addToolModal.added')} </span> ) @@ -98,7 +99,8 @@ const Tool: FC<Props> = ({ const selectedInfo = useMemo(() => { if (isHovering && !isAllSelected) { return ( - <span className='system-xs-regular text-components-button-secondary-accent-text' + <span + className="system-xs-regular text-components-button-secondary-accent-text" onClick={() => { onSelectMultiple?.(BlockEnum.Tool, actions.filter(action => !getIsDisabled(action)).map((tool) => { const params: Record<string, string> = {} @@ -135,11 +137,10 @@ const Tool: FC<Props> = ({ return <></> return ( - <span className='system-xs-regular text-text-tertiary'> + <span className="system-xs-regular text-text-tertiary"> {isAllSelected ? t('workflow.tabs.allAdded') - : `${selectedToolsNum} / ${totalToolsNum}` - } + : `${selectedToolsNum} / ${totalToolsNum}`} </span> ) }, [actions, getIsDisabled, isAllSelected, isHovering, language, onSelectMultiple, payload.id, payload.is_team_authorization, payload.name, payload.type, selectedToolsNum, t, totalToolsNum]) @@ -176,7 +177,7 @@ const Tool: FC<Props> = ({ > <div className={cn(className)}> <div - className='group/item flex w-full cursor-pointer select-none items-center justify-between rounded-lg pl-3 pr-1 hover:bg-state-base-hover' + className="group/item flex w-full cursor-pointer select-none items-center justify-between rounded-lg pl-3 pr-1 hover:bg-state-base-hover" onClick={() => { if (hasAction) { setFold(!isFold) @@ -210,20 +211,20 @@ const Tool: FC<Props> = ({ > <div className={cn('flex h-8 grow items-center', isShowCanNotChooseMCPTip && 'opacity-30')}> <BlockIcon - className='shrink-0' + className="shrink-0" type={BlockEnum.Tool} toolIcon={providerIcon} /> - <div className='ml-2 flex w-0 grow items-center text-sm text-text-primary'> - <span className='max-w-[250px] truncate'>{notShowProvider ? actions[0]?.label[language] : payload.label[language]}</span> + <div className="ml-2 flex w-0 grow items-center text-sm text-text-primary"> + <span className="max-w-[250px] truncate">{notShowProvider ? actions[0]?.label[language] : payload.label[language]}</span> {isFlatView && groupName && ( - <span className='system-xs-regular ml-2 shrink-0 text-text-quaternary'>{groupName}</span> + <span className="system-xs-regular ml-2 shrink-0 text-text-quaternary">{groupName}</span> )} - {isMCPTool && <Mcp className='ml-2 size-3.5 shrink-0 text-text-quaternary' />} + {isMCPTool && <Mcp className="ml-2 size-3.5 shrink-0 text-text-quaternary" />} </div> </div> - <div className='ml-2 flex items-center'> + <div className="ml-2 flex items-center"> {!isShowCanNotChooseMCPTip && !canNotSelectMultiple && (notShowProvider ? notShowProviderSelectInfo : selectedInfo)} {isShowCanNotChooseMCPTip && <McpToolNotSupportTooltip />} {hasAction && ( diff --git a/web/app/components/workflow/block-selector/tools.tsx b/web/app/components/workflow/block-selector/tools.tsx index 788905323e..8035cc2dbf 100644 --- a/web/app/components/workflow/block-selector/tools.tsx +++ b/web/app/components/workflow/block-selector/tools.tsx @@ -1,14 +1,13 @@ -import { memo, useMemo, useRef } from 'react' import type { BlockEnum, ToolWithProvider } from '../types' -import IndexBar, { groupItems } from './index-bar' -import type { ToolDefaultValue, ToolValue } from './types' -import type { ToolTypeEnum } from './types' -import { ViewType } from './view-type-select' +import type { ToolDefaultValue, ToolTypeEnum, ToolValue } from './types' +import { memo, useMemo, useRef } from 'react' import Empty from '@/app/components/tools/provider/empty' import { useGetLanguage } from '@/context/i18n' -import ToolListTreeView from './tool/tool-list-tree-view/list' -import ToolListFlatView from './tool/tool-list-flat-view/list' import { cn } from '@/utils/classnames' +import IndexBar, { groupItems } from './index-bar' +import ToolListFlatView from './tool/tool-list-flat-view/list' +import ToolListTreeView from './tool/tool-list-tree-view/list' +import { ViewType } from './view-type-select' type ToolsProps = { onSelect: (type: BlockEnum, tool: ToolDefaultValue) => void @@ -93,36 +92,38 @@ const Tools = ({ return ( <div className={cn('max-w-[100%] p-1', className)}> {!tools.length && !hasSearchText && ( - <div className='py-10'> + <div className="py-10"> <Empty type={toolType!} isAgent={isAgent} /> </div> )} {!!tools.length && ( - isFlatView ? ( - <ToolListFlatView - toolRefs={toolRefs} - letters={letters} - payload={listViewToolData} - isShowLetterIndex={isShowLetterIndex} - hasSearchText={hasSearchText} - onSelect={onSelect} - canNotSelectMultiple={canNotSelectMultiple} - onSelectMultiple={onSelectMultiple} - selectedTools={selectedTools} - canChooseMCPTool={canChooseMCPTool} - indexBar={<IndexBar letters={letters} itemRefs={toolRefs} className={indexBarClassName} />} - /> - ) : ( - <ToolListTreeView - payload={treeViewToolsData} - hasSearchText={hasSearchText} - onSelect={onSelect} - canNotSelectMultiple={canNotSelectMultiple} - onSelectMultiple={onSelectMultiple} - selectedTools={selectedTools} - canChooseMCPTool={canChooseMCPTool} - /> - ) + isFlatView + ? ( + <ToolListFlatView + toolRefs={toolRefs} + letters={letters} + payload={listViewToolData} + isShowLetterIndex={isShowLetterIndex} + hasSearchText={hasSearchText} + onSelect={onSelect} + canNotSelectMultiple={canNotSelectMultiple} + onSelectMultiple={onSelectMultiple} + selectedTools={selectedTools} + canChooseMCPTool={canChooseMCPTool} + indexBar={<IndexBar letters={letters} itemRefs={toolRefs} className={indexBarClassName} />} + /> + ) + : ( + <ToolListTreeView + payload={treeViewToolsData} + hasSearchText={hasSearchText} + onSelect={onSelect} + canNotSelectMultiple={canNotSelectMultiple} + onSelectMultiple={onSelectMultiple} + selectedTools={selectedTools} + canChooseMCPTool={canChooseMCPTool} + /> + ) )} </div> ) diff --git a/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx b/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx index e22712c248..c0edec474a 100644 --- a/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx +++ b/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx @@ -1,15 +1,14 @@ 'use client' import type { FC } from 'react' -import React from 'react' -import type { TriggerWithProvider } from '../types' +import type { TriggerDefaultValue, TriggerWithProvider } from '../types' import type { Event } from '@/app/components/tools/types' -import { BlockEnum } from '../../types' -import type { TriggerDefaultValue } from '../types' +import React from 'react' +import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import { useGetLanguage } from '@/context/i18n' -import BlockIcon from '../../block-icon' import { cn } from '@/utils/classnames' -import { useTranslation } from 'react-i18next' +import BlockIcon from '../../block-icon' +import { BlockEnum } from '../../types' type Props = { provider: TriggerWithProvider @@ -32,27 +31,28 @@ const TriggerPluginActionItem: FC<Props> = ({ return ( <Tooltip key={payload.name} - position='right' + position="right" needsDelay={false} - popupClassName='!p-0 !px-3 !py-2.5 !w-[224px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !rounded-xl !shadow-lg' + popupClassName="!p-0 !px-3 !py-2.5 !w-[224px] !leading-[18px] !text-xs !text-gray-700 !border-[0.5px] !border-black/5 !rounded-xl !shadow-lg" popupContent={( <div> <BlockIcon - size='md' - className='mb-2' + size="md" + className="mb-2" type={BlockEnum.TriggerPlugin} toolIcon={provider.icon} /> - <div className='mb-1 text-sm leading-5 text-text-primary'>{payload.label[language]}</div> - <div className='text-xs leading-[18px] text-text-secondary'>{payload.description[language]}</div> + <div className="mb-1 text-sm leading-5 text-text-primary">{payload.label[language]}</div> + <div className="text-xs leading-[18px] text-text-secondary">{payload.description[language]}</div> </div> )} > <div key={payload.name} - className='flex cursor-pointer items-center justify-between rounded-lg pl-[21px] pr-1 hover:bg-state-base-hover' + className="flex cursor-pointer items-center justify-between rounded-lg pl-[21px] pr-1 hover:bg-state-base-hover" onClick={() => { - if (disabled) return + if (disabled) + return const params: Record<string, string> = {} if (payload.parameters) { payload.parameters.forEach((item: any) => { @@ -81,10 +81,10 @@ const TriggerPluginActionItem: FC<Props> = ({ <span className={cn(disabled && 'opacity-30')}>{payload.label[language]}</span> </div> {isAdded && ( - <div className='system-xs-regular mr-4 text-text-tertiary'>{t('tools.addToolModal.added')}</div> + <div className="system-xs-regular mr-4 text-text-tertiary">{t('tools.addToolModal.added')}</div> )} </div> - </Tooltip > + </Tooltip> ) } export default React.memo(TriggerPluginActionItem) diff --git a/web/app/components/workflow/block-selector/trigger-plugin/item.tsx b/web/app/components/workflow/block-selector/trigger-plugin/item.tsx index 15b8d638fe..a4de7b30dd 100644 --- a/web/app/components/workflow/block-selector/trigger-plugin/item.tsx +++ b/web/app/components/workflow/block-selector/trigger-plugin/item.tsx @@ -1,18 +1,18 @@ 'use client' -import { useGetLanguage } from '@/context/i18n' -import { cn } from '@/utils/classnames' -import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react' import type { FC } from 'react' +import type { TriggerDefaultValue, TriggerWithProvider } from '@/app/components/workflow/block-selector/types' +import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react' import React, { useEffect, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import { CollectionType } from '@/app/components/tools/types' import BlockIcon from '@/app/components/workflow/block-icon' import { BlockEnum } from '@/app/components/workflow/types' -import type { TriggerDefaultValue, TriggerWithProvider } from '@/app/components/workflow/block-selector/types' -import TriggerPluginActionItem from './action-item' -import { Theme } from '@/types/app' +import { useGetLanguage } from '@/context/i18n' import useTheme from '@/hooks/use-theme' +import { Theme } from '@/types/app' +import { cn } from '@/utils/classnames' import { basePath } from '@/utils/var' +import TriggerPluginActionItem from './action-item' const normalizeProviderIcon = (icon?: TriggerWithProvider['icon']) => { if (!icon) @@ -93,7 +93,7 @@ const TriggerPluginItem: FC<Props> = ({ > <div className={cn(className)}> <div - className='group/item flex w-full cursor-pointer select-none items-center justify-between rounded-lg pl-3 pr-1 hover:bg-state-base-hover' + className="group/item flex w-full cursor-pointer select-none items-center justify-between rounded-lg pl-3 pr-1 hover:bg-state-base-hover" onClick={() => { if (hasAction) { setFold(!isFold) @@ -124,19 +124,19 @@ const TriggerPluginItem: FC<Props> = ({ }) }} > - <div className='flex h-8 grow items-center'> + <div className="flex h-8 grow items-center"> <BlockIcon - className='shrink-0' + className="shrink-0" type={BlockEnum.TriggerPlugin} toolIcon={providerIcon} /> - <div className='ml-2 flex min-w-0 flex-1 items-center text-sm text-text-primary'> - <span className='max-w-[200px] truncate'>{notShowProvider ? actions[0]?.label[language] : payload.label[language]}</span> - <span className='system-xs-regular ml-2 truncate text-text-quaternary'>{groupName}</span> + <div className="ml-2 flex min-w-0 flex-1 items-center text-sm text-text-primary"> + <span className="max-w-[200px] truncate">{notShowProvider ? actions[0]?.label[language] : payload.label[language]}</span> + <span className="system-xs-regular ml-2 truncate text-text-quaternary">{groupName}</span> </div> </div> - <div className='ml-2 flex items-center'> + <div className="ml-2 flex items-center"> {hasAction && ( <FoldIcon className={cn('h-4 w-4 shrink-0 text-text-tertiary group-hover/item:text-text-tertiary', isFold && 'text-text-quaternary')} /> )} diff --git a/web/app/components/workflow/block-selector/trigger-plugin/list.tsx b/web/app/components/workflow/block-selector/trigger-plugin/list.tsx index 3caf1149dd..126583be73 100644 --- a/web/app/components/workflow/block-selector/trigger-plugin/list.tsx +++ b/web/app/components/workflow/block-selector/trigger-plugin/list.tsx @@ -1,10 +1,10 @@ 'use client' -import { memo, useEffect, useMemo } from 'react' -import { useAllTriggerPlugins } from '@/service/use-triggers' -import TriggerPluginItem from './item' import type { BlockEnum } from '../../types' import type { TriggerDefaultValue, TriggerWithProvider } from '../types' +import { memo, useEffect, useMemo } from 'react' import { useGetLanguage } from '@/context/i18n' +import { useAllTriggerPlugins } from '@/service/use-triggers' +import TriggerPluginItem from './item' type TriggerPluginListProps = { onSelect: (type: BlockEnum, trigger?: TriggerDefaultValue) => void diff --git a/web/app/components/workflow/block-selector/types.ts b/web/app/components/workflow/block-selector/types.ts index 1e5acbbeb3..6ed4d7f2d5 100644 --- a/web/app/components/workflow/block-selector/types.ts +++ b/web/app/components/workflow/block-selector/types.ts @@ -1,6 +1,6 @@ -import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { ParametersSchema, PluginMeta, PluginTriggerSubscriptionConstructor, SupportedCreationMethods, TriggerEvent } from '../../plugins/types' import type { Collection, Event } from '../../tools/types' +import type { TypeWithI18N } from '@/app/components/header/account-setting/model-provider-page/declarations' export enum TabsEnum { Start = 'start', @@ -99,7 +99,7 @@ export type DataSourceItem = { identity: { author: string description: TypeWithI18N - icon: string | { background: string; content: string } + icon: string | { background: string, content: string } label: TypeWithI18N name: string tags: string[] @@ -108,7 +108,7 @@ export type DataSourceItem = { description: TypeWithI18N identity: { author: string - icon?: string | { background: string; content: string } + icon?: string | { background: string, content: string } label: TypeWithI18N name: string provider: string @@ -130,7 +130,7 @@ export type TriggerParameter = { label: TypeWithI18N description?: TypeWithI18N type: 'string' | 'number' | 'boolean' | 'select' | 'file' | 'files' - | 'model-selector' | 'app-selector' | 'object' | 'array' | 'dynamic-select' + | 'model-selector' | 'app-selector' | 'object' | 'array' | 'dynamic-select' auto_generate?: { type: string value?: any @@ -154,7 +154,7 @@ export type TriggerParameter = { export type TriggerCredentialField = { type: 'secret-input' | 'text-input' | 'select' | 'boolean' - | 'app-selector' | 'model-selector' | 'tools-selector' + | 'app-selector' | 'model-selector' | 'tools-selector' name: string scope?: string | null required: boolean diff --git a/web/app/components/workflow/block-selector/use-check-vertical-scrollbar.ts b/web/app/components/workflow/block-selector/use-check-vertical-scrollbar.ts index 98986cf3b6..e8f5fc0559 100644 --- a/web/app/components/workflow/block-selector/use-check-vertical-scrollbar.ts +++ b/web/app/components/workflow/block-selector/use-check-vertical-scrollbar.ts @@ -5,7 +5,8 @@ const useCheckVerticalScrollbar = (ref: React.RefObject<HTMLElement>) => { useEffect(() => { const elem = ref.current - if (!elem) return + if (!elem) + return const checkScrollbar = () => { setHasVerticalScrollbar(elem.scrollHeight > elem.clientHeight) diff --git a/web/app/components/workflow/block-selector/use-sticky-scroll.ts b/web/app/components/workflow/block-selector/use-sticky-scroll.ts index 7933d63b39..67ea70d94e 100644 --- a/web/app/components/workflow/block-selector/use-sticky-scroll.ts +++ b/web/app/components/workflow/block-selector/use-sticky-scroll.ts @@ -1,5 +1,5 @@ -import React from 'react' import { useThrottleFn } from 'ahooks' +import React from 'react' export enum ScrollPosition { belowTheWrap = 'belowTheWrap', diff --git a/web/app/components/workflow/block-selector/utils.ts b/web/app/components/workflow/block-selector/utils.ts index 4272e61644..f4a0baf05b 100644 --- a/web/app/components/workflow/block-selector/utils.ts +++ b/web/app/components/workflow/block-selector/utils.ts @@ -1,5 +1,5 @@ -import type { Tool } from '@/app/components/tools/types' import type { DataSourceItem } from './types' +import type { Tool } from '@/app/components/tools/types' export const transformDataSourceToTool = (dataSourceItem: DataSourceItem) => { return { diff --git a/web/app/components/workflow/block-selector/view-type-select.tsx b/web/app/components/workflow/block-selector/view-type-select.tsx index 900453fedb..a4830d8e81 100644 --- a/web/app/components/workflow/block-selector/view-type-select.tsx +++ b/web/app/components/workflow/block-selector/view-type-select.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' import { RiNodeTree, RiSortAlphabetAsc } from '@remixicon/react' +import React, { useCallback } from 'react' import { cn } from '@/utils/classnames' export enum ViewType { @@ -27,30 +27,26 @@ const ViewTypeSelect: FC<Props> = ({ }, [viewType, onChange]) return ( - <div className='flex items-center rounded-lg bg-components-segmented-control-bg-normal p-px'> + <div className="flex items-center rounded-lg bg-components-segmented-control-bg-normal p-px"> <div className={ - cn('rounded-lg p-[3px]', - viewType === ViewType.flat - ? 'bg-components-segmented-control-item-active-bg text-text-accent-light-mode-only shadow-xs' - : 'cursor-pointer text-text-tertiary', - ) + cn('rounded-lg p-[3px]', viewType === ViewType.flat + ? 'bg-components-segmented-control-item-active-bg text-text-accent-light-mode-only shadow-xs' + : 'cursor-pointer text-text-tertiary') } onClick={handleChange(ViewType.flat)} > - <RiSortAlphabetAsc className='h-4 w-4' /> + <RiSortAlphabetAsc className="h-4 w-4" /> </div> <div className={ - cn('rounded-lg p-[3px]', - viewType === ViewType.tree - ? 'bg-components-segmented-control-item-active-bg text-text-accent-light-mode-only shadow-xs' - : 'cursor-pointer text-text-tertiary', - ) + cn('rounded-lg p-[3px]', viewType === ViewType.tree + ? 'bg-components-segmented-control-item-active-bg text-text-accent-light-mode-only shadow-xs' + : 'cursor-pointer text-text-tertiary') } onClick={handleChange(ViewType.tree)} > - <RiNodeTree className='h-4 w-4 ' /> + <RiNodeTree className="h-4 w-4 " /> </div> </div> ) diff --git a/web/app/components/workflow/candidate-node-main.tsx b/web/app/components/workflow/candidate-node-main.tsx index 41a38e0b2a..9df5510627 100644 --- a/web/app/components/workflow/candidate-node-main.tsx +++ b/web/app/components/workflow/candidate-node-main.tsx @@ -4,27 +4,27 @@ import type { import type { Node, } from '@/app/components/workflow/types' +import { useEventListener } from 'ahooks' +import { produce } from 'immer' import { memo, } from 'react' -import { produce } from 'immer' import { useReactFlow, useStoreApi, useViewport, } from 'reactflow' -import { useEventListener } from 'ahooks' +import { CUSTOM_NODE } from './constants' +import { useAutoGenerateWebhookUrl, useNodesInteractions, useNodesSyncDraft, useWorkflowHistory, WorkflowHistoryEvent } from './hooks' +import CustomNode from './nodes' +import CustomNoteNode from './note-node' +import { CUSTOM_NOTE_NODE } from './note-node/constants' import { useStore, useWorkflowStore, } from './store' -import { WorkflowHistoryEvent, useAutoGenerateWebhookUrl, useNodesInteractions, useNodesSyncDraft, useWorkflowHistory } from './hooks' -import { CUSTOM_NODE } from './constants' -import { getIterationStartNode, getLoopStartNode } from './utils' -import CustomNode from './nodes' -import CustomNoteNode from './note-node' -import { CUSTOM_NOTE_NODE } from './note-node/constants' import { BlockEnum } from './types' +import { getIterationStartNode, getLoopStartNode } from './utils' type Props = { candidateNode: Node @@ -94,7 +94,7 @@ const CandidateNodeMain: FC<Props> = ({ return ( <div - className='absolute z-10' + className="absolute z-10" style={{ left: mousePosition.elementX, top: mousePosition.elementY, diff --git a/web/app/components/workflow/candidate-node.tsx b/web/app/components/workflow/candidate-node.tsx index bdbb1f4433..2c61f0f8ad 100644 --- a/web/app/components/workflow/candidate-node.tsx +++ b/web/app/components/workflow/candidate-node.tsx @@ -2,10 +2,10 @@ import { memo, } from 'react' +import CandidateNodeMain from './candidate-node-main' import { useStore, } from './store' -import CandidateNodeMain from './candidate-node-main' const CandidateNode = () => { const candidateNode = useStore(s => s.candidateNode) diff --git a/web/app/components/workflow/constants.ts b/web/app/components/workflow/constants.ts index ad498ff65b..4d95db7fcf 100644 --- a/web/app/components/workflow/constants.ts +++ b/web/app/components/workflow/constants.ts @@ -1,5 +1,6 @@ import type { Var } from './types' import { BlockEnum, VarType } from './types' + export const MAX_ITERATION_PARALLEL_NUM = 10 export const MIN_ITERATION_PARALLEL_NUM = 1 export const DEFAULT_ITER_TIMES = 1 @@ -42,16 +43,18 @@ export const isInWorkflowPage = () => { export const getGlobalVars = (isChatMode: boolean): Var[] => { const isInWorkflow = isInWorkflowPage() const vars: Var[] = [ - ...(isChatMode ? [ - { - variable: 'sys.dialogue_count', - type: VarType.number, - }, - { - variable: 'sys.conversation_id', - type: VarType.string, - }, - ] : []), + ...(isChatMode + ? [ + { + variable: 'sys.dialogue_count', + type: VarType.number, + }, + { + variable: 'sys.conversation_id', + type: VarType.string, + }, + ] + : []), { variable: 'sys.user_id', type: VarType.string, @@ -68,12 +71,14 @@ export const getGlobalVars = (isChatMode: boolean): Var[] => { variable: 'sys.workflow_run_id', type: VarType.string, }, - ...((isInWorkflow && !isChatMode) ? [ - { - variable: 'sys.timestamp', - type: VarType.number, - }, - ] : []), + ...((isInWorkflow && !isChatMode) + ? [ + { + variable: 'sys.timestamp', + type: VarType.number, + }, + ] + : []), ] return vars } @@ -104,11 +109,25 @@ export const RETRIEVAL_OUTPUT_STRUCT = `{ }` export const SUPPORT_OUTPUT_VARS_NODE = [ - BlockEnum.Start, BlockEnum.TriggerWebhook, BlockEnum.TriggerPlugin, BlockEnum.LLM, BlockEnum.KnowledgeRetrieval, BlockEnum.Code, BlockEnum.TemplateTransform, - BlockEnum.HttpRequest, BlockEnum.Tool, BlockEnum.VariableAssigner, BlockEnum.VariableAggregator, BlockEnum.QuestionClassifier, - BlockEnum.ParameterExtractor, BlockEnum.Iteration, BlockEnum.Loop, - BlockEnum.DocExtractor, BlockEnum.ListFilter, - BlockEnum.Agent, BlockEnum.DataSource, + BlockEnum.Start, + BlockEnum.TriggerWebhook, + BlockEnum.TriggerPlugin, + BlockEnum.LLM, + BlockEnum.KnowledgeRetrieval, + BlockEnum.Code, + BlockEnum.TemplateTransform, + BlockEnum.HttpRequest, + BlockEnum.Tool, + BlockEnum.VariableAssigner, + BlockEnum.VariableAggregator, + BlockEnum.QuestionClassifier, + BlockEnum.ParameterExtractor, + BlockEnum.Iteration, + BlockEnum.Loop, + BlockEnum.DocExtractor, + BlockEnum.ListFilter, + BlockEnum.Agent, + BlockEnum.DataSource, ] export const AGENT_OUTPUT_STRUCT: Var[] = [ diff --git a/web/app/components/workflow/constants/node.ts b/web/app/components/workflow/constants/node.ts index 5b5007e748..5de9512752 100644 --- a/web/app/components/workflow/constants/node.ts +++ b/web/app/components/workflow/constants/node.ts @@ -1,25 +1,25 @@ -import llmDefault from '@/app/components/workflow/nodes/llm/default' -import knowledgeRetrievalDefault from '@/app/components/workflow/nodes/knowledge-retrieval/default' import agentDefault from '@/app/components/workflow/nodes/agent/default' - -import questionClassifierDefault from '@/app/components/workflow/nodes/question-classifier/default' - -import ifElseDefault from '@/app/components/workflow/nodes/if-else/default' -import iterationDefault from '@/app/components/workflow/nodes/iteration/default' -import iterationStartDefault from '@/app/components/workflow/nodes/iteration-start/default' -import loopDefault from '@/app/components/workflow/nodes/loop/default' -import loopStartDefault from '@/app/components/workflow/nodes/loop-start/default' -import loopEndDefault from '@/app/components/workflow/nodes/loop-end/default' - -import codeDefault from '@/app/components/workflow/nodes/code/default' -import templateTransformDefault from '@/app/components/workflow/nodes/template-transform/default' -import variableAggregatorDefault from '@/app/components/workflow/nodes/variable-assigner/default' -import documentExtractorDefault from '@/app/components/workflow/nodes/document-extractor/default' import assignerDefault from '@/app/components/workflow/nodes/assigner/default' +import codeDefault from '@/app/components/workflow/nodes/code/default' + +import documentExtractorDefault from '@/app/components/workflow/nodes/document-extractor/default' + import httpRequestDefault from '@/app/components/workflow/nodes/http/default' -import parameterExtractorDefault from '@/app/components/workflow/nodes/parameter-extractor/default' +import ifElseDefault from '@/app/components/workflow/nodes/if-else/default' +import iterationStartDefault from '@/app/components/workflow/nodes/iteration-start/default' +import iterationDefault from '@/app/components/workflow/nodes/iteration/default' +import knowledgeRetrievalDefault from '@/app/components/workflow/nodes/knowledge-retrieval/default' import listOperatorDefault from '@/app/components/workflow/nodes/list-operator/default' + +import llmDefault from '@/app/components/workflow/nodes/llm/default' +import loopEndDefault from '@/app/components/workflow/nodes/loop-end/default' +import loopStartDefault from '@/app/components/workflow/nodes/loop-start/default' +import loopDefault from '@/app/components/workflow/nodes/loop/default' +import parameterExtractorDefault from '@/app/components/workflow/nodes/parameter-extractor/default' +import questionClassifierDefault from '@/app/components/workflow/nodes/question-classifier/default' +import templateTransformDefault from '@/app/components/workflow/nodes/template-transform/default' import toolDefault from '@/app/components/workflow/nodes/tool/default' +import variableAggregatorDefault from '@/app/components/workflow/nodes/variable-assigner/default' export const WORKFLOW_COMMON_NODES = [ llmDefault, diff --git a/web/app/components/workflow/context.tsx b/web/app/components/workflow/context.tsx index 0b77239dd1..ca3185b714 100644 --- a/web/app/components/workflow/context.tsx +++ b/web/app/components/workflow/context.tsx @@ -1,12 +1,12 @@ +import type { StateCreator } from 'zustand' +import type { SliceFromInjection } from './store' import { createContext, useRef, } from 'react' -import type { SliceFromInjection } from './store' import { createWorkflowStore, } from './store' -import type { StateCreator } from 'zustand' type WorkflowStore = ReturnType<typeof createWorkflowStore> export const WorkflowContext = createContext<WorkflowStore | null>(null) diff --git a/web/app/components/workflow/custom-connection-line.tsx b/web/app/components/workflow/custom-connection-line.tsx index c187f16fe1..fb218722c4 100644 --- a/web/app/components/workflow/custom-connection-line.tsx +++ b/web/app/components/workflow/custom-connection-line.tsx @@ -1,8 +1,8 @@ -import { memo } from 'react' import type { ConnectionLineComponentProps } from 'reactflow' +import { memo } from 'react' import { - Position, getBezierPath, + Position, } from 'reactflow' const CustomConnectionLine = ({ fromX, fromY, toX, toY }: ConnectionLineComponentProps) => { @@ -22,7 +22,7 @@ const CustomConnectionLine = ({ fromX, fromY, toX, toY }: ConnectionLineComponen <g> <path fill="none" - stroke='#D0D5DD' + stroke="#D0D5DD" strokeWidth={2} d={edgePath} /> @@ -31,7 +31,7 @@ const CustomConnectionLine = ({ fromX, fromY, toX, toY }: ConnectionLineComponen y={toY - 4} width={2} height={8} - fill='#2970FF' + fill="#2970FF" /> </g> ) diff --git a/web/app/components/workflow/custom-edge-linear-gradient-render.tsx b/web/app/components/workflow/custom-edge-linear-gradient-render.tsx index b799bb36b2..313fa2cafd 100644 --- a/web/app/components/workflow/custom-edge-linear-gradient-render.tsx +++ b/web/app/components/workflow/custom-edge-linear-gradient-render.tsx @@ -25,21 +25,21 @@ const CustomEdgeLinearGradientRender = ({ <defs> <linearGradient id={id} - gradientUnits='userSpaceOnUse' + gradientUnits="userSpaceOnUse" x1={x1} y1={y1} x2={x2} y2={y2} > <stop - offset='0%' + offset="0%" style={{ stopColor: startColor, stopOpacity: 1, }} /> <stop - offset='100%' + offset="100%" style={{ stopColor, stopOpacity: 1, diff --git a/web/app/components/workflow/custom-edge.tsx b/web/app/components/workflow/custom-edge.tsx index 2a53abb327..3b73e07536 100644 --- a/web/app/components/workflow/custom-edge.tsx +++ b/web/app/components/workflow/custom-edge.tsx @@ -1,32 +1,32 @@ +import type { EdgeProps } from 'reactflow' +import type { + Edge, + OnSelectBlock, +} from './types' +import { intersection } from 'lodash-es' import { memo, useCallback, useMemo, useState, } from 'react' -import { intersection } from 'lodash-es' -import type { EdgeProps } from 'reactflow' import { BaseEdge, EdgeLabelRenderer, - Position, getBezierPath, + Position, } from 'reactflow' +import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' +import { cn } from '@/utils/classnames' +import BlockSelector from './block-selector' +import { ITERATION_CHILDREN_Z_INDEX, LOOP_CHILDREN_Z_INDEX } from './constants' +import CustomEdgeLinearGradientRender from './custom-edge-linear-gradient-render' import { useAvailableBlocks, useNodesInteractions, } from './hooks' -import BlockSelector from './block-selector' -import type { - Edge, - OnSelectBlock, -} from './types' import { NodeRunningStatus } from './types' import { getEdgeColor } from './utils' -import { ITERATION_CHILDREN_Z_INDEX, LOOP_CHILDREN_Z_INDEX } from './constants' -import CustomEdgeLinearGradientRender from './custom-edge-linear-gradient-render' -import { cn } from '@/utils/classnames' -import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' const CustomEdge = ({ id, @@ -75,8 +75,9 @@ const CustomEdge = ({ || _targetRunningStatus === NodeRunningStatus.Exception || _targetRunningStatus === NodeRunningStatus.Running ) - ) + ) { return id + } }, [_sourceRunningStatus, _targetRunningStatus, id]) const handleOpenChange = useCallback((v: boolean) => { diff --git a/web/app/components/workflow/datasets-detail-store/provider.tsx b/web/app/components/workflow/datasets-detail-store/provider.tsx index a75b7e1d29..7cd3b88bfb 100644 --- a/web/app/components/workflow/datasets-detail-store/provider.tsx +++ b/web/app/components/workflow/datasets-detail-store/provider.tsx @@ -1,10 +1,10 @@ import type { FC } from 'react' -import { createContext, useCallback, useEffect, useRef } from 'react' -import { createDatasetsDetailStore } from './store' -import type { CommonNodeType, Node } from '../types' -import { BlockEnum } from '../types' import type { KnowledgeRetrievalNodeType } from '../nodes/knowledge-retrieval/types' +import type { CommonNodeType, Node } from '../types' +import { createContext, useCallback, useEffect, useRef } from 'react' import { fetchDatasets } from '@/service/datasets' +import { BlockEnum } from '../types' +import { createDatasetsDetailStore } from './store' type DatasetsDetailStoreApi = ReturnType<typeof createDatasetsDetailStore> @@ -33,12 +33,14 @@ const DatasetsDetailProvider: FC<DatasetsDetailProviderProps> = ({ }, []) useEffect(() => { - if (!storeRef.current) return + if (!storeRef.current) + return const knowledgeRetrievalNodes = nodes.filter(node => node.data.type === BlockEnum.KnowledgeRetrieval) const allDatasetIds = knowledgeRetrievalNodes.reduce<string[]>((acc, node) => { return Array.from(new Set([...acc, ...(node.data as CommonNodeType<KnowledgeRetrievalNodeType>).dataset_ids])) }, []) - if (allDatasetIds.length === 0) return + if (allDatasetIds.length === 0) + return updateDatasetsDetail(allDatasetIds) }, []) diff --git a/web/app/components/workflow/datasets-detail-store/store.ts b/web/app/components/workflow/datasets-detail-store/store.ts index 80f19bfea0..e2910b84b1 100644 --- a/web/app/components/workflow/datasets-detail-store/store.ts +++ b/web/app/components/workflow/datasets-detail-store/store.ts @@ -1,8 +1,8 @@ +import type { DataSet } from '@/models/datasets' +import { produce } from 'immer' import { useContext } from 'react' import { createStore, useStore } from 'zustand' -import type { DataSet } from '@/models/datasets' import { DatasetsDetailContext } from './provider' -import { produce } from 'immer' type DatasetsDetailStore = { datasetsDetail: Record<string, DataSet> diff --git a/web/app/components/workflow/dsl-export-confirm-modal.tsx b/web/app/components/workflow/dsl-export-confirm-modal.tsx index ff5498abc5..63100876c6 100644 --- a/web/app/components/workflow/dsl-export-confirm-modal.tsx +++ b/web/app/components/workflow/dsl-export-confirm-modal.tsx @@ -1,14 +1,14 @@ 'use client' +import type { EnvironmentVariable } from '@/app/components/workflow/types' +import { RiCloseLine, RiLock2Line } from '@remixicon/react' +import { noop } from 'lodash-es' import React, { useState } from 'react' import { useTranslation } from 'react-i18next' -import { RiCloseLine, RiLock2Line } from '@remixicon/react' -import { cn } from '@/utils/classnames' +import Button from '@/app/components/base/button' +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 Checkbox from '@/app/components/base/checkbox' -import Button from '@/app/components/base/button' -import type { EnvironmentVariable } from '@/app/components/workflow/types' -import { noop } from 'lodash-es' +import { cn } from '@/utils/classnames' export type DSLExportConfirmModalProps = { envList: EnvironmentVariable[] @@ -36,47 +36,47 @@ const DSLExportConfirmModal = ({ onClose={noop} className={cn('w-[480px] max-w-[480px]')} > - <div className='title-2xl-semi-bold relative pb-6 text-text-primary'>{t('workflow.env.export.title')}</div> - <div className='absolute right-4 top-4 cursor-pointer p-2' onClick={onClose}> - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <div className="title-2xl-semi-bold relative pb-6 text-text-primary">{t('workflow.env.export.title')}</div> + <div className="absolute right-4 top-4 cursor-pointer p-2" onClick={onClose}> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> - <div className='relative'> - <table className='radius-md w-full border-separate border-spacing-0 border border-divider-regular shadow-xs'> - <thead className='system-xs-medium-uppercase text-text-tertiary'> + <div className="relative"> + <table className="radius-md w-full border-separate border-spacing-0 border border-divider-regular shadow-xs"> + <thead className="system-xs-medium-uppercase text-text-tertiary"> <tr> - <td width={220} className='h-7 border-b border-r border-divider-regular pl-3'>NAME</td> - <td className='h-7 border-b border-divider-regular pl-3'>VALUE</td> + <td width={220} className="h-7 border-b border-r border-divider-regular pl-3">NAME</td> + <td className="h-7 border-b border-divider-regular pl-3">VALUE</td> </tr> </thead> <tbody> {envList.map((env, index) => ( <tr key={env.name}> <td className={cn('system-xs-medium h-7 border-r pl-3', index + 1 !== envList.length && 'border-b')}> - <div className='flex w-[200px] items-center gap-1'> - <Env className='h-4 w-4 shrink-0 text-util-colors-violet-violet-600' /> - <div className='truncate text-text-primary'>{env.name}</div> - <div className='shrink-0 text-text-tertiary'>Secret</div> - <RiLock2Line className='h-3 w-3 shrink-0 text-text-tertiary' /> + <div className="flex w-[200px] items-center gap-1"> + <Env className="h-4 w-4 shrink-0 text-util-colors-violet-violet-600" /> + <div className="truncate text-text-primary">{env.name}</div> + <div className="shrink-0 text-text-tertiary">Secret</div> + <RiLock2Line className="h-3 w-3 shrink-0 text-text-tertiary" /> </div> </td> <td className={cn('h-7 pl-3', index + 1 !== envList.length && 'border-b')}> - <div className='system-xs-regular truncate text-text-secondary'>{env.value}</div> + <div className="system-xs-regular truncate text-text-secondary">{env.value}</div> </td> </tr> ))} </tbody> </table> </div> - <div className='mt-4 flex gap-2'> + <div className="mt-4 flex gap-2"> <Checkbox - className='shrink-0' + className="shrink-0" checked={exportSecrets} onCheck={() => setExportSecrets(!exportSecrets)} /> - <div className='system-sm-medium cursor-pointer text-text-primary' onClick={() => setExportSecrets(!exportSecrets)}>{t('workflow.env.export.checkbox')}</div> + <div className="system-sm-medium cursor-pointer text-text-primary" onClick={() => setExportSecrets(!exportSecrets)}>{t('workflow.env.export.checkbox')}</div> </div> - <div className='flex flex-row-reverse pt-6'> - <Button className='ml-2' variant='primary' onClick={submit}>{exportSecrets ? t('workflow.env.export.export') : t('workflow.env.export.ignore')}</Button> + <div className="flex flex-row-reverse pt-6"> + <Button className="ml-2" variant="primary" onClick={submit}>{exportSecrets ? t('workflow.env.export.export') : t('workflow.env.export.ignore')}</Button> <Button onClick={onClose}>{t('common.operation.cancel')}</Button> </div> </Modal> diff --git a/web/app/components/workflow/features.tsx b/web/app/components/workflow/features.tsx index b54ffdf167..08c79a497a 100644 --- a/web/app/components/workflow/features.tsx +++ b/web/app/components/workflow/features.tsx @@ -1,19 +1,20 @@ +import type { StartNodeType } from './nodes/start/types' +import type { CommonNodeType, InputVar, Node } from './types' +import type { PromptVariable } from '@/models/debug' import { memo, useCallback, } from 'react' import { useNodes } from 'reactflow' -import { useStore } from './store' +import NewFeaturePanel from '@/app/components/base/features/new-feature-panel' import { useIsChatMode, useNodesReadOnly, useNodesSyncDraft, } from './hooks' -import { type CommonNodeType, type InputVar, InputVarType, type Node } from './types' import useConfig from './nodes/start/use-config' -import type { StartNodeType } from './nodes/start/types' -import type { PromptVariable } from '@/models/debug' -import NewFeaturePanel from '@/app/components/base/features/new-feature-panel' +import { useStore } from './store' +import { InputVarType } from './types' const Features = () => { const setShowFeaturesPanel = useStore(s => s.setShowFeaturesPanel) diff --git a/web/app/components/workflow/header/chat-variable-button.tsx b/web/app/components/workflow/header/chat-variable-button.tsx index b424ecffdc..86c88969ca 100644 --- a/web/app/components/workflow/header/chat-variable-button.tsx +++ b/web/app/components/workflow/header/chat-variable-button.tsx @@ -28,9 +28,9 @@ const ChatVariableButton = ({ disabled }: { disabled: boolean }) => { )} disabled={disabled} onClick={handleClick} - variant='ghost' + variant="ghost" > - <BubbleX className='h-4 w-4 text-components-button-secondary-text' /> + <BubbleX className="h-4 w-4 text-components-button-secondary-text" /> </Button> ) } diff --git a/web/app/components/workflow/header/checklist.tsx b/web/app/components/workflow/header/checklist.tsx index e284cca791..c984ae6c48 100644 --- a/web/app/components/workflow/header/checklist.tsx +++ b/web/app/components/workflow/header/checklist.tsx @@ -1,3 +1,12 @@ +import type { ChecklistItem } from '../hooks/use-checklist' +import type { + BlockEnum, + CommonEdgeType, +} from '../types' +import { + RiCloseLine, + RiListCheck3, +} from '@remixicon/react' import { memo, useState, @@ -6,34 +15,23 @@ import { useTranslation } from 'react-i18next' import { useEdges, } from 'reactflow' +import { Warning } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' +import { IconR } from '@/app/components/base/icons/src/vender/line/arrows' import { - RiCloseLine, - RiListCheck3, -} from '@remixicon/react' -import BlockIcon from '../block-icon' -import { - useChecklist, - useNodesInteractions, -} from '../hooks' -import type { ChecklistItem } from '../hooks/use-checklist' -import type { - CommonEdgeType, -} from '../types' -import { cn } from '@/utils/classnames' + ChecklistSquare, +} from '@/app/components/base/icons/src/vender/line/general' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { - ChecklistSquare, -} from '@/app/components/base/icons/src/vender/line/general' -import { Warning } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' -import { IconR } from '@/app/components/base/icons/src/vender/line/arrows' -import type { - BlockEnum, -} from '../types' import useNodes from '@/app/components/workflow/store/workflow/use-nodes' +import { cn } from '@/utils/classnames' +import BlockIcon from '../block-icon' +import { + useChecklist, + useNodesInteractions, +} from '../hooks' type WorkflowChecklistProps = { disabled: boolean @@ -65,7 +63,7 @@ const WorkflowChecklist = ({ return ( <PortalToFollowElem - placement='bottom-end' + placement="bottom-end" offset={{ mainAxis: 12, crossAxis: 4, @@ -89,35 +87,38 @@ const WorkflowChecklist = ({ </div> { !!needWarningNodes.length && ( - <div className='absolute -right-1.5 -top-1.5 flex h-[18px] min-w-[18px] items-center justify-center rounded-full border border-gray-100 bg-[#F79009] text-[11px] font-semibold text-white'> + <div className="absolute -right-1.5 -top-1.5 flex h-[18px] min-w-[18px] items-center justify-center rounded-full border border-gray-100 bg-[#F79009] text-[11px] font-semibold text-white"> {needWarningNodes.length} </div> ) } </div> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[12]'> + <PortalToFollowElemContent className="z-[12]"> <div - className='w-[420px] overflow-y-auto rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg' + className="w-[420px] overflow-y-auto rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg" style={{ maxHeight: 'calc(2 / 3 * 100vh)', }} > - <div className='text-md sticky top-0 z-[1] flex h-[44px] items-center bg-components-panel-bg pl-4 pr-3 pt-3 font-semibold text-text-primary'> - <div className='grow'>{t('workflow.panel.checklist')}{needWarningNodes.length ? `(${needWarningNodes.length})` : ''}</div> + <div className="text-md sticky top-0 z-[1] flex h-[44px] items-center bg-components-panel-bg pl-4 pr-3 pt-3 font-semibold text-text-primary"> + <div className="grow"> + {t('workflow.panel.checklist')} + {needWarningNodes.length ? `(${needWarningNodes.length})` : ''} + </div> <div - className='flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center' + className="flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center" onClick={() => setOpen(false)} > - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> - <div className='pb-2'> + <div className="pb-2"> { !!needWarningNodes.length && ( <> - <div className='px-4 pt-1 text-xs text-text-tertiary'>{t('workflow.panel.checklistTip')}</div> - <div className='px-4 py-2'> + <div className="px-4 pt-1 text-xs text-text-tertiary">{t('workflow.panel.checklistTip')}</div> + <div className="px-4 py-2"> { needWarningNodes.map(node => ( <div @@ -128,22 +129,22 @@ const WorkflowChecklist = ({ )} onClick={() => handleChecklistItemClick(node)} > - <div className='flex h-9 items-center p-2 text-xs font-medium text-text-secondary'> + <div className="flex h-9 items-center p-2 text-xs font-medium text-text-secondary"> <BlockIcon type={node.type as BlockEnum} - className='mr-1.5' + className="mr-1.5" toolIcon={node.toolIcon} /> - <span className='grow truncate'> + <span className="grow truncate"> {node.title} </span> { (showGoTo && node.canNavigate && !node.disableGoTo) && ( - <div className='flex h-4 w-[60px] shrink-0 items-center justify-center gap-1 opacity-0 transition-opacity duration-200 group-hover:opacity-100'> - <span className='whitespace-nowrap text-xs font-medium leading-4 text-primary-600'> + <div className="flex h-4 w-[60px] shrink-0 items-center justify-center gap-1 opacity-0 transition-opacity duration-200 group-hover:opacity-100"> + <span className="whitespace-nowrap text-xs font-medium leading-4 text-primary-600"> {t('workflow.panel.goTo')} </span> - <IconR className='h-3.5 w-3.5 text-primary-600' /> + <IconR className="h-3.5 w-3.5 text-primary-600" /> </div> ) } @@ -156,9 +157,9 @@ const WorkflowChecklist = ({ > { node.unConnected && ( - <div className='px-3 py-1 first:pt-1.5 last:pb-1.5'> - <div className='flex text-xs leading-4 text-text-tertiary'> - <Warning className='mr-2 mt-[2px] h-3 w-3 text-[#F79009]' /> + <div className="px-3 py-1 first:pt-1.5 last:pb-1.5"> + <div className="flex text-xs leading-4 text-text-tertiary"> + <Warning className="mr-2 mt-[2px] h-3 w-3 text-[#F79009]" /> {t('workflow.common.needConnectTip')} </div> </div> @@ -166,9 +167,9 @@ const WorkflowChecklist = ({ } { node.errorMessage && ( - <div className='px-3 py-1 first:pt-1.5 last:pb-1.5'> - <div className='flex text-xs leading-4 text-text-tertiary'> - <Warning className='mr-2 mt-[2px] h-3 w-3 text-[#F79009]' /> + <div className="px-3 py-1 first:pt-1.5 last:pb-1.5"> + <div className="flex text-xs leading-4 text-text-tertiary"> + <Warning className="mr-2 mt-[2px] h-3 w-3 text-[#F79009]" /> {node.errorMessage} </div> </div> @@ -184,8 +185,8 @@ const WorkflowChecklist = ({ } { !needWarningNodes.length && ( - <div className='mx-4 mb-3 rounded-lg bg-components-panel-bg py-4 text-center text-xs text-text-tertiary'> - <ChecklistSquare className='mx-auto mb-[5px] h-8 w-8 text-text-quaternary' /> + <div className="mx-4 mb-3 rounded-lg bg-components-panel-bg py-4 text-center text-xs text-text-tertiary"> + <ChecklistSquare className="mx-auto mb-[5px] h-8 w-8 text-text-quaternary" /> {t('workflow.panel.checklistResolved')} </div> ) diff --git a/web/app/components/workflow/header/editing-title.tsx b/web/app/components/workflow/header/editing-title.tsx index 81249b05bd..8fa03f3c26 100644 --- a/web/app/components/workflow/header/editing-title.tsx +++ b/web/app/components/workflow/header/editing-title.tsx @@ -1,7 +1,7 @@ import { memo } from 'react' import { useTranslation } from 'react-i18next' -import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' import { useStore } from '@/app/components/workflow/store' +import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' import useTimestamp from '@/hooks/use-timestamp' const EditingTitle = () => { @@ -18,11 +18,13 @@ const EditingTitle = () => { { !!draftUpdatedAt && ( <> - {t('workflow.common.autoSaved')} {formatTime(draftUpdatedAt / 1000, 'HH:mm:ss')} + {t('workflow.common.autoSaved')} + {' '} + {formatTime(draftUpdatedAt / 1000, 'HH:mm:ss')} </> ) } - <span className='mx-1 flex items-center'>·</span> + <span className="mx-1 flex items-center">·</span> { publishedAt ? `${t('workflow.common.published')} ${formatTimeFromNow(publishedAt)}` @@ -31,7 +33,7 @@ const EditingTitle = () => { { isSyncingWorkflowDraft && ( <> - <span className='mx-1 flex items-center'>·</span> + <span className="mx-1 flex items-center">·</span> {t('workflow.common.syncingData')} </> ) diff --git a/web/app/components/workflow/header/env-button.tsx b/web/app/components/workflow/header/env-button.tsx index f053097a0d..12dc193261 100644 --- a/web/app/components/workflow/header/env-button.tsx +++ b/web/app/components/workflow/header/env-button.tsx @@ -1,10 +1,10 @@ import { memo } from 'react' import Button from '@/app/components/base/button' import { Env } from '@/app/components/base/icons/src/vender/line/others' +import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks' import { useStore } from '@/app/components/workflow/store' import useTheme from '@/hooks/use-theme' import { cn } from '@/utils/classnames' -import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks' const EnvButton = ({ disabled }: { disabled: boolean }) => { const { theme } = useTheme() @@ -29,11 +29,11 @@ const EnvButton = ({ disabled }: { disabled: boolean }) => { 'p-2', theme === 'dark' && showEnvPanel && 'rounded-lg border border-black/5 bg-white/10 backdrop-blur-sm', )} - variant='ghost' + variant="ghost" disabled={disabled} onClick={handleClick} > - <Env className='h-4 w-4 text-components-button-secondary-text' /> + <Env className="h-4 w-4 text-components-button-secondary-text" /> </Button> ) } diff --git a/web/app/components/workflow/header/global-variable-button.tsx b/web/app/components/workflow/header/global-variable-button.tsx index 6859521aee..a2d78edc4b 100644 --- a/web/app/components/workflow/header/global-variable-button.tsx +++ b/web/app/components/workflow/header/global-variable-button.tsx @@ -1,10 +1,10 @@ import { memo } from 'react' import Button from '@/app/components/base/button' import { GlobalVariable } from '@/app/components/base/icons/src/vender/line/others' +import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks' import { useStore } from '@/app/components/workflow/store' import useTheme from '@/hooks/use-theme' import { cn } from '@/utils/classnames' -import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks' const GlobalVariableButton = ({ disabled }: { disabled: boolean }) => { const { theme } = useTheme() @@ -31,9 +31,9 @@ const GlobalVariableButton = ({ disabled }: { disabled: boolean }) => { )} disabled={disabled} onClick={handleClick} - variant='ghost' + variant="ghost" > - <GlobalVariable className='h-4 w-4 text-components-button-secondary-text' /> + <GlobalVariable className="h-4 w-4 text-components-button-secondary-text" /> </Button> ) } diff --git a/web/app/components/workflow/header/header-in-normal.tsx b/web/app/components/workflow/header/header-in-normal.tsx index 20fdafaff5..52ffee4ed5 100644 --- a/web/app/components/workflow/header/header-in-normal.tsx +++ b/web/app/components/workflow/header/header-in-normal.tsx @@ -1,26 +1,26 @@ +import type { StartNodeType } from '../nodes/start/types' +import type { RunAndHistoryProps } from './run-and-history' import { useCallback, } from 'react' import { useNodes } from 'reactflow' -import { - useStore, - useWorkflowStore, -} from '../store' -import type { StartNodeType } from '../nodes/start/types' +import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks' +import Divider from '../../base/divider' import { useNodesInteractions, useNodesReadOnly, useWorkflowRun, } from '../hooks' -import Divider from '../../base/divider' -import type { RunAndHistoryProps } from './run-and-history' -import RunAndHistory from './run-and-history' +import { + useStore, + useWorkflowStore, +} from '../store' import EditingTitle from './editing-title' import EnvButton from './env-button' -import VersionHistoryButton from './version-history-button' -import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks' -import ScrollToSelectedNodeButton from './scroll-to-selected-node-button' import GlobalVariableButton from './global-variable-button' +import RunAndHistory from './run-and-history' +import ScrollToSelectedNodeButton from './scroll-to-selected-node-button' +import VersionHistoryButton from './version-history-button' export type HeaderInNormalProps = { components?: { @@ -64,18 +64,18 @@ const HeaderInNormal = ({ }, [workflowStore, handleBackupDraft, selectedNode, handleNodeSelect, setShowWorkflowVersionHistoryPanel, setShowEnvPanel, setShowDebugAndPreviewPanel, setShowVariableInspectPanel, setShowChatVariablePanel, setShowGlobalVariablePanel]) return ( - <div className='flex w-full items-center justify-between'> + <div className="flex w-full items-center justify-between"> <div> <EditingTitle /> </div> <div> <ScrollToSelectedNodeButton /> </div> - <div className='flex items-center gap-2'> + <div className="flex items-center gap-2"> {components?.left} - <Divider type='vertical' className='mx-auto h-3.5' /> + <Divider type="vertical" className="mx-auto h-3.5" /> <RunAndHistory {...runAndHistoryProps} /> - <div className='shrink-0 cursor-pointer rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg shadow-xs backdrop-blur-[10px]'> + <div className="shrink-0 cursor-pointer rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg shadow-xs backdrop-blur-[10px]"> {components?.chatVariableTrigger} <EnvButton disabled={nodesReadOnly} /> <GlobalVariableButton disabled={nodesReadOnly} /> diff --git a/web/app/components/workflow/header/header-in-restoring.tsx b/web/app/components/workflow/header/header-in-restoring.tsx index 53abe2375d..2abe031b61 100644 --- a/web/app/components/workflow/header/header-in-restoring.tsx +++ b/web/app/components/workflow/header/header-in-restoring.tsx @@ -1,8 +1,18 @@ +import { RiHistoryLine } from '@remixicon/react' import { useCallback, } from 'react' -import { RiHistoryLine } from '@remixicon/react' import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' +import useTheme from '@/hooks/use-theme' +import { useInvalidAllLastRun } from '@/service/use-workflow' +import { cn } from '@/utils/classnames' +import Toast from '../../base/toast' +import { + useNodesSyncDraft, + useWorkflowRun, +} from '../hooks' +import { useHooksStore } from '../hooks-store' import { useStore, useWorkflowStore, @@ -10,17 +20,7 @@ import { import { WorkflowVersion, } from '../types' -import { - useNodesSyncDraft, - useWorkflowRun, -} from '../hooks' -import Toast from '../../base/toast' import RestoringTitle from './restoring-title' -import Button from '@/app/components/base/button' -import { useInvalidAllLastRun } from '@/service/use-workflow' -import { useHooksStore } from '../hooks-store' -import useTheme from '@/hooks/use-theme' -import { cn } from '@/utils/classnames' export type HeaderInRestoringProps = { onRestoreSettled?: () => void @@ -80,11 +80,11 @@ const HeaderInRestoring = ({ <div> <RestoringTitle /> </div> - <div className=' flex items-center justify-end gap-x-2'> + <div className=" flex items-center justify-end gap-x-2"> <Button onClick={handleRestore} disabled={!currentVersion || currentVersion.version === WorkflowVersion.Draft} - variant='primary' + variant="primary" className={cn( theme === 'dark' && 'rounded-lg border border-black/5 bg-white/10 backdrop-blur-sm', )} @@ -98,9 +98,9 @@ const HeaderInRestoring = ({ theme === 'dark' && 'rounded-lg border border-black/5 bg-white/10 backdrop-blur-sm', )} > - <div className='flex items-center gap-x-0.5'> - <RiHistoryLine className='h-4 w-4' /> - <span className='px-0.5'>{t('workflow.common.exitVersions')}</span> + <div className="flex items-center gap-x-0.5"> + <RiHistoryLine className="h-4 w-4" /> + <span className="px-0.5">{t('workflow.common.exitVersions')}</span> </div> </Button> </div> 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 70189a9469..17b1dea52f 100644 --- a/web/app/components/workflow/header/header-in-view-history.tsx +++ b/web/app/components/workflow/header/header-in-view-history.tsx @@ -1,19 +1,19 @@ +import type { ViewHistoryProps } from './view-history' import { useCallback, } from 'react' import { useTranslation } from 'react-i18next' -import { - useWorkflowStore, -} from '../store' +import Button from '@/app/components/base/button' +import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows' +import Divider from '../../base/divider' import { useWorkflowRun, } from '../hooks' -import Divider from '../../base/divider' +import { + useWorkflowStore, +} from '../store' import RunningTitle from './running-title' -import type { ViewHistoryProps } from './view-history' import ViewHistory from './view-history' -import Button from '@/app/components/base/button' -import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows' export type HeaderInHistoryProps = { viewHistoryProps?: ViewHistoryProps @@ -38,14 +38,14 @@ const HeaderInHistory = ({ <div> <RunningTitle /> </div> - <div className='flex items-center space-x-2'> + <div className="flex items-center space-x-2"> <ViewHistory {...viewHistoryProps} withText /> - <Divider type='vertical' className='mx-auto h-3.5' /> + <Divider type="vertical" className="mx-auto h-3.5" /> <Button - variant='primary' + variant="primary" onClick={handleGoBackToEdit} > - <ArrowNarrowLeft className='mr-1 h-4 w-4' /> + <ArrowNarrowLeft className="mr-1 h-4 w-4" /> {t('workflow.common.goBackToEdit')} </Button> </div> diff --git a/web/app/components/workflow/header/index.tsx b/web/app/components/workflow/header/index.tsx index e37a92638a..0590c016f2 100644 --- a/web/app/components/workflow/header/index.tsx +++ b/web/app/components/workflow/header/index.tsx @@ -1,13 +1,13 @@ +import type { HeaderInNormalProps } from './header-in-normal' +import type { HeaderInRestoringProps } from './header-in-restoring' +import type { HeaderInHistoryProps } from './header-in-view-history' +import dynamic from 'next/dynamic' import { usePathname } from 'next/navigation' import { useWorkflowMode, } from '../hooks' -import type { HeaderInNormalProps } from './header-in-normal' -import HeaderInNormal from './header-in-normal' -import type { HeaderInHistoryProps } from './header-in-view-history' -import type { HeaderInRestoringProps } from './header-in-restoring' import { useStore } from '../store' -import dynamic from 'next/dynamic' +import HeaderInNormal from './header-in-normal' const HeaderInHistory = dynamic(() => import('./header-in-view-history'), { ssr: false, @@ -38,9 +38,9 @@ const Header = ({ return ( <div - className='absolute left-0 top-7 z-10 flex h-0 w-full items-center justify-between bg-mask-top2bottom-gray-50-to-transparent px-3' + className="absolute left-0 top-7 z-10 flex h-0 w-full items-center justify-between bg-mask-top2bottom-gray-50-to-transparent px-3" > - {(inWorkflowCanvas || isPipelineCanvas) && maximizeCanvas && <div className='h-14 w-[52px]' />} + {(inWorkflowCanvas || isPipelineCanvas) && maximizeCanvas && <div className="h-14 w-[52px]" />} { normal && ( <HeaderInNormal diff --git a/web/app/components/workflow/header/restoring-title.tsx b/web/app/components/workflow/header/restoring-title.tsx index e6631d3684..737fe4fec5 100644 --- a/web/app/components/workflow/header/restoring-title.tsx +++ b/web/app/components/workflow/header/restoring-title.tsx @@ -1,9 +1,9 @@ import { memo, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' +import useTimestamp from '@/hooks/use-timestamp' import { useStore } from '../store' import { WorkflowVersion } from '../types' -import useTimestamp from '@/hooks/use-timestamp' const RestoringTitle = () => { const { t } = useTranslation() @@ -20,16 +20,16 @@ const RestoringTitle = () => { }, [currentVersion, t, isDraft]) return ( - <div className='flex flex-col gap-y-0.5'> - <div className='flex items-center gap-x-1'> - <span className='system-sm-semibold text-text-primary'> + <div className="flex flex-col gap-y-0.5"> + <div className="flex items-center gap-x-1"> + <span className="system-sm-semibold text-text-primary"> {versionName} </span> - <span className='system-2xs-medium-uppercase rounded-[5px] border border-text-accent-secondary bg-components-badge-bg-dimm px-1 py-0.5 text-text-accent-secondary'> + <span className="system-2xs-medium-uppercase rounded-[5px] border border-text-accent-secondary bg-components-badge-bg-dimm px-1 py-0.5 text-text-accent-secondary"> {t('workflow.common.viewOnly')} </span> </div> - <div className='system-xs-regular flex h-4 items-center gap-x-1 text-text-tertiary'> + <div className="system-xs-regular flex h-4 items-center gap-x-1 text-text-tertiary"> { currentVersion && ( <> diff --git a/web/app/components/workflow/header/run-and-history.tsx b/web/app/components/workflow/header/run-and-history.tsx index ae4b462b29..f4b115e255 100644 --- a/web/app/components/workflow/header/run-and-history.tsx +++ b/web/app/components/workflow/header/run-and-history.tsx @@ -1,17 +1,17 @@ -import { memo } from 'react' -import { useTranslation } from 'react-i18next' +import type { ViewHistoryProps } from './view-history' import { RiPlayLargeLine, } from '@remixicon/react' +import { memo } from 'react' +import { useTranslation } from 'react-i18next' +import { cn } from '@/utils/classnames' import { useNodesReadOnly, useWorkflowStartRun, } from '../hooks' -import type { ViewHistoryProps } from './view-history' -import ViewHistory from './view-history' import Checklist from './checklist' -import { cn } from '@/utils/classnames' import RunMode from './run-mode' +import ViewHistory from './view-history' const PreviewMode = memo(() => { const { t } = useTranslation() @@ -25,7 +25,7 @@ const PreviewMode = memo(() => { )} onClick={() => handleWorkflowStartRunInChatflow()} > - <RiPlayLargeLine className='mr-1 h-4 w-4' /> + <RiPlayLargeLine className="mr-1 h-4 w-4" /> {t('workflow.common.debugAndPreview')} </div> ) @@ -56,7 +56,7 @@ const RunAndHistory = ({ const { RunMode: CustomRunMode } = components || {} return ( - <div className='flex h-8 items-center rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-0.5 shadow-xs'> + <div className="flex h-8 items-center rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-0.5 shadow-xs"> { showRunButton && ( CustomRunMode ? <CustomRunMode text={runButtonText} /> : <RunMode text={runButtonText} /> @@ -65,7 +65,7 @@ const RunAndHistory = ({ { showPreviewButton && <PreviewMode /> } - <div className='mx-0.5 h-3.5 w-[1px] bg-divider-regular'></div> + <div className="mx-0.5 h-3.5 w-[1px] bg-divider-regular"></div> <ViewHistory {...viewHistoryProps} /> <Checklist disabled={nodesReadOnly} /> </div> diff --git a/web/app/components/workflow/header/run-mode.tsx b/web/app/components/workflow/header/run-mode.tsx index 6ab826cc48..82e33b5c30 100644 --- a/web/app/components/workflow/header/run-mode.tsx +++ b/web/app/components/workflow/header/run-mode.tsx @@ -1,18 +1,19 @@ +import type { TestRunMenuRef, TriggerOption } from './test-run-menu' +import { RiLoader2Line, RiPlayLargeLine } from '@remixicon/react' import React, { 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 { useToastContext } from '@/app/components/base/toast' import { useWorkflowRun, useWorkflowRunValidation, useWorkflowStartRun } from '@/app/components/workflow/hooks' import { useStore } from '@/app/components/workflow/store' import { WorkflowRunningStatus } from '@/app/components/workflow/types' -import { useEventEmitterContextContext } from '@/context/event-emitter' -import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types' import { getKeyboardKeyNameBySystem } from '@/app/components/workflow/utils' +import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types' +import { useEventEmitterContextContext } from '@/context/event-emitter' import { cn } from '@/utils/classnames' -import { RiLoader2Line, RiPlayLargeLine } from '@remixicon/react' -import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' import { useDynamicTestRunOptions } from '../hooks/use-dynamic-test-run-options' -import TestRunMenu, { type TestRunMenuRef, type TriggerOption, TriggerType } from './test-run-menu' -import { useToastContext } from '@/app/components/base/toast' -import { trackEvent } from '@/app/components/base/amplitude' +import TestRunMenu, { TriggerType } from './test-run-menu' type RunModeProps = { text?: string @@ -112,57 +113,57 @@ const RunMode = ({ }) return ( - <div className='flex items-center gap-x-px'> + <div className="flex items-center gap-x-px"> { isRunning ? ( - <button - type='button' - className={cn( - 'system-xs-medium flex h-7 cursor-not-allowed items-center gap-x-1 rounded-l-md bg-state-accent-hover px-1.5 text-text-accent', - )} - disabled={true} - > - <RiLoader2Line className='mr-1 size-4 animate-spin' /> - {isListening ? t('workflow.common.listening') : t('workflow.common.running')} - </button> - ) - : ( - <TestRunMenu - ref={testRunMenuRef} - options={dynamicOptions} - onSelect={handleTriggerSelect} - > - <div + <button + type="button" className={cn( - 'system-xs-medium flex h-7 cursor-pointer items-center gap-x-1 rounded-md px-1.5 text-text-accent hover:bg-state-accent-hover', + 'system-xs-medium flex h-7 cursor-not-allowed items-center gap-x-1 rounded-l-md bg-state-accent-hover px-1.5 text-text-accent', )} - style={{ userSelect: 'none' }} + disabled={true} > - <RiPlayLargeLine className='mr-1 size-4' /> - {text ?? t('workflow.common.run')} - <div className='system-kbd flex items-center gap-x-0.5 text-text-tertiary'> - <div className='flex size-4 items-center justify-center rounded-[4px] bg-components-kbd-bg-gray'> - {getKeyboardKeyNameBySystem('alt')} - </div> - <div className='flex size-4 items-center justify-center rounded-[4px] bg-components-kbd-bg-gray'> - R + <RiLoader2Line className="mr-1 size-4 animate-spin" /> + {isListening ? t('workflow.common.listening') : t('workflow.common.running')} + </button> + ) + : ( + <TestRunMenu + ref={testRunMenuRef} + options={dynamicOptions} + onSelect={handleTriggerSelect} + > + <div + className={cn( + 'system-xs-medium flex h-7 cursor-pointer items-center gap-x-1 rounded-md px-1.5 text-text-accent hover:bg-state-accent-hover', + )} + style={{ userSelect: 'none' }} + > + <RiPlayLargeLine className="mr-1 size-4" /> + {text ?? t('workflow.common.run')} + <div className="system-kbd flex items-center gap-x-0.5 text-text-tertiary"> + <div className="flex size-4 items-center justify-center rounded-[4px] bg-components-kbd-bg-gray"> + {getKeyboardKeyNameBySystem('alt')} + </div> + <div className="flex size-4 items-center justify-center rounded-[4px] bg-components-kbd-bg-gray"> + R + </div> </div> </div> - </div> - </TestRunMenu> - ) + </TestRunMenu> + ) } { isRunning && ( <button - type='button' + type="button" className={cn( 'flex size-7 items-center justify-center rounded-r-md bg-state-accent-active', )} onClick={handleStop} > - <StopCircle className='size-4 text-text-accent' /> + <StopCircle className="size-4 text-text-accent" /> </button> ) } diff --git a/web/app/components/workflow/header/running-title.tsx b/web/app/components/workflow/header/running-title.tsx index 95e763ead7..80a9361983 100644 --- a/web/app/components/workflow/header/running-title.tsx +++ b/web/app/components/workflow/header/running-title.tsx @@ -1,9 +1,9 @@ import { memo } from 'react' import { useTranslation } from 'react-i18next' +import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time' import { useIsChatMode } from '../hooks' import { useStore } from '../store' import { formatWorkflowRunIdentifier } from '../utils' -import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time' const RunningTitle = () => { const { t } = useTranslation() @@ -11,11 +11,11 @@ const RunningTitle = () => { const historyWorkflowData = useStore(s => s.historyWorkflowData) return ( - <div className='flex h-[18px] items-center text-xs text-gray-500'> - <ClockPlay className='mr-1 h-3 w-3 text-gray-500' /> + <div className="flex h-[18px] items-center text-xs text-gray-500"> + <ClockPlay className="mr-1 h-3 w-3 text-gray-500" /> <span>{isChatMode ? `Test Chat${formatWorkflowRunIdentifier(historyWorkflowData?.finished_at)}` : `Test Run${formatWorkflowRunIdentifier(historyWorkflowData?.finished_at)}`}</span> - <span className='mx-1'>·</span> - <span className='ml-1 flex h-[18px] items-center rounded-[5px] border border-indigo-300 bg-white/[0.48] px-1 text-[10px] font-semibold uppercase text-indigo-600'> + <span className="mx-1">·</span> + <span className="ml-1 flex h-[18px] items-center rounded-[5px] border border-indigo-300 bg-white/[0.48] px-1 text-[10px] font-semibold uppercase text-indigo-600"> {t('workflow.common.viewOnly')} </span> </div> diff --git a/web/app/components/workflow/header/scroll-to-selected-node-button.tsx b/web/app/components/workflow/header/scroll-to-selected-node-button.tsx index 58aeccea1b..6606df5538 100644 --- a/web/app/components/workflow/header/scroll-to-selected-node-button.tsx +++ b/web/app/components/workflow/header/scroll-to-selected-node-button.tsx @@ -1,10 +1,10 @@ import type { FC } from 'react' -import { useCallback } from 'react' -import { useNodes } from 'reactflow' -import { useTranslation } from 'react-i18next' import type { CommonNodeType } from '../types' -import { scrollToWorkflowNode } from '../utils/node-navigation' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import { useNodes } from 'reactflow' import { cn } from '@/utils/classnames' +import { scrollToWorkflowNode } from '../utils/node-navigation' const ScrollToSelectedNodeButton: FC = () => { const { t } = useTranslation() @@ -12,7 +12,8 @@ const ScrollToSelectedNodeButton: FC = () => { const selectedNode = nodes.find(node => node.data.selected) const handleScrollToSelectedNode = useCallback(() => { - if (!selectedNode) return + if (!selectedNode) + return scrollToWorkflowNode(selectedNode.id) }, [selectedNode]) diff --git a/web/app/components/workflow/header/test-run-menu.tsx b/web/app/components/workflow/header/test-run-menu.tsx index 40aabab6f8..8f4af3b592 100644 --- a/web/app/components/workflow/header/test-run-menu.tsx +++ b/web/app/components/workflow/header/test-run-menu.tsx @@ -1,10 +1,9 @@ +import type { MouseEvent, MouseEventHandler, ReactElement } from 'react' import { - type MouseEvent, - type MouseEventHandler, - type ReactElement, cloneElement, forwardRef, isValidElement, + useCallback, useEffect, useImperativeHandle, @@ -158,14 +157,14 @@ const TestRunMenu = forwardRef<TestRunMenuRef, TestRunMenuProps>(({ return ( <div key={option.id} - className='system-md-regular flex cursor-pointer items-center rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover' + className="system-md-regular flex cursor-pointer items-center rounded-lg px-3 py-1.5 text-text-secondary hover:bg-state-base-hover" onClick={() => handleSelect(option)} > - <div className='flex min-w-0 flex-1 items-center'> - <div className='flex h-6 w-6 shrink-0 items-center justify-center'> + <div className="flex min-w-0 flex-1 items-center"> + <div className="flex h-6 w-6 shrink-0 items-center justify-center"> {option.icon} </div> - <span className='ml-2 truncate'>{option.name}</span> + <span className="ml-2 truncate">{option.name}</span> </div> {shortcutKey && ( <ShortcutsName keys={[shortcutKey]} className="ml-2" textColor="secondary" /> @@ -214,7 +213,7 @@ const TestRunMenu = forwardRef<TestRunMenuRef, TestRunMenuProps>(({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 8, crossAxis: -4 }} > <PortalToFollowElemTrigger asChild onClick={() => setOpen(!open)}> @@ -222,16 +221,16 @@ const TestRunMenu = forwardRef<TestRunMenuRef, TestRunMenuProps>(({ {children} </div> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[12]'> - <div className='w-[284px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-1 shadow-lg'> - <div className='mb-2 px-3 pt-2 text-sm font-medium text-text-primary'> + <PortalToFollowElemContent className="z-[12]"> + <div className="w-[284px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-1 shadow-lg"> + <div className="mb-2 px-3 pt-2 text-sm font-medium text-text-primary"> {t('workflow.common.chooseStartNodeToRun')} </div> <div> {hasUserInput && renderOption(options.userInput!)} {(hasTriggers || hasRunAll) && hasUserInput && ( - <div className='mx-3 my-1 h-px bg-divider-subtle' /> + <div className="mx-3 my-1 h-px bg-divider-subtle" /> )} {hasRunAll && renderOption(options.runAll!)} diff --git a/web/app/components/workflow/header/undo-redo.tsx b/web/app/components/workflow/header/undo-redo.tsx index 4b2e9abc36..27e9af2865 100644 --- a/web/app/components/workflow/header/undo-redo.tsx +++ b/web/app/components/workflow/header/undo-redo.tsx @@ -1,18 +1,18 @@ import type { FC } from 'react' -import { memo, useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' import { RiArrowGoBackLine, RiArrowGoForwardFill, } from '@remixicon/react' +import { memo, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import ViewWorkflowHistory from '@/app/components/workflow/header/view-workflow-history' +import { useNodesReadOnly } from '@/app/components/workflow/hooks' +import { cn } from '@/utils/classnames' +import Divider from '../../base/divider' import TipPopup from '../operator/tip-popup' import { useWorkflowHistoryStore } from '../workflow-history-store' -import Divider from '../../base/divider' -import { useNodesReadOnly } from '@/app/components/workflow/hooks' -import ViewWorkflowHistory from '@/app/components/workflow/header/view-workflow-history' -import { cn } from '@/utils/classnames' -export type UndoRedoProps = { handleUndo: () => void; handleRedo: () => void } +export type UndoRedoProps = { handleUndo: () => void, handleRedo: () => void } const UndoRedo: FC<UndoRedoProps> = ({ handleUndo, handleRedo }) => { const { t } = useTranslation() const { store } = useWorkflowHistoryStore() @@ -31,34 +31,34 @@ const UndoRedo: FC<UndoRedoProps> = ({ handleUndo, handleRedo }) => { const { nodesReadOnly } = useNodesReadOnly() return ( - <div className='flex items-center space-x-0.5 rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-lg backdrop-blur-[5px]'> + <div className="flex items-center space-x-0.5 rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-lg backdrop-blur-[5px]"> <TipPopup title={t('workflow.common.undo')!} shortcuts={['ctrl', 'z']}> <div - data-tooltip-id='workflow.undo' + data-tooltip-id="workflow.undo" className={ - cn('system-sm-medium flex h-8 w-8 cursor-pointer select-none items-center rounded-md px-1.5 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary', - (nodesReadOnly || buttonsDisabled.undo) - && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled')} + cn('system-sm-medium flex h-8 w-8 cursor-pointer select-none items-center rounded-md px-1.5 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary', (nodesReadOnly || buttonsDisabled.undo) + && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled') + } onClick={() => !nodesReadOnly && !buttonsDisabled.undo && handleUndo()} > - <RiArrowGoBackLine className='h-4 w-4' /> - </div> - </TipPopup > - <TipPopup title={t('workflow.common.redo')!} shortcuts={['ctrl', 'y']}> - <div - data-tooltip-id='workflow.redo' - className={ - cn('system-sm-medium flex h-8 w-8 cursor-pointer select-none items-center rounded-md px-1.5 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary', - (nodesReadOnly || buttonsDisabled.redo) - && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled')} - onClick={() => !nodesReadOnly && !buttonsDisabled.redo && handleRedo()} - > - <RiArrowGoForwardFill className='h-4 w-4' /> + <RiArrowGoBackLine className="h-4 w-4" /> </div> </TipPopup> - <Divider type='vertical' className="mx-0.5 h-3.5" /> + <TipPopup title={t('workflow.common.redo')!} shortcuts={['ctrl', 'y']}> + <div + data-tooltip-id="workflow.redo" + className={ + cn('system-sm-medium flex h-8 w-8 cursor-pointer select-none items-center rounded-md px-1.5 text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary', (nodesReadOnly || buttonsDisabled.redo) + && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled') + } + onClick={() => !nodesReadOnly && !buttonsDisabled.redo && handleRedo()} + > + <RiArrowGoForwardFill className="h-4 w-4" /> + </div> + </TipPopup> + <Divider type="vertical" className="mx-0.5 h-3.5" /> <ViewWorkflowHistory /> - </div > + </div> ) } diff --git a/web/app/components/workflow/header/version-history-button.tsx b/web/app/components/workflow/header/version-history-button.tsx index 416c6ef7e5..b29608a022 100644 --- a/web/app/components/workflow/header/version-history-button.tsx +++ b/web/app/components/workflow/header/version-history-button.tsx @@ -1,12 +1,13 @@ -import React, { type FC, useCallback } from 'react' +import type { FC } from 'react' import { RiHistoryLine } from '@remixicon/react' -import { useTranslation } from 'react-i18next' import { useKeyPress } from 'ahooks' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import useTheme from '@/hooks/use-theme' +import { cn } from '@/utils/classnames' import Button from '../../base/button' import Tooltip from '../../base/tooltip' import { getKeyboardKeyCodeBySystem, getKeyboardKeyNameBySystem } from '../utils' -import useTheme from '@/hooks/use-theme' -import { cn } from '@/utils/classnames' type VersionHistoryButtonProps = { onClick: () => Promise<unknown> | unknown @@ -17,15 +18,15 @@ const VERSION_HISTORY_SHORTCUT = ['ctrl', '⇧', 'H'] const PopupContent = React.memo(() => { const { t } = useTranslation() return ( - <div className='flex items-center gap-x-1'> - <div className='system-xs-medium px-0.5 text-text-secondary'> + <div className="flex items-center gap-x-1"> + <div className="system-xs-medium px-0.5 text-text-secondary"> {t('workflow.common.versionHistory')} </div> - <div className='flex items-center gap-x-0.5'> + <div className="flex items-center gap-x-0.5"> {VERSION_HISTORY_SHORTCUT.map(key => ( <span key={key} - className='system-kbd rounded-[4px] bg-components-kbd-bg-white px-[1px] text-text-tertiary' + className="system-kbd rounded-[4px] bg-components-kbd-bg-white px-[1px] text-text-tertiary" > {getKeyboardKeyNameBySystem(key)} </span> @@ -50,22 +51,24 @@ const VersionHistoryButton: FC<VersionHistoryButtonProps> = ({ handleViewVersionHistory() }, { exactMatch: true, useCapture: true }) - return <Tooltip - popupContent={<PopupContent />} - noDecoration - popupClassName='rounded-lg border-[0.5px] border-components-panel-border bg-components-tooltip-bg - shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px] p-1.5' - > - <Button - className={cn( - 'p-2', - theme === 'dark' && 'rounded-lg border border-black/5 bg-white/10 backdrop-blur-sm', - )} - onClick={handleViewVersionHistory} + return ( + <Tooltip + popupContent={<PopupContent />} + noDecoration + popupClassName="rounded-lg border-[0.5px] border-components-panel-border bg-components-tooltip-bg + shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px] p-1.5" > - <RiHistoryLine className='h-4 w-4 text-components-button-secondary-text' /> - </Button> - </Tooltip> + <Button + className={cn( + 'p-2', + theme === 'dark' && 'rounded-lg border border-black/5 bg-white/10 backdrop-blur-sm', + )} + onClick={handleViewVersionHistory} + > + <RiHistoryLine className="h-4 w-4 text-components-button-secondary-text" /> + </Button> + </Tooltip> + ) } export default VersionHistoryButton diff --git a/web/app/components/workflow/header/view-history.tsx b/web/app/components/workflow/header/view-history.tsx index 7e9e0ee3bb..63a2dd25ab 100644 --- a/web/app/components/workflow/header/view-history.tsx +++ b/web/app/components/workflow/header/view-history.tsx @@ -1,44 +1,44 @@ -import { - memo, - useState, -} from 'react' import type { Fetcher } from 'swr' -import useSWR from 'swr' -import { useTranslation } from 'react-i18next' -import { noop } from 'lodash-es' +import type { WorkflowRunHistoryResponse } from '@/types/workflow' import { RiCheckboxCircleLine, RiCloseLine, RiErrorWarningLine, } from '@remixicon/react' +import { noop } from 'lodash-es' import { - useIsChatMode, - useNodesInteractions, - useWorkflowInteractions, - useWorkflowRun, -} from '../hooks' -import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' -import { ControlMode, WorkflowRunningStatus } from '../types' -import { formatWorkflowRunIdentifier } from '../utils' -import { cn } from '@/utils/classnames' + memo, + useState, +} from 'react' +import { useTranslation } from 'react-i18next' +import useSWR from 'swr' +import { AlertTriangle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' +import { + ClockPlay, + ClockPlaySlim, +} from '@/app/components/base/icons/src/vender/line/time' +import Loading from '@/app/components/base/loading' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' import Tooltip from '@/app/components/base/tooltip' -import { - ClockPlay, - ClockPlaySlim, -} from '@/app/components/base/icons/src/vender/line/time' -import { AlertTriangle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' -import Loading from '@/app/components/base/loading' +import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks' import { useStore, useWorkflowStore, } from '@/app/components/workflow/store' -import type { WorkflowRunHistoryResponse } from '@/types/workflow' -import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks' +import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' +import { cn } from '@/utils/classnames' +import { + useIsChatMode, + useNodesInteractions, + useWorkflowInteractions, + useWorkflowRun, +} from '../hooks' +import { ControlMode, WorkflowRunningStatus } from '../types' +import { formatWorkflowRunIdentifier } from '../utils' export type ViewHistoryProps = { withText?: boolean @@ -92,9 +92,10 @@ const ViewHistory = ({ 'flex h-8 items-center rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-3 shadow-xs', 'cursor-pointer text-[13px] font-medium text-components-button-secondary-text hover:bg-components-button-secondary-bg-hover', open && 'bg-components-button-secondary-bg-hover', - )}> + )} + > <ClockPlay - className={'mr-1 h-4 w-4'} + className="mr-1 h-4 w-4" /> {t('workflow.common.showRunHistory')} </div> @@ -117,40 +118,40 @@ const ViewHistory = ({ ) } </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[12]'> + <PortalToFollowElemContent className="z-[12]"> <div - className='ml-2 flex w-[240px] flex-col overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl' + className="ml-2 flex w-[240px] flex-col overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl" style={{ maxHeight: 'calc(2 / 3 * 100vh)', }} > - <div className='sticky top-0 flex items-center justify-between bg-components-panel-bg px-4 pt-3 text-base font-semibold text-text-primary'> - <div className='grow'>{t('workflow.common.runHistory')}</div> + <div className="sticky top-0 flex items-center justify-between bg-components-panel-bg px-4 pt-3 text-base font-semibold text-text-primary"> + <div className="grow">{t('workflow.common.runHistory')}</div> <div - className='flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center' + className="flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center" onClick={() => { onClearLogAndMessageModal?.() setOpen(false) }} > - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> { isLoading && ( - <div className='flex h-10 items-center justify-center'> + <div className="flex h-10 items-center justify-center"> <Loading /> </div> ) } { !isLoading && ( - <div className='p-2'> + <div className="p-2"> { !data?.data.length && ( - <div className='py-12'> - <ClockPlaySlim className='mx-auto mb-2 h-8 w-8 text-text-quaternary' /> - <div className='text-center text-[13px] text-text-quaternary'> + <div className="py-12"> + <ClockPlaySlim className="mx-auto mb-2 h-8 w-8 text-text-quaternary" /> + <div className="text-center text-[13px] text-text-quaternary"> {t('workflow.common.notRunning')} </div> </div> @@ -180,17 +181,17 @@ const ViewHistory = ({ > { !isChatMode && item.status === WorkflowRunningStatus.Stopped && ( - <AlertTriangle className='mr-1.5 mt-0.5 h-3.5 w-3.5 text-[#F79009]' /> + <AlertTriangle className="mr-1.5 mt-0.5 h-3.5 w-3.5 text-[#F79009]" /> ) } { !isChatMode && item.status === WorkflowRunningStatus.Failed && ( - <RiErrorWarningLine className='mr-1.5 mt-0.5 h-3.5 w-3.5 text-[#F04438]' /> + <RiErrorWarningLine className="mr-1.5 mt-0.5 h-3.5 w-3.5 text-[#F04438]" /> ) } { !isChatMode && item.status === WorkflowRunningStatus.Succeeded && ( - <RiCheckboxCircleLine className='mr-1.5 mt-0.5 h-3.5 w-3.5 text-[#12B76A]' /> + <RiCheckboxCircleLine className="mr-1.5 mt-0.5 h-3.5 w-3.5 text-[#12B76A]" /> ) } <div> @@ -202,8 +203,11 @@ const ViewHistory = ({ > {`Test ${isChatMode ? 'Chat' : 'Run'}${formatWorkflowRunIdentifier(item.finished_at)}`} </div> - <div className='flex items-center text-xs leading-[18px] text-text-tertiary'> - {item.created_by_account?.name} · {formatTimeFromNow((item.finished_at || item.created_at) * 1000)} + <div className="flex items-center text-xs leading-[18px] text-text-tertiary"> + {item.created_by_account?.name} + {' '} + · + {formatTimeFromNow((item.finished_at || item.created_at) * 1000)} </div> </div> </div> diff --git a/web/app/components/workflow/header/view-workflow-history.tsx b/web/app/components/workflow/header/view-workflow-history.tsx index bfef85382e..3d52247075 100644 --- a/web/app/components/workflow/header/view-workflow-history.tsx +++ b/web/app/components/workflow/header/view-workflow-history.tsx @@ -1,30 +1,30 @@ +import type { WorkflowHistoryState } from '../workflow-history-store' +import { + RiCloseLine, + RiHistoryLine, +} from '@remixicon/react' import { memo, useCallback, useMemo, useState, } from 'react' -import { - RiCloseLine, - RiHistoryLine, -} from '@remixicon/react' import { useTranslation } from 'react-i18next' -import { useShallow } from 'zustand/react/shallow' import { useStoreApi } from 'reactflow' -import { - useNodesReadOnly, - useWorkflowHistory, -} from '../hooks' -import TipPopup from '../operator/tip-popup' -import type { WorkflowHistoryState } from '../workflow-history-store' -import Divider from '../../base/divider' +import { useShallow } from 'zustand/react/shallow' +import { useStore as useAppStore } from '@/app/components/app/store' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { useStore as useAppStore } from '@/app/components/app/store' import { cn } from '@/utils/classnames' +import Divider from '../../base/divider' +import { + useNodesReadOnly, + useWorkflowHistory, +} from '../hooks' +import TipPopup from '../operator/tip-popup' type ChangeHistoryEntry = { label: string @@ -96,10 +96,12 @@ const ViewWorkflowHistory = () => { index: reverse ? list.length - 1 - index - startIndex : index - startIndex, state: { ...state, - workflowHistoryEventMeta: state.workflowHistoryEventMeta ? { - ...state.workflowHistoryEventMeta, - nodeTitle: state.workflowHistoryEventMeta.nodeTitle || targetTitle, - } : undefined, + workflowHistoryEventMeta: state.workflowHistoryEventMeta + ? { + ...state.workflowHistoryEventMeta, + nodeTitle: state.workflowHistoryEventMeta.nodeTitle || targetTitle, + } + : undefined, }, } }).filter(Boolean) @@ -127,7 +129,7 @@ const ViewWorkflowHistory = () => { return ( ( <PortalToFollowElem - placement='bottom-end' + placement="bottom-end" offset={{ mainAxis: 4, crossAxis: 131, @@ -141,9 +143,8 @@ const ViewWorkflowHistory = () => { > <div className={ - cn('flex h-8 w-8 cursor-pointer items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary', - open && 'bg-state-accent-active text-text-accent', - nodesReadOnly && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled')} + cn('flex h-8 w-8 cursor-pointer items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary', open && 'bg-state-accent-active text-text-accent', nodesReadOnly && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled') + } onClick={() => { if (nodesReadOnly) return @@ -151,110 +152,115 @@ const ViewWorkflowHistory = () => { setShowMessageLogModal(false) }} > - <RiHistoryLine className='h-4 w-4' /> + <RiHistoryLine className="h-4 w-4" /> </div> </TipPopup> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[12]'> + <PortalToFollowElemContent className="z-[12]"> <div - className='ml-2 flex min-w-[240px] max-w-[360px] flex-col overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-xl backdrop-blur-[5px]' + className="ml-2 flex min-w-[240px] max-w-[360px] flex-col overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-xl backdrop-blur-[5px]" > - <div className='sticky top-0 flex items-center justify-between px-4 pt-3'> - <div className='system-mg-regular grow text-text-secondary'>{t('workflow.changeHistory.title')}</div> + <div className="sticky top-0 flex items-center justify-between px-4 pt-3"> + <div className="system-mg-regular grow text-text-secondary">{t('workflow.changeHistory.title')}</div> <div - className='flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center' + className="flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center" onClick={() => { setCurrentLogItem() setShowMessageLogModal(false) setOpen(false) }} > - <RiCloseLine className='h-4 w-4 text-text-secondary' /> + <RiCloseLine className="h-4 w-4 text-text-secondary" /> + </div> + </div> + <div + className="overflow-y-auto p-2" + style={{ + maxHeight: 'calc(1 / 2 * 100vh)', + }} + > + { + !calculateChangeList.statesCount && ( + <div className="py-12"> + <RiHistoryLine className="mx-auto mb-2 h-8 w-8 text-text-tertiary" /> + <div className="text-center text-[13px] text-text-tertiary"> + {t('workflow.changeHistory.placeholder')} + </div> + </div> + ) + } + <div className="flex flex-col"> + { + calculateChangeList.futureStates.map((item: ChangeHistoryEntry) => ( + <div + key={item?.index} + className={cn( + 'mb-0.5 flex cursor-pointer rounded-lg px-2 py-[7px] text-text-secondary hover:bg-state-base-hover', + item?.index === currentHistoryStateIndex && 'bg-state-base-hover', + )} + onClick={() => { + handleSetState(item) + setOpen(false) + }} + > + <div> + <div + className={cn( + 'flex items-center text-[13px] font-medium leading-[18px] text-text-secondary', + )} + > + {composeHistoryItemLabel( + item?.state?.workflowHistoryEventMeta?.nodeTitle, + item?.label || t('workflow.changeHistory.sessionStart'), + )} + {' '} + ( + {calculateStepLabel(item?.index)} + {item?.index === currentHistoryStateIndex && t('workflow.changeHistory.currentState')} + ) + </div> + </div> + </div> + )) + } + { + calculateChangeList.pastStates.map((item: ChangeHistoryEntry) => ( + <div + key={item?.index} + className={cn( + 'mb-0.5 flex cursor-pointer rounded-lg px-2 py-[7px] hover:bg-state-base-hover', + item?.index === calculateChangeList.statesCount - 1 && 'bg-state-base-hover', + )} + onClick={() => { + handleSetState(item) + setOpen(false) + }} + > + <div> + <div + className={cn( + 'flex items-center text-[13px] font-medium leading-[18px] text-text-secondary', + )} + > + {composeHistoryItemLabel( + item?.state?.workflowHistoryEventMeta?.nodeTitle, + item?.label || t('workflow.changeHistory.sessionStart'), + )} + {' '} + ( + {calculateStepLabel(item?.index)} + ) + </div> + </div> + </div> + )) + } </div> </div> - { - ( - <div - className='overflow-y-auto p-2' - style={{ - maxHeight: 'calc(1 / 2 * 100vh)', - }} - > - { - !calculateChangeList.statesCount && ( - <div className='py-12'> - <RiHistoryLine className='mx-auto mb-2 h-8 w-8 text-text-tertiary' /> - <div className='text-center text-[13px] text-text-tertiary'> - {t('workflow.changeHistory.placeholder')} - </div> - </div> - ) - } - <div className='flex flex-col'> - { - calculateChangeList.futureStates.map((item: ChangeHistoryEntry) => ( - <div - key={item?.index} - className={cn( - 'mb-0.5 flex cursor-pointer rounded-lg px-2 py-[7px] text-text-secondary hover:bg-state-base-hover', - item?.index === currentHistoryStateIndex && 'bg-state-base-hover', - )} - onClick={() => { - handleSetState(item) - setOpen(false) - }} - > - <div> - <div - className={cn( - 'flex items-center text-[13px] font-medium leading-[18px] text-text-secondary', - )} - > - {composeHistoryItemLabel( - item?.state?.workflowHistoryEventMeta?.nodeTitle, - item?.label || t('workflow.changeHistory.sessionStart'), - )} ({calculateStepLabel(item?.index)}{item?.index === currentHistoryStateIndex && t('workflow.changeHistory.currentState')}) - </div> - </div> - </div> - )) - } - { - calculateChangeList.pastStates.map((item: ChangeHistoryEntry) => ( - <div - key={item?.index} - className={cn( - 'mb-0.5 flex cursor-pointer rounded-lg px-2 py-[7px] hover:bg-state-base-hover', - item?.index === calculateChangeList.statesCount - 1 && 'bg-state-base-hover', - )} - onClick={() => { - handleSetState(item) - setOpen(false) - }} - > - <div> - <div - className={cn( - 'flex items-center text-[13px] font-medium leading-[18px] text-text-secondary', - )} - > - {composeHistoryItemLabel( - item?.state?.workflowHistoryEventMeta?.nodeTitle, - item?.label || t('workflow.changeHistory.sessionStart'), - )} ({calculateStepLabel(item?.index)}) - </div> - </div> - </div> - )) - } - </div> - </div> - ) - } { !!calculateChangeList.statesCount && ( - <div className='px-0.5'> - <Divider className='m-0' /> + <div className="px-0.5"> + <Divider className="m-0" /> <div className={cn( 'my-0.5 flex cursor-pointer rounded-lg px-2 py-[7px] text-text-secondary', @@ -278,7 +284,7 @@ const ViewWorkflowHistory = () => { </div> ) } - <div className="w-[240px] px-3 py-2 text-xs text-text-tertiary" > + <div className="w-[240px] px-3 py-2 text-xs text-text-tertiary"> <div className="mb-1 flex h-[22px] items-center font-medium uppercase">{t('workflow.changeHistory.hint')}</div> <div className="mb-1 leading-[18px] text-text-tertiary">{t('workflow.changeHistory.hintText')}</div> </div> diff --git a/web/app/components/workflow/help-line/index.tsx b/web/app/components/workflow/help-line/index.tsx index d0dc984a5e..632df6e4a1 100644 --- a/web/app/components/workflow/help-line/index.tsx +++ b/web/app/components/workflow/help-line/index.tsx @@ -1,10 +1,10 @@ -import { memo } from 'react' -import { useViewport } from 'reactflow' -import { useStore } from '../store' import type { HelpLineHorizontalPosition, HelpLineVerticalPosition, } from './types' +import { memo } from 'react' +import { useViewport } from 'reactflow' +import { useStore } from '../store' const HelpLineHorizontal = memo(({ top, @@ -15,7 +15,7 @@ const HelpLineHorizontal = memo(({ return ( <div - className='absolute z-[9] h-px bg-primary-300' + className="absolute z-[9] h-px bg-primary-300" style={{ top: top * zoom + y, left: left * zoom + x, @@ -35,7 +35,7 @@ const HelpLineVertical = memo(({ return ( <div - className='absolute z-[9] w-[1px] bg-primary-300' + className="absolute z-[9] w-[1px] bg-primary-300" style={{ top: top * zoom + y, left: left * zoom + x, diff --git a/web/app/components/workflow/hooks-store/provider.tsx b/web/app/components/workflow/hooks-store/provider.tsx index 8233664004..365e97eb14 100644 --- a/web/app/components/workflow/hooks-store/provider.tsx +++ b/web/app/components/workflow/hooks-store/provider.tsx @@ -1,3 +1,4 @@ +import type { Shape } from './store' import { createContext, useEffect, @@ -7,7 +8,6 @@ import { useStore } from 'reactflow' import { createHooksStore, } from './store' -import type { Shape } from './store' type HooksStore = ReturnType<typeof createHooksStore> export const HooksStoreContext = createContext<HooksStore | null | undefined>(null) diff --git a/web/app/components/workflow/hooks-store/store.ts b/web/app/components/workflow/hooks-store/store.ts index 3e205f9521..79cfb7dbce 100644 --- a/web/app/components/workflow/hooks-store/store.ts +++ b/web/app/components/workflow/hooks-store/store.ts @@ -1,26 +1,24 @@ -import { useContext } from 'react' +import type { FileUpload } from '../../base/features/types' +import type { + BlockEnum, + Node, + NodeDefault, + ToolWithProvider, + ValueSelector, +} from '@/app/components/workflow/types' +import type { IOtherOptions } from '@/service/base' +import type { SchemaTypeDefinition } from '@/service/use-common' +import type { FlowType } from '@/types/common' +import type { VarInInspect } from '@/types/workflow' import { noop, } from 'lodash-es' +import { useContext } from 'react' import { useStore as useZustandStore, } from 'zustand' import { createStore } from 'zustand/vanilla' import { HooksStoreContext } from './provider' -import type { - BlockEnum, - NodeDefault, - ToolWithProvider, -} from '@/app/components/workflow/types' -import type { IOtherOptions } from '@/service/base' -import type { VarInInspect } from '@/types/workflow' -import type { - Node, - ValueSelector, -} from '@/app/components/workflow/types' -import type { FlowType } from '@/types/common' -import type { FileUpload } from '../../base/features/types' -import type { SchemaTypeDefinition } from '@/service/use-common' export type AvailableNodesMetaData = { nodes: NodeDefault[] @@ -32,7 +30,7 @@ export type CommonHooksFnMap = { callback?: { onSuccess?: () => void onError?: () => void - onSettled?: () => void, + onSettled?: () => void }, ) => Promise<void> syncWorkflowDraftWhenPageClose: () => void @@ -50,7 +48,7 @@ export type CommonHooksFnMap = { handleWorkflowTriggerPluginRunInWorkflow: (nodeId?: string) => void handleWorkflowRunAllTriggersInWorkflow: (nodeIds: string[]) => void availableNodesMetaData?: AvailableNodesMetaData - getWorkflowRunAndTraceUrl: (runId?: string) => { runUrl: string; traceUrl: string } + getWorkflowRunAndTraceUrl: (runId?: string) => { runUrl: string, traceUrl: string } exportCheck?: () => Promise<void> handleExportDSL?: (include?: boolean, flowId?: string) => Promise<void> fetchInspectVars: (params: { passInVars?: boolean, vars?: VarInInspect[], passedInAllPluginInfoList?: Record<string, ToolWithProvider[]>, passedInSchemaTypeDefinitions?: SchemaTypeDefinition[] }) => Promise<void> diff --git a/web/app/components/workflow/hooks/index.ts b/web/app/components/workflow/hooks/index.ts index 1131836b35..4b879738e7 100644 --- a/web/app/components/workflow/hooks/index.ts +++ b/web/app/components/workflow/hooks/index.ts @@ -1,26 +1,26 @@ +export * from './use-auto-generate-webhook-url' +export * from './use-available-blocks' +export * from './use-checklist' +export * from './use-DSL' export * from './use-edges-interactions' +export * from './use-inspect-vars-crud' export * from './use-node-data-update' export * from './use-nodes-interactions' -export * from './use-nodes-sync-draft' -export * from './use-workflow' -export * from './use-workflow-run' -export * from './use-checklist' -export * from './use-selection-interactions' -export * from './use-panel-interactions' -export * from './use-workflow-start-run' export * from './use-nodes-layout' -export * from './use-workflow-history' -export * from './use-workflow-variables' +export * from './use-nodes-meta-data' +export * from './use-nodes-sync-draft' +export * from './use-panel-interactions' +export * from './use-selection-interactions' +export * from './use-serial-async-callback' +export * from './use-set-workflow-vars-with-value' export * from './use-shortcuts' +export * from './use-tool-icon' +export * from './use-workflow' +export * from './use-workflow-history' export * from './use-workflow-interactions' export * from './use-workflow-mode' -export * from './use-nodes-meta-data' -export * from './use-available-blocks' export * from './use-workflow-refresh-draft' -export * from './use-tool-icon' -export * from './use-DSL' -export * from './use-inspect-vars-crud' -export * from './use-set-workflow-vars-with-value' +export * from './use-workflow-run' export * from './use-workflow-search' -export * from './use-auto-generate-webhook-url' -export * from './use-serial-async-callback' +export * from './use-workflow-start-run' +export * from './use-workflow-variables' diff --git a/web/app/components/workflow/hooks/use-auto-generate-webhook-url.ts b/web/app/components/workflow/hooks/use-auto-generate-webhook-url.ts index d7d66e31ef..a046346e6b 100644 --- a/web/app/components/workflow/hooks/use-auto-generate-webhook-url.ts +++ b/web/app/components/workflow/hooks/use-auto-generate-webhook-url.ts @@ -1,5 +1,5 @@ -import { useCallback } from 'react' import { produce } from 'immer' +import { useCallback } from 'react' import { useStoreApi } from 'reactflow' import { useStore as useAppStore } from '@/app/components/app/store' import { BlockEnum } from '@/app/components/workflow/types' diff --git a/web/app/components/workflow/hooks/use-available-blocks.ts b/web/app/components/workflow/hooks/use-available-blocks.ts index e1a1919afd..4969cb9d89 100644 --- a/web/app/components/workflow/hooks/use-available-blocks.ts +++ b/web/app/components/workflow/hooks/use-available-blocks.ts @@ -23,8 +23,9 @@ export const useAvailableBlocks = (nodeType?: BlockEnum, inContainer?: boolean) const availablePrevBlocks = useMemo(() => { if (!nodeType || nodeType === BlockEnum.Start || nodeType === BlockEnum.DataSource || nodeType === BlockEnum.TriggerPlugin || nodeType === BlockEnum.TriggerWebhook - || nodeType === BlockEnum.TriggerSchedule) + || nodeType === BlockEnum.TriggerSchedule) { return [] + } return availableNodesType }, [availableNodesType, nodeType]) diff --git a/web/app/components/workflow/hooks/use-checklist.ts b/web/app/components/workflow/hooks/use-checklist.ts index cd1f051eb5..6e49bfa0be 100644 --- a/web/app/components/workflow/hooks/use-checklist.ts +++ b/web/app/components/workflow/hooks/use-checklist.ts @@ -1,10 +1,9 @@ -import { - useCallback, - useMemo, - useRef, -} from 'react' -import { useTranslation } from 'react-i18next' -import { useEdges, useStoreApi } from 'reactflow' +import type { AgentNodeType } from '../nodes/agent/types' +import type { DataSourceNodeType } from '../nodes/data-source/types' +import type { KnowledgeBaseNodeType } from '../nodes/knowledge-base/types' +import type { KnowledgeRetrievalNodeType } from '../nodes/knowledge-retrieval/types' +import type { ToolNodeType } from '../nodes/tool/types' +import type { PluginTriggerNodeType } from '../nodes/trigger-plugin/types' import type { CommonEdgeType, CommonNodeType, @@ -12,51 +11,52 @@ import type { Node, ValueSelector, } from '../types' -import { BlockEnum } from '../types' +import type { Emoji } from '@/app/components/tools/types' +import type { DataSet } from '@/models/datasets' +import { + useCallback, + useMemo, + useRef, +} from 'react' +import { useTranslation } from 'react-i18next' +import { useEdges, useStoreApi } from 'reactflow' +import { useStore as useAppStore } from '@/app/components/app/store' +import { useToastContext } from '@/app/components/base/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' +import { MAX_TREE_DEPTH } from '@/config' +import { useGetLanguage } from '@/context/i18n' +import { fetchDatasets } from '@/service/datasets' +import { useStrategyProviders } from '@/service/use-strategy' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllWorkflowTools, +} from '@/service/use-tools' +import { useAllTriggerPlugins } from '@/service/use-triggers' +import { AppModeEnum } from '@/types/app' +import { + CUSTOM_NODE, +} from '../constants' +import { useDatasetsDetailStore } from '../datasets-detail-store/store' +import { + useGetToolIcon, + useNodesMetaData, +} from '../hooks' +import { getNodeUsedVars, isSpecialVar } from '../nodes/_base/components/variable/utils' import { useStore, useWorkflowStore, } from '../store' +import { BlockEnum } from '../types' import { getDataSourceCheckParams, getToolCheckParams, getValidTreeNodes, } from '../utils' import { getTriggerCheckParams } from '../utils/trigger' -import { - CUSTOM_NODE, -} from '../constants' -import { - useGetToolIcon, - useNodesMetaData, -} from '../hooks' -import type { ToolNodeType } from '../nodes/tool/types' -import type { DataSourceNodeType } from '../nodes/data-source/types' -import type { PluginTriggerNodeType } from '../nodes/trigger-plugin/types' -import { useToastContext } from '@/app/components/base/toast' -import { useGetLanguage } from '@/context/i18n' -import type { AgentNodeType } from '../nodes/agent/types' -import { useStrategyProviders } from '@/service/use-strategy' -import { useAllTriggerPlugins } from '@/service/use-triggers' -import { useDatasetsDetailStore } from '../datasets-detail-store/store' -import type { KnowledgeRetrievalNodeType } from '../nodes/knowledge-retrieval/types' -import type { DataSet } from '@/models/datasets' -import { fetchDatasets } from '@/service/datasets' -import { MAX_TREE_DEPTH } from '@/config' import useNodesAvailableVarList, { useGetNodesAvailableVarList } from './use-nodes-available-var-list' -import { getNodeUsedVars, isSpecialVar } from '../nodes/_base/components/variable/utils' -import type { Emoji } from '@/app/components/tools/types' -import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' -import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import type { KnowledgeBaseNodeType } from '../nodes/knowledge-base/types' -import { - useAllBuiltInTools, - useAllCustomTools, - useAllWorkflowTools, -} from '@/service/use-tools' -import { useStore as useAppStore } from '@/app/components/app/store' -import { AppModeEnum } from '@/types/app' -import useNodes from '@/app/components/workflow/store/workflow/use-nodes' export type ChecklistItem = { id: string diff --git a/web/app/components/workflow/hooks/use-config-vision.ts b/web/app/components/workflow/hooks/use-config-vision.ts index ab859a5ba3..12a2ad38ad 100644 --- a/web/app/components/workflow/hooks/use-config-vision.ts +++ b/web/app/components/workflow/hooks/use-config-vision.ts @@ -1,12 +1,12 @@ +import type { ModelConfig, VisionSetting } from '@/app/components/workflow/types' import { produce } from 'immer' import { useCallback } from 'react' -import { useIsChatMode } from './use-workflow' -import type { ModelConfig, VisionSetting } from '@/app/components/workflow/types' -import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' import { ModelFeatureEnum, } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' import { Resolution } from '@/types/app' +import { useIsChatMode } from './use-workflow' type Payload = { enabled: boolean diff --git a/web/app/components/workflow/hooks/use-dynamic-test-run-options.tsx b/web/app/components/workflow/hooks/use-dynamic-test-run-options.tsx index 276e2f38e7..cc43857b7d 100644 --- a/web/app/components/workflow/hooks/use-dynamic-test-run-options.tsx +++ b/web/app/components/workflow/hooks/use-dynamic-test-run-options.tsx @@ -1,13 +1,15 @@ +import type { TestRunOptions, TriggerOption } from '../header/test-run-menu' +import type { CommonNodeType } from '../types' import { useMemo } from 'react' -import useNodes from '@/app/components/workflow/store/workflow/use-nodes' import { useTranslation } from 'react-i18next' -import { BlockEnum, type CommonNodeType } from '../types' -import { getWorkflowEntryNode } from '../utils/workflow-entry' -import { type TestRunOptions, type TriggerOption, TriggerType } from '../header/test-run-menu' import { TriggerAll } from '@/app/components/base/icons/src/vender/workflow' -import BlockIcon from '../block-icon' -import { useStore } from '../store' +import useNodes from '@/app/components/workflow/store/workflow/use-nodes' import { useAllTriggerPlugins } from '@/service/use-triggers' +import BlockIcon from '../block-icon' +import { TriggerType } from '../header/test-run-menu' +import { useStore } from '../store' +import { BlockEnum } from '../types' +import { getWorkflowEntryNode } from '../utils/workflow-entry' export const useDynamicTestRunOptions = (): TestRunOptions => { const { t } = useTranslation() @@ -25,7 +27,8 @@ export const useDynamicTestRunOptions = (): TestRunOptions => { for (const node of nodes) { const nodeData = node.data as CommonNodeType - if (!nodeData?.type) continue + if (!nodeData?.type) + continue if (nodeData.type === BlockEnum.Start) { userInput = { @@ -35,7 +38,7 @@ export const useDynamicTestRunOptions = (): TestRunOptions => { icon: ( <BlockIcon type={BlockEnum.Start} - size='md' + size="md" /> ), nodeId: node.id, @@ -50,7 +53,7 @@ export const useDynamicTestRunOptions = (): TestRunOptions => { icon: ( <BlockIcon type={BlockEnum.TriggerSchedule} - size='md' + size="md" /> ), nodeId: node.id, @@ -65,7 +68,7 @@ export const useDynamicTestRunOptions = (): TestRunOptions => { icon: ( <BlockIcon type={BlockEnum.TriggerWebhook} - size='md' + size="md" /> ), nodeId: node.id, @@ -83,7 +86,7 @@ export const useDynamicTestRunOptions = (): TestRunOptions => { const icon = ( <BlockIcon type={BlockEnum.TriggerPlugin} - size='md' + size="md" toolIcon={triggerIcon} /> ) @@ -109,7 +112,7 @@ export const useDynamicTestRunOptions = (): TestRunOptions => { icon: ( <BlockIcon type={BlockEnum.Start} - size='md' + size="md" /> ), nodeId: startNode.id, @@ -122,18 +125,20 @@ export const useDynamicTestRunOptions = (): TestRunOptions => { .map(trigger => trigger.nodeId) .filter((nodeId): nodeId is string => Boolean(nodeId)) - const runAll: TriggerOption | undefined = triggerNodeIds.length > 1 ? { - id: 'run-all', - type: TriggerType.All, - name: t('workflow.common.runAllTriggers'), - icon: ( - <div className="flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 bg-util-colors-purple-purple-500 text-white shadow-md"> - <TriggerAll className="h-4.5 w-4.5" /> - </div> - ), - relatedNodeIds: triggerNodeIds, - enabled: true, - } : undefined + const runAll: TriggerOption | undefined = triggerNodeIds.length > 1 + ? { + id: 'run-all', + type: TriggerType.All, + name: t('workflow.common.runAllTriggers'), + icon: ( + <div className="flex h-6 w-6 items-center justify-center rounded-lg border-[0.5px] border-white/2 bg-util-colors-purple-purple-500 text-white shadow-md"> + <TriggerAll className="h-4.5 w-4.5" /> + </div> + ), + relatedNodeIds: triggerNodeIds, + enabled: true, + } + : undefined return { userInput, diff --git a/web/app/components/workflow/hooks/use-edges-interactions-without-sync.ts b/web/app/components/workflow/hooks/use-edges-interactions-without-sync.ts index e7b4fb0a42..99673b70f8 100644 --- a/web/app/components/workflow/hooks/use-edges-interactions-without-sync.ts +++ b/web/app/components/workflow/hooks/use-edges-interactions-without-sync.ts @@ -1,5 +1,5 @@ -import { useCallback } from 'react' import { produce } from 'immer' +import { useCallback } from 'react' import { useStoreApi } from 'reactflow' export const useEdgesInteractionsWithoutSync = () => { diff --git a/web/app/components/workflow/hooks/use-edges-interactions.ts b/web/app/components/workflow/hooks/use-edges-interactions.ts index 297535ac24..5104b47ef4 100644 --- a/web/app/components/workflow/hooks/use-edges-interactions.ts +++ b/web/app/components/workflow/hooks/use-edges-interactions.ts @@ -1,19 +1,19 @@ -import { useCallback } from 'react' -import { produce } from 'immer' import type { EdgeMouseHandler, OnEdgesChange, } from 'reactflow' -import { - useStoreApi, -} from 'reactflow' import type { Node, } from '../types' +import { produce } from 'immer' +import { useCallback } from 'react' +import { + useStoreApi, +} from 'reactflow' import { getNodesConnectedSourceOrTargetHandleIdsMap } from '../utils' import { useNodesSyncDraft } from './use-nodes-sync-draft' import { useNodesReadOnly } from './use-workflow' -import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history' +import { useWorkflowHistory, WorkflowHistoryEvent } from './use-workflow-history' export const useEdgesInteractions = () => { const store = useStoreApi() diff --git a/web/app/components/workflow/hooks/use-fetch-workflow-inspect-vars.ts b/web/app/components/workflow/hooks/use-fetch-workflow-inspect-vars.ts index 60f839b93d..54c2c77d0d 100644 --- a/web/app/components/workflow/hooks/use-fetch-workflow-inspect-vars.ts +++ b/web/app/components/workflow/hooks/use-fetch-workflow-inspect-vars.ts @@ -1,22 +1,21 @@ -import type { NodeWithVar, VarInInspect } from '@/types/workflow' -import { useStore, useWorkflowStore } from '@/app/components/workflow/store' -import { useStoreApi } from 'reactflow' -import type { ToolWithProvider } from '@/app/components/workflow/types' -import type { Node } from '@/app/components/workflow/types' -import { fetchAllInspectVars } from '@/service/workflow' -import { useInvalidateConversationVarValues, useInvalidateSysVarValues } from '@/service/use-workflow' -import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync' -import type { FlowType } from '@/types/common' -import useMatchSchemaType, { getMatchedSchemaType } from '../nodes/_base/components/variable/use-match-schema-type' -import { toNodeOutputVars } from '../nodes/_base/components/variable/utils' +import type { Node, ToolWithProvider } from '@/app/components/workflow/types' import type { SchemaTypeDefinition } from '@/service/use-common' +import type { FlowType } from '@/types/common' +import type { NodeWithVar, VarInInspect } from '@/types/workflow' import { useCallback } from 'react' +import { useStoreApi } from 'reactflow' +import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync' +import { useStore, useWorkflowStore } from '@/app/components/workflow/store' import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, } from '@/service/use-tools' +import { useInvalidateConversationVarValues, useInvalidateSysVarValues } from '@/service/use-workflow' +import { fetchAllInspectVars } from '@/service/workflow' +import useMatchSchemaType, { getMatchedSchemaType } from '../nodes/_base/components/variable/use-match-schema-type' +import { toNodeOutputVars } from '../nodes/_base/components/variable/utils' type Params = { flowType: FlowType @@ -99,9 +98,9 @@ export const useSetWorkflowVarsWithValue = ({ } const fetchInspectVars = useCallback(async (params: { - passInVars?: boolean, - vars?: VarInInspect[], - passedInAllPluginInfoList?: Record<string, ToolWithProvider[]>, + passInVars?: boolean + vars?: VarInInspect[] + passedInAllPluginInfoList?: Record<string, ToolWithProvider[]> passedInSchemaTypeDefinitions?: SchemaTypeDefinition[] }) => { const { passInVars, vars, passedInAllPluginInfoList, passedInSchemaTypeDefinitions } = params diff --git a/web/app/components/workflow/hooks/use-helpline.ts b/web/app/components/workflow/hooks/use-helpline.ts index 55979904fb..681a7c9802 100644 --- a/web/app/components/workflow/hooks/use-helpline.ts +++ b/web/app/components/workflow/hooks/use-helpline.ts @@ -1,8 +1,8 @@ +import type { Node } from '../types' import { useCallback } from 'react' import { useStoreApi } from 'reactflow' -import type { Node } from '../types' -import { BlockEnum, isTriggerNode } from '../types' import { useWorkflowStore } from '../store' +import { BlockEnum, isTriggerNode } from '../types' // Entry node (Start/Trigger) wrapper offsets // The EntryNodeContainer adds a wrapper with status indicator above the actual node diff --git a/web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts b/web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts index 6b7acd0a85..316da71d9d 100644 --- a/web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts +++ b/web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts @@ -1,29 +1,28 @@ -import { fetchNodeInspectVars } from '@/service/workflow' -import { useWorkflowStore } from '@/app/components/workflow/store' -import type { ValueSelector } from '@/app/components/workflow/types' +import type { Node, ValueSelector } from '@/app/components/workflow/types' +import type { SchemaTypeDefinition } from '@/service/use-common' +import type { FlowType } from '@/types/common' import type { VarInInspect } from '@/types/workflow' -import { VarInInspectType } from '@/types/workflow' +import { produce } from 'immer' import { useCallback } from 'react' +import { useStoreApi } from 'reactflow' +import { useEdgesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-edges-interactions-without-sync' +import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync' import { isConversationVar, isENV, isSystemVar, toNodeOutputVars, } from '@/app/components/workflow/nodes/_base/components/variable/utils' -import { produce } from 'immer' -import type { Node } from '@/app/components/workflow/types' -import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync' -import { useEdgesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-edges-interactions-without-sync' -import type { FlowType } from '@/types/common' +import { useWorkflowStore } from '@/app/components/workflow/store' import useFLow from '@/service/use-flow' -import { useStoreApi } from 'reactflow' -import type { SchemaTypeDefinition } from '@/service/use-common' import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, } from '@/service/use-tools' +import { fetchNodeInspectVars } from '@/service/workflow' +import { VarInInspectType } from '@/types/workflow' type Params = { flowId: string diff --git a/web/app/components/workflow/hooks/use-inspect-vars-crud.ts b/web/app/components/workflow/hooks/use-inspect-vars-crud.ts index 0f58cf8be2..2a97019383 100644 --- a/web/app/components/workflow/hooks/use-inspect-vars-crud.ts +++ b/web/app/components/workflow/hooks/use-inspect-vars-crud.ts @@ -1,11 +1,11 @@ -import { useStore } from '../store' +import { produce } from 'immer' import { useHooksStore } from '@/app/components/workflow/hooks-store' import { useConversationVarValues, useSysVarValues, } from '@/service/use-workflow' import { FlowType } from '@/types/common' -import { produce } from 'immer' +import { useStore } from '../store' import { BlockEnum } from '../types' const varsAppendStartNodeKeys = ['query', 'files'] @@ -16,19 +16,19 @@ const useInspectVarsCrud = () => { const { data: conversationVars } = useConversationVarValues(configsMap?.flowType, !isRagPipeline ? configsMap?.flowId : '') const { data: allSystemVars } = useSysVarValues(configsMap?.flowType, !isRagPipeline ? configsMap?.flowId : '') const { varsAppendStartNode, systemVars } = (() => { - if(allSystemVars?.length === 0) + if (allSystemVars?.length === 0) return { varsAppendStartNode: [], systemVars: [] } const varsAppendStartNode = allSystemVars?.filter(({ name }) => varsAppendStartNodeKeys.includes(name)) || [] const systemVars = allSystemVars?.filter(({ name }) => !varsAppendStartNodeKeys.includes(name)) || [] return { varsAppendStartNode, systemVars } })() const nodesWithInspectVars = (() => { - if(!partOfNodesWithInspectVars || partOfNodesWithInspectVars.length === 0) + if (!partOfNodesWithInspectVars || partOfNodesWithInspectVars.length === 0) return [] const nodesWithInspectVars = produce(partOfNodesWithInspectVars, (draft) => { draft.forEach((nodeWithVars) => { - if(nodeWithVars.nodeType === BlockEnum.Start) + if (nodeWithVars.nodeType === BlockEnum.Start) nodeWithVars.vars = [...nodeWithVars.vars, ...varsAppendStartNode] }) }) diff --git a/web/app/components/workflow/hooks/use-node-data-update.ts b/web/app/components/workflow/hooks/use-node-data-update.ts index ac7dca9e4c..e40b467db2 100644 --- a/web/app/components/workflow/hooks/use-node-data-update.ts +++ b/web/app/components/workflow/hooks/use-node-data-update.ts @@ -1,7 +1,7 @@ -import { useCallback } from 'react' -import { produce } from 'immer' -import { useStoreApi } from 'reactflow' import type { SyncCallback } from './use-nodes-sync-draft' +import { produce } from 'immer' +import { useCallback } from 'react' +import { useStoreApi } from 'reactflow' import { useNodesSyncDraft } from './use-nodes-sync-draft' import { useNodesReadOnly } from './use-workflow' diff --git a/web/app/components/workflow/hooks/use-node-plugin-installation.ts b/web/app/components/workflow/hooks/use-node-plugin-installation.ts index 96e3919e67..d275759cc0 100644 --- a/web/app/components/workflow/hooks/use-node-plugin-installation.ts +++ b/web/app/components/workflow/hooks/use-node-plugin-installation.ts @@ -1,9 +1,10 @@ -import { useCallback, useMemo } from 'react' -import { BlockEnum, type CommonNodeType } from '../types' +import type { DataSourceNodeType } from '../nodes/data-source/types' import type { ToolNodeType } from '../nodes/tool/types' import type { PluginTriggerNodeType } from '../nodes/trigger-plugin/types' -import type { DataSourceNodeType } from '../nodes/data-source/types' +import type { CommonNodeType } from '../types' +import { useCallback, useMemo } from 'react' import { CollectionType } from '@/app/components/tools/types' +import { useInvalidDataSourceList } from '@/service/use-pipeline' import { useAllBuiltInTools, useAllCustomTools, @@ -15,9 +16,9 @@ import { useAllTriggerPlugins, useInvalidateAllTriggerPlugins, } from '@/service/use-triggers' -import { useInvalidDataSourceList } from '@/service/use-pipeline' -import { useStore } from '../store' import { canFindTool } from '@/utils' +import { useStore } from '../store' +import { BlockEnum } from '../types' type InstallationState = { isChecking: boolean diff --git a/web/app/components/workflow/hooks/use-nodes-available-var-list.ts b/web/app/components/workflow/hooks/use-nodes-available-var-list.ts index b78597ea37..cb04b43002 100644 --- a/web/app/components/workflow/hooks/use-nodes-available-var-list.ts +++ b/web/app/components/workflow/hooks/use-nodes-available-var-list.ts @@ -1,10 +1,12 @@ +import type { Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' import { useCallback } from 'react' import { useIsChatMode, useWorkflow, useWorkflowVariables, } from '@/app/components/workflow/hooks' -import { BlockEnum, type Node, type NodeOutPutVar, type ValueSelector, type Var } from '@/app/components/workflow/types' +import { BlockEnum } from '@/app/components/workflow/types' + type Params = { onlyLeafNodeVar?: boolean hideEnv?: boolean diff --git a/web/app/components/workflow/hooks/use-nodes-interactions-without-sync.ts b/web/app/components/workflow/hooks/use-nodes-interactions-without-sync.ts index e3d661ff08..0c343f4eb8 100644 --- a/web/app/components/workflow/hooks/use-nodes-interactions-without-sync.ts +++ b/web/app/components/workflow/hooks/use-nodes-interactions-without-sync.ts @@ -1,5 +1,5 @@ -import { useCallback } from 'react' import { produce } from 'immer' +import { useCallback } from 'react' import { useStoreApi } from 'reactflow' import { NodeRunningStatus } from '../types' @@ -31,7 +31,7 @@ export const useNodesInteractionsWithoutSync = () => { const nodes = getNodes() const newNodes = produce(nodes, (draft) => { draft.forEach((node) => { - if(node.data._runningStatus === NodeRunningStatus.Succeeded) + if (node.data._runningStatus === NodeRunningStatus.Succeeded) node.data._runningStatus = undefined }) }) diff --git a/web/app/components/workflow/hooks/use-nodes-interactions.ts b/web/app/components/workflow/hooks/use-nodes-interactions.ts index d56b85893e..fd9fe3300f 100644 --- a/web/app/components/workflow/hooks/use-nodes-interactions.ts +++ b/web/app/components/workflow/hooks/use-nodes-interactions.ts @@ -1,7 +1,4 @@ import type { MouseEvent } from 'react' -import { useCallback, useRef, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { produce } from 'immer' import type { NodeDragHandler, NodeMouseHandler, @@ -10,16 +7,21 @@ import type { OnConnectStart, ResizeParamsWithDirection, } from 'reactflow' +import type { PluginDefaultValue } from '../block-selector/types' +import type { IterationNodeType } from '../nodes/iteration/types' +import type { LoopNodeType } from '../nodes/loop/types' +import type { VariableAssignerNodeType } from '../nodes/variable-assigner/types' +import type { Edge, Node, OnNodeAdd } from '../types' +import type { RAGPipelineVariables } from '@/models/pipeline' +import { produce } from 'immer' +import { useCallback, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' import { getConnectedEdges, getOutgoers, useReactFlow, useStoreApi, } from 'reactflow' -import type { PluginDefaultValue } from '../block-selector/types' -import type { Edge, Node, OnNodeAdd } from '../types' -import { BlockEnum, isTriggerNode } from '../types' -import { useWorkflowStore } from '../store' import { CUSTOM_EDGE, ITERATION_CHILDREN_Z_INDEX, @@ -30,39 +32,37 @@ import { X_OFFSET, Y_OFFSET, } from '../constants' +import { getNodeUsedVars } from '../nodes/_base/components/variable/utils' +import { CUSTOM_ITERATION_START_NODE } from '../nodes/iteration-start/constants' +import { useNodeIterationInteractions } from '../nodes/iteration/use-interactions' +import { CUSTOM_LOOP_START_NODE } from '../nodes/loop-start/constants' +import { useNodeLoopInteractions } from '../nodes/loop/use-interactions' +import { CUSTOM_NOTE_NODE } from '../note-node/constants' +import { useWorkflowStore } from '../store' +import { BlockEnum, isTriggerNode } from '../types' import { - genNewNodeTitleFromOld, generateNewNode, + genNewNodeTitleFromOld, getNestedNodePosition, getNodeCustomTypeByNodeDataType, getNodesConnectedSourceOrTargetHandleIdsMap, getTopLeftNodePosition, } from '../utils' -import { CUSTOM_NOTE_NODE } from '../note-node/constants' -import type { IterationNodeType } from '../nodes/iteration/types' -import type { LoopNodeType } from '../nodes/loop/types' -import { CUSTOM_ITERATION_START_NODE } from '../nodes/iteration-start/constants' -import { CUSTOM_LOOP_START_NODE } from '../nodes/loop-start/constants' -import type { VariableAssignerNodeType } from '../nodes/variable-assigner/types' -import { useNodeIterationInteractions } from '../nodes/iteration/use-interactions' -import { useNodeLoopInteractions } from '../nodes/loop/use-interactions' import { useWorkflowHistoryStore } from '../workflow-history-store' -import { useNodesSyncDraft } from './use-nodes-sync-draft' +import { useAutoGenerateWebhookUrl } from './use-auto-generate-webhook-url' import { useHelpline } from './use-helpline' +import useInspectVarsCrud from './use-inspect-vars-crud' +import { useNodesMetaData } from './use-nodes-meta-data' +import { useNodesSyncDraft } from './use-nodes-sync-draft' import { useNodesReadOnly, useWorkflow, useWorkflowReadOnly, } from './use-workflow' import { - WorkflowHistoryEvent, useWorkflowHistory, + WorkflowHistoryEvent, } from './use-workflow-history' -import { useNodesMetaData } from './use-nodes-meta-data' -import { useAutoGenerateWebhookUrl } from './use-auto-generate-webhook-url' -import type { RAGPipelineVariables } from '@/models/pipeline' -import useInspectVarsCrud from './use-inspect-vars-crud' -import { getNodeUsedVars } from '../nodes/_base/components/variable/utils' // Entry node deletion restriction has been removed to allow empty workflows @@ -89,8 +89,8 @@ export const useNodesInteractions = () => { const { handleNodeLoopChildDrag, handleNodeLoopChildrenCopy } = useNodeLoopInteractions() const dragNodeStartPosition = useRef({ x: 0, y: 0 } as { - x: number; - y: number; + x: number + y: number }) const { nodesMap: nodesMetaDataMap } = useNodesMetaData() @@ -101,19 +101,22 @@ export const useNodesInteractions = () => { (_, node) => { workflowStore.setState({ nodeAnimation: false }) - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return if ( node.type === CUSTOM_ITERATION_START_NODE || node.type === CUSTOM_NOTE_NODE - ) + ) { return + } if ( node.type === CUSTOM_LOOP_START_NODE || node.type === CUSTOM_NOTE_NODE - ) + ) { return + } dragNodeStartPosition.current = { x: node.position.x, @@ -125,11 +128,14 @@ export const useNodesInteractions = () => { const handleNodeDrag = useCallback<NodeDragHandler>( (e, node: Node) => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return - if (node.type === CUSTOM_ITERATION_START_NODE) return + if (node.type === CUSTOM_ITERATION_START_NODE) + return - if (node.type === CUSTOM_LOOP_START_NODE) return + if (node.type === CUSTOM_LOOP_START_NODE) + return const { getNodes, setNodes } = store.getState() e.stopPropagation() @@ -211,7 +217,8 @@ export const useNodesInteractions = () => { const { setHelpLineHorizontal, setHelpLineVertical } = workflowStore.getState() - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return const { x, y } = dragNodeStartPosition.current if (!(x === node.position.x && y === node.position.y)) { @@ -237,19 +244,22 @@ export const useNodesInteractions = () => { const handleNodeEnter = useCallback<NodeMouseHandler>( (_, node) => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return if ( node.type === CUSTOM_NOTE_NODE || node.type === CUSTOM_ITERATION_START_NODE - ) + ) { return + } if ( node.type === CUSTOM_LOOP_START_NODE || node.type === CUSTOM_NOTE_NODE - ) + ) { return + } const { getNodes, setNodes, edges, setEdges } = store.getState() const nodes = getNodes() @@ -257,7 +267,8 @@ export const useNodesInteractions = () => { = workflowStore.getState() if (connectingNodePayload) { - if (connectingNodePayload.nodeId === node.id) return + if (connectingNodePayload.nodeId === node.id) + return const connectingNode: Node = nodes.find( n => n.id === connectingNodePayload.nodeId, )! @@ -288,8 +299,9 @@ export const useNodesInteractions = () => { || connectingNode.data.type === BlockEnum.VariableAggregator) && node.data.type !== BlockEnum.IfElse && node.data.type !== BlockEnum.QuestionClassifier - ) + ) { n.data._isEntering = true + } }) }) setNodes(newNodes) @@ -300,7 +312,8 @@ export const useNodesInteractions = () => { connectedEdges.forEach((edge) => { const currentEdge = draft.find(e => e.id === edge.id) - if (currentEdge) currentEdge.data._connectedNodeIsHovering = true + if (currentEdge) + currentEdge.data._connectedNodeIsHovering = true }) }) setEdges(newEdges) @@ -310,19 +323,22 @@ export const useNodesInteractions = () => { const handleNodeLeave = useCallback<NodeMouseHandler>( (_, node) => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return if ( node.type === CUSTOM_NOTE_NODE || node.type === CUSTOM_ITERATION_START_NODE - ) + ) { return + } if ( node.type === CUSTOM_NOTE_NODE || node.type === CUSTOM_LOOP_START_NODE - ) + ) { return + } const { setEnteringNodePayload } = workflowStore.getState() setEnteringNodePayload(undefined) @@ -356,11 +372,13 @@ export const useNodesInteractions = () => { const nodes = getNodes() const selectedNode = nodes.find(node => node.data.selected) - if (!cancelSelection && selectedNode?.id === nodeId) return + if (!cancelSelection && selectedNode?.id === nodeId) + return const newNodes = produce(nodes, (draft) => { draft.forEach((node) => { - if (node.id === nodeId) node.data.selected = !cancelSelection + if (node.id === nodeId) + node.data.selected = !cancelSelection else node.data.selected = false }) }) @@ -395,10 +413,14 @@ export const useNodesInteractions = () => { const handleNodeClick = useCallback<NodeMouseHandler>( (_, node) => { - if (node.type === CUSTOM_ITERATION_START_NODE) return - if (node.type === CUSTOM_LOOP_START_NODE) return - if (node.data.type === BlockEnum.DataSourceEmpty) return - if (node.data._pluginInstallLocked) return + if (node.type === CUSTOM_ITERATION_START_NODE) + return + if (node.type === CUSTOM_LOOP_START_NODE) + return + if (node.data.type === BlockEnum.DataSourceEmpty) + return + if (node.data._pluginInstallLocked) + return handleNodeSelect(node.id) }, [handleNodeSelect], @@ -406,21 +428,25 @@ export const useNodesInteractions = () => { const handleNodeConnect = useCallback<OnConnect>( ({ source, sourceHandle, target, targetHandle }) => { - if (source === target) return - if (getNodesReadOnly()) return + if (source === target) + return + if (getNodesReadOnly()) + return const { getNodes, setNodes, edges, setEdges } = store.getState() const nodes = getNodes() const targetNode = nodes.find(node => node.id === target!) const sourceNode = nodes.find(node => node.id === source!) - if (targetNode?.parentId !== sourceNode?.parentId) return + if (targetNode?.parentId !== sourceNode?.parentId) + return if ( sourceNode?.type === CUSTOM_NOTE_NODE || targetNode?.type === CUSTOM_NOTE_NODE - ) + ) { return + } if ( edges.find( @@ -430,8 +456,9 @@ export const useNodesInteractions = () => { && edge.target === target && edge.targetHandle === targetHandle, ) - ) + ) { return + } const parendNode = nodes.find(node => node.id === targetNode?.parentId) const isInIteration @@ -497,20 +524,24 @@ export const useNodesInteractions = () => { const handleNodeConnectStart = useCallback<OnConnectStart>( (_, { nodeId, handleType, handleId }) => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return if (nodeId && handleType) { const { setConnectingNodePayload } = workflowStore.getState() const { getNodes } = store.getState() const node = getNodes().find(n => n.id === nodeId)! - if (node.type === CUSTOM_NOTE_NODE) return + if (node.type === CUSTOM_NOTE_NODE) + return if ( node.data.type === BlockEnum.VariableAggregator || node.data.type === BlockEnum.VariableAssigner - ) - if (handleType === 'target') return + ) { + if (handleType === 'target') + return + } setConnectingNodePayload({ nodeId, @@ -525,7 +556,8 @@ export const useNodesInteractions = () => { const handleNodeConnectEnd = useCallback<OnConnectEnd>( (e: any) => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return const { connectingNodePayload, @@ -547,7 +579,8 @@ export const useNodesInteractions = () => { const toNode = nodes.find(n => n.id === enteringNodePayload.nodeId)! const toParentNode = nodes.find(n => n.id === toNode.parentId) - if (fromNode.parentId !== toNode.parentId) return + if (fromNode.parentId !== toNode.parentId) + return const { x, y } = screenToFlowPosition({ x: e.x, y: e.y }) @@ -602,7 +635,8 @@ export const useNodesInteractions = () => { const handleNodeDelete = useCallback( (nodeId: string) => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return const { getNodes, setNodes, edges, setEdges } = store.getState() @@ -610,13 +644,15 @@ export const useNodesInteractions = () => { const currentNodeIndex = nodes.findIndex(node => node.id === nodeId) const currentNode = nodes[currentNodeIndex] - if (!currentNode) return + if (!currentNode) + return if ( nodesMetaDataMap?.[currentNode.data.type as BlockEnum]?.metaData .isUndeletable - ) + ) { return + } deleteNodeInspectorVars(nodeId) if (currentNode.data.type === BlockEnum.Iteration) { @@ -706,7 +742,8 @@ export const useNodesInteractions = () => { if (ragPipelineVariables && setRagPipelineVariables) { const newRagPipelineVariables: RAGPipelineVariables = [] ragPipelineVariables.forEach((variable) => { - if (variable.belong_to_node_id === id) return + if (variable.belong_to_node_id === id) + return newRagPipelineVariables.push(variable) }) setRagPipelineVariables(newRagPipelineVariables) @@ -781,7 +818,8 @@ export const useNodesInteractions = () => { }, { prevNodeId, prevNodeSourceHandle, nextNodeId, nextNodeTargetHandle }, ) => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return const { getNodes, setNodes, edges, setEdges } = store.getState() const nodes = getNodes() @@ -935,9 +973,11 @@ export const useNodesInteractions = () => { }) draft.push(newNode) - if (newIterationStartNode) draft.push(newIterationStartNode) + if (newIterationStartNode) + draft.push(newIterationStartNode) - if (newLoopStartNode) draft.push(newLoopStartNode) + if (newLoopStartNode) + draft.push(newLoopStartNode) }) if ( @@ -964,7 +1004,8 @@ export const useNodesInteractions = () => { _connectedNodeIsSelected: false, } }) - if (newEdge) draft.push(newEdge) + if (newEdge) + draft.push(newEdge) }) setNodes(newNodes) @@ -976,8 +1017,9 @@ export const useNodesInteractions = () => { if ( nodeType !== BlockEnum.IfElse && nodeType !== BlockEnum.QuestionClassifier - ) + ) { newNode.data._connectedSourceHandleIds = [sourceHandle] + } newNode.data._connectedTargetHandleIds = [] newNode.position = { x: nextNode.position.x, @@ -1101,8 +1143,10 @@ export const useNodesInteractions = () => { } }) draft.push(newNode) - if (newIterationStartNode) draft.push(newIterationStartNode) - if (newLoopStartNode) draft.push(newLoopStartNode) + if (newIterationStartNode) + draft.push(newIterationStartNode) + if (newLoopStartNode) + draft.push(newLoopStartNode) }) if (newEdge) { const newEdges = produce(edges, (draft) => { @@ -1192,10 +1236,10 @@ export const useNodesInteractions = () => { = nodes.find(node => node.id === nextNode.parentId) || null const isNextNodeInIteration = !!nextNodeParentNode - && nextNodeParentNode.data.type === BlockEnum.Iteration + && nextNodeParentNode.data.type === BlockEnum.Iteration const isNextNodeInLoop = !!nextNodeParentNode - && nextNodeParentNode.data.type === BlockEnum.Loop + && nextNodeParentNode.data.type === BlockEnum.Loop if ( nodeType !== BlockEnum.IfElse @@ -1274,8 +1318,10 @@ export const useNodesInteractions = () => { } }) draft.push(newNode) - if (newIterationStartNode) draft.push(newIterationStartNode) - if (newLoopStartNode) draft.push(newLoopStartNode) + if (newIterationStartNode) + draft.push(newIterationStartNode) + if (newLoopStartNode) + draft.push(newLoopStartNode) }) setNodes(newNodes) if ( @@ -1303,9 +1349,11 @@ export const useNodesInteractions = () => { _connectedNodeIsSelected: false, } }) - if (newPrevEdge) draft.push(newPrevEdge) + if (newPrevEdge) + draft.push(newPrevEdge) - if (newNextEdge) draft.push(newNextEdge) + if (newNextEdge) + draft.push(newNextEdge) }) setEdges(newEdges) } @@ -1330,7 +1378,8 @@ export const useNodesInteractions = () => { sourceHandle: string, pluginDefaultValue?: PluginDefaultValue, ) => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return const { getNodes, setNodes, edges, setEdges } = store.getState() const nodes = getNodes() @@ -1388,8 +1437,10 @@ export const useNodesInteractions = () => { const index = draft.findIndex(node => node.id === currentNodeId) draft.splice(index, 1, newCurrentNode) - if (newIterationStartNode) draft.push(newIterationStartNode) - if (newLoopStartNode) draft.push(newLoopStartNode) + if (newIterationStartNode) + draft.push(newIterationStartNode) + if (newLoopStartNode) + draft.push(newLoopStartNode) }) setNodes(newNodes) const newEdges = produce(edges, (draft) => { @@ -1443,14 +1494,16 @@ export const useNodesInteractions = () => { if ( node.type === CUSTOM_NOTE_NODE || node.type === CUSTOM_ITERATION_START_NODE - ) + ) { return + } if ( node.type === CUSTOM_NOTE_NODE || node.type === CUSTOM_LOOP_START_NODE - ) + ) { return + } e.preventDefault() const container = document.querySelector('#workflow-container') @@ -1469,7 +1522,8 @@ export const useNodesInteractions = () => { const handleNodesCopy = useCallback( (nodeId?: string) => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return const { setClipboardElements } = workflowStore.getState() @@ -1489,15 +1543,19 @@ export const useNodesInteractions = () => { && node.data.type !== BlockEnum.KnowledgeBase && node.data.type !== BlockEnum.DataSourceEmpty, ) - if (nodeToCopy) setClipboardElements([nodeToCopy]) + if (nodeToCopy) + setClipboardElements([nodeToCopy]) } else { // If no nodeId is provided, fall back to the current behavior const bundledNodes = nodes.filter((node) => { - if (!node.data._isBundled) return false - if (node.type === CUSTOM_NOTE_NODE) return true + if (!node.data._isBundled) + return false + if (node.type === CUSTOM_NOTE_NODE) + return true const { metaData } = nodesMetaDataMap![node.data.type as BlockEnum] - if (metaData.isSingleton) return false + if (metaData.isSingleton) + return false return !node.data.isInIteration && !node.data.isInLoop }) @@ -1507,20 +1565,24 @@ export const useNodesInteractions = () => { } const selectedNode = nodes.find((node) => { - if (!node.data.selected) return false - if (node.type === CUSTOM_NOTE_NODE) return true + if (!node.data.selected) + return false + if (node.type === CUSTOM_NOTE_NODE) + return true const { metaData } = nodesMetaDataMap![node.data.type as BlockEnum] return !metaData.isSingleton }) - if (selectedNode) setClipboardElements([selectedNode]) + if (selectedNode) + setClipboardElements([selectedNode]) } }, [getNodesReadOnly, store, workflowStore], ) const handleNodesPaste = useCallback(() => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return const { clipboardElements, mousePosition } = workflowStore.getState() @@ -1647,7 +1709,8 @@ export const useNodesInteractions = () => { nodesToPaste.push(newNode) - if (newChildren.length) nodesToPaste.push(...newChildren) + if (newChildren.length) + nodesToPaste.push(...newChildren) }) // only handle edge when paste nested block @@ -1691,7 +1754,8 @@ export const useNodesInteractions = () => { const handleNodesDuplicate = useCallback( (nodeId?: string) => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return handleNodesCopy(nodeId) handleNodesPaste() @@ -1700,7 +1764,8 @@ export const useNodesInteractions = () => { ) const handleNodesDelete = useCallback(() => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return const { getNodes, edges } = store.getState() @@ -1716,18 +1781,21 @@ export const useNodesInteractions = () => { } const edgeSelected = edges.some(edge => edge.selected) - if (edgeSelected) return + if (edgeSelected) + return const selectedNode = nodes.find( node => node.data.selected, ) - if (selectedNode) handleNodeDelete(selectedNode.id) + if (selectedNode) + handleNodeDelete(selectedNode.id) }, [store, getNodesReadOnly, handleNodeDelete]) const handleNodeResize = useCallback( (nodeId: string, params: ResizeParamsWithDirection) => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return const { getNodes, setNodes } = store.getState() const { x, y, width, height } = params @@ -1752,8 +1820,9 @@ export const useNodesInteractions = () => { if ( n.position.y + n.height! > bottomNode.position.y + bottomNode.height! - ) + ) { bottomNode = n + } } else { bottomNode = n @@ -1772,8 +1841,9 @@ export const useNodesInteractions = () => { if ( height < bottomNode.position.y + bottomNode.height! + paddingMap.bottom - ) + ) { return + } } const newNodes = produce(nodes, (draft) => { draft.forEach((n) => { @@ -1796,7 +1866,8 @@ export const useNodesInteractions = () => { const handleNodeDisconnect = useCallback( (nodeId: string) => { - if (getNodesReadOnly()) return + if (getNodesReadOnly()) + return const { getNodes, setNodes, edges, setEdges } = store.getState() const nodes = getNodes() @@ -1834,13 +1905,15 @@ export const useNodesInteractions = () => { ) const handleHistoryBack = useCallback(() => { - if (getNodesReadOnly() || getWorkflowReadOnly()) return + if (getNodesReadOnly() || getWorkflowReadOnly()) + return const { setEdges, setNodes } = store.getState() undo() const { edges, nodes } = workflowHistoryStore.getState() - if (edges.length === 0 && nodes.length === 0) return + if (edges.length === 0 && nodes.length === 0) + return setEdges(edges) setNodes(nodes) @@ -1853,13 +1926,15 @@ export const useNodesInteractions = () => { ]) const handleHistoryForward = useCallback(() => { - if (getNodesReadOnly() || getWorkflowReadOnly()) return + if (getNodesReadOnly() || getWorkflowReadOnly()) + return const { setEdges, setNodes } = store.getState() redo() const { edges, nodes } = workflowHistoryStore.getState() - if (edges.length === 0 && nodes.length === 0) return + if (edges.length === 0 && nodes.length === 0) + return setEdges(edges) setNodes(nodes) @@ -1874,12 +1949,14 @@ export const useNodesInteractions = () => { const [isDimming, setIsDimming] = useState(false) /** Add opacity-30 to all nodes except the nodeId */ const dimOtherNodes = useCallback(() => { - if (isDimming) return + if (isDimming) + return const { getNodes, setNodes, edges, setEdges } = store.getState() const nodes = getNodes() const selectedNode = nodes.find(n => n.data.selected) - if (!selectedNode) return + if (!selectedNode) + return setIsDimming(true) @@ -1890,8 +1967,10 @@ export const useNodesInteractions = () => { const dependencyNodes: Node[] = [] usedVars.forEach((valueSelector) => { const node = workflowNodes.find(node => node.id === valueSelector?.[0]) - if (node) - if (!dependencyNodes.includes(node)) dependencyNodes.push(node) + if (node) { + if (!dependencyNodes.includes(node)) + dependencyNodes.push(node) + } }) const outgoers = getOutgoers(selectedNode as Node, nodes as Node[], edges) @@ -1900,7 +1979,8 @@ export const useNodesInteractions = () => { const outgoersForNode = getOutgoers(node, nodes as Node[], edges) outgoersForNode.forEach((item) => { const existed = outgoers.some(v => v.id === item.id) - if (!existed) outgoers.push(item) + if (!existed) + outgoers.push(item) }) } @@ -1910,7 +1990,8 @@ export const useNodesInteractions = () => { const used = usedVars.some(v => v?.[0] === selectedNode.id) if (used) { const existed = dependentNodes.some(v => v.id === node.id) - if (!existed) dependentNodes.push(node) + if (!existed) + dependentNodes.push(node) } }) @@ -1919,7 +2000,8 @@ export const useNodesInteractions = () => { const newNodes = produce(nodes, (draft) => { draft.forEach((n) => { const dimNode = dimNodes.find(v => v.id === n.id) - if (!dimNode) n.data._dimmed = true + if (!dimNode) + n.data._dimmed = true }) }) diff --git a/web/app/components/workflow/hooks/use-nodes-layout.ts b/web/app/components/workflow/hooks/use-nodes-layout.ts index 594ac8b029..653b37008c 100644 --- a/web/app/components/workflow/hooks/use-nodes-layout.ts +++ b/web/app/components/workflow/hooks/use-nodes-layout.ts @@ -1,16 +1,16 @@ -import { useCallback } from 'react' -import ELK from 'elkjs/lib/elk.bundled.js' -import { - useReactFlow, - useStoreApi, -} from 'reactflow' -import { cloneDeep } from 'lodash-es' import type { Edge, Node, } from '../types' -import { useWorkflowStore } from '../store' +import ELK from 'elkjs/lib/elk.bundled.js' +import { cloneDeep } from 'lodash-es' +import { useCallback } from 'react' +import { + useReactFlow, + useStoreApi, +} from 'reactflow' import { AUTO_LAYOUT_OFFSET } from '../constants' +import { useWorkflowStore } from '../store' import { useNodesSyncDraft } from './use-nodes-sync-draft' const layoutOptions = { diff --git a/web/app/components/workflow/hooks/use-nodes-meta-data.ts b/web/app/components/workflow/hooks/use-nodes-meta-data.ts index fd63f23590..2ea2fd9e9f 100644 --- a/web/app/components/workflow/hooks/use-nodes-meta-data.ts +++ b/web/app/components/workflow/hooks/use-nodes-meta-data.ts @@ -1,17 +1,17 @@ -import { useMemo } from 'react' import type { AvailableNodesMetaData } from '@/app/components/workflow/hooks-store' -import { useHooksStore } from '@/app/components/workflow/hooks-store' -import { BlockEnum } from '@/app/components/workflow/types' import type { Node } from '@/app/components/workflow/types' +import { useMemo } from 'react' import { CollectionType } from '@/app/components/tools/types' +import { useHooksStore } from '@/app/components/workflow/hooks-store' import { useStore } from '@/app/components/workflow/store' -import { canFindTool } from '@/utils' +import { BlockEnum } from '@/app/components/workflow/types' import { useGetLanguage } from '@/context/i18n' import { useAllBuiltInTools, useAllCustomTools, useAllWorkflowTools, } from '@/service/use-tools' +import { canFindTool } from '@/utils' export const useNodesMetaData = () => { const availableNodesMetaData = useHooksStore(s => s.availableNodesMetaData) diff --git a/web/app/components/workflow/hooks/use-nodes-sync-draft.ts b/web/app/components/workflow/hooks/use-nodes-sync-draft.ts index a4c9a45542..db65bfa6ad 100644 --- a/web/app/components/workflow/hooks/use-nodes-sync-draft.ts +++ b/web/app/components/workflow/hooks/use-nodes-sync-draft.ts @@ -1,7 +1,7 @@ import { useCallback } from 'react' +import { useHooksStore } from '@/app/components/workflow/hooks-store' import { useStore } from '../store' import { useNodesReadOnly } from './use-workflow' -import { useHooksStore } from '@/app/components/workflow/hooks-store' export type SyncCallback = { onSuccess?: () => void diff --git a/web/app/components/workflow/hooks/use-selection-interactions.ts b/web/app/components/workflow/hooks/use-selection-interactions.ts index 481483d762..dbdef306c5 100644 --- a/web/app/components/workflow/hooks/use-selection-interactions.ts +++ b/web/app/components/workflow/hooks/use-selection-interactions.ts @@ -1,14 +1,14 @@ import type { MouseEvent } from 'react' -import { - useCallback, -} from 'react' -import { produce } from 'immer' import type { OnSelectionChangeFunc, } from 'reactflow' +import type { Node } from '../types' +import { produce } from 'immer' +import { + useCallback, +} from 'react' import { useStoreApi } from 'reactflow' import { useWorkflowStore } from '../store' -import type { Node } from '../types' export const useSelectionInteractions = () => { const store = useStoreApi() diff --git a/web/app/components/workflow/hooks/use-shortcuts.ts b/web/app/components/workflow/hooks/use-shortcuts.ts index 16502c97c4..00ed25bad0 100644 --- a/web/app/components/workflow/hooks/use-shortcuts.ts +++ b/web/app/components/workflow/hooks/use-shortcuts.ts @@ -1,13 +1,7 @@ -import { useReactFlow } from 'reactflow' import { useKeyPress } from 'ahooks' import { useCallback, useEffect } from 'react' +import { useReactFlow } from 'reactflow' import { ZEN_TOGGLE_EVENT } from '@/app/components/goto-anything/actions/commands/zen' -import { - getKeyboardKeyCodeBySystem, - isEventTargetInputArea, -} from '../utils' -import { useWorkflowHistoryStore } from '../workflow-history-store' -import { useWorkflowStore } from '../store' import { useEdgesInteractions, useNodesInteractions, @@ -16,6 +10,12 @@ import { useWorkflowMoveMode, useWorkflowOrganize, } from '.' +import { useWorkflowStore } from '../store' +import { + getKeyboardKeyCodeBySystem, + isEventTargetInputArea, +} from '../utils' +import { useWorkflowHistoryStore } from '../workflow-history-store' export const useShortcuts = (): void => { const { diff --git a/web/app/components/workflow/hooks/use-tool-icon.ts b/web/app/components/workflow/hooks/use-tool-icon.ts index faf962d450..a300021bad 100644 --- a/web/app/components/workflow/hooks/use-tool-icon.ts +++ b/web/app/components/workflow/hooks/use-tool-icon.ts @@ -1,9 +1,11 @@ -import { useCallback, useMemo } from 'react' +import type { TriggerWithProvider } from '../block-selector/types' +import type { DataSourceNodeType } from '../nodes/data-source/types' +import type { ToolNodeType } from '../nodes/tool/types' +import type { PluginTriggerNodeType } from '../nodes/trigger-plugin/types' import type { Node, ToolWithProvider } from '../types' -import { BlockEnum } from '../types' -import { useStore, useWorkflowStore } from '../store' +import { useCallback, useMemo } from 'react' import { CollectionType } from '@/app/components/tools/types' -import { canFindTool } from '@/utils' +import useTheme from '@/hooks/use-theme' import { useAllBuiltInTools, useAllCustomTools, @@ -11,11 +13,9 @@ import { useAllWorkflowTools, } from '@/service/use-tools' import { useAllTriggerPlugins } from '@/service/use-triggers' -import type { PluginTriggerNodeType } from '../nodes/trigger-plugin/types' -import type { ToolNodeType } from '../nodes/tool/types' -import type { DataSourceNodeType } from '../nodes/data-source/types' -import type { TriggerWithProvider } from '../block-selector/types' -import useTheme from '@/hooks/use-theme' +import { canFindTool } from '@/utils' +import { useStore, useWorkflowStore } from '../store' +import { BlockEnum } from '../types' const isTriggerPluginNode = (data: Node['data']): data is PluginTriggerNodeType => data.type === BlockEnum.TriggerPlugin diff --git a/web/app/components/workflow/hooks/use-workflow-history.ts b/web/app/components/workflow/hooks/use-workflow-history.ts index 58bbe415a8..3e6043a1fd 100644 --- a/web/app/components/workflow/hooks/use-workflow-history.ts +++ b/web/app/components/workflow/hooks/use-workflow-history.ts @@ -1,14 +1,15 @@ +import type { WorkflowHistoryEventMeta } from '../workflow-history-store' +import { debounce } from 'lodash-es' import { useCallback, - useRef, useState, + useRef, + useState, } from 'react' -import { debounce } from 'lodash-es' +import { useTranslation } from 'react-i18next' import { useStoreApi, } from 'reactflow' -import { useTranslation } from 'react-i18next' import { useWorkflowHistoryStore } from '../workflow-history-store' -import type { WorkflowHistoryEventMeta } from '../workflow-history-store' /** * All supported Events that create a new history state. diff --git a/web/app/components/workflow/hooks/use-workflow-interactions.ts b/web/app/components/workflow/hooks/use-workflow-interactions.ts index e56c39d51e..7a58581a99 100644 --- a/web/app/components/workflow/hooks/use-workflow-interactions.ts +++ b/web/app/components/workflow/hooks/use-workflow-interactions.ts @@ -1,16 +1,23 @@ +import type { WorkflowDataUpdater } from '../types' +import type { LayoutResult } from '../utils' +import { produce } from 'immer' import { useCallback, } from 'react' import { useReactFlow, useStoreApi } from 'reactflow' -import { produce } from 'immer' -import { useStore, useWorkflowStore } from '../store' +import { useEventEmitterContextContext } from '@/context/event-emitter' import { CUSTOM_NODE, NODE_LAYOUT_HORIZONTAL_PADDING, NODE_LAYOUT_VERTICAL_PADDING, WORKFLOW_DATA_UPDATE, } from '../constants' -import type { WorkflowDataUpdater } from '../types' +import { + useNodesReadOnly, + useSelectionInteractions, + useWorkflowReadOnly, +} from '../hooks' +import { useStore, useWorkflowStore } from '../store' import { BlockEnum, ControlMode } from '../types' import { getLayoutByDagre, @@ -18,17 +25,10 @@ import { initialEdges, initialNodes, } from '../utils' -import type { LayoutResult } from '../utils' -import { - useNodesReadOnly, - useSelectionInteractions, - useWorkflowReadOnly, -} from '../hooks' import { useEdgesInteractionsWithoutSync } from './use-edges-interactions-without-sync' import { useNodesInteractionsWithoutSync } from './use-nodes-interactions-without-sync' import { useNodesSyncDraft } from './use-nodes-sync-draft' -import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history' -import { useEventEmitterContextContext } from '@/context/event-emitter' +import { useWorkflowHistory, WorkflowHistoryEvent } from './use-workflow-history' export const useWorkflowInteractions = () => { const workflowStore = useWorkflowStore() @@ -99,8 +99,8 @@ export const useWorkflowOrganize = () => { const loopAndIterationNodes = nodes.filter( node => (node.data.type === BlockEnum.Loop || node.data.type === BlockEnum.Iteration) - && !node.parentId - && node.type === CUSTOM_NODE, + && !node.parentId + && node.type === CUSTOM_NODE, ) const childLayoutEntries = await Promise.all( @@ -119,7 +119,8 @@ export const useWorkflowOrganize = () => { loopAndIterationNodes.forEach((parentNode) => { const childLayout = childLayoutsMap[parentNode.id] - if (!childLayout) return + if (!childLayout) + return const { bounds, @@ -141,7 +142,7 @@ export const useWorkflowOrganize = () => { const nodesWithUpdatedSizes = produce(nodes, (draft) => { draft.forEach((node) => { if ((node.data.type === BlockEnum.Loop || node.data.type === BlockEnum.Iteration) - && containerSizeChanges[node.id]) { + && containerSizeChanges[node.id]) { node.width = containerSizeChanges[node.id].width node.height = containerSizeChanges[node.id].height @@ -160,7 +161,7 @@ export const useWorkflowOrganize = () => { const layout = await getLayoutByDagre(nodesWithUpdatedSizes, edges) // Build layer map for vertical alignment - nodes in the same layer should align - const layerMap = new Map<number, { minY: number; maxHeight: number }>() + const layerMap = new Map<number, { minY: number, maxHeight: number }>() layout.nodes.forEach((layoutInfo) => { if (layoutInfo.layer !== undefined) { const existing = layerMap.get(layoutInfo.layer) diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/index.ts b/web/app/components/workflow/hooks/use-workflow-run-event/index.ts index 67bc6c15ef..3f31b423ad 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/index.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/index.ts @@ -1,15 +1,15 @@ -export * from './use-workflow-started' -export * from './use-workflow-finished' +export * from './use-workflow-agent-log' export * from './use-workflow-failed' -export * from './use-workflow-node-started' +export * from './use-workflow-finished' export * from './use-workflow-node-finished' -export * from './use-workflow-node-iteration-started' -export * from './use-workflow-node-iteration-next' export * from './use-workflow-node-iteration-finished' -export * from './use-workflow-node-loop-started' -export * from './use-workflow-node-loop-next' +export * from './use-workflow-node-iteration-next' +export * from './use-workflow-node-iteration-started' export * from './use-workflow-node-loop-finished' +export * from './use-workflow-node-loop-next' +export * from './use-workflow-node-loop-started' export * from './use-workflow-node-retry' +export * from './use-workflow-node-started' +export * from './use-workflow-started' export * from './use-workflow-text-chunk' export * from './use-workflow-text-replace' -export * from './use-workflow-agent-log' diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-agent-log.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-agent-log.ts index a9deb9bc70..08fb526000 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-agent-log.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-agent-log.ts @@ -1,6 +1,6 @@ -import { useCallback } from 'react' -import { produce } from 'immer' import type { AgentLogResponse } from '@/types/workflow' +import { produce } from 'immer' +import { useCallback } from 'react' import { useWorkflowStore } from '@/app/components/workflow/store' export const useWorkflowAgentLog = () => { diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-failed.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-failed.ts index ff16aa1935..b2d9755ab5 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-failed.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-failed.ts @@ -1,5 +1,5 @@ -import { useCallback } from 'react' import { produce } from 'immer' +import { useCallback } from 'react' import { useWorkflowStore } from '@/app/components/workflow/store' import { WorkflowRunningStatus } from '@/app/components/workflow/types' diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-finished.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-finished.ts index 681a123b93..5fc8807873 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-finished.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-finished.ts @@ -1,8 +1,8 @@ -import { useCallback } from 'react' -import { produce } from 'immer' import type { WorkflowFinishedResponse } from '@/types/workflow' -import { useWorkflowStore } from '@/app/components/workflow/store' +import { produce } from 'immer' +import { useCallback } from 'react' import { getFilesInLogs } from '@/app/components/base/file-uploader/utils' +import { useWorkflowStore } from '@/app/components/workflow/store' export const useWorkflowFinished = () => { const workflowStore = useWorkflowStore() diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-finished.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-finished.ts index 9b90a2ebee..cf0d9bcef1 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-finished.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-finished.ts @@ -1,13 +1,13 @@ +import type { NodeFinishedResponse } from '@/types/workflow' +import { produce } from 'immer' import { useCallback } from 'react' import { useStoreApi } from 'reactflow' -import { produce } from 'immer' -import type { NodeFinishedResponse } from '@/types/workflow' +import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' +import { useWorkflowStore } from '@/app/components/workflow/store' import { BlockEnum, NodeRunningStatus, } from '@/app/components/workflow/types' -import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' -import { useWorkflowStore } from '@/app/components/workflow/store' export const useWorkflowNodeFinished = () => { const store = useStoreApi() diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-finished.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-finished.ts index 50700a7fad..4491104c08 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-finished.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-finished.ts @@ -1,9 +1,9 @@ +import type { IterationFinishedResponse } from '@/types/workflow' +import { produce } from 'immer' import { useCallback } from 'react' import { useStoreApi } from 'reactflow' -import { produce } from 'immer' -import type { IterationFinishedResponse } from '@/types/workflow' -import { useWorkflowStore } from '@/app/components/workflow/store' import { DEFAULT_ITER_TIMES } from '@/app/components/workflow/constants' +import { useWorkflowStore } from '@/app/components/workflow/store' export const useWorkflowNodeIterationFinished = () => { const store = useStoreApi() diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-next.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-next.ts index 3b88a02bfc..70fe6fbf22 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-next.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-next.ts @@ -1,7 +1,7 @@ +import type { IterationNextResponse } from '@/types/workflow' +import { produce } from 'immer' import { useCallback } from 'react' import { useStoreApi } from 'reactflow' -import { produce } from 'immer' -import type { IterationNextResponse } from '@/types/workflow' import { useWorkflowStore } from '@/app/components/workflow/store' export const useWorkflowNodeIterationNext = () => { diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-started.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-started.ts index c064809a10..388d3936ed 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-started.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-iteration-started.ts @@ -1,13 +1,13 @@ +import type { IterationStartedResponse } from '@/types/workflow' +import { produce } from 'immer' import { useCallback } from 'react' import { useReactFlow, useStoreApi, } from 'reactflow' -import { produce } from 'immer' -import { useWorkflowStore } from '@/app/components/workflow/store' -import type { IterationStartedResponse } from '@/types/workflow' -import { NodeRunningStatus } from '@/app/components/workflow/types' import { DEFAULT_ITER_TIMES } from '@/app/components/workflow/constants' +import { useWorkflowStore } from '@/app/components/workflow/store' +import { NodeRunningStatus } from '@/app/components/workflow/types' export const useWorkflowNodeIterationStarted = () => { const store = useStoreApi() @@ -17,8 +17,8 @@ export const useWorkflowNodeIterationStarted = () => { const handleWorkflowNodeIterationStarted = useCallback(( params: IterationStartedResponse, containerParams: { - clientWidth: number, - clientHeight: number, + clientWidth: number + clientHeight: number }, ) => { const { data } = params diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-loop-finished.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-loop-finished.ts index a7605dfc55..24df8ac6aa 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-loop-finished.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-loop-finished.ts @@ -1,7 +1,7 @@ +import type { LoopFinishedResponse } from '@/types/workflow' +import { produce } from 'immer' import { useCallback } from 'react' import { useStoreApi } from 'reactflow' -import { produce } from 'immer' -import type { LoopFinishedResponse } from '@/types/workflow' import { useWorkflowStore } from '@/app/components/workflow/store' export const useWorkflowNodeLoopFinished = () => { diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-loop-next.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-loop-next.ts index 26f52bc23c..2f92e2bae1 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-loop-next.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-loop-next.ts @@ -1,7 +1,7 @@ +import type { LoopNextResponse } from '@/types/workflow' +import { produce } from 'immer' import { useCallback } from 'react' import { useStoreApi } from 'reactflow' -import { produce } from 'immer' -import type { LoopNextResponse } from '@/types/workflow' import { NodeRunningStatus } from '@/app/components/workflow/types' export const useWorkflowNodeLoopNext = () => { diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-loop-started.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-loop-started.ts index d83b9a2ab8..c5ace9c692 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-loop-started.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-loop-started.ts @@ -1,11 +1,11 @@ +import type { LoopStartedResponse } from '@/types/workflow' +import { produce } from 'immer' import { useCallback } from 'react' import { useReactFlow, useStoreApi, } from 'reactflow' -import { produce } from 'immer' import { useWorkflowStore } from '@/app/components/workflow/store' -import type { LoopStartedResponse } from '@/types/workflow' import { NodeRunningStatus } from '@/app/components/workflow/types' export const useWorkflowNodeLoopStarted = () => { @@ -16,8 +16,8 @@ export const useWorkflowNodeLoopStarted = () => { const handleWorkflowNodeLoopStarted = useCallback(( params: LoopStartedResponse, containerParams: { - clientWidth: number, - clientHeight: number, + clientWidth: number + clientHeight: number }, ) => { const { data } = params diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-retry.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-retry.ts index b7fb631a4f..5b09d27ca4 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-retry.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-retry.ts @@ -1,9 +1,9 @@ -import { useCallback } from 'react' -import { useStoreApi } from 'reactflow' -import { produce } from 'immer' import type { NodeFinishedResponse, } from '@/types/workflow' +import { produce } from 'immer' +import { useCallback } from 'react' +import { useStoreApi } from 'reactflow' import { useWorkflowStore } from '@/app/components/workflow/store' export const useWorkflowNodeRetry = () => { diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-started.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-started.ts index a3ae0407ea..03c7387d38 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-started.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-started.ts @@ -1,12 +1,12 @@ +import type { NodeStartedResponse } from '@/types/workflow' +import { produce } from 'immer' import { useCallback } from 'react' import { useReactFlow, useStoreApi, } from 'reactflow' -import { produce } from 'immer' -import type { NodeStartedResponse } from '@/types/workflow' -import { NodeRunningStatus } from '@/app/components/workflow/types' import { useWorkflowStore } from '@/app/components/workflow/store' +import { NodeRunningStatus } from '@/app/components/workflow/types' export const useWorkflowNodeStarted = () => { const store = useStoreApi() @@ -16,8 +16,8 @@ export const useWorkflowNodeStarted = () => { const handleWorkflowNodeStarted = useCallback(( params: NodeStartedResponse, containerParams: { - clientWidth: number, - clientHeight: number, + clientWidth: number + clientHeight: number }, ) => { const { data } = params diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-started.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-started.ts index 2843cf0f1c..16ad976607 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-started.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-started.ts @@ -1,9 +1,9 @@ +import type { WorkflowStartedResponse } from '@/types/workflow' +import { produce } from 'immer' import { useCallback } from 'react' import { useStoreApi } from 'reactflow' -import { produce } from 'immer' -import type { WorkflowStartedResponse } from '@/types/workflow' -import { WorkflowRunningStatus } from '@/app/components/workflow/types' import { useWorkflowStore } from '@/app/components/workflow/store' +import { WorkflowRunningStatus } from '@/app/components/workflow/types' export const useWorkflowStarted = () => { const store = useStoreApi() diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-text-chunk.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-text-chunk.ts index 1667265548..dfca7f61a6 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-text-chunk.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-text-chunk.ts @@ -1,6 +1,6 @@ -import { useCallback } from 'react' -import { produce } from 'immer' import type { TextChunkResponse } from '@/types/workflow' +import { produce } from 'immer' +import { useCallback } from 'react' import { useWorkflowStore } from '@/app/components/workflow/store' export const useWorkflowTextChunk = () => { diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-text-replace.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-text-replace.ts index 3efd287c9b..74bb0a7ad3 100644 --- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-text-replace.ts +++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-text-replace.ts @@ -1,6 +1,6 @@ -import { useCallback } from 'react' -import { produce } from 'immer' import type { TextReplaceResponse } from '@/types/workflow' +import { produce } from 'immer' +import { useCallback } from 'react' import { useWorkflowStore } from '@/app/components/workflow/store' export const useWorkflowTextReplace = () => { diff --git a/web/app/components/workflow/hooks/use-workflow-search.tsx b/web/app/components/workflow/hooks/use-workflow-search.tsx index 68ad9873f9..8ca597f94e 100644 --- a/web/app/components/workflow/hooks/use-workflow-search.tsx +++ b/web/app/components/workflow/hooks/use-workflow-search.tsx @@ -1,23 +1,23 @@ 'use client' +import type { LLMNodeType } from '../nodes/llm/types' +import type { CommonNodeType } from '../types' +import type { Emoji } from '@/app/components/tools/types' import { useCallback, useEffect, useMemo } from 'react' import { useNodes } from 'reactflow' -import { useNodesInteractions } from './use-nodes-interactions' -import type { CommonNodeType } from '../types' import { workflowNodesAction } from '@/app/components/goto-anything/actions/workflow-nodes' -import BlockIcon from '@/app/components/workflow/block-icon' -import { setupNodeSelectionListener } from '../utils/node-navigation' -import { BlockEnum } from '../types' -import type { Emoji } from '@/app/components/tools/types' import { CollectionType } from '@/app/components/tools/types' -import { canFindTool } from '@/utils' -import type { LLMNodeType } from '../nodes/llm/types' +import BlockIcon from '@/app/components/workflow/block-icon' import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, } from '@/service/use-tools' +import { canFindTool } from '@/utils' +import { BlockEnum } from '../types' +import { setupNodeSelectionListener } from '../utils/node-navigation' +import { useNodesInteractions } from './use-nodes-interactions' /** * Hook to register workflow nodes search functionality @@ -34,7 +34,8 @@ export const useWorkflowSearch = () => { // Extract tool icon logic - clean separation of concerns const getToolIcon = useCallback((nodeData: CommonNodeType): string | Emoji | undefined => { - if (nodeData?.type !== BlockEnum.Tool) return undefined + if (nodeData?.type !== BlockEnum.Tool) + return undefined const toolCollections: Record<string, any[]> = { [CollectionType.builtIn]: buildInTools || [], @@ -48,19 +49,23 @@ export const useWorkflowSearch = () => { // Extract model info logic - clean extraction const getModelInfo = useCallback((nodeData: CommonNodeType) => { - if (nodeData?.type !== BlockEnum.LLM) return {} + if (nodeData?.type !== BlockEnum.LLM) + return {} const llmNodeData = nodeData as LLMNodeType - return llmNodeData.model ? { - provider: llmNodeData.model.provider, - name: llmNodeData.model.name, - mode: llmNodeData.model.mode, - } : {} + return llmNodeData.model + ? { + provider: llmNodeData.model.provider, + name: llmNodeData.model.name, + mode: llmNodeData.model.mode, + } + : {} }, []) const searchableNodes = useMemo(() => { const filteredNodes = nodes.filter((node) => { - if (!node.id || !node.data || node.type === 'sticky') return false + if (!node.id || !node.data || node.type === 'sticky') + return false const nodeData = node.data as CommonNodeType const nodeType = nodeData?.type @@ -87,12 +92,13 @@ export const useWorkflowSearch = () => { // Calculate search score - clean scoring logic const calculateScore = useCallback((node: { - title: string; - type: string; - desc: string; - modelInfo: { provider?: string; name?: string; mode?: string } + title: string + type: string + desc: string + modelInfo: { provider?: string, name?: string, mode?: string } }, searchTerm: string): number => { - if (!searchTerm) return 1 + if (!searchTerm) + return 1 const titleMatch = node.title.toLowerCase() const typeMatch = node.type.toLowerCase() @@ -104,27 +110,36 @@ export const useWorkflowSearch = () => { let score = 0 // Title matching (exact prefix > partial match) - if (titleMatch.startsWith(searchTerm)) score += 100 - else if (titleMatch.includes(searchTerm)) score += 50 + if (titleMatch.startsWith(searchTerm)) + score += 100 + else if (titleMatch.includes(searchTerm)) + score += 50 // Type matching (exact > partial) - if (typeMatch === searchTerm) score += 80 - else if (typeMatch.includes(searchTerm)) score += 30 + if (typeMatch === searchTerm) + score += 80 + else if (typeMatch.includes(searchTerm)) + score += 30 // Description matching (additive) - if (descMatch.includes(searchTerm)) score += 20 + if (descMatch.includes(searchTerm)) + score += 20 // LLM model matching (additive - can combine multiple matches) - if (modelNameMatch && modelNameMatch.includes(searchTerm)) score += 60 - if (modelProviderMatch && modelProviderMatch.includes(searchTerm)) score += 40 - if (modelModeMatch && modelModeMatch.includes(searchTerm)) score += 30 + if (modelNameMatch && modelNameMatch.includes(searchTerm)) + score += 60 + if (modelProviderMatch && modelProviderMatch.includes(searchTerm)) + score += 40 + if (modelModeMatch && modelModeMatch.includes(searchTerm)) + score += 30 return score }, []) // Create search function for workflow nodes const searchWorkflowNodes = useCallback((query: string) => { - if (!searchableNodes.length) return [] + if (!searchableNodes.length) + return [] const searchTerm = query.toLowerCase().trim() @@ -132,32 +147,35 @@ export const useWorkflowSearch = () => { .map((node) => { const score = calculateScore(node, searchTerm) - return score > 0 ? { - id: node.id, - title: node.title, - description: node.desc || node.type, - type: 'workflow-node' as const, - path: `#${node.id}`, - icon: ( - <BlockIcon - type={node.blockType} - className="shrink-0" - size="sm" - toolIcon={node.toolIcon} - /> - ), - metadata: { - nodeId: node.id, - nodeData: node.nodeData, - }, - data: node.nodeData, - score, - } : null + return score > 0 + ? { + id: node.id, + title: node.title, + description: node.desc || node.type, + type: 'workflow-node' as const, + path: `#${node.id}`, + icon: ( + <BlockIcon + type={node.blockType} + className="shrink-0" + size="sm" + toolIcon={node.toolIcon} + /> + ), + metadata: { + nodeId: node.id, + nodeData: node.nodeData, + }, + data: node.nodeData, + score, + } + : null }) .filter((node): node is NonNullable<typeof node> => node !== null) .sort((a, b) => { // If no search term, sort alphabetically - if (!searchTerm) return a.title.localeCompare(b.title) + if (!searchTerm) + return a.title.localeCompare(b.title) // Sort by relevance score (higher score first) return (b.score || 0) - (a.score || 0) }) diff --git a/web/app/components/workflow/hooks/use-workflow-variables.ts b/web/app/components/workflow/hooks/use-workflow-variables.ts index 871937365a..d6a5b8bdd1 100644 --- a/web/app/components/workflow/hooks/use-workflow-variables.ts +++ b/web/app/components/workflow/hooks/use-workflow-variables.ts @@ -1,23 +1,23 @@ -import { useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import { useWorkflowStore } from '../store' -import { getVarType, toNodeAvailableVars } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import type { Type } from '../nodes/llm/types' import type { Node, NodeOutPutVar, ValueSelector, Var, } from '@/app/components/workflow/types' -import { useIsChatMode } from './use-workflow' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import { useStoreApi } from 'reactflow' -import type { Type } from '../nodes/llm/types' -import useMatchSchemaType from '../nodes/_base/components/variable/use-match-schema-type' +import { getVarType, toNodeAvailableVars } from '@/app/components/workflow/nodes/_base/components/variable/utils' import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, } from '@/service/use-tools' +import useMatchSchemaType from '../nodes/_base/components/variable/use-match-schema-type' +import { useWorkflowStore } from '../store' +import { useIsChatMode } from './use-workflow' export const useWorkflowVariables = () => { const { t } = useTranslation() @@ -137,8 +137,8 @@ export const useWorkflowVariableType = () => { nodeId, valueSelector, }: { - nodeId: string, - valueSelector: ValueSelector, + nodeId: string + valueSelector: ValueSelector }) => { const node = getNodes().find(n => n.id === nodeId) const isInIteration = !!node?.data.isInIteration diff --git a/web/app/components/workflow/hooks/use-workflow.ts b/web/app/components/workflow/hooks/use-workflow.ts index e6746085b8..c958bb6b83 100644 --- a/web/app/components/workflow/hooks/use-workflow.ts +++ b/web/app/components/workflow/hooks/use-workflow.ts @@ -1,46 +1,46 @@ -import { - useCallback, -} from 'react' -import { uniqBy } from 'lodash-es' -import { - getIncomers, - getOutgoers, - useStoreApi, -} from 'reactflow' import type { Connection, } from 'reactflow' +import type { IterationNodeType } from '../nodes/iteration/types' +import type { LoopNodeType } from '../nodes/loop/types' import type { BlockEnum, Edge, Node, ValueSelector, } from '../types' +import { uniqBy } from 'lodash-es' import { - WorkflowRunningStatus, -} from '../types' + useCallback, +} from 'react' +import { + getIncomers, + getOutgoers, + useStoreApi, +} from 'reactflow' +import { useStore as useAppStore } from '@/app/components/app/store' +import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants' +import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants' +import { AppModeEnum } from '@/types/app' +import { useNodesMetaData } from '.' +import { + SUPPORT_OUTPUT_VARS_NODE, +} from '../constants' +import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils' +import { CUSTOM_NOTE_NODE } from '../note-node/constants' + import { useStore, useWorkflowStore, } from '../store' +import { + WorkflowRunningStatus, +} from '../types' import { getWorkflowEntryNode, isWorkflowEntryNode, } from '../utils/workflow-entry' -import { - SUPPORT_OUTPUT_VARS_NODE, -} from '../constants' -import type { IterationNodeType } from '../nodes/iteration/types' -import type { LoopNodeType } from '../nodes/loop/types' -import { CUSTOM_NOTE_NODE } from '../note-node/constants' -import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils' import { useAvailableBlocks } from './use-available-blocks' -import { useStore as useAppStore } from '@/app/components/app/store' - -import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants' -import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants' -import { useNodesMetaData } from '.' -import { AppModeEnum } from '@/types/app' export const useIsChatMode = () => { const appDetail = useAppStore(s => s.appDetail) diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx index 4f6ee4e64a..ab31f36406 100644 --- a/web/app/components/workflow/index.tsx +++ b/web/app/components/workflow/index.tsx @@ -1,6 +1,21 @@ 'use client' import type { FC } from 'react' +import type { + Viewport, +} from 'reactflow' +import type { Shape as HooksStoreShape } from './hooks-store' +import type { + Edge, + Node, +} from './types' +import type { VarInInspect } from '@/types/workflow' +import { + useEventListener, +} from 'ahooks' +import { setAutoFreeze } from 'immer' +import { isEqual } from 'lodash-es' +import dynamic from 'next/dynamic' import { memo, useCallback, @@ -9,10 +24,6 @@ import { useRef, useState, } from 'react' -import { setAutoFreeze } from 'immer' -import { - useEventListener, -} from 'ahooks' import ReactFlow, { Background, ReactFlowProvider, @@ -24,18 +35,26 @@ import ReactFlow, { useReactFlow, useStoreApi, } from 'reactflow' -import type { - Viewport, -} from 'reactflow' -import 'reactflow/dist/style.css' -import './style.css' -import type { - Edge, - Node, -} from './types' +import { useEventEmitterContextContext } from '@/context/event-emitter' import { - ControlMode, -} from './types' + useAllBuiltInTools, + useAllCustomTools, + useAllMCPTools, + useAllWorkflowTools, +} from '@/service/use-tools' +import { fetchAllInspectVars } from '@/service/workflow' +import { cn } from '@/utils/classnames' +import CandidateNode from './candidate-node' +import { + CUSTOM_EDGE, + CUSTOM_NODE, + ITERATION_CHILDREN_Z_INDEX, + WORKFLOW_DATA_UPDATE, +} from './constants' +import CustomConnectionLine from './custom-connection-line' +import CustomEdge from './custom-edge' +import DatasetsDetailProvider from './datasets-detail-store/provider' +import HelpLine from './help-line' import { useEdgesInteractions, useNodesInteractions, @@ -49,56 +68,37 @@ import { useWorkflowReadOnly, useWorkflowRefreshDraft, } from './hooks' +import { HooksStoreContextProvider, useHooksStore } from './hooks-store' +import { useWorkflowSearch } from './hooks/use-workflow-search' +import NodeContextmenu from './node-contextmenu' import CustomNode from './nodes' -import CustomNoteNode from './note-node' -import { CUSTOM_NOTE_NODE } from './note-node/constants' +import useMatchSchemaType from './nodes/_base/components/variable/use-match-schema-type' +import CustomDataSourceEmptyNode from './nodes/data-source-empty' +import { CUSTOM_DATA_SOURCE_EMPTY_NODE } from './nodes/data-source-empty/constants' import CustomIterationStartNode from './nodes/iteration-start' import { CUSTOM_ITERATION_START_NODE } from './nodes/iteration-start/constants' import CustomLoopStartNode from './nodes/loop-start' import { CUSTOM_LOOP_START_NODE } from './nodes/loop-start/constants' +import CustomNoteNode from './note-node' +import { CUSTOM_NOTE_NODE } from './note-node/constants' +import Operator from './operator' +import Control from './operator/control' +import PanelContextmenu from './panel-contextmenu' +import SelectionContextmenu from './selection-contextmenu' import CustomSimpleNode from './simple-node' import { CUSTOM_SIMPLE_NODE } from './simple-node/constants' -import CustomDataSourceEmptyNode from './nodes/data-source-empty' -import { CUSTOM_DATA_SOURCE_EMPTY_NODE } from './nodes/data-source-empty/constants' -import Operator from './operator' -import { useWorkflowSearch } from './hooks/use-workflow-search' -import Control from './operator/control' -import CustomEdge from './custom-edge' -import CustomConnectionLine from './custom-connection-line' -import HelpLine from './help-line' -import CandidateNode from './candidate-node' -import PanelContextmenu from './panel-contextmenu' -import NodeContextmenu from './node-contextmenu' -import SelectionContextmenu from './selection-contextmenu' -import SyncingDataModal from './syncing-data-modal' -import { setupScrollToNodeListener } from './utils/node-navigation' import { useStore, useWorkflowStore, } from './store' +import SyncingDataModal from './syncing-data-modal' import { - CUSTOM_EDGE, - CUSTOM_NODE, - ITERATION_CHILDREN_Z_INDEX, - WORKFLOW_DATA_UPDATE, -} from './constants' + ControlMode, +} from './types' +import { setupScrollToNodeListener } from './utils/node-navigation' import { WorkflowHistoryProvider } from './workflow-history-store' -import { useEventEmitterContextContext } from '@/context/event-emitter' -import DatasetsDetailProvider from './datasets-detail-store/provider' -import { HooksStoreContextProvider, useHooksStore } from './hooks-store' -import type { Shape as HooksStoreShape } from './hooks-store' -import dynamic from 'next/dynamic' -import useMatchSchemaType from './nodes/_base/components/variable/use-match-schema-type' -import type { VarInInspect } from '@/types/workflow' -import { fetchAllInspectVars } from '@/service/workflow' -import { cn } from '@/utils/classnames' -import { - useAllBuiltInTools, - useAllCustomTools, - useAllMCPTools, - useAllWorkflowTools, -} from '@/service/use-tools' -import { isEqual } from 'lodash-es' +import 'reactflow/dist/style.css' +import './style.css' const Confirm = dynamic(() => import('@/app/components/base/confirm'), { ssr: false, @@ -362,7 +362,7 @@ export const Workflow: FC<WorkflowProps> = memo(({ return ( <div - id='workflow-container' + id="workflow-container" className={cn( 'relative h-full w-full min-w-[960px]', workflowReadOnly && 'workflow-panel-animation', @@ -373,7 +373,7 @@ export const Workflow: FC<WorkflowProps> = memo(({ <SyncingDataModal /> <CandidateNode /> <div - className='pointer-events-none absolute left-0 top-0 z-10 flex w-12 items-center justify-center p-1 pl-2' + className="pointer-events-none absolute left-0 top-0 z-10 flex w-12 items-center justify-center p-1 pl-2" style={{ height: controlHeight }} > <Control /> @@ -443,7 +443,7 @@ export const Workflow: FC<WorkflowProps> = memo(({ gap={[14, 14]} size={2} className="bg-workflow-canvas-workflow-bg" - color='var(--color-workflow-canvas-workflow-dot-color)' + color="var(--color-workflow-canvas-workflow-dot-color)" /> </ReactFlow> </div> @@ -466,9 +466,9 @@ export const WorkflowWithInnerContext = memo(({ type WorkflowWithDefaultContextProps = Pick<WorkflowProps, 'edges' | 'nodes'> - & { - children: React.ReactNode - } + & { + children: React.ReactNode + } const WorkflowWithDefaultContext = ({ nodes, @@ -479,7 +479,8 @@ const WorkflowWithDefaultContext = ({ <ReactFlowProvider> <WorkflowHistoryProvider nodes={nodes} - edges={edges} > + edges={edges} + > <DatasetsDetailProvider nodes={nodes}> {children} </DatasetsDetailProvider> diff --git a/web/app/components/workflow/node-contextmenu.tsx b/web/app/components/workflow/node-contextmenu.tsx index 86708981fe..cd749fefc0 100644 --- a/web/app/components/workflow/node-contextmenu.tsx +++ b/web/app/components/workflow/node-contextmenu.tsx @@ -1,14 +1,14 @@ +import type { Node } from './types' +import { useClickAway } from 'ahooks' import { memo, useEffect, useRef, } from 'react' -import { useClickAway } from 'ahooks' import useNodes from '@/app/components/workflow/store/workflow/use-nodes' -import PanelOperatorPopup from './nodes/_base/components/panel-operator/panel-operator-popup' -import type { Node } from './types' -import { useStore } from './store' import { usePanelInteractions } from './hooks' +import PanelOperatorPopup from './nodes/_base/components/panel-operator/panel-operator-popup' +import { useStore } from './store' const NodeContextmenu = () => { const ref = useRef(null) @@ -30,7 +30,7 @@ const NodeContextmenu = () => { return ( <div - className='absolute z-[9]' + className="absolute z-[9]" style={{ left: nodeMenu.left, top: nodeMenu.top, 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 12bf649cda..99ccc61fe5 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 React from 'react' import { RiAddLine, } from '@remixicon/react' -import { cn } from '@/utils/classnames' +import React from 'react' import Button from '@/app/components/base/button' +import { cn } from '@/utils/classnames' type Props = { className?: string @@ -21,11 +21,11 @@ const AddButton: FC<Props> = ({ return ( <Button className={cn('w-full', className)} - variant='tertiary' - size='medium' + variant="tertiary" + size="medium" onClick={onClick} > - <RiAddLine className='mr-1 h-3.5 w-3.5' /> + <RiAddLine className="mr-1 h-3.5 w-3.5" /> <div>{text}</div> </Button> ) diff --git a/web/app/components/workflow/nodes/_base/components/add-variable-popup-with-position.tsx b/web/app/components/workflow/nodes/_base/components/add-variable-popup-with-position.tsx index 6d54e38556..bdbe6c1415 100644 --- a/web/app/components/workflow/nodes/_base/components/add-variable-popup-with-position.tsx +++ b/web/app/components/workflow/nodes/_base/components/add-variable-popup-with-position.tsx @@ -1,22 +1,22 @@ +import type { + ValueSelector, + Var, + VarType, +} from '../../../types' +import { useClickAway } from 'ahooks' import { memo, useCallback, useMemo, useRef, } from 'react' -import { useClickAway } from 'ahooks' -import { useStore } from '../../../store' import { useIsChatMode, useNodeDataUpdate, useWorkflow, useWorkflowVariables, } from '../../../hooks' -import type { - ValueSelector, - Var, - VarType, -} from '../../../types' +import { useStore } from '../../../store' import { useVariableAssigner } from '../../variable-assigner/hooks' import { filterVar } from '../../variable-assigner/utils' import AddVariablePopup from './add-variable-popup' @@ -112,7 +112,7 @@ const AddVariablePopupWithPosition = ({ return ( <div - className='absolute z-10' + className="absolute z-10" style={{ left: showAssignVariablePopup.x, top: showAssignVariablePopup.y, diff --git a/web/app/components/workflow/nodes/_base/components/add-variable-popup.tsx b/web/app/components/workflow/nodes/_base/components/add-variable-popup.tsx index 663ed074e8..92176cad8a 100644 --- a/web/app/components/workflow/nodes/_base/components/add-variable-popup.tsx +++ b/web/app/components/workflow/nodes/_base/components/add-variable-popup.tsx @@ -1,11 +1,11 @@ -import { memo } from 'react' -import { useTranslation } from 'react-i18next' -import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' import type { NodeOutPutVar, ValueSelector, Var, } from '@/app/components/workflow/types' +import { memo } from 'react' +import { useTranslation } from 'react-i18next' +import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' export type AddVariablePopupProps = { availableVars: NodeOutPutVar[] @@ -18,11 +18,11 @@ export const AddVariablePopup = ({ const { t } = useTranslation() return ( - <div className='w-[240px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg'> - <div className='flex h-[34px] items-center border-b-[0.5px] border-b-divider-regular px-4 text-[13px] font-semibold text-text-secondary'> + <div className="w-[240px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg"> + <div className="flex h-[34px] items-center border-b-[0.5px] border-b-divider-regular px-4 text-[13px] font-semibold text-text-secondary"> {t('workflow.nodes.variableAssigner.setAssignVariable')} </div> - <div className='p-1'> + <div className="p-1"> <VarReferenceVars hideSearch vars={availableVars} diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx index 96ae7e03e1..188d37bbff 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx @@ -1,59 +1,62 @@ -import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' import type { ReactNode } from 'react' -import { memo, useEffect, useMemo, useRef, useState } from 'react' -import type { Strategy } from './agent-strategy' -import { cn } from '@/utils/classnames' -import { RiArrowDownSLine, RiErrorWarningFill } from '@remixicon/react' -import Tooltip from '@/app/components/base/tooltip' -import Link from 'next/link' -import { InstallPluginButton } from './install-plugin-button' -import ViewTypeSelect, { ViewType } from '../../../block-selector/view-type-select' -import SearchInput from '@/app/components/base/search-input' -import Tools from '../../../block-selector/tools' -import { useTranslation } from 'react-i18next' -import { useStrategyProviders } from '@/service/use-strategy' -import { PluginCategoryEnum, type StrategyPluginDetail } from '@/app/components/plugins/types' import type { ToolWithProvider } from '../../../types' -import { CollectionType } from '@/app/components/tools/types' -import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' -import { useStrategyInfo } from '../../agent/use-config' -import { SwitchPluginVersion } from './switch-plugin-version' -import type { ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list' -import PluginList, { type ListProps } from '@/app/components/workflow/block-selector/market-place-plugin/list' -import { useMarketplacePlugins } from '@/app/components/plugins/marketplace/hooks' +import type { Strategy } from './agent-strategy' +import type { StrategyPluginDetail } from '@/app/components/plugins/types' +import type { ListProps, ListRef } from '@/app/components/workflow/block-selector/market-place-plugin/list' +import { RiArrowDownSLine, RiErrorWarningFill } from '@remixicon/react' +import Link from 'next/link' +import { memo, useEffect, useMemo, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' +import SearchInput from '@/app/components/base/search-input' +import Tooltip from '@/app/components/base/tooltip' import { ToolTipContent } from '@/app/components/base/tooltip/content' +import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' +import { useMarketplacePlugins } from '@/app/components/plugins/marketplace/hooks' +import { PluginCategoryEnum } from '@/app/components/plugins/types' +import { CollectionType } from '@/app/components/tools/types' +import PluginList from '@/app/components/workflow/block-selector/market-place-plugin/list' import { useGlobalPublicStore } from '@/context/global-public-context' +import { useStrategyProviders } from '@/service/use-strategy' +import { cn } from '@/utils/classnames' +import Tools from '../../../block-selector/tools' +import ViewTypeSelect, { ViewType } from '../../../block-selector/view-type-select' +import { useStrategyInfo } from '../../agent/use-config' +import { InstallPluginButton } from './install-plugin-button' +import { SwitchPluginVersion } from './switch-plugin-version' const DEFAULT_TAGS: ListProps['tags'] = [] const NotFoundWarn = (props: { - title: ReactNode, + title: ReactNode description: ReactNode }) => { const { title, description } = props const { t } = useTranslation() - return <Tooltip - popupContent={ - <div className='space-y-1 text-xs'> - <h3 className='font-semibold text-text-primary'> - {title} - </h3> - <p className='tracking-tight text-text-secondary'> - {description} - </p> - <p> - <Link href={'/plugins'} className='tracking-tight text-text-accent'> - {t('workflow.nodes.agent.linkToPlugin')} - </Link> - </p> + return ( + <Tooltip + popupContent={( + <div className="space-y-1 text-xs"> + <h3 className="font-semibold text-text-primary"> + {title} + </h3> + <p className="tracking-tight text-text-secondary"> + {description} + </p> + <p> + <Link href="/plugins" className="tracking-tight text-text-accent"> + {t('workflow.nodes.agent.linkToPlugin')} + </Link> + </p> + </div> + )} + > + <div> + <RiErrorWarningFill className="size-4 text-text-destructive" /> </div> - } - > - <div> - <RiErrorWarningFill className='size-4 text-text-destructive' /> - </div> - </Tooltip> + </Tooltip> + ) } function formatStrategy(input: StrategyPluginDetail[], getIcon: (i: string) => string): ToolWithProvider[] { @@ -87,9 +90,9 @@ function formatStrategy(input: StrategyPluginDetail[], getIcon: (i: string) => s } export type AgentStrategySelectorProps = { - value?: Strategy, - onChange: (value?: Strategy) => void, - canChooseMCPTool: boolean, + value?: Strategy + onChange: (value?: Strategy) => void + canChooseMCPTool: boolean } export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => { @@ -103,7 +106,8 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => const { getIconUrl } = useGetIcon() const list = stra.data ? formatStrategy(stra.data, getIconUrl) : undefined const filteredTools = useMemo(() => { - if (!list) return [] + if (!list) + return [] return list.filter(tool => tool.name.toLowerCase().includes(query.toLowerCase())) }, [query, list]) const { strategyStatus, refetch: refetchStrategyInfo } = useStrategyInfo( @@ -136,7 +140,8 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => } = useMarketplacePlugins() useEffect(() => { - if (!enable_marketplace) return + if (!enable_marketplace) + return if (query) { fetchPlugins({ query, @@ -147,97 +152,115 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => const pluginRef = useRef<ListRef>(null) - return <PortalToFollowElem open={open} onOpenChange={setOpen} placement='bottom'> - <PortalToFollowElemTrigger className='w-full'> - <div - className='flex h-8 w-full select-none items-center gap-0.5 rounded-lg bg-components-input-bg-normal p-1 hover:bg-state-base-hover-alt' - onClick={() => setOpen(o => !o)} - > - { } - {icon && <div className='flex h-6 w-6 items-center justify-center'><img - src={icon} - width={20} - height={20} - className='rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge' - alt='icon' - /></div>} - <p - className={cn(value ? 'text-components-input-text-filled' : 'text-components-input-text-placeholder', 'px-1 text-xs')} + return ( + <PortalToFollowElem open={open} onOpenChange={setOpen} placement="bottom"> + <PortalToFollowElemTrigger className="w-full"> + <div + className="flex h-8 w-full select-none items-center gap-0.5 rounded-lg bg-components-input-bg-normal p-1 hover:bg-state-base-hover-alt" + onClick={() => setOpen(o => !o)} > - {value?.agent_strategy_label || t('workflow.nodes.agent.strategy.selectTip')} - </p> - <div className='ml-auto flex items-center gap-1'> - {showInstallButton && value && <InstallPluginButton - onClick={e => e.stopPropagation()} - size={'small'} - uniqueIdentifier={value.plugin_unique_identifier} - />} - {showPluginNotInstalledWarn - ? <NotFoundWarn - title={t('workflow.nodes.agent.pluginNotInstalled')} - description={t('workflow.nodes.agent.pluginNotInstalledDesc')} - /> - : showUnsupportedStrategy - ? <NotFoundWarn - title={t('workflow.nodes.agent.unsupportedStrategy')} - description={t('workflow.nodes.agent.strategyNotFoundDesc')} + { } + {icon && ( + <div className="flex h-6 w-6 items-center justify-center"> + <img + src={icon} + width={20} + height={20} + className="rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge" + alt="icon" /> - : <RiArrowDownSLine className='size-4 text-text-tertiary' /> - } - {showSwitchVersion && <SwitchPluginVersion - uniqueIdentifier={value.plugin_unique_identifier} - tooltip={<ToolTipContent - title={t('workflow.nodes.agent.unsupportedStrategy')}> - {t('workflow.nodes.agent.strategyNotFoundDescAndSwitchVersion')} - </ToolTipContent>} - onChange={() => { - refetchStrategyInfo() - }} - />} + </div> + )} + <p + className={cn(value ? 'text-components-input-text-filled' : 'text-components-input-text-placeholder', 'px-1 text-xs')} + > + {value?.agent_strategy_label || t('workflow.nodes.agent.strategy.selectTip')} + </p> + <div className="ml-auto flex items-center gap-1"> + {showInstallButton && value && ( + <InstallPluginButton + onClick={e => e.stopPropagation()} + size="small" + uniqueIdentifier={value.plugin_unique_identifier} + /> + )} + {showPluginNotInstalledWarn + ? ( + <NotFoundWarn + title={t('workflow.nodes.agent.pluginNotInstalled')} + description={t('workflow.nodes.agent.pluginNotInstalledDesc')} + /> + ) + : showUnsupportedStrategy + ? ( + <NotFoundWarn + title={t('workflow.nodes.agent.unsupportedStrategy')} + description={t('workflow.nodes.agent.strategyNotFoundDesc')} + /> + ) + : <RiArrowDownSLine className="size-4 text-text-tertiary" />} + {showSwitchVersion && ( + <SwitchPluginVersion + uniqueIdentifier={value.plugin_unique_identifier} + tooltip={( + <ToolTipContent + title={t('workflow.nodes.agent.unsupportedStrategy')} + > + {t('workflow.nodes.agent.strategyNotFoundDescAndSwitchVersion')} + </ToolTipContent> + )} + onChange={() => { + refetchStrategyInfo() + }} + /> + )} + </div> </div> - </div> - </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-10'> - <div className='w-[388px] overflow-hidden rounded-md border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow'> - <header className='flex gap-1 p-2'> - <SearchInput placeholder={t('workflow.nodes.agent.strategy.searchPlaceholder')} value={query} onChange={setQuery} className={'w-full'} /> - <ViewTypeSelect viewType={viewType} onChange={setViewType} /> - </header> - <main className="relative flex w-full flex-col overflow-hidden md:max-h-[300px] xl:max-h-[400px] 2xl:max-h-[564px]" ref={wrapElemRef}> - <Tools - tools={filteredTools} - viewType={viewType} - onSelect={(_, tool) => { - onChange({ - agent_strategy_name: tool!.tool_name, - agent_strategy_provider_name: tool!.provider_name, - agent_strategy_label: tool!.tool_label, - agent_output_schema: tool!.output_schema || {}, - plugin_unique_identifier: tool!.provider_id, - meta: tool!.meta, - }) - setOpen(false) - }} - className='h-full max-h-full max-w-none overflow-y-auto' - indexBarClassName='top-0 xl:top-36' - hasSearchText={false} - canNotSelectMultiple - canChooseMCPTool={canChooseMCPTool} - isAgent - /> - {enable_marketplace && <PluginList - ref={pluginRef} - wrapElemRef={wrapElemRef} - list={notInstalledPlugins} - searchText={query} - tags={DEFAULT_TAGS} - category={PluginCategoryEnum.agent} - disableMaxWidth - />} - </main> - </div> - </PortalToFollowElemContent> - </PortalToFollowElem> + </PortalToFollowElemTrigger> + <PortalToFollowElemContent className="z-10"> + <div className="w-[388px] overflow-hidden rounded-md border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow"> + <header className="flex gap-1 p-2"> + <SearchInput placeholder={t('workflow.nodes.agent.strategy.searchPlaceholder')} value={query} onChange={setQuery} className="w-full" /> + <ViewTypeSelect viewType={viewType} onChange={setViewType} /> + </header> + <main className="relative flex w-full flex-col overflow-hidden md:max-h-[300px] xl:max-h-[400px] 2xl:max-h-[564px]" ref={wrapElemRef}> + <Tools + tools={filteredTools} + viewType={viewType} + onSelect={(_, tool) => { + onChange({ + agent_strategy_name: tool!.tool_name, + agent_strategy_provider_name: tool!.provider_name, + agent_strategy_label: tool!.tool_label, + agent_output_schema: tool!.output_schema || {}, + plugin_unique_identifier: tool!.provider_id, + meta: tool!.meta, + }) + setOpen(false) + }} + className="h-full max-h-full max-w-none overflow-y-auto" + indexBarClassName="top-0 xl:top-36" + hasSearchText={false} + canNotSelectMultiple + canChooseMCPTool={canChooseMCPTool} + isAgent + /> + {enable_marketplace && ( + <PluginList + ref={pluginRef} + wrapElemRef={wrapElemRef} + list={notInstalledPlugins} + searchText={query} + tags={DEFAULT_TAGS} + category={PluginCategoryEnum.agent} + disableMaxWidth + /> + )} + </main> + </div> + </PortalToFollowElemContent> + </PortalToFollowElem> + ) }) AgentStrategySelector.displayName = 'AgentStrategySelector' 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 c207c82037..d7592b3c6f 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx @@ -1,28 +1,29 @@ -import type { CredentialFormSchemaNumberInput, CredentialFormSchemaTextInput } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { type CredentialFormSchema, FormTypeEnum, ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import type { ToolVarInputs } from '../../tool/types' -import ListEmpty from '@/app/components/base/list-empty' -import { AgentStrategySelector } from './agent-strategy-selector' -import Link from 'next/link' -import { useTranslation } from 'react-i18next' -import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form' -import { Agent } from '@/app/components/base/icons/src/vender/workflow' -import { InputNumber } from '@/app/components/base/input-number' -import Slider from '@/app/components/base/slider' -import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector' -import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-tool-selector' -import Field from './field' -import { type ComponentProps, memo } from 'react' -import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' -import Editor from './prompt/editor' -import { useWorkflowStore } from '../../../store' -import { useRenderI18nObject } from '@/hooks/use-i18n' -import type { NodeOutPutVar } from '../../../types' +import type { ComponentProps } from 'react' import type { Node } from 'reactflow' +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 'lodash-es' +import Link from 'next/link' +import { memo } from 'react' +import { useTranslation } from 'react-i18next' +import { Agent } from '@/app/components/base/icons/src/vender/workflow' +import { InputNumber } from '@/app/components/base/input-number' +import ListEmpty from '@/app/components/base/list-empty' +import Slider from '@/app/components/base/slider' +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' +import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-tool-selector' +import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector' import { useDocLink } from '@/context/i18n' +import { useRenderI18nObject } from '@/hooks/use-i18n' import { AppModeEnum } from '@/types/app' +import { useWorkflowStore } from '../../../store' +import { AgentStrategySelector } from './agent-strategy-selector' +import Field from './field' +import Editor from './prompt/editor' export type Strategy = { agent_strategy_provider_name: string @@ -39,8 +40,8 @@ export type AgentStrategyProps = { formSchema: CredentialFormSchema[] formValue: ToolVarInputs onFormValueChange: (value: ToolVarInputs) => void - nodeOutputVars?: NodeOutPutVar[], - availableNodes?: Node[], + nodeOutputVars?: NodeOutPutVar[] + availableNodes?: Node[] nodeId?: string canChooseMCPTool: boolean } @@ -78,38 +79,41 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => { onChange(value) setControlPromptEditorRerenderKey(Math.random()) } - return <Editor - value={value} - onChange={onChange} - onGenerated={handleGenerated} - instanceId={instanceId} - key={instanceId} - title={renderI18nObject(schema.label)} - headerClassName='bg-transparent px-0 text-text-secondary system-sm-semibold-uppercase' - containerBackgroundClassName='bg-transparent' - gradientBorder={false} - nodeId={nodeId} - isSupportPromptGenerator={!!def.auto_generate?.type} - titleTooltip={schema.tooltip && renderI18nObject(schema.tooltip)} - editorContainerClassName='px-0 bg-components-input-bg-normal focus-within:bg-components-input-bg-active rounded-lg' - availableNodes={availableNodes} - nodesOutputVars={nodeOutputVars} - isSupportJinja={def.template?.enabled} - required={def.required} - varList={[]} - modelConfig={ - defaultModel.data - ? { - mode: AppModeEnum.CHAT, - name: defaultModel.data.model, - provider: defaultModel.data.provider.provider, - completion_params: {}, - } : undefined - } - placeholderClassName='px-2 py-1' - titleClassName='system-sm-semibold-uppercase text-text-secondary text-[13px]' - inputClassName='px-2 py-1' - /> + return ( + <Editor + value={value} + onChange={onChange} + onGenerated={handleGenerated} + instanceId={instanceId} + key={instanceId} + title={renderI18nObject(schema.label)} + headerClassName="bg-transparent px-0 text-text-secondary system-sm-semibold-uppercase" + containerBackgroundClassName="bg-transparent" + gradientBorder={false} + nodeId={nodeId} + isSupportPromptGenerator={!!def.auto_generate?.type} + titleTooltip={schema.tooltip && renderI18nObject(schema.tooltip)} + editorContainerClassName="px-0 bg-components-input-bg-normal focus-within:bg-components-input-bg-active rounded-lg" + availableNodes={availableNodes} + nodesOutputVars={nodeOutputVars} + isSupportJinja={def.template?.enabled} + required={def.required} + varList={[]} + modelConfig={ + defaultModel.data + ? { + mode: AppModeEnum.CHAT, + name: defaultModel.data.model, + provider: defaultModel.data.provider.provider, + completion_params: {}, + } + : undefined + } + placeholderClassName="px-2 py-1" + titleClassName="system-sm-semibold-uppercase text-text-secondary text-[13px]" + inputClassName="px-2 py-1" + /> + ) } case FormTypeEnum.textNumber: { const def = schema as CredentialFormSchemaNumberInput @@ -121,34 +125,40 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => { const onChange = (value: number) => { props.onChange({ ...props.value, [schema.variable]: value }) } - return <Field - title={<> - {renderI18nObject(def.label)} {def.required && <span className='text-red-500'>*</span>} - </>} - key={def.variable} - tooltip={def.tooltip && renderI18nObject(def.tooltip)} - inline - > - <div className='flex w-[200px] items-center gap-3'> - <Slider - value={value} - onChange={onChange} - className='w-full' - min={def.min} - max={def.max} - /> - <InputNumber - value={value} - // TODO: maybe empty, handle this - onChange={onChange as any} - defaultValue={defaultValue} - size='regular' - min={def.min} - max={def.max} - className='w-12' - /> - </div> - </Field> + return ( + <Field + title={( + <> + {renderI18nObject(def.label)} + {' '} + {def.required && <span className="text-red-500">*</span>} + </> + )} + key={def.variable} + tooltip={def.tooltip && renderI18nObject(def.tooltip)} + inline + > + <div className="flex w-[200px] items-center gap-3"> + <Slider + value={value} + onChange={onChange} + className="w-full" + min={def.min} + max={def.max} + /> + <InputNumber + value={value} + // TODO: maybe empty, handle this + onChange={onChange as any} + defaultValue={defaultValue} + size="regular" + min={def.min} + max={def.max} + className="w-12" + /> + </div> + </Field> + ) } } }, @@ -162,9 +172,13 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => { } return ( <Field - title={<> - {renderI18nObject(schema.label)} {schema.required && <span className='text-red-500'>*</span>} - </>} + title={( + <> + {renderI18nObject(schema.label)} + {' '} + {schema.required && <span className="text-red-500">*</span>} + </> + )} tooltip={schema.tooltip && renderI18nObject(schema.tooltip)} > <ToolSelector @@ -204,46 +218,59 @@ export const AgentStrategy = memo((props: AgentStrategyProps) => { } } } - return <div className='space-y-2'> - <AgentStrategySelector value={strategy} onChange={onStrategyChange} canChooseMCPTool={canChooseMCPTool} /> - { - strategy - ? <div> - <Form<CustomField> - formSchemas={[ - ...formSchema, - ]} - value={formValue} - onChange={onFormValueChange} - validating={false} - showOnVariableMap={{}} - isEditMode={true} - isAgentStrategy={true} - fieldLabelClassName='uppercase' - customRenderField={renderField} - override={override} - nodeId={nodeId} - nodeOutputVars={nodeOutputVars || []} - availableNodes={availableNodes || []} - canChooseMCPTool={canChooseMCPTool} - /> - </div> - : <ListEmpty - icon={<Agent className='h-5 w-5 shrink-0 text-text-accent' />} - title={t('workflow.nodes.agent.strategy.configureTip')} - description={<div className='text-xs text-text-tertiary'> - {t('workflow.nodes.agent.strategy.configureTipDesc')} <br /> - <Link href={docLink('/guides/workflow/node/agent#select-an-agent-strategy', { - 'zh-Hans': '/guides/workflow/node/agent#选择-agent-策略', - 'ja-JP': '/guides/workflow/node/agent#エージェント戦略の選択', - })} - className='text-text-accent-secondary' target='_blank'> - {t('workflow.nodes.agent.learnMore')} - </Link> - </div>} - /> - } - </div> + return ( + <div className="space-y-2"> + <AgentStrategySelector value={strategy} onChange={onStrategyChange} canChooseMCPTool={canChooseMCPTool} /> + { + strategy + ? ( + <div> + <Form<CustomField> + formSchemas={[ + ...formSchema, + ]} + value={formValue} + onChange={onFormValueChange} + validating={false} + showOnVariableMap={{}} + isEditMode={true} + isAgentStrategy={true} + fieldLabelClassName="uppercase" + customRenderField={renderField} + override={override} + nodeId={nodeId} + nodeOutputVars={nodeOutputVars || []} + availableNodes={availableNodes || []} + canChooseMCPTool={canChooseMCPTool} + /> + </div> + ) + : ( + <ListEmpty + icon={<Agent className="h-5 w-5 shrink-0 text-text-accent" />} + title={t('workflow.nodes.agent.strategy.configureTip')} + description={( + <div className="text-xs text-text-tertiary"> + {t('workflow.nodes.agent.strategy.configureTipDesc')} + {' '} + <br /> + <Link + href={docLink('/guides/workflow/node/agent#select-an-agent-strategy', { + 'zh-Hans': '/guides/workflow/node/agent#选择-agent-策略', + 'ja-JP': '/guides/workflow/node/agent#エージェント戦略の選択', + })} + className="text-text-accent-secondary" + target="_blank" + > + {t('workflow.nodes.agent.learnMore')} + </Link> + </div> + )} + /> + ) + } + </div> + ) }) AgentStrategy.displayName = 'AgentStrategy' diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/bool-input.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/bool-input.tsx index 73219a551b..db32627dc2 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/bool-input.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/bool-input.tsx @@ -1,8 +1,8 @@ 'use client' -import Checkbox from '@/app/components/base/checkbox' import type { FC } from 'react' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' +import Checkbox from '@/app/components/base/checkbox' type Props = { name: string @@ -22,15 +22,15 @@ const BoolInput: FC<Props> = ({ onChange(!value) }, [value, onChange]) return ( - <div className='flex h-6 items-center gap-2'> + <div className="flex h-6 items-center gap-2"> <Checkbox - className='!h-4 !w-4' + className="!h-4 !w-4" checked={!!value} onCheck={handleChange} /> - <div className='system-sm-medium flex items-center gap-1 text-text-secondary'> + <div className="system-sm-medium flex items-center gap-1 text-text-secondary"> {name} - {!required && <span className='system-xs-regular text-text-tertiary'>{t('workflow.panel.optional')}</span>} + {!required && <span className="system-xs-regular text-text-tertiary">{t('workflow.panel.optional')}</span>} </div> </div> ) diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx index 440cb1e338..c33deae438 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx @@ -1,31 +1,31 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useMemo } from 'react' -import { useTranslation } from 'react-i18next' -import { produce } from 'immer' +import type { InputVar } from '../../../../types' +import type { FileEntity } from '@/app/components/base/file-uploader/types' import { RiDeleteBinLine, } from '@remixicon/react' -import type { InputVar } from '../../../../types' -import { BlockEnum, InputVarType, SupportUploadFileTypes } from '../../../../types' -import CodeEditor from '../editor/code-editor' -import { CodeLanguage } from '../../../code/types' -import TextEditor from '../editor/text-editor' -import Select from '@/app/components/base/select' -import Input from '@/app/components/base/input' -import Textarea from '@/app/components/base/textarea' -import TextGenerationImageUploader from '@/app/components/base/image-uploader/text-generation-image-uploader' +import { produce } from 'immer' +import React, { useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader' -import { Resolution, TransferMethod } from '@/types/app' -import { VarBlockIcon } from '@/app/components/workflow/block-icon' import { Line3 } from '@/app/components/base/icons/src/public/common' -import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { BubbleX } from '@/app/components/base/icons/src/vender/line/others' +import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' +import TextGenerationImageUploader from '@/app/components/base/image-uploader/text-generation-image-uploader' +import Input from '@/app/components/base/input' import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' -import { cn } from '@/utils/classnames' -import type { FileEntity } from '@/app/components/base/file-uploader/types' -import BoolInput from './bool-input' +import Select from '@/app/components/base/select' +import Textarea from '@/app/components/base/textarea' +import { VarBlockIcon } from '@/app/components/workflow/block-icon' import { useHooksStore } from '@/app/components/workflow/hooks-store' +import { Resolution, TransferMethod } from '@/types/app' +import { cn } from '@/utils/classnames' +import { BlockEnum, InputVarType, SupportUploadFileTypes } from '../../../../types' +import { CodeLanguage } from '../../../code/types' +import CodeEditor from '../editor/code-editor' +import TextEditor from '../editor/text-editor' +import BoolInput from './bool-input' type Props = { payload: InputVar @@ -69,22 +69,22 @@ const FormItem: FC<Props> = ({ if (typeof payload.label === 'object') { const { nodeType, nodeName, variable, isChatVar } = payload.label return ( - <div className='flex h-full items-center'> + <div className="flex h-full items-center"> {!isChatVar && ( - <div className='flex items-center'> - <div className='p-[1px]'> + <div className="flex items-center"> + <div className="p-[1px]"> <VarBlockIcon type={nodeType || BlockEnum.Start} /> </div> - <div className='mx-0.5 max-w-[150px] truncate text-xs font-medium text-gray-700' title={nodeName}> + <div className="mx-0.5 max-w-[150px] truncate text-xs font-medium text-gray-700" title={nodeName}> {nodeName} </div> - <Line3 className='mr-0.5'></Line3> + <Line3 className="mr-0.5"></Line3> </div> )} - <div className='flex items-center text-primary-600'> - {!isChatVar && <Variable02 className='h-3.5 w-3.5' />} - {isChatVar && <BubbleX className='h-3.5 w-3.5 text-util-colors-teal-teal-700' />} - <div className={cn('ml-0.5 max-w-[150px] truncate text-xs font-medium', isChatVar && 'text-text-secondary')} title={variable} > + <div className="flex items-center text-primary-600"> + {!isChatVar && <Variable02 className="h-3.5 w-3.5" />} + {isChatVar && <BubbleX className="h-3.5 w-3.5 text-util-colors-teal-teal-700" />} + <div className={cn('ml-0.5 max-w-[150px] truncate text-xs font-medium', isChatVar && 'text-text-secondary')} title={variable}> {variable} </div> </div> @@ -117,24 +117,26 @@ const FormItem: FC<Props> = ({ return ( <div className={cn(className)}> {!isArrayLikeType && !isBooleanType && ( - <div className='system-sm-semibold mb-1 flex h-6 items-center gap-1 text-text-secondary'> - <div className='truncate'> + <div className="system-sm-semibold mb-1 flex h-6 items-center gap-1 text-text-secondary"> + <div className="truncate"> {typeof payload.label === 'object' ? nodeKey : payload.label} </div> - {payload.hide === true ? ( - <span className='system-xs-regular text-text-tertiary'> - {t('workflow.panel.optional_and_hidden')} - </span> - ) : ( - !payload.required && ( - <span className='system-xs-regular text-text-tertiary'> - {t('workflow.panel.optional')} - </span> - ) - )} + {payload.hide === true + ? ( + <span className="system-xs-regular text-text-tertiary"> + {t('workflow.panel.optional_and_hidden')} + </span> + ) + : ( + !payload.required && ( + <span className="system-xs-regular text-text-tertiary"> + {t('workflow.panel.optional')} + </span> + ) + )} </div> )} - <div className='grow'> + <div className="grow"> { type === InputVarType.textInput && ( <Input @@ -206,9 +208,9 @@ const FormItem: FC<Props> = ({ language={CodeLanguage.json} onChange={onChange} noWrapper - className='bg h-[80px] overflow-y-auto rounded-[10px] bg-components-input-bg-normal p-1' + className="bg h-[80px] overflow-y-auto rounded-[10px] bg-components-input-bg-normal p-1" placeholder={ - <div className='whitespace-pre'>{payload.json_schema}</div> + <div className="whitespace-pre">{payload.json_schema}</div> } /> )} @@ -219,19 +221,19 @@ const FormItem: FC<Props> = ({ fileConfig={{ allowed_file_types: inStepRun && (!payload.allowed_file_types || payload.allowed_file_types.length === 0) ? [ - SupportUploadFileTypes.image, - SupportUploadFileTypes.document, - SupportUploadFileTypes.audio, - SupportUploadFileTypes.video, - ] + SupportUploadFileTypes.image, + SupportUploadFileTypes.document, + SupportUploadFileTypes.audio, + SupportUploadFileTypes.video, + ] : payload.allowed_file_types, allowed_file_extensions: inStepRun && (!payload.allowed_file_extensions || payload.allowed_file_extensions.length === 0) ? [ - ...FILE_EXTS[SupportUploadFileTypes.image], - ...FILE_EXTS[SupportUploadFileTypes.document], - ...FILE_EXTS[SupportUploadFileTypes.audio], - ...FILE_EXTS[SupportUploadFileTypes.video], - ] + ...FILE_EXTS[SupportUploadFileTypes.image], + ...FILE_EXTS[SupportUploadFileTypes.document], + ...FILE_EXTS[SupportUploadFileTypes.audio], + ...FILE_EXTS[SupportUploadFileTypes.video], + ] : payload.allowed_file_extensions, allowed_file_upload_methods: inStepRun ? [TransferMethod.local_file, TransferMethod.remote_url] : payload.allowed_file_upload_methods, number_limits: 1, @@ -246,19 +248,19 @@ const FormItem: FC<Props> = ({ fileConfig={{ allowed_file_types: (inStepRun || isIteratorItemFile) && (!payload.allowed_file_types || payload.allowed_file_types.length === 0) ? [ - SupportUploadFileTypes.image, - SupportUploadFileTypes.document, - SupportUploadFileTypes.audio, - SupportUploadFileTypes.video, - ] + SupportUploadFileTypes.image, + SupportUploadFileTypes.document, + SupportUploadFileTypes.audio, + SupportUploadFileTypes.video, + ] : payload.allowed_file_types, allowed_file_extensions: (inStepRun || isIteratorItemFile) && (!payload.allowed_file_extensions || payload.allowed_file_extensions.length === 0) ? [ - ...FILE_EXTS[SupportUploadFileTypes.image], - ...FILE_EXTS[SupportUploadFileTypes.document], - ...FILE_EXTS[SupportUploadFileTypes.audio], - ...FILE_EXTS[SupportUploadFileTypes.video], - ] + ...FILE_EXTS[SupportUploadFileTypes.image], + ...FILE_EXTS[SupportUploadFileTypes.document], + ...FILE_EXTS[SupportUploadFileTypes.audio], + ...FILE_EXTS[SupportUploadFileTypes.video], + ] : payload.allowed_file_extensions, allowed_file_upload_methods: (inStepRun || isIteratorItemFile) ? [TransferMethod.local_file, TransferMethod.remote_url] : payload.allowed_file_upload_methods, number_limits: (inStepRun || isIteratorItemFile) ? 5 : payload.max_length, @@ -286,7 +288,7 @@ const FormItem: FC<Props> = ({ { isContext && ( - <div className='space-y-2'> + <div className="space-y-2"> {(value || []).map((item: any, index: number) => ( <CodeEditor key={index} @@ -294,10 +296,12 @@ const FormItem: FC<Props> = ({ title={<span>JSON</span>} headerRight={ (value as any).length > 1 - ? (<RiDeleteBinLine - onClick={handleArrayItemRemove(index)} - className='mr-1 h-3.5 w-3.5 cursor-pointer text-text-tertiary' - />) + ? ( + <RiDeleteBinLine + onClick={handleArrayItemRemove(index)} + className="mr-1 h-3.5 w-3.5 cursor-pointer text-text-tertiary" + /> + ) : undefined } language={CodeLanguage.json} @@ -310,20 +314,29 @@ const FormItem: FC<Props> = ({ { (isIterator && !isIteratorItemFile) && ( - <div className='space-y-2'> + <div className="space-y-2"> {(value || []).map((item: any, index: number) => ( <TextEditor key={index} isInNode value={item} - title={<span>{t('appDebug.variableConfig.content')} {index + 1} </span>} + title={( + <span> + {t('appDebug.variableConfig.content')} + {' '} + {index + 1} + {' '} + </span> + )} onChange={handleArrayItemChange(index)} headerRight={ (value as any).length > 1 - ? (<RiDeleteBinLine - onClick={handleArrayItemRemove(index)} - className='mr-1 h-3.5 w-3.5 cursor-pointer text-text-tertiary' - />) + ? ( + <RiDeleteBinLine + onClick={handleArrayItemRemove(index)} + className="mr-1 h-3.5 w-3.5 cursor-pointer text-text-tertiary" + /> + ) : undefined } /> diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx index 69873d8be2..e45f001924 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx @@ -1,13 +1,13 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useEffect, useMemo, useRef } from 'react' -import { produce } from 'immer' import type { InputVar } from '../../../../types' -import FormItem from './form-item' -import { cn } from '@/utils/classnames' -import { InputVarType } from '@/app/components/workflow/types' +import { produce } from 'immer' +import React, { useCallback, useEffect, useMemo, useRef } from 'react' import AddButton from '@/app/components/base/button/add-button' import { RETRIEVAL_OUTPUT_STRUCT } from '@/app/components/workflow/constants' +import { InputVarType } from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' +import FormItem from './form-item' export type Props = { className?: string @@ -77,8 +77,8 @@ const Form: FC<Props> = ({ return ( <div className={cn(className, 'space-y-2')}> {label && ( - <div className='mb-1 flex items-center justify-between'> - <div className='system-xs-medium-uppercase flex h-6 items-center text-text-tertiary'>{label}</div> + <div className="mb-1 flex items-center justify-between"> + <div className="system-xs-medium-uppercase flex h-6 items-center text-text-tertiary">{label}</div> {isArrayLikeType && !isIteratorItemFile && ( <AddButton onClick={handleAddContext} /> )} 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 abde5bb7e8..9957d79cf6 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 @@ -1,20 +1,21 @@ 'use client' import type { FC } from 'react' -import React, { useEffect, useRef } from 'react' -import { useTranslation } from 'react-i18next' import type { Props as FormProps } from './form' -import Form from './form' -import { cn } from '@/utils/classnames' -import Button from '@/app/components/base/button' -import Split from '@/app/components/workflow/nodes/_base/components/split' -import { InputVarType } from '@/app/components/workflow/types' -import Toast from '@/app/components/base/toast' -import { TransferMethod } from '@/types/app' -import { getProcessedFiles } from '@/app/components/base/file-uploader/utils' -import type { BlockEnum, NodeRunningStatus } from '@/app/components/workflow/types' import type { Emoji } from '@/app/components/tools/types' import type { SpecialResultPanelProps } from '@/app/components/workflow/run/special-result-panel' +import type { BlockEnum, NodeRunningStatus } from '@/app/components/workflow/types' +import React, { useEffect, useRef } from 'react' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' +import { getProcessedFiles } from '@/app/components/base/file-uploader/utils' +import Toast from '@/app/components/base/toast' +import Split from '@/app/components/workflow/nodes/_base/components/split' +import { InputVarType } from '@/app/components/workflow/types' +import { TransferMethod } from '@/types/app' +import { cn } from '@/utils/classnames' +import Form from './form' import PanelWrap from './panel-wrap' + const i18nPrefix = 'workflow.singleRun' export type BeforeRunFormProps = { @@ -32,9 +33,9 @@ export type BeforeRunFormProps = { } & Partial<SpecialResultPanelProps> function formatValue(value: string | any, type: InputVarType) { - if(type === InputVarType.checkbox) + if (type === InputVarType.checkbox) return !!value - if(value === undefined || value === null) + if (value === undefined || value === null) return value if (type === InputVarType.number) return Number.parseFloat(value) @@ -138,14 +139,14 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({ const hasRun = useRef(false) useEffect(() => { // React 18 run twice in dev mode - if(hasRun.current) + if (hasRun.current) return hasRun.current = true - if(filteredExistVarForms.length === 0) + if (filteredExistVarForms.length === 0) onRun({}) }, [filteredExistVarForms, onRun]) - if(filteredExistVarForms.length === 0) + if (filteredExistVarForms.length === 0) return null return ( @@ -153,8 +154,8 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({ nodeName={nodeName} onHide={onHide} > - <div className='h-0 grow overflow-y-auto pb-4'> - <div className='mt-3 space-y-4 px-4'> + <div className="h-0 grow overflow-y-auto pb-4"> + <div className="mt-3 space-y-4 px-4"> {filteredExistVarForms.map((form, index) => ( <div key={index}> <Form @@ -166,8 +167,8 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({ </div> ))} </div> - <div className='mt-4 flex justify-between space-x-2 px-4' > - <Button disabled={!isFileLoaded} variant='primary' className='w-0 grow space-x-2' onClick={handleRun}> + <div className="mt-4 flex justify-between space-x-2 px-4"> + <Button disabled={!isFileLoaded} variant="primary" className="w-0 grow space-x-2" onClick={handleRun}> <div>{t(`${i18nPrefix}.startRun`)}</div> </Button> </div> diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/panel-wrap.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/panel-wrap.tsx index 7312adf6c6..61e614bb9e 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/panel-wrap.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/panel-wrap.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' -import React from 'react' -import { useTranslation } from 'react-i18next' import { RiCloseLine, } from '@remixicon/react' +import React from 'react' +import { useTranslation } from 'react-i18next' const i18nPrefix = 'workflow.singleRun' @@ -21,16 +21,21 @@ const PanelWrap: FC<Props> = ({ }) => { const { t } = useTranslation() return ( - <div className='absolute inset-0 z-10 rounded-2xl bg-background-overlay-alt'> - <div className='flex h-full flex-col rounded-2xl bg-components-panel-bg'> - <div className='flex h-8 shrink-0 items-center justify-between pl-4 pr-3 pt-3'> - <div className='truncate text-base font-semibold text-text-primary'> - {t(`${i18nPrefix}.testRun`)} {nodeName} + <div className="absolute inset-0 z-10 rounded-2xl bg-background-overlay-alt"> + <div className="flex h-full flex-col rounded-2xl bg-components-panel-bg"> + <div className="flex h-8 shrink-0 items-center justify-between pl-4 pr-3 pt-3"> + <div className="truncate text-base font-semibold text-text-primary"> + {t(`${i18nPrefix}.testRun`)} + {' '} + {nodeName} </div> - <div className='ml-2 shrink-0 cursor-pointer p-1' onClick={() => { - onHide() - }}> - <RiCloseLine className='h-4 w-4 text-text-tertiary ' /> + <div + className="ml-2 shrink-0 cursor-pointer p-1" + onClick={() => { + onHide() + }} + > + <RiCloseLine className="h-4 w-4 text-text-tertiary " /> </div> </div> {children} diff --git a/web/app/components/workflow/nodes/_base/components/code-generator-button.tsx b/web/app/components/workflow/nodes/_base/components/code-generator-button.tsx index 58dec6baba..6888bff96c 100644 --- a/web/app/components/workflow/nodes/_base/components/code-generator-button.tsx +++ b/web/app/components/workflow/nodes/_base/components/code-generator-button.tsx @@ -1,14 +1,14 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' -import { useBoolean } from 'ahooks' -import { cn } from '@/utils/classnames' import type { CodeLanguage } from '../../code/types' -import { Generator } from '@/app/components/base/icons/src/vender/other' -import { ActionButton } from '@/app/components/base/action-button' -import { AppModeEnum } from '@/types/app' import type { GenRes } from '@/service/debug' +import { useBoolean } from 'ahooks' +import React, { useCallback } from 'react' import { GetCodeGeneratorResModal } from '@/app/components/app/configuration/config/code-generator/get-code-generator-res' +import { ActionButton } from '@/app/components/base/action-button' +import { Generator } from '@/app/components/base/icons/src/vender/other' +import { AppModeEnum } from '@/types/app' +import { cn } from '@/utils/classnames' import { useHooksStore } from '../../../hooks-store' type Props = { @@ -36,9 +36,10 @@ const CodeGenerateBtn: FC<Props> = ({ return ( <div className={cn(className)}> <ActionButton - className='hover:bg-[#155EFF]/8' - onClick={showAutomaticTrue}> - <Generator className='h-4 w-4 text-primary-600' /> + className="hover:bg-[#155EFF]/8" + onClick={showAutomaticTrue} + > + <Generator className="h-4 w-4 text-primary-600" /> </ActionButton> {showAutomatic && ( <GetCodeGeneratorResModal diff --git a/web/app/components/workflow/nodes/_base/components/collapse/field-collapse.tsx b/web/app/components/workflow/nodes/_base/components/collapse/field-collapse.tsx index 2390dfd74e..777bd82035 100644 --- a/web/app/components/workflow/nodes/_base/components/collapse/field-collapse.tsx +++ b/web/app/components/workflow/nodes/_base/components/collapse/field-collapse.tsx @@ -16,16 +16,16 @@ const FieldCollapse = ({ operations, }: FieldCollapseProps) => { return ( - <div className='py-4'> + <div className="py-4"> <Collapse trigger={ - <div className='system-sm-semibold-uppercase flex h-6 cursor-pointer items-center text-text-secondary'>{title}</div> + <div className="system-sm-semibold-uppercase flex h-6 cursor-pointer items-center text-text-secondary">{title}</div> } operations={operations} collapsed={collapsed} onCollapse={onCollapse} > - <div className='px-4'> + <div className="px-4"> {children} </div> </Collapse> diff --git a/web/app/components/workflow/nodes/_base/components/collapse/index.tsx b/web/app/components/workflow/nodes/_base/components/collapse/index.tsx index f7cf95ce7e..6aeff0242f 100644 --- a/web/app/components/workflow/nodes/_base/components/collapse/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/collapse/index.tsx @@ -40,9 +40,9 @@ const Collapse = ({ }, [collapsedMerged, disabled]) return ( <> - <div className='group/collapse flex items-center'> + <div className="group/collapse flex items-center"> <div - className='ml-4 flex grow items-center' + className="ml-4 flex grow items-center" onClick={() => { if (!disabled) { setCollapsedLocal(!collapsedMerged) @@ -52,7 +52,7 @@ const Collapse = ({ > {typeof trigger === 'function' ? trigger(collapseIcon) : trigger} {!hideCollapseIcon && ( - <div className='h-4 w-4 shrink-0'> + <div className="h-4 w-4 shrink-0"> {collapseIcon} </div> )} 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 0132ce147e..3c2cc217a7 100644 --- a/web/app/components/workflow/nodes/_base/components/config-vision.tsx +++ b/web/app/components/workflow/nodes/_base/components/config-vision.tsx @@ -1,15 +1,17 @@ 'use client' import type { FC } from 'react' +import type { ValueSelector, Var, VisionSetting } from '@/app/components/workflow/types' +import { produce } from 'immer' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { produce } from 'immer' -import VarReferencePicker from './variable/var-reference-picker' -import ResolutionPicker from '@/app/components/workflow/nodes/llm/components/resolution-picker' -import Field from '@/app/components/workflow/nodes/_base/components/field' import Switch from '@/app/components/base/switch' -import { type ValueSelector, type Var, VarType, type VisionSetting } from '@/app/components/workflow/types' -import { Resolution } from '@/types/app' 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' +import { VarType } from '@/app/components/workflow/types' +import { Resolution } from '@/types/app' +import VarReferencePicker from './variable/var-reference-picker' + const i18nPrefix = 'workflow.nodes.llm' type Props = { @@ -57,32 +59,32 @@ const ConfigVision: FC<Props> = ({ <Field title={t(`${i18nPrefix}.vision`)} tooltip={t('appDebug.vision.description')!} - operations={ + operations={( <Tooltip popupContent={t('appDebug.vision.onlySupportVisionModelTip')!} disabled={isVisionModel} > - <Switch disabled={readOnly || !isVisionModel} size='md' defaultValue={!isVisionModel ? false : enabled} onChange={onEnabledChange} /> + <Switch disabled={readOnly || !isVisionModel} size="md" defaultValue={!isVisionModel ? false : enabled} onChange={onEnabledChange} /> </Tooltip> - } + )} > {(enabled && isVisionModel) ? ( - <div> - <VarReferencePicker - className='mb-4' - filterVar={filterVar} - nodeId={nodeId} - value={config.variable_selector || []} - onChange={handleVarSelectorChange} - readonly={readOnly} - /> - <ResolutionPicker - value={config.detail} - onChange={handleVisionResolutionChange} - /> - </div> - ) + <div> + <VarReferencePicker + className="mb-4" + filterVar={filterVar} + nodeId={nodeId} + value={config.variable_selector || []} + onChange={handleVarSelectorChange} + readonly={readOnly} + /> + <ResolutionPicker + value={config.detail} + onChange={handleVisionResolutionChange} + /> + </div> + ) : null} </Field> diff --git a/web/app/components/workflow/nodes/_base/components/editor/base.tsx b/web/app/components/workflow/nodes/_base/components/editor/base.tsx index 0b88f8c67d..95aabc0ec0 100644 --- a/web/app/components/workflow/nodes/_base/components/editor/base.tsx +++ b/web/app/components/workflow/nodes/_base/components/editor/base.tsx @@ -1,22 +1,22 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useRef, useState } from 'react' -import copy from 'copy-to-clipboard' -import ToggleExpandBtn from '../toggle-expand-btn' -import CodeGeneratorButton from '../code-generator-button' import type { CodeLanguage } from '../../../code/types' -import Wrap from './wrap' -import { cn } from '@/utils/classnames' +import type { FileEntity } from '@/app/components/base/file-uploader/types' +import type { Node, NodeOutPutVar } from '@/app/components/workflow/types' +import copy from 'copy-to-clipboard' +import React, { useCallback, useRef, useState } from 'react' import PromptEditorHeightResizeWrap from '@/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap' +import ActionButton from '@/app/components/base/action-button' +import FileListInLog from '@/app/components/base/file-uploader/file-list-in-log' import { Copy, CopyCheck, } from '@/app/components/base/icons/src/vender/line/files' import useToggleExpend from '@/app/components/workflow/nodes/_base/hooks/use-toggle-expend' -import type { FileEntity } from '@/app/components/base/file-uploader/types' -import FileListInLog from '@/app/components/base/file-uploader/file-list-in-log' -import ActionButton from '@/app/components/base/action-button' -import type { Node, NodeOutPutVar } from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' +import CodeGeneratorButton from '../code-generator-button' +import ToggleExpandBtn from '../toggle-expand-btn' +import Wrap from './wrap' type Props = { nodeId?: string @@ -84,15 +84,18 @@ const Base: FC<Props> = ({ return ( <Wrap className={cn(wrapClassName)} style={wrapStyle} isInNode={isInNode} isExpand={isExpand}> <div ref={ref} className={cn(className, isExpand && 'h-full', 'rounded-lg border', !isFocus ? 'border-transparent bg-components-input-bg-normal' : 'overflow-hidden border-components-input-border-hover bg-components-input-bg-hover')}> - <div className='flex h-7 items-center justify-between pl-3 pr-2 pt-1'> - <div className='system-xs-semibold-uppercase text-text-secondary'>{title}</div> - <div className='flex items-center' onClick={(e) => { - e.nativeEvent.stopImmediatePropagation() - e.stopPropagation() - }}> + <div className="flex h-7 items-center justify-between pl-3 pr-2 pt-1"> + <div className="system-xs-semibold-uppercase text-text-secondary">{title}</div> + <div + className="flex items-center" + onClick={(e) => { + e.nativeEvent.stopImmediatePropagation() + e.stopPropagation() + }} + > {headerRight} {showCodeGenerator && codeLanguages && ( - <div className='ml-1'> + <div className="ml-1"> <CodeGeneratorButton onGenerated={onGenerated} codeLanguages={codeLanguages} @@ -101,29 +104,28 @@ const Base: FC<Props> = ({ /> </div> )} - <ActionButton className='ml-1' onClick={handleCopy}> + <ActionButton className="ml-1" onClick={handleCopy}> {!isCopied ? ( - <Copy className='h-4 w-4 cursor-pointer' /> - ) + <Copy className="h-4 w-4 cursor-pointer" /> + ) : ( - <CopyCheck className='h-4 w-4' /> - ) - } + <CopyCheck className="h-4 w-4" /> + )} </ActionButton> - <div className='ml-1'> + <div className="ml-1"> <ToggleExpandBtn isExpand={isExpand} onExpandChange={setIsExpand} /> </div> </div> </div> - {tip && <div className='px-1 py-0.5'>{tip}</div>} + {tip && <div className="px-1 py-0.5">{tip}</div>} <PromptEditorHeightResizeWrap height={isExpand ? editorExpandHeight : editorContentHeight} minHeight={editorContentMinHeight} onHeightChange={setEditorContentHeight} hideResize={isExpand} > - <div className='h-full pb-2 pl-2'> + <div className="h-full pb-2 pl-2"> {children} </div> </PromptEditorHeightResizeWrap> diff --git a/web/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars.tsx b/web/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars.tsx index 0c6ad12540..d4d43ae796 100644 --- a/web/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars.tsx +++ b/web/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars.tsx @@ -1,13 +1,13 @@ 'use client' import type { FC } from 'react' -import React, { useEffect, useRef, useState } from 'react' -import { useBoolean } from 'ahooks' -import { useTranslation } from 'react-i18next' import type { Props as EditorProps } from '.' -import Editor from '.' -import { cn } from '@/utils/classnames' -import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' import type { NodeOutPutVar, Variable } from '@/app/components/workflow/types' +import { useBoolean } from 'ahooks' +import React, { useEffect, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' +import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' +import { cn } from '@/utils/classnames' +import Editor from '.' const TO_WINDOW_OFFSET = 8 @@ -149,7 +149,7 @@ const CodeEditor: FC<Props> = ({ {isShowVarPicker && ( <div ref={popupRef} - className='w-[228px] space-y-1 rounded-lg border border-components-panel-border bg-components-panel-bg p-1 shadow-lg' + className="w-[228px] space-y-1 rounded-lg border border-components-panel-border bg-components-panel-bg p-1 shadow-lg" style={{ position: 'fixed', top: popupPosition.y, diff --git a/web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx b/web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx index 7ddea94036..b98e9085de 100644 --- a/web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx @@ -1,18 +1,18 @@ 'use client' import type { FC } from 'react' import Editor, { loader } from '@monaco-editor/react' +import { noop } from 'lodash-es' import React, { useEffect, useMemo, useRef, useState } from 'react' -import Base from '../base' -import { cn } from '@/utils/classnames' -import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import { getFilesInLogs, } from '@/app/components/base/file-uploader/utils' -import { Theme } from '@/types/app' +import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import useTheme from '@/hooks/use-theme' -import './style.css' -import { noop } from 'lodash-es' +import { Theme } from '@/types/app' +import { cn } from '@/utils/classnames' import { basePath } from '@/utils/var' +import Base from '../base' +import './style.css' // load file from local instead of cdn https://github.com/suren-atoyan/monaco-react/issues/482 if (typeof window !== 'undefined') @@ -145,7 +145,7 @@ const CodeEditor: FC<Props> = ({ language={languageMap[language] || 'javascript'} theme={isMounted ? theme : 'default-theme'} // sometimes not load the default theme value={outPutValue} - loading={<span className='text-text-primary'>Loading...</span>} + loading={<span className="text-text-primary">Loading...</span>} onChange={handleEditorChange} // https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IEditorOptions.html options={{ @@ -166,40 +166,45 @@ const CodeEditor: FC<Props> = ({ }} onMount={handleEditorDidMount} /> - {!outPutValue && !isFocus && <div className='pointer-events-none absolute left-[36px] top-0 text-[13px] font-normal leading-[18px] text-gray-300'>{placeholder}</div>} + {!outPutValue && !isFocus && <div className="pointer-events-none absolute left-[36px] top-0 text-[13px] font-normal leading-[18px] text-gray-300">{placeholder}</div>} </> ) return ( <div className={cn(isExpand && 'h-full', className)}> {noWrapper - ? <div className='no-wrapper relative' style={{ - height: isExpand ? '100%' : (editorContentHeight) / 2 + CODE_EDITOR_LINE_HEIGHT, // In IDE, the last line can always be in lop line. So there is some blank space in the bottom. - minHeight: CODE_EDITOR_LINE_HEIGHT, - }}> - {main} - </div> + ? ( + <div + className="no-wrapper relative" + style={{ + height: isExpand ? '100%' : (editorContentHeight) / 2 + CODE_EDITOR_LINE_HEIGHT, // In IDE, the last line can always be in lop line. So there is some blank space in the bottom. + minHeight: CODE_EDITOR_LINE_HEIGHT, + }} + > + {main} + </div> + ) : ( - <Base - nodeId={nodeId} - className='relative' - title={title} - value={outPutValue} - headerRight={headerRight} - isFocus={isFocus && !readOnly} - minHeight={minHeight} - isInNode={isInNode} - onGenerated={onGenerated} - codeLanguages={language} - fileList={fileList as any} - showFileList={showFileList} - showCodeGenerator={showCodeGenerator} - tip={tip} - footer={footer} - > - {main} - </Base> - )} + <Base + nodeId={nodeId} + className="relative" + title={title} + value={outPutValue} + headerRight={headerRight} + isFocus={isFocus && !readOnly} + minHeight={minHeight} + isInNode={isInNode} + onGenerated={onGenerated} + codeLanguages={language} + fileList={fileList as any} + showFileList={showFileList} + showCodeGenerator={showCodeGenerator} + tip={tip} + footer={footer} + > + {main} + </Base> + )} </div> ) } diff --git a/web/app/components/workflow/nodes/_base/components/editor/text-editor.tsx b/web/app/components/workflow/nodes/_base/components/editor/text-editor.tsx index 252f69c246..6fa3c2adfd 100644 --- a/web/app/components/workflow/nodes/_base/components/editor/text-editor.tsx +++ b/web/app/components/workflow/nodes/_base/components/editor/text-editor.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' import { useBoolean } from 'ahooks' +import React, { useCallback } from 'react' import Base from './base' type Props = { @@ -52,7 +52,7 @@ const TextEditor: FC<Props> = ({ onChange={e => onChange(e.target.value)} onFocus={setIsFocus} onBlur={handleBlur} - className='h-full w-full resize-none border-none bg-transparent px-3 text-[13px] font-normal leading-[18px] text-gray-900 placeholder:text-gray-300 focus:outline-none' + className="h-full w-full resize-none border-none bg-transparent px-3 text-[13px] font-normal leading-[18px] text-gray-900 placeholder:text-gray-300 focus:outline-none" placeholder={placeholder} readOnly={readonly} /> diff --git a/web/app/components/workflow/nodes/_base/components/error-handle/default-value.tsx b/web/app/components/workflow/nodes/_base/components/error-handle/default-value.tsx index f9292be477..32c715a28a 100644 --- a/web/app/components/workflow/nodes/_base/components/error-handle/default-value.tsx +++ b/web/app/components/workflow/nodes/_base/components/error-handle/default-value.tsx @@ -1,10 +1,10 @@ +import type { DefaultValueForm } from './types' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import type { DefaultValueForm } from './types' import Input from '@/app/components/base/input' -import { VarType } from '@/app/components/workflow/types' -import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' +import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' +import { VarType } from '@/app/components/workflow/types' import { useDocLink } from '@/context/i18n' type DefaultValueProps = { @@ -31,31 +31,31 @@ const DefaultValue = ({ }, [onFormChange]) return ( - <div className='px-4 pt-2'> - <div className='body-xs-regular mb-2 text-text-tertiary'> + <div className="px-4 pt-2"> + <div className="body-xs-regular mb-2 text-text-tertiary"> {t('workflow.nodes.common.errorHandle.defaultValue.desc')}   <a href={docLink('/guides/workflow/error-handling/README', { 'zh-Hans': '/guides/workflow/error-handling/readme', })} - target='_blank' - className='text-text-accent' + target="_blank" + className="text-text-accent" > {t('workflow.common.learnMore')} </a> </div> - <div className='space-y-1'> + <div className="space-y-1"> { forms.map((form, index) => { return ( <div key={index} - className='py-1' + className="py-1" > - <div className='mb-1 flex items-center'> - <div className='system-sm-medium mr-1 text-text-primary'>{form.key}</div> - <div className='system-xs-regular text-text-tertiary'>{form.type}</div> + <div className="mb-1 flex items-center"> + <div className="system-sm-medium mr-1 text-text-primary">{form.key}</div> + <div className="system-xs-regular text-text-tertiary">{form.type}</div> </div> { (form.type === VarType.string || form.type === VarType.number) && ( diff --git a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-node.tsx b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-node.tsx index a786f5adf4..c6d5e01138 100644 --- a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-node.tsx +++ b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-node.tsx @@ -1,11 +1,11 @@ +import type { Node } from '@/app/components/workflow/types' import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import { useUpdateNodeInternals } from 'reactflow' -import { NodeSourceHandle } from '../node-handle' -import { ErrorHandleTypeEnum } from './types' -import type { Node } from '@/app/components/workflow/types' import { NodeRunningStatus } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' +import { NodeSourceHandle } from '../node-handle' +import { ErrorHandleTypeEnum } from './types' type ErrorHandleOnNodeProps = Pick<Node, 'id' | 'data'> const ErrorHandleOnNode = ({ @@ -25,18 +25,20 @@ const ErrorHandleOnNode = ({ return null return ( - <div className='relative px-3 pb-2 pt-1'> + <div className="relative px-3 pb-2 pt-1"> <div className={cn( 'relative flex h-6 items-center justify-between rounded-md bg-workflow-block-parma-bg px-[5px]', data._runningStatus === NodeRunningStatus.Exception && 'border-[0.5px] border-components-badge-status-light-warning-halo bg-state-warning-hover', - )}> - <div className='system-xs-medium-uppercase text-text-tertiary'> + )} + > + <div className="system-xs-medium-uppercase text-text-tertiary"> {t('workflow.common.onFailure')} </div> <div className={cn( 'system-xs-medium text-text-secondary', data._runningStatus === NodeRunningStatus.Exception && 'text-text-warning', - )}> + )} + > { error_strategy === ErrorHandleTypeEnum.defaultValue && ( t('workflow.nodes.common.errorHandle.defaultValue.output') @@ -54,8 +56,8 @@ const ErrorHandleOnNode = ({ id={id} data={data} handleId={ErrorHandleTypeEnum.failBranch} - handleClassName='!top-1/2 !-right-[21px] !-translate-y-1/2 after:!bg-workflow-link-line-failure-button-bg' - nodeSelectorClassName='!bg-workflow-link-line-failure-button-bg' + handleClassName="!top-1/2 !-right-[21px] !-translate-y-1/2 after:!bg-workflow-link-line-failure-button-bg" + nodeSelectorClassName="!bg-workflow-link-line-failure-button-bg" /> ) } diff --git a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx index cfcbae80f3..bfd3097d27 100644 --- a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx +++ b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-on-panel.tsx @@ -1,20 +1,20 @@ -import { useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import Collapse from '../collapse' -import { ErrorHandleTypeEnum } from './types' -import ErrorHandleTypeSelector from './error-handle-type-selector' -import FailBranchCard from './fail-branch-card' -import DefaultValue from './default-value' -import { - useDefaultValue, - useErrorHandle, -} from './hooks' import type { DefaultValueForm } from './types' import type { CommonNodeType, Node, } from '@/app/components/workflow/types' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' +import Collapse from '../collapse' +import DefaultValue from './default-value' +import ErrorHandleTypeSelector from './error-handle-type-selector' +import FailBranchCard from './fail-branch-card' +import { + useDefaultValue, + useErrorHandle, +} from './hooks' +import { ErrorHandleTypeEnum } from './types' type ErrorHandleProps = Pick<Node, 'id' | 'data'> const ErrorHandle = ({ @@ -44,7 +44,7 @@ const ErrorHandle = ({ return ( <> - <div className='py-4'> + <div className="py-4"> <Collapse disabled={!error_strategy} collapsed={collapsed} @@ -52,9 +52,9 @@ const ErrorHandle = ({ hideCollapseIcon trigger={ collapseIcon => ( - <div className='flex grow items-center justify-between pr-4'> - <div className='flex items-center'> - <div className='system-sm-semibold-uppercase mr-0.5 text-text-secondary'> + <div className="flex grow items-center justify-between pr-4"> + <div className="flex items-center"> + <div className="system-sm-semibold-uppercase mr-0.5 text-text-secondary"> {t('workflow.nodes.common.errorHandle.title')} </div> <Tooltip popupContent={t('workflow.nodes.common.errorHandle.tip')} /> @@ -65,7 +65,8 @@ const ErrorHandle = ({ onSelected={getHandleErrorHandleTypeChange(data)} /> </div> - )} + ) + } > <> { diff --git a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip.tsx b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip.tsx index dc98c4003d..225185da36 100644 --- a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip.tsx +++ b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip.tsx @@ -1,6 +1,6 @@ +import { RiAlertFill } from '@remixicon/react' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { RiAlertFill } from '@remixicon/react' import { ErrorHandleTypeEnum } from './types' type ErrorHandleTipProps = { @@ -24,16 +24,17 @@ const ErrorHandleTip = ({ return ( <div - className='relative flex rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-2 pr-[52px] shadow-xs' + className="relative flex rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-2 pr-[52px] shadow-xs" > <div - className='absolute inset-0 rounded-lg opacity-40' + className="absolute inset-0 rounded-lg opacity-40" style={{ background: 'linear-gradient(92deg, rgba(247, 144, 9, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%)', }} - ></div> - <RiAlertFill className='mr-1 h-4 w-4 shrink-0 text-text-warning-secondary' /> - <div className='system-xs-medium grow text-text-primary'> + > + </div> + <RiAlertFill className="mr-1 h-4 w-4 shrink-0 text-text-warning-secondary" /> + <div className="system-xs-medium grow text-text-primary"> {text} </div> </div> 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 d9516dfcf5..3f97687b14 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,16 +1,16 @@ -import { useState } from 'react' -import { useTranslation } from 'react-i18next' import { RiArrowDownSLine, RiCheckLine, } from '@remixicon/react' -import { ErrorHandleTypeEnum } from './types' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import Button from '@/app/components/base/button' +import { ErrorHandleTypeEnum } from './types' type ErrorHandleTypeSelectorProps = { value: ErrorHandleTypeEnum @@ -45,28 +45,29 @@ const ErrorHandleTypeSelector = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-end' + placement="bottom-end" offset={4} > <PortalToFollowElemTrigger onClick={(e) => { e.stopPropagation() e.nativeEvent.stopImmediatePropagation() setOpen(v => !v) - }}> + }} + > <Button - size='small' + size="small" > {selectedOption?.label} - <RiArrowDownSLine className='h-3.5 w-3.5' /> + <RiArrowDownSLine className="h-3.5 w-3.5" /> </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[11]'> - <div className='w-[280px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'> + <PortalToFollowElemContent className="z-[11]"> + <div className="w-[280px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg"> { options.map(option => ( <div key={option.value} - className='flex cursor-pointer rounded-lg p-2 pr-3 hover:bg-state-base-hover' + className="flex cursor-pointer rounded-lg p-2 pr-3 hover:bg-state-base-hover" onClick={(e) => { e.stopPropagation() e.nativeEvent.stopImmediatePropagation() @@ -74,16 +75,16 @@ const ErrorHandleTypeSelector = ({ setOpen(false) }} > - <div className='mr-1 w-4 shrink-0'> + <div className="mr-1 w-4 shrink-0"> { value === option.value && ( - <RiCheckLine className='h-4 w-4 text-text-accent' /> + <RiCheckLine className="h-4 w-4 text-text-accent" /> ) } </div> - <div className='grow'> - <div className='system-sm-semibold mb-0.5 text-text-secondary'>{option.label}</div> - <div className='system-xs-regular text-text-tertiary'>{option.description}</div> + <div className="grow"> + <div className="system-sm-semibold mb-0.5 text-text-secondary">{option.label}</div> + <div className="system-xs-regular text-text-tertiary">{option.description}</div> </div> </div> )) diff --git a/web/app/components/workflow/nodes/_base/components/error-handle/fail-branch-card.tsx b/web/app/components/workflow/nodes/_base/components/error-handle/fail-branch-card.tsx index fa9cff3dc8..793478b4dd 100644 --- a/web/app/components/workflow/nodes/_base/components/error-handle/fail-branch-card.tsx +++ b/web/app/components/workflow/nodes/_base/components/error-handle/fail-branch-card.tsx @@ -7,21 +7,21 @@ const FailBranchCard = () => { const docLink = useDocLink() return ( - <div className='px-4 pt-2'> - <div className='rounded-[10px] bg-workflow-process-bg p-4'> - <div className='mb-2 flex h-8 w-8 items-center justify-center rounded-[10px] border-[0.5px] border-components-card-border bg-components-card-bg shadow-lg'> - <RiMindMap className='h-5 w-5 text-text-tertiary' /> + <div className="px-4 pt-2"> + <div className="rounded-[10px] bg-workflow-process-bg p-4"> + <div className="mb-2 flex h-8 w-8 items-center justify-center rounded-[10px] border-[0.5px] border-components-card-border bg-components-card-bg shadow-lg"> + <RiMindMap className="h-5 w-5 text-text-tertiary" /> </div> - <div className='system-sm-medium mb-1 text-text-secondary'> + <div className="system-sm-medium mb-1 text-text-secondary"> {t('workflow.nodes.common.errorHandle.failBranch.customize')} </div> - <div className='system-xs-regular text-text-tertiary'> + <div className="system-xs-regular text-text-tertiary"> {t('workflow.nodes.common.errorHandle.failBranch.customizeTip')}   <a href={docLink('/guides/workflow/error-handling/error-type')} - target='_blank' - className='text-text-accent' + target="_blank" + className="text-text-accent" > {t('workflow.common.learnMore')} </a> diff --git a/web/app/components/workflow/nodes/_base/components/error-handle/hooks.ts b/web/app/components/workflow/nodes/_base/components/error-handle/hooks.ts index 06eb4fc48f..86f07c8d1e 100644 --- a/web/app/components/workflow/nodes/_base/components/error-handle/hooks.ts +++ b/web/app/components/workflow/nodes/_base/components/error-handle/hooks.ts @@ -1,18 +1,18 @@ +import type { DefaultValueForm } from './types' +import type { + CommonNodeType, +} from '@/app/components/workflow/types' import { useCallback, useMemo, useState, } from 'react' -import { ErrorHandleTypeEnum } from './types' -import type { DefaultValueForm } from './types' -import { getDefaultValue } from './utils' -import type { - CommonNodeType, -} from '@/app/components/workflow/types' import { useEdgesInteractions, useNodeDataUpdate, } from '@/app/components/workflow/hooks' +import { ErrorHandleTypeEnum } from './types' +import { getDefaultValue } from './utils' export const useDefaultValue = ( id: string, diff --git a/web/app/components/workflow/nodes/_base/components/error-handle/utils.ts b/web/app/components/workflow/nodes/_base/components/error-handle/utils.ts index eef9677c48..ac48407c03 100644 --- a/web/app/components/workflow/nodes/_base/components/error-handle/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/error-handle/utils.ts @@ -1,9 +1,9 @@ +import type { CodeNodeType } from '@/app/components/workflow/nodes/code/types' import type { CommonNodeType } from '@/app/components/workflow/types' import { BlockEnum, VarType, } from '@/app/components/workflow/types' -import type { CodeNodeType } from '@/app/components/workflow/nodes/code/types' const getDefaultValueByType = (type: VarType) => { if (type === VarType.string) diff --git a/web/app/components/workflow/nodes/_base/components/field.tsx b/web/app/components/workflow/nodes/_base/components/field.tsx index b77fa511cb..9f46546700 100644 --- a/web/app/components/workflow/nodes/_base/components/field.tsx +++ b/web/app/components/workflow/nodes/_base/components/field.tsx @@ -1,12 +1,12 @@ 'use client' import type { FC, ReactNode } from 'react' -import React from 'react' import { RiArrowDownSLine, } from '@remixicon/react' import { useBoolean } from 'ahooks' -import { cn } from '@/utils/classnames' +import React from 'react' import Tooltip from '@/app/components/base/tooltip' +import { cn } from '@/utils/classnames' type Props = { className?: string @@ -38,23 +38,26 @@ const Field: FC<Props> = ({ <div className={cn(className, inline && 'flex w-full items-center justify-between')}> <div onClick={() => supportFold && toggleFold()} - className={cn('flex items-center justify-between', supportFold && 'cursor-pointer')}> - <div className='flex h-6 items-center'> + className={cn('flex items-center justify-between', supportFold && 'cursor-pointer')} + > + <div className="flex h-6 items-center"> <div className={cn(isSubTitle ? 'system-xs-medium-uppercase text-text-tertiary' : 'system-sm-semibold-uppercase text-text-secondary')}> - {title} {required && <span className='text-text-destructive'>*</span>} + {title} + {' '} + {required && <span className="text-text-destructive">*</span>} </div> {tooltip && ( <Tooltip popupContent={tooltip} - popupClassName='ml-1' - triggerClassName='w-4 h-4 ml-1' + popupClassName="ml-1" + triggerClassName="w-4 h-4 ml-1" /> )} </div> - <div className='flex'> + <div className="flex"> {operations && <div>{operations}</div>} {supportFold && ( - <RiArrowDownSLine className='h-4 w-4 cursor-pointer text-text-tertiary transition-transform' style={{ transform: fold ? 'rotate(-90deg)' : 'rotate(0deg)' }} /> + <RiArrowDownSLine className="h-4 w-4 cursor-pointer text-text-tertiary transition-transform" style={{ transform: fold ? 'rotate(-90deg)' : 'rotate(0deg)' }} /> )} </div> </div> diff --git a/web/app/components/workflow/nodes/_base/components/file-type-item.tsx b/web/app/components/workflow/nodes/_base/components/file-type-item.tsx index fd07465225..3dc1e7b132 100644 --- a/web/app/components/workflow/nodes/_base/components/file-type-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/file-type-item.tsx @@ -1,14 +1,14 @@ 'use client' import type { FC } from 'react' +import { noop } from 'lodash-es' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { SupportUploadFileTypes } from '../../../types' -import { cn } from '@/utils/classnames' -import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' -import TagInput from '@/app/components/base/tag-input' import Checkbox from '@/app/components/base/checkbox' import { FileTypeIcon } from '@/app/components/base/file-uploader' -import { noop } from 'lodash-es' +import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' +import TagInput from '@/app/components/base/tag-input' +import { cn } from '@/utils/classnames' +import { SupportUploadFileTypes } from '../../../types' type Props = { type: SupportUploadFileTypes.image | SupportUploadFileTypes.document | SupportUploadFileTypes.audio | SupportUploadFileTypes.video | SupportUploadFileTypes.custom @@ -45,31 +45,31 @@ const FileTypeItem: FC<Props> = ({ > {isCustomSelected ? ( - <div> - <div className='flex items-center border-b border-divider-subtle p-3 pb-2'> - <FileTypeIcon className='shrink-0' type={type} size='lg' /> - <div className='system-sm-medium mx-2 grow text-text-primary'>{t(`appDebug.variableConfig.file.${type}.name`)}</div> - <Checkbox className='shrink-0' checked={selected} /> + <div> + <div className="flex items-center border-b border-divider-subtle p-3 pb-2"> + <FileTypeIcon className="shrink-0" type={type} size="lg" /> + <div className="system-sm-medium mx-2 grow text-text-primary">{t(`appDebug.variableConfig.file.${type}.name`)}</div> + <Checkbox className="shrink-0" checked={selected} /> + </div> + <div className="p-3" onClick={e => e.stopPropagation()}> + <TagInput + items={customFileTypes} + onChange={onCustomFileTypesChange} + placeholder={t('appDebug.variableConfig.file.custom.createPlaceholder')!} + /> + </div> </div> - <div className='p-3' onClick={e => e.stopPropagation()}> - <TagInput - items={customFileTypes} - onChange={onCustomFileTypesChange} - placeholder={t('appDebug.variableConfig.file.custom.createPlaceholder')!} - /> - </div> - </div> - ) + ) : ( - <div className='flex items-center'> - <FileTypeIcon className='shrink-0' type={type} size='lg' /> - <div className='mx-2 grow'> - <div className='system-sm-medium text-text-primary'>{t(`appDebug.variableConfig.file.${type}.name`)}</div> - <div className='system-2xs-regular-uppercase mt-1 text-text-tertiary'>{type !== SupportUploadFileTypes.custom ? FILE_EXTS[type].join(', ') : t('appDebug.variableConfig.file.custom.description')}</div> + <div className="flex items-center"> + <FileTypeIcon className="shrink-0" type={type} size="lg" /> + <div className="mx-2 grow"> + <div className="system-sm-medium text-text-primary">{t(`appDebug.variableConfig.file.${type}.name`)}</div> + <div className="system-2xs-regular-uppercase mt-1 text-text-tertiary">{type !== SupportUploadFileTypes.custom ? FILE_EXTS[type].join(', ') : t('appDebug.variableConfig.file.custom.description')}</div> + </div> + <Checkbox className="shrink-0" checked={selected} /> </div> - <Checkbox className='shrink-0' checked={selected} /> - </div> - )} + )} </div> ) diff --git a/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx index ce41777471..c6330daf4d 100644 --- a/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx +++ b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx @@ -1,18 +1,18 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' -import { produce } from 'immer' -import { useTranslation } from 'react-i18next' import type { UploadFileSetting } from '../../../types' +import { produce } from 'immer' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import Field from '@/app/components/app/configuration/config-var/config-modal/field' +import { useFileSizeLimit } from '@/app/components/base/file-uploader/hooks' +import { useFileUploadConfig } from '@/service/use-common' +import { TransferMethod } from '@/types/app' +import { formatFileSize } from '@/utils/format' import { SupportUploadFileTypes } from '../../../types' -import OptionCard from './option-card' import FileTypeItem from './file-type-item' import InputNumberWithSlider from './input-number-with-slider' -import Field from '@/app/components/app/configuration/config-var/config-modal/field' -import { TransferMethod } from '@/types/app' -import { useFileSizeLimit } from '@/app/components/base/file-uploader/hooks' -import { formatFileSize } from '@/utils/format' -import { useFileUploadConfig } from '@/service/use-common' +import OptionCard from './option-card' type Props = { payload: UploadFileSetting @@ -100,7 +100,7 @@ const FileUploadSetting: FC<Props> = ({ <Field title={t('appDebug.variableConfig.file.supportFileTypes')} > - <div className='space-y-1'> + <div className="space-y-1"> { [SupportUploadFileTypes.document, SupportUploadFileTypes.image, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => ( <FileTypeItem @@ -123,9 +123,9 @@ const FileUploadSetting: FC<Props> = ({ )} <Field title={t('appDebug.variableConfig.uploadFileTypes')} - className='mt-4' + className="mt-4" > - <div className='grid grid-cols-3 gap-2'> + <div className="grid grid-cols-3 gap-2"> <OptionCard title={t('appDebug.variableConfig.localUpload')} selected={allowed_file_upload_methods.length === 1 && allowed_file_upload_methods.includes(TransferMethod.local_file)} @@ -145,16 +145,18 @@ const FileUploadSetting: FC<Props> = ({ </Field> {isMultiple && ( <Field - className='mt-4' + className="mt-4" title={t('appDebug.variableConfig.maxNumberOfUploads')!} > <div> - <div className='body-xs-regular mb-1.5 text-text-tertiary'>{t('appDebug.variableConfig.maxNumberTip', { - imgLimit: formatFileSize(imgSizeLimit), - docLimit: formatFileSize(docSizeLimit), - audioLimit: formatFileSize(audioSizeLimit), - videoLimit: formatFileSize(videoSizeLimit), - })}</div> + <div className="body-xs-regular mb-1.5 text-text-tertiary"> + {t('appDebug.variableConfig.maxNumberTip', { + imgLimit: formatFileSize(imgSizeLimit), + docLimit: formatFileSize(docSizeLimit), + audioLimit: formatFileSize(audioSizeLimit), + videoLimit: formatFileSize(videoSizeLimit), + })} + </div> <InputNumberWithSlider value={max_length} @@ -168,9 +170,9 @@ const FileUploadSetting: FC<Props> = ({ {inFeaturePanel && !hideSupportFileType && ( <Field title={t('appDebug.variableConfig.file.supportFileTypes')} - className='mt-4' + className="mt-4" > - <div className='space-y-1'> + <div className="space-y-1"> { [SupportUploadFileTypes.document, SupportUploadFileTypes.image, SupportUploadFileTypes.audio, SupportUploadFileTypes.video].map((type: SupportUploadFileTypes) => ( <FileTypeItem diff --git a/web/app/components/workflow/nodes/_base/components/form-input-boolean.tsx b/web/app/components/workflow/nodes/_base/components/form-input-boolean.tsx index 2767d3b2fb..f5f82f8a71 100644 --- a/web/app/components/workflow/nodes/_base/components/form-input-boolean.tsx +++ b/web/app/components/workflow/nodes/_base/components/form-input-boolean.tsx @@ -12,7 +12,7 @@ const FormInputBoolean: FC<Props> = ({ onChange, }) => { return ( - <div className='flex w-full space-x-1'> + <div className="flex w-full space-x-1"> <div className={cn( 'system-sm-regular flex h-8 grow cursor-default items-center justify-center rounded-md border border-components-option-card-option-border bg-components-option-card-option-bg px-2 text-text-secondary', @@ -20,7 +20,9 @@ const FormInputBoolean: FC<Props> = ({ value && 'system-sm-medium border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg shadow-xs', )} onClick={() => onChange(true)} - >True</div> + > + True + </div> <div className={cn( 'system-sm-regular flex h-8 grow cursor-default items-center justify-center rounded-md border border-components-option-card-option-border bg-components-option-card-option-bg px-2 text-text-secondary', @@ -28,7 +30,9 @@ const FormInputBoolean: FC<Props> = ({ !value && 'system-sm-medium border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg shadow-xs', )} onClick={() => onChange(false)} - >False</div> + > + False + </div> </div> ) } diff --git a/web/app/components/workflow/nodes/_base/components/form-input-item.tsx b/web/app/components/workflow/nodes/_base/components/form-input-item.tsx index 7cf0043520..419f905fa5 100644 --- a/web/app/components/workflow/nodes/_base/components/form-input-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/form-input-item.tsx @@ -1,35 +1,35 @@ 'use client' import type { FC } from 'react' -import { useEffect, useMemo, useState } from 'react' -import { type ResourceVarInputs, VarKindType } from '../types' +import type { ResourceVarInputs } from '../types' import type { CredentialFormSchema, FormOption } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' +import type { Event, Tool } from '@/app/components/tools/types' +import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' +import type { ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types' +import { Listbox, ListboxButton, ListboxOption, ListboxOptions } from '@headlessui/react' +import { ChevronDownIcon } from '@heroicons/react/20/solid' + +import { RiCheckLine, RiLoader4Line } from '@remixicon/react' +import { useEffect, useMemo, useState } from 'react' +import CheckboxList from '@/app/components/base/checkbox-list' +import Input from '@/app/components/base/input' +import { SimpleSelect } from '@/app/components/base/select' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' +import AppSelector from '@/app/components/plugins/plugin-detail-panel/app-selector' +import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector' +import { PluginCategoryEnum } from '@/app/components/plugins/types' +import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' +import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' +import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' +import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' +import MixedVariableTextInput from '@/app/components/workflow/nodes/tool/components/mixed-variable-text-input' import { VarType } from '@/app/components/workflow/types' import { useFetchDynamicOptions } from '@/service/use-plugins' import { useTriggerPluginDynamicOptions } from '@/service/use-triggers' - -import type { ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types' -import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' -import type { Tool } from '@/app/components/tools/types' -import FormInputTypeSwitch from './form-input-type-switch' -import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' -import Input from '@/app/components/base/input' -import { SimpleSelect } from '@/app/components/base/select' -import MixedVariableTextInput from '@/app/components/workflow/nodes/tool/components/mixed-variable-text-input' -import AppSelector from '@/app/components/plugins/plugin-detail-panel/app-selector' -import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector' -import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' -import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' -import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import { cn } from '@/utils/classnames' -import { Listbox, ListboxButton, ListboxOption, ListboxOptions } from '@headlessui/react' -import { ChevronDownIcon } from '@heroicons/react/20/solid' -import { RiCheckLine, RiLoader4Line } from '@remixicon/react' -import type { Event } from '@/app/components/tools/types' -import { PluginCategoryEnum } from '@/app/components/plugins/types' -import CheckboxList from '@/app/components/base/checkbox-list' +import { VarKindType } from '../types' import FormInputBoolean from './form-input-boolean' +import FormInputTypeSwitch from './form-input-type-switch' type Props = { readOnly: boolean @@ -284,7 +284,7 @@ const FormInputItem: FC<Props> = ({ } const availableCheckboxOptions = useMemo(() => ( - (options || []).filter((option: { show_on?: Array<{ variable: string; value: any }> }) => { + (options || []).filter((option: { show_on?: Array<{ variable: string, value: any }> }) => { if (option.show_on?.length) return option.show_on.every(showOnItem => value[showOnItem.variable]?.value === showOnItem.value || value[showOnItem.variable] === showOnItem.value) return true @@ -292,7 +292,7 @@ const FormInputItem: FC<Props> = ({ ), [options, value]) const checkboxListOptions = useMemo(() => ( - availableCheckboxOptions.map((option: { value: string; label: Record<string, string> }) => ({ + availableCheckboxOptions.map((option: { value: string, label: Record<string, string> }) => ({ value: option.value, label: option.label?.[language] || option.label?.en_US || option.value, })) @@ -341,8 +341,8 @@ const FormInputItem: FC<Props> = ({ )} {isNumber && isConstant && ( <Input - className='h-8 grow' - type='number' + className="h-8 grow" + type="number" value={Number.isNaN(varInput?.value) ? '' : varInput?.value} onChange={e => handleValueChange(e.target.value)} placeholder={placeholder?.[language] || placeholder?.en_US} @@ -355,7 +355,7 @@ const FormInputItem: FC<Props> = ({ onChange={handleCheckboxListChange} options={checkboxListOptions} disabled={readOnly} - maxHeight='200px' + maxHeight="200px" /> )} {isBoolean && isConstant && ( @@ -366,7 +366,7 @@ const FormInputItem: FC<Props> = ({ )} {isSelect && isConstant && !isMultipleSelect && ( <SimpleSelect - wrapperClassName='h-8 grow' + wrapperClassName="h-8 grow" disabled={readOnly} defaultValue={varInput?.value} items={options.filter((option: { show_on: any[] }) => { @@ -374,21 +374,23 @@ const FormInputItem: FC<Props> = ({ return option.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value) return true - }).map((option: { value: any; label: { [x: string]: any; en_US: any }; icon?: string }) => ({ + }).map((option: { value: any, label: { [x: string]: any, en_US: any }, icon?: string }) => ({ value: option.value, name: option.label[language] || option.label.en_US, icon: option.icon, }))} onSelect={item => handleValueChange(item.value as string)} placeholder={placeholder?.[language] || placeholder?.en_US} - renderOption={options.some((opt: any) => opt.icon) ? ({ item }) => ( - <div className="flex items-center"> - {item.icon && ( - <img src={item.icon} alt="" className="mr-2 h-4 w-4" /> - )} - <span>{item.name}</span> - </div> - ) : undefined} + renderOption={options.some((opt: any) => opt.icon) + ? ({ item }) => ( + <div className="flex items-center"> + {item.icon && ( + <img src={item.icon} alt="" className="mr-2 h-4 w-4" /> + )} + <span>{item.name}</span> + </div> + ) + : undefined} /> )} {isSelect && isConstant && isMultipleSelect && ( @@ -400,9 +402,7 @@ const FormInputItem: FC<Props> = ({ > <div className="group/simple-select relative h-8 grow"> <ListboxButton className="flex h-full w-full cursor-pointer items-center rounded-lg border-0 bg-components-input-bg-normal pl-3 pr-10 focus-visible:bg-state-base-hover-alt focus-visible:outline-none group-hover/simple-select:bg-state-base-hover-alt sm:text-sm sm:leading-6"> - <span className={cn('system-sm-regular block truncate text-left', - varInput?.value?.length > 0 ? 'text-components-input-text-filled' : 'text-components-input-text-placeholder', - )}> + <span className={cn('system-sm-regular block truncate text-left', varInput?.value?.length > 0 ? 'text-components-input-text-filled' : 'text-components-input-text-placeholder')}> {getSelectedLabels(varInput?.value) || placeholder?.[language] || placeholder?.en_US || 'Select options'} </span> <span className="absolute inset-y-0 right-0 flex items-center pr-2"> @@ -417,15 +417,12 @@ const FormInputItem: FC<Props> = ({ if (option.show_on?.length) return option.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value) return true - }).map((option: { value: any; label: { [x: string]: any; en_US: any }; icon?: string }) => ( + }).map((option: { value: any, label: { [x: string]: any, en_US: any }, icon?: string }) => ( <ListboxOption key={option.value} value={option.value} className={({ focus }) => - cn('relative cursor-pointer select-none rounded-lg py-2 pl-3 pr-9 text-text-secondary hover:bg-state-base-hover', - focus && 'bg-state-base-hover', - ) - } + cn('relative cursor-pointer select-none rounded-lg py-2 pl-3 pr-9 text-text-secondary hover:bg-state-base-hover', focus && 'bg-state-base-hover')} > {({ selected }) => ( <> @@ -452,7 +449,7 @@ const FormInputItem: FC<Props> = ({ )} {isDynamicSelect && !isMultipleSelect && ( <SimpleSelect - wrapperClassName='h-8 grow' + wrapperClassName="h-8 grow" disabled={readOnly || isLoadingOptions} defaultValue={varInput?.value} items={(dynamicOptions || options || []).filter((option: { show_on?: any[] }) => { @@ -460,7 +457,7 @@ const FormInputItem: FC<Props> = ({ return option.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value) return true - }).map((option: { value: any; label: { [x: string]: any; en_US: any }; icon?: string }) => ({ + }).map((option: { value: any, label: { [x: string]: any, en_US: any }, icon?: string }) => ({ value: option.value, name: option.label[language] || option.label.en_US, icon: option.icon, @@ -486,23 +483,25 @@ const FormInputItem: FC<Props> = ({ > <div className="group/simple-select relative h-8 grow"> <ListboxButton className="flex h-full w-full cursor-pointer items-center rounded-lg border-0 bg-components-input-bg-normal pl-3 pr-10 focus-visible:bg-state-base-hover-alt focus-visible:outline-none group-hover/simple-select:bg-state-base-hover-alt sm:text-sm sm:leading-6"> - <span className={cn('system-sm-regular block truncate text-left', - isLoadingOptions ? 'text-components-input-text-placeholder' - : varInput?.value?.length > 0 ? 'text-components-input-text-filled' : 'text-components-input-text-placeholder', - )}> + <span className={cn('system-sm-regular block truncate text-left', isLoadingOptions + ? 'text-components-input-text-placeholder' + : varInput?.value?.length > 0 ? 'text-components-input-text-filled' : 'text-components-input-text-placeholder')} + > {isLoadingOptions ? 'Loading...' : getSelectedLabels(varInput?.value) || placeholder?.[language] || placeholder?.en_US || 'Select options'} </span> <span className="absolute inset-y-0 right-0 flex items-center pr-2"> - {isLoadingOptions ? ( - <RiLoader4Line className='h-3.5 w-3.5 animate-spin text-text-secondary' /> - ) : ( - <ChevronDownIcon - className="h-4 w-4 text-text-quaternary group-hover/simple-select:text-text-secondary" - aria-hidden="true" - /> - )} + {isLoadingOptions + ? ( + <RiLoader4Line className="h-3.5 w-3.5 animate-spin text-text-secondary" /> + ) + : ( + <ChevronDownIcon + className="h-4 w-4 text-text-quaternary group-hover/simple-select:text-text-secondary" + aria-hidden="true" + /> + )} </span> </ListboxButton> <ListboxOptions className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur px-1 py-1 text-base shadow-lg backdrop-blur-sm focus:outline-none sm:text-sm"> @@ -510,15 +509,12 @@ const FormInputItem: FC<Props> = ({ if (option.show_on?.length) return option.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value) return true - }).map((option: { value: any; label: { [x: string]: any; en_US: any }; icon?: string }) => ( + }).map((option: { value: any, label: { [x: string]: any, en_US: any }, icon?: string }) => ( <ListboxOption key={option.value} value={option.value} className={({ focus }) => - cn('relative cursor-pointer select-none rounded-lg py-2 pl-3 pr-9 text-text-secondary hover:bg-state-base-hover', - focus && 'bg-state-base-hover', - ) - } + cn('relative cursor-pointer select-none rounded-lg py-2 pl-3 pr-9 text-text-secondary hover:bg-state-base-hover', focus && 'bg-state-base-hover')} > {({ selected }) => ( <> @@ -544,16 +540,16 @@ const FormInputItem: FC<Props> = ({ </Listbox> )} {isShowJSONEditor && isConstant && ( - <div className='mt-1 w-full'> + <div className="mt-1 w-full"> <CodeEditor - title='JSON' + title="JSON" value={varInput?.value as any} isExpand isInNode language={CodeLanguage.json} onChange={handleValueChange} - className='w-full' - placeholder={<div className='whitespace-pre'>{placeholder?.[language] || placeholder?.en_US}</div>} + className="w-full" + placeholder={<div className="whitespace-pre">{placeholder?.[language] || placeholder?.en_US}</div>} /> </div> )} @@ -567,7 +563,7 @@ const FormInputItem: FC<Props> = ({ )} {isModelSelector && isConstant && ( <ModelParameterModal - popupClassName='!w-[387px]' + popupClassName="!w-[387px]" isAdvancedMode isInWorkflow value={varInput?.value} @@ -579,7 +575,7 @@ const FormInputItem: FC<Props> = ({ {showVariableSelector && ( <VarReferencePicker zIndex={inPanel ? 1000 : undefined} - className='h-8 grow' + className="h-8 grow" readonly={readOnly} isShowNodeName nodeId={nodeId} diff --git a/web/app/components/workflow/nodes/_base/components/form-input-type-switch.tsx b/web/app/components/workflow/nodes/_base/components/form-input-type-switch.tsx index c7af679e23..04e711511f 100644 --- a/web/app/components/workflow/nodes/_base/components/form-input-type-switch.tsx +++ b/web/app/components/workflow/nodes/_base/components/form-input-type-switch.tsx @@ -1,9 +1,9 @@ 'use client' import type { FC } from 'react' -import { useTranslation } from 'react-i18next' import { RiEditLine, } from '@remixicon/react' +import { useTranslation } from 'react-i18next' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import Tooltip from '@/app/components/base/tooltip' import { VarType } from '@/app/components/workflow/nodes/tool/types' @@ -20,7 +20,7 @@ const FormInputTypeSwitch: FC<Props> = ({ }) => { const { t } = useTranslation() return ( - <div className='inline-flex h-8 shrink-0 gap-px rounded-[10px] bg-components-segmented-control-bg-normal p-0.5'> + <div className="inline-flex h-8 shrink-0 gap-px rounded-[10px] bg-components-segmented-control-bg-normal p-0.5"> <Tooltip popupContent={value === VarType.variable ? '' : t('workflow.nodes.common.typeSwitch.variable')} > @@ -28,7 +28,7 @@ const FormInputTypeSwitch: FC<Props> = ({ className={cn('cursor-pointer rounded-lg px-2.5 py-1.5 text-text-tertiary hover:bg-state-base-hover', value === VarType.variable && 'bg-components-segmented-control-item-active-bg text-text-secondary shadow-xs hover:bg-components-segmented-control-item-active-bg')} onClick={() => onChange(VarType.variable)} > - <Variable02 className='h-4 w-4' /> + <Variable02 className="h-4 w-4" /> </div> </Tooltip> <Tooltip @@ -38,7 +38,7 @@ const FormInputTypeSwitch: FC<Props> = ({ className={cn('cursor-pointer rounded-lg px-2.5 py-1.5 text-text-tertiary hover:bg-state-base-hover', value === VarType.constant && 'bg-components-segmented-control-item-active-bg text-text-secondary shadow-xs hover:bg-components-segmented-control-item-active-bg')} onClick={() => onChange(VarType.constant)} > - <RiEditLine className='h-4 w-4' /> + <RiEditLine className="h-4 w-4" /> </div> </Tooltip> </div> diff --git a/web/app/components/workflow/nodes/_base/components/group.tsx b/web/app/components/workflow/nodes/_base/components/group.tsx index 80157aca13..366f86df05 100644 --- a/web/app/components/workflow/nodes/_base/components/group.tsx +++ b/web/app/components/workflow/nodes/_base/components/group.tsx @@ -1,13 +1,15 @@ -import { cn } from '@/utils/classnames' import type { ComponentProps, FC, PropsWithChildren, ReactNode } from 'react' +import { cn } from '@/utils/classnames' export type GroupLabelProps = ComponentProps<'div'> export const GroupLabel: FC<GroupLabelProps> = (props) => { const { children, className, ...rest } = props - return <div {...rest} className={cn('system-2xs-medium-uppercase mb-1 text-text-tertiary', className)}> - {children} - </div> + return ( + <div {...rest} className={cn('system-2xs-medium-uppercase mb-1 text-text-tertiary', className)}> + {children} + </div> + ) } export type GroupProps = PropsWithChildren<{ @@ -16,10 +18,12 @@ export type GroupProps = PropsWithChildren<{ export const Group: FC<GroupProps> = (props) => { const { children, label } = props - return <div className={cn('py-1')}> - {label} - <div className='space-y-0.5'> - {children} + return ( + <div className={cn('py-1')}> + {label} + <div className="space-y-0.5"> + {children} + </div> </div> - </div> + ) } diff --git a/web/app/components/workflow/nodes/_base/components/help-link.tsx b/web/app/components/workflow/nodes/_base/components/help-link.tsx index 8bf74529f1..adc153f029 100644 --- a/web/app/components/workflow/nodes/_base/components/help-link.tsx +++ b/web/app/components/workflow/nodes/_base/components/help-link.tsx @@ -1,9 +1,9 @@ +import type { BlockEnum } from '@/app/components/workflow/types' +import { RiBookOpenLine } from '@remixicon/react' import { memo } from 'react' import { useTranslation } from 'react-i18next' -import { RiBookOpenLine } from '@remixicon/react' -import { useNodeHelpLink } from '../hooks/use-node-help-link' import TooltipPlus from '@/app/components/base/tooltip' -import type { BlockEnum } from '@/app/components/workflow/types' +import { useNodeHelpLink } from '../hooks/use-node-help-link' type HelpLinkProps = { nodeType: BlockEnum @@ -23,10 +23,10 @@ const HelpLink = ({ > <a href={link} - target='_blank' - className='mr-1 flex h-6 w-6 items-center justify-center rounded-md hover:bg-state-base-hover' + target="_blank" + className="mr-1 flex h-6 w-6 items-center justify-center rounded-md hover:bg-state-base-hover" > - <RiBookOpenLine className='h-4 w-4 text-gray-500' /> + <RiBookOpenLine className="h-4 w-4 text-gray-500" /> </a> </TooltipPlus> diff --git a/web/app/components/workflow/nodes/_base/components/info-panel.tsx b/web/app/components/workflow/nodes/_base/components/info-panel.tsx index 88511b1de7..cc2426c24f 100644 --- a/web/app/components/workflow/nodes/_base/components/info-panel.tsx +++ b/web/app/components/workflow/nodes/_base/components/info-panel.tsx @@ -13,11 +13,11 @@ const InfoPanel: FC<Props> = ({ }) => { return ( <div> - <div className='flex flex-col gap-y-0.5 rounded-md bg-workflow-block-parma-bg px-[5px] py-[3px]'> - <div className='system-2xs-semibold-uppercase uppercase text-text-secondary'> + <div className="flex flex-col gap-y-0.5 rounded-md bg-workflow-block-parma-bg px-[5px] py-[3px]"> + <div className="system-2xs-semibold-uppercase uppercase text-text-secondary"> {title} </div> - <div className='system-xs-regular break-words text-text-tertiary'> + <div className="system-xs-regular break-words text-text-tertiary"> {content} </div> </div> diff --git a/web/app/components/workflow/nodes/_base/components/input-field/add.tsx b/web/app/components/workflow/nodes/_base/components/input-field/add.tsx index a104973399..fcefa8da57 100644 --- a/web/app/components/workflow/nodes/_base/components/input-field/add.tsx +++ b/web/app/components/workflow/nodes/_base/components/input-field/add.tsx @@ -4,7 +4,7 @@ import ActionButton from '@/app/components/base/action-button' const Add = () => { return ( <ActionButton> - <RiAddLine className='h-4 w-4' /> + <RiAddLine className="h-4 w-4" /> </ActionButton> ) } 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 17208fe54d..0a021402ba 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 @@ -38,11 +38,11 @@ const InputNumberWithSlider: FC<InputNumberWithSliderProps> = ({ }, [onChange]) return ( - <div className='flex h-8 items-center justify-between space-x-2'> + <div className="flex h-8 items-center justify-between space-x-2"> <input value={value} - className='block h-8 w-12 shrink-0 appearance-none rounded-lg bg-components-input-bg-normal pl-3 text-[13px] text-components-input-text-filled outline-none' - type='number' + className="block h-8 w-12 shrink-0 appearance-none rounded-lg bg-components-input-bg-normal pl-3 text-[13px] text-components-input-text-filled outline-none" + type="number" min={min} max={max} step={1} @@ -51,7 +51,7 @@ const InputNumberWithSlider: FC<InputNumberWithSliderProps> = ({ disabled={readonly} /> <Slider - className='grow' + className="grow" value={value} min={min} max={max} diff --git a/web/app/components/workflow/nodes/_base/components/input-support-select-var.tsx b/web/app/components/workflow/nodes/_base/components/input-support-select-var.tsx index 8ffe301d67..c06fe55375 100644 --- a/web/app/components/workflow/nodes/_base/components/input-support-select-var.tsx +++ b/web/app/components/workflow/nodes/_base/components/input-support-select-var.tsx @@ -1,19 +1,19 @@ 'use client' import type { FC } from 'react' -import React, { useEffect } from 'react' -import { useBoolean } from 'ahooks' -import { useTranslation } from 'react-i18next' -import { cn } from '@/utils/classnames' import type { Node, NodeOutPutVar, } from '@/app/components/workflow/types' -import { BlockEnum } from '@/app/components/workflow/types' -import PromptEditor from '@/app/components/base/prompt-editor' -import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' -import Tooltip from '@/app/components/base/tooltip' +import { useBoolean } from 'ahooks' import { noop } from 'lodash-es' +import React, { useEffect } from 'react' +import { useTranslation } from 'react-i18next' +import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' +import PromptEditor from '@/app/components/base/prompt-editor' +import Tooltip from '@/app/components/base/tooltip' import { useStore } from '@/app/components/workflow/store' +import { BlockEnum } from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' type Props = { instanceId?: string @@ -115,20 +115,20 @@ const Editor: FC<Props> = ({ onFocus={setFocus} /> {/* to patch Editor not support dynamic change editable status */} - {readOnly && <div className='absolute inset-0 z-10'></div>} + {readOnly && <div className="absolute inset-0 z-10"></div>} {isFocus && ( <div className={cn('absolute z-10', insertVarTipToLeft ? 'left-[-12px] top-1.5' : ' right-1 top-[-9px]')}> <Tooltip popupContent={`${t('workflow.common.insertVarTip')}`} > - <div className='cursor-pointer rounded-[5px] border-[0.5px] border-divider-regular bg-components-badge-white-to-dark p-0.5 shadow-lg'> - <Variable02 className='h-3.5 w-3.5 text-components-button-secondary-accent-text' /> + <div className="cursor-pointer rounded-[5px] border-[0.5px] border-divider-regular bg-components-badge-white-to-dark p-0.5 shadow-lg"> + <Variable02 className="h-3.5 w-3.5 text-components-button-secondary-accent-text" /> </div> </Tooltip> </div> )} </> - </div > + </div> ) } export default React.memo(Editor) diff --git a/web/app/components/workflow/nodes/_base/components/input-var-type-icon.tsx b/web/app/components/workflow/nodes/_base/components/input-var-type-icon.tsx index 70fd1051b9..7e529013cb 100644 --- a/web/app/components/workflow/nodes/_base/components/input-var-type-icon.tsx +++ b/web/app/components/workflow/nodes/_base/components/input-var-type-icon.tsx @@ -1,6 +1,5 @@ 'use client' import type { FC } from 'react' -import React from 'react' import { RiAlignLeft, RiBracesLine, @@ -11,6 +10,7 @@ import { RiHashtag, RiTextSnippet, } from '@remixicon/react' +import React from 'react' import { InputVarType } from '../../../types' type Props = { 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 385b69ee43..f77a24c965 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,12 +1,12 @@ -import Button from '@/app/components/base/button' -import { RiInstallLine, RiLoader2Line } from '@remixicon/react' import type { ComponentProps, MouseEventHandler } from 'react' +import { RiInstallLine, RiLoader2Line } from '@remixicon/react' import { useState } from 'react' -import { cn } from '@/utils/classnames' import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/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' +import { cn } from '@/utils/classnames' type InstallPluginButtonProps = Omit<ComponentProps<typeof Button>, 'children' | 'loading'> & { uniqueIdentifier: string @@ -83,22 +83,26 @@ export const InstallPluginButton = (props: InstallPluginButtonProps) => { }, }) } - if (!manifest.data) return null + if (!manifest.data) + return null const identifierSet = new Set(identifiers) const isInstalled = manifest.data.plugins.some(plugin => ( identifierSet.has(plugin.id) || (plugin.plugin_unique_identifier && identifierSet.has(plugin.plugin_unique_identifier)) || (plugin.plugin_id && identifierSet.has(plugin.plugin_id)) )) - if (isInstalled) return null - return <Button - variant={'secondary'} - disabled={isLoading} - {...rest} - onClick={handleInstall} - className={cn('flex items-center', className)} - > - {!isLoading ? t('workflow.nodes.agent.pluginInstaller.install') : t('workflow.nodes.agent.pluginInstaller.installing')} - {!isLoading ? <RiInstallLine className='ml-1 size-3.5' /> : <RiLoader2Line className='ml-1 size-3.5 animate-spin' />} - </Button> + if (isInstalled) + return null + return ( + <Button + variant="secondary" + disabled={isLoading} + {...rest} + onClick={handleInstall} + className={cn('flex items-center', className)} + > + {!isLoading ? t('workflow.nodes.agent.pluginInstaller.install') : t('workflow.nodes.agent.pluginInstaller.installing')} + {!isLoading ? <RiInstallLine className="ml-1 size-3.5" /> : <RiLoader2Line className="ml-1 size-3.5 animate-spin" />} + </Button> + ) } diff --git a/web/app/components/workflow/nodes/_base/components/layout/box-group-field.tsx b/web/app/components/workflow/nodes/_base/components/layout/box-group-field.tsx index f89f13e1c5..a6df026a5b 100644 --- a/web/app/components/workflow/nodes/_base/components/layout/box-group-field.tsx +++ b/web/app/components/workflow/nodes/_base/components/layout/box-group-field.tsx @@ -1,9 +1,9 @@ import type { ReactNode } from 'react' -import { memo } from 'react' import type { BoxGroupProps, FieldProps, } from '.' +import { memo } from 'react' import { BoxGroup, Field, diff --git a/web/app/components/workflow/nodes/_base/components/layout/box-group.tsx b/web/app/components/workflow/nodes/_base/components/layout/box-group.tsx index 7d2d4b0dcb..edf0c116ec 100644 --- a/web/app/components/workflow/nodes/_base/components/layout/box-group.tsx +++ b/web/app/components/workflow/nodes/_base/components/layout/box-group.tsx @@ -1,13 +1,13 @@ import type { ReactNode } from 'react' +import type { + BoxProps, + GroupProps, +} from '.' import { memo } from 'react' import { Box, Group, } from '.' -import type { - BoxProps, - GroupProps, -} from '.' export type BoxGroupProps = { children?: ReactNode diff --git a/web/app/components/workflow/nodes/_base/components/layout/box.tsx b/web/app/components/workflow/nodes/_base/components/layout/box.tsx index ec4869d305..62e709efc6 100644 --- a/web/app/components/workflow/nodes/_base/components/layout/box.tsx +++ b/web/app/components/workflow/nodes/_base/components/layout/box.tsx @@ -18,7 +18,8 @@ export const Box = memo(({ 'py-2', withBorderBottom && 'border-b border-divider-subtle', className, - )}> + )} + > {children} </div> ) 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 1a19ac2ab4..e5e8fe950d 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 @@ -33,7 +33,7 @@ export const FieldTitle = memo(({ return ( <div className={cn('mb-0.5', !!subTitle && 'mb-1')}> <div - className='group/collapse flex items-center justify-between py-1' + className="group/collapse flex items-center justify-between py-1" onClick={() => { if (!disabled) { setCollapsedLocal(!collapsedMerged) @@ -41,7 +41,7 @@ export const FieldTitle = memo(({ } }} > - <div className='system-sm-semibold-uppercase flex items-center text-text-secondary'> + <div className="system-sm-semibold-uppercase flex items-center text-text-secondary"> {title} { showArrow && ( @@ -57,7 +57,7 @@ export const FieldTitle = memo(({ tooltip && ( <Tooltip popupContent={tooltip} - triggerClassName='w-4 h-4 ml-1' + triggerClassName="w-4 h-4 ml-1" /> ) } diff --git a/web/app/components/workflow/nodes/_base/components/layout/field.tsx b/web/app/components/workflow/nodes/_base/components/layout/field.tsx index 46951d89e3..70f35e0ba7 100644 --- a/web/app/components/workflow/nodes/_base/components/layout/field.tsx +++ b/web/app/components/workflow/nodes/_base/components/layout/field.tsx @@ -1,9 +1,9 @@ import type { ReactNode } from 'react' +import type { FieldTitleProps } from '.' import { memo, useState, } from 'react' -import type { FieldTitleProps } from '.' import { FieldTitle } from '.' export type FieldProps = { diff --git a/web/app/components/workflow/nodes/_base/components/layout/group-field.tsx b/web/app/components/workflow/nodes/_base/components/layout/group-field.tsx index b7238f0c0c..800a21edb7 100644 --- a/web/app/components/workflow/nodes/_base/components/layout/group-field.tsx +++ b/web/app/components/workflow/nodes/_base/components/layout/group-field.tsx @@ -1,9 +1,9 @@ import type { ReactNode } from 'react' -import { memo } from 'react' import type { FieldProps, GroupProps, } from '.' +import { memo } from 'react' import { Field, Group, diff --git a/web/app/components/workflow/nodes/_base/components/layout/group.tsx b/web/app/components/workflow/nodes/_base/components/layout/group.tsx index 446588eb45..6e35cb7b69 100644 --- a/web/app/components/workflow/nodes/_base/components/layout/group.tsx +++ b/web/app/components/workflow/nodes/_base/components/layout/group.tsx @@ -18,7 +18,8 @@ export const Group = memo(({ 'px-4 py-2', withBorderBottom && 'border-b border-divider-subtle', className, - )}> + )} + > {children} </div> ) diff --git a/web/app/components/workflow/nodes/_base/components/layout/index.tsx b/web/app/components/workflow/nodes/_base/components/layout/index.tsx index fc5b211bdc..f470cbf20f 100644 --- a/web/app/components/workflow/nodes/_base/components/layout/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/layout/index.tsx @@ -1,7 +1,7 @@ export * from './box' -export * from './group' export * from './box-group' -export * from './field-title' -export * from './field' -export * from './group-field' export * from './box-group-field' +export * from './field' +export * from './field-title' +export * from './group' +export * from './group-field' diff --git a/web/app/components/workflow/nodes/_base/components/list-no-data-placeholder.tsx b/web/app/components/workflow/nodes/_base/components/list-no-data-placeholder.tsx index d4a7f07b7e..98e2dc4f29 100644 --- a/web/app/components/workflow/nodes/_base/components/list-no-data-placeholder.tsx +++ b/web/app/components/workflow/nodes/_base/components/list-no-data-placeholder.tsx @@ -10,7 +10,7 @@ const ListNoDataPlaceholder: FC<Props> = ({ children, }) => { return ( - <div className='system-xs-regular flex min-h-[42px] w-full items-center justify-center rounded-[10px] bg-background-section text-text-tertiary'> + <div className="system-xs-regular flex min-h-[42px] w-full items-center justify-center rounded-[10px] bg-background-section text-text-tertiary"> {children} </div> ) diff --git a/web/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip.tsx b/web/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip.tsx index b314082af2..33da12239b 100644 --- a/web/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip.tsx +++ b/web/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip.tsx @@ -1,21 +1,21 @@ 'use client' -import Tooltip from '@/app/components/base/tooltip' -import { RiAlertFill } from '@remixicon/react' import type { FC } from 'react' +import { RiAlertFill } from '@remixicon/react' import React from 'react' import { useTranslation } from 'react-i18next' +import Tooltip from '@/app/components/base/tooltip' const McpToolNotSupportTooltip: FC = () => { const { t } = useTranslation() return ( <Tooltip - popupContent={ - <div className='w-[256px]'> + popupContent={( + <div className="w-[256px]"> {t('plugin.detailPanel.toolSelector.unsupportedMCPTool')} </div> - } + )} > - <RiAlertFill className='size-4 text-text-warning-secondary' /> + <RiAlertFill className="size-4 text-text-warning-secondary" /> </Tooltip> ) } 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 989f3f635d..1272f132a6 100644 --- a/web/app/components/workflow/nodes/_base/components/memory-config.tsx +++ b/web/app/components/workflow/nodes/_base/components/memory-config.tsx @@ -1,15 +1,15 @@ 'use client' import type { FC } from 'react' +import type { Memory } from '../../../types' +import { produce } from 'immer' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { produce } from 'immer' -import type { Memory } from '../../../types' -import { MemoryRole } from '../../../types' -import { cn } from '@/utils/classnames' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import Switch from '@/app/components/base/switch' -import Slider from '@/app/components/base/slider' import Input from '@/app/components/base/input' +import Slider from '@/app/components/base/slider' +import Switch from '@/app/components/base/switch' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import { cn } from '@/utils/classnames' +import { MemoryRole } from '../../../types' const i18nPrefix = 'workflow.nodes.common.memory' const WINDOW_SIZE_MIN = 1 @@ -31,14 +31,15 @@ const RoleItem: FC<RoleItemProps> = ({ onChange(e.target.value) }, [onChange]) return ( - <div className='flex items-center justify-between'> - <div className='text-[13px] font-normal text-text-secondary'>{title}</div> + <div className="flex items-center justify-between"> + <div className="text-[13px] font-normal text-text-secondary">{title}</div> <Input readOnly={readonly} value={value} onChange={handleChange} - className='h-8 w-[200px]' - type='text' /> + className="h-8 w-[200px]" + type="text" + /> </div> ) } @@ -132,31 +133,31 @@ const MemoryConfig: FC<Props> = ({ <Field title={t(`${i18nPrefix}.memory`)} tooltip={t(`${i18nPrefix}.memoryTip`)!} - operations={ + operations={( <Switch defaultValue={!!payload} onChange={handleMemoryEnabledChange} - size='md' + size="md" disabled={readonly} /> - } + )} > {payload && ( <> {/* window size */} - <div className='flex justify-between'> - <div className='flex h-8 items-center space-x-2'> + <div className="flex justify-between"> + <div className="flex h-8 items-center space-x-2"> <Switch defaultValue={payload?.window?.enabled} onChange={handleWindowEnabledChange} - size='md' + size="md" disabled={readonly} /> - <div className='system-xs-medium-uppercase text-text-tertiary'>{t(`${i18nPrefix}.windowSize`)}</div> + <div className="system-xs-medium-uppercase text-text-tertiary">{t(`${i18nPrefix}.windowSize`)}</div> </div> - <div className='flex h-8 items-center space-x-2'> + <div className="flex h-8 items-center space-x-2"> <Slider - className='w-[144px]' + className="w-[144px]" value={(payload.window?.size || WINDOW_SIZE_DEFAULT) as number} min={WINDOW_SIZE_MIN} max={WINDOW_SIZE_MAX} @@ -166,9 +167,9 @@ const MemoryConfig: FC<Props> = ({ /> <Input value={(payload.window?.size || WINDOW_SIZE_DEFAULT) as number} - wrapperClassName='w-12' - className='appearance-none pr-0' - type='number' + wrapperClassName="w-12" + className="appearance-none pr-0" + type="number" min={WINDOW_SIZE_MIN} max={WINDOW_SIZE_MAX} step={1} @@ -179,9 +180,9 @@ const MemoryConfig: FC<Props> = ({ </div> </div> {canSetRoleName && ( - <div className='mt-4'> - <div className='text-xs font-medium uppercase leading-6 text-text-tertiary'>{t(`${i18nPrefix}.conversationRoleName`)}</div> - <div className='mt-1 space-y-2'> + <div className="mt-4"> + <div className="text-xs font-medium uppercase leading-6 text-text-tertiary">{t(`${i18nPrefix}.conversationRoleName`)}</div> + <div className="mt-1 space-y-2"> <RoleItem readonly={readonly} title={t(`${i18nPrefix}.user`)} diff --git a/web/app/components/workflow/nodes/_base/components/mixed-variable-text-input/index.tsx b/web/app/components/workflow/nodes/_base/components/mixed-variable-text-input/index.tsx index 5fc6bd0528..acc395439c 100644 --- a/web/app/components/workflow/nodes/_base/components/mixed-variable-text-input/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/mixed-variable-text-input/index.tsx @@ -1,15 +1,15 @@ +import type { + Node, + NodeOutPutVar, +} from '@/app/components/workflow/types' import { memo, } from 'react' import { useTranslation } from 'react-i18next' import PromptEditor from '@/app/components/base/prompt-editor' -import Placeholder from './placeholder' -import type { - Node, - NodeOutPutVar, -} from '@/app/components/workflow/types' import { BlockEnum } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' +import Placeholder from './placeholder' type MixedVariableTextInputProps = { readOnly?: boolean @@ -33,7 +33,7 @@ const MixedVariableTextInput = ({ 'hover:border-components-input-border-hover hover:bg-components-input-bg-hover', 'focus-within:border-components-input-border-active focus-within:bg-components-input-bg-active focus-within:shadow-xs', )} - className='caret:text-text-accent' + className="caret:text-text-accent" editable={!readOnly} value={value} workflowVariableBlock={{ diff --git a/web/app/components/workflow/nodes/_base/components/mixed-variable-text-input/placeholder.tsx b/web/app/components/workflow/nodes/_base/components/mixed-variable-text-input/placeholder.tsx index 75d4c91996..b839f1a426 100644 --- a/web/app/components/workflow/nodes/_base/components/mixed-variable-text-input/placeholder.tsx +++ b/web/app/components/workflow/nodes/_base/components/mixed-variable-text-input/placeholder.tsx @@ -1,10 +1,9 @@ +import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' +import { $insertNodes, FOCUS_COMMAND } from 'lexical' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' -import { FOCUS_COMMAND } from 'lexical' -import { $insertNodes } from 'lexical' -import { CustomTextNode } from '@/app/components/base/prompt-editor/plugins/custom-text/node' import Badge from '@/app/components/base/badge' +import { CustomTextNode } from '@/app/components/base/prompt-editor/plugins/custom-text/node' const Placeholder = () => { const { t } = useTranslation() @@ -20,17 +19,17 @@ const Placeholder = () => { return ( <div - className='pointer-events-auto flex h-full w-full cursor-text items-center px-2' + className="pointer-events-auto flex h-full w-full cursor-text items-center px-2" onClick={(e) => { e.stopPropagation() handleInsert('') }} > - <div className='flex grow items-center'> + <div className="flex grow items-center"> {t('workflow.nodes.tool.insertPlaceholder1')} - <div className='system-kbd mx-0.5 flex h-4 w-4 items-center justify-center rounded bg-components-kbd-bg-gray text-text-placeholder'>/</div> + <div className="system-kbd mx-0.5 flex h-4 w-4 items-center justify-center rounded bg-components-kbd-bg-gray text-text-placeholder">/</div> <div - className='system-sm-regular cursor-pointer text-components-input-text-placeholder underline decoration-dotted decoration-auto underline-offset-auto hover:text-text-tertiary' + className="system-sm-regular cursor-pointer text-components-input-text-placeholder underline decoration-dotted decoration-auto underline-offset-auto hover:text-text-tertiary" onMouseDown={((e) => { e.preventDefault() e.stopPropagation() @@ -41,8 +40,8 @@ const Placeholder = () => { </div> </div> <Badge - className='shrink-0' - text='String' + className="shrink-0" + text="String" uppercase={false} /> </div> diff --git a/web/app/components/workflow/nodes/_base/components/next-step/add.tsx b/web/app/components/workflow/nodes/_base/components/next-step/add.tsx index 3001274c31..6395d0b3ca 100644 --- a/web/app/components/workflow/nodes/_base/components/next-step/add.tsx +++ b/web/app/components/workflow/nodes/_base/components/next-step/add.tsx @@ -1,3 +1,10 @@ +import type { + CommonNodeType, + OnSelectBlock, +} from '@/app/components/workflow/types' +import { + RiAddLine, +} from '@remixicon/react' import { memo, useCallback, @@ -5,19 +12,12 @@ import { useState, } from 'react' import { useTranslation } from 'react-i18next' -import { - RiAddLine, -} from '@remixicon/react' +import BlockSelector from '@/app/components/workflow/block-selector' import { useAvailableBlocks, useNodesInteractions, useNodesReadOnly, } from '@/app/components/workflow/hooks' -import BlockSelector from '@/app/components/workflow/block-selector' -import type { - CommonNodeType, - OnSelectBlock, -} from '@/app/components/workflow/types' type AddProps = { nodeId: string @@ -75,10 +75,10 @@ const Add = ({ ${nodesReadOnly && '!cursor-not-allowed'} `} > - <div className='mr-1.5 flex h-5 w-5 items-center justify-center rounded-[5px] bg-background-default-dimmed'> - <RiAddLine className='h-3 w-3' /> + <div className="mr-1.5 flex h-5 w-5 items-center justify-center rounded-[5px] bg-background-default-dimmed"> + <RiAddLine className="h-3 w-3" /> </div> - <div className='flex items-center uppercase'> + <div className="flex items-center uppercase"> {tip} </div> </div> @@ -91,10 +91,10 @@ const Add = ({ onOpenChange={handleOpenChange} disabled={nodesReadOnly} onSelect={handleSelect} - placement='top' + placement="top" offset={0} trigger={renderTrigger} - popupClassName='!w-[328px]' + popupClassName="!w-[328px]" availableBlocksTypes={availableNextBlocks} /> ) diff --git a/web/app/components/workflow/nodes/_base/components/next-step/container.tsx b/web/app/components/workflow/nodes/_base/components/next-step/container.tsx index 5971ec8598..3fa295a10e 100644 --- a/web/app/components/workflow/nodes/_base/components/next-step/container.tsx +++ b/web/app/components/workflow/nodes/_base/components/next-step/container.tsx @@ -1,10 +1,10 @@ -import Add from './add' -import Item from './item' import type { CommonNodeType, Node, } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' +import Add from './add' +import Item from './item' type ContainerProps = { nodeId: string @@ -27,7 +27,8 @@ const Container = ({ <div className={cn( 'space-y-0.5 rounded-[10px] bg-background-section-burn p-0.5', isFailBranch && 'border-[0.5px] border-state-warning-hover-alt bg-state-warning-hover', - )}> + )} + > { branchName && ( <div @@ -47,7 +48,7 @@ const Container = ({ key={nextNode.id} nodeId={nextNode.id} data={nextNode.data} - sourceHandle='source' + sourceHandle="source" /> )) } diff --git a/web/app/components/workflow/nodes/_base/components/next-step/index.tsx b/web/app/components/workflow/nodes/_base/components/next-step/index.tsx index 25d1a0aa63..0f23161261 100644 --- a/web/app/components/workflow/nodes/_base/components/next-step/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/next-step/index.tsx @@ -1,21 +1,21 @@ +import type { + Node, +} from '../../../../types' +import { isEqual } from 'lodash-es' import { memo, useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { isEqual } from 'lodash-es' import { getConnectedEdges, getOutgoers, useStore, } from 'reactflow' -import { useToolIcon } from '../../../../hooks' -import BlockIcon from '../../../../block-icon' -import type { - Node, -} from '../../../../types' -import { BlockEnum } from '../../../../types' -import Line from './line' -import Container from './container' -import { hasErrorHandleNode } from '@/app/components/workflow/utils' import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' +import { hasErrorHandleNode } from '@/app/components/workflow/utils' +import BlockIcon from '../../../../block-icon' +import { useToolIcon } from '../../../../hooks' +import { BlockEnum } from '../../../../types' +import Container from './container' +import Line from './line' type NextStepProps = { selectedNode: Node @@ -89,8 +89,8 @@ const NextStep = ({ }, [branches, connectedEdges, data.error_strategy, data.type, outgoers, t]) return ( - <div className='flex py-1'> - <div className='relative flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border-[0.5px] border-divider-regular bg-background-default shadow-xs'> + <div className="flex py-1"> + <div className="relative flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border-[0.5px] border-divider-regular bg-background-default shadow-xs"> <BlockIcon type={selectedNode!.data.type} toolIcon={toolIcon} @@ -99,7 +99,7 @@ const NextStep = ({ <Line list={list.length ? list.map(item => item.nextNodes.length + 1) : [1]} /> - <div className='grow space-y-2'> + <div className="grow space-y-2"> { list.map((item, index) => { return ( diff --git a/web/app/components/workflow/nodes/_base/components/next-step/item.tsx b/web/app/components/workflow/nodes/_base/components/next-step/item.tsx index d9998fd226..0b8a09aa87 100644 --- a/web/app/components/workflow/nodes/_base/components/next-step/item.tsx +++ b/web/app/components/workflow/nodes/_base/components/next-step/item.tsx @@ -1,21 +1,21 @@ +import type { + CommonNodeType, +} from '@/app/components/workflow/types' import { memo, useCallback, useState, } from 'react' import { useTranslation } from 'react-i18next' -import Operator from './operator' -import type { - CommonNodeType, -} from '@/app/components/workflow/types' +import Button from '@/app/components/base/button' import BlockIcon from '@/app/components/workflow/block-icon' import { useNodesInteractions, useNodesReadOnly, useToolIcon, } from '@/app/components/workflow/hooks' -import Button from '@/app/components/base/button' import { cn } from '@/utils/classnames' +import Operator from './operator' type ItemProps = { nodeId: string @@ -39,15 +39,15 @@ const Item = ({ return ( <div - className='group relative flex h-9 cursor-pointer items-center rounded-lg border-[0.5px] border-divider-regular bg-background-default px-2 text-xs text-text-secondary shadow-xs last-of-type:mb-0 hover:bg-background-default-hover' + className="group relative flex h-9 cursor-pointer items-center rounded-lg border-[0.5px] border-divider-regular bg-background-default px-2 text-xs text-text-secondary shadow-xs last-of-type:mb-0 hover:bg-background-default-hover" > <BlockIcon type={data.type} toolIcon={toolIcon} - className='mr-1.5 shrink-0' + className="mr-1.5 shrink-0" /> <div - className='system-xs-medium grow truncate text-text-secondary' + className="system-xs-medium grow truncate text-text-secondary" title={data.title} > {data.title} @@ -56,8 +56,8 @@ const Item = ({ !nodesReadOnly && ( <> <Button - className='mr-1 hidden shrink-0 group-hover:flex' - size='small' + className="mr-1 hidden shrink-0 group-hover:flex" + size="small" onClick={() => handleNodeSelect(nodeId)} > {t('workflow.common.jumpToNode')} diff --git a/web/app/components/workflow/nodes/_base/components/next-step/line.tsx b/web/app/components/workflow/nodes/_base/components/next-step/line.tsx index 35b0a73ff2..862657e34b 100644 --- a/web/app/components/workflow/nodes/_base/components/next-step/line.tsx +++ b/web/app/components/workflow/nodes/_base/components/next-step/line.tsx @@ -19,7 +19,7 @@ const Line = ({ const svgHeight = processedList[processedListLength - 1] + (processedListLength - 1) * 8 return ( - <svg className='w-6 shrink-0' style={{ height: svgHeight }}> + <svg className="w-6 shrink-0" style={{ height: svgHeight }}> { processedList.map((item, index) => { const prevItem = index > 0 ? processedList[index - 1] : 0 @@ -30,17 +30,17 @@ const Line = ({ index === 0 && ( <> <path - d='M0,18 L24,18' + d="M0,18 L24,18" strokeWidth={1} - fill='none' - className='stroke-divider-solid' + fill="none" + className="stroke-divider-solid" /> <rect x={0} y={16} width={1} height={4} - className='fill-divider-solid-alt' + className="fill-divider-solid-alt" /> </> ) @@ -50,8 +50,8 @@ const Line = ({ <path d={`M0,18 Q12,18 12,28 L12,${space - 10 + 2} Q12,${space + 2} 24,${space + 2}`} strokeWidth={1} - fill='none' - className='stroke-divider-solid' + fill="none" + className="stroke-divider-solid" /> ) } @@ -60,7 +60,7 @@ const Line = ({ y={space} width={1} height={4} - className='fill-divider-solid-alt' + className="fill-divider-solid-alt" /> </g> ) diff --git a/web/app/components/workflow/nodes/_base/components/next-step/operator.tsx b/web/app/components/workflow/nodes/_base/components/next-step/operator.tsx index 7143e6fe43..1b550f035d 100644 --- a/web/app/components/workflow/nodes/_base/components/next-step/operator.tsx +++ b/web/app/components/workflow/nodes/_base/components/next-step/operator.tsx @@ -1,24 +1,24 @@ +import type { + CommonNodeType, + OnSelectBlock, +} from '@/app/components/workflow/types' +import { RiMoreFill } from '@remixicon/react' +import { intersection } from 'lodash-es' import { useCallback, } from 'react' import { useTranslation } from 'react-i18next' -import { RiMoreFill } from '@remixicon/react' -import { intersection } from 'lodash-es' +import Button from '@/app/components/base/button' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import Button from '@/app/components/base/button' import BlockSelector from '@/app/components/workflow/block-selector' import { useAvailableBlocks, useNodesInteractions, } from '@/app/components/workflow/hooks' -import type { - CommonNodeType, - OnSelectBlock, -} from '@/app/components/workflow/types' type ChangeItemProps = { data: CommonNodeType @@ -44,7 +44,7 @@ const ChangeItem = ({ const renderTrigger = useCallback(() => { return ( - <div className='flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover'> + <div className="flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover"> {t('workflow.panel.change')} </div> ) @@ -53,13 +53,13 @@ const ChangeItem = ({ return ( <BlockSelector onSelect={handleSelect} - placement='top-end' + placement="top-end" offset={{ mainAxis: 6, crossAxis: 8, }} trigger={renderTrigger} - popupClassName='!w-[328px]' + popupClassName="!w-[328px]" availableBlocksTypes={intersection(availablePrevBlocks, availableNextBlocks).filter(item => item !== data.type)} /> ) @@ -87,34 +87,34 @@ const Operator = ({ return ( <PortalToFollowElem - placement='bottom-end' + placement="bottom-end" offset={{ mainAxis: 4, crossAxis: -4 }} open={open} onOpenChange={onOpenChange} > <PortalToFollowElemTrigger onClick={() => onOpenChange(!open)}> - <Button className='h-6 w-6 p-0'> - <RiMoreFill className='h-4 w-4' /> + <Button className="h-6 w-6 p-0"> + <RiMoreFill className="h-4 w-4" /> </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-10'> - <div className='system-md-regular min-w-[120px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur text-text-secondary shadow-lg'> - <div className='p-1'> + <PortalToFollowElemContent className="z-10"> + <div className="system-md-regular min-w-[120px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur text-text-secondary shadow-lg"> + <div className="p-1"> <ChangeItem data={data} nodeId={nodeId} sourceHandle={sourceHandle} /> <div - className='flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover" onClick={() => handleNodeDisconnect(nodeId)} > {t('workflow.common.disconnect')} </div> </div> - <div className='p-1'> + <div className="p-1"> <div - className='flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover" onClick={() => handleNodeDelete(nodeId)} > {t('common.operation.delete')} 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 2a52737bbd..da99528622 100644 --- a/web/app/components/workflow/nodes/_base/components/node-control.tsx +++ b/web/app/components/workflow/nodes/_base/components/node-control.tsx @@ -1,24 +1,25 @@ import type { FC } from 'react' +import type { Node } from '../../../types' +import { + RiPlayLargeLine, +} from '@remixicon/react' import { memo, useCallback, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { - RiPlayLargeLine, -} from '@remixicon/react' -import { - useNodesInteractions, -} from '../../../hooks' -import { type Node, NodeRunningStatus } from '../../../types' -import { canRunBySingle } from '../../../utils' -import PanelOperator from './panel-operator' import { Stop, } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' import Tooltip from '@/app/components/base/tooltip' import { useWorkflowStore } from '@/app/components/workflow/store' +import { + useNodesInteractions, +} from '../../../hooks' +import { NodeRunningStatus } from '../../../types' +import { canRunBySingle } from '../../../utils' +import PanelOperator from './panel-operator' type NodeControlProps = Pick<Node, 'id' | 'data'> const NodeControl: FC<NodeControlProps> = ({ @@ -45,7 +46,7 @@ const NodeControl: FC<NodeControlProps> = ({ `} > <div - className='flex h-6 items-center rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg px-0.5 text-text-tertiary shadow-md backdrop-blur-[5px]' + className="flex h-6 items-center rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg px-0.5 text-text-tertiary shadow-md backdrop-blur-[5px]" onClick={e => e.stopPropagation()} > { @@ -66,15 +67,15 @@ const NodeControl: FC<NodeControlProps> = ({ > { isSingleRunning - ? <Stop className='h-3 w-3' /> + ? <Stop className="h-3 w-3" /> : ( - <Tooltip - popupContent={t('workflow.panel.runThisStep')} - asChild={false} - > - <RiPlayLargeLine className='h-3 w-3' /> - </Tooltip> - ) + <Tooltip + popupContent={t('workflow.panel.runThisStep')} + asChild={false} + > + <RiPlayLargeLine className="h-3 w-3" /> + </Tooltip> + ) } </div> ) @@ -84,7 +85,7 @@ const NodeControl: FC<NodeControlProps> = ({ data={data} offset={0} onOpenChange={handleOpenChange} - triggerClassName='!w-5 !h-5' + triggerClassName="!w-5 !h-5" /> </div> </div> diff --git a/web/app/components/workflow/nodes/_base/components/node-handle.tsx b/web/app/components/workflow/nodes/_base/components/node-handle.tsx index 5b46e79616..85a4b18188 100644 --- a/web/app/components/workflow/nodes/_base/components/node-handle.tsx +++ b/web/app/components/workflow/nodes/_base/components/node-handle.tsx @@ -1,22 +1,19 @@ import type { MouseEvent } from 'react' +import type { PluginDefaultValue } from '../../../block-selector/types' +import type { Node } from '../../../types' import { memo, useCallback, useEffect, useState, } from 'react' +import { useTranslation } from 'react-i18next' import { Handle, Position, } from 'reactflow' -import { useTranslation } from 'react-i18next' -import { - BlockEnum, - NodeRunningStatus, -} from '../../../types' -import type { Node } from '../../../types' +import { cn } from '@/utils/classnames' import BlockSelector from '../../../block-selector' -import type { PluginDefaultValue } from '../../../block-selector/types' import { useAvailableBlocks, useIsChatMode, @@ -27,7 +24,10 @@ import { useStore, useWorkflowStore, } from '../../../store' -import { cn } from '@/utils/classnames' +import { + BlockEnum, + NodeRunningStatus, +} from '../../../types' type NodeHandleProps = { handleId: string @@ -75,7 +75,7 @@ export const NodeTargetHandle = memo(({ <> <Handle id={handleId} - type='target' + type="target" position={Position.Left} className={cn( 'z-[1] !h-4 !w-4 !rounded-none !border-none !bg-transparent !outline-none', @@ -101,7 +101,7 @@ export const NodeTargetHandle = memo(({ onOpenChange={handleOpenChange} onSelect={handleSelect} asChild - placement='left' + placement="left" triggerClassName={open => ` hidden absolute left-0 top-0 pointer-events-none ${nodeSelectorClassName} @@ -186,7 +186,7 @@ export const NodeSourceHandle = memo(({ return ( <Handle id={handleId} - type='source' + type="source" position={Position.Right} className={cn( 'group/handle z-[1] !h-4 !w-4 !rounded-none !border-none !bg-transparent !outline-none', @@ -201,14 +201,14 @@ export const NodeSourceHandle = memo(({ isConnectable={isConnectable} onClick={handleHandleClick} > - <div className='absolute -top-1 left-1/2 hidden -translate-x-1/2 -translate-y-full rounded-lg border-[0.5px] border-components-panel-border bg-components-tooltip-bg p-1.5 shadow-lg group-hover/handle:block'> - <div className='system-xs-regular text-text-tertiary'> - <div className=' whitespace-nowrap'> - <span className='system-xs-medium text-text-secondary'>{t('workflow.common.parallelTip.click.title')}</span> + <div className="absolute -top-1 left-1/2 hidden -translate-x-1/2 -translate-y-full rounded-lg border-[0.5px] border-components-panel-border bg-components-tooltip-bg p-1.5 shadow-lg group-hover/handle:block"> + <div className="system-xs-regular text-text-tertiary"> + <div className=" whitespace-nowrap"> + <span className="system-xs-medium text-text-secondary">{t('workflow.common.parallelTip.click.title')}</span> {t('workflow.common.parallelTip.click.desc')} </div> <div> - <span className='system-xs-medium text-text-secondary'>{t('workflow.common.parallelTip.drag.title')}</span> + <span className="system-xs-medium text-text-secondary">{t('workflow.common.parallelTip.drag.title')}</span> {t('workflow.common.parallelTip.drag.desc')} </div> </div> diff --git a/web/app/components/workflow/nodes/_base/components/node-resizer.tsx b/web/app/components/workflow/nodes/_base/components/node-resizer.tsx index 479e1ad56e..db55e1e533 100644 --- a/web/app/components/workflow/nodes/_base/components/node-resizer.tsx +++ b/web/app/components/workflow/nodes/_base/components/node-resizer.tsx @@ -1,12 +1,12 @@ +import type { OnResize } from 'reactflow' +import type { CommonNodeType } from '../../../types' import { memo, useCallback, } from 'react' -import type { OnResize } from 'reactflow' import { NodeResizeControl } from 'reactflow' -import { useNodesInteractions } from '../../../hooks' -import type { CommonNodeType } from '../../../types' import { cn } from '@/utils/classnames' +import { useNodesInteractions } from '../../../hooks' const Icon = () => { return ( @@ -42,16 +42,17 @@ const NodeResizer = ({ <div className={cn( 'hidden group-hover:block', nodeData.selected && '!block', - )}> + )} + > <NodeResizeControl - position='bottom-right' - className='!border-none !bg-transparent' + position="bottom-right" + className="!border-none !bg-transparent" onResize={handleResize} minWidth={minWidth} minHeight={minHeight} maxWidth={maxWidth} > - <div className='absolute bottom-[1px] right-[1px]'>{icon}</div> + <div className="absolute bottom-[1px] right-[1px]">{icon}</div> </NodeResizeControl> </div> ) diff --git a/web/app/components/workflow/nodes/_base/components/option-card.tsx b/web/app/components/workflow/nodes/_base/components/option-card.tsx index ebbdc92b2d..7c3a06daeb 100644 --- a/web/app/components/workflow/nodes/_base/components/option-card.tsx +++ b/web/app/components/workflow/nodes/_base/components/option-card.tsx @@ -1,10 +1,10 @@ 'use client' -import type { FC } from 'react' -import React, { useCallback } from 'react' import type { VariantProps } from 'class-variance-authority' +import type { FC } from 'react' import { cva } from 'class-variance-authority' -import { cn } from '@/utils/classnames' +import React, { useCallback } from 'react' import Tooltip from '@/app/components/base/tooltip' +import { cn } from '@/utils/classnames' const variants = cva([], { variants: { @@ -17,8 +17,7 @@ const variants = cva([], { defaultVariants: { align: 'center', }, -}, -) +}) type Props = { className?: string @@ -59,14 +58,15 @@ const OptionCard: FC<Props> = ({ > <span>{title}</span> {tooltip - && <Tooltip - popupContent={ - <div className='w-[240px]'> - {tooltip} - </div> - } - /> - } + && ( + <Tooltip + popupContent={( + <div className="w-[240px]"> + {tooltip} + </div> + )} + /> + )} </div> ) } diff --git a/web/app/components/workflow/nodes/_base/components/output-vars.tsx b/web/app/components/workflow/nodes/_base/components/output-vars.tsx index b1599ce541..2c646148b3 100644 --- a/web/app/components/workflow/nodes/_base/components/output-vars.tsx +++ b/web/app/components/workflow/nodes/_base/components/output-vars.tsx @@ -3,8 +3,8 @@ import type { FC, ReactNode } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse' -import TreeIndentLine from './variable/object-child-tree-panel/tree-indent-line' import { cn } from '@/utils/classnames' +import TreeIndentLine from './variable/object-child-tree-panel/tree-indent-line' type Props = { className?: string @@ -56,17 +56,17 @@ export const VarItem: FC<VarItemProps> = ({ return ( <div className={cn('flex', isIndent && 'relative left-[-7px]')}> {isIndent && <TreeIndentLine depth={1} />} - <div className='py-1'> - <div className='flex'> - <div className='flex items-center leading-[18px]'> - <div className='code-sm-semibold text-text-secondary'>{name}</div> - <div className='system-xs-regular ml-2 text-text-tertiary'>{type}</div> + <div className="py-1"> + <div className="flex"> + <div className="flex items-center leading-[18px]"> + <div className="code-sm-semibold text-text-secondary">{name}</div> + <div className="system-xs-regular ml-2 text-text-tertiary">{type}</div> </div> </div> - <div className='system-xs-regular mt-0.5 text-text-tertiary'> + <div className="system-xs-regular mt-0.5 text-text-tertiary"> {description} {subItems && ( - <div className='ml-2 border-l border-gray-200 pl-2'> + <div className="ml-2 border-l border-gray-200 pl-2"> {subItems.map((item, index) => ( <VarItem key={index} diff --git a/web/app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx b/web/app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx index 8b6d137127..47e1c4b22b 100644 --- a/web/app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx +++ b/web/app/components/workflow/nodes/_base/components/panel-operator/change-block.tsx @@ -1,10 +1,14 @@ +import type { + Node, + OnSelectBlock, +} from '@/app/components/workflow/types' +import { intersection } from 'lodash-es' import { memo, useCallback, useMemo, } from 'react' import { useTranslation } from 'react-i18next' -import { intersection } from 'lodash-es' import BlockSelector from '@/app/components/workflow/block-selector' import { useAvailableBlocks, @@ -12,10 +16,6 @@ import { useNodesInteractions, } from '@/app/components/workflow/hooks' import { useHooksStore } from '@/app/components/workflow/hooks-store' -import type { - Node, - OnSelectBlock, -} from '@/app/components/workflow/types' import { BlockEnum, isTriggerNode } from '@/app/components/workflow/types' import { FlowType } from '@/types/common' @@ -60,7 +60,7 @@ const ChangeBlock = ({ const renderTrigger = useCallback(() => { return ( - <div className='flex h-8 w-[232px] cursor-pointer items-center rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover'> + <div className="flex h-8 w-[232px] cursor-pointer items-center rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover"> {t('workflow.panel.changeBlock')} </div> ) @@ -68,14 +68,14 @@ const ChangeBlock = ({ return ( <BlockSelector - placement='bottom-end' + placement="bottom-end" offset={{ mainAxis: -36, crossAxis: 4, }} onSelect={handleSelect} trigger={renderTrigger} - popupClassName='min-w-[240px]' + popupClassName="min-w-[240px]" availableBlocksTypes={availableNodes} showStartTab={showStartTab} ignoreNodeIds={ignoreNodeIds} diff --git a/web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx b/web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx index e1af8cc84f..665b1441b0 100644 --- a/web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx @@ -1,17 +1,17 @@ +import type { OffsetOptions } from '@floating-ui/react' +import type { Node } from '@/app/components/workflow/types' +import { RiMoreFill } from '@remixicon/react' import { memo, useCallback, useState, } from 'react' -import { RiMoreFill } from '@remixicon/react' -import type { OffsetOptions } from '@floating-ui/react' -import PanelOperatorPopup from './panel-operator-popup' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import type { Node } from '@/app/components/workflow/types' +import PanelOperatorPopup from './panel-operator-popup' type PanelOperatorProps = { id: string @@ -44,7 +44,7 @@ const PanelOperator = ({ return ( <PortalToFollowElem - placement='bottom-end' + placement="bottom-end" offset={offset} open={open} onOpenChange={handleOpenChange} @@ -58,10 +58,10 @@ const PanelOperator = ({ ${triggerClassName} `} > - <RiMoreFill className={'h-4 w-4 text-text-tertiary'} /> + <RiMoreFill className="h-4 w-4 text-text-tertiary" /> </div> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[11]'> + <PortalToFollowElemContent className="z-[11]"> <PanelOperatorPopup id={id} data={data} diff --git a/web/app/components/workflow/nodes/_base/components/panel-operator/panel-operator-popup.tsx b/web/app/components/workflow/nodes/_base/components/panel-operator/panel-operator-popup.tsx index 613744a50e..43419dc957 100644 --- a/web/app/components/workflow/nodes/_base/components/panel-operator/panel-operator-popup.tsx +++ b/web/app/components/workflow/nodes/_base/components/panel-operator/panel-operator-popup.tsx @@ -1,13 +1,11 @@ +import type { Node } from '@/app/components/workflow/types' import { memo, useMemo, } from 'react' import { useTranslation } from 'react-i18next' import { useEdges } from 'reactflow' -import ChangeBlock from './change-block' -import { - canRunBySingle, -} from '@/app/components/workflow/utils' +import { CollectionType } from '@/app/components/tools/types' import { useNodeDataUpdate, useNodeMetaData, @@ -16,11 +14,13 @@ import { useNodesSyncDraft, } from '@/app/components/workflow/hooks' import ShortcutsName from '@/app/components/workflow/shortcuts-name' -import type { Node } from '@/app/components/workflow/types' import { BlockEnum } from '@/app/components/workflow/types' -import { CollectionType } from '@/app/components/tools/types' +import { + canRunBySingle, +} from '@/app/components/workflow/utils' import { useAllWorkflowTools } from '@/service/use-tools' import { canFindTool } from '@/utils' +import ChangeBlock from './change-block' type PanelOperatorPopupProps = { id: string @@ -53,17 +53,18 @@ const PanelOperatorPopup = ({ const { data: workflowTools } = useAllWorkflowTools() const isWorkflowTool = data.type === BlockEnum.Tool && data.provider_type === CollectionType.workflow const workflowAppId = useMemo(() => { - if (!isWorkflowTool || !workflowTools || !data.provider_id) return undefined + if (!isWorkflowTool || !workflowTools || !data.provider_id) + return undefined const workflowTool = workflowTools.find(item => canFindTool(item.id, data.provider_id)) return workflowTool?.workflow_app_id }, [isWorkflowTool, workflowTools, data.provider_id]) return ( - <div className='w-[240px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl'> + <div className="w-[240px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl"> { (showChangeBlock || canRunBySingle(data.type, isChildNode)) && ( <> - <div className='p-1'> + <div className="p-1"> { canRunBySingle(data.type, isChildNode) && ( <div @@ -92,7 +93,7 @@ const PanelOperatorPopup = ({ ) } </div> - <div className='h-px bg-divider-regular'></div> + <div className="h-px bg-divider-regular"></div> </> ) } @@ -102,9 +103,9 @@ const PanelOperatorPopup = ({ { !nodeMetaData.isSingleton && ( <> - <div className='p-1'> + <div className="p-1"> <div - className='flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => { onClosePopup() handleNodesCopy(id) @@ -114,7 +115,7 @@ const PanelOperatorPopup = ({ <ShortcutsName keys={['ctrl', 'c']} /> </div> <div - className='flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => { onClosePopup() handleNodesDuplicate(id) @@ -124,14 +125,14 @@ const PanelOperatorPopup = ({ <ShortcutsName keys={['ctrl', 'd']} /> </div> </div> - <div className='h-px bg-divider-regular'></div> + <div className="h-px bg-divider-regular"></div> </> ) } { !nodeMetaData.isUndeletable && ( <> - <div className='p-1'> + <div className="p-1"> <div className={` flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary @@ -143,7 +144,7 @@ const PanelOperatorPopup = ({ <ShortcutsName keys={['del']} /> </div> </div> - <div className='h-px bg-divider-regular'></div> + <div className="h-px bg-divider-regular"></div> </> ) } @@ -153,43 +154,45 @@ const PanelOperatorPopup = ({ { isWorkflowTool && workflowAppId && ( <> - <div className='p-1'> + <div className="p-1"> <a href={`/app/${workflowAppId}/workflow`} - target='_blank' - className='flex h-8 cursor-pointer items-center rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + target="_blank" + className="flex h-8 cursor-pointer items-center rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" > {t('workflow.panel.openWorkflow')} </a> </div> - <div className='h-px bg-divider-regular'></div> + <div className="h-px bg-divider-regular"></div> </> ) } { showHelpLink && nodeMetaData.helpLinkUri && ( <> - <div className='p-1'> + <div className="p-1"> <a href={nodeMetaData.helpLinkUri} - target='_blank' - className='flex h-8 cursor-pointer items-center rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + target="_blank" + className="flex h-8 cursor-pointer items-center rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" > {t('workflow.panel.helpLink')} </a> </div> - <div className='h-px bg-divider-regular'></div> + <div className="h-px bg-divider-regular"></div> </> ) } - <div className='p-1'> - <div className='px-3 py-2 text-xs text-text-tertiary'> - <div className='mb-1 flex h-[22px] items-center font-medium'> + <div className="p-1"> + <div className="px-3 py-2 text-xs text-text-tertiary"> + <div className="mb-1 flex h-[22px] items-center font-medium"> {t('workflow.panel.about').toLocaleUpperCase()} </div> - <div className='mb-1 leading-[18px] text-text-secondary'>{nodeMetaData.description}</div> - <div className='leading-[18px]'> - {t('workflow.panel.createdBy')} {nodeMetaData.author} + <div className="mb-1 leading-[18px] text-text-secondary">{nodeMetaData.description}</div> + <div className="leading-[18px]"> + {t('workflow.panel.createdBy')} + {' '} + {nodeMetaData.author} </div> </div> </div> diff --git a/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx b/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx index ff0e7c90b2..c2bc6481ff 100644 --- a/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx +++ b/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx @@ -1,41 +1,41 @@ 'use client' import type { FC, ReactNode } from 'react' -import React, { useCallback, useRef } from 'react' -import { - RiDeleteBinLine, -} from '@remixicon/react' -import copy from 'copy-to-clipboard' -import { useTranslation } from 'react-i18next' -import { useBoolean } from 'ahooks' -import { BlockEnum, EditionType } from '../../../../types' import type { ModelConfig, Node, NodeOutPutVar, Variable, } from '../../../../types' +import { + RiDeleteBinLine, +} from '@remixicon/react' +import { useBoolean } from 'ahooks' +import copy from 'copy-to-clipboard' +import React, { useCallback, useRef } from 'react' +import { useTranslation } from 'react-i18next' +import ActionButton from '@/app/components/base/action-button' -import Wrap from '../editor/wrap' -import { CodeLanguage } from '../../../code/types' -import PromptGeneratorBtn from '../../../llm/components/prompt-generator-btn' -import { cn } from '@/utils/classnames' -import ToggleExpandBtn from '@/app/components/workflow/nodes/_base/components/toggle-expand-btn' -import useToggleExpend from '@/app/components/workflow/nodes/_base/hooks/use-toggle-expend' -import PromptEditor from '@/app/components/base/prompt-editor' import { Copy, CopyCheck, } from '@/app/components/base/icons/src/vender/line/files' -import { useEventEmitterContextContext } from '@/context/event-emitter' -import { PROMPT_EDITOR_INSERT_QUICKLY } from '@/app/components/base/prompt-editor/plugins/update-block' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' -import ActionButton from '@/app/components/base/action-button' -import Tooltip from '@/app/components/base/tooltip' -import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars' -import Switch from '@/app/components/base/switch' import { Jinja } from '@/app/components/base/icons/src/vender/workflow' -import { useStore } from '@/app/components/workflow/store' +import PromptEditor from '@/app/components/base/prompt-editor' +import { PROMPT_EDITOR_INSERT_QUICKLY } from '@/app/components/base/prompt-editor/plugins/update-block' +import Switch from '@/app/components/base/switch' +import Tooltip from '@/app/components/base/tooltip' import { useWorkflowVariableType } from '@/app/components/workflow/hooks' +import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars' +import ToggleExpandBtn from '@/app/components/workflow/nodes/_base/components/toggle-expand-btn' +import useToggleExpend from '@/app/components/workflow/nodes/_base/hooks/use-toggle-expend' +import { useStore } from '@/app/components/workflow/store' +import { useEventEmitterContextContext } from '@/context/event-emitter' +import { cn } from '@/utils/classnames' +import { BlockEnum, EditionType } from '../../../../types' +import { CodeLanguage } from '../../../code/types' +import PromptGeneratorBtn from '../../../llm/components/prompt-generator-btn' +import Wrap from '../editor/wrap' type Props = { className?: string @@ -158,39 +158,43 @@ const Editor: FC<Props> = ({ <div ref={ref} className={cn(isFocus ? (gradientBorder && 'bg-gradient-to-r from-components-input-border-active-prompt-1 to-components-input-border-active-prompt-2') : 'bg-transparent', isExpand && 'h-full', '!rounded-[9px] p-0.5', containerClassName)}> <div className={cn(isFocus ? 'bg-background-default' : 'bg-components-input-bg-normal', isExpand && 'flex h-full flex-col', 'rounded-lg', containerClassName)}> <div className={cn('flex items-center justify-between pl-3 pr-2 pt-1', headerClassName)}> - <div className='flex gap-2'> - <div className={cn('text-xs font-semibold uppercase leading-4 text-text-secondary', titleClassName)}>{title} {required && <span className='text-text-destructive'>*</span>}</div> + <div className="flex gap-2"> + <div className={cn('text-xs font-semibold uppercase leading-4 text-text-secondary', titleClassName)}> + {title} + {' '} + {required && <span className="text-text-destructive">*</span>} + </div> {titleTooltip && <Tooltip popupContent={titleTooltip} />} </div> - <div className='flex items-center'> - <div className='text-xs font-medium leading-[18px] text-text-tertiary'>{value?.length || 0}</div> + <div className="flex items-center"> + <div className="text-xs font-medium leading-[18px] text-text-tertiary">{value?.length || 0}</div> {isSupportPromptGenerator && ( <PromptGeneratorBtn nodeId={nodeId!} editorId={editorId} - className='ml-[5px]' + className="ml-[5px]" onGenerated={onGenerated} modelConfig={modelConfig} currentPrompt={value} /> )} - <div className='ml-2 mr-2 h-3 w-px bg-divider-regular'></div> + <div className="ml-2 mr-2 h-3 w-px bg-divider-regular"></div> {/* Operations */} - <div className='flex items-center space-x-[2px]'> + <div className="flex items-center space-x-[2px]"> {isSupportJinja && ( <Tooltip - popupContent={ + popupContent={( <div> <div>{t('workflow.common.enableJinja')}</div> - <a className='text-text-accent' target='_blank' href='https://jinja.palletsprojects.com/en/2.10.x/'>{t('workflow.common.learnMore')}</a> + <a className="text-text-accent" target="_blank" href="https://jinja.palletsprojects.com/en/2.10.x/">{t('workflow.common.learnMore')}</a> </div> - } + )} > <div className={cn(editionType === EditionType.jinja2 && 'border-components-button-ghost-bg-hover bg-components-button-ghost-bg-hover', 'flex h-[22px] items-center space-x-0.5 rounded-[5px] border border-transparent px-1.5 hover:border-components-button-ghost-bg-hover')}> - <Jinja className='h-3 w-6 text-text-quaternary' /> + <Jinja className="h-3 w-6 text-text-quaternary" /> <Switch - size='sm' + size="sm" defaultValue={editionType === EditionType.jinja2} onChange={(checked) => { onEditionTypeChange?.(checked ? EditionType.jinja2 : EditionType.basic) @@ -205,27 +209,26 @@ const Editor: FC<Props> = ({ popupContent={`${t('workflow.common.insertVarTip')}`} > <ActionButton onClick={handleInsertVariable}> - <Variable02 className='h-4 w-4' /> + <Variable02 className="h-4 w-4" /> </ActionButton> </Tooltip> )} {showRemove && ( <ActionButton onClick={onRemove}> - <RiDeleteBinLine className='h-4 w-4' /> + <RiDeleteBinLine className="h-4 w-4" /> </ActionButton> )} {!isCopied ? ( - <ActionButton onClick={handleCopy}> - <Copy className='h-4 w-4' /> - </ActionButton> - ) + <ActionButton onClick={handleCopy}> + <Copy className="h-4 w-4" /> + </ActionButton> + ) : ( - <ActionButton> - <CopyCheck className='h-4 w-4' /> - </ActionButton> - ) - } + <ActionButton> + <CopyCheck className="h-4 w-4" /> + </ActionButton> + )} <ToggleExpandBtn isExpand={isExpand} onExpandChange={setIsExpand} /> </div> @@ -236,83 +239,83 @@ const Editor: FC<Props> = ({ <div className={cn('pb-2', isExpand && 'flex grow flex-col')}> {!(isSupportJinja && editionType === EditionType.jinja2) ? ( - <div className={cn(isExpand ? 'grow' : 'max-h-[536px]', 'relative min-h-[56px] overflow-y-auto px-3', editorContainerClassName)}> - <PromptEditor - key={controlPromptEditorRerenderKey} - placeholder={placeholder} - placeholderClassName={placeholderClassName} - instanceId={instanceId} - compact - className={cn('min-h-[56px]', inputClassName)} - style={isExpand ? { height: editorExpandHeight - 5 } : {}} - value={value} - contextBlock={{ - show: justVar ? false : isShowContext, - selectable: !hasSetBlockStatus?.context, - canNotAddContext: true, - }} - historyBlock={{ - show: justVar ? false : isShowHistory, - selectable: !hasSetBlockStatus?.history, - history: { - user: 'Human', - assistant: 'Assistant', - }, - }} - queryBlock={{ - show: false, // use [sys.query] instead of query block - selectable: false, - }} - workflowVariableBlock={{ - show: true, - variables: nodesOutputVars || [], - getVarType: getVarType as any, - workflowNodesMap: availableNodes.reduce((acc, node) => { - acc[node.id] = { - title: node.data.title, - type: node.data.type, - width: node.width, - height: node.height, - position: node.position, - } - if (node.data.type === BlockEnum.Start) { - acc.sys = { - title: t('workflow.blocks.start'), - type: BlockEnum.Start, + <div className={cn(isExpand ? 'grow' : 'max-h-[536px]', 'relative min-h-[56px] overflow-y-auto px-3', editorContainerClassName)}> + <PromptEditor + key={controlPromptEditorRerenderKey} + placeholder={placeholder} + placeholderClassName={placeholderClassName} + instanceId={instanceId} + compact + className={cn('min-h-[56px]', inputClassName)} + style={isExpand ? { height: editorExpandHeight - 5 } : {}} + value={value} + contextBlock={{ + show: justVar ? false : isShowContext, + selectable: !hasSetBlockStatus?.context, + canNotAddContext: true, + }} + historyBlock={{ + show: justVar ? false : isShowHistory, + selectable: !hasSetBlockStatus?.history, + history: { + user: 'Human', + assistant: 'Assistant', + }, + }} + queryBlock={{ + show: false, // use [sys.query] instead of query block + selectable: false, + }} + workflowVariableBlock={{ + show: true, + variables: nodesOutputVars || [], + getVarType: getVarType as any, + workflowNodesMap: availableNodes.reduce((acc, node) => { + acc[node.id] = { + title: node.data.title, + type: node.data.type, + width: node.width, + height: node.height, + position: node.position, } - } - return acc - }, {} as any), - showManageInputField: !!pipelineId, - onManageInputField: () => setShowInputFieldPanel?.(true), - }} - onChange={onChange} - onBlur={setBlur} - onFocus={setFocus} - editable={!readOnly} - isSupportFileVar={isSupportFileVar} - /> - {/* to patch Editor not support dynamic change editable status */} - {readOnly && <div className='absolute inset-0 z-10'></div>} - </div> - ) + if (node.data.type === BlockEnum.Start) { + acc.sys = { + title: t('workflow.blocks.start'), + type: BlockEnum.Start, + } + } + return acc + }, {} as any), + showManageInputField: !!pipelineId, + onManageInputField: () => setShowInputFieldPanel?.(true), + }} + onChange={onChange} + onBlur={setBlur} + onFocus={setFocus} + editable={!readOnly} + isSupportFileVar={isSupportFileVar} + /> + {/* to patch Editor not support dynamic change editable status */} + {readOnly && <div className="absolute inset-0 z-10"></div>} + </div> + ) : ( - <div className={cn(isExpand ? 'grow' : 'max-h-[536px]', 'relative min-h-[56px] overflow-y-auto px-3', editorContainerClassName)}> - <CodeEditor - availableVars={nodesOutputVars || []} - varList={varList} - onAddVar={handleAddVariable} - isInNode - readOnly={readOnly} - language={CodeLanguage.python3} - value={value} - onChange={onChange} - noWrapper - isExpand={isExpand} - className={inputClassName} - /> - </div> - )} + <div className={cn(isExpand ? 'grow' : 'max-h-[536px]', 'relative min-h-[56px] overflow-y-auto px-3', editorContainerClassName)}> + <CodeEditor + availableVars={nodesOutputVars || []} + varList={varList} + onAddVar={handleAddVariable} + isInNode + readOnly={readOnly} + language={CodeLanguage.python3} + value={value} + onChange={onChange} + noWrapper + isExpand={isExpand} + className={inputClassName} + /> + </div> + )} </div> </div> </div> diff --git a/web/app/components/workflow/nodes/_base/components/readonly-input-with-select-var.tsx b/web/app/components/workflow/nodes/_base/components/readonly-input-with-select-var.tsx index 062ce278e2..fbec61d516 100644 --- a/web/app/components/workflow/nodes/_base/components/readonly-input-with-select-var.tsx +++ b/web/app/components/workflow/nodes/_base/components/readonly-input-with-select-var.tsx @@ -1,13 +1,14 @@ 'use client' import type { FC } from 'react' import React from 'react' +import { + VariableLabelInText, +} from '@/app/components/workflow/nodes/_base/components/variable/variable-label' import { cn } from '@/utils/classnames' import { useWorkflow } from '../../../hooks' import { BlockEnum } from '../../../types' import { getNodeInfoById, isSystemVar } from './variable/utils' -import { - VariableLabelInText, -} from '@/app/components/workflow/nodes/_base/components/variable/variable-label' + type Props = { nodeId: string value: string @@ -29,29 +30,31 @@ const ReadonlyInputWithSelectVar: FC<Props> = ({ const res = (() => { const vars: string[] = [] - const strWithVarPlaceholder = value.replaceAll(/{{#([^#]*)#}}/g, (_match, p1) => { + const strWithVarPlaceholder = value.replaceAll(/\{\{#([^#]*)#\}\}/g, (_match, p1) => { vars.push(p1) return VAR_PLACEHOLDER }) const html: React.JSX.Element[] = strWithVarPlaceholder.split(VAR_PLACEHOLDER).map((str, index) => { if (!vars[index]) - return <span className='relative top-[-3px] leading-[16px]' key={index}>{str}</span> + return <span className="relative top-[-3px] leading-[16px]" key={index}>{str}</span> const value = vars[index].split('.') const isSystem = isSystemVar(value) const node = (isSystem ? startNode : getNodeInfoById(availableNodes, value[0]))?.data const isShowAPart = value.length > 2 - return (<span key={index}> - <span className='relative top-[-3px] leading-[16px]'>{str}</span> - <VariableLabelInText - nodeTitle={node?.title} - nodeType={node?.type} - notShowFullPath={isShowAPart} - variables={value} - /> - </span>) + return ( + <span key={index}> + <span className="relative top-[-3px] leading-[16px]">{str}</span> + <VariableLabelInText + nodeTitle={node?.title} + nodeType={node?.type} + notShowFullPath={isShowAPart} + variables={value} + /> + </span> + ) }) return html })() diff --git a/web/app/components/workflow/nodes/_base/components/remove-button.tsx b/web/app/components/workflow/nodes/_base/components/remove-button.tsx index 62381f8c2a..7b77f956d3 100644 --- a/web/app/components/workflow/nodes/_base/components/remove-button.tsx +++ b/web/app/components/workflow/nodes/_base/components/remove-button.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' -import React from 'react' import { RiDeleteBinLine } from '@remixicon/react' +import React from 'react' import ActionButton from '@/app/components/base/action-button' type Props = { @@ -13,8 +13,8 @@ const Remove: FC<Props> = ({ onClick, }) => { return ( - <ActionButton size='l' className='group shrink-0 hover:!bg-state-destructive-hover' onClick={onClick}> - <RiDeleteBinLine className='h-4 w-4 text-text-tertiary group-hover:text-text-destructive' /> + <ActionButton size="l" className="group shrink-0 hover:!bg-state-destructive-hover" onClick={onClick}> + <RiDeleteBinLine className="h-4 w-4 text-text-tertiary group-hover:text-text-destructive" /> </ActionButton> ) } diff --git a/web/app/components/workflow/nodes/_base/components/retry/hooks.ts b/web/app/components/workflow/nodes/_base/components/retry/hooks.ts index f8285358a0..9f3e68c9e9 100644 --- a/web/app/components/workflow/nodes/_base/components/retry/hooks.ts +++ b/web/app/components/workflow/nodes/_base/components/retry/hooks.ts @@ -1,12 +1,12 @@ +import type { WorkflowRetryConfig } from './types' +import type { NodeTracing } from '@/types/workflow' import { useCallback, useState, } from 'react' -import type { WorkflowRetryConfig } from './types' import { useNodeDataUpdate, } from '@/app/components/workflow/hooks' -import type { NodeTracing } from '@/types/workflow' export const useRetryConfig = ( id: string, diff --git a/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx b/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx index a7594a9567..6e9c5f962b 100644 --- a/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx +++ b/web/app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx @@ -1,11 +1,11 @@ -import { useMemo } from 'react' -import { useTranslation } from 'react-i18next' +import type { Node } from '@/app/components/workflow/types' import { RiAlertFill, RiCheckboxCircleFill, RiLoader2Line, } from '@remixicon/react' -import type { Node } from '@/app/components/workflow/types' +import { useMemo } from 'react' +import { useTranslation } from 'react-i18next' import { NodeRunningStatus } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' @@ -38,14 +38,15 @@ const RetryOnNode = ({ return null return ( - <div className='mb-1 px-3'> + <div className="mb-1 px-3"> <div className={cn( 'system-xs-medium-uppercase flex items-center justify-between rounded-md border-[0.5px] border-transparent bg-workflow-block-parma-bg px-[5px] py-1 text-text-tertiary', isRunning && 'border-state-accent-active bg-state-accent-hover text-text-accent', isSuccessful && 'border-state-success-active bg-state-success-hover text-text-success', (isException || isFailed) && 'border-state-warning-active bg-state-warning-hover text-text-warning', - )}> - <div className='flex items-center'> + )} + > + <div className="flex items-center"> { showDefault && ( t('workflow.nodes.common.retry.retryTimes', { times: retry_config.max_retries }) @@ -54,7 +55,7 @@ const RetryOnNode = ({ { isRunning && ( <> - <RiLoader2Line className='mr-1 h-3.5 w-3.5 animate-spin' /> + <RiLoader2Line className="mr-1 h-3.5 w-3.5 animate-spin" /> {t('workflow.nodes.common.retry.retrying')} </> ) @@ -62,7 +63,7 @@ const RetryOnNode = ({ { isSuccessful && ( <> - <RiCheckboxCircleFill className='mr-1 h-3.5 w-3.5' /> + <RiCheckboxCircleFill className="mr-1 h-3.5 w-3.5" /> {t('workflow.nodes.common.retry.retrySuccessful')} </> ) @@ -70,7 +71,7 @@ const RetryOnNode = ({ { (isFailed || isException) && ( <> - <RiAlertFill className='mr-1 h-3.5 w-3.5' /> + <RiAlertFill className="mr-1 h-3.5 w-3.5" /> {t('workflow.nodes.common.retry.retryFailed')} </> ) @@ -79,7 +80,9 @@ const RetryOnNode = ({ { !showDefault && !!data._retryIndex && ( <div> - {data._retryIndex}/{data.retry_config?.max_retries} + {data._retryIndex} + / + {data.retry_config?.max_retries} </div> ) } diff --git a/web/app/components/workflow/nodes/_base/components/retry/retry-on-panel.tsx b/web/app/components/workflow/nodes/_base/components/retry/retry-on-panel.tsx index d074d0c60c..bf35e28ae2 100644 --- a/web/app/components/workflow/nodes/_base/components/retry/retry-on-panel.tsx +++ b/web/app/components/workflow/nodes/_base/components/retry/retry-on-panel.tsx @@ -1,13 +1,13 @@ -import { useTranslation } from 'react-i18next' -import { useRetryConfig } from './hooks' -import s from './style.module.css' -import Switch from '@/app/components/base/switch' -import Slider from '@/app/components/base/slider' -import Input from '@/app/components/base/input' import type { Node, } from '@/app/components/workflow/types' +import { useTranslation } from 'react-i18next' +import Input from '@/app/components/base/input' +import Slider from '@/app/components/base/slider' +import Switch from '@/app/components/base/switch' import Split from '@/app/components/workflow/nodes/_base/components/split' +import { useRetryConfig } from './hooks' +import s from './style.module.css' type RetryOnPanelProps = Pick<Node, 'id' | 'data'> const RetryOnPanel = ({ @@ -52,10 +52,10 @@ const RetryOnPanel = ({ return ( <> - <div className='pt-2'> - <div className='flex h-10 items-center justify-between px-4 py-2'> - <div className='flex items-center'> - <div className='system-sm-semibold-uppercase mr-0.5 text-text-secondary'>{t('workflow.nodes.common.retry.retryOnFailure')}</div> + <div className="pt-2"> + <div className="flex h-10 items-center justify-between px-4 py-2"> + <div className="flex items-center"> + <div className="system-sm-semibold-uppercase mr-0.5 text-text-secondary">{t('workflow.nodes.common.retry.retryOnFailure')}</div> </div> <Switch defaultValue={retry_config?.retry_enabled} @@ -64,45 +64,43 @@ const RetryOnPanel = ({ </div> { retry_config?.retry_enabled && ( - <div className='px-4 pb-2'> - <div className='mb-1 flex w-full items-center'> - <div className='system-xs-medium-uppercase mr-2 grow text-text-secondary'>{t('workflow.nodes.common.retry.maxRetries')}</div> + <div className="px-4 pb-2"> + <div className="mb-1 flex w-full items-center"> + <div className="system-xs-medium-uppercase mr-2 grow text-text-secondary">{t('workflow.nodes.common.retry.maxRetries')}</div> <Slider - className='mr-3 w-[108px]' + className="mr-3 w-[108px]" value={retry_config?.max_retries || 3} onChange={handleMaxRetriesChange} min={1} max={10} /> <Input - type='number' - wrapperClassName='w-[100px]' + type="number" + wrapperClassName="w-[100px]" value={retry_config?.max_retries || 3} onChange={e => - handleMaxRetriesChange(Number.parseInt(e.currentTarget.value, 10) || 3) - } + handleMaxRetriesChange(Number.parseInt(e.currentTarget.value, 10) || 3)} min={1} max={10} unit={t('workflow.nodes.common.retry.times') || ''} className={s.input} /> </div> - <div className='flex items-center'> - <div className='system-xs-medium-uppercase mr-2 grow text-text-secondary'>{t('workflow.nodes.common.retry.retryInterval')}</div> + <div className="flex items-center"> + <div className="system-xs-medium-uppercase mr-2 grow text-text-secondary">{t('workflow.nodes.common.retry.retryInterval')}</div> <Slider - className='mr-3 w-[108px]' + className="mr-3 w-[108px]" value={retry_config?.retry_interval || 1000} onChange={handleRetryIntervalChange} min={100} max={5000} /> <Input - type='number' - wrapperClassName='w-[100px]' + type="number" + wrapperClassName="w-[100px]" value={retry_config?.retry_interval || 1000} onChange={e => - handleRetryIntervalChange(Number.parseInt(e.currentTarget.value, 10) || 1000) - } + handleRetryIntervalChange(Number.parseInt(e.currentTarget.value, 10) || 1000)} min={100} max={5000} unit={t('workflow.nodes.common.retry.ms') || ''} @@ -113,7 +111,7 @@ const RetryOnPanel = ({ ) } </div> - <Split className='mx-4 mt-2' /> + <Split className="mx-4 mt-2" /> </> ) } diff --git a/web/app/components/workflow/nodes/_base/components/selector.tsx b/web/app/components/workflow/nodes/_base/components/selector.tsx index 7b02303b29..58b1ecfa31 100644 --- a/web/app/components/workflow/nodes/_base/components/selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/selector.tsx @@ -1,10 +1,11 @@ 'use client' import type { FC } from 'react' -import React from 'react' import { useBoolean, useClickAway } from 'ahooks' -import { cn } from '@/utils/classnames' +import React from 'react' import { ChevronSelectorVertical } from '@/app/components/base/icons/src/vender/line/arrows' import { Check } from '@/app/components/base/icons/src/vender/line/general' +import { cn } from '@/utils/classnames' + type Item = { value: string label: string @@ -55,21 +56,22 @@ const TypeSelector: FC<Props> = ({ <div className={cn(!trigger && !noLeft && 'left-[-8px]', 'relative select-none', className)} ref={ref}> {trigger ? ( - <div - onClick={toggleShow} - className={cn(!readonly && 'cursor-pointer')} - > - {trigger} - </div> - ) + <div + onClick={toggleShow} + className={cn(!readonly && 'cursor-pointer')} + > + {trigger} + </div> + ) : ( - <div - onClick={toggleShow} - className={cn(showOption && 'bg-state-base-hover', 'flex h-5 cursor-pointer items-center rounded-md pl-1 pr-0.5 text-xs font-semibold text-text-secondary hover:bg-state-base-hover')}> - <div className={cn('text-sm font-semibold', uppercase && 'uppercase', noValue && 'text-text-tertiary', triggerClassName)}>{!noValue ? item?.label : placeholder}</div> - {!readonly && <DropDownIcon className='h-3 w-3 ' />} - </div> - )} + <div + onClick={toggleShow} + className={cn(showOption && 'bg-state-base-hover', 'flex h-5 cursor-pointer items-center rounded-md pl-1 pr-0.5 text-xs font-semibold text-text-secondary hover:bg-state-base-hover')} + > + <div className={cn('text-sm font-semibold', uppercase && 'uppercase', noValue && 'text-text-tertiary', triggerClassName)}>{!noValue ? item?.label : placeholder}</div> + {!readonly && <DropDownIcon className="h-3 w-3 " />} + </div> + )} {(showOption && !readonly) && ( <div className={cn('absolute top-[24px] z-10 w-[120px] select-none rounded-lg border border-components-panel-border bg-components-panel-bg p-1 shadow-lg', popupClassName)}> @@ -83,13 +85,11 @@ const TypeSelector: FC<Props> = ({ className={cn(itemClassName, uppercase && 'uppercase', 'flex h-[30px] min-w-[44px] cursor-pointer items-center justify-between rounded-lg px-3 text-[13px] font-medium text-text-secondary hover:bg-state-base-hover')} > <div>{item.label}</div> - {showChecked && item.value === value && <Check className='h-4 w-4 text-text-primary' />} + {showChecked && item.value === value && <Check className="h-4 w-4 text-text-primary" />} </div> - )) - } + ))} </div> - ) - } + )} </div> ) } diff --git a/web/app/components/workflow/nodes/_base/components/setting-item.tsx b/web/app/components/workflow/nodes/_base/components/setting-item.tsx index 5dbb962624..c629dba46b 100644 --- a/web/app/components/workflow/nodes/_base/components/setting-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/setting-item.tsx @@ -1,7 +1,8 @@ +import type { ComponentProps, PropsWithChildren, ReactNode } from 'react' +import { memo } from 'react' import Tooltip from '@/app/components/base/tooltip' import Indicator from '@/app/components/header/indicator' import { cn } from '@/utils/classnames' -import { type ComponentProps, type PropsWithChildren, type ReactNode, memo } from 'react' export type SettingItemProps = PropsWithChildren<{ label: string @@ -12,17 +13,19 @@ export type SettingItemProps = PropsWithChildren<{ export const SettingItem = memo(({ label, children, status, tooltip }: SettingItemProps) => { const indicator: ComponentProps<typeof Indicator>['color'] = status === 'error' ? 'red' : status === 'warning' ? 'yellow' : undefined const needTooltip = ['error', 'warning'].includes(status as any) - return <div className='relative flex items-center justify-between space-x-1 rounded-md bg-workflow-block-parma-bg px-1.5 py-1 text-xs font-normal'> - <div className={cn('system-xs-medium-uppercase max-w-full shrink-0 truncate text-text-tertiary', !!children && 'max-w-[100px]')}> - {label} - </div> - <Tooltip popupContent={tooltip} disabled={!needTooltip}> - <div className='system-xs-medium truncate text-right text-text-secondary'> - {children} + return ( + <div className="relative flex items-center justify-between space-x-1 rounded-md bg-workflow-block-parma-bg px-1.5 py-1 text-xs font-normal"> + <div className={cn('system-xs-medium-uppercase max-w-full shrink-0 truncate text-text-tertiary', !!children && 'max-w-[100px]')}> + {label} </div> - </Tooltip> - {indicator && <Indicator color={indicator} className='absolute -right-0.5 -top-0.5' />} - </div> + <Tooltip popupContent={tooltip} disabled={!needTooltip}> + <div className="system-xs-medium truncate text-right text-text-secondary"> + {children} + </div> + </Tooltip> + {indicator && <Indicator color={indicator} className="absolute -right-0.5 -top-0.5" />} + </div> + ) }) SettingItem.displayName = 'SettingItem' diff --git a/web/app/components/workflow/nodes/_base/components/support-var-input/index.tsx b/web/app/components/workflow/nodes/_base/components/support-var-input/index.tsx index 816bac812f..d3753bb5ff 100644 --- a/web/app/components/workflow/nodes/_base/components/support-var-input/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/support-var-input/index.tsx @@ -1,8 +1,9 @@ 'use client' import type { FC } from 'react' import React from 'react' -import { cn } from '@/utils/classnames' import VarHighlight from '@/app/components/app/configuration/base/var-highlight' +import { cn } from '@/utils/classnames' + type Props = { isFocus?: boolean onFocus?: () => void @@ -46,20 +47,21 @@ const SupportVarInput: FC<Props> = ({ <div className={ cn(wrapClassName, 'flex h-full w-full') - } onClick={onFocus} + } + onClick={onFocus} > {(isFocus && !readonly && children) ? ( - children - ) + children + ) : ( - <div - className={cn(textClassName, 'h-full w-0 grow truncate whitespace-nowrap')} - title={value} - > - {renderSafeContent(value || '')} - </div> - )} + <div + className={cn(textClassName, 'h-full w-0 grow truncate whitespace-nowrap')} + title={value} + > + {renderSafeContent(value || '')} + </div> + )} </div> ) } diff --git a/web/app/components/workflow/nodes/_base/components/switch-plugin-version.tsx b/web/app/components/workflow/nodes/_base/components/switch-plugin-version.tsx index c9d698337d..28e0603707 100644 --- a/web/app/components/workflow/nodes/_base/components/switch-plugin-version.tsx +++ b/web/app/components/workflow/nodes/_base/components/switch-plugin-version.tsx @@ -1,20 +1,20 @@ 'use client' -import Badge from '@/app/components/base/badge' -import Tooltip from '@/app/components/base/tooltip' -import PluginVersionPicker from '@/app/components/plugins/update-plugin/plugin-version-picker' +import type { FC, ReactNode } from 'react' import { RiArrowLeftRightLine, RiExternalLinkLine } from '@remixicon/react' -import type { ReactNode } from 'react' -import { type FC, useCallback, useState } from 'react' import { useBoolean } from 'ahooks' -import { useCheckInstalled, useUpdatePackageFromMarketPlace } from '@/service/use-plugins' -import { cn } from '@/utils/classnames' -import PluginMutationModel from '@/app/components/plugins/plugin-mutation-model' +import Link from 'next/link' +import { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Badge from '@/app/components/base/badge' +import { Badge as Badge2, BadgeState } from '@/app/components/base/badge/index' +import Tooltip from '@/app/components/base/tooltip' import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' import { pluginManifestToCardPluginProps } from '@/app/components/plugins/install-plugin/utils' -import { Badge as Badge2, BadgeState } from '@/app/components/base/badge/index' -import Link from 'next/link' -import { useTranslation } from 'react-i18next' +import PluginMutationModel from '@/app/components/plugins/plugin-mutation-model' +import PluginVersionPicker from '@/app/components/plugins/update-plugin/plugin-version-picker' +import { useCheckInstalled, useUpdatePackageFromMarketPlace } from '@/service/use-plugins' +import { cn } from '@/utils/classnames' import { getMarketplaceUrl } from '@/utils/var' export type SwitchPluginVersionProps = { @@ -31,8 +31,8 @@ export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => { const [isShow, setIsShow] = useState(false) const [isShowUpdateModal, { setTrue: showUpdateModal, setFalse: hideUpdateModal }] = useBoolean(false) const [target, setTarget] = useState<{ - version: string, - pluginUniqueIden: string; + version: string + pluginUniqueIden: string }>() const pluginDetails = useCheckInstalled({ pluginIds: [pluginId], @@ -58,7 +58,8 @@ export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => { onSuccess() { handleUpdatedFromMarketplace() }, - }) + }, + ) } const { t } = useTranslation() @@ -66,67 +67,75 @@ export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => { if (!uniqueIdentifier || !pluginId) return null - return <Tooltip popupContent={!isShow && !isShowUpdateModal && tooltip} triggerMethod='hover'> - <div className={cn('flex w-fit items-center justify-center', className)} onClick={e => e.stopPropagation()}> - {isShowUpdateModal && pluginDetail && <PluginMutationModel - onCancel={hideUpdateModal} - plugin={pluginManifestToCardPluginProps({ - ...pluginDetail.declaration, - icon: icon!, - })} - mutation={mutation} - mutate={install} - confirmButtonText={t('workflow.nodes.agent.installPlugin.install')} - cancelButtonText={t('workflow.nodes.agent.installPlugin.cancel')} - modelTitle={t('workflow.nodes.agent.installPlugin.title')} - description={t('workflow.nodes.agent.installPlugin.desc')} - cardTitleLeft={<> - <Badge2 className='mx-1' size="s" state={BadgeState.Warning}> - {`${pluginDetail.version} -> ${target!.version}`} - </Badge2> - </>} - modalBottomLeft={ - <Link - className='flex items-center justify-center gap-1' - href={getMarketplaceUrl(`/plugins/${pluginDetail.declaration.author}/${pluginDetail.declaration.name}`)} - target='_blank' - > - <span className='system-xs-regular text-xs text-text-accent'> - {t('workflow.nodes.agent.installPlugin.changelog')} - </span> - <RiExternalLinkLine className='size-3 text-text-accent' /> - </Link> - } - />} - {pluginDetail && <PluginVersionPicker - isShow={isShow} - onShowChange={setIsShow} - pluginID={pluginId} - currentVersion={pluginDetail.version} - onSelect={(state) => { - setTarget({ - pluginUniqueIden: state.unique_identifier, - version: state.version, - }) - showUpdateModal() - }} - trigger={ - <Badge - className={cn( - 'mx-1 flex hover:bg-state-base-hover', - isShow && 'bg-state-base-hover', - )} - uppercase={true} - text={ + return ( + <Tooltip popupContent={!isShow && !isShowUpdateModal && tooltip} triggerMethod="hover"> + <div className={cn('flex w-fit items-center justify-center', className)} onClick={e => e.stopPropagation()}> + {isShowUpdateModal && pluginDetail && ( + <PluginMutationModel + onCancel={hideUpdateModal} + plugin={pluginManifestToCardPluginProps({ + ...pluginDetail.declaration, + icon: icon!, + })} + mutation={mutation} + mutate={install} + confirmButtonText={t('workflow.nodes.agent.installPlugin.install')} + cancelButtonText={t('workflow.nodes.agent.installPlugin.cancel')} + modelTitle={t('workflow.nodes.agent.installPlugin.title')} + description={t('workflow.nodes.agent.installPlugin.desc')} + cardTitleLeft={( <> - <div>{pluginDetail.version}</div> - <RiArrowLeftRightLine className='ml-1 h-3 w-3 text-text-tertiary' /> + <Badge2 className="mx-1" size="s" state={BadgeState.Warning}> + {`${pluginDetail.version} -> ${target!.version}`} + </Badge2> </> - } - hasRedCornerMark={true} + )} + modalBottomLeft={( + <Link + className="flex items-center justify-center gap-1" + href={getMarketplaceUrl(`/plugins/${pluginDetail.declaration.author}/${pluginDetail.declaration.name}`)} + target="_blank" + > + <span className="system-xs-regular text-xs text-text-accent"> + {t('workflow.nodes.agent.installPlugin.changelog')} + </span> + <RiExternalLinkLine className="size-3 text-text-accent" /> + </Link> + )} /> - } - />} - </div> - </Tooltip> + )} + {pluginDetail && ( + <PluginVersionPicker + isShow={isShow} + onShowChange={setIsShow} + pluginID={pluginId} + currentVersion={pluginDetail.version} + onSelect={(state) => { + setTarget({ + pluginUniqueIden: state.unique_identifier, + version: state.version, + }) + showUpdateModal() + }} + trigger={( + <Badge + className={cn( + 'mx-1 flex hover:bg-state-base-hover', + isShow && 'bg-state-base-hover', + )} + uppercase={true} + text={( + <> + <div>{pluginDetail.version}</div> + <RiArrowLeftRightLine className="ml-1 h-3 w-3 text-text-tertiary" /> + </> + )} + hasRedCornerMark={true} + /> + )} + /> + )} + </div> + </Tooltip> + ) } diff --git a/web/app/components/workflow/nodes/_base/components/title-description-input.tsx b/web/app/components/workflow/nodes/_base/components/title-description-input.tsx index 062190aee9..082aafade8 100644 --- a/web/app/components/workflow/nodes/_base/components/title-description-input.tsx +++ b/web/app/components/workflow/nodes/_base/components/title-description-input.tsx @@ -3,8 +3,8 @@ import { useCallback, useState, } from 'react' -import Textarea from 'react-textarea-autosize' import { useTranslation } from 'react-i18next' +import Textarea from 'react-textarea-autosize' type TitleInputProps = { value: string diff --git a/web/app/components/workflow/nodes/_base/components/toggle-expand-btn.tsx b/web/app/components/workflow/nodes/_base/components/toggle-expand-btn.tsx index f597b24b9f..116825ae95 100644 --- a/web/app/components/workflow/nodes/_base/components/toggle-expand-btn.tsx +++ b/web/app/components/workflow/nodes/_base/components/toggle-expand-btn.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' import { RiCollapseDiagonalLine, RiExpandDiagonalLine, } from '@remixicon/react' +import React, { useCallback } from 'react' import ActionButton from '@/app/components/base/action-button' type Props = { @@ -23,7 +23,7 @@ const ExpandBtn: FC<Props> = ({ const Icon = isExpand ? RiCollapseDiagonalLine : RiExpandDiagonalLine return ( <ActionButton onClick={handleToggle}> - <Icon className='h-4 w-4' /> + <Icon className="h-4 w-4" /> </ActionButton> ) } diff --git a/web/app/components/workflow/nodes/_base/components/variable-tag.tsx b/web/app/components/workflow/nodes/_base/components/variable-tag.tsx index 71af3ad4fd..c6a7d5c6d5 100644 --- a/web/app/components/workflow/nodes/_base/components/variable-tag.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable-tag.tsx @@ -1,18 +1,18 @@ -import { useCallback, useMemo } from 'react' -import { useNodes, useReactFlow, useStoreApi } from 'reactflow' -import { useTranslation } from 'react-i18next' import type { CommonNodeType, Node, ValueSelector, VarType, } from '@/app/components/workflow/types' -import { BlockEnum } from '@/app/components/workflow/types' +import { useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { useNodes, useReactFlow, useStoreApi } from 'reactflow' import { getNodeInfoById, isConversationVar, isENV, isGlobalVar, isRagVariableVar, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' -import { isExceptionVariable } from '@/app/components/workflow/utils' import { VariableLabelInSelect, } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' +import { BlockEnum } from '@/app/components/workflow/types' +import { isExceptionVariable } from '@/app/components/workflow/utils' type VariableTagProps = { valueSelector: ValueSelector diff --git a/web/app/components/workflow/nodes/_base/components/variable/assigned-var-reference-popup.tsx b/web/app/components/workflow/nodes/_base/components/variable/assigned-var-reference-popup.tsx index 0e086fabcd..7907c83fc3 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/assigned-var-reference-popup.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/assigned-var-reference-popup.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' +import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import VarReferenceVars from './var-reference-vars' -import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' import ListEmpty from '@/app/components/base/list-empty' +import VarReferenceVars from './var-reference-vars' type Props = { vars: NodeOutPutVar[] @@ -19,21 +19,24 @@ const AssignedVarReferencePopup: FC<Props> = ({ const { t } = useTranslation() // max-h-[300px] overflow-y-auto todo: use portal to handle long list return ( - <div className='bg-components-panel-bg-bur w-[352px] rounded-lg border-[0.5px] border-components-panel-border p-1 shadow-lg' > + <div className="bg-components-panel-bg-bur w-[352px] rounded-lg border-[0.5px] border-components-panel-border p-1 shadow-lg"> {(!vars || vars.length === 0) - ? <ListEmpty - title={t('workflow.nodes.assigner.noAssignedVars') || ''} - description={t('workflow.nodes.assigner.assignedVarsDescription')} - /> - : <VarReferenceVars - searchBoxClassName='mt-1' - vars={vars} - onChange={onChange} - itemWidth={itemWidth} - isSupportFileVar - /> - } - </div > + ? ( + <ListEmpty + title={t('workflow.nodes.assigner.noAssignedVars') || ''} + description={t('workflow.nodes.assigner.assignedVarsDescription')} + /> + ) + : ( + <VarReferenceVars + searchBoxClassName="mt-1" + vars={vars} + onChange={onChange} + itemWidth={itemWidth} + isSupportFileVar + /> + )} + </div> ) } export default React.memo(AssignedVarReferencePopup) diff --git a/web/app/components/workflow/nodes/_base/components/variable/constant-field.tsx b/web/app/components/workflow/nodes/_base/components/variable/constant-field.tsx index 0d965e2d22..310e82ff12 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/constant-field.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/constant-field.tsx @@ -1,12 +1,12 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' import type { CredentialFormSchema, CredentialFormSchemaNumberInput, CredentialFormSchemaSelect } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { Var } from '@/app/components/workflow/types' +import React, { useCallback } from 'react' +import { SimpleSelect } from '@/app/components/base/select' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' -import type { Var } from '@/app/components/workflow/types' -import { SimpleSelect } from '@/app/components/base/select' type Props = { schema: Partial<CredentialFormSchema> @@ -42,8 +42,8 @@ const ConstantField: FC<Props> = ({ <> {(schema.type === FormTypeEnum.select || schema.type === FormTypeEnum.dynamicSelect) && ( <SimpleSelect - wrapperClassName='w-full !h-8' - className='flex items-center' + wrapperClassName="w-full !h-8" + className="flex items-center" disabled={readonly} defaultValue={value} items={(schema as CredentialFormSchemaSelect).options.map(option => ({ value: option.value, name: option.label[language] || option.label.en_US }))} @@ -55,8 +55,8 @@ const ConstantField: FC<Props> = ({ )} {schema.type === FormTypeEnum.textNumber && ( <input - type='number' - className='h-8 w-full overflow-hidden rounded-lg bg-workflow-block-parma-bg p-2 text-[13px] font-normal leading-8 text-text-secondary placeholder:text-gray-400 focus:outline-none' + type="number" + className="h-8 w-full overflow-hidden rounded-lg bg-workflow-block-parma-bg p-2 text-[13px] font-normal leading-8 text-text-secondary placeholder:text-gray-400 focus:outline-none" value={value} onChange={handleStaticChange} readOnly={readonly} diff --git a/web/app/components/workflow/nodes/_base/components/variable/manage-input-field.tsx b/web/app/components/workflow/nodes/_base/components/variable/manage-input-field.tsx index a0588959ea..87ca719e88 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/manage-input-field.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/manage-input-field.tsx @@ -1,5 +1,5 @@ -import { useTranslation } from 'react-i18next' import { RiAddLine } from '@remixicon/react' +import { useTranslation } from 'react-i18next' type ManageInputFieldProps = { onManage: () => void @@ -11,22 +11,22 @@ const ManageInputField = ({ const { t } = useTranslation() return ( - <div className='flex items-center border-t border-divider-subtle pt-1'> + <div className="flex items-center border-t border-divider-subtle pt-1"> <div - className='flex h-8 grow cursor-pointer items-center px-3' + className="flex h-8 grow cursor-pointer items-center px-3" onClick={onManage} > - <RiAddLine className='mr-1 h-4 w-4 text-text-tertiary' /> + <RiAddLine className="mr-1 h-4 w-4 text-text-tertiary" /> <div - className='system-xs-medium truncate text-text-tertiary' - title='Create user input field' + className="system-xs-medium truncate text-text-tertiary" + title="Create user input field" > {t('pipeline.inputField.create')} </div> </div> - <div className='mx-1 h-3 w-[1px] shrink-0 bg-divider-regular'></div> + <div className="mx-1 h-3 w-[1px] shrink-0 bg-divider-regular"></div> <div - className='system-xs-medium flex h-8 shrink-0 cursor-pointer items-center justify-center px-3 text-text-tertiary' + className="system-xs-medium flex h-8 shrink-0 cursor-pointer items-center justify-center px-3 text-text-tertiary" onClick={onManage} > {t('pipeline.inputField.manage')} diff --git a/web/app/components/workflow/nodes/_base/components/variable/match-schema-type.ts b/web/app/components/workflow/nodes/_base/components/variable/match-schema-type.ts index 9ca4981065..79b95942e0 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/match-schema-type.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/match-schema-type.ts @@ -7,7 +7,7 @@ function matchTheSchemaType(scheme: AnyObj, target: AnyObj): boolean { const isMatch = (schema: AnyObj, t: AnyObj): boolean => { const oSchema = isObj(schema) const oT = isObj(t) - if(!oSchema) + if (!oSchema) return true if (!oT) { // ignore the object without type // deep find oSchema has type @@ -24,14 +24,18 @@ function matchTheSchemaType(scheme: AnyObj, target: AnyObj): boolean { const ty = (t as any).type const isTypeValueObj = isObj(tx) - if(!isTypeValueObj) // caution: type can be object, so that it would not be compare by value - if (tx !== ty) return false + if (!isTypeValueObj) { // caution: type can be object, so that it would not be compare by value + if (tx !== ty) + return false + } // recurse into all keys const keys = new Set([...Object.keys(schema as object), ...Object.keys(t as object)]) for (const k of keys) { - if (k === 'type' && !isTypeValueObj) continue // already checked - if (!isMatch((schema as any)[k], (t as any)[k])) return false + if (k === 'type' && !isTypeValueObj) + continue // already checked + if (!isMatch((schema as any)[k], (t as any)[k])) + return false } return true } diff --git a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/field.tsx b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/field.tsx index ca8317e8d7..f533c33108 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/field.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/field.tsx @@ -1,22 +1,22 @@ 'use client' import type { FC } from 'react' +import type { Field as FieldType } from '../../../../../llm/types' +import type { ValueSelector } from '@/app/components/workflow/types' +import { RiMoreFill } from '@remixicon/react' import React from 'react' +import { useTranslation } from 'react-i18next' +import Tooltip from '@/app/components/base/tooltip' +import { cn } from '@/utils/classnames' import { Type } from '../../../../../llm/types' import { getFieldType } from '../../../../../llm/utils' -import type { Field as FieldType } from '../../../../../llm/types' -import { cn } from '@/utils/classnames' import TreeIndentLine from '../tree-indent-line' -import { RiMoreFill } from '@remixicon/react' -import Tooltip from '@/app/components/base/tooltip' -import type { ValueSelector } from '@/app/components/workflow/types' -import { useTranslation } from 'react-i18next' const MAX_DEPTH = 10 type Props = { valueSelector: ValueSelector - name: string, - payload: FieldType, + name: string + payload: FieldType depth?: number readonly?: boolean onSelect?: (valueSelector: ValueSelector) => void @@ -43,15 +43,17 @@ const Field: FC<Props> = ({ className={cn('flex items-center justify-between rounded-md pr-2', !readonly && 'hover:bg-state-base-hover', depth !== MAX_DEPTH + 1 && 'cursor-pointer')} onMouseDown={() => !readonly && onSelect?.([...valueSelector, name])} > - <div className='flex grow items-stretch'> + <div className="flex grow items-stretch"> <TreeIndentLine depth={depth} /> - {depth === MAX_DEPTH + 1 ? ( - <RiMoreFill className='h-3 w-3 text-text-tertiary' /> - ) : (<div className={cn('system-sm-medium h-6 w-0 grow truncate leading-6 text-text-secondary', isHighlight && 'text-text-accent')}>{name}</div>)} + {depth === MAX_DEPTH + 1 + ? ( + <RiMoreFill className="h-3 w-3 text-text-tertiary" /> + ) + : (<div className={cn('system-sm-medium h-6 w-0 grow truncate leading-6 text-text-secondary', isHighlight && 'text-text-accent')}>{name}</div>)} </div> {depth < MAX_DEPTH + 1 && ( - <div className='system-xs-regular ml-2 shrink-0 text-text-tertiary'>{getFieldType(payload)}</div> + <div className="system-xs-regular ml-2 shrink-0 text-text-tertiary">{getFieldType(payload)}</div> )} </div> </Tooltip> diff --git a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/index.tsx b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/index.tsx index 219d46df9c..baf3cfcbd2 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/index.tsx @@ -1,11 +1,11 @@ 'use client' import type { FC } from 'react' -import React, { useRef } from 'react' import type { StructuredOutput } from '../../../../../llm/types' -import Field from './field' -import { cn } from '@/utils/classnames' -import { useHover } from 'ahooks' import type { ValueSelector } from '@/app/components/workflow/types' +import { useHover } from 'ahooks' +import React, { useRef } from 'react' +import { cn } from '@/utils/classnames' +import Field from './field' type Props = { className?: string @@ -42,17 +42,17 @@ export const PickerPanelMain: FC<Props> = ({ return ( <div className={cn(className)} ref={ref}> {/* Root info */} - <div className='flex items-center justify-between px-2 py-1'> - <div className='flex'> + <div className="flex items-center justify-between px-2 py-1"> + <div className="flex"> {root.nodeName && ( <> - <div className='system-sm-medium max-w-[100px] truncate text-text-tertiary'>{root.nodeName}</div> - <div className='system-sm-medium text-text-tertiary'>.</div> + <div className="system-sm-medium max-w-[100px] truncate text-text-tertiary">{root.nodeName}</div> + <div className="system-sm-medium text-text-tertiary">.</div> </> )} - <div className='system-sm-medium text-text-secondary'>{root.attrName}</div> + <div className="system-sm-medium text-text-secondary">{root.attrName}</div> </div> - <div className='system-xs-regular ml-2 truncate text-text-tertiary' title={root.attrAlias || 'object'}>{root.attrAlias || 'object'}</div> + <div className="system-xs-regular ml-2 truncate text-text-tertiary" title={root.attrAlias || 'object'}>{root.attrAlias || 'object'}</div> </div> {fieldNames.map(name => ( <Field diff --git a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/field.tsx b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/field.tsx index e101d91021..fc23cda205 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/field.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/field.tsx @@ -1,20 +1,20 @@ 'use client' -import { cn } from '@/utils/classnames' +import type { FC } from 'react' +import type { Field as FieldType } from '../../../../../llm/types' import { RiArrowDropDownLine } from '@remixicon/react' import { useBoolean } from 'ahooks' -import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' -import type { Field as FieldType } from '../../../../../llm/types' +import { cn } from '@/utils/classnames' import { Type } from '../../../../../llm/types' import { getFieldType } from '../../../../../llm/utils' import TreeIndentLine from '../tree-indent-line' type Props = { - name: string, - payload: FieldType, - required: boolean, - depth?: number, + name: string + payload: FieldType + required: boolean + depth?: number rootClassName?: string } @@ -36,8 +36,8 @@ const Field: FC<Props> = ({ <div> <div className={cn('flex pr-2')}> <TreeIndentLine depth={depth} /> - <div className='w-0 grow'> - <div className='relative flex select-none'> + <div className="w-0 grow"> + <div className="relative flex select-none"> {hasChildren && ( <RiArrowDropDownLine className={cn('absolute left-[-18px] top-[50%] h-4 w-4 translate-y-[-50%] cursor-pointer bg-components-panel-bg text-text-tertiary', fold && 'rotate-[270deg] text-text-accent')} @@ -45,20 +45,20 @@ const Field: FC<Props> = ({ /> )} <div className={cn('system-sm-medium ml-[7px] h-6 truncate leading-6 text-text-secondary', isRoot && rootClassName)}>{name}</div> - <div className='system-xs-regular ml-3 shrink-0 leading-6 text-text-tertiary'> + <div className="system-xs-regular ml-3 shrink-0 leading-6 text-text-tertiary"> {getFieldType(payload)} {(payload.schemaType && payload.schemaType !== 'file' && ` (${payload.schemaType})`)} </div> - {required && <div className='system-2xs-medium-uppercase ml-3 leading-6 text-text-warning'>{t('app.structOutput.required')}</div>} + {required && <div className="system-2xs-medium-uppercase ml-3 leading-6 text-text-warning">{t('app.structOutput.required')}</div>} </div> {payload.description && ( - <div className='ml-[7px] flex'> - <div className='system-xs-regular w-0 grow truncate text-text-tertiary'>{payload.description}</div> + <div className="ml-[7px] flex"> + <div className="system-xs-regular w-0 grow truncate text-text-tertiary">{payload.description}</div> </div> )} {hasEnum && ( - <div className='ml-[7px] flex'> - <div className='system-xs-regular w-0 grow text-text-quaternary'> + <div className="ml-[7px] flex"> + <div className="system-xs-regular w-0 grow text-text-quaternary"> {payload.enum!.map((value, index) => ( <span key={index}> {typeof value === 'string' ? `"${value}"` : value} diff --git a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/index.tsx b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/index.tsx index 86f707af13..deaab09e6c 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/index.tsx @@ -1,9 +1,9 @@ 'use client' import type { FC } from 'react' -import React from 'react' import type { StructuredOutput } from '../../../../../llm/types' -import Field from './field' +import React from 'react' import { useTranslation } from 'react-i18next' +import Field from './field' type Props = { payload: StructuredOutput @@ -23,7 +23,7 @@ const ShowPanel: FC<Props> = ({ }, } return ( - <div className='relative left-[-7px]'> + <div className="relative left-[-7px]"> {Object.keys(schema.schema.properties!).map(name => ( <Field key={name} diff --git a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/tree-indent-line.tsx b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/tree-indent-line.tsx index bbec1d7a00..9e45a4cccc 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/tree-indent-line.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/tree-indent-line.tsx @@ -4,8 +4,8 @@ import React from 'react' import { cn } from '@/utils/classnames' type Props = { - depth?: number, - className?: string, + depth?: number + className?: string } const TreeIndentLine: FC<Props> = ({ diff --git a/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx b/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx index 3c2415bf9d..7eccbe23de 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx @@ -1,17 +1,17 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useState } from 'react' -import { produce } from 'immer' -import { useTranslation } from 'react-i18next' import type { OutputVar } from '../../../code/types' +import type { ToastHandle } from '@/app/components/base/toast' +import type { VarType } from '@/app/components/workflow/types' +import { useDebounceFn } from 'ahooks' +import { produce } from 'immer' +import React, { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Input from '@/app/components/base/input' +import Toast from '@/app/components/base/toast' +import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' import RemoveButton from '../remove-button' import VarTypePicker from './var-type-picker' -import Input from '@/app/components/base/input' -import type { VarType } from '@/app/components/workflow/types' -import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' -import type { ToastHandle } from '@/app/components/base/toast' -import Toast from '@/app/components/base/toast' -import { useDebounceFn } from 'ahooks' type Props = { readonly: boolean @@ -93,14 +93,14 @@ const OutputVarList: FC<Props> = ({ }, [onRemove]) return ( - <div className='space-y-2'> + <div className="space-y-2"> {list.map((item, index) => ( - <div className='flex items-center space-x-1' key={index}> + <div className="flex items-center space-x-1" key={index}> <Input readOnly={readonly} value={item.variable} onChange={handleVarNameChange(index)} - wrapperClassName='grow' + wrapperClassName="grow" /> <VarTypePicker readonly={readonly} @@ -108,7 +108,7 @@ const OutputVarList: FC<Props> = ({ onChange={handleVarTypeChange(index)} /> <RemoveButton - className='!bg-gray-100 !p-2 hover:!bg-gray-200' + className="!bg-gray-100 !p-2 hover:!bg-gray-200" onClick={handleVarRemove(index)} /> </div> diff --git a/web/app/components/workflow/nodes/_base/components/variable/use-match-schema-type.ts b/web/app/components/workflow/nodes/_base/components/variable/use-match-schema-type.ts index 2c0c91b7f5..67fb230eeb 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/use-match-schema-type.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/use-match-schema-type.ts @@ -1,10 +1,11 @@ +import type { AnyObj } from './match-schema-type' import type { SchemaTypeDefinition } from '@/service/use-common' import { useSchemaTypeDefinitions } from '@/service/use-common' -import type { AnyObj } from './match-schema-type' import matchTheSchemaType from './match-schema-type' export const getMatchedSchemaType = (obj: AnyObj, schemaTypeDefinitions?: SchemaTypeDefinition[]): string => { - if(!schemaTypeDefinitions || obj === undefined || obj === null) return '' + if (!schemaTypeDefinitions || obj === undefined || obj === null) + return '' const matched = schemaTypeDefinitions.find(def => matchTheSchemaType(obj, def.schema)) return matched ? matched.name : '' } diff --git a/web/app/components/workflow/nodes/_base/components/variable/utils.ts b/web/app/components/workflow/nodes/_base/components/variable/utils.ts index eb76021c40..5a5a9b826a 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -1,32 +1,26 @@ -import { produce } from 'immer' -import { isArray, uniq } from 'lodash-es' -import type { CodeNodeType } from '../../../code/types' -import type { EndNodeType } from '../../../end/types' +import type { AgentNodeType } from '../../../agent/types' import type { AnswerNodeType } from '../../../answer/types' -import { - type LLMNodeType, - type StructuredOutput, - Type, -} from '../../../llm/types' -import type { KnowledgeRetrievalNodeType } from '../../../knowledge-retrieval/types' -import type { IfElseNodeType } from '../../../if-else/types' -import type { TemplateTransformNodeType } from '../../../template-transform/types' -import type { QuestionClassifierNodeType } from '../../../question-classifier/types' -import type { HttpNodeType } from '../../../http/types' -import { VarType as ToolVarType } from '../../../tool/types' -import type { ToolNodeType } from '../../../tool/types' -import type { ParameterExtractorNodeType } from '../../../parameter-extractor/types' -import type { IterationNodeType } from '../../../iteration/types' -import type { LoopNodeType } from '../../../loop/types' -import type { ListFilterNodeType } from '../../../list-operator/types' -import { OUTPUT_FILE_SUB_VARIABLES } from '../../../constants' +import type { CodeNodeType } from '../../../code/types' import type { DocExtractorNodeType } from '../../../document-extractor/types' -import { - BlockEnum, - InputVarType, - VarType, -} from '@/app/components/workflow/types' +import type { EndNodeType } from '../../../end/types' +import type { HttpNodeType } from '../../../http/types' +import type { IfElseNodeType } from '../../../if-else/types' +import type { IterationNodeType } from '../../../iteration/types' +import type { KnowledgeRetrievalNodeType } from '../../../knowledge-retrieval/types' +import type { ListFilterNodeType } from '../../../list-operator/types' +import type { LLMNodeType, StructuredOutput } from '../../../llm/types' +import type { LoopNodeType } from '../../../loop/types' +import type { ParameterExtractorNodeType } from '../../../parameter-extractor/types' +import type { QuestionClassifierNodeType } from '../../../question-classifier/types' +import type { TemplateTransformNodeType } from '../../../template-transform/types' +import type { ToolNodeType } from '../../../tool/types' +import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' +import type { CaseItem, Condition } from '@/app/components/workflow/nodes/if-else/types' +import type { Field as StructField } from '@/app/components/workflow/nodes/llm/types' import type { StartNodeType } from '@/app/components/workflow/nodes/start/types' +import type { PluginTriggerNodeType } from '@/app/components/workflow/nodes/trigger-plugin/types' +import type { WebhookTriggerNodeType } from '@/app/components/workflow/nodes/trigger-webhook/types' +import type { VariableAssignerNodeType } from '@/app/components/workflow/nodes/variable-assigner/types' import type { ConversationVariable, EnvironmentVariable, @@ -36,16 +30,15 @@ import type { ValueSelector, Var, } from '@/app/components/workflow/types' -import type { VariableAssignerNodeType } from '@/app/components/workflow/nodes/variable-assigner/types' -import type { Field as StructField } from '@/app/components/workflow/nodes/llm/types' +import type { PromptItem } from '@/models/debug' import type { RAGPipelineVariable } from '@/models/pipeline' -import type { WebhookTriggerNodeType } from '@/app/components/workflow/nodes/trigger-webhook/types' -import type { PluginTriggerNodeType } from '@/app/components/workflow/nodes/trigger-plugin/types' -import PluginTriggerNodeDefault from '@/app/components/workflow/nodes/trigger-plugin/default' -import type { CaseItem, Condition } from '@/app/components/workflow/nodes/if-else/types' +import type { SchemaTypeDefinition } from '@/service/use-common' +import { produce } from 'immer' +import { isArray, uniq } from 'lodash-es' import { AGENT_OUTPUT_STRUCT, FILE_STRUCT, + getGlobalVars, HTTP_REQUEST_OUTPUT_STRUCT, KNOWLEDGE_RETRIEVAL_OUTPUT_STRUCT, LLM_OUTPUT_STRUCT, @@ -54,23 +47,31 @@ import { SUPPORT_OUTPUT_VARS_NODE, TEMPLATE_TRANSFORM_OUTPUT_STRUCT, TOOL_OUTPUT_STRUCT, - getGlobalVars, } from '@/app/components/workflow/constants' -import ToolNodeDefault from '@/app/components/workflow/nodes/tool/default' import DataSourceNodeDefault from '@/app/components/workflow/nodes/data-source/default' -import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' -import type { PromptItem } from '@/models/debug' +import ToolNodeDefault from '@/app/components/workflow/nodes/tool/default' +import PluginTriggerNodeDefault from '@/app/components/workflow/nodes/trigger-plugin/default' +import { + BlockEnum, + InputVarType, + VarType, +} from '@/app/components/workflow/types' import { VAR_REGEX } from '@/config' -import type { AgentNodeType } from '../../../agent/types' -import type { SchemaTypeDefinition } from '@/service/use-common' import { AppModeEnum } from '@/types/app' +import { OUTPUT_FILE_SUB_VARIABLES } from '../../../constants' +import { + + Type, +} from '../../../llm/types' +import { VarType as ToolVarType } from '../../../tool/types' export const isSystemVar = (valueSelector: ValueSelector) => { return valueSelector[0] === 'sys' || valueSelector[1] === 'sys' } export const isGlobalVar = (valueSelector: ValueSelector) => { - if (!isSystemVar(valueSelector)) return false + if (!isSystemVar(valueSelector)) + return false const second = valueSelector[1] if (['query', 'files'].includes(second)) @@ -87,7 +88,8 @@ export const isConversationVar = (valueSelector: ValueSelector) => { } export const isRagVariableVar = (valueSelector: ValueSelector) => { - if (!valueSelector) return false + if (!valueSelector) + return false return valueSelector[0] === 'rag' } @@ -247,8 +249,7 @@ const findExceptVarInObject = ( isFile?: boolean, ): Var => { const { children } = obj - const isStructuredOutput = !!(children as StructuredOutput)?.schema - ?.properties + const isStructuredOutput = !!(children as StructuredOutput)?.schema?.properties let childrenResult: Var[] | StructuredOutput | undefined @@ -281,9 +282,12 @@ const findExceptVarInObject = ( if ( (item.type === VarType.object || item.type === VarType.file) && itemChildren - ) + ) { passesFilter = itemHasValidChildren || filterVar(item, currSelector) - else passesFilter = itemHasValidChildren + } + else { + passesFilter = itemHasValidChildren + } return { item, @@ -294,7 +298,8 @@ const findExceptVarInObject = ( .filter(({ passesFilter }) => passesFilter) .map(({ item, filteredObj }) => { const { children: itemChildren } = item - if (!itemChildren || !filteredObj) return item + if (!itemChildren || !filteredObj) + return item return { ...item, @@ -415,11 +420,11 @@ const formatItem = ( const { outputs } = data as CodeNodeType res.vars = outputs ? Object.keys(outputs).map((key) => { - return { - variable: key, - type: outputs[key].type, - } - }) + return { + variable: key, + type: outputs[key].type, + } + }) : [] break } @@ -562,7 +567,8 @@ const formatItem = ( } case BlockEnum.ListFilter: { - if (!(data as ListFilterNodeType).var_type) break + if (!(data as ListFilterNodeType).var_type) + break res.vars = [ { @@ -690,12 +696,14 @@ const formatItem = ( (() => { const variableArr = v.variable.split('.') const [first] = variableArr - if (isSpecialVar(first)) return variableArr + if (isSpecialVar(first)) + return variableArr return [...selector, ...variableArr] })(), ) - if (isCurrentMatched) return true + if (isCurrentMatched) + return true const isFile = v.type === VarType.file const children = (() => { @@ -710,7 +718,8 @@ const formatItem = ( } return v.children })() - if (!children) return false + if (!children) + return false const obj = findExceptVarInObject( isFile ? { ...v, children } : v, @@ -737,7 +746,8 @@ const formatItem = ( return v })() - if (!children) return v + if (!children) + return v return findExceptVarInObject( isFile ? { ...v, children } : v, @@ -813,14 +823,22 @@ export const toNodeOutputVars = ( } // Sort nodes in reverse chronological order (most recent first) const sortedNodes = [...nodes].sort((a, b) => { - if (a.data.type === BlockEnum.Start) return 1 - if (b.data.type === BlockEnum.Start) return -1 - if (a.data.type === 'env') return 1 - if (b.data.type === 'env') return -1 - if (a.data.type === 'conversation') return 1 - if (b.data.type === 'conversation') return -1 - if (a.data.type === 'global') return 1 - if (b.data.type === 'global') return -1 + if (a.data.type === BlockEnum.Start) + return 1 + if (b.data.type === BlockEnum.Start) + return -1 + if (a.data.type === 'env') + return 1 + if (b.data.type === 'env') + return -1 + if (a.data.type === 'conversation') + return 1 + if (b.data.type === 'conversation') + return -1 + if (a.data.type === 'global') + return 1 + if (b.data.type === 'global') + return -1 // sort nodes by x position return (b.position?.x || 0) - (a.position?.x || 0) }) @@ -872,8 +890,8 @@ const getIterationItemType = ({ valueSelector, beforeNodesOutputVars, }: { - valueSelector: ValueSelector; - beforeNodesOutputVars: NodeOutPutVar[]; + valueSelector: ValueSelector + beforeNodesOutputVars: NodeOutPutVar[] }): VarType => { const outputVarNodeId = valueSelector[0] const isSystem = isSystemVar(valueSelector) @@ -883,7 +901,8 @@ const getIterationItemType = ({ ? beforeNodesOutputVars.find(v => v.isStartNode) : beforeNodesOutputVars.find(v => v.nodeId === outputVarNodeId) - if (!targetVar) return VarType.string + if (!targetVar) + return VarType.string let arrayType: VarType = VarType.string @@ -899,7 +918,8 @@ const getIterationItemType = ({ const isLast = i === valueSelector.length - 1 curr = Array.isArray(curr) ? curr.find(v => v.variable === key) : [] - if (isLast) arrayType = curr?.type + if (isLast) + arrayType = curr?.type else if (curr?.type === VarType.object || curr?.type === VarType.file) curr = curr.children || [] } @@ -927,8 +947,8 @@ const getLoopItemType = ({ valueSelector, beforeNodesOutputVars, }: { - valueSelector: ValueSelector; - beforeNodesOutputVars: NodeOutPutVar[]; + valueSelector: ValueSelector + beforeNodesOutputVars: NodeOutPutVar[] }): VarType => { const outputVarNodeId = valueSelector[0] const isSystem = isSystemVar(valueSelector) @@ -936,7 +956,8 @@ const getLoopItemType = ({ const targetVar = isSystem ? beforeNodesOutputVars.find(v => v.isStartNode) : beforeNodesOutputVars.find(v => v.nodeId === outputVarNodeId) - if (!targetVar) return VarType.string + if (!targetVar) + return VarType.string let arrayType: VarType = VarType.string @@ -993,21 +1014,22 @@ export const getVarType = ({ schemaTypeDefinitions, preferSchemaType, }: { - valueSelector: ValueSelector; - parentNode?: Node | null; - isIterationItem?: boolean; - isLoopItem?: boolean; - availableNodes: any[]; - isChatMode: boolean; - isConstant?: boolean; - environmentVariables?: EnvironmentVariable[]; - conversationVariables?: ConversationVariable[]; - ragVariables?: RAGPipelineVariable[]; - allPluginInfoList: Record<string, ToolWithProvider[]>; - schemaTypeDefinitions?: SchemaTypeDefinition[]; - preferSchemaType?: boolean; + valueSelector: ValueSelector + parentNode?: Node | null + isIterationItem?: boolean + isLoopItem?: boolean + availableNodes: any[] + isChatMode: boolean + isConstant?: boolean + environmentVariables?: EnvironmentVariable[] + conversationVariables?: ConversationVariable[] + ragVariables?: RAGPipelineVariable[] + allPluginInfoList: Record<string, ToolWithProvider[]> + schemaTypeDefinitions?: SchemaTypeDefinition[] + preferSchemaType?: boolean }): VarType => { - if (isConstant) return VarType.string + if (isConstant) + return VarType.string const beforeNodesOutputVars = toNodeOutputVars( availableNodes, @@ -1035,7 +1057,8 @@ export const getVarType = ({ }) return itemType } - if (valueSelector[1] === 'index') return VarType.number + if (valueSelector[1] === 'index') + return VarType.number } const isLoopInnerVar = parentNode?.data.type === BlockEnum.Loop @@ -1053,7 +1076,8 @@ export const getVarType = ({ }) return itemType } - if (valueSelector[1] === 'index') return VarType.number + if (valueSelector[1] === 'index') + return VarType.number } const isGlobal = isGlobalVar(valueSelector) @@ -1070,16 +1094,20 @@ export const getVarType = ({ }) const targetVarNodeId = (() => { - if (isInStartNodeSysVar) return startNode?.id - if (isGlobal) return 'global' - if (isInNodeRagVariable) return valueSelector[1] + if (isInStartNodeSysVar) + return startNode?.id + if (isGlobal) + return 'global' + if (isInNodeRagVariable) + return valueSelector[1] return valueSelector[0] })() const targetVar = beforeNodesOutputVars.find( v => v.nodeId === targetVarNodeId, ) - if (!targetVar) return VarType.string + if (!targetVar) + return VarType.string let type: VarType = VarType.string let curr: any = targetVar.vars @@ -1091,12 +1119,15 @@ export const getVarType = ({ } else { const targetVar = curr.find((v: any) => { - if (isInNodeRagVariable) return v.variable === valueSelector.join('.') + if (isInNodeRagVariable) + return v.variable === valueSelector.join('.') return v.variable === valueSelector[1] }) - if (!targetVar) return VarType.string + if (!targetVar) + return VarType.string - if (isInNodeRagVariable) return targetVar.type + if (isInNodeRagVariable) + return targetVar.type const isStructuredOutputVar = !!targetVar.children?.schema?.properties if (isStructuredOutputVar) { @@ -1109,10 +1140,12 @@ export const getVarType = ({ let currProperties = targetVar.children.schema; (valueSelector as ValueSelector).slice(2).forEach((key, i) => { const isLast = i === valueSelector.length - 3 - if (!currProperties) return + if (!currProperties) + return currProperties = currProperties.properties[key] - if (isLast) type = structTypeToVarType(currProperties?.type) + if (isLast) + type = structTypeToVarType(currProperties?.type) }) return type } @@ -1148,20 +1181,20 @@ export const toNodeAvailableVars = ({ allPluginInfoList, schemaTypeDefinitions, }: { - parentNode?: Node | null; - t?: any; + parentNode?: Node | null + t?: any // to get those nodes output vars - beforeNodes: Node[]; - isChatMode: boolean; + beforeNodes: Node[] + isChatMode: boolean // env - environmentVariables?: EnvironmentVariable[]; + environmentVariables?: EnvironmentVariable[] // chat var - conversationVariables?: ConversationVariable[]; + conversationVariables?: ConversationVariable[] // rag variables - ragVariables?: RAGPipelineVariable[]; - filterVar: (payload: Var, selector: ValueSelector) => boolean; - allPluginInfoList: Record<string, ToolWithProvider[]>; - schemaTypeDefinitions?: SchemaTypeDefinition[]; + ragVariables?: RAGPipelineVariable[] + filterVar: (payload: Var, selector: ValueSelector) => boolean + allPluginInfoList: Record<string, ToolWithProvider[]> + schemaTypeDefinitions?: SchemaTypeDefinition[] }): NodeOutPutVar[] => { const beforeNodesOutputVars = toNodeOutputVars( beforeNodes, @@ -1190,13 +1223,13 @@ export const toNodeAvailableVars = ({ const itemChildren = itemType === VarType.file ? { - children: OUTPUT_FILE_SUB_VARIABLES.map((key) => { - return { - variable: key, - type: key === 'size' ? VarType.number : VarType.string, - } - }), - } + children: OUTPUT_FILE_SUB_VARIABLES.map((key) => { + return { + variable: key, + type: key === 'size' ? VarType.number : VarType.string, + } + }), + } : {} const iterationVar = { nodeId: iterationNode?.id, @@ -1216,24 +1249,28 @@ export const toNodeAvailableVars = ({ const iterationIndex = beforeNodesOutputVars.findIndex( v => v.nodeId === iterationNode?.id, ) - if (iterationIndex > -1) beforeNodesOutputVars.splice(iterationIndex, 1) + if (iterationIndex > -1) + beforeNodesOutputVars.splice(iterationIndex, 1) beforeNodesOutputVars.unshift(iterationVar) } return beforeNodesOutputVars } export const getNodeInfoById = (nodes: any, id: string) => { - if (!isArray(nodes)) return + if (!isArray(nodes)) + return return nodes.find((node: any) => node.id === id) } const matchNotSystemVars = (prompts: string[]) => { - if (!prompts) return [] + if (!prompts) + return [] const allVars: string[] = [] prompts.forEach((prompt) => { VAR_REGEX.lastIndex = 0 - if (typeof prompt !== 'string') return + if (typeof prompt !== 'string') + return allVars.push(...(prompt.match(VAR_REGEX) || [])) }) const uniqVars = uniq(allVars).map(v => @@ -1247,9 +1284,11 @@ const replaceOldVarInText = ( oldVar: ValueSelector, newVar: ValueSelector, ) => { - if (!text || typeof text !== 'string') return text + if (!text || typeof text !== 'string') + return text - if (!newVar || newVar.length === 0) return text + if (!newVar || newVar.length === 0) + return text return text.replaceAll( `{{#${oldVar.join('.')}#}}`, @@ -1308,7 +1347,8 @@ export const getNodeUsedVars = (node: Node): ValueSelector[] => { .flatMap(c => c.conditions || []) .flatMap((c) => { const selectors: ValueSelector[] = [] - if (c.variable_selector) selectors.push(c.variable_selector) + if (c.variable_selector) + selectors.push(c.variable_selector) // Handle sub-variable conditions if (c.sub_variable_condition && c.sub_variable_condition.conditions) { selectors.push( @@ -1391,7 +1431,7 @@ export const getNodeUsedVars = (node: Node): ValueSelector[] => { payload.datasource_parameters[key].type === ToolVarType.variable, ) .map(key => payload.datasource_parameters[key].value as string) - || [] + || [] res = [...(mixVars as ValueSelector[]), ...(vars as any)] break } @@ -1436,7 +1476,8 @@ export const getNodeUsedVars = (node: Node): ValueSelector[] => { case BlockEnum.Agent: { const payload = data as AgentNodeType const valueSelectors: ValueSelector[] = [] - if (!payload.agent_parameters) break + if (!payload.agent_parameters) + break Object.keys(payload.agent_parameters || {}).forEach((key) => { const { value } = payload.agent_parameters![key] @@ -1490,7 +1531,8 @@ export const getNodeUsedVarPassToServerKey = ( return undefined } const targetVar = findConditionInCases((data as IfElseNodeType).cases || []) - if (targetVar) res = `#${valueSelector.join('.')}#` + if (targetVar) + res = `#${valueSelector.join('.')}#` break } case BlockEnum.Code: { @@ -1500,7 +1542,8 @@ export const getNodeUsedVarPassToServerKey = ( && v.value_selector && v.value_selector.join('.') === valueSelector.join('.'), ) - if (targetVar) res = targetVar.variable + if (targetVar) + res = targetVar.variable break } case BlockEnum.TemplateTransform: { @@ -1510,7 +1553,8 @@ export const getNodeUsedVarPassToServerKey = ( && v.value_selector && v.value_selector.join('.') === valueSelector.join('.'), ) - if (targetVar) res = targetVar.variable + if (targetVar) + res = targetVar.variable break } case BlockEnum.QuestionClassifier: { @@ -1552,7 +1596,8 @@ export const findUsedVarNodes = ( const res: Node[] = [] availableNodes.forEach((node) => { const vars = getNodeUsedVars(node) - if (vars.find(v => v.join('.') === varSelector.join('.'))) res.push(node) + if (vars.find(v => v.join('.') === varSelector.join('.'))) + res.push(node) }) return res } @@ -1626,8 +1671,9 @@ export const updateNodeVars = ( if ( payload.context?.variable_selector?.join('.') === oldVarSelector.join('.') - ) + ) { payload.context.variable_selector = newVarSelector + } break } @@ -1661,8 +1707,9 @@ export const updateNodeVars = ( if ( subC.variable_selector?.join('.') === oldVarSelector.join('.') - ) + ) { subC.variable_selector = newVarSelector + } return subC }) } @@ -1820,7 +1867,8 @@ export const updateNodeVars = ( const payload = data as VariableAssignerNodeType if (payload.variables) { payload.variables = payload.variables.map((v) => { - if (v.join('.') === oldVarSelector.join('.')) v = newVarSelector + if (v.join('.') === oldVarSelector.join('.')) + v = newVarSelector return v }) } @@ -1831,7 +1879,8 @@ export const updateNodeVars = ( const payload = data as VariableAssignerNodeType if (payload.variables) { payload.variables = payload.variables.map((v) => { - if (v.join('.') === oldVarSelector.join('.')) v = newVarSelector + if (v.join('.') === oldVarSelector.join('.')) + v = newVarSelector return v }) } @@ -1882,11 +1931,11 @@ const varToValueSelectorList = ( parentValueSelector: ValueSelector, res: ValueSelector[], ) => { - if (!v.variable) return + if (!v.variable) + return res.push([...parentValueSelector, v.variable]) - const isStructuredOutput = !!(v.children as StructuredOutput)?.schema - ?.properties + const isStructuredOutput = !!(v.children as StructuredOutput)?.schema?.properties if ((v.children as Var[])?.length > 0) { (v.children as Var[]).forEach((child) => { @@ -1897,8 +1946,7 @@ const varToValueSelectorList = ( Object.keys( (v.children as StructuredOutput)?.schema?.properties || {}, ).forEach((key) => { - const type = (v.children as StructuredOutput)?.schema?.properties[key] - .type + const type = (v.children as StructuredOutput)?.schema?.properties[key].type const isArray = type === Type.array const arrayType = (v.children as StructuredOutput)?.schema?.properties[ key diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-full-path-panel.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-full-path-panel.tsx index 54e27b5e38..744567daae 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-full-path-panel.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-full-path-panel.tsx @@ -1,11 +1,11 @@ 'use client' import type { FC } from 'react' -import React from 'react' import type { Field, StructuredOutput, TypeWithArray } from '../../../llm/types' -import { Type } from '../../../llm/types' -import { PickerPanelMain as Panel } from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker' +import React from 'react' import BlockIcon from '@/app/components/workflow/block-icon' +import { PickerPanelMain as Panel } from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker' import { BlockEnum } from '@/app/components/workflow/types' +import { Type } from '../../../llm/types' type Props = { nodeName: string @@ -35,20 +35,20 @@ const VarFullPathPanel: FC<Props> = ({ type: isLast ? varType : Type.object, properties: {}, } as Field - current = current.properties[name] as { type: Type.object; properties: { [key: string]: Field; }; required: never[]; additionalProperties: false; } + current = current.properties[name] as { type: Type.object, properties: { [key: string]: Field }, required: never[], additionalProperties: false } } return { schema, } })() return ( - <div className='w-[280px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur pb-0 shadow-lg backdrop-blur-[5px]'> - <div className='flex space-x-1 border-b-[0.5px] border-divider-subtle p-3 pb-2 '> - <BlockIcon size='xs' type={nodeType} /> - <div className='system-xs-medium w-0 grow truncate text-text-secondary'>{nodeName}</div> + <div className="w-[280px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur pb-0 shadow-lg backdrop-blur-[5px]"> + <div className="flex space-x-1 border-b-[0.5px] border-divider-subtle p-3 pb-2 "> + <BlockIcon size="xs" type={nodeType} /> + <div className="system-xs-medium w-0 grow truncate text-text-secondary">{nodeName}</div> </div> <Panel - className='px-1 pb-3 pt-2' + className="px-1 pb-3 pt-2" root={{ attrName: path[0] }} payload={schema} readonly diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx index d37851f187..c9cad91236 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx @@ -1,21 +1,21 @@ 'use client' import type { FC } from 'react' +import type { ToastHandle } from '@/app/components/base/toast' +import type { ValueSelector, Var, Variable } from '@/app/components/workflow/types' +import { RiDraggable } from '@remixicon/react' +import { useDebounceFn } from 'ahooks' +import { produce } from 'immer' import React, { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { produce } from 'immer' -import RemoveButton from '../remove-button' -import VarReferencePicker from './var-reference-picker' -import Input from '@/app/components/base/input' -import type { ValueSelector, Var, Variable } from '@/app/components/workflow/types' -import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' -import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' -import type { ToastHandle } from '@/app/components/base/toast' -import Toast from '@/app/components/base/toast' import { ReactSortable } from 'react-sortablejs' import { v4 as uuid4 } from 'uuid' -import { RiDraggable } from '@remixicon/react' +import Input from '@/app/components/base/input' +import Toast from '@/app/components/base/toast' +import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' import { cn } from '@/utils/classnames' -import { useDebounceFn } from 'ahooks' +import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' +import RemoveButton from '../remove-button' +import VarReferencePicker from './var-reference-picker' type Props = { nodeId: string @@ -131,11 +131,11 @@ const VarList: FC<Props> = ({ return ( <ReactSortable - className='space-y-2' + className="space-y-2" list={listWithIds} setList={(list) => { onChange(list.map(item => item.variable)) }} - handle='.handle' - ghostClass='opacity-50' + handle=".handle" + ghostClass="opacity-50" animation={150} > {list.map((variable, index) => { @@ -147,7 +147,7 @@ const VarList: FC<Props> = ({ return ( <div className={cn('flex items-center space-x-1', 'group relative')} key={index}> <Input - wrapperClassName='w-[120px]' + wrapperClassName="w-[120px]" disabled={readonly} value={variable.variable} onChange={handleVarNameChange(index)} @@ -157,7 +157,7 @@ const VarList: FC<Props> = ({ nodeId={nodeId} readonly={readonly} isShowNodeName - className='grow' + className="grow" value={variable.variable_type === VarKindType.constant ? (variable.value || '') : (variable.value_selector || [])} isSupportConstantValue={isSupportConstantValue} onChange={handleVarReferenceChange(index)} @@ -167,12 +167,15 @@ const VarList: FC<Props> = ({ isSupportFileVar={isSupportFileVar} /> {!readonly && ( - <RemoveButton onClick={handleVarRemove(index)}/> + <RemoveButton onClick={handleVarRemove(index)} /> + )} + {canDrag && ( + <RiDraggable className={cn( + 'handle absolute -left-4 top-2.5 hidden h-3 w-3 cursor-pointer text-text-quaternary', + 'group-hover:block', + )} + /> )} - {canDrag && <RiDraggable className={cn( - 'handle absolute -left-4 top-2.5 hidden h-3 w-3 cursor-pointer text-text-quaternary', - 'group-hover:block', - )} />} </div> ) })} diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx index f532754aed..3692cb6413 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx @@ -1,7 +1,9 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { useTranslation } from 'react-i18next' +import type { CredentialFormSchema, CredentialFormSchemaSelect, FormOption } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { Tool } from '@/app/components/tools/types' +import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' +import type { CommonNodeType, Node, NodeOutPutVar, ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types' import { RiArrowDownSLine, RiCloseLine, @@ -10,23 +12,16 @@ import { RiMoreLine, } from '@remixicon/react' import { produce } from 'immer' +import { noop } from 'lodash-es' +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useNodes, useReactFlow, useStoreApi, } from 'reactflow' -import RemoveButton from '../remove-button' -import useAvailableVarList from '../../hooks/use-available-var-list' -import VarReferencePopup from './var-reference-popup' -import { getNodeInfoById, isConversationVar, isENV, isGlobalVar, isRagVariableVar, isSystemVar, removeFileVars, varTypeToStructType } from './utils' -import ConstantField from './constant-field' -import { cn } from '@/utils/classnames' -import type { CommonNodeType, Node, NodeOutPutVar, ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types' -import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' -import type { CredentialFormSchemaSelect } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { type CredentialFormSchema, type FormOption, FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { BlockEnum } from '@/app/components/workflow/types' -import { VarBlockIcon } from '@/app/components/workflow/block-icon' +import Badge from '@/app/components/base/badge' +import AddButton from '@/app/components/base/button/add-button' import { Line3 } from '@/app/components/base/icons/src/public/common' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { @@ -34,23 +29,28 @@ import { PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' +import Tooltip from '@/app/components/base/tooltip' +import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { VarBlockIcon } from '@/app/components/workflow/block-icon' +import { VAR_SHOW_NAME_MAP } from '@/app/components/workflow/constants' import { useIsChatMode, useWorkflowVariables, } from '@/app/components/workflow/hooks' -import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' // import type { BaseResource, BaseResourceProvider } from '@/app/components/workflow/nodes/_base/types' import TypeSelector from '@/app/components/workflow/nodes/_base/components/selector' -import AddButton from '@/app/components/base/button/add-button' -import Badge from '@/app/components/base/badge' -import Tooltip from '@/app/components/base/tooltip' -import { isExceptionVariable } from '@/app/components/workflow/utils' -import VarFullPathPanel from './var-full-path-panel' -import { noop } from 'lodash-es' -import type { Tool } from '@/app/components/tools/types' -import { useFetchDynamicOptions } from '@/service/use-plugins' import { VariableIconWithColor } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' -import { VAR_SHOW_NAME_MAP } from '@/app/components/workflow/constants' +import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { isExceptionVariable } from '@/app/components/workflow/utils' +import { useFetchDynamicOptions } from '@/service/use-plugins' +import { cn } from '@/utils/classnames' +import useAvailableVarList from '../../hooks/use-available-var-list' +import RemoveButton from '../remove-button' +import ConstantField from './constant-field' +import { getNodeInfoById, isConversationVar, isENV, isGlobalVar, isRagVariableVar, isSystemVar, removeFileVars, varTypeToStructType } from './utils' +import VarFullPathPanel from './var-full-path-panel' +import VarReferencePopup from './var-reference-popup' const TRIGGER_DEFAULT_WIDTH = 227 @@ -207,7 +207,7 @@ const VarReferencePicker: FC<Props> = ({ if (!hasValue) return '' const showName = VAR_SHOW_NAME_MAP[(value as ValueSelector).join('.')] - if(showName) + if (showName) return showName const isSystem = isSystemVar(value as ValueSelector) @@ -336,7 +336,8 @@ const VarReferencePicker: FC<Props> = ({ path={(value as ValueSelector).slice(1)} varType={varTypeToStructType(type)} nodeType={outputVarNode?.type} - />) + /> + ) } if (!isValidVar && hasValue) return t('workflow.errorMsg.invalidVariable') @@ -347,7 +348,10 @@ const VarReferencePicker: FC<Props> = ({ const [dynamicOptions, setDynamicOptions] = useState<FormOption[] | null>(null) const [isLoading, setIsLoading] = useState(false) const { mutateAsync: fetchDynamicOptions } = useFetchDynamicOptions( - currentProvider?.plugin_id || '', currentProvider?.name || '', currentTool?.name || '', (schema as CredentialFormSchemaSelect)?.variable || '', + currentProvider?.plugin_id || '', + currentProvider?.name || '', + currentTool?.name || '', + (schema as CredentialFormSchemaSelect)?.variable || '', 'tool', ) const handleFetchDynamicOptions = async () => { @@ -398,11 +402,16 @@ const VarReferencePicker: FC<Props> = ({ }, [schema, dynamicOptions, isLoading, value]) const variableCategory = useMemo(() => { - if (isEnv) return 'environment' - if (isChatVar) return 'conversation' - if (isGlobal) return 'global' - if (isLoopVar) return 'loop' - if (isRagVar) return 'rag' + if (isEnv) + return 'environment' + if (isChatVar) + return 'conversation' + if (isGlobal) + return 'global' + if (isLoopVar) + return 'loop' + if (isRagVar) + return 'rag' return 'system' }, [isEnv, isChatVar, isGlobal, isLoopVar, isRagVar]) @@ -413,166 +422,210 @@ const VarReferencePicker: FC<Props> = ({ onOpenChange={setOpen} placement={isAddBtnTrigger ? 'bottom-end' : 'bottom-start'} > - <WrapElem onClick={() => { - if (readonly) - return - if (!isConstant) - setOpen(!open) - else - setControlFocus(Date.now()) - }} className='group/picker-trigger-wrap relative !flex'> + <WrapElem + onClick={() => { + if (readonly) + return + if (!isConstant) + setOpen(!open) + else + setControlFocus(Date.now()) + }} + className="group/picker-trigger-wrap relative !flex" + > <> {isAddBtnTrigger ? ( - <div> - <AddButton onClick={noop}></AddButton> - </div> - ) - : (<div ref={!isSupportConstantValue ? triggerRef : null} className={cn((open || isFocus) ? 'border-gray-300' : 'border-gray-100', 'group/wrap relative flex h-8 w-full items-center', !isSupportConstantValue && 'rounded-lg bg-components-input-bg-normal p-1', isInTable && 'border-none bg-transparent', readonly && 'bg-components-input-bg-disabled')}> - {isSupportConstantValue - ? <div onClick={(e) => { - e.stopPropagation() - setOpen(false) - setControlFocus(Date.now()) - }} className='mr-1 flex h-full items-center space-x-1'> - <TypeSelector - noLeft - trigger={ - <div className='radius-md flex h-8 items-center bg-components-input-bg-normal px-2'> - <div className='system-sm-regular mr-1 text-components-input-text-filled'>{varKindTypes.find(item => item.value === varKindType)?.label}</div> - <RiArrowDownSLine className='h-4 w-4 text-text-quaternary' /> - </div> - } - popupClassName='top-8' - readonly={readonly} - value={varKindType} - options={varKindTypes} - onChange={handleVarKindTypeChange} - showChecked - /> + <div> + <AddButton onClick={noop}></AddButton> </div> - : (!hasValue && <div className='ml-1.5 mr-1'> - <Variable02 className={`h-4 w-4 ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'}`} /> - </div>)} - {isConstant - ? ( - <ConstantField - value={value as string} - onChange={onChange as ((value: string | number, varKindType: VarKindType, varInfo?: Var) => void)} - schema={schemaWithDynamicSelect as CredentialFormSchema} - readonly={readonly} - isLoading={isLoading} - /> - ) - : ( - <VarPickerWrap - onClick={() => { - if (readonly) - return - if (!isConstant) - setOpen(!open) - else - setControlFocus(Date.now()) - }} - className='h-full grow' - > - <div ref={isSupportConstantValue ? triggerRef : null} className={cn('h-full', isSupportConstantValue && 'flex items-center rounded-lg bg-components-panel-bg py-1 pl-1')}> - <Tooltip noDecoration={isShowAPart} popupContent={tooltipPopup}> - <div className={cn('h-full items-center rounded-[5px] px-1.5', hasValue ? 'inline-flex bg-components-badge-white-to-dark' : 'flex')}> - {hasValue - ? ( - <> - {isShowNodeName && !isEnv && !isChatVar && !isGlobal && !isRagVar && ( - <div className='flex items-center' onClick={(e) => { - if (e.metaKey || e.ctrlKey) { - e.stopPropagation() - handleVariableJump(outputVarNode?.id) - } - }}> - <div className='h-3 px-[1px]'> - {outputVarNode?.type && <VarBlockIcon - className='!text-text-primary' - type={outputVarNode.type} - />} - </div> - <div className='mx-0.5 truncate text-xs font-medium text-text-secondary' title={outputVarNode?.title} style={{ - maxWidth: maxNodeNameWidth, - }}>{outputVarNode?.title}</div> - <Line3 className='mr-0.5'></Line3> - </div> - )} - {isShowAPart && ( - <div className='flex items-center'> - <RiMoreLine className='h-3 w-3 text-text-secondary' /> - <Line3 className='mr-0.5 text-divider-deep'></Line3> - </div> - )} - <div className='flex items-center text-text-accent'> - {isLoading && <RiLoader4Line className='h-3.5 w-3.5 animate-spin text-text-secondary' />} - <VariableIconWithColor - variables={value as ValueSelector} - variableCategory={variableCategory} - isExceptionVariable={isException} - /> - <div className={cn('ml-0.5 truncate text-xs font-medium', isEnv && '!text-text-secondary', isChatVar && 'text-util-colors-teal-teal-700', isException && 'text-text-warning', isGlobal && 'text-util-colors-orange-orange-600')} title={varName} style={{ - maxWidth: maxVarNameWidth, - }}>{varName}</div> - </div> - <div className='system-xs-regular ml-0.5 truncate text-center capitalize text-text-tertiary' title={type} style={{ - maxWidth: maxTypeWidth, - }}>{type}</div> - {!isValidVar && <RiErrorWarningFill className='ml-0.5 h-3 w-3 text-text-destructive' />} - </> - ) - : <div className={`overflow-hidden ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'} system-sm-regular text-ellipsis`}> - {isLoading ? ( - <div className='flex items-center'> - <RiLoader4Line className='mr-1 h-3.5 w-3.5 animate-spin text-text-secondary' /> - <span>{placeholder ?? t('workflow.common.setVarValuePlaceholder')}</span> - </div> - ) : ( - placeholder ?? t('workflow.common.setVarValuePlaceholder') - )} - </div>} + ) + : ( + <div ref={!isSupportConstantValue ? triggerRef : null} className={cn((open || isFocus) ? 'border-gray-300' : 'border-gray-100', 'group/wrap relative flex h-8 w-full items-center', !isSupportConstantValue && 'rounded-lg bg-components-input-bg-normal p-1', isInTable && 'border-none bg-transparent', readonly && 'bg-components-input-bg-disabled')}> + {isSupportConstantValue + ? ( + <div + onClick={(e) => { + e.stopPropagation() + setOpen(false) + setControlFocus(Date.now()) + }} + className="mr-1 flex h-full items-center space-x-1" + > + <TypeSelector + noLeft + trigger={( + <div className="radius-md flex h-8 items-center bg-components-input-bg-normal px-2"> + <div className="system-sm-regular mr-1 text-components-input-text-filled">{varKindTypes.find(item => item.value === varKindType)?.label}</div> + <RiArrowDownSLine className="h-4 w-4 text-text-quaternary" /> + </div> + )} + popupClassName="top-8" + readonly={readonly} + value={varKindType} + options={varKindTypes} + onChange={handleVarKindTypeChange} + showChecked + /> </div> - </Tooltip> - </div> + ) + : (!hasValue && ( + <div className="ml-1.5 mr-1"> + <Variable02 className={`h-4 w-4 ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'}`} /> + </div> + ))} + {isConstant + ? ( + <ConstantField + value={value as string} + onChange={onChange as ((value: string | number, varKindType: VarKindType, varInfo?: Var) => void)} + schema={schemaWithDynamicSelect as CredentialFormSchema} + readonly={readonly} + isLoading={isLoading} + /> + ) + : ( + <VarPickerWrap + onClick={() => { + if (readonly) + return + if (!isConstant) + setOpen(!open) + else + setControlFocus(Date.now()) + }} + className="h-full grow" + > + <div ref={isSupportConstantValue ? triggerRef : null} className={cn('h-full', isSupportConstantValue && 'flex items-center rounded-lg bg-components-panel-bg py-1 pl-1')}> + <Tooltip noDecoration={isShowAPart} popupContent={tooltipPopup}> + <div className={cn('h-full items-center rounded-[5px] px-1.5', hasValue ? 'inline-flex bg-components-badge-white-to-dark' : 'flex')}> + {hasValue + ? ( + <> + {isShowNodeName && !isEnv && !isChatVar && !isGlobal && !isRagVar && ( + <div + className="flex items-center" + onClick={(e) => { + if (e.metaKey || e.ctrlKey) { + e.stopPropagation() + handleVariableJump(outputVarNode?.id) + } + }} + > + <div className="h-3 px-[1px]"> + {outputVarNode?.type && ( + <VarBlockIcon + className="!text-text-primary" + type={outputVarNode.type} + /> + )} + </div> + <div + className="mx-0.5 truncate text-xs font-medium text-text-secondary" + title={outputVarNode?.title} + style={{ + maxWidth: maxNodeNameWidth, + }} + > + {outputVarNode?.title} + </div> + <Line3 className="mr-0.5"></Line3> + </div> + )} + {isShowAPart && ( + <div className="flex items-center"> + <RiMoreLine className="h-3 w-3 text-text-secondary" /> + <Line3 className="mr-0.5 text-divider-deep"></Line3> + </div> + )} + <div className="flex items-center text-text-accent"> + {isLoading && <RiLoader4Line className="h-3.5 w-3.5 animate-spin text-text-secondary" />} + <VariableIconWithColor + variables={value as ValueSelector} + variableCategory={variableCategory} + isExceptionVariable={isException} + /> + <div + className={cn('ml-0.5 truncate text-xs font-medium', isEnv && '!text-text-secondary', isChatVar && 'text-util-colors-teal-teal-700', isException && 'text-text-warning', isGlobal && 'text-util-colors-orange-orange-600')} + title={varName} + style={{ + maxWidth: maxVarNameWidth, + }} + > + {varName} + </div> + </div> + <div + className="system-xs-regular ml-0.5 truncate text-center capitalize text-text-tertiary" + title={type} + style={{ + maxWidth: maxTypeWidth, + }} + > + {type} + </div> + {!isValidVar && <RiErrorWarningFill className="ml-0.5 h-3 w-3 text-text-destructive" />} + </> + ) + : ( + <div className={`overflow-hidden ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'} system-sm-regular text-ellipsis`}> + {isLoading + ? ( + <div className="flex items-center"> + <RiLoader4Line className="mr-1 h-3.5 w-3.5 animate-spin text-text-secondary" /> + <span>{placeholder ?? t('workflow.common.setVarValuePlaceholder')}</span> + </div> + ) + : ( + placeholder ?? t('workflow.common.setVarValuePlaceholder') + )} + </div> + )} + </div> + </Tooltip> + </div> - </VarPickerWrap> - )} - {(hasValue && !readonly && !isInTable) && (<div - className='group invisible absolute right-1 top-[50%] h-5 translate-y-[-50%] cursor-pointer rounded-md p-1 hover:bg-state-base-hover group-hover/wrap:visible' - onClick={handleClearVar} - > - <RiCloseLine className='h-3.5 w-3.5 text-text-tertiary group-hover:text-text-secondary' /> - </div>)} - {!hasValue && valueTypePlaceHolder && ( - <Badge - className=' absolute right-1 top-[50%] translate-y-[-50%] capitalize' - text={valueTypePlaceHolder} - uppercase={false} - /> + </VarPickerWrap> + )} + {(hasValue && !readonly && !isInTable) && ( + <div + className="group invisible absolute right-1 top-[50%] h-5 translate-y-[-50%] cursor-pointer rounded-md p-1 hover:bg-state-base-hover group-hover/wrap:visible" + onClick={handleClearVar} + > + <RiCloseLine className="h-3.5 w-3.5 text-text-tertiary group-hover:text-text-secondary" /> + </div> + )} + {!hasValue && valueTypePlaceHolder && ( + <Badge + className=" absolute right-1 top-[50%] translate-y-[-50%] capitalize" + text={valueTypePlaceHolder} + uppercase={false} + /> + )} + </div> )} - </div>)} {!readonly && isInTable && ( <RemoveButton - className='absolute right-1 top-0.5 hidden group-hover/picker-trigger-wrap:block' + className="absolute right-1 top-0.5 hidden group-hover/picker-trigger-wrap:block" onClick={() => onRemove?.()} /> )} {!hasValue && typePlaceHolder && ( <Badge - className='absolute right-2 top-1.5' + className="absolute right-2 top-1.5" text={typePlaceHolder} uppercase={false} /> )} </> </WrapElem> - <PortalToFollowElemContent style={{ - zIndex: zIndex || 100, - }} className='mt-1'> + <PortalToFollowElemContent + style={{ + zIndex: zIndex || 100, + }} + className="mt-1" + > {!isConstant && ( <VarReferencePopup vars={outputVars} @@ -586,7 +639,7 @@ const VarReferencePicker: FC<Props> = ({ )} </PortalToFollowElemContent> </PortalToFollowElem> - </div > + </div> ) } export default React.memo(VarReferencePicker) diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx index 5f68bcbf0c..45ad5d9f8c 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx @@ -1,12 +1,12 @@ 'use client' import type { FC } from 'react' +import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' import React, { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import VarReferenceVars from './var-reference-vars' -import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' import ListEmpty from '@/app/components/base/list-empty' import { useStore } from '@/app/components/workflow/store' import { useDocLink } from '@/context/i18n' +import VarReferenceVars from './var-reference-vars' type Props = { vars: NodeOutPutVar[] @@ -33,48 +33,59 @@ const VarReferencePopup: FC<Props> = ({ const docLink = useDocLink() // max-h-[300px] overflow-y-auto todo: use portal to handle long list return ( - <div className='space-y-1 rounded-lg border border-components-panel-border bg-components-panel-bg p-1 shadow-lg' style={{ - width: itemWidth || 228, - }}> + <div + className="space-y-1 rounded-lg border border-components-panel-border bg-components-panel-bg p-1 shadow-lg" + style={{ + width: itemWidth || 228, + }} + > {((!vars || vars.length === 0) && popupFor) ? (popupFor === 'toAssigned' - ? ( - <ListEmpty - title={t('workflow.variableReference.noAvailableVars') || ''} - description={<div className='system-xs-regular text-text-tertiary'> - {t('workflow.variableReference.noVarsForOperation')} - </div>} + ? ( + <ListEmpty + title={t('workflow.variableReference.noAvailableVars') || ''} + description={( + <div className="system-xs-regular text-text-tertiary"> + {t('workflow.variableReference.noVarsForOperation')} + </div> + )} + /> + ) + : ( + <ListEmpty + title={t('workflow.variableReference.noAssignedVars') || ''} + description={( + <div className="system-xs-regular text-text-tertiary"> + {t('workflow.variableReference.assignedVarsDescription')} + <a + target="_blank" + rel="noopener noreferrer" + className="text-text-accent-secondary" + href={docLink('/guides/workflow/variables#conversation-variables', { + 'zh-Hans': '/guides/workflow/variables#会话变量', + 'ja-JP': '/guides/workflow/variables#会話変数', + })} + > + {t('workflow.variableReference.conversationVars')} + </a> + </div> + )} + /> + )) + : ( + <VarReferenceVars + searchBoxClassName="mt-1" + vars={vars} + onChange={onChange} + itemWidth={itemWidth} + isSupportFileVar={isSupportFileVar} + zIndex={zIndex} + showManageInputField={showManageRagInputFields} + onManageInputField={() => setShowInputFieldPanel?.(true)} + preferSchemaType={preferSchemaType} /> - ) - : ( - <ListEmpty - title={t('workflow.variableReference.noAssignedVars') || ''} - description={<div className='system-xs-regular text-text-tertiary'> - {t('workflow.variableReference.assignedVarsDescription')} - <a target='_blank' rel='noopener noreferrer' - className='text-text-accent-secondary' - href={docLink('/guides/workflow/variables#conversation-variables', { - 'zh-Hans': '/guides/workflow/variables#会话变量', - 'ja-JP': '/guides/workflow/variables#会話変数', - })}> - {t('workflow.variableReference.conversationVars')} - </a> - </div>} - /> - )) - : <VarReferenceVars - searchBoxClassName='mt-1' - vars={vars} - onChange={onChange} - itemWidth={itemWidth} - isSupportFileVar={isSupportFileVar} - zIndex={zIndex} - showManageInputField={showManageRagInputFields} - onManageInputField={() => setShowInputFieldPanel?.(true)} - preferSchemaType={preferSchemaType} - /> - } - </div > + )} + </div> ) } export default React.memo(VarReferencePopup) diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx index 8461f0e5f6..5482eea996 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx @@ -1,29 +1,30 @@ 'use client' import type { FC } from 'react' -import React, { useEffect, useMemo, useRef, useState } from 'react' +import type { StructuredOutput } from '../../../llm/types' +import type { Field } from '@/app/components/workflow/nodes/llm/types' +import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' import { useHover } from 'ahooks' +import { noop } from 'lodash-es' +import React, { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { cn } from '@/utils/classnames' -import { type NodeOutPutVar, type ValueSelector, type Var, VarType } from '@/app/components/workflow/types' import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows' +import { CodeAssistant, MagicEdit } from '@/app/components/base/icons/src/vender/line/general' +import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' +import Input from '@/app/components/base/input' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import Input from '@/app/components/base/input' -import { checkKeys } from '@/utils/var' -import type { StructuredOutput } from '../../../llm/types' -import { Type } from '../../../llm/types' -import PickerStructurePanel from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker' -import { isSpecialVar, varTypeToStructType } from './utils' -import type { Field } from '@/app/components/workflow/nodes/llm/types' -import { noop } from 'lodash-es' -import { CodeAssistant, MagicEdit } from '@/app/components/base/icons/src/vender/line/general' -import ManageInputField from './manage-input-field' -import { VariableIconWithColor } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' -import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { VAR_SHOW_NAME_MAP } from '@/app/components/workflow/constants' +import PickerStructurePanel from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker' +import { VariableIconWithColor } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' +import { VarType } from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' +import { checkKeys } from '@/utils/var' +import { Type } from '../../../llm/types' +import ManageInputField from './manage-input-field' +import { isSpecialVar, varTypeToStructType } from './utils' type ItemProps = { nodeId: string @@ -74,16 +75,16 @@ const Item: FC<ItemProps> = ({ switch (variable) { case 'current': Icon = isInCodeGeneratorInstructionEditor ? CodeAssistant : MagicEdit - return <Icon className='h-3.5 w-3.5 shrink-0 text-util-colors-violet-violet-600' /> + return <Icon className="h-3.5 w-3.5 shrink-0 text-util-colors-violet-violet-600" /> case 'error_message': - return <Variable02 className='h-3.5 w-3.5 shrink-0 text-util-colors-orange-dark-orange-dark-600' /> + return <Variable02 className="h-3.5 w-3.5 shrink-0 text-util-colors-orange-dark-orange-dark-600" /> default: - return <Variable02 className='h-3.5 w-3.5 shrink-0 text-text-accent' /> + return <Variable02 className="h-3.5 w-3.5 shrink-0 text-text-accent" /> } }, [isFlat, isInCodeGeneratorInstructionEditor, itemData.variable]) const varName = useMemo(() => { - if(VAR_SHOW_NAME_MAP[itemData.variable]) + if (VAR_SHOW_NAME_MAP[itemData.variable]) return VAR_SHOW_NAME_MAP[itemData.variable] if (!isFlat) @@ -95,7 +96,8 @@ const Item: FC<ItemProps> = ({ }, [isFlat, isInCodeGeneratorInstructionEditor, itemData.variable]) const objStructuredOutput: StructuredOutput | null = useMemo(() => { - if (!isObj) return null + if (!isObj) + return null const properties: Record<string, Field> = {} const childrenVars = (itemData.children as Var[]) || [] childrenVars.forEach((c) => { @@ -160,19 +162,23 @@ const Item: FC<ItemProps> = ({ } } const variableCategory = useMemo(() => { - if (isEnv) return 'environment' - if (isChatVar) return 'conversation' - if (isLoopVar) return 'loop' - if (isRagVariable) return 'rag' + if (isEnv) + return 'environment' + if (isChatVar) + return 'conversation' + if (isLoopVar) + return 'loop' + if (isRagVariable) + return 'rag' return 'system' }, [isEnv, isChatVar, isSys, isLoopVar, isRagVariable]) return ( <PortalToFollowElem open={open} onOpenChange={noop} - placement='left-start' + placement="left-start" > - <PortalToFollowElemTrigger className='w-full'> + <PortalToFollowElemTrigger className="w-full"> <div ref={itemRef} className={cn( @@ -180,43 +186,45 @@ const Item: FC<ItemProps> = ({ isHovering && ((isObj || isStructureOutput) ? 'bg-components-panel-on-panel-item-bg-hover' : 'bg-state-base-hover'), 'relative flex h-6 w-full cursor-pointer items-center rounded-md pl-3', className, - ) - } + )} onClick={handleChosen} onMouseDown={e => e.preventDefault()} > - <div className='flex w-0 grow items-center'> - {!isFlat && <VariableIconWithColor - variables={itemData.variable.split('.')} - variableCategory={variableCategory} - isExceptionVariable={isException} - />} + <div className="flex w-0 grow items-center"> + {!isFlat && ( + <VariableIconWithColor + variables={itemData.variable.split('.')} + variableCategory={variableCategory} + isExceptionVariable={isException} + /> + )} {isFlat && flatVarIcon} {!isEnv && !isChatVar && !isRagVariable && ( - <div title={itemData.variable} className='system-sm-medium ml-1 w-0 grow truncate text-text-secondary'>{varName}</div> + <div title={itemData.variable} className="system-sm-medium ml-1 w-0 grow truncate text-text-secondary">{varName}</div> )} {isEnv && ( - <div title={itemData.variable} className='system-sm-medium ml-1 w-0 grow truncate text-text-secondary'>{itemData.variable.replace('env.', '')}</div> + <div title={itemData.variable} className="system-sm-medium ml-1 w-0 grow truncate text-text-secondary">{itemData.variable.replace('env.', '')}</div> )} {isChatVar && ( - <div title={itemData.des} className='system-sm-medium ml-1 w-0 grow truncate text-text-secondary'>{itemData.variable.replace('conversation.', '')}</div> + <div title={itemData.des} className="system-sm-medium ml-1 w-0 grow truncate text-text-secondary">{itemData.variable.replace('conversation.', '')}</div> )} {isRagVariable && ( - <div title={itemData.des} className='system-sm-medium ml-1 w-0 grow truncate text-text-secondary'>{itemData.variable.split('.').slice(-1)[0]}</div> + <div title={itemData.des} className="system-sm-medium ml-1 w-0 grow truncate text-text-secondary">{itemData.variable.split('.').slice(-1)[0]}</div> )} </div> - <div className='ml-1 shrink-0 text-xs font-normal capitalize text-text-tertiary'>{(preferSchemaType && itemData.schemaType) ? itemData.schemaType : itemData.type}</div> + <div className="ml-1 shrink-0 text-xs font-normal capitalize text-text-tertiary">{(preferSchemaType && itemData.schemaType) ? itemData.schemaType : itemData.type}</div> { (isObj || isStructureOutput) && ( <ChevronRight className={cn('ml-0.5 h-3 w-3 text-text-quaternary', isHovering && 'text-text-tertiary')} /> ) } - </div > - </PortalToFollowElemTrigger > + </div> + </PortalToFollowElemTrigger> <PortalToFollowElemContent style={{ zIndex: zIndex || 100, - }}> + }} + > {(isStructureOutput || isObj) && ( <PickerStructurePanel root={{ nodeId, nodeName: title, attrName: itemData.variable, attrAlias: itemData.schemaType }} @@ -228,7 +236,7 @@ const Item: FC<ItemProps> = ({ /> )} </PortalToFollowElemContent> - </PortalToFollowElem > + </PortalToFollowElem> ) } @@ -308,7 +316,7 @@ const VarReferenceVars: FC<Props> = ({ <> <div className={cn('var-search-input-wrapper mx-2 mb-2 mt-2', searchBoxClassName)} onClick={e => e.stopPropagation()}> <Input - className='var-search-input' + className="var-search-input" showLeftIcon showClearIcon value={searchText} @@ -320,54 +328,63 @@ const VarReferenceVars: FC<Props> = ({ autoFocus={autoFocus} /> </div> - <div className='relative left-[-4px] h-[0.5px] bg-black/5' style={{ - width: 'calc(100% + 8px)', - }}></div> + <div + className="relative left-[-4px] h-[0.5px] bg-black/5" + style={{ + width: 'calc(100% + 8px)', + }} + > + </div> </> ) } {filteredVars.length > 0 - ? <div className={cn('max-h-[85vh] overflow-y-auto', maxHeightClass)}> + ? ( + <div className={cn('max-h-[85vh] overflow-y-auto', maxHeightClass)}> - { - filteredVars.map((item, i) => ( - <div key={i} className={cn(!item.isFlat && 'mt-3', i === 0 && item.isFlat && 'mt-2')}> - {!item.isFlat && ( - <div - className='system-xs-medium-uppercase truncate px-3 leading-[22px] text-text-tertiary' - title={item.title} - >{item.title}</div> - )} - {item.vars.map((v, j) => ( - <Item - key={j} - title={item.title} - nodeId={item.nodeId} - objPath={[]} - itemData={v} - onChange={onChange} - itemWidth={itemWidth} - isSupportFileVar={isSupportFileVar} - isException={v.isException} - isLoopVar={item.isLoop} - isFlat={item.isFlat} - isInCodeGeneratorInstructionEditor={isInCodeGeneratorInstructionEditor} - zIndex={zIndex} - preferSchemaType={preferSchemaType} - /> - ))} - {item.isFlat && !filteredVars[i + 1]?.isFlat && !!filteredVars.find(item => !item.isFlat) && ( - <div className='relative mt-[14px] flex items-center space-x-1'> - <div className='h-0 w-3 shrink-0 border border-divider-subtle'></div> - <div className='system-2xs-semibold-uppercase text-text-tertiary'>{t('workflow.debug.lastOutput')}</div> - <div className='h-0 shrink-0 grow border border-divider-subtle'></div> + { + filteredVars.map((item, i) => ( + <div key={i} className={cn(!item.isFlat && 'mt-3', i === 0 && item.isFlat && 'mt-2')}> + {!item.isFlat && ( + <div + className="system-xs-medium-uppercase truncate px-3 leading-[22px] text-text-tertiary" + title={item.title} + > + {item.title} + </div> + )} + {item.vars.map((v, j) => ( + <Item + key={j} + title={item.title} + nodeId={item.nodeId} + objPath={[]} + itemData={v} + onChange={onChange} + itemWidth={itemWidth} + isSupportFileVar={isSupportFileVar} + isException={v.isException} + isLoopVar={item.isLoop} + isFlat={item.isFlat} + isInCodeGeneratorInstructionEditor={isInCodeGeneratorInstructionEditor} + zIndex={zIndex} + preferSchemaType={preferSchemaType} + /> + ))} + {item.isFlat && !filteredVars[i + 1]?.isFlat && !!filteredVars.find(item => !item.isFlat) && ( + <div className="relative mt-[14px] flex items-center space-x-1"> + <div className="h-0 w-3 shrink-0 border border-divider-subtle"></div> + <div className="system-2xs-semibold-uppercase text-text-tertiary">{t('workflow.debug.lastOutput')}</div> + <div className="h-0 shrink-0 grow border border-divider-subtle"></div> + </div> + )} </div> - )} - </div>)) - } - </div> - : <div className='mt-2 pl-3 text-xs font-medium uppercase leading-[18px] text-gray-500'>{t('workflow.common.noVar')}</div>} + )) + } + </div> + ) + : <div className="mt-2 pl-3 text-xs font-medium uppercase leading-[18px] text-gray-500">{t('workflow.common.noVar')}</div>} { showManageInputField && ( <ManageInputField diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-type-picker.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-type-picker.tsx index fa2b9c1d6a..b6b08bc799 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-type-picker.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-type-picker.tsx @@ -1,15 +1,15 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useState } from 'react' import { RiArrowDownSLine } from '@remixicon/react' -import { cn } from '@/utils/classnames' +import React, { useCallback, useState } from 'react' +import { Check } from '@/app/components/base/icons/src/vender/line/general' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Check } from '@/app/components/base/icons/src/vender/line/general' import { VarType } from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' type Props = { className?: string @@ -39,27 +39,28 @@ const VarReferencePicker: FC<Props> = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-start' + placement="bottom-start" offset={4} > - <PortalToFollowElemTrigger onClick={() => setOpen(!open)} className='w-[120px] cursor-pointer'> - <div className='flex h-8 items-center justify-between rounded-lg border-0 bg-components-input-bg-normal px-2.5 text-[13px] text-text-primary'> - <div className='w-0 grow truncate capitalize' title={value}>{value}</div> - <RiArrowDownSLine className='h-3.5 w-3.5 shrink-0 text-text-secondary' /> + <PortalToFollowElemTrigger onClick={() => setOpen(!open)} className="w-[120px] cursor-pointer"> + <div className="flex h-8 items-center justify-between rounded-lg border-0 bg-components-input-bg-normal px-2.5 text-[13px] text-text-primary"> + <div className="w-0 grow truncate capitalize" title={value}>{value}</div> + <RiArrowDownSLine className="h-3.5 w-3.5 shrink-0 text-text-secondary" /> </div> </PortalToFollowElemTrigger> <PortalToFollowElemContent style={{ zIndex: 100, - }}> - <div className='w-[120px] rounded-lg bg-components-panel-bg p-1 shadow-sm'> + }} + > + <div className="w-[120px] rounded-lg bg-components-panel-bg p-1 shadow-sm"> {TYPES.map(type => ( <div key={type} - className='flex h-[30px] cursor-pointer items-center justify-between rounded-lg pl-3 pr-2 text-[13px] text-text-primary hover:bg-state-base-hover' + className="flex h-[30px] cursor-pointer items-center justify-between rounded-lg pl-3 pr-2 text-[13px] text-text-primary hover:bg-state-base-hover" onClick={handleChange(type)} > - <div className='w-0 grow truncate capitalize'>{type}</div> - {type === value && <Check className='h-4 w-4 shrink-0 text-text-accent' />} + <div className="w-0 grow truncate capitalize">{type}</div> + {type === value && <Check className="h-4 w-4 shrink-0 text-text-accent" />} </div> ))} </div> diff --git a/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-icon.tsx b/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-icon.tsx index b97287da1e..c515cd9e7a 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-icon.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-icon.tsx @@ -1,7 +1,7 @@ +import type { VarInInspectType } from '@/types/workflow' import { memo } from 'react' import { cn } from '@/utils/classnames' import { useVarIcon } from '../hooks' -import type { VarInInspectType } from '@/types/workflow' export type VariableIconProps = { className?: string diff --git a/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-label.tsx b/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-label.tsx index 3eb31ae5e0..63b392482f 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-label.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-label.tsx @@ -1,17 +1,17 @@ -import { memo } from 'react' -import { capitalize } from 'lodash-es' +import type { VariablePayload } from '../types' import { RiErrorWarningFill, RiMoreLine, } from '@remixicon/react' -import type { VariablePayload } from '../types' +import { capitalize } from 'lodash-es' +import { memo } from 'react' +import Tooltip from '@/app/components/base/tooltip' +import { cn } from '@/utils/classnames' +import { isConversationVar, isENV, isGlobalVar, isRagVariableVar } from '../../utils' import { useVarColor } from '../hooks' -import VariableNodeLabel from './variable-node-label' import VariableIcon from './variable-icon' import VariableName from './variable-name' -import { cn } from '@/utils/classnames' -import Tooltip from '@/app/components/base/tooltip' -import { isConversationVar, isENV, isGlobalVar, isRagVariableVar } from '../../utils' +import VariableNodeLabel from './variable-node-label' const VariableLabel = ({ nodeType, @@ -46,8 +46,8 @@ const VariableLabel = ({ { notShowFullPath && ( <> - <RiMoreLine className='h-3 w-3 shrink-0 text-text-secondary' /> - <div className='system-xs-regular shrink-0 text-divider-deep'>/</div> + <RiMoreLine className="h-3 w-3 shrink-0 text-text-secondary" /> + <div className="system-xs-regular shrink-0 text-divider-deep">/</div> </> ) } @@ -62,7 +62,7 @@ const VariableLabel = ({ /> { variableType && ( - <div className='system-xs-regular shrink-0 text-text-tertiary'> + <div className="system-xs-regular shrink-0 text-text-tertiary"> {capitalize(variableType)} </div> ) @@ -73,7 +73,7 @@ const VariableLabel = ({ popupContent={errorMsg} asChild > - <RiErrorWarningFill className='h-3 w-3 shrink-0 text-text-destructive' /> + <RiErrorWarningFill className="h-3 w-3 shrink-0 text-text-destructive" /> </Tooltip> ) } diff --git a/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-name.tsx b/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-name.tsx index ea1ee539ed..d3d9b6bbcf 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-name.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-name.tsx @@ -1,6 +1,6 @@ import { memo } from 'react' -import { useVarName } from '../hooks' import { cn } from '@/utils/classnames' +import { useVarName } from '../hooks' type VariableNameProps = { variables: string[] diff --git a/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-node-label.tsx b/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-node-label.tsx index 35b539d97a..35434141ca 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-node-label.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-node-label.tsx @@ -1,6 +1,6 @@ +import type { BlockEnum } from '@/app/components/workflow/types' import { memo } from 'react' import { VarBlockIcon } from '@/app/components/workflow/block-icon' -import type { BlockEnum } from '@/app/components/workflow/types' type VariableNodeLabelProps = { nodeType?: BlockEnum @@ -17,19 +17,19 @@ const VariableNodeLabel = ({ <> <VarBlockIcon type={nodeType} - className='shrink-0 text-text-secondary' + className="shrink-0 text-text-secondary" /> { nodeTitle && ( <div - className='system-xs-medium max-w-[60px] truncate text-text-secondary' + className="system-xs-medium max-w-[60px] truncate text-text-secondary" title={nodeTitle} > {nodeTitle} </div> ) } - <div className='system-xs-regular shrink-0 text-divider-deep'>/</div> + <div className="system-xs-regular shrink-0 text-divider-deep">/</div> </> ) } diff --git a/web/app/components/workflow/nodes/_base/components/variable/variable-label/hooks.ts b/web/app/components/workflow/nodes/_base/components/variable/variable-label/hooks.ts index bb388d429a..a892bd9987 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/variable-label/hooks.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/variable-label/hooks.ts @@ -1,8 +1,10 @@ import { useMemo } from 'react' -import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { BubbleX, Env, GlobalVariable } from '@/app/components/base/icons/src/vender/line/others' -import { Loop } from '@/app/components/base/icons/src/vender/workflow' import { InputField } from '@/app/components/base/icons/src/vender/pipeline' +import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' +import { Loop } from '@/app/components/base/icons/src/vender/workflow' +import { VAR_SHOW_NAME_MAP } from '@/app/components/workflow/constants' +import { VarInInspectType } from '@/types/workflow' import { isConversationVar, isENV, @@ -10,8 +12,6 @@ import { isRagVariableVar, isSystemVar, } from '../utils' -import { VarInInspectType } from '@/types/workflow' -import { VAR_SHOW_NAME_MAP } from '@/app/components/workflow/constants' export const useVarIcon = (variables: string[], variableCategory?: VarInInspectType | string) => { if (variableCategory === 'loop') diff --git a/web/app/components/workflow/nodes/_base/components/variable/variable-label/index.tsx b/web/app/components/workflow/nodes/_base/components/variable/variable-label/index.tsx index 012522e0aa..9cb2176fcd 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/variable-label/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/variable-label/index.tsx @@ -1,5 +1,5 @@ -export { default as VariableLabelInSelect } from './variable-label-in-select' +export { default as VariableIconWithColor } from './variable-icon-with-color' export { default as VariableLabelInEditor } from './variable-label-in-editor' export { default as VariableLabelInNode } from './variable-label-in-node' +export { default as VariableLabelInSelect } from './variable-label-in-select' export { default as VariableLabelInText } from './variable-label-in-text' -export { default as VariableIconWithColor } from './variable-icon-with-color' diff --git a/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-icon-with-color.tsx b/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-icon-with-color.tsx index 793f6a93e5..bbe580e79e 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-icon-with-color.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-icon-with-color.tsx @@ -1,8 +1,8 @@ -import { memo } from 'react' -import VariableIcon from './base/variable-icon' import type { VariableIconProps } from './base/variable-icon' -import { useVarColor } from './hooks' +import { memo } from 'react' import { cn } from '@/utils/classnames' +import VariableIcon from './base/variable-icon' +import { useVarColor } from './hooks' type VariableIconWithColorProps = { isExceptionVariable?: boolean diff --git a/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-editor.tsx b/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-editor.tsx index 05774e59c2..8a65996dc0 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-editor.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-editor.tsx @@ -1,8 +1,8 @@ -import { memo } from 'react' import type { VariablePayload } from './types' +import { memo } from 'react' +import { cn } from '@/utils/classnames' import VariableLabel from './base/variable-label' import { useVarBgColorInEditor } from './hooks' -import { cn } from '@/utils/classnames' type VariableLabelInEditorProps = { isSelected?: boolean diff --git a/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-node.tsx b/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-node.tsx index db3484affa..5aebf00e11 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-node.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-node.tsx @@ -1,7 +1,7 @@ -import { memo } from 'react' import type { VariablePayload } from './types' -import VariableLabel from './base/variable-label' +import { memo } from 'react' import { cn } from '@/utils/classnames' +import VariableLabel from './base/variable-label' const VariableLabelInNode = (variablePayload: VariablePayload) => { return ( diff --git a/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-select.tsx b/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-select.tsx index 34e7b5f461..259fc26689 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-select.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-select.tsx @@ -1,5 +1,5 @@ -import { memo } from 'react' import type { VariablePayload } from './types' +import { memo } from 'react' import VariableLabel from './base/variable-label' const VariableLabelInSelect = (variablePayload: VariablePayload) => { diff --git a/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-text.tsx b/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-text.tsx index eb66943fbc..2636ad747b 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-text.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/variable-label/variable-label-in-text.tsx @@ -1,7 +1,7 @@ -import { memo } from 'react' import type { VariablePayload } from './types' -import VariableLabel from './base/variable-label' +import { memo } from 'react' import { cn } from '@/utils/classnames' +import VariableLabel from './base/variable-label' const VariableLabelInText = (variablePayload: VariablePayload) => { return ( diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx index 309b88ffe2..3624e10bb1 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx @@ -1,6 +1,27 @@ +import type { FC, ReactNode } from 'react' +import type { SimpleSubscription } from '@/app/components/plugins/plugin-detail-panel/subscription-list' +import type { CustomRunFormProps } from '@/app/components/workflow/nodes/data-source/types' +import type { Node } from '@/app/components/workflow/types' +import { + RiCloseLine, + RiPlayLargeLine, +} from '@remixicon/react' +import { debounce } from 'lodash-es' +import React, { + cloneElement, + memo, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react' +import { useTranslation } from 'react-i18next' +import { useShallow } from 'zustand/react/shallow' import { useStore as useAppStore } from '@/app/components/app/store' import { Stop } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' import Tooltip from '@/app/components/base/tooltip' +import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import { AuthCategory, @@ -10,11 +31,9 @@ import { PluginAuthInDataSourceNode, } from '@/app/components/plugins/plugin-auth' import { usePluginStore } from '@/app/components/plugins/plugin-detail-panel/store' -import type { SimpleSubscription } from '@/app/components/plugins/plugin-detail-panel/subscription-list' import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance' import BlockIcon from '@/app/components/workflow/block-icon' import { - WorkflowHistoryEvent, useAvailableBlocks, useNodeDataUpdate, useNodesInteractions, @@ -22,17 +41,17 @@ import { useNodesReadOnly, useToolIcon, useWorkflowHistory, + WorkflowHistoryEvent, } from '@/app/components/workflow/hooks' import { useHooksStore } from '@/app/components/workflow/hooks-store' import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud' import Split from '@/app/components/workflow/nodes/_base/components/split' import DataSourceBeforeRunForm from '@/app/components/workflow/nodes/data-source/before-run-form' -import type { CustomRunFormProps } from '@/app/components/workflow/nodes/data-source/types' import { DataSourceClassification } from '@/app/components/workflow/nodes/data-source/types' import { useLogs } from '@/app/components/workflow/run/hooks' import SpecialResultPanel from '@/app/components/workflow/run/special-result-panel' import { useStore } from '@/app/components/workflow/store' -import { BlockEnum, type Node, NodeRunningStatus } from '@/app/components/workflow/types' +import { BlockEnum, NodeRunningStatus } from '@/app/components/workflow/types' import { canRunBySingle, hasErrorHandleNode, @@ -45,24 +64,6 @@ import { useAllTriggerPlugins } from '@/service/use-triggers' import { FlowType } from '@/types/common' import { canFindTool } from '@/utils' import { cn } from '@/utils/classnames' -import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' -import { - RiCloseLine, - RiPlayLargeLine, -} from '@remixicon/react' -import { debounce } from 'lodash-es' -import type { FC, ReactNode } from 'react' -import React, { - cloneElement, - memo, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react' -import { useTranslation } from 'react-i18next' -import { useShallow } from 'zustand/react/shallow' import { useResizePanel } from '../../hooks/use-resize-panel' import BeforeRunForm from '../before-run-form' import PanelWrap from '../before-run-form/panel-wrap' @@ -83,7 +84,14 @@ const getCustomRunForm = (params: CustomRunFormProps): React.JSX.Element => { case BlockEnum.DataSource: return <DataSourceBeforeRunForm {...params} /> default: - return <div>Custom Run Form: {nodeType} not found</div> + return ( + <div> + Custom Run Form: + {nodeType} + {' '} + not found + </div> + ) } } @@ -362,7 +370,7 @@ const BasePanel: FC<BasePanelProps> = ({ default: break } - return !pluginDetail ? null : <ReadmeEntrance pluginDetail={pluginDetail as any} className='mt-auto' /> + return !pluginDetail ? null : <ReadmeEntrance pluginDetail={pluginDetail as any} className="mt-auto" /> }, [data.type, currToolCollection, currentDataSource, currentTriggerPlugin]) const selectedNode = useMemo(() => ({ @@ -373,7 +381,8 @@ const BasePanel: FC<BasePanelProps> = ({ return ( <div className={cn( 'relative mr-1 h-full', - )}> + )} + > <div ref={containerRef} className={cn('flex h-full flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg', showSingleRunPanel ? 'overflow-hidden' : 'overflow-y-auto')} @@ -385,7 +394,7 @@ const BasePanel: FC<BasePanelProps> = ({ nodeName={data.title} onHide={hideSingleRun} > - <div className='h-0 grow overflow-y-auto pb-4'> + <div className="h-0 grow overflow-y-auto pb-4"> <SpecialResultPanel {...passedLogParams} /> </div> </PanelWrap> @@ -412,7 +421,8 @@ const BasePanel: FC<BasePanelProps> = ({ return ( <div className={cn( 'relative mr-1 h-full', - )}> + )} + > <div ref={containerRef} className={cn('flex h-full flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-lg', showSingleRunPanel ? 'overflow-hidden' : 'overflow-y-auto')} @@ -420,20 +430,22 @@ const BasePanel: FC<BasePanelProps> = ({ width: `${nodePanelWidth}px`, }} > - {isSupportCustomRunForm(data.type) ? ( - form - ) : ( - <BeforeRunForm - nodeName={data.title} - nodeType={data.type} - onHide={hideSingleRun} - onRun={handleRunWithParams} - {...singleRunParams!} - {...passedLogParams} - existVarValuesInForms={getExistVarValuesInForms(singleRunParams?.forms as any)} - filteredExistVarForms={getFilteredExistVarForms(singleRunParams?.forms as any)} - /> - )} + {isSupportCustomRunForm(data.type) + ? ( + form + ) + : ( + <BeforeRunForm + nodeName={data.title} + nodeType={data.type} + onHide={hideSingleRun} + onRun={handleRunWithParams} + {...singleRunParams!} + {...passedLogParams} + existVarValuesInForms={getExistVarValuesInForms(singleRunParams?.forms as any)} + filteredExistVarForms={getFilteredExistVarForms(singleRunParams?.forms as any)} + /> + )} </div> </div> @@ -452,8 +464,9 @@ const BasePanel: FC<BasePanelProps> = ({ > <div ref={triggerRef} - className='absolute -left-1 top-0 flex h-full w-1 cursor-col-resize resize-x items-center justify-center'> - <div className='h-10 w-0.5 rounded-sm bg-state-base-handle hover:h-full hover:bg-state-accent-solid active:h-full active:bg-state-accent-solid'></div> + className="absolute -left-1 top-0 flex h-full w-1 cursor-col-resize resize-x items-center justify-center" + > + <div className="h-10 w-0.5 rounded-sm bg-state-base-handle hover:h-full hover:bg-state-accent-solid active:h-full active:bg-state-accent-solid"></div> </div> <div ref={containerRef} @@ -462,28 +475,28 @@ const BasePanel: FC<BasePanelProps> = ({ width: `${nodePanelWidth}px`, }} > - <div className='sticky top-0 z-10 shrink-0 border-b-[0.5px] border-divider-regular bg-components-panel-bg'> - <div className='flex items-center px-4 pb-1 pt-4'> + <div className="sticky top-0 z-10 shrink-0 border-b-[0.5px] border-divider-regular bg-components-panel-bg"> + <div className="flex items-center px-4 pb-1 pt-4"> <BlockIcon - className='mr-1 shrink-0' + className="mr-1 shrink-0" type={data.type} toolIcon={toolIcon} - size='md' + size="md" /> <TitleInput value={data.title || ''} onBlur={handleTitleBlur} /> - <div className='flex shrink-0 items-center text-text-tertiary'> + <div className="flex shrink-0 items-center text-text-tertiary"> { isSupportSingleRun && !nodesReadOnly && ( <Tooltip popupContent={t('workflow.panel.runThisStep')} - popupClassName='mr-1' + popupClassName="mr-1" disabled={isSingleRunning} > <div - className='mr-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded-md hover:bg-state-base-hover' + className="mr-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded-md hover:bg-state-base-hover" onClick={() => { if (isSingleRunning) handleStop() @@ -492,8 +505,9 @@ const BasePanel: FC<BasePanelProps> = ({ }} > { - isSingleRunning ? <Stop className='h-4 w-4 text-text-tertiary' /> - : <RiPlayLargeLine className='h-4 w-4 text-text-tertiary' /> + isSingleRunning + ? <Stop className="h-4 w-4 text-text-tertiary" /> + : <RiPlayLargeLine className="h-4 w-4 text-text-tertiary" /> } </div> </Tooltip> @@ -501,16 +515,16 @@ const BasePanel: FC<BasePanelProps> = ({ } <HelpLink nodeType={data.type} /> <PanelOperator id={id} data={data} showHelpLink={false} /> - <div className='mx-3 h-3.5 w-[1px] bg-divider-regular' /> + <div className="mx-3 h-3.5 w-[1px] bg-divider-regular" /> <div - className='flex h-6 w-6 cursor-pointer items-center justify-center' + className="flex h-6 w-6 cursor-pointer items-center justify-center" onClick={() => handleNodeSelect(id, true)} > - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> </div> - <div className='p-2'> + <div className="p-2"> <DescriptionInput value={data.desc || ''} onChange={handleDescriptionChange} @@ -519,7 +533,7 @@ const BasePanel: FC<BasePanelProps> = ({ { needsToolAuth && ( <PluginAuth - className='px-4 pb-2' + className="px-4 pb-2" pluginPayload={{ provider: currToolCollection?.name || '', providerType: currToolCollection?.type || '', @@ -527,7 +541,7 @@ const BasePanel: FC<BasePanelProps> = ({ detail: currToolCollection as any, }} > - <div className='flex items-center justify-between pl-4 pr-3'> + <div className="flex items-center justify-between pl-4 pr-3"> <Tab value={tabType} onChange={setTabType} @@ -552,7 +566,7 @@ const BasePanel: FC<BasePanelProps> = ({ onJumpToDataSourcePage={handleJumpToDataSourcePage} isAuthorized={currentDataSource.is_authorized} > - <div className='flex items-center justify-between pl-4 pr-3'> + <div className="flex items-center justify-between pl-4 pr-3"> <Tab value={tabType} onChange={setTabType} @@ -580,7 +594,7 @@ const BasePanel: FC<BasePanelProps> = ({ } { !needsToolAuth && !currentDataSource && !currentTriggerPlugin && ( - <div className='flex items-center justify-between pl-4 pr-3'> + <div className="flex items-center justify-between pl-4 pr-3"> <Tab value={tabType} onChange={setTabType} @@ -591,7 +605,7 @@ const BasePanel: FC<BasePanelProps> = ({ <Split /> </div> {tabType === TabType.settings && ( - <div className='flex flex-1 flex-col overflow-y-auto'> + <div className="flex flex-1 flex-col overflow-y-auto"> <div> {cloneElement(children as any, { id, @@ -625,11 +639,11 @@ const BasePanel: FC<BasePanelProps> = ({ } { !!availableNextBlocks.length && ( - <div className='border-t-[0.5px] border-divider-regular p-4'> - <div className='system-sm-semibold-uppercase mb-1 flex items-center text-text-secondary'> + <div className="border-t-[0.5px] border-divider-regular p-4"> + <div className="system-sm-semibold-uppercase mb-1 flex items-center text-text-secondary"> {t('workflow.panel.nextStep').toLocaleUpperCase()} </div> - <div className='system-xs-regular mb-2 text-text-tertiary'> + <div className="system-xs-regular mb-2 text-text-tertiary"> {t('workflow.panel.addNextStep')} </div> <NextStep selectedNode={selectedNode} /> diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx index 43dab49ed8..93d5debb51 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx @@ -1,15 +1,15 @@ 'use client' +import type { FC } from 'react' import type { ResultPanelProps } from '@/app/components/workflow/run/result-panel' +import type { NodeTracing } from '@/types/workflow' +import { RiLoader2Line } from '@remixicon/react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' +import { useHooksStore } from '@/app/components/workflow/hooks-store' import ResultPanel from '@/app/components/workflow/run/result-panel' import { NodeRunningStatus } from '@/app/components/workflow/types' -import type { FC } from 'react' -import React, { useCallback, useEffect, useMemo, useState } from 'react' -import NoData from './no-data' import { useLastRun } from '@/service/use-workflow' -import { RiLoader2Line } from '@remixicon/react' -import type { NodeTracing } from '@/types/workflow' -import { useHooksStore } from '@/app/components/workflow/hooks-store' import { FlowType } from '@/types/common' +import NoData from './no-data' type Props = { appId: string @@ -49,10 +49,10 @@ const LastRun: FC<Props> = ({ const canRunLastRun = !isRunAfterSingleRun || isOneStepRunSucceed || isOneStepRunFailed || (pageHasHide && hidePageOneStepRunFinished) const { data: lastRunResult, isFetching, error } = useLastRun(configsMap?.flowType || FlowType.appFlow, configsMap?.flowId || '', nodeId, canRunLastRun) const isRunning = useMemo(() => { - if(isPaused) + if (isPaused) return false - if(!isRunAfterSingleRun) + if (!isRunAfterSingleRun) return isFetching return [NodeRunningStatus.Running, NodeRunningStatus.NotStart].includes(oneStepRunRunningStatus!) }, [isFetching, isPaused, isRunAfterSingleRun, oneStepRunRunningStatus]) @@ -86,7 +86,7 @@ const LastRun: FC<Props> = ({ }, [isOneStepRunSucceed, isOneStepRunFailed, oneStepRunRunningStatus]) useEffect(() => { - if([NodeRunningStatus.Succeeded, NodeRunningStatus.Failed].includes(oneStepRunRunningStatus!)) + if ([NodeRunningStatus.Succeeded, NodeRunningStatus.Failed].includes(oneStepRunRunningStatus!)) setHidePageOneStepFinishedStatus(oneStepRunRunningStatus!) }, [oneStepRunRunningStatus]) @@ -110,13 +110,14 @@ const LastRun: FC<Props> = ({ if (isFetching && !isRunAfterSingleRun) { return ( - <div className='flex h-0 grow flex-col items-center justify-center'> - <RiLoader2Line className='size-4 animate-spin text-text-tertiary' /> - </div>) + <div className="flex h-0 grow flex-col items-center justify-center"> + <RiLoader2Line className="size-4 animate-spin text-text-tertiary" /> + </div> + ) } if (isRunning) - return <ResultPanel status='running' showSteps={false} /> + return <ResultPanel status="running" showSteps={false} /> if (!isPaused && (noLastRun || !runResult)) { return ( <NoData canSingleRun={canSingleRun} onSingleRun={onSingleRunClicked} /> diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/no-data.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/no-data.tsx index ad0058efae..4ae6ccd31f 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/no-data.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/no-data.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' -import React from 'react' -import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time' -import Button from '@/app/components/base/button' import { RiPlayLine } from '@remixicon/react' +import React from 'react' import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' +import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time' type Props = { canSingleRun: boolean @@ -17,16 +17,16 @@ const NoData: FC<Props> = ({ }) => { const { t } = useTranslation() return ( - <div className='flex h-0 grow flex-col items-center justify-center'> - <ClockPlay className='h-8 w-8 text-text-quaternary' /> - <div className='system-xs-regular my-2 text-text-tertiary'>{t('workflow.debug.noData.description')}</div> + <div className="flex h-0 grow flex-col items-center justify-center"> + <ClockPlay className="h-8 w-8 text-text-quaternary" /> + <div className="system-xs-regular my-2 text-text-tertiary">{t('workflow.debug.noData.description')}</div> {canSingleRun && ( <Button - className='flex' - size='small' + className="flex" + size="small" onClick={onSingleRun} > - <RiPlayLine className='mr-1 h-3.5 w-3.5' /> + <RiPlayLine className="mr-1 h-3.5 w-3.5" /> <div>{t('workflow.debug.noData.runThisNode')}</div> </Button> )} diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts index ac9f2051c3..0de98db032 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts @@ -1,42 +1,42 @@ -import useOneStepRun from '@/app/components/workflow/nodes/_base/hooks/use-one-step-run' -import type { Params as OneStepRunParams } from '@/app/components/workflow/nodes/_base/hooks/use-one-step-run' -import { useCallback, useEffect, useState } from 'react' -import { TabType } from '../tab' import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form' -import useStartSingleRunFormParams from '@/app/components/workflow/nodes/start/use-single-run-form-params' -import useLLMSingleRunFormParams from '@/app/components/workflow/nodes/llm/use-single-run-form-params' -import useKnowledgeRetrievalSingleRunFormParams from '@/app/components/workflow/nodes/knowledge-retrieval/use-single-run-form-params' -import useCodeSingleRunFormParams from '@/app/components/workflow/nodes/code/use-single-run-form-params' -import useTemplateTransformSingleRunFormParams from '@/app/components/workflow/nodes/template-transform/use-single-run-form-params' -import useQuestionClassifierSingleRunFormParams from '@/app/components/workflow/nodes/question-classifier/use-single-run-form-params' -import useParameterExtractorSingleRunFormParams from '@/app/components/workflow/nodes/parameter-extractor/use-single-run-form-params' -import useHttpRequestSingleRunFormParams from '@/app/components/workflow/nodes/http/use-single-run-form-params' -import useToolSingleRunFormParams from '@/app/components/workflow/nodes/tool/use-single-run-form-params' -import useIterationSingleRunFormParams from '@/app/components/workflow/nodes/iteration/use-single-run-form-params' -import useAgentSingleRunFormParams from '@/app/components/workflow/nodes/agent/use-single-run-form-params' -import useDocExtractorSingleRunFormParams from '@/app/components/workflow/nodes/document-extractor/use-single-run-form-params' -import useLoopSingleRunFormParams from '@/app/components/workflow/nodes/loop/use-single-run-form-params' -import useIfElseSingleRunFormParams from '@/app/components/workflow/nodes/if-else/use-single-run-form-params' -import useVariableAggregatorSingleRunFormParams from '@/app/components/workflow/nodes/variable-assigner/use-single-run-form-params' -import useVariableAssignerSingleRunFormParams from '@/app/components/workflow/nodes/assigner/use-single-run-form-params' -import useKnowledgeBaseSingleRunFormParams from '@/app/components/workflow/nodes/knowledge-base/use-single-run-form-params' - -import useToolGetDataForCheckMore from '@/app/components/workflow/nodes/tool/use-get-data-for-check-more' -import useTriggerPluginGetDataForCheckMore from '@/app/components/workflow/nodes/trigger-plugin/use-check-params' -import { VALUE_SELECTOR_DELIMITER as DELIMITER } from '@/config' - +import type { Params as OneStepRunParams } from '@/app/components/workflow/nodes/_base/hooks/use-one-step-run' // import import type { CommonNodeType, ValueSelector } from '@/app/components/workflow/types' -import { BlockEnum } from '@/app/components/workflow/types' +import { useCallback, useEffect, useState } from 'react' +import Toast from '@/app/components/base/toast' import { useNodesSyncDraft, } from '@/app/components/workflow/hooks' import { useWorkflowRunValidation } from '@/app/components/workflow/hooks/use-checklist' import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud' -import { useInvalidLastRun } from '@/service/use-workflow' +import useOneStepRun from '@/app/components/workflow/nodes/_base/hooks/use-one-step-run' +import useAgentSingleRunFormParams from '@/app/components/workflow/nodes/agent/use-single-run-form-params' +import useVariableAssignerSingleRunFormParams from '@/app/components/workflow/nodes/assigner/use-single-run-form-params' +import useCodeSingleRunFormParams from '@/app/components/workflow/nodes/code/use-single-run-form-params' +import useDocExtractorSingleRunFormParams from '@/app/components/workflow/nodes/document-extractor/use-single-run-form-params' +import useHttpRequestSingleRunFormParams from '@/app/components/workflow/nodes/http/use-single-run-form-params' +import useIfElseSingleRunFormParams from '@/app/components/workflow/nodes/if-else/use-single-run-form-params' +import useIterationSingleRunFormParams from '@/app/components/workflow/nodes/iteration/use-single-run-form-params' +import useKnowledgeBaseSingleRunFormParams from '@/app/components/workflow/nodes/knowledge-base/use-single-run-form-params' +import useKnowledgeRetrievalSingleRunFormParams from '@/app/components/workflow/nodes/knowledge-retrieval/use-single-run-form-params' +import useLLMSingleRunFormParams from '@/app/components/workflow/nodes/llm/use-single-run-form-params' +import useLoopSingleRunFormParams from '@/app/components/workflow/nodes/loop/use-single-run-form-params' +import useParameterExtractorSingleRunFormParams from '@/app/components/workflow/nodes/parameter-extractor/use-single-run-form-params' +import useQuestionClassifierSingleRunFormParams from '@/app/components/workflow/nodes/question-classifier/use-single-run-form-params' + +import useStartSingleRunFormParams from '@/app/components/workflow/nodes/start/use-single-run-form-params' +import useTemplateTransformSingleRunFormParams from '@/app/components/workflow/nodes/template-transform/use-single-run-form-params' +import useToolGetDataForCheckMore from '@/app/components/workflow/nodes/tool/use-get-data-for-check-more' + +import useToolSingleRunFormParams from '@/app/components/workflow/nodes/tool/use-single-run-form-params' +import useTriggerPluginGetDataForCheckMore from '@/app/components/workflow/nodes/trigger-plugin/use-check-params' +import useVariableAggregatorSingleRunFormParams from '@/app/components/workflow/nodes/variable-assigner/use-single-run-form-params' import { useStore, useWorkflowStore } from '@/app/components/workflow/store' +import { BlockEnum } from '@/app/components/workflow/types' import { isSupportCustomRunForm } from '@/app/components/workflow/utils' -import Toast from '@/app/components/base/toast' +import { VALUE_SELECTOR_DELIMITER as DELIMITER } from '@/config' +import { useInvalidLastRun } from '@/service/use-workflow' +import { TabType } from '../tab' const singleRunFormParamsHooks: Record<BlockEnum, any> = { [BlockEnum.LLM]: useLLMSingleRunFormParams, diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/tab.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/tab.tsx index 08bbdf4068..53ae913a8e 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/tab.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/tab.tsx @@ -1,8 +1,8 @@ 'use client' -import TabHeader from '@/app/components/base/tab-header' import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' +import TabHeader from '@/app/components/base/tab-header' export enum TabType { settings = 'settings', @@ -11,7 +11,7 @@ export enum TabType { } type Props = { - value: TabType, + value: TabType onChange: (value: TabType) => void } @@ -26,7 +26,7 @@ const Tab: FC<Props> = ({ { id: TabType.settings, name: t('workflow.debug.settingsTab').toLocaleUpperCase() }, { id: TabType.lastRun, name: t('workflow.debug.lastRunTab').toLocaleUpperCase() }, ]} - itemClassName='ml-0' + itemClassName="ml-0" value={value} onChange={onChange as any} /> diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/trigger-subscription.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/trigger-subscription.tsx index 52c6d4fe18..a6dd65ea81 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/trigger-subscription.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/trigger-subscription.tsx @@ -1,9 +1,9 @@ +import type { FC } from 'react' import type { SimpleSubscription } from '@/app/components/plugins/plugin-detail-panel/subscription-list' import { CreateButtonType, CreateSubscriptionButton } from '@/app/components/plugins/plugin-detail-panel/subscription-list/create' import { SubscriptionSelectorEntry } from '@/app/components/plugins/plugin-detail-panel/subscription-list/selector-entry' import { useSubscriptionList } from '@/app/components/plugins/plugin-detail-panel/subscription-list/use-subscription-list' import { cn } from '@/utils/classnames' -import type { FC } from 'react' type TriggerSubscriptionProps = { subscriptionIdSelected?: string @@ -15,12 +15,16 @@ export const TriggerSubscription: FC<TriggerSubscriptionProps> = ({ subscription const { subscriptions } = useSubscriptionList() const subscriptionCount = subscriptions?.length || 0 - return <div className={cn('px-4', subscriptionCount > 0 && 'flex items-center justify-between pr-3')}> - {!subscriptionCount && <CreateSubscriptionButton buttonType={CreateButtonType.FULL_BUTTON} />} - {children} - {subscriptionCount > 0 && <SubscriptionSelectorEntry - selectedId={subscriptionIdSelected} - onSelect={onSubscriptionChange} - />} - </div> + return ( + <div className={cn('px-4', subscriptionCount > 0 && 'flex items-center justify-between pr-3')}> + {!subscriptionCount && <CreateSubscriptionButton buttonType={CreateButtonType.FULL_BUTTON} />} + {children} + {subscriptionCount > 0 && ( + <SubscriptionSelectorEntry + selectedId={subscriptionIdSelected} + onSelect={onSubscriptionChange} + /> + )} + </div> + ) } diff --git a/web/app/components/workflow/nodes/_base/hooks/use-available-var-list.ts b/web/app/components/workflow/nodes/_base/hooks/use-available-var-list.ts index 2ce9dfe809..f226900899 100644 --- a/web/app/components/workflow/nodes/_base/hooks/use-available-var-list.ts +++ b/web/app/components/workflow/nodes/_base/hooks/use-available-var-list.ts @@ -1,13 +1,13 @@ -import useNodeInfo from './use-node-info' +import type { Node, NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' import { useIsChatMode, useWorkflow, useWorkflowVariables, } from '@/app/components/workflow/hooks' -import type { NodeOutPutVar } from '@/app/components/workflow/types' -import { BlockEnum, type Node, type ValueSelector, type Var } from '@/app/components/workflow/types' import { useStore as useWorkflowStore } from '@/app/components/workflow/store' +import { BlockEnum } from '@/app/components/workflow/types' import { inputVarTypeToVarType } from '../../data-source/utils' +import useNodeInfo from './use-node-info' type Params = { onlyLeafNodeVar?: boolean diff --git a/web/app/components/workflow/nodes/_base/hooks/use-node-crud.ts b/web/app/components/workflow/nodes/_base/hooks/use-node-crud.ts index 51d2fdb80c..d1741f0bbb 100644 --- a/web/app/components/workflow/nodes/_base/hooks/use-node-crud.ts +++ b/web/app/components/workflow/nodes/_base/hooks/use-node-crud.ts @@ -1,5 +1,6 @@ -import { useNodeDataUpdate } from '@/app/components/workflow/hooks' import type { CommonNodeType } from '@/app/components/workflow/types' +import { useNodeDataUpdate } from '@/app/components/workflow/hooks' + const useNodeCrud = <T>(id: string, data: CommonNodeType<T>) => { const { handleNodeDataUpdateWithSyncDraft } = useNodeDataUpdate() diff --git a/web/app/components/workflow/nodes/_base/hooks/use-node-help-link.ts b/web/app/components/workflow/nodes/_base/hooks/use-node-help-link.ts index e40f6271ef..d97a87bfba 100644 --- a/web/app/components/workflow/nodes/_base/hooks/use-node-help-link.ts +++ b/web/app/components/workflow/nodes/_base/hooks/use-node-help-link.ts @@ -1,5 +1,5 @@ -import { useMemo } from 'react' import type { BlockEnum } from '@/app/components/workflow/types' +import { useMemo } from 'react' import { useNodesMetaData } from '@/app/components/workflow/hooks' export const useNodeHelpLink = (nodeType: BlockEnum) => { diff --git a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts index dad62ae2a4..59774ab96a 100644 --- a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts +++ b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts @@ -1,15 +1,39 @@ +import type { CommonNodeType, InputVar, TriggerNodeType, ValueSelector, Var, Variable } from '@/app/components/workflow/types' +import type { FlowType } from '@/types/common' +import type { NodeRunResult, NodeTracing } from '@/types/workflow' +import { produce } from 'immer' + +import { noop, unionBy } from 'lodash-es' import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import { unionBy } from 'lodash-es' -import { produce } from 'immer' +import { + useStoreApi, +} from 'reactflow' +import { trackEvent } from '@/app/components/base/amplitude' +import { getInputVars as doGetInputVars } from '@/app/components/base/prompt-editor/constants' +import Toast from '@/app/components/base/toast' import { useIsChatMode, useNodeDataUpdate, useWorkflow, } from '@/app/components/workflow/hooks' +import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud' import { getNodeInfoById, isConversationVar, isENV, isSystemVar, toNodeOutputVars } from '@/app/components/workflow/nodes/_base/components/variable/utils' - -import type { CommonNodeType, InputVar, ValueSelector, Var, Variable } from '@/app/components/workflow/types' +import Assigner from '@/app/components/workflow/nodes/assigner/default' +import CodeDefault from '@/app/components/workflow/nodes/code/default' +import DocumentExtractorDefault from '@/app/components/workflow/nodes/document-extractor/default' +import HTTPDefault from '@/app/components/workflow/nodes/http/default' +import IfElseDefault from '@/app/components/workflow/nodes/if-else/default' +import IterationDefault from '@/app/components/workflow/nodes/iteration/default' +import KnowledgeRetrievalDefault from '@/app/components/workflow/nodes/knowledge-retrieval/default' +import LLMDefault from '@/app/components/workflow/nodes/llm/default' +import LoopDefault from '@/app/components/workflow/nodes/loop/default' +import ParameterExtractorDefault from '@/app/components/workflow/nodes/parameter-extractor/default' +import QuestionClassifyDefault from '@/app/components/workflow/nodes/question-classifier/default' +import TemplateTransformDefault from '@/app/components/workflow/nodes/template-transform/default' +import ToolDefault from '@/app/components/workflow/nodes/tool/default' +import VariableAssigner from '@/app/components/workflow/nodes/variable-assigner/default' +import { useStore, useWorkflowStore } from '@/app/components/workflow/store' import { BlockEnum, InputVarType, @@ -17,29 +41,19 @@ import { VarType, WorkflowRunningStatus, } from '@/app/components/workflow/types' -import type { TriggerNodeType } from '@/app/components/workflow/types' import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types' -import { useStore, useWorkflowStore } from '@/app/components/workflow/store' -import { fetchNodeInspectVars, getIterationSingleNodeRunUrl, getLoopSingleNodeRunUrl, singleNodeRun } from '@/service/workflow' -import Toast from '@/app/components/base/toast' -import LLMDefault from '@/app/components/workflow/nodes/llm/default' -import KnowledgeRetrievalDefault from '@/app/components/workflow/nodes/knowledge-retrieval/default' -import IfElseDefault from '@/app/components/workflow/nodes/if-else/default' -import CodeDefault from '@/app/components/workflow/nodes/code/default' -import TemplateTransformDefault from '@/app/components/workflow/nodes/template-transform/default' -import QuestionClassifyDefault from '@/app/components/workflow/nodes/question-classifier/default' -import HTTPDefault from '@/app/components/workflow/nodes/http/default' -import ToolDefault from '@/app/components/workflow/nodes/tool/default' -import VariableAssigner from '@/app/components/workflow/nodes/variable-assigner/default' -import Assigner from '@/app/components/workflow/nodes/assigner/default' -import ParameterExtractorDefault from '@/app/components/workflow/nodes/parameter-extractor/default' -import IterationDefault from '@/app/components/workflow/nodes/iteration/default' -import DocumentExtractorDefault from '@/app/components/workflow/nodes/document-extractor/default' -import LoopDefault from '@/app/components/workflow/nodes/loop/default' +import { useEventEmitterContextContext } from '@/context/event-emitter' import { post, ssePost } from '@/service/base' -import { noop } from 'lodash-es' -import { getInputVars as doGetInputVars } from '@/app/components/base/prompt-editor/constants' -import type { NodeRunResult, NodeTracing } from '@/types/workflow' +import { + useAllBuiltInTools, + useAllCustomTools, + useAllMCPTools, + useAllWorkflowTools, +} from '@/service/use-tools' +import { useInvalidLastRun } from '@/service/use-workflow' +import { fetchNodeInspectVars, getIterationSingleNodeRunUrl, getLoopSingleNodeRunUrl, singleNodeRun } from '@/service/workflow' +import useMatchSchemaType from '../components/variable/use-match-schema-type' + const { checkValid: checkLLMValid } = LLMDefault const { checkValid: checkKnowledgeRetrievalValid } = KnowledgeRetrievalDefault const { checkValid: checkIfElseValid } = IfElseDefault @@ -54,21 +68,6 @@ const { checkValid: checkParameterExtractorValid } = ParameterExtractorDefault const { checkValid: checkIterationValid } = IterationDefault const { checkValid: checkDocumentExtractorValid } = DocumentExtractorDefault const { checkValid: checkLoopValid } = LoopDefault -import { - useStoreApi, -} from 'reactflow' -import { useInvalidLastRun } from '@/service/use-workflow' -import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud' -import type { FlowType } from '@/types/common' -import useMatchSchemaType from '../components/variable/use-match-schema-type' -import { useEventEmitterContextContext } from '@/context/event-emitter' -import { - useAllBuiltInTools, - useAllCustomTools, - useAllMCPTools, - useAllWorkflowTools, -} from '@/service/use-tools' -import { trackEvent } from '@/app/components/base/amplitude' // eslint-disable-next-line ts/no-unsafe-function-type const checkValidFns: Partial<Record<BlockEnum, Function>> = { diff --git a/web/app/components/workflow/nodes/_base/hooks/use-output-var-list.ts b/web/app/components/workflow/nodes/_base/hooks/use-output-var-list.ts index 87a5f69c95..09b8fde0b5 100644 --- a/web/app/components/workflow/nodes/_base/hooks/use-output-var-list.ts +++ b/web/app/components/workflow/nodes/_base/hooks/use-output-var-list.ts @@ -1,6 +1,3 @@ -import { useCallback, useRef, useState } from 'react' -import { produce } from 'immer' -import { useBoolean, useDebounceFn } from 'ahooks' import type { CodeNodeType, OutputVar, @@ -8,15 +5,18 @@ import type { import type { ValueSelector, } from '@/app/components/workflow/types' -import { - BlockEnum, - VarType, -} from '@/app/components/workflow/types' +import { useBoolean, useDebounceFn } from 'ahooks' +import { produce } from 'immer' +import { useCallback, useRef, useState } from 'react' import { useWorkflow, } from '@/app/components/workflow/hooks' import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' import { getDefaultValue } from '@/app/components/workflow/nodes/_base/components/error-handle/utils' +import { + BlockEnum, + VarType, +} from '@/app/components/workflow/types' import useInspectVarsCrud from '../../../hooks/use-inspect-vars-crud' type Params<T> = { @@ -74,7 +74,7 @@ function useOutputVarList<T>({ if (newKey) { handleOutVarRenameChange(id, [id, outputKeyOrders[changedIndex!]], [id, newKey]) - if(!(id in oldNameRecord.current)) + if (!(id in oldNameRecord.current)) oldNameRecord.current[id] = outputKeyOrders[changedIndex!] renameInspectNameWithDebounce(id, newKey) } @@ -82,7 +82,7 @@ function useOutputVarList<T>({ const varId = nodesWithInspectVars.find(node => node.nodeId === id)?.vars.find((varItem) => { return varItem.name === Object.keys(newVars)[0] })?.id - if(varId) + if (varId) deleteInspectVar(id, varId) } }, [inputs, setInputs, varKey, outputKeyOrders, onOutputKeyOrdersChange, handleOutVarRenameChange, id, renameInspectNameWithDebounce, nodesWithInspectVars, deleteInspectVar]) @@ -120,7 +120,7 @@ function useOutputVarList<T>({ const varId = nodesWithInspectVars.find(node => node.nodeId === id)?.vars.find((varItem) => { return varItem.name === removedVar[1] })?.id - if(varId) + if (varId) deleteInspectVar(id, varId) removeUsedVarInNodes(removedVar) hideRemoveVarConfirm() @@ -145,7 +145,7 @@ function useOutputVarList<T>({ const varId = nodesWithInspectVars.find(node => node.nodeId === id)?.vars.find((varItem) => { return varItem.name === key })?.id - if(varId) + if (varId) deleteInspectVar(id, varId) }, [outputKeyOrders, isVarUsedInNodes, id, inputs, setInputs, onOutputKeyOrdersChange, nodesWithInspectVars, deleteInspectVar, showRemoveVarConfirm, varKey]) diff --git a/web/app/components/workflow/nodes/_base/hooks/use-toggle-expend.ts b/web/app/components/workflow/nodes/_base/hooks/use-toggle-expend.ts index 48c1e95eb2..c123c00e2d 100644 --- a/web/app/components/workflow/nodes/_base/hooks/use-toggle-expend.ts +++ b/web/app/components/workflow/nodes/_base/hooks/use-toggle-expend.ts @@ -11,7 +11,8 @@ const useToggleExpend = ({ ref, hasFooter = true, isInNode }: Params) => { const [wrapHeight, setWrapHeight] = useState(ref?.current?.clientHeight) const editorExpandHeight = isExpand ? wrapHeight! - (hasFooter ? 56 : 29) : 0 useEffect(() => { - if (!ref?.current) return + if (!ref?.current) + return setWrapHeight(ref.current?.clientHeight) }, [isExpand]) @@ -26,8 +27,8 @@ const useToggleExpend = ({ ref, hasFooter = true, isInNode }: Params) => { })() const wrapStyle = isExpand ? { - boxShadow: '0px 0px 12px -4px rgba(16, 24, 40, 0.05), 0px -3px 6px -2px rgba(16, 24, 40, 0.03)', - } + boxShadow: '0px 0px 12px -4px rgba(16, 24, 40, 0.05), 0px -3px 6px -2px rgba(16, 24, 40, 0.03)', + } : {} return { wrapClassName, diff --git a/web/app/components/workflow/nodes/_base/hooks/use-var-list.ts b/web/app/components/workflow/nodes/_base/hooks/use-var-list.ts index ffacf9a2df..9b3bbbb9aa 100644 --- a/web/app/components/workflow/nodes/_base/hooks/use-var-list.ts +++ b/web/app/components/workflow/nodes/_base/hooks/use-var-list.ts @@ -1,6 +1,6 @@ -import { useCallback } from 'react' -import { produce } from 'immer' import type { Variable } from '@/app/components/workflow/types' +import { produce } from 'immer' +import { useCallback } from 'react' type Params<T> = { inputs: T diff --git a/web/app/components/workflow/nodes/_base/node.tsx b/web/app/components/workflow/nodes/_base/node.tsx index 263732cd70..c2d0816449 100644 --- a/web/app/components/workflow/nodes/_base/node.tsx +++ b/web/app/components/workflow/nodes/_base/node.tsx @@ -2,6 +2,14 @@ import type { FC, ReactElement, } from 'react' +import type { IterationNodeType } from '@/app/components/workflow/nodes/iteration/types' +import type { NodeProps } from '@/app/components/workflow/types' +import { + RiAlertFill, + RiCheckboxCircleFill, + RiErrorWarningFill, + RiLoader2Line, +} from '@remixicon/react' import { cloneElement, memo, @@ -9,40 +17,32 @@ import { useMemo, useRef, } from 'react' -import { - RiAlertFill, - RiCheckboxCircleFill, - RiErrorWarningFill, - RiLoader2Line, -} from '@remixicon/react' import { useTranslation } from 'react-i18next' -import type { NodeProps } from '@/app/components/workflow/types' -import { - BlockEnum, - NodeRunningStatus, - isTriggerNode, -} from '@/app/components/workflow/types' +import Tooltip from '@/app/components/base/tooltip' +import BlockIcon from '@/app/components/workflow/block-icon' +import { ToolTypeEnum } from '@/app/components/workflow/block-selector/types' import { useNodesReadOnly, useToolIcon } from '@/app/components/workflow/hooks' -import { hasErrorHandleNode, hasRetryNode } from '@/app/components/workflow/utils' +import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud' import { useNodeIterationInteractions } from '@/app/components/workflow/nodes/iteration/use-interactions' import { useNodeLoopInteractions } from '@/app/components/workflow/nodes/loop/use-interactions' -import type { IterationNodeType } from '@/app/components/workflow/nodes/iteration/types' import CopyID from '@/app/components/workflow/nodes/tool/components/copy-id' +import { + BlockEnum, + isTriggerNode, + NodeRunningStatus, +} from '@/app/components/workflow/types' +import { hasErrorHandleNode, hasRetryNode } from '@/app/components/workflow/utils' +import { cn } from '@/utils/classnames' +import AddVariablePopupWithPosition from './components/add-variable-popup-with-position' +import EntryNodeContainer, { StartNodeTypeEnum } from './components/entry-node-container' +import ErrorHandleOnNode from './components/error-handle/error-handle-on-node' +import NodeControl from './components/node-control' import { NodeSourceHandle, NodeTargetHandle, } from './components/node-handle' import NodeResizer from './components/node-resizer' -import NodeControl from './components/node-control' -import ErrorHandleOnNode from './components/error-handle/error-handle-on-node' import RetryOnNode from './components/retry/retry-on-node' -import AddVariablePopupWithPosition from './components/add-variable-popup-with-position' -import EntryNodeContainer, { StartNodeTypeEnum } from './components/entry-node-container' -import { cn } from '@/utils/classnames' -import BlockIcon from '@/app/components/workflow/block-icon' -import Tooltip from '@/app/components/base/tooltip' -import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud' -import { ToolTypeEnum } from '@/app/components/workflow/block-selector/types' type NodeChildProps = { id: string @@ -160,13 +160,13 @@ const BaseNode: FC<BaseNodeProps> = ({ ? 'pointer-events-auto z-30 bg-workflow-block-parma-bg opacity-80 backdrop-blur-[2px]' : 'pointer-events-none z-20 bg-workflow-block-parma-bg opacity-50', )} - data-testid='workflow-node-install-overlay' + data-testid="workflow-node-install-overlay" /> )} { data.type === BlockEnum.DataSource && ( - <div className='absolute inset-[-2px] top-[-22px] z-[-1] rounded-[18px] bg-node-data-source-bg p-0.5 backdrop-blur-[6px]'> - <div className='system-2xs-semibold-uppercase flex h-5 items-center px-2.5 text-text-tertiary'> + <div className="absolute inset-[-2px] top-[-22px] z-[-1] rounded-[18px] bg-node-data-source-bg p-0.5 backdrop-blur-[6px]"> + <div className="system-2xs-semibold-uppercase flex h-5 items-center px-2.5 text-text-tertiary"> {t('workflow.blocks.datasource')} </div> </div> @@ -215,8 +215,8 @@ const BaseNode: FC<BaseNodeProps> = ({ <NodeTargetHandle id={id} data={data} - handleClassName='!top-4 !-left-[9px] !translate-y-0' - handleId='target' + handleClassName="!top-4 !-left-[9px] !translate-y-0" + handleId="target" /> ) } @@ -225,8 +225,8 @@ const BaseNode: FC<BaseNodeProps> = ({ <NodeSourceHandle id={id} data={data} - handleClassName='!top-4 !-right-[9px] !translate-y-0' - handleId='source' + handleClassName="!top-4 !-right-[9px] !translate-y-0" + handleId="source" /> ) } @@ -241,31 +241,33 @@ const BaseNode: FC<BaseNodeProps> = ({ <div className={cn( 'flex items-center rounded-t-2xl px-3 pb-2 pt-3', (data.type === BlockEnum.Iteration || data.type === BlockEnum.Loop) && 'bg-transparent', - )}> + )} + > <BlockIcon - className='mr-2 shrink-0' + className="mr-2 shrink-0" type={data.type} - size='md' + size="md" toolIcon={toolIcon} /> <div title={data.title} - className='system-sm-semibold-uppercase mr-1 flex grow items-center truncate text-text-primary' + className="system-sm-semibold-uppercase mr-1 flex grow items-center truncate text-text-primary" > <div> {data.title} </div> { data.type === BlockEnum.Iteration && (data as IterationNodeType).is_parallel && ( - <Tooltip popupContent={ - <div className='w-[180px]'> - <div className='font-extrabold'> + <Tooltip popupContent={( + <div className="w-[180px]"> + <div className="font-extrabold"> {t('workflow.nodes.iteration.parallelModeEnableTitle')} </div> {t('workflow.nodes.iteration.parallelModeEnableDesc')} - </div>} + </div> + )} > - <div className='system-2xs-medium-uppercase ml-1 flex items-center justify-center rounded-[5px] border-[1px] border-text-warning px-[5px] py-[3px] text-text-warning '> + <div className="system-2xs-medium-uppercase ml-1 flex items-center justify-center rounded-[5px] border-[1px] border-text-warning px-[5px] py-[3px] text-text-warning "> {t('workflow.nodes.iteration.parallelModeUpper')} </div> </Tooltip> @@ -274,8 +276,10 @@ const BaseNode: FC<BaseNodeProps> = ({ </div> { data._iterationLength && data._iterationIndex && data._runningStatus === NodeRunningStatus.Running && ( - <div className='mr-1.5 text-xs font-medium text-text-accent'> - {data._iterationIndex > data._iterationLength ? data._iterationLength : data._iterationIndex}/{data._iterationLength} + <div className="mr-1.5 text-xs font-medium text-text-accent"> + {data._iterationIndex > data._iterationLength ? data._iterationLength : data._iterationIndex} + / + {data._iterationLength} </div> ) } @@ -284,14 +288,14 @@ const BaseNode: FC<BaseNodeProps> = ({ } { isLoading - ? <RiLoader2Line className='h-3.5 w-3.5 animate-spin text-text-accent' /> + ? <RiLoader2Line className="h-3.5 w-3.5 animate-spin text-text-accent" /> : data._runningStatus === NodeRunningStatus.Failed - ? <RiErrorWarningFill className='h-3.5 w-3.5 text-text-destructive' /> + ? <RiErrorWarningFill className="h-3.5 w-3.5 text-text-destructive" /> : data._runningStatus === NodeRunningStatus.Exception - ? <RiAlertFill className='h-3.5 w-3.5 text-text-warning-secondary' /> + ? <RiAlertFill className="h-3.5 w-3.5 text-text-warning-secondary" /> : (data._runningStatus === NodeRunningStatus.Succeeded || hasVarValue) - ? <RiCheckboxCircleFill className='h-3.5 w-3.5 text-text-success' /> - : null + ? <RiCheckboxCircleFill className="h-3.5 w-3.5 text-text-success" /> + : null } </div> { @@ -301,7 +305,7 @@ const BaseNode: FC<BaseNodeProps> = ({ } { (data.type === BlockEnum.Iteration || data.type === BlockEnum.Loop) && ( - <div className='grow pb-1 pl-1 pr-1'> + <div className="grow pb-1 pl-1 pr-1"> {cloneElement(children, { id, data } as any)} </div> ) @@ -324,13 +328,13 @@ const BaseNode: FC<BaseNodeProps> = ({ } { data.desc && data.type !== BlockEnum.Iteration && data.type !== BlockEnum.Loop && ( - <div className='system-xs-regular whitespace-pre-line break-words px-3 pb-2 pt-1 text-text-tertiary'> + <div className="system-xs-regular whitespace-pre-line break-words px-3 pb-2 pt-1 text-text-tertiary"> {data.desc} </div> ) } {data.type === BlockEnum.Tool && data.provider_type === ToolTypeEnum.MCP && ( - <div className='px-3 pb-2'> + <div className="px-3 pb-2"> <CopyID content={data.provider_id || ''} /> </div> )} @@ -341,13 +345,15 @@ const BaseNode: FC<BaseNodeProps> = ({ const isStartNode = data.type === BlockEnum.Start const isEntryNode = isTriggerNode(data.type as any) || isStartNode - return isEntryNode ? ( - <EntryNodeContainer - nodeType={isStartNode ? StartNodeTypeEnum.Start : StartNodeTypeEnum.Trigger} - > - {nodeContent} - </EntryNodeContainer> - ) : nodeContent + return isEntryNode + ? ( + <EntryNodeContainer + nodeType={isStartNode ? StartNodeTypeEnum.Start : StartNodeTypeEnum.Trigger} + > + {nodeContent} + </EntryNodeContainer> + ) + : nodeContent } export default memo(BaseNode) diff --git a/web/app/components/workflow/nodes/agent/components/model-bar.tsx b/web/app/components/workflow/nodes/agent/components/model-bar.tsx index 1b2007070f..2f13cb84b2 100644 --- a/web/app/components/workflow/nodes/agent/components/model-bar.tsx +++ b/web/app/components/workflow/nodes/agent/components/model-bar.tsx @@ -1,10 +1,11 @@ +import type { FC } from 'react' +import { useMemo } from 'react' +import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' 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 ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' import Indicator from '@/app/components/header/indicator' -import { type FC, useMemo } from 'react' -import { useTranslation } from 'react-i18next' export type ModelBarProps = { provider: string @@ -35,41 +36,46 @@ export const ModelBar: FC<ModelBarProps> = (props) => { const { t } = useTranslation() const modelList = useAllModel() if (!('provider' in props)) { - return <Tooltip - popupContent={t('workflow.nodes.agent.modelNotSelected')} - triggerMethod='hover' - > - <div className='relative'> - <ModelSelector - modelList={[]} - triggerClassName='bg-workflow-block-parma-bg !h-6 !rounded-md' - defaultModel={undefined} - showDeprecatedWarnIcon={false} - readonly - deprecatedClassName='opacity-50' - /> - <Indicator color={'red'} className='absolute -right-0.5 -top-0.5' /> - </div> - </Tooltip> + return ( + <Tooltip + popupContent={t('workflow.nodes.agent.modelNotSelected')} + triggerMethod="hover" + > + <div className="relative"> + <ModelSelector + modelList={[]} + triggerClassName="bg-workflow-block-parma-bg !h-6 !rounded-md" + defaultModel={undefined} + showDeprecatedWarnIcon={false} + readonly + deprecatedClassName="opacity-50" + /> + <Indicator color="red" className="absolute -right-0.5 -top-0.5" /> + </div> + </Tooltip> + ) } const modelInstalled = modelList?.some( - provider => provider.provider === props.provider && provider.models.some(model => model.model === props.model)) + provider => provider.provider === props.provider && provider.models.some(model => model.model === props.model), + ) const showWarn = modelList && !modelInstalled - return modelList && <Tooltip - popupContent={t('workflow.nodes.agent.modelNotInstallTooltip')} - triggerMethod='hover' - disabled={!modelList || modelInstalled} - > - <div className='relative'> - <ModelSelector - modelList={modelList} - triggerClassName='bg-workflow-block-parma-bg !h-6 !rounded-md' - defaultModel={props} - showDeprecatedWarnIcon={false} - readonly - deprecatedClassName='opacity-50' - /> - {showWarn && <Indicator color={'red'} className='absolute -right-0.5 -top-0.5' />} - </div> - </Tooltip> + return modelList && ( + <Tooltip + popupContent={t('workflow.nodes.agent.modelNotInstallTooltip')} + triggerMethod="hover" + disabled={!modelList || modelInstalled} + > + <div className="relative"> + <ModelSelector + modelList={modelList} + triggerClassName="bg-workflow-block-parma-bg !h-6 !rounded-md" + defaultModel={props} + showDeprecatedWarnIcon={false} + readonly + deprecatedClassName="opacity-50" + /> + {showWarn && <Indicator color="red" className="absolute -right-0.5 -top-0.5" />} + </div> + </Tooltip> + ) } diff --git a/web/app/components/workflow/nodes/agent/components/tool-icon.tsx b/web/app/components/workflow/nodes/agent/components/tool-icon.tsx index 0d2be2bee4..129458f623 100644 --- a/web/app/components/workflow/nodes/agent/components/tool-icon.tsx +++ b/web/app/components/workflow/nodes/agent/components/tool-icon.tsx @@ -1,12 +1,12 @@ +import { memo, useMemo, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' +import AppIcon from '@/app/components/base/app-icon' +import { Group } from '@/app/components/base/icons/src/vender/other' import Tooltip from '@/app/components/base/tooltip' import Indicator from '@/app/components/header/indicator' -import { cn } from '@/utils/classnames' -import { memo, useMemo, useRef, useState } from 'react' import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools } from '@/service/use-tools' +import { cn } from '@/utils/classnames' import { getIconFromMarketPlace } from '@/utils/get-icon' -import { useTranslation } from 'react-i18next' -import { Group } from '@/app/components/base/icons/src/vender/other' -import AppIcon from '@/app/components/base/app-icon' type Status = 'not-installed' | 'not-authorized' | undefined @@ -33,63 +33,75 @@ export const ToolIcon = memo(({ providerName }: ToolIconProps) => { const author = providerNameParts[0] const name = providerNameParts[1] const icon = useMemo(() => { - if (!isDataReady) return '' - if (currentProvider) return currentProvider.icon + if (!isDataReady) + return '' + if (currentProvider) + return currentProvider.icon const iconFromMarketPlace = getIconFromMarketPlace(`${author}/${name}`) return iconFromMarketPlace }, [author, currentProvider, name, isDataReady]) const status: Status = useMemo(() => { - if (!isDataReady) return undefined - if (!currentProvider) return 'not-installed' - if (currentProvider.is_team_authorization === false) return 'not-authorized' + if (!isDataReady) + return undefined + if (!currentProvider) + return 'not-installed' + if (currentProvider.is_team_authorization === false) + return 'not-authorized' return undefined }, [currentProvider, isDataReady]) const indicator = status === 'not-installed' ? 'red' : status === 'not-authorized' ? 'yellow' : undefined const notSuccess = (['not-installed', 'not-authorized'] as Array<Status>).includes(status) const { t } = useTranslation() const tooltip = useMemo(() => { - if (!notSuccess) return undefined - if (status === 'not-installed') return t('workflow.nodes.agent.toolNotInstallTooltip', { tool: name }) - if (status === 'not-authorized') return t('workflow.nodes.agent.toolNotAuthorizedTooltip', { tool: name }) + if (!notSuccess) + return undefined + if (status === 'not-installed') + return t('workflow.nodes.agent.toolNotInstallTooltip', { tool: name }) + if (status === 'not-authorized') + return t('workflow.nodes.agent.toolNotAuthorizedTooltip', { tool: name }) throw new Error('Unknown status') }, [name, notSuccess, status, t]) const [iconFetchError, setIconFetchError] = useState(false) - return <Tooltip - triggerMethod='hover' - popupContent={tooltip} - disabled={!notSuccess} - > - <div - className={cn('relative')} - ref={containerRef} + return ( + <Tooltip + triggerMethod="hover" + popupContent={tooltip} + disabled={!notSuccess} > - <div className="flex size-5 items-center justify-center overflow-hidden rounded-[6px] border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge"> - {(() => { - if (iconFetchError || !icon) + <div + className={cn('relative')} + ref={containerRef} + > + <div className="flex size-5 items-center justify-center overflow-hidden rounded-[6px] border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge"> + {(() => { + if (iconFetchError || !icon) + return <Group className="h-3 w-3 opacity-35" /> + if (typeof icon === 'string') { + return ( + <img + src={icon} + alt="tool icon" + className={cn('size-3.5 h-full w-full object-cover', notSuccess && 'opacity-50')} + onError={() => setIconFetchError(true)} + /> + ) + } + if (typeof icon === 'object') { + return ( + <AppIcon + className={cn('size-3.5 h-full w-full object-cover', notSuccess && 'opacity-50')} + icon={icon?.content} + background={icon?.background} + /> + ) + } return <Group className="h-3 w-3 opacity-35" /> - if (typeof icon === 'string') { - return <img - src={icon} - alt='tool icon' - className={cn('size-3.5 h-full w-full object-cover', - notSuccess && 'opacity-50')} - onError={() => setIconFetchError(true)} - /> - } - if (typeof icon === 'object') { - return <AppIcon - className={cn('size-3.5 h-full w-full object-cover', - notSuccess && 'opacity-50')} - icon={icon?.content} - background={icon?.background} - /> - } - return <Group className="h-3 w-3 opacity-35" /> - })()} + })()} + </div> + {indicator && <Indicator color={indicator} className="absolute -right-[1px] -top-[1px]" />} </div> - {indicator && <Indicator color={indicator} className="absolute -right-[1px] -top-[1px]" />} - </div> - </Tooltip> + </Tooltip> + ) }) ToolIcon.displayName = 'ToolIcon' diff --git a/web/app/components/workflow/nodes/agent/default.ts b/web/app/components/workflow/nodes/agent/default.ts index 5038ed424b..4f4e604e2f 100644 --- a/web/app/components/workflow/nodes/agent/default.ts +++ b/web/app/components/workflow/nodes/agent/default.ts @@ -1,14 +1,15 @@ -import type { StrategyDetail, StrategyPluginDetail } from '@/app/components/plugins/types' -import { BlockEnum, type NodeDefault } from '../../types' +import type { NodeDefault } from '../../types' import type { AgentNodeType } from './types' +import type { StrategyDetail, StrategyPluginDetail } from '@/app/components/plugins/types' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { renderI18nObject } from '@/i18n-config' +import { BlockEnum } from '../../types' import { genNodeMetaData } from '../../utils' const metaData = genNodeMetaData({ sort: 3, type: BlockEnum.Agent, }) -import { renderI18nObject } from '@/i18n-config' const nodeDefault: NodeDefault<AgentNodeType> = { metaData, @@ -16,7 +17,7 @@ const nodeDefault: NodeDefault<AgentNodeType> = { tool_node_version: '2', }, checkValid(payload, t, moreDataForCheckValid: { - strategyProvider?: StrategyPluginDetail, + strategyProvider?: StrategyPluginDetail strategy?: StrategyDetail language: string isReadyForCheckValid: boolean diff --git a/web/app/components/workflow/nodes/agent/node.tsx b/web/app/components/workflow/nodes/agent/node.tsx index fe87bc7cda..d2e552f6f4 100644 --- a/web/app/components/workflow/nodes/agent/node.tsx +++ b/web/app/components/workflow/nodes/agent/node.tsx @@ -1,22 +1,24 @@ -import { type FC, memo, useMemo } from 'react' +import type { FC } from 'react' import type { NodeProps } from '../../types' -import type { AgentNodeType } from './types' -import { SettingItem } from '../_base/components/setting-item' -import { Group, GroupLabel } from '../_base/components/group' import type { ToolIconProps } from './components/tool-icon' -import { ToolIcon } from './components/tool-icon' -import useConfig from './use-config' +import type { AgentNodeType } from './types' +import { memo, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useRenderI18nObject } from '@/hooks/use-i18n' +import { Group, GroupLabel } from '../_base/components/group' +import { SettingItem } from '../_base/components/setting-item' import { ModelBar } from './components/model-bar' +import { ToolIcon } from './components/tool-icon' +import useConfig from './use-config' const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => { const { inputs, currentStrategy, currentStrategyStatus, pluginDetail } = useConfig(props.id, props.data) const renderI18nObject = useRenderI18nObject() const { t } = useTranslation() const models = useMemo(() => { - if (!inputs) return [] + if (!inputs) + return [] // if selected, show in node // if required and not selected, show empty selector // if not required and not selected, show nothing @@ -65,49 +67,64 @@ const AgentNode: FC<NodeProps<AgentNodeType>> = (props) => { }) return tools }, [currentStrategy?.parameters, inputs.agent_parameters]) - return <div className='mb-1 space-y-1 px-3'> - {inputs.agent_strategy_name - ? <SettingItem - label={t('workflow.nodes.agent.strategy.shortLabel')} - status={ - currentStrategyStatus && !currentStrategyStatus.isExistInPlugin - ? 'error' - : undefined - } - tooltip={ - (currentStrategyStatus && !currentStrategyStatus.isExistInPlugin) - ? t('workflow.nodes.agent.strategyNotInstallTooltip', { - plugin: pluginDetail?.declaration.label - ? renderI18nObject(pluginDetail?.declaration.label) - : undefined, - strategy: inputs.agent_strategy_label, - }) - : undefined - } - > - {inputs.agent_strategy_label} - </SettingItem> - : <SettingItem label={t('workflow.nodes.agent.strategyNotSet')} />} - {models.length > 0 && <Group - label={<GroupLabel className='mt-1'> - {t('workflow.nodes.agent.model')} - </GroupLabel>} - > - {models.map((model) => { - return <ModelBar - {...model} - key={model.param} - /> - })} - </Group>} - {tools.length > 0 && <Group label={<GroupLabel className='mt-1'> - {t('workflow.nodes.agent.toolbox')} - </GroupLabel>}> - <div className='grid grid-cols-10 gap-0.5'> - {tools.map((tool, i) => <ToolIcon {...tool} key={tool.id + i} />)} - </div> - </Group>} - </div> + return ( + <div className="mb-1 space-y-1 px-3"> + {inputs.agent_strategy_name + ? ( + <SettingItem + label={t('workflow.nodes.agent.strategy.shortLabel')} + status={ + currentStrategyStatus && !currentStrategyStatus.isExistInPlugin + ? 'error' + : undefined + } + tooltip={ + (currentStrategyStatus && !currentStrategyStatus.isExistInPlugin) + ? t('workflow.nodes.agent.strategyNotInstallTooltip', { + plugin: pluginDetail?.declaration.label + ? renderI18nObject(pluginDetail?.declaration.label) + : undefined, + strategy: inputs.agent_strategy_label, + }) + : undefined + } + > + {inputs.agent_strategy_label} + </SettingItem> + ) + : <SettingItem label={t('workflow.nodes.agent.strategyNotSet')} />} + {models.length > 0 && ( + <Group + label={( + <GroupLabel className="mt-1"> + {t('workflow.nodes.agent.model')} + </GroupLabel> + )} + > + {models.map((model) => { + return ( + <ModelBar + {...model} + key={model.param} + /> + ) + })} + </Group> + )} + {tools.length > 0 && ( + <Group label={( + <GroupLabel className="mt-1"> + {t('workflow.nodes.agent.toolbox')} + </GroupLabel> + )} + > + <div className="grid grid-cols-10 gap-0.5"> + {tools.map((tool, i) => <ToolIcon {...tool} key={tool.id + i} />)} + </div> + </Group> + )} + </div> + ) } AgentNode.displayName = 'AgentNode' diff --git a/web/app/components/workflow/nodes/agent/panel.tsx b/web/app/components/workflow/nodes/agent/panel.tsx index 7c87da8121..d0f3f83b99 100644 --- a/web/app/components/workflow/nodes/agent/panel.tsx +++ b/web/app/components/workflow/nodes/agent/panel.tsx @@ -1,18 +1,20 @@ import type { FC } from 'react' -import { memo } from 'react' import type { NodePanelProps } from '../../types' -import { AgentFeature, type AgentNodeType } from './types' -import Field from '../_base/components/field' -import { AgentStrategy } from '../_base/components/agent-strategy' -import useConfig from './use-config' -import { useTranslation } from 'react-i18next' -import OutputVars, { VarItem } from '../_base/components/output-vars' -import type { StrategyParamItem } from '@/app/components/plugins/types' +import type { AgentNodeType } from './types' import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { StrategyParamItem } from '@/app/components/plugins/types' +import { memo } from 'react' +import { useTranslation } from 'react-i18next' import { toType } from '@/app/components/tools/utils/to-form-schema' import { useStore } from '../../store' -import Split from '../_base/components/split' +import { AgentStrategy } from '../_base/components/agent-strategy' +import Field from '../_base/components/field' import MemoryConfig from '../_base/components/memory-config' +import OutputVars, { VarItem } from '../_base/components/output-vars' +import Split from '../_base/components/split' +import { AgentFeature } from './types' +import useConfig from './use-config' + const i18nPrefix = 'workflow.nodes.agent' export function strategyParamToCredientialForm(param: StrategyParamItem): CredentialFormSchema { @@ -43,89 +45,94 @@ const AgentPanel: FC<NodePanelProps<AgentNodeType>> = (props) => { const { t } = useTranslation() const resetEditor = useStore(s => s.setControlPromptEditorRerenderKey) - return <div className='my-2'> - <Field - required - title={t('workflow.nodes.agent.strategy.label')} - className='px-4 py-2' - tooltip={t('workflow.nodes.agent.strategy.tooltip')} > - <AgentStrategy - strategy={inputs.agent_strategy_name ? { - agent_strategy_provider_name: inputs.agent_strategy_provider_name!, - agent_strategy_name: inputs.agent_strategy_name!, - agent_strategy_label: inputs.agent_strategy_label!, - agent_output_schema: inputs.output_schema, - plugin_unique_identifier: inputs.plugin_unique_identifier!, - meta: inputs.meta, - } : undefined} - onStrategyChange={(strategy) => { - setInputs({ - ...inputs, - agent_strategy_provider_name: strategy?.agent_strategy_provider_name, - agent_strategy_name: strategy?.agent_strategy_name, - agent_strategy_label: strategy?.agent_strategy_label, - output_schema: strategy!.agent_output_schema, - plugin_unique_identifier: strategy!.plugin_unique_identifier, - meta: strategy?.meta, - }) - resetEditor(Date.now()) - }} - formSchema={currentStrategy?.parameters?.map(strategyParamToCredientialForm) || []} - formValue={formData} - onFormValueChange={onFormChange} - nodeOutputVars={availableVars} - availableNodes={availableNodesWithParent} - nodeId={props.id} - canChooseMCPTool={canChooseMCPTool} - /> - </Field> - <div className='px-4 py-2'> - {isChatMode && currentStrategy?.features?.includes(AgentFeature.HISTORY_MESSAGES) && ( - <> - <Split /> - <MemoryConfig - className='mt-4' - readonly={readOnly} - config={{ data: inputs.memory }} - onChange={handleMemoryChange} - canSetRoleName={false} - /> - </> - )} - </div> - <div> - <OutputVars> - <VarItem - name='text' - type='String' - description={t(`${i18nPrefix}.outputVars.text`)} + return ( + <div className="my-2"> + <Field + required + title={t('workflow.nodes.agent.strategy.label')} + className="px-4 py-2" + tooltip={t('workflow.nodes.agent.strategy.tooltip')} + > + <AgentStrategy + strategy={inputs.agent_strategy_name + ? { + agent_strategy_provider_name: inputs.agent_strategy_provider_name!, + agent_strategy_name: inputs.agent_strategy_name!, + agent_strategy_label: inputs.agent_strategy_label!, + agent_output_schema: inputs.output_schema, + plugin_unique_identifier: inputs.plugin_unique_identifier!, + meta: inputs.meta, + } + : undefined} + onStrategyChange={(strategy) => { + setInputs({ + ...inputs, + agent_strategy_provider_name: strategy?.agent_strategy_provider_name, + agent_strategy_name: strategy?.agent_strategy_name, + agent_strategy_label: strategy?.agent_strategy_label, + output_schema: strategy!.agent_output_schema, + plugin_unique_identifier: strategy!.plugin_unique_identifier, + meta: strategy?.meta, + }) + resetEditor(Date.now()) + }} + formSchema={currentStrategy?.parameters?.map(strategyParamToCredientialForm) || []} + formValue={formData} + onFormValueChange={onFormChange} + nodeOutputVars={availableVars} + availableNodes={availableNodesWithParent} + nodeId={props.id} + canChooseMCPTool={canChooseMCPTool} /> - <VarItem - name='usage' - type='object' - description={t(`${i18nPrefix}.outputVars.usage`)} - /> - <VarItem - name='files' - type='Array[File]' - description={t(`${i18nPrefix}.outputVars.files.title`)} - /> - <VarItem - name='json' - type='Array[Object]' - description={t(`${i18nPrefix}.outputVars.json`)} - /> - {outputSchema.map(({ name, type, description }) => ( + </Field> + <div className="px-4 py-2"> + {isChatMode && currentStrategy?.features?.includes(AgentFeature.HISTORY_MESSAGES) && ( + <> + <Split /> + <MemoryConfig + className="mt-4" + readonly={readOnly} + config={{ data: inputs.memory }} + onChange={handleMemoryChange} + canSetRoleName={false} + /> + </> + )} + </div> + <div> + <OutputVars> <VarItem - key={name} - name={name} - type={type} - description={description} + name="text" + type="String" + description={t(`${i18nPrefix}.outputVars.text`)} /> - ))} - </OutputVars> + <VarItem + name="usage" + type="object" + description={t(`${i18nPrefix}.outputVars.usage`)} + /> + <VarItem + name="files" + type="Array[File]" + description={t(`${i18nPrefix}.outputVars.files.title`)} + /> + <VarItem + name="json" + type="Array[Object]" + description={t(`${i18nPrefix}.outputVars.json`)} + /> + {outputSchema.map(({ name, type, description }) => ( + <VarItem + key={name} + name={name} + type={type} + description={description} + /> + ))} + </OutputVars> + </div> </div> - </div> + ) } AgentPanel.displayName = 'AgentPanel' diff --git a/web/app/components/workflow/nodes/agent/types.ts b/web/app/components/workflow/nodes/agent/types.ts index f163b3572a..efc7c0cd9a 100644 --- a/web/app/components/workflow/nodes/agent/types.ts +++ b/web/app/components/workflow/nodes/agent/types.ts @@ -1,6 +1,6 @@ -import type { CommonNodeType, Memory } from '@/app/components/workflow/types' import type { ToolVarInputs } from '../tool/types' import type { PluginMeta } from '@/app/components/plugins/types' +import type { CommonNodeType, Memory } from '@/app/components/workflow/types' export type AgentNodeType = CommonNodeType & { agent_strategy_provider_name?: string diff --git a/web/app/components/workflow/nodes/agent/use-config.ts b/web/app/components/workflow/nodes/agent/use-config.ts index 90d6736f51..49af451f4c 100644 --- a/web/app/components/workflow/nodes/agent/use-config.ts +++ b/web/app/components/workflow/nodes/agent/use-config.ts @@ -1,21 +1,22 @@ -import { useStrategyProviderDetail } from '@/service/use-strategy' -import useNodeCrud from '../_base/hooks/use-node-crud' -import useVarList from '../_base/hooks/use-var-list' +import type { Memory, Var } from '../../types' +import type { ToolVarInputs } from '../tool/types' import type { AgentNodeType } from './types' +import { produce } from 'immer' +import { useCallback, useEffect, useMemo } from 'react' +import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { generateAgentToolValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema' import { useIsChatMode, useNodesReadOnly, } from '@/app/components/workflow/hooks' -import { useCallback, useEffect, useMemo } from 'react' -import { type ToolVarInputs, VarType } from '../tool/types' import { useCheckInstalled, useFetchPluginsInMarketPlaceByIds } from '@/service/use-plugins' -import type { Memory, Var } from '../../types' +import { useStrategyProviderDetail } from '@/service/use-strategy' +import { isSupportMCP } from '@/utils/plugin-version-feature' import { VarType as VarKindType } from '../../types' import useAvailableVarList from '../_base/hooks/use-available-var-list' -import { produce } from 'immer' -import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { isSupportMCP } from '@/utils/plugin-version-feature' -import { generateAgentToolValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema' +import useNodeCrud from '../_base/hooks/use-node-crud' +import useVarList from '../_base/hooks/use-var-list' +import { VarType } from '../tool/types' export type StrategyStatus = { plugin: { @@ -100,7 +101,8 @@ const useConfig = (id: string, payload: AgentNodeType) => { const isVariable = currentStrategy?.parameters.some( param => param.name === paramName && param.type === FormTypeEnum.any, ) - if (isVariable) return VarType.variable + if (isVariable) + return VarType.variable return VarType.constant }, [currentStrategy?.parameters]) diff --git a/web/app/components/workflow/nodes/agent/use-single-run-form-params.ts b/web/app/components/workflow/nodes/agent/use-single-run-form-params.ts index 0b9a40aea4..f12c1074ce 100644 --- a/web/app/components/workflow/nodes/agent/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/agent/use-single-run-form-params.ts @@ -1,17 +1,17 @@ import type { RefObject } from 'react' -import type { InputVar, Variable } from '@/app/components/workflow/types' -import { useMemo } from 'react' -import useNodeCrud from '../_base/hooks/use-node-crud' import type { AgentNodeType } from './types' -import { useTranslation } from 'react-i18next' import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form' -import { useStrategyInfo } from './use-config' +import type { InputVar, Variable } from '@/app/components/workflow/types' import type { NodeTracing } from '@/types/workflow' +import { useMemo } from 'react' +import { useTranslation } from 'react-i18next' import formatTracing from '@/app/components/workflow/run/utils/format-log' +import useNodeCrud from '../_base/hooks/use-node-crud' +import { useStrategyInfo } from './use-config' type Params = { - id: string, - payload: AgentNodeType, + id: string + payload: AgentNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] diff --git a/web/app/components/workflow/nodes/answer/default.ts b/web/app/components/workflow/nodes/answer/default.ts index f115274900..0011fd09ef 100644 --- a/web/app/components/workflow/nodes/answer/default.ts +++ b/web/app/components/workflow/nodes/answer/default.ts @@ -1,7 +1,7 @@ import type { NodeDefault } from '../../types' import type { AnswerNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' const metaData = genNodeMetaData({ sort: 2.1, diff --git a/web/app/components/workflow/nodes/answer/node.tsx b/web/app/components/workflow/nodes/answer/node.tsx index bb28066d95..12c7b10a1a 100644 --- a/web/app/components/workflow/nodes/answer/node.tsx +++ b/web/app/components/workflow/nodes/answer/node.tsx @@ -1,10 +1,11 @@ import type { FC } from 'react' +import type { AnswerNodeType } from './types' +import type { NodeProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' import InfoPanel from '../_base/components/info-panel' import ReadonlyInputWithSelectVar from '../_base/components/readonly-input-with-select-var' -import type { AnswerNodeType } from './types' -import type { NodeProps } from '@/app/components/workflow/types' + const Node: FC<NodeProps<AnswerNodeType>> = ({ id, data, @@ -12,13 +13,16 @@ const Node: FC<NodeProps<AnswerNodeType>> = ({ const { t } = useTranslation() return ( - <div className='mb-1 px-3 py-1'> - <InfoPanel title={t('workflow.nodes.answer.answer')} content={ - <ReadonlyInputWithSelectVar - value={data.answer} - nodeId={id} - /> - } /> + <div className="mb-1 px-3 py-1"> + <InfoPanel + title={t('workflow.nodes.answer.answer')} + content={( + <ReadonlyInputWithSelectVar + value={data.answer} + nodeId={id} + /> + )} + /> </div> ) } diff --git a/web/app/components/workflow/nodes/answer/panel.tsx b/web/app/components/workflow/nodes/answer/panel.tsx index 2a4b70ee32..170cd17bf8 100644 --- a/web/app/components/workflow/nodes/answer/panel.tsx +++ b/web/app/components/workflow/nodes/answer/panel.tsx @@ -1,11 +1,12 @@ import type { FC } from 'react' +import type { AnswerNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import useConfig from './use-config' -import type { AnswerNodeType } from './types' import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' -import type { NodePanelProps } from '@/app/components/workflow/types' import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' +import useConfig from './use-config' + const i18nPrefix = 'workflow.nodes.answer' const Panel: FC<NodePanelProps<AnswerNodeType>> = ({ @@ -29,7 +30,7 @@ const Panel: FC<NodePanelProps<AnswerNodeType>> = ({ }) return ( - <div className='mb-2 mt-2 space-y-4 px-4'> + <div className="mb-2 mt-2 space-y-4 px-4"> <Editor readOnly={readOnly} justVar diff --git a/web/app/components/workflow/nodes/answer/use-config.ts b/web/app/components/workflow/nodes/answer/use-config.ts index c00b2bd7c5..aad75d6591 100644 --- a/web/app/components/workflow/nodes/answer/use-config.ts +++ b/web/app/components/workflow/nodes/answer/use-config.ts @@ -1,13 +1,13 @@ -import { useCallback } from 'react' -import { produce } from 'immer' -import useVarList from '../_base/hooks/use-var-list' import type { Var } from '../../types' -import { VarType } from '../../types' import type { AnswerNodeType } from './types' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { produce } from 'immer' +import { useCallback } from 'react' import { useNodesReadOnly, } from '@/app/components/workflow/hooks' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { VarType } from '../../types' +import useVarList from '../_base/hooks/use-var-list' const useConfig = (id: string, payload: AnswerNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() diff --git a/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx b/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx index 986a1b034b..fd346e632d 100644 --- a/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx +++ b/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx @@ -1,20 +1,20 @@ import type { FC } from 'react' -import { useState } from 'react' +import type { WriteMode } from '../types' +import type { VarType } from '@/app/components/workflow/types' import { RiArrowDownSLine, RiCheckLine, } from '@remixicon/react' -import { cn } from '@/utils/classnames' +import { useState } from 'react' import { useTranslation } from 'react-i18next' -import type { WriteMode } from '../types' -import { getOperationItems } from '../utils' +import Divider from '@/app/components/base/divider' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import type { VarType } from '@/app/components/workflow/types' -import Divider from '@/app/components/base/divider' +import { cn } from '@/utils/classnames' +import { getOperationItems } from '../utils' type Item = { value: string | number @@ -58,19 +58,16 @@ const OperationSelector: FC<OperationSelectorProps> = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-start' + placement="bottom-start" offset={4} > <PortalToFollowElemTrigger onClick={() => !disabled && setOpen(v => !v)} > <div - className={cn('flex items-center gap-0.5 rounded-lg bg-components-input-bg-normal px-2 py-1', - disabled ? 'cursor-not-allowed !bg-components-input-bg-disabled' : 'cursor-pointer hover:bg-state-base-hover-alt', - open && 'bg-state-base-hover-alt', - className)} + className={cn('flex items-center gap-0.5 rounded-lg bg-components-input-bg-normal px-2 py-1', disabled ? 'cursor-not-allowed !bg-components-input-bg-disabled' : 'cursor-pointer hover:bg-state-base-hover-alt', open && 'bg-state-base-hover-alt', className)} > - <div className='flex items-center p-1'> + <div className="flex items-center p-1"> <span className={`system-sm-regular overflow-hidden truncate text-ellipsis ${selectedItem ? 'text-components-input-text-filled' : 'text-components-input-text-disabled'}`} @@ -83,36 +80,35 @@ const OperationSelector: FC<OperationSelectorProps> = ({ </PortalToFollowElemTrigger> <PortalToFollowElemContent className={`z-20 ${popupClassName}`}> - <div className='flex w-[140px] flex-col items-start rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'> - <div className='flex flex-col items-start self-stretch p-1'> - <div className='flex items-start self-stretch px-3 pb-0.5 pt-1'> - <div className='system-xs-medium-uppercase flex grow text-text-tertiary'>{t(`${i18nPrefix}.operations.title`)}</div> + <div className="flex w-[140px] flex-col items-start rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg"> + <div className="flex flex-col items-start self-stretch p-1"> + <div className="flex items-start self-stretch px-3 pb-0.5 pt-1"> + <div className="system-xs-medium-uppercase flex grow text-text-tertiary">{t(`${i18nPrefix}.operations.title`)}</div> </div> {items.map(item => ( item.value === 'divider' ? ( - <Divider key="divider" className="my-1" /> - ) + <Divider key="divider" className="my-1" /> + ) : ( - <div - key={item.value} - className={cn('flex items-center gap-1 self-stretch rounded-lg px-2 py-1', - 'cursor-pointer hover:bg-state-base-hover')} - onClick={() => { - onSelect(item) - setOpen(false) - }} - > - <div className='flex min-h-5 grow items-center gap-1 px-1'> - <span className={'system-sm-medium flex grow text-text-secondary'}>{t(`${i18nPrefix}.operations.${item.name}`)}</span> - </div> - {item.value === value && ( - <div className='flex items-center justify-center'> - <RiCheckLine className='h-4 w-4 text-text-accent' /> + <div + key={item.value} + className={cn('flex items-center gap-1 self-stretch rounded-lg px-2 py-1', 'cursor-pointer hover:bg-state-base-hover')} + onClick={() => { + onSelect(item) + setOpen(false) + }} + > + <div className="flex min-h-5 grow items-center gap-1 px-1"> + <span className="system-sm-medium flex grow text-text-secondary">{t(`${i18nPrefix}.operations.${item.name}`)}</span> </div> - )} - </div> - ) + {item.value === value && ( + <div className="flex items-center justify-center"> + <RiCheckLine className="h-4 w-4 text-text-accent" /> + </div> + )} + </div> + ) ))} </div> </div> diff --git a/web/app/components/workflow/nodes/assigner/components/var-list/index.tsx b/web/app/components/workflow/nodes/assigner/components/var-list/index.tsx index b81277d740..3f99121835 100644 --- a/web/app/components/workflow/nodes/assigner/components/var-list/index.tsx +++ b/web/app/components/workflow/nodes/assigner/components/var-list/index.tsx @@ -1,23 +1,23 @@ 'use client' import type { FC } from 'react' -import { useTranslation } from 'react-i18next' -import React, { useCallback } from 'react' -import { produce } from 'immer' -import { RiDeleteBinLine } from '@remixicon/react' -import OperationSelector from '../operation-selector' -import { AssignerNodeInputType, WriteMode } from '../../types' import type { AssignerNodeOperation } from '../../types' -import ListNoDataPlaceholder from '@/app/components/workflow/nodes/_base/components/list-no-data-placeholder' -import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' import type { ValueSelector, Var } from '@/app/components/workflow/types' -import { VarType } from '@/app/components/workflow/types' -import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' +import { RiDeleteBinLine } from '@remixicon/react' +import { produce } from 'immer' +import { noop } from 'lodash-es' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' 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 { noop } from 'lodash-es' +import ListNoDataPlaceholder from '@/app/components/workflow/nodes/_base/components/list-no-data-placeholder' +import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' +import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import BoolValue from '@/app/components/workflow/panel/chat-variable-panel/components/bool-value' +import { VarType } from '@/app/components/workflow/types' +import { AssignerNodeInputType, WriteMode } from '../../types' +import OperationSelector from '../operation-selector' type Props = { readonly: boolean @@ -105,7 +105,8 @@ const VarList: FC<Props> = ({ const handleFilterToAssignedVar = useCallback((index: number) => { return (payload: Var) => { const { variable_selector, operation } = list[index] - if (!variable_selector || !operation || !filterToAssignedVar) return true + if (!variable_selector || !operation || !filterToAssignedVar) + return true const assignedVarType = getAssignedVarType?.(variable_selector) const isSameVariable = Array.isArray(variable_selector) && variable_selector.join('.') === `${payload.nodeId}.${payload.variable}` @@ -123,7 +124,7 @@ const VarList: FC<Props> = ({ } return ( - <div className='flex flex-col items-start gap-4 self-stretch'> + <div className="flex flex-col items-start gap-4 self-stretch"> {list.map((item, index) => { const assignedVarType = item.variable_selector ? getAssignedVarType?.(item.variable_selector) : undefined const toAssignedVarType = (assignedVarType && item.operation && getToAssignedVarType) @@ -131,9 +132,9 @@ const VarList: FC<Props> = ({ : undefined return ( - <div className='flex items-start gap-1 self-stretch' key={index}> - <div className='flex grow flex-col items-start gap-1'> - <div className='flex items-center gap-1 self-stretch'> + <div className="flex items-start gap-1 self-stretch" key={index}> + <div className="flex grow flex-col items-start gap-1"> + <div className="flex items-center gap-1 self-stretch"> <VarReferencePicker readonly={readonly} nodeId={nodeId} @@ -144,12 +145,12 @@ const VarList: FC<Props> = ({ filterVar={filterVar} placeholder={t('workflow.nodes.assigner.selectAssignedVariable') as string} minWidth={352} - popupFor='assigned' - className='w-full' + popupFor="assigned" + className="w-full" /> <OperationSelector value={item.operation} - placeholder='Operation' + placeholder="Operation" disabled={!item.variable_selector || item.variable_selector.length === 0} onSelect={handleOperationChange(index, assignedVarType!)} assignedVarType={assignedVarType} @@ -172,11 +173,10 @@ const VarList: FC<Props> = ({ valueTypePlaceHolder={toAssignedVarType} placeholder={t('workflow.nodes.assigner.setParameter') as string} minWidth={352} - popupFor='toAssigned' - className='w-full' + popupFor="toAssigned" + className="w-full" /> - ) - } + )} {item.operation === WriteMode.set && assignedVarType && ( <> {assignedVarType === 'number' && ( @@ -184,14 +184,14 @@ const VarList: FC<Props> = ({ type="number" value={item.value as number} onChange={e => handleToAssignedVarChange(index)(Number(e.target.value))} - className='w-full' + className="w-full" /> )} {assignedVarType === 'string' && ( <Textarea value={item.value as string} onChange={e => handleToAssignedVarChange(index)(e.target.value)} - className='w-full' + className="w-full" /> )} {assignedVarType === 'boolean' && ( @@ -205,28 +205,29 @@ const VarList: FC<Props> = ({ value={item.value as string} language={CodeLanguage.json} onChange={value => handleToAssignedVarChange(index)(value)} - className='w-full' + className="w-full" readOnly={readonly} /> )} </> )} {writeModeTypesNum?.includes(item.operation) - && <Input - type="number" - value={item.value as number} - onChange={e => handleToAssignedVarChange(index)(Number(e.target.value))} - placeholder="Enter number value..." - className='w-full' - /> - } + && ( + <Input + type="number" + value={item.value as number} + onChange={e => handleToAssignedVarChange(index)(Number(e.target.value))} + placeholder="Enter number value..." + className="w-full" + /> + )} </div> <ActionButton - size='l' - className='group shrink-0 hover:!bg-state-destructive-hover' + size="l" + className="group shrink-0 hover:!bg-state-destructive-hover" onClick={handleVarRemove(index)} > - <RiDeleteBinLine className='h-4 w-4 text-text-tertiary group-hover:text-text-destructive' /> + <RiDeleteBinLine className="h-4 w-4 text-text-tertiary group-hover:text-text-destructive" /> </ActionButton> </div> ) diff --git a/web/app/components/workflow/nodes/assigner/components/var-list/use-var-list.ts b/web/app/components/workflow/nodes/assigner/components/var-list/use-var-list.ts index c97006d023..51f938d9b4 100644 --- a/web/app/components/workflow/nodes/assigner/components/var-list/use-var-list.ts +++ b/web/app/components/workflow/nodes/assigner/components/var-list/use-var-list.ts @@ -1,6 +1,6 @@ -import { useCallback } from 'react' -import { produce } from 'immer' import type { AssignerNodeOperation, AssignerNodeType } from '../../types' +import { produce } from 'immer' +import { useCallback } from 'react' import { AssignerNodeInputType, WriteMode } from '../../types' type Params = { diff --git a/web/app/components/workflow/nodes/assigner/default.ts b/web/app/components/workflow/nodes/assigner/default.ts index 049c91f6c2..dcf2a1b5ac 100644 --- a/web/app/components/workflow/nodes/assigner/default.ts +++ b/web/app/components/workflow/nodes/assigner/default.ts @@ -1,8 +1,10 @@ import type { NodeDefault } from '../../types' -import { type AssignerNodeType, WriteMode } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' +import type { AssignerNodeType } from './types' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' +import { WriteMode } from './types' + const i18nPrefix = 'workflow.errorMsg' const metaData = genNodeMetaData({ diff --git a/web/app/components/workflow/nodes/assigner/hooks.ts b/web/app/components/workflow/nodes/assigner/hooks.ts index d42fb8ee8a..d708222868 100644 --- a/web/app/components/workflow/nodes/assigner/hooks.ts +++ b/web/app/components/workflow/nodes/assigner/hooks.ts @@ -1,15 +1,15 @@ +import type { + Node, + Var, +} from '../../types' +import { uniqBy } from 'lodash-es' import { useCallback } from 'react' import { useNodes } from 'reactflow' -import { uniqBy } from 'lodash-es' import { useIsChatMode, useWorkflow, useWorkflowVariables, } from '../../hooks' -import type { - Node, - Var, -} from '../../types' import { AssignerNodeInputType, WriteMode } from './types' export const useGetAvailableVars = () => { diff --git a/web/app/components/workflow/nodes/assigner/node.tsx b/web/app/components/workflow/nodes/assigner/node.tsx index 5e5950d715..c7777c4541 100644 --- a/web/app/components/workflow/nodes/assigner/node.tsx +++ b/web/app/components/workflow/nodes/assigner/node.tsx @@ -1,14 +1,15 @@ import type { FC } from 'react' -import React from 'react' -import { useNodes } from 'reactflow' -import { useTranslation } from 'react-i18next' import type { AssignerNodeType } from './types' +import type { Node, NodeProps } from '@/app/components/workflow/types' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { useNodes } from 'reactflow' +import Badge from '@/app/components/base/badge' import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' -import { BlockEnum, type Node, type NodeProps } from '@/app/components/workflow/types' import { VariableLabelInNode, } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' -import Badge from '@/app/components/base/badge' +import { BlockEnum } from '@/app/components/workflow/types' const i18nPrefix = 'workflow.nodes.assigner' @@ -25,17 +26,17 @@ const NodeComponent: FC<NodeProps<AssignerNodeType>> = ({ if (validOperationItems.length === 0) { return ( - <div className='relative flex flex-col items-start gap-0.5 self-stretch px-3 py-1'> - <div className='flex flex-col items-start gap-1 self-stretch'> - <div className='flex items-center gap-1 self-stretch rounded-md bg-workflow-block-parma-bg px-[5px] py-1'> - <div className='system-xs-medium flex-1 text-text-tertiary'>{t(`${i18nPrefix}.varNotSet`)}</div> + <div className="relative flex flex-col items-start gap-0.5 self-stretch px-3 py-1"> + <div className="flex flex-col items-start gap-1 self-stretch"> + <div className="flex items-center gap-1 self-stretch rounded-md bg-workflow-block-parma-bg px-[5px] py-1"> + <div className="system-xs-medium flex-1 text-text-tertiary">{t(`${i18nPrefix}.varNotSet`)}</div> </div> </div> </div> ) } return ( - <div className='relative flex flex-col items-start gap-0.5 self-stretch px-3 py-1'> + <div className="relative flex flex-col items-start gap-0.5 self-stretch px-3 py-1"> {operationItems.map((value, index) => { const variable = value.variable_selector if (!variable || variable.length === 0) @@ -49,7 +50,7 @@ const NodeComponent: FC<NodeProps<AssignerNodeType>> = ({ nodeType={node?.data.type} nodeTitle={node?.data.title} rightSlot={ - value.operation && <Badge className='!ml-auto shrink-0' text={t(`${i18nPrefix}.operations.${value.operation}`)} /> + value.operation && <Badge className="!ml-auto shrink-0" text={t(`${i18nPrefix}.operations.${value.operation}`)} /> } /> ) @@ -66,13 +67,13 @@ const NodeComponent: FC<NodeProps<AssignerNodeType>> = ({ const node = isSystem ? nodes.find(node => node.data.type === BlockEnum.Start) : nodes.find(node => node.id === variable[0]) return ( - <div className='relative flex flex-col items-start gap-0.5 self-stretch px-3 py-1'> + <div className="relative flex flex-col items-start gap-0.5 self-stretch px-3 py-1"> <VariableLabelInNode variables={variable} nodeType={node?.data.type} nodeTitle={node?.data.title} rightSlot={ - writeMode && <Badge className='!ml-auto shrink-0' text={t(`${i18nPrefix}.operations.${writeMode}`)} /> + writeMode && <Badge className="!ml-auto shrink-0" text={t(`${i18nPrefix}.operations.${writeMode}`)} /> } /> </div> diff --git a/web/app/components/workflow/nodes/assigner/panel.tsx b/web/app/components/workflow/nodes/assigner/panel.tsx index 430f1ae27f..04da330fd4 100644 --- a/web/app/components/workflow/nodes/assigner/panel.tsx +++ b/web/app/components/workflow/nodes/assigner/panel.tsx @@ -1,15 +1,15 @@ import type { FC } from 'react' -import React from 'react' -import { useTranslation } from 'react-i18next' +import type { AssignerNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import { RiAddLine, } from '@remixicon/react' -import VarList from './components/var-list' -import useConfig from './use-config' -import type { AssignerNodeType } from './types' -import type { NodePanelProps } from '@/app/components/workflow/types' -import { useHandleAddOperationItem } from './hooks' +import React from 'react' +import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' +import VarList from './components/var-list' +import { useHandleAddOperationItem } from './hooks' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.assigner' @@ -37,12 +37,12 @@ const Panel: FC<NodePanelProps<AssignerNodeType>> = ({ } return ( - <div className='flex flex-col items-start self-stretch py-2'> - <div className='flex w-full flex-col items-start justify-center gap-1 self-stretch px-4 py-2'> - <div className='flex items-start gap-2 self-stretch'> - <div className='system-sm-semibold-uppercase flex grow flex-col items-start justify-center text-text-secondary'>{t(`${i18nPrefix}.variables`)}</div> + <div className="flex flex-col items-start self-stretch py-2"> + <div className="flex w-full flex-col items-start justify-center gap-1 self-stretch px-4 py-2"> + <div className="flex items-start gap-2 self-stretch"> + <div className="system-sm-semibold-uppercase flex grow flex-col items-start justify-center text-text-secondary">{t(`${i18nPrefix}.variables`)}</div> <ActionButton onClick={handleAddOperation}> - <RiAddLine className='h-4 w-4 shrink-0 text-text-tertiary' /> + <RiAddLine className="h-4 w-4 shrink-0 text-text-tertiary" /> </ActionButton> </div> <VarList diff --git a/web/app/components/workflow/nodes/assigner/use-config.ts b/web/app/components/workflow/nodes/assigner/use-config.ts index a69ddd2464..b9f34c4a9c 100644 --- a/web/app/components/workflow/nodes/assigner/use-config.ts +++ b/web/app/components/workflow/nodes/assigner/use-config.ts @@ -1,20 +1,19 @@ -import { useCallback, useMemo } from 'react' -import { produce } from 'immer' -import { useStoreApi } from 'reactflow' -import { VarType } from '../../types' import type { ValueSelector, Var } from '../../types' -import { WriteMode } from './types' import type { AssignerNodeOperation, AssignerNodeType } from './types' -import { writeModeTypesNum } from './types' -import { useGetAvailableVars } from './hooks' -import { convertV1ToV2 } from './utils' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { produce } from 'immer' +import { useCallback, useMemo } from 'react' +import { useStoreApi } from 'reactflow' import { useIsChatMode, useNodesReadOnly, useWorkflow, useWorkflowVariables, } from '@/app/components/workflow/hooks' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { VarType } from '../../types' +import { useGetAvailableVars } from './hooks' +import { WriteMode, writeModeTypesNum } from './types' +import { convertV1ToV2 } from './utils' const useConfig = (id: string, rawPayload: AssignerNodeType) => { const payload = useMemo(() => convertV1ToV2(rawPayload), [rawPayload]) @@ -75,8 +74,9 @@ const useConfig = (id: string, rawPayload: AssignerNodeType) => { const getToAssignedVarType = useCallback((assignedVarType: VarType, write_mode: WriteMode) => { if (write_mode === WriteMode.overwrite || write_mode === WriteMode.increment || write_mode === WriteMode.decrement - || write_mode === WriteMode.multiply || write_mode === WriteMode.divide || write_mode === WriteMode.extend) + || write_mode === WriteMode.multiply || write_mode === WriteMode.divide || write_mode === WriteMode.extend) { return assignedVarType + } if (write_mode === WriteMode.append) { if (assignedVarType === VarType.arrayString) return VarType.string diff --git a/web/app/components/workflow/nodes/assigner/use-single-run-form-params.ts b/web/app/components/workflow/nodes/assigner/use-single-run-form-params.ts index 403157b132..002fac719d 100644 --- a/web/app/components/workflow/nodes/assigner/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/assigner/use-single-run-form-params.ts @@ -1,13 +1,13 @@ import type { RefObject } from 'react' +import type { AssignerNodeType } from './types' import type { InputVar, ValueSelector, Variable } from '@/app/components/workflow/types' import { useMemo } from 'react' import useNodeCrud from '../_base/hooks/use-node-crud' -import { type AssignerNodeType, WriteMode } from './types' -import { writeModeTypesNum } from './types' +import { WriteMode, writeModeTypesNum } from './types' type Params = { - id: string, - payload: AssignerNodeType, + id: string + payload: AssignerNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] @@ -26,8 +26,8 @@ const useSingleRunFormParams = ({ const vars = (inputs.items ?? []).filter((item) => { return item.operation !== WriteMode.clear && item.operation !== WriteMode.set - && item.operation !== WriteMode.removeFirst && item.operation !== WriteMode.removeLast - && !writeModeTypesNum.includes(item.operation) + && item.operation !== WriteMode.removeFirst && item.operation !== WriteMode.removeLast + && !writeModeTypesNum.includes(item.operation) }).map(item => item.value as ValueSelector) const forms = useMemo(() => { diff --git a/web/app/components/workflow/nodes/code/code-parser.spec.ts b/web/app/components/workflow/nodes/code/code-parser.spec.ts index 67f2c218e1..d7fd590f28 100644 --- a/web/app/components/workflow/nodes/code/code-parser.spec.ts +++ b/web/app/components/workflow/nodes/code/code-parser.spec.ts @@ -31,27 +31,27 @@ const SAMPLE_CODES = { describe('extractFunctionParams', () => { describe('Python3', () => { - test('handles no parameters', () => { + it('handles no parameters', () => { const result = extractFunctionParams(SAMPLE_CODES.python3.noParams, CodeLanguage.python3) expect(result).toEqual([]) }) - test('extracts single parameter', () => { + it('extracts single parameter', () => { const result = extractFunctionParams(SAMPLE_CODES.python3.singleParam, CodeLanguage.python3) expect(result).toEqual(['param1']) }) - test('extracts multiple parameters', () => { + it('extracts multiple parameters', () => { const result = extractFunctionParams(SAMPLE_CODES.python3.multipleParams, CodeLanguage.python3) expect(result).toEqual(['param1', 'param2', 'param3']) }) - test('handles type hints', () => { + it('handles type hints', () => { const result = extractFunctionParams(SAMPLE_CODES.python3.withTypes, CodeLanguage.python3) expect(result).toEqual(['param1', 'param2', 'param3']) }) - test('handles default values', () => { + it('handles default values', () => { const result = extractFunctionParams(SAMPLE_CODES.python3.withDefaults, CodeLanguage.python3) expect(result).toEqual(['param1', 'param2']) }) @@ -59,27 +59,27 @@ describe('extractFunctionParams', () => { // JavaScript のテストケース describe('JavaScript', () => { - test('handles no parameters', () => { + it('handles no parameters', () => { const result = extractFunctionParams(SAMPLE_CODES.javascript.noParams, CodeLanguage.javascript) expect(result).toEqual([]) }) - test('extracts single parameter', () => { + it('extracts single parameter', () => { const result = extractFunctionParams(SAMPLE_CODES.javascript.singleParam, CodeLanguage.javascript) expect(result).toEqual(['param1']) }) - test('extracts multiple parameters', () => { + it('extracts multiple parameters', () => { const result = extractFunctionParams(SAMPLE_CODES.javascript.multipleParams, CodeLanguage.javascript) expect(result).toEqual(['param1', 'param2', 'param3']) }) - test('handles comments in code', () => { + it('handles comments in code', () => { const result = extractFunctionParams(SAMPLE_CODES.javascript.withComments, CodeLanguage.javascript) expect(result).toEqual(['param1', 'param2']) }) - test('handles whitespace', () => { + it('handles whitespace', () => { const result = extractFunctionParams(SAMPLE_CODES.javascript.withSpaces, CodeLanguage.javascript) expect(result).toEqual(['param1', 'param2']) }) @@ -182,7 +182,7 @@ function main(name, age, city) { describe('extractReturnType', () => { // Python3 のテスト describe('Python3', () => { - test('extracts single return value', () => { + it('extracts single return value', () => { const result = extractReturnType(RETURN_TYPE_SAMPLES.python3.singleReturn, CodeLanguage.python3) expect(result).toEqual({ result: { @@ -192,7 +192,7 @@ describe('extractReturnType', () => { }) }) - test('extracts multiple return values', () => { + it('extracts multiple return values', () => { const result = extractReturnType(RETURN_TYPE_SAMPLES.python3.multipleReturns, CodeLanguage.python3) expect(result).toEqual({ result: { @@ -206,12 +206,12 @@ describe('extractReturnType', () => { }) }) - test('returns empty object when no return statement', () => { + it('returns empty object when no return statement', () => { const result = extractReturnType(RETURN_TYPE_SAMPLES.python3.noReturn, CodeLanguage.python3) expect(result).toEqual({}) }) - test('handles complex return statement', () => { + it('handles complex return statement', () => { const result = extractReturnType(RETURN_TYPE_SAMPLES.python3.complexReturn, CodeLanguage.python3) expect(result).toEqual({ result: { @@ -228,7 +228,7 @@ describe('extractReturnType', () => { }, }) }) - test('handles nested object structure', () => { + it('handles nested object structure', () => { const result = extractReturnType(RETURN_TYPE_SAMPLES.python3.nestedObject, CodeLanguage.python3) expect(result).toEqual({ personal_info: { @@ -249,7 +249,7 @@ describe('extractReturnType', () => { // JavaScript のテスト describe('JavaScript', () => { - test('extracts single return value', () => { + it('extracts single return value', () => { const result = extractReturnType(RETURN_TYPE_SAMPLES.javascript.singleReturn, CodeLanguage.javascript) expect(result).toEqual({ result: { @@ -259,7 +259,7 @@ describe('extractReturnType', () => { }) }) - test('extracts multiple return values', () => { + it('extracts multiple return values', () => { const result = extractReturnType(RETURN_TYPE_SAMPLES.javascript.multipleReturns, CodeLanguage.javascript) expect(result).toEqual({ result: { @@ -273,7 +273,7 @@ describe('extractReturnType', () => { }) }) - test('handles return with parentheses', () => { + it('handles return with parentheses', () => { const result = extractReturnType(RETURN_TYPE_SAMPLES.javascript.withParentheses, CodeLanguage.javascript) expect(result).toEqual({ result: { @@ -287,12 +287,12 @@ describe('extractReturnType', () => { }) }) - test('returns empty object when no return statement', () => { + it('returns empty object when no return statement', () => { const result = extractReturnType(RETURN_TYPE_SAMPLES.javascript.noReturn, CodeLanguage.javascript) expect(result).toEqual({}) }) - test('handles quoted keys', () => { + it('handles quoted keys', () => { const result = extractReturnType(RETURN_TYPE_SAMPLES.javascript.withQuotes, CodeLanguage.javascript) expect(result).toEqual({ result: { @@ -305,7 +305,7 @@ describe('extractReturnType', () => { }, }) }) - test('handles nested object structure', () => { + it('handles nested object structure', () => { const result = extractReturnType(RETURN_TYPE_SAMPLES.javascript.nestedObject, CodeLanguage.javascript) expect(result).toEqual({ personal_info: { diff --git a/web/app/components/workflow/nodes/code/code-parser.ts b/web/app/components/workflow/nodes/code/code-parser.ts index 7550e62e96..cdd8a69a26 100644 --- a/web/app/components/workflow/nodes/code/code-parser.ts +++ b/web/app/components/workflow/nodes/code/code-parser.ts @@ -1,5 +1,5 @@ -import { VarType } from '../../types' import type { OutputVar } from './types' +import { VarType } from '../../types' import { CodeLanguage } from './types' export const extractFunctionParams = (code: string, language: CodeLanguage) => { @@ -68,7 +68,7 @@ export const extractReturnType = (code: string, language: CodeLanguage): OutputV const result: OutputVar = {} - const keyRegex = /['"]?(\w+)['"]?\s*:(?![^{]*})/g + const keyRegex = /['"]?(\w+)['"]?\s*:(?![^{]*\})/g const matches = returnContent.matchAll(keyRegex) for (const match of matches) { diff --git a/web/app/components/workflow/nodes/code/default.ts b/web/app/components/workflow/nodes/code/default.ts index 7cf40db63f..6a0ae7861d 100644 --- a/web/app/components/workflow/nodes/code/default.ts +++ b/web/app/components/workflow/nodes/code/default.ts @@ -1,8 +1,9 @@ import type { NodeDefault } from '../../types' -import { CodeLanguage, type CodeNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' +import type { CodeNodeType } from './types' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' +import { CodeLanguage } from './types' const i18nPrefix = 'workflow.errorMsg' diff --git a/web/app/components/workflow/nodes/code/dependency-picker.tsx b/web/app/components/workflow/nodes/code/dependency-picker.tsx index a302a21366..2e0e0c0d59 100644 --- a/web/app/components/workflow/nodes/code/dependency-picker.tsx +++ b/web/app/components/workflow/nodes/code/dependency-picker.tsx @@ -1,13 +1,13 @@ import type { FC } from 'react' -import React, { useCallback, useState } from 'react' -import { t } from 'i18next' +import type { CodeDependency } from './types' import { RiArrowDownSLine, } from '@remixicon/react' -import type { CodeDependency } from './types' -import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' -import Input from '@/app/components/base/input' +import { t } from 'i18next' +import React, { useCallback, useState } from 'react' import { Check } from '@/app/components/base/icons/src/vender/line/general' +import Input from '@/app/components/base/input' +import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' type Props = { value: CodeDependency @@ -34,22 +34,26 @@ const DependencyPicker: FC<Props> = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-start' + placement="bottom-start" offset={4} > - <PortalToFollowElemTrigger onClick={() => setOpen(!open)} className='grow cursor-pointer'> - <div className='flex h-8 items-center justify-between rounded-lg border-0 bg-gray-100 px-2.5 text-[13px] text-gray-900'> - <div className='w-0 grow truncate' title={value.name}>{value.name}</div> - <RiArrowDownSLine className='h-3.5 w-3.5 shrink-0 text-gray-700' /> + <PortalToFollowElemTrigger onClick={() => setOpen(!open)} className="grow cursor-pointer"> + <div className="flex h-8 items-center justify-between rounded-lg border-0 bg-gray-100 px-2.5 text-[13px] text-gray-900"> + <div className="w-0 grow truncate" title={value.name}>{value.name}</div> + <RiArrowDownSLine className="h-3.5 w-3.5 shrink-0 text-gray-700" /> </div> </PortalToFollowElemTrigger> <PortalToFollowElemContent style={{ zIndex: 100, - }}> - <div className='rounded-lg bg-white p-1 shadow-sm' style={{ - width: 350, - }}> - <div className='mx-1 mb-2'> + }} + > + <div + className="rounded-lg bg-white p-1 shadow-sm" + style={{ + width: 350, + }} + > + <div className="mx-1 mb-2"> <Input showLeftIcon showClearIcon @@ -60,7 +64,7 @@ const DependencyPicker: FC<Props> = ({ autoFocus /> </div> - <div className='max-h-[30vh] overflow-y-auto'> + <div className="max-h-[30vh] overflow-y-auto"> {available_dependencies.filter((v) => { if (!searchText) return true @@ -68,11 +72,11 @@ const DependencyPicker: FC<Props> = ({ }).map(dependency => ( <div key={dependency.name} - className='flex h-[30px] cursor-pointer items-center justify-between rounded-lg pl-3 pr-2 text-[13px] text-gray-900 hover:bg-gray-100' + className="flex h-[30px] cursor-pointer items-center justify-between rounded-lg pl-3 pr-2 text-[13px] text-gray-900 hover:bg-gray-100" onClick={handleChange(dependency)} > - <div className='w-0 grow truncate'>{dependency.name}</div> - {dependency.name === value.name && <Check className='h-4 w-4 shrink-0 text-primary-600' />} + <div className="w-0 grow truncate">{dependency.name}</div> + {dependency.name === value.name && <Check className="h-4 w-4 shrink-0 text-primary-600" />} </div> ))} </div> diff --git a/web/app/components/workflow/nodes/code/node.tsx b/web/app/components/workflow/nodes/code/node.tsx index 03e16f56e9..5fa002913d 100644 --- a/web/app/components/workflow/nodes/code/node.tsx +++ b/web/app/components/workflow/nodes/code/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' -import React from 'react' import type { CodeNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' +import React from 'react' const Node: FC<NodeProps<CodeNodeType>> = () => { return ( diff --git a/web/app/components/workflow/nodes/code/panel.tsx b/web/app/components/workflow/nodes/code/panel.tsx index afbf293911..261195c4c5 100644 --- a/web/app/components/workflow/nodes/code/panel.tsx +++ b/web/app/components/workflow/nodes/code/panel.tsx @@ -1,20 +1,21 @@ import type { FC } from 'react' +import type { CodeNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import RemoveEffectVarConfirm from '../_base/components/remove-effect-var-confirm' -import useConfig from './use-config' -import type { CodeNodeType } from './types' -import { CodeLanguage } from './types' -import { extractFunctionParams, extractReturnType } from './code-parser' -import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list' -import OutputVarList from '@/app/components/workflow/nodes/_base/components/variable/output-var-list' import AddButton from '@/app/components/base/button/add-button' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import Split from '@/app/components/workflow/nodes/_base/components/split' -import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' -import TypeSelector from '@/app/components/workflow/nodes/_base/components/selector' -import type { NodePanelProps } from '@/app/components/workflow/types' import SyncButton from '@/app/components/base/button/sync-button' +import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import TypeSelector from '@/app/components/workflow/nodes/_base/components/selector' +import Split from '@/app/components/workflow/nodes/_base/components/split' +import OutputVarList from '@/app/components/workflow/nodes/_base/components/variable/output-var-list' +import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list' +import RemoveEffectVarConfirm from '../_base/components/remove-effect-var-confirm' +import { extractFunctionParams, extractReturnType } from './code-parser' +import { CodeLanguage } from './types' +import useConfig from './use-config' + const i18nPrefix = 'workflow.nodes.code' const codeLanguages = [ @@ -65,17 +66,19 @@ const Panel: FC<NodePanelProps<CodeNodeType>> = ({ } return ( - <div className='mt-2'> - <div className='space-y-4 px-4 pb-4'> + <div className="mt-2"> + <div className="space-y-4 px-4 pb-4"> <Field title={t(`${i18nPrefix}.inputVars`)} operations={ - !readOnly ? ( - <div className="flex gap-2"> - <SyncButton popupContent={t(`${i18nPrefix}.syncFunctionSignature`)} onClick={handleSyncFunctionSignature} /> - <AddButton onClick={handleAddVariable} /> - </div> - ) : undefined + !readOnly + ? ( + <div className="flex gap-2"> + <SyncButton popupContent={t(`${i18nPrefix}.syncFunctionSignature`)} onClick={handleSyncFunctionSignature} /> + <AddButton onClick={handleAddVariable} /> + </div> + ) + : undefined } > <VarList @@ -92,13 +95,13 @@ const Panel: FC<NodePanelProps<CodeNodeType>> = ({ nodeId={id} isInNode readOnly={readOnly} - title={ + title={( <TypeSelector options={codeLanguages} value={inputs.code_language} onChange={handleCodeLanguageChange} /> - } + )} language={inputs.code_language} value={inputs.code} onChange={handleCodeChange} @@ -107,7 +110,7 @@ const Panel: FC<NodePanelProps<CodeNodeType>> = ({ /> </div> <Split /> - <div className='px-4 pb-2 pt-4'> + <div className="px-4 pb-2 pt-4"> <Field title={t(`${i18nPrefix}.outputVars`)} operations={ @@ -129,7 +132,7 @@ const Panel: FC<NodePanelProps<CodeNodeType>> = ({ onCancel={hideRemoveVarConfirm} onConfirm={onRemoveVarConfirm} /> - </div > + </div> ) } diff --git a/web/app/components/workflow/nodes/code/types.ts b/web/app/components/workflow/nodes/code/types.ts index 265fd9d25d..ab85fccc86 100644 --- a/web/app/components/workflow/nodes/code/types.ts +++ b/web/app/components/workflow/nodes/code/types.ts @@ -1,4 +1,4 @@ -import type { CommonNodeType, VarType, Variable } from '@/app/components/workflow/types' +import type { CommonNodeType, Variable, VarType } from '@/app/components/workflow/types' export enum CodeLanguage { python3 = 'python3', diff --git a/web/app/components/workflow/nodes/code/use-config.ts b/web/app/components/workflow/nodes/code/use-config.ts index 39137ada30..fdb1c8ce51 100644 --- a/web/app/components/workflow/nodes/code/use-config.ts +++ b/web/app/components/workflow/nodes/code/use-config.ts @@ -1,20 +1,20 @@ -import { useCallback, useEffect, useState } from 'react' -import { produce } from 'immer' -import useVarList from '../_base/hooks/use-var-list' -import useOutputVarList from '../_base/hooks/use-output-var-list' -import { BlockEnum, VarType } from '../../types' import type { Var, Variable } from '../../types' -import { useStore } from '../../store' import type { CodeNodeType, OutputVar } from './types' -import { CodeLanguage } from './types' +import { produce } from 'immer' +import { useCallback, useEffect, useState } from 'react' +import { + useNodesReadOnly, +} from '@/app/components/workflow/hooks' import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import { fetchNodeDefault, fetchPipelineNodeDefault, } from '@/service/workflow' -import { - useNodesReadOnly, -} from '@/app/components/workflow/hooks' +import { useStore } from '../../store' +import { BlockEnum, VarType } from '../../types' +import useOutputVarList from '../_base/hooks/use-output-var-list' +import useVarList from '../_base/hooks/use-var-list' +import { CodeLanguage } from './types' const useConfig = (id: string, payload: CodeNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() diff --git a/web/app/components/workflow/nodes/code/use-single-run-form-params.ts b/web/app/components/workflow/nodes/code/use-single-run-form-params.ts index cda882ac89..18f31f19c4 100644 --- a/web/app/components/workflow/nodes/code/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/code/use-single-run-form-params.ts @@ -1,12 +1,12 @@ import type { RefObject } from 'react' +import type { CodeNodeType } from './types' import type { InputVar, Variable } from '@/app/components/workflow/types' import { useCallback, useMemo } from 'react' import useNodeCrud from '../_base/hooks/use-node-crud' -import type { CodeNodeType } from './types' type Params = { - id: string, - payload: CodeNodeType, + id: string + payload: CodeNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] diff --git a/web/app/components/workflow/nodes/components.ts b/web/app/components/workflow/nodes/components.ts index d8da8b9dae..87c0066d15 100644 --- a/web/app/components/workflow/nodes/components.ts +++ b/web/app/components/workflow/nodes/components.ts @@ -1,53 +1,53 @@ import type { ComponentType } from 'react' import { BlockEnum } from '../types' -import StartNode from './start/node' -import StartPanel from './start/panel' -import EndNode from './end/node' -import EndPanel from './end/panel' -import AnswerNode from './answer/node' -import AnswerPanel from './answer/panel' -import LLMNode from './llm/node' -import LLMPanel from './llm/panel' -import KnowledgeRetrievalNode from './knowledge-retrieval/node' -import KnowledgeRetrievalPanel from './knowledge-retrieval/panel' -import QuestionClassifierNode from './question-classifier/node' -import QuestionClassifierPanel from './question-classifier/panel' -import IfElseNode from './if-else/node' -import IfElsePanel from './if-else/panel' -import CodeNode from './code/node' -import CodePanel from './code/panel' -import TemplateTransformNode from './template-transform/node' -import TemplateTransformPanel from './template-transform/panel' -import HttpNode from './http/node' -import HttpPanel from './http/panel' -import ToolNode from './tool/node' -import ToolPanel from './tool/panel' -import VariableAssignerNode from './variable-assigner/node' -import VariableAssignerPanel from './variable-assigner/panel' -import AssignerNode from './assigner/node' -import AssignerPanel from './assigner/panel' -import ParameterExtractorNode from './parameter-extractor/node' -import ParameterExtractorPanel from './parameter-extractor/panel' -import IterationNode from './iteration/node' -import IterationPanel from './iteration/panel' -import LoopNode from './loop/node' -import LoopPanel from './loop/panel' -import DocExtractorNode from './document-extractor/node' -import DocExtractorPanel from './document-extractor/panel' -import ListFilterNode from './list-operator/node' -import ListFilterPanel from './list-operator/panel' import AgentNode from './agent/node' import AgentPanel from './agent/panel' +import AnswerNode from './answer/node' +import AnswerPanel from './answer/panel' +import AssignerNode from './assigner/node' +import AssignerPanel from './assigner/panel' +import CodeNode from './code/node' +import CodePanel from './code/panel' import DataSourceNode from './data-source/node' import DataSourcePanel from './data-source/panel' +import DocExtractorNode from './document-extractor/node' +import DocExtractorPanel from './document-extractor/panel' +import EndNode from './end/node' +import EndPanel from './end/panel' +import HttpNode from './http/node' +import HttpPanel from './http/panel' +import IfElseNode from './if-else/node' +import IfElsePanel from './if-else/panel' +import IterationNode from './iteration/node' +import IterationPanel from './iteration/panel' import KnowledgeBaseNode from './knowledge-base/node' import KnowledgeBasePanel from './knowledge-base/panel' +import KnowledgeRetrievalNode from './knowledge-retrieval/node' +import KnowledgeRetrievalPanel from './knowledge-retrieval/panel' +import ListFilterNode from './list-operator/node' +import ListFilterPanel from './list-operator/panel' +import LLMNode from './llm/node' +import LLMPanel from './llm/panel' +import LoopNode from './loop/node' +import LoopPanel from './loop/panel' +import ParameterExtractorNode from './parameter-extractor/node' +import ParameterExtractorPanel from './parameter-extractor/panel' +import QuestionClassifierNode from './question-classifier/node' +import QuestionClassifierPanel from './question-classifier/panel' +import StartNode from './start/node' +import StartPanel from './start/panel' +import TemplateTransformNode from './template-transform/node' +import TemplateTransformPanel from './template-transform/panel' +import ToolNode from './tool/node' +import ToolPanel from './tool/panel' +import TriggerPluginNode from './trigger-plugin/node' +import TriggerPluginPanel from './trigger-plugin/panel' import TriggerScheduleNode from './trigger-schedule/node' import TriggerSchedulePanel from './trigger-schedule/panel' import TriggerWebhookNode from './trigger-webhook/node' import TriggerWebhookPanel from './trigger-webhook/panel' -import TriggerPluginNode from './trigger-plugin/node' -import TriggerPluginPanel from './trigger-plugin/panel' +import VariableAssignerNode from './variable-assigner/node' +import VariableAssignerPanel from './variable-assigner/panel' export const NodeComponentMap: Record<string, ComponentType<any>> = { [BlockEnum.Start]: StartNode, diff --git a/web/app/components/workflow/nodes/data-source-empty/default.ts b/web/app/components/workflow/nodes/data-source-empty/default.ts index 69d9904217..5ba1ca7543 100644 --- a/web/app/components/workflow/nodes/data-source-empty/default.ts +++ b/web/app/components/workflow/nodes/data-source-empty/default.ts @@ -1,7 +1,7 @@ import type { NodeDefault } from '../../types' import type { DataSourceEmptyNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' const metaData = genNodeMetaData({ sort: -1, diff --git a/web/app/components/workflow/nodes/data-source-empty/hooks.ts b/web/app/components/workflow/nodes/data-source-empty/hooks.ts index a17f0b2acb..93191b0261 100644 --- a/web/app/components/workflow/nodes/data-source-empty/hooks.ts +++ b/web/app/components/workflow/nodes/data-source-empty/hooks.ts @@ -1,9 +1,9 @@ +import type { OnSelectBlock } from '@/app/components/workflow/types' +import { produce } from 'immer' import { useCallback } from 'react' import { useStoreApi } from 'reactflow' -import { produce } from 'immer' -import type { OnSelectBlock } from '@/app/components/workflow/types' -import { generateNewNode } from '@/app/components/workflow/utils' import { useNodesMetaData } from '@/app/components/workflow/hooks' +import { generateNewNode } from '@/app/components/workflow/utils' export const useReplaceDataSourceNode = (id: string) => { const store = useStoreApi() @@ -20,7 +20,8 @@ export const useReplaceDataSourceNode = (id: string) => { const nodes = getNodes() const emptyNodeIndex = nodes.findIndex(node => node.id === id) - if (emptyNodeIndex < 0) return + if (emptyNodeIndex < 0) + return const { defaultValue, } = nodesMetaDataMap![type] diff --git a/web/app/components/workflow/nodes/data-source-empty/index.tsx b/web/app/components/workflow/nodes/data-source-empty/index.tsx index b85cb94e95..378b9c80c4 100644 --- a/web/app/components/workflow/nodes/data-source-empty/index.tsx +++ b/web/app/components/workflow/nodes/data-source-empty/index.tsx @@ -1,13 +1,13 @@ +import type { NodeProps } from 'reactflow' +import { RiAddLine } from '@remixicon/react' import { memo, useCallback, } from 'react' import { useTranslation } from 'react-i18next' -import type { NodeProps } from 'reactflow' -import { RiAddLine } from '@remixicon/react' -import { cn } from '@/utils/classnames' import Button from '@/app/components/base/button' import BlockSelector from '@/app/components/workflow/block-selector' +import { cn } from '@/utils/classnames' import { useReplaceDataSourceNode } from './hooks' const DataSourceEmptyNode = ({ id, data }: NodeProps) => { @@ -17,10 +17,10 @@ const DataSourceEmptyNode = ({ id, data }: NodeProps) => { const renderTrigger = useCallback(() => { return ( <Button - variant='primary' - className='w-full' + variant="primary" + className="w-full" > - <RiAddLine className='mr-1 h-4 w-4' /> + <RiAddLine className="mr-1 h-4 w-4" /> {t('workflow.nodes.dataSource.add')} </Button> ) @@ -37,8 +37,8 @@ const DataSourceEmptyNode = ({ id, data }: NodeProps) => { height: data.height, }} > - <div className='absolute inset-[-2px] top-[-22px] z-[-1] rounded-[18px] bg-node-data-source-bg p-0.5 backdrop-blur-[6px]'> - <div className='system-2xs-semibold-uppercase flex h-5 items-center px-2.5 text-text-tertiary'> + <div className="absolute inset-[-2px] top-[-22px] z-[-1] rounded-[18px] bg-node-data-source-bg p-0.5 backdrop-blur-[6px]"> + <div className="system-2xs-semibold-uppercase flex h-5 items-center px-2.5 text-text-tertiary"> {t('workflow.blocks.datasource')} </div> </div> @@ -51,15 +51,16 @@ const DataSourceEmptyNode = ({ id, data }: NodeProps) => { > <div className={cn( 'flex items-center rounded-t-2xl p-3', - )}> + )} + > <BlockSelector asChild onSelect={handleReplaceNode} trigger={renderTrigger} noBlocks noTools - popupClassName='w-[320px]' - placement='bottom-start' + popupClassName="w-[320px]" + placement="bottom-start" offset={{ mainAxis: 4, crossAxis: 0, diff --git a/web/app/components/workflow/nodes/data-source/before-run-form.tsx b/web/app/components/workflow/nodes/data-source/before-run-form.tsx index 521fdfb087..a091211fa5 100644 --- a/web/app/components/workflow/nodes/data-source/before-run-form.tsx +++ b/web/app/components/workflow/nodes/data-source/before-run-form.tsx @@ -1,17 +1,17 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' import type { CustomRunFormProps } from './types' -import { DatasourceType } from '@/models/pipeline' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' import LocalFile from '@/app/components/datasets/documents/create-from-pipeline/data-source/local-file' import OnlineDocuments from '@/app/components/datasets/documents/create-from-pipeline/data-source/online-documents' -import WebsiteCrawl from '@/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl' import OnlineDrive from '@/app/components/datasets/documents/create-from-pipeline/data-source/online-drive' import { useDataSourceStore } from '@/app/components/datasets/documents/create-from-pipeline/data-source/store' -import { useOnlineDocument, useOnlineDrive, useWebsiteCrawl } from '@/app/components/rag-pipeline/components/panel/test-run/preparation/hooks' -import Button from '@/app/components/base/button' -import { useTranslation } from 'react-i18next' import DataSourceProvider from '@/app/components/datasets/documents/create-from-pipeline/data-source/store/provider' +import WebsiteCrawl from '@/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl' +import { useOnlineDocument, useOnlineDrive, useWebsiteCrawl } from '@/app/components/rag-pipeline/components/panel/test-run/preparation/hooks' +import { DatasourceType } from '@/models/pipeline' import PanelWrap from '../_base/components/before-run-form/panel-wrap' import useBeforeRunForm from './hooks/use-before-run-form' @@ -56,7 +56,7 @@ const BeforeRunForm: FC<CustomRunFormProps> = (props) => { nodeName={payload.title} onHide={onCancel} > - <div className='flex flex-col gap-y-5 px-4 pt-4'> + <div className="flex flex-col gap-y-5 px-4 pt-4"> {datasourceType === DatasourceType.localFile && ( <LocalFile allowedExtensions={datasourceNodeData.fileExtensions || []} @@ -90,13 +90,13 @@ const BeforeRunForm: FC<CustomRunFormProps> = (props) => { supportBatchUpload={false} /> )} - <div className='flex justify-end gap-x-2'> + <div className="flex justify-end gap-x-2"> <Button onClick={onCancel}> {t('common.operation.cancel')} </Button> <Button onClick={handleRunWithSyncDraft} - variant='primary' + variant="primary" loading={isPending} disabled={isPending || startRunBtnDisabled} > diff --git a/web/app/components/workflow/nodes/data-source/default.ts b/web/app/components/workflow/nodes/data-source/default.ts index 82e69679a2..565bfb0d24 100644 --- a/web/app/components/workflow/nodes/data-source/default.ts +++ b/web/app/components/workflow/nodes/data-source/default.ts @@ -1,14 +1,14 @@ import type { NodeDefault } from '../../types' import type { DataSourceNodeType } from './types' -import { DataSourceClassification } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' +import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' +import { getMatchedSchemaType } from '../_base/components/variable/use-match-schema-type' import { COMMON_OUTPUT, LOCAL_FILE_OUTPUT, } from './constants' -import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' -import { getMatchedSchemaType } from '../_base/components/variable/use-match-schema-type' +import { DataSourceClassification } from './types' const i18nPrefix = 'workflow.errorMsg' @@ -86,12 +86,14 @@ const nodeDefault: NodeDefault<DataSourceNodeType> = { type, description: output.description, schemaType, - children: output.type === 'object' ? { - schema: { - type: 'object', - properties: output.properties, - }, - } : undefined, + children: output.type === 'object' + ? { + schema: { + type: 'object', + properties: output.properties, + }, + } + : undefined, }) }) } diff --git a/web/app/components/workflow/nodes/data-source/hooks/use-before-run-form.ts b/web/app/components/workflow/nodes/data-source/hooks/use-before-run-form.ts index 1e42d032e8..004da29bf5 100644 --- a/web/app/components/workflow/nodes/data-source/hooks/use-before-run-form.ts +++ b/web/app/components/workflow/nodes/data-source/hooks/use-before-run-form.ts @@ -1,17 +1,17 @@ -import { useStoreApi } from 'reactflow' import type { CustomRunFormProps, DataSourceNodeType } from '../types' -import { useEffect, useMemo, useRef } from 'react' -import { useNodeDataUpdate, useNodesSyncDraft } from '../../../hooks' -import { NodeRunningStatus } from '../../../types' -import { useInvalidLastRun } from '@/service/use-workflow' import type { NodeRunResult } from '@/types/workflow' -import { fetchNodeInspectVars } from '@/service/workflow' -import { FlowType } from '@/types/common' -import { useDatasourceSingleRun } from '@/service/use-pipeline' +import { useEffect, useMemo, useRef } from 'react' +import { useStoreApi } from 'reactflow' +import { useShallow } from 'zustand/react/shallow' import { useDataSourceStore, useDataSourceStoreWithSelector } from '@/app/components/datasets/documents/create-from-pipeline/data-source/store' import { DatasourceType } from '@/models/pipeline' +import { useDatasourceSingleRun } from '@/service/use-pipeline' +import { useInvalidLastRun } from '@/service/use-workflow' +import { fetchNodeInspectVars } from '@/service/workflow' import { TransferMethod } from '@/types/app' -import { useShallow } from 'zustand/react/shallow' +import { FlowType } from '@/types/common' +import { useNodeDataUpdate, useNodesSyncDraft } from '../../../hooks' +import { NodeRunningStatus } from '../../../types' const useBeforeRunForm = ({ nodeId, @@ -46,7 +46,8 @@ const useBeforeRunForm = ({ }))) const startRunBtnDisabled = useMemo(() => { - if (!datasourceNodeData) return false + if (!datasourceNodeData) + return false if (datasourceType === DatasourceType.localFile) return !localFileList.length || localFileList.some(file => !file.file.id) if (datasourceType === DatasourceType.onlineDocument) diff --git a/web/app/components/workflow/nodes/data-source/hooks/use-config.ts b/web/app/components/workflow/nodes/data-source/hooks/use-config.ts index 98683e70e7..08f66e4089 100644 --- a/web/app/components/workflow/nodes/data-source/hooks/use-config.ts +++ b/web/app/components/workflow/nodes/data-source/hooks/use-config.ts @@ -1,3 +1,7 @@ +import type { + DataSourceNodeType, + ToolVarInputs, +} from '../types' import { useCallback, useEffect, @@ -5,10 +9,6 @@ import { } from 'react' import { useStoreApi } from 'reactflow' import { useNodeDataUpdate } from '@/app/components/workflow/hooks' -import type { - DataSourceNodeType, - ToolVarInputs, -} from '../types' export const useConfig = (id: string, dataSourceList?: any[]) => { const store = useStoreApi() @@ -61,7 +61,8 @@ export const useConfig = (id: string, dataSourceList?: any[]) => { const outputSchema = useMemo(() => { const nodeData = getNodeData() - if (!nodeData?.data || !dataSourceList) return [] + if (!nodeData?.data || !dataSourceList) + return [] const currentDataSource = dataSourceList.find((ds: any) => ds.plugin_id === nodeData.data.plugin_id) const currentDataSourceItem = currentDataSource?.tools?.find((tool: any) => tool.name === nodeData.data.datasource_name) @@ -95,7 +96,8 @@ export const useConfig = (id: string, dataSourceList?: any[]) => { const hasObjectOutput = useMemo(() => { const nodeData = getNodeData() - if (!nodeData?.data || !dataSourceList) return false + if (!nodeData?.data || !dataSourceList) + return false const currentDataSource = dataSourceList.find((ds: any) => ds.plugin_id === nodeData.data.plugin_id) const currentDataSourceItem = currentDataSource?.tools?.find((tool: any) => tool.name === nodeData.data.datasource_name) diff --git a/web/app/components/workflow/nodes/data-source/node.tsx b/web/app/components/workflow/nodes/data-source/node.tsx index b490aea2a9..be8a2c50b2 100644 --- a/web/app/components/workflow/nodes/data-source/node.tsx +++ b/web/app/components/workflow/nodes/data-source/node.tsx @@ -1,10 +1,10 @@ import type { FC } from 'react' -import { memo, useEffect } from 'react' -import type { NodeProps } from '@/app/components/workflow/types' -import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button' -import { useNodePluginInstallation } from '@/app/components/workflow/hooks/use-node-plugin-installation' -import { useNodeDataUpdate } from '@/app/components/workflow/hooks/use-node-data-update' import type { DataSourceNodeType } from './types' +import type { NodeProps } from '@/app/components/workflow/types' +import { memo, useEffect } from 'react' +import { useNodeDataUpdate } from '@/app/components/workflow/hooks/use-node-data-update' +import { useNodePluginInstallation } from '@/app/components/workflow/hooks/use-node-plugin-installation' +import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button' const Node: FC<NodeProps<DataSourceNodeType>> = ({ id, @@ -39,15 +39,15 @@ const Node: FC<NodeProps<DataSourceNodeType>> = ({ return null return ( - <div className='relative mb-1 px-3 py-1'> - <div className='pointer-events-auto absolute right-3 top-[-32px] z-40'> + <div className="relative mb-1 px-3 py-1"> + <div className="pointer-events-auto absolute right-3 top-[-32px] z-40"> <InstallPluginButton - size='small' + size="small" extraIdentifiers={[ data.plugin_id, data.provider_name, ].filter(Boolean) as string[]} - className='!font-medium !text-text-accent' + className="!font-medium !text-text-accent" uniqueIdentifier={uniqueIdentifier!} onSuccess={onInstallSuccess} /> diff --git a/web/app/components/workflow/nodes/data-source/panel.tsx b/web/app/components/workflow/nodes/data-source/panel.tsx index cbb506964b..607caa79d3 100644 --- a/web/app/components/workflow/nodes/data-source/panel.tsx +++ b/web/app/components/workflow/nodes/data-source/panel.tsx @@ -1,29 +1,29 @@ import type { FC } from 'react' +import type { DataSourceNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import { + memo, useMemo, } from 'react' import { useTranslation } from 'react-i18next' -import { memo } from 'react' -import type { DataSourceNodeType } from './types' -import { DataSourceClassification } from './types' -import type { NodePanelProps } from '@/app/components/workflow/types' +import TagInput from '@/app/components/base/tag-input' +import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema' +import { useNodesReadOnly } from '@/app/components/workflow/hooks' import { BoxGroupField, } from '@/app/components/workflow/nodes/_base/components/layout' import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' import StructureOutputItem from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show' -import TagInput from '@/app/components/base/tag-input' -import { useNodesReadOnly } from '@/app/components/workflow/hooks' -import { useConfig } from './hooks/use-config' +import { useStore } from '@/app/components/workflow/store' +import { wrapStructuredVarItem } from '@/app/components/workflow/utils/tool' +import useMatchSchemaType, { getMatchedSchemaType } from '../_base/components/variable/use-match-schema-type' +import ToolForm from '../tool/components/tool-form' import { COMMON_OUTPUT, LOCAL_FILE_OUTPUT, } from './constants' -import { useStore } from '@/app/components/workflow/store' -import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema' -import ToolForm from '../tool/components/tool-form' -import { wrapStructuredVarItem } from '@/app/components/workflow/utils/tool' -import useMatchSchemaType, { getMatchedSchemaType } from '../_base/components/variable/use-match-schema-type' +import { useConfig } from './hooks/use-config' +import { DataSourceClassification } from './types' const Panel: FC<NodePanelProps<DataSourceNodeType>> = ({ id, data }) => { const { t } = useTranslation() @@ -52,7 +52,7 @@ const Panel: FC<NodePanelProps<DataSourceNodeType>> = ({ id, data }) => { const setShowInputFieldPanel = useStore(s => s.setShowInputFieldPanel) const { schemaTypeDefinitions } = useMatchSchemaType() return ( - <div > + <div> { currentDataSource?.is_authorized && !isLocalFile && !!formSchemas?.length && ( <BoxGroupField @@ -94,12 +94,12 @@ const Panel: FC<NodePanelProps<DataSourceNodeType>> = ({ id, data }) => { }, }} > - <div className='rounded-lg bg-components-input-bg-normal p-1 pt-0'> + <div className="rounded-lg bg-components-input-bg-normal p-1 pt-0"> <TagInput items={fileExtensions} onChange={handleFileExtensionsChange} placeholder={t('workflow.nodes.dataSource.supportedFileFormatsPlaceholder')} - inputClassName='bg-transparent' + inputClassName="bg-transparent" disableAdd={nodesReadOnly} disableRemove={nodesReadOnly} /> @@ -141,7 +141,7 @@ const Panel: FC<NodePanelProps<DataSourceNodeType>> = ({ id, data }) => { <div key={outputItem.name}> {outputItem.value?.type === 'object' ? ( <StructureOutputItem - rootClassName='code-sm-semibold text-text-secondary' + rootClassName="code-sm-semibold text-text-secondary" payload={wrapStructuredVarItem(outputItem, schemaType)} /> ) : ( diff --git a/web/app/components/workflow/nodes/data-source/types.ts b/web/app/components/workflow/nodes/data-source/types.ts index d0bc034b89..19b4bada3f 100644 --- a/web/app/components/workflow/nodes/data-source/types.ts +++ b/web/app/components/workflow/nodes/data-source/types.ts @@ -1,8 +1,9 @@ +import type { Dispatch, SetStateAction } from 'react' +import type { ResourceVarInputs } from '../_base/types' import type { CommonNodeType, Node } from '@/app/components/workflow/types' import type { FlowType } from '@/types/common' import type { NodeRunResult, VarInInspect } from '@/types/workflow' -import type { Dispatch, SetStateAction } from 'react' -import type { ResourceVarInputs } from '../_base/types' + export { VarKindType as VarType } from '../_base/types' export enum DataSourceClassification { diff --git a/web/app/components/workflow/nodes/data-source/utils.ts b/web/app/components/workflow/nodes/data-source/utils.ts index fbda69f3fd..94c1f81ec8 100644 --- a/web/app/components/workflow/nodes/data-source/utils.ts +++ b/web/app/components/workflow/nodes/data-source/utils.ts @@ -1,5 +1,5 @@ -import { PipelineInputVarType } from '@/models/pipeline' import { VarType } from '@/app/components/workflow/types' +import { PipelineInputVarType } from '@/models/pipeline' export const inputVarTypeToVarType = (type: PipelineInputVarType): VarType => { return ({ diff --git a/web/app/components/workflow/nodes/document-extractor/default.ts b/web/app/components/workflow/nodes/document-extractor/default.ts index 77a847b5fd..73ed069b7d 100644 --- a/web/app/components/workflow/nodes/document-extractor/default.ts +++ b/web/app/components/workflow/nodes/document-extractor/default.ts @@ -1,8 +1,9 @@ import type { NodeDefault } from '../../types' import type { DocExtractorNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' + const i18nPrefix = 'workflow.errorMsg' const metaData = genNodeMetaData({ diff --git a/web/app/components/workflow/nodes/document-extractor/node.tsx b/web/app/components/workflow/nodes/document-extractor/node.tsx index a0437a4f54..c092cd353a 100644 --- a/web/app/components/workflow/nodes/document-extractor/node.tsx +++ b/web/app/components/workflow/nodes/document-extractor/node.tsx @@ -1,13 +1,14 @@ import type { FC } from 'react' -import React from 'react' -import { useNodes } from 'reactflow' -import { useTranslation } from 'react-i18next' import type { DocExtractorNodeType } from './types' +import type { Node, NodeProps } from '@/app/components/workflow/types' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { useNodes } from 'reactflow' import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' -import { BlockEnum, type Node, type NodeProps } from '@/app/components/workflow/types' import { VariableLabelInNode, } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' +import { BlockEnum } from '@/app/components/workflow/types' const i18nPrefix = 'workflow.nodes.docExtractor' @@ -25,8 +26,8 @@ const NodeComponent: FC<NodeProps<DocExtractorNodeType>> = ({ const isSystem = isSystemVar(variable) const node = isSystem ? nodes.find(node => node.data.type === BlockEnum.Start) : nodes.find(node => node.id === variable[0]) return ( - <div className='relative mb-1 px-3 py-1'> - <div className='system-2xs-medium-uppercase mb-1 text-text-tertiary'>{t(`${i18nPrefix}.inputVar`)}</div> + <div className="relative mb-1 px-3 py-1"> + <div className="system-2xs-medium-uppercase mb-1 text-text-tertiary">{t(`${i18nPrefix}.inputVar`)}</div> <VariableLabelInNode variables={variable} nodeType={node?.data.type} diff --git a/web/app/components/workflow/nodes/document-extractor/panel.tsx b/web/app/components/workflow/nodes/document-extractor/panel.tsx index 7165dc06df..b7cfddea4b 100644 --- a/web/app/components/workflow/nodes/document-extractor/panel.tsx +++ b/web/app/components/workflow/nodes/document-extractor/panel.tsx @@ -1,18 +1,19 @@ import type { FC } from 'react' +import type { DocExtractorNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' -import VarReferencePicker from '../_base/components/variable/var-reference-picker' -import OutputVars, { VarItem } from '../_base/components/output-vars' -import Split from '../_base/components/split' -import { useNodeHelpLink } from '../_base/hooks/use-node-help-link' -import useConfig from './use-config' -import type { DocExtractorNodeType } from './types' import Field from '@/app/components/workflow/nodes/_base/components/field' -import { BlockEnum, type NodePanelProps } from '@/app/components/workflow/types' +import { BlockEnum } from '@/app/components/workflow/types' import I18n from '@/context/i18n' import { LanguagesSupported } from '@/i18n-config/language' import { useFileSupportTypes } from '@/service/use-common' +import OutputVars, { VarItem } from '../_base/components/output-vars' +import Split from '../_base/components/split' +import VarReferencePicker from '../_base/components/variable/var-reference-picker' +import { useNodeHelpLink } from '../_base/hooks/use-node-help-link' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.docExtractor' @@ -48,8 +49,8 @@ const Panel: FC<NodePanelProps<DocExtractorNodeType>> = ({ } = useConfig(id, data) return ( - <div className='mt-2'> - <div className='space-y-4 px-4 pb-4'> + <div className="mt-2"> + <div className="space-y-4 px-4 pb-4"> <Field title={t(`${i18nPrefix}.inputVar`)} required @@ -62,11 +63,11 @@ const Panel: FC<NodePanelProps<DocExtractorNodeType>> = ({ value={inputs.variable_selector || []} onChange={handleVarChanges} filterVar={filterVar} - typePlaceHolder='File | Array[File]' + typePlaceHolder="File | Array[File]" /> - <div className='body-xs-regular mt-1 py-0.5 text-text-tertiary'> + <div className="body-xs-regular mt-1 py-0.5 text-text-tertiary"> {t(`${i18nPrefix}.supportFileTypes`, { types: supportTypesShowNames })} - <a className='text-text-accent' href={link} target='_blank'>{t(`${i18nPrefix}.learnMore`)}</a> + <a className="text-text-accent" href={link} target="_blank">{t(`${i18nPrefix}.learnMore`)}</a> </div> </> </Field> @@ -75,7 +76,7 @@ const Panel: FC<NodePanelProps<DocExtractorNodeType>> = ({ <div> <OutputVars> <VarItem - name='text' + name="text" type={inputs.is_array_file ? 'array[string]' : 'string'} description={t(`${i18nPrefix}.outputVars.text`)} /> diff --git a/web/app/components/workflow/nodes/document-extractor/use-config.ts b/web/app/components/workflow/nodes/document-extractor/use-config.ts index 53393aa030..b5ff863821 100644 --- a/web/app/components/workflow/nodes/document-extractor/use-config.ts +++ b/web/app/components/workflow/nodes/document-extractor/use-config.ts @@ -1,16 +1,16 @@ -import { useCallback, useMemo } from 'react' -import { produce } from 'immer' -import { useStoreApi } from 'reactflow' import type { ValueSelector, Var } from '../../types' -import { VarType } from '../../types' import type { DocExtractorNodeType } from './types' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { produce } from 'immer' +import { useCallback, useMemo } from 'react' +import { useStoreApi } from 'reactflow' import { useIsChatMode, useNodesReadOnly, useWorkflow, useWorkflowVariables, } from '@/app/components/workflow/hooks' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { VarType } from '../../types' const useConfig = (id: string, payload: DocExtractorNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() diff --git a/web/app/components/workflow/nodes/document-extractor/use-single-run-form-params.ts b/web/app/components/workflow/nodes/document-extractor/use-single-run-form-params.ts index f60f1cbd77..afd1bb17f4 100644 --- a/web/app/components/workflow/nodes/document-extractor/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/document-extractor/use-single-run-form-params.ts @@ -1,15 +1,15 @@ import type { RefObject } from 'react' +import type { DocExtractorNodeType } from './types' import type { InputVar, Variable } from '@/app/components/workflow/types' import { useCallback, useMemo } from 'react' -import type { DocExtractorNodeType } from './types' import { useTranslation } from 'react-i18next' import { InputVarType } from '@/app/components/workflow/types' const i18nPrefix = 'workflow.nodes.docExtractor' type Params = { - id: string, - payload: DocExtractorNodeType, + id: string + payload: DocExtractorNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] @@ -50,7 +50,7 @@ const useSingleRunFormParams = ({ } const getDependentVar = (variable: string) => { - if(variable === 'files') + if (variable === 'files') return payload.variable_selector } diff --git a/web/app/components/workflow/nodes/end/default.ts b/web/app/components/workflow/nodes/end/default.ts index 881c16986b..eee07cb0ff 100644 --- a/web/app/components/workflow/nodes/end/default.ts +++ b/web/app/components/workflow/nodes/end/default.ts @@ -1,7 +1,7 @@ import type { NodeDefault } from '../../types' import type { EndNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' const metaData = genNodeMetaData({ sort: 2.1, diff --git a/web/app/components/workflow/nodes/end/node.tsx b/web/app/components/workflow/nodes/end/node.tsx index 2583e61b68..26e7b7d022 100644 --- a/web/app/components/workflow/nodes/end/node.tsx +++ b/web/app/components/workflow/nodes/end/node.tsx @@ -1,16 +1,16 @@ import type { FC } from 'react' -import React from 'react' import type { EndNodeType } from './types' import type { NodeProps, Variable } from '@/app/components/workflow/types' +import React from 'react' import { useIsChatMode, useWorkflow, useWorkflowVariables, } from '@/app/components/workflow/hooks' -import { BlockEnum } from '@/app/components/workflow/types' import { VariableLabelInNode, } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' +import { BlockEnum } from '@/app/components/workflow/types' const Node: FC<NodeProps<EndNodeType>> = ({ id, @@ -36,7 +36,7 @@ const Node: FC<NodeProps<EndNodeType>> = ({ return null return ( - <div className='mb-1 space-y-0.5 px-3 py-1'> + <div className="mb-1 space-y-0.5 px-3 py-1"> {filteredOutputs.map(({ value_selector }, index) => { const node = getNode(value_selector[0]) const varType = getCurrentVariableType({ diff --git a/web/app/components/workflow/nodes/end/panel.tsx b/web/app/components/workflow/nodes/end/panel.tsx index 420280d7c5..3970dc8efe 100644 --- a/web/app/components/workflow/nodes/end/panel.tsx +++ b/web/app/components/workflow/nodes/end/panel.tsx @@ -1,12 +1,12 @@ import type { FC } from 'react' +import type { EndNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import useConfig from './use-config' -import type { EndNodeType } from './types' -import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list' -import Field from '@/app/components/workflow/nodes/_base/components/field' import AddButton from '@/app/components/base/button/add-button' -import type { NodePanelProps } from '@/app/components/workflow/types' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.end' @@ -25,8 +25,8 @@ const Panel: FC<NodePanelProps<EndNodeType>> = ({ const outputs = inputs.outputs return ( - <div className='mt-2'> - <div className='space-y-4 px-4 pb-4'> + <div className="mt-2"> + <div className="space-y-4 px-4 pb-4"> <Field title={t(`${i18nPrefix}.output.variable`)} diff --git a/web/app/components/workflow/nodes/end/use-config.ts b/web/app/components/workflow/nodes/end/use-config.ts index b9876f95ed..251b47c821 100644 --- a/web/app/components/workflow/nodes/end/use-config.ts +++ b/web/app/components/workflow/nodes/end/use-config.ts @@ -1,9 +1,10 @@ -import useVarList from '../_base/hooks/use-var-list' import type { EndNodeType } from './types' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import { useNodesReadOnly, } from '@/app/components/workflow/hooks' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import useVarList from '../_base/hooks/use-var-list' + const useConfig = (id: string, payload: EndNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() const { inputs, setInputs } = useNodeCrud<EndNodeType>(id, payload) diff --git a/web/app/components/workflow/nodes/http/components/api-input.tsx b/web/app/components/workflow/nodes/http/components/api-input.tsx index 62ce0f15c6..a72fc9fde0 100644 --- a/web/app/components/workflow/nodes/http/components/api-input.tsx +++ b/web/app/components/workflow/nodes/http/components/api-input.tsx @@ -1,15 +1,15 @@ 'use client' import type { FC } from 'react' +import type { Var } from '../../../types' +import { RiArrowDownSLine } from '@remixicon/react' import React, { useState } from 'react' import { useTranslation } from 'react-i18next' -import { RiArrowDownSLine } from '@remixicon/react' -import { Method } from '../types' +import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' +import { cn } from '@/utils/classnames' +import { VarType } from '../../../types' import Selector from '../../_base/components/selector' import useAvailableVarList from '../../_base/hooks/use-available-var-list' -import { VarType } from '../../../types' -import type { Var } from '../../../types' -import { cn } from '@/utils/classnames' -import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' +import { Method } from '../types' const MethodOptions = [ { label: 'GET', value: Method.get }, @@ -47,24 +47,24 @@ const ApiInput: FC<Props> = ({ }) return ( - <div className='flex items-start space-x-1'> + <div className="flex items-start space-x-1"> <Selector value={method} onChange={onMethodChange} options={MethodOptions} - trigger={ - <div className={cn(readonly && 'cursor-pointer', 'flex h-8 shrink-0 items-center rounded-lg border border-components-button-secondary-border bg-components-button-secondary-bg px-2.5')} > - <div className='w-12 pl-0.5 text-xs font-medium uppercase leading-[18px] text-text-primary'>{method}</div> - {!readonly && <RiArrowDownSLine className='ml-1 h-3.5 w-3.5 text-text-secondary' />} + trigger={( + <div className={cn(readonly && 'cursor-pointer', 'flex h-8 shrink-0 items-center rounded-lg border border-components-button-secondary-border bg-components-button-secondary-bg px-2.5')}> + <div className="w-12 pl-0.5 text-xs font-medium uppercase leading-[18px] text-text-primary">{method}</div> + {!readonly && <RiArrowDownSLine className="ml-1 h-3.5 w-3.5 text-text-secondary" />} </div> - } - popupClassName='top-[34px] w-[108px]' + )} + popupClassName="top-[34px] w-[108px]" showChecked readonly={readonly} /> <Input - instanceId='http-api-url' + instanceId="http-api-url" className={cn(isFocus ? 'border-components-input-border-active bg-components-input-bg-active shadow-xs' : 'border-components-input-border-hover bg-components-input-bg-normal', 'w-0 grow rounded-lg border px-3 py-[6px]')} value={url} onChange={onUrlChange} @@ -73,9 +73,9 @@ const ApiInput: FC<Props> = ({ availableNodes={availableNodesWithParent} onFocusChange={setIsFocus} placeholder={!readonly ? t('workflow.nodes.http.apiPlaceholder')! : ''} - placeholderClassName='!leading-[21px]' + placeholderClassName="!leading-[21px]" /> - </div > + </div> ) } export default React.memo(ApiInput) diff --git a/web/app/components/workflow/nodes/http/components/authorization/index.tsx b/web/app/components/workflow/nodes/http/components/authorization/index.tsx index 7fd811dfbc..50505fd4c8 100644 --- a/web/app/components/workflow/nodes/http/components/authorization/index.tsx +++ b/web/app/components/workflow/nodes/http/components/authorization/index.tsx @@ -1,19 +1,19 @@ 'use client' import type { FC } from 'react' -import { useTranslation } from 'react-i18next' -import React, { useCallback, useState } from 'react' -import { produce } from 'immer' import type { Authorization as AuthorizationPayloadType } from '../../types' -import { APIType, AuthorizationType } from '../../types' -import RadioGroup from './radio-group' +import type { Var } from '@/app/components/workflow/types' +import { produce } from 'immer' +import React, { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' +import BaseInput from '@/app/components/base/input' +import Modal from '@/app/components/base/modal' +import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' import { VarType } from '@/app/components/workflow/types' -import type { Var } from '@/app/components/workflow/types' -import Modal from '@/app/components/base/modal' -import Button from '@/app/components/base/button' -import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' -import BaseInput from '@/app/components/base/input' import { cn } from '@/utils/classnames' +import { APIType, AuthorizationType } from '../../types' +import RadioGroup from './radio-group' const i18nPrefix = 'workflow.nodes.http.authorization' @@ -25,12 +25,12 @@ type Props = { onHide: () => void } -const Field = ({ title, isRequired, children }: { title: string; isRequired?: boolean; children: React.JSX.Element }) => { +const Field = ({ title, isRequired, children }: { title: string, isRequired?: boolean, children: React.JSX.Element }) => { return ( <div> - <div className='text-[13px] font-medium leading-8 text-text-secondary'> + <div className="text-[13px] font-medium leading-8 text-text-secondary"> {title} - {isRequired && <span className='ml-0.5 text-text-destructive'>*</span>} + {isRequired && <span className="ml-0.5 text-text-destructive">*</span>} </div> <div>{children}</div> </div> @@ -120,7 +120,7 @@ const Authorization: FC<Props> = ({ onClose={onHide} > <div> - <div className='space-y-2'> + <div className="space-y-2"> <Field title={t(`${i18nPrefix}.authorizationType`)}> <RadioGroup options={[ @@ -155,9 +155,9 @@ const Authorization: FC<Props> = ({ )} <Field title={t(`${i18nPrefix}.api-key-title`)} isRequired> - <div className='flex'> + <div className="flex"> <Input - instanceId='http-api-key' + instanceId="http-api-key" className={cn(isFocus ? 'border-components-input-border-active bg-components-input-bg-active shadow-xs' : 'border-components-input-border-hover bg-components-input-bg-normal', 'w-0 grow rounded-lg border px-3 py-[6px]')} value={tempPayload.config?.api_key || ''} onChange={handleAPIKeyChange} @@ -165,16 +165,16 @@ const Authorization: FC<Props> = ({ availableNodes={availableNodesWithParent} onFocusChange={setIsFocus} placeholder={' '} - placeholderClassName='!leading-[21px]' + placeholderClassName="!leading-[21px]" /> </div> </Field> </> )} </div> - <div className='mt-6 flex justify-end space-x-2'> + <div className="mt-6 flex justify-end space-x-2"> <Button onClick={onHide}>{t('common.operation.cancel')}</Button> - <Button variant='primary' onClick={handleConfirm}>{t('common.operation.save')}</Button> + <Button variant="primary" onClick={handleConfirm}>{t('common.operation.save')}</Button> </div> </div> </Modal> diff --git a/web/app/components/workflow/nodes/http/components/authorization/radio-group.tsx b/web/app/components/workflow/nodes/http/components/authorization/radio-group.tsx index d5d12d7f34..6edd325b18 100644 --- a/web/app/components/workflow/nodes/http/components/authorization/radio-group.tsx +++ b/web/app/components/workflow/nodes/http/components/authorization/radio-group.tsx @@ -47,7 +47,7 @@ const RadioGroup: FC<Props> = ({ return () => onChange(value) }, [onChange]) return ( - <div className='flex space-x-2'> + <div className="flex space-x-2"> {options.map(option => ( <Item key={option.value} diff --git a/web/app/components/workflow/nodes/http/components/curl-panel.tsx b/web/app/components/workflow/nodes/http/components/curl-panel.tsx index 4b9ee56f85..2710fd3c5d 100644 --- a/web/app/components/workflow/nodes/http/components/curl-panel.tsx +++ b/web/app/components/workflow/nodes/http/components/curl-panel.tsx @@ -1,13 +1,14 @@ 'use client' import type { FC } from 'react' +import type { HttpNodeType } from '../types' import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import { BodyPayloadValueType, BodyType, type HttpNodeType, Method } from '../types' -import Modal from '@/app/components/base/modal' import Button from '@/app/components/base/button' +import Modal from '@/app/components/base/modal' import Textarea from '@/app/components/base/textarea' import Toast from '@/app/components/base/toast' import { useNodesInteractions } from '@/app/components/workflow/hooks' +import { BodyPayloadValueType, BodyType, Method } from '../types' type Props = { nodeId: string @@ -16,7 +17,7 @@ type Props = { handleCurlImport: (node: HttpNodeType) => void } -const parseCurl = (curlCommand: string): { node: HttpNodeType | null; error: string | null } => { +const parseCurl = (curlCommand: string): { node: HttpNodeType | null, error: string | null } => { if (!curlCommand.trim().toLowerCase().startsWith('curl')) return { node: null, error: 'Invalid cURL command. Command must start with "curl".' } @@ -29,7 +30,7 @@ const parseCurl = (curlCommand: string): { node: HttpNodeType | null; error: str params: '', body: { type: BodyType.none, data: '' }, } - const args = curlCommand.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || [] + const args = curlCommand.match(/(?:[^\s"']|"[^"]*"|'[^']*')+/g) || [] let hasData = false for (let i = 1; i < args.length; i++) { @@ -145,19 +146,22 @@ const CurlPanel: FC<Props> = ({ nodeId, isShow, onHide, handleCurlImport }) => { title={t('workflow.nodes.http.curl.title')} isShow={isShow} onClose={onHide} - className='!w-[400px] !max-w-[400px] !p-4' + className="!w-[400px] !max-w-[400px] !p-4" > <div> <Textarea value={inputString} - className='my-3 h-40 w-full grow' + className="my-3 h-40 w-full grow" onChange={e => setInputString(e.target.value)} placeholder={t('workflow.nodes.http.curl.placeholder')!} /> </div> - <div className='mt-4 flex justify-end space-x-2'> - <Button className='!w-[95px]' onClick={onHide} >{t('common.operation.cancel')}</Button> - <Button className='!w-[95px]' variant='primary' onClick={handleSave} > {t('common.operation.save')}</Button> + <div className="mt-4 flex justify-end space-x-2"> + <Button className="!w-[95px]" onClick={onHide}>{t('common.operation.cancel')}</Button> + <Button className="!w-[95px]" variant="primary" onClick={handleSave}> + {' '} + {t('common.operation.save')} + </Button> </div> </Modal> ) diff --git a/web/app/components/workflow/nodes/http/components/edit-body/index.tsx b/web/app/components/workflow/nodes/http/components/edit-body/index.tsx index 050f54f040..1770d01ef5 100644 --- a/web/app/components/workflow/nodes/http/components/edit-body/index.tsx +++ b/web/app/components/workflow/nodes/http/components/edit-body/index.tsx @@ -1,17 +1,17 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useMemo } from 'react' +import type { Body, BodyPayload, KeyValue as KeyValueType } from '../../types' +import type { ValueSelector, Var } from '@/app/components/workflow/types' import { produce } from 'immer' import { uniqueId } from 'lodash-es' -import type { Body, BodyPayload, KeyValue as KeyValueType } from '../../types' +import React, { useCallback, useMemo } from 'react' +import InputWithVar from '@/app/components/workflow/nodes/_base/components/prompt/editor' +import { VarType } from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' +import VarReferencePicker from '../../../_base/components/variable/var-reference-picker' +import useAvailableVarList from '../../../_base/hooks/use-available-var-list' import { BodyPayloadValueType, BodyType } from '../../types' import KeyValue from '../key-value' -import useAvailableVarList from '../../../_base/hooks/use-available-var-list' -import VarReferencePicker from '../../../_base/components/variable/var-reference-picker' -import { cn } from '@/utils/classnames' -import InputWithVar from '@/app/components/workflow/nodes/_base/components/prompt/editor' -import type { ValueSelector, Var } from '@/app/components/workflow/types' -import { VarType } from '@/app/components/workflow/types' const UNIQUE_ID_PREFIX = 'key-value-' @@ -68,13 +68,13 @@ const EditBody: FC<Props> = ({ type: newType, data: hasKeyValue ? [ - { - id: uniqueId(UNIQUE_ID_PREFIX), - type: BodyPayloadValueType.text, - key: '', - value: '', - }, - ] + { + id: uniqueId(UNIQUE_ID_PREFIX), + type: BodyPayloadValueType.text, + key: '', + value: '', + }, + ] : [], }) }, [onChange]) @@ -133,9 +133,9 @@ const EditBody: FC<Props> = ({ return ( <div> {/* body type */} - <div className='flex flex-wrap'> + <div className="flex flex-wrap"> {allTypes.map(t => ( - <label key={t} htmlFor={`body-type-${t}`} className='mr-4 flex h-7 items-center space-x-2'> + <label key={t} htmlFor={`body-type-${t}`} className="mr-4 flex h-7 items-center space-x-2"> <input type="radio" id={`body-type-${t}`} @@ -144,7 +144,7 @@ const EditBody: FC<Props> = ({ onChange={handleTypeChange} disabled={readonly} /> - <div className='text-[13px] font-normal leading-[18px] text-text-secondary'>{bodyTextMap[t]}</div> + <div className="text-[13px] font-normal leading-[18px] text-text-secondary">{bodyTextMap[t]}</div> </label> ))} </div> @@ -164,8 +164,8 @@ const EditBody: FC<Props> = ({ {type === BodyType.rawText && ( <InputWithVar - instanceId={'http-body-raw'} - title={<div className='uppercase'>Raw text</div>} + instanceId="http-body-raw" + title={<div className="uppercase">Raw text</div>} onChange={handleBodyValueChange} value={stringValue} justVar @@ -177,8 +177,8 @@ const EditBody: FC<Props> = ({ {type === BodyType.json && ( <InputWithVar - instanceId={'http-body-json'} - title='JSON' + instanceId="http-body-json" + title="JSON" value={stringValue} onChange={handleBodyValueChange} justVar diff --git a/web/app/components/workflow/nodes/http/components/key-value/bulk-edit/index.tsx b/web/app/components/workflow/nodes/http/components/key-value/bulk-edit/index.tsx index 43c766c878..ea43c726e2 100644 --- a/web/app/components/workflow/nodes/http/components/key-value/bulk-edit/index.tsx +++ b/web/app/components/workflow/nodes/http/components/key-value/bulk-edit/index.tsx @@ -2,8 +2,8 @@ import type { FC } from 'react' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import TextEditor from '@/app/components/workflow/nodes/_base/components/editor/text-editor' import { LayoutGrid02 } from '@/app/components/base/icons/src/vender/line/layout' +import TextEditor from '@/app/components/workflow/nodes/_base/components/editor/text-editor' const i18nPrefix = 'workflow.nodes.http' @@ -38,22 +38,22 @@ const BulkEdit: FC<Props> = ({ <div> <TextEditor isInNode - title={<div className='uppercase'>{t(`${i18nPrefix}.bulkEdit`)}</div>} + title={<div className="uppercase">{t(`${i18nPrefix}.bulkEdit`)}</div>} value={tempValue} onChange={handleChange} onBlur={handleBlur} - headerRight={ - <div className='flex h-[18px] items-center'> + headerRight={( + <div className="flex h-[18px] items-center"> <div - className='flex cursor-pointer items-center space-x-1' + className="flex cursor-pointer items-center space-x-1" onClick={handleSwitchToKeyValueEdit} > - <LayoutGrid02 className='h-3 w-3 text-gray-500' /> - <div className='text-xs font-normal leading-[18px] text-gray-500'>{t(`${i18nPrefix}.keyValueEdit`)}</div> + <LayoutGrid02 className="h-3 w-3 text-gray-500" /> + <div className="text-xs font-normal leading-[18px] text-gray-500">{t(`${i18nPrefix}.keyValueEdit`)}</div> </div> - <div className='ml-3 mr-1.5 h-3 w-px bg-gray-200'></div> + <div className="ml-3 mr-1.5 h-3 w-px bg-gray-200"></div> </div> - } + )} minHeight={150} /> </div> diff --git a/web/app/components/workflow/nodes/http/components/key-value/index.tsx b/web/app/components/workflow/nodes/http/components/key-value/index.tsx index e930114f32..0191cb0c7a 100644 --- a/web/app/components/workflow/nodes/http/components/key-value/index.tsx +++ b/web/app/components/workflow/nodes/http/components/key-value/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' -import React from 'react' import type { KeyValue } from '../../types' +import React from 'react' import KeyValueEdit from './key-value-edit' type Props = { @@ -44,15 +44,17 @@ const KeyValueList: FC<Props> = ({ // }).join('\n') // return res // })() - return <KeyValueEdit - readonly={readonly} - nodeId={nodeId} - list={list} - onChange={onChange} - onAdd={onAdd} - isSupportFile={isSupportFile} - // onSwitchToBulkEdit={toggleKeyValueEdit} - /> + return ( + <KeyValueEdit + readonly={readonly} + nodeId={nodeId} + list={list} + onChange={onChange} + onAdd={onAdd} + isSupportFile={isSupportFile} + // onSwitchToBulkEdit={toggleKeyValueEdit} + /> + ) // : <BulkEdit // value={bulkList} // onChange={handleBulkValueChange} diff --git a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/index.tsx b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/index.tsx index d107520c75..61d6292e06 100644 --- a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/index.tsx +++ b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/index.tsx @@ -1,11 +1,11 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' -import { produce } from 'immer' -import { useTranslation } from 'react-i18next' import type { KeyValue } from '../../../types' -import KeyValueItem from './item' +import { produce } from 'immer' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' +import KeyValueItem from './item' const i18nPrefix = 'workflow.nodes.http' @@ -56,10 +56,10 @@ const KeyValueList: FC<Props> = ({ return null return ( - <div className='overflow-hidden rounded-lg border border-divider-regular'> + <div className="overflow-hidden rounded-lg border border-divider-regular"> <div className={cn('system-xs-medium-uppercase flex h-7 items-center leading-7 text-text-tertiary')}> <div className={cn('h-full border-r border-divider-regular pl-3', isSupportFile ? 'w-[140px]' : 'w-1/2')}>{t(`${i18nPrefix}.key`)}</div> - {isSupportFile && <div className='h-full w-[70px] shrink-0 border-r border-divider-regular pl-3'>{t(`${i18nPrefix}.type`)}</div>} + {isSupportFile && <div className="h-full w-[70px] shrink-0 border-r border-divider-regular pl-3">{t(`${i18nPrefix}.type`)}</div>} <div className={cn('h-full items-center justify-between pl-3 pr-1', isSupportFile ? 'grow' : 'w-1/2')}>{t(`${i18nPrefix}.value`)}</div> </div> { diff --git a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/input-item.tsx b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/input-item.tsx index 80c42209d6..2f1857f7af 100644 --- a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/input-item.tsx +++ b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/input-item.tsx @@ -1,13 +1,14 @@ 'use client' import type { FC } from 'react' +import type { Var } from '@/app/components/workflow/types' import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import useAvailableVarList from '../../../../_base/hooks/use-available-var-list' -import { cn } from '@/utils/classnames' -import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button' import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' -import type { Var } from '@/app/components/workflow/types' +import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button' import { VarType } from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' +import useAvailableVarList from '../../../../_base/hooks/use-available-var-list' + type Props = { className?: string instanceId?: string @@ -60,29 +61,9 @@ const InputItem: FC<Props> = ({ <div className={cn(className, 'hover:cursor-text hover:bg-state-base-hover', 'relative flex h-full')}> {(!readOnly) ? ( - <Input - instanceId={instanceId} - className={cn(isFocus ? 'bg-components-input-bg-active' : 'bg-width', 'w-0 grow px-3 py-1')} - value={value} - onChange={onChange} - readOnly={readOnly} - nodesOutputVars={availableVars} - availableNodes={availableNodesWithParent} - onFocusChange={setIsFocus} - placeholder={t('workflow.nodes.http.insertVarPlaceholder')!} - placeholderClassName='!leading-[21px]' - promptMinHeightClassName='h-full' - insertVarTipToLeft={insertVarTipToLeft} - /> - ) - : <div - className="h-[18px] w-full pl-0.5 leading-[18px]" - > - {!hasValue && <div className='text-xs font-normal text-text-quaternary'>{placeholder}</div>} - {hasValue && ( <Input instanceId={instanceId} - className={cn(isFocus ? 'border-components-input-border-active bg-components-input-bg-active shadow-xs' : 'border-components-input-border-hover bg-components-input-bg-normal', 'w-0 grow rounded-lg border px-3 py-[6px]')} + className={cn(isFocus ? 'bg-components-input-bg-active' : 'bg-width', 'w-0 grow px-3 py-1')} value={value} onChange={onChange} readOnly={readOnly} @@ -90,16 +71,38 @@ const InputItem: FC<Props> = ({ availableNodes={availableNodesWithParent} onFocusChange={setIsFocus} placeholder={t('workflow.nodes.http.insertVarPlaceholder')!} - placeholderClassName='!leading-[21px]' - promptMinHeightClassName='h-full' + placeholderClassName="!leading-[21px]" + promptMinHeightClassName="h-full" insertVarTipToLeft={insertVarTipToLeft} /> - )} + ) + : ( + <div + className="h-[18px] w-full pl-0.5 leading-[18px]" + > + {!hasValue && <div className="text-xs font-normal text-text-quaternary">{placeholder}</div>} + {hasValue && ( + <Input + instanceId={instanceId} + className={cn(isFocus ? 'border-components-input-border-active bg-components-input-bg-active shadow-xs' : 'border-components-input-border-hover bg-components-input-bg-normal', 'w-0 grow rounded-lg border px-3 py-[6px]')} + value={value} + onChange={onChange} + readOnly={readOnly} + nodesOutputVars={availableVars} + availableNodes={availableNodesWithParent} + onFocusChange={setIsFocus} + placeholder={t('workflow.nodes.http.insertVarPlaceholder')!} + placeholderClassName="!leading-[21px]" + promptMinHeightClassName="h-full" + insertVarTipToLeft={insertVarTipToLeft} + /> + )} - </div>} + </div> + )} {hasRemove && !isFocus && ( <RemoveButton - className='absolute right-1 top-0.5 hidden group-hover:block' + className="absolute right-1 top-0.5 hidden group-hover:block" onClick={handleRemove} /> )} diff --git a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx index 5a27d1efa1..365367fd97 100644 --- a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx +++ b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx @@ -1,15 +1,15 @@ 'use client' import type { FC } from 'react' +import type { KeyValue } from '../../../types' +import type { ValueSelector, Var } from '@/app/components/workflow/types' +import { produce } from 'immer' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { produce } from 'immer' -import type { KeyValue } from '../../../types' +import { PortalSelect } from '@/app/components/base/select' +import { VarType } from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' import VarReferencePicker from '../../../../_base/components/variable/var-reference-picker' import InputItem from './input-item' -import { cn } from '@/utils/classnames' -import { PortalSelect } from '@/app/components/base/select' -import type { ValueSelector, Var } from '@/app/components/workflow/types' -import { VarType } from '@/app/components/workflow/types' // import Input from '@/app/components/base/input' const i18nPrefix = 'workflow.nodes.http' @@ -66,27 +66,27 @@ const KeyValueItem: FC<Props> = ({ <div className={cn('shrink-0 border-r border-divider-regular', isSupportFile ? 'w-[140px]' : 'w-1/2')}> {!keyNotSupportVar ? ( - <InputItem - instanceId={`http-key-${instanceId}`} - nodeId={nodeId} - value={payload.key} - onChange={handleChange('key')} - hasRemove={false} - placeholder={t(`${i18nPrefix}.key`)!} - readOnly={readonly} - insertVarTipToLeft={insertVarTipToLeft} - /> - ) + <InputItem + instanceId={`http-key-${instanceId}`} + nodeId={nodeId} + value={payload.key} + onChange={handleChange('key')} + hasRemove={false} + placeholder={t(`${i18nPrefix}.key`)!} + readOnly={readonly} + insertVarTipToLeft={insertVarTipToLeft} + /> + ) : ( - <input - className='system-sm-regular focus:bg-gray-100! appearance-none rounded-none border-none bg-transparent outline-none hover:bg-components-input-bg-hover focus:ring-0' - value={payload.key} - onChange={e => handleChange('key')(e.target.value)} - /> - )} + <input + className="system-sm-regular focus:bg-gray-100! appearance-none rounded-none border-none bg-transparent outline-none hover:bg-components-input-bg-hover focus:ring-0" + value={payload.key} + onChange={e => handleChange('key')(e.target.value)} + /> + )} </div> {isSupportFile && ( - <div className='w-[70px] shrink-0 border-r border-divider-regular'> + <div className="w-[70px] shrink-0 border-r border-divider-regular"> <PortalSelect value={payload.type!} onSelect={item => handleChange('type')(item.value as string)} @@ -95,38 +95,39 @@ const KeyValueItem: FC<Props> = ({ { name: 'file', value: 'file' }, ]} readonly={readonly} - triggerClassName='rounded-none h-7 text-text-primary' + triggerClassName="rounded-none h-7 text-text-primary" triggerClassNameFn={isOpen => isOpen ? 'bg-state-base-hover' : 'bg-transparent'} - popupClassName='w-[80px] h-7' + popupClassName="w-[80px] h-7" /> - </div>)} + </div> + )} <div className={cn(isSupportFile ? 'grow' : 'w-1/2')} onClick={() => isLastItem && onAdd()}> {(isSupportFile && payload.type === 'file') ? ( - <VarReferencePicker - nodeId={nodeId} - readonly={readonly} - value={payload.file || []} - onChange={handleChange('file')} - filterVar={filterOnlyFileVariable} - isInTable - onRemove={onRemove} - /> - ) + <VarReferencePicker + nodeId={nodeId} + readonly={readonly} + value={payload.file || []} + onChange={handleChange('file')} + filterVar={filterOnlyFileVariable} + isInTable + onRemove={onRemove} + /> + ) : ( - <InputItem - instanceId={`http-value-${instanceId}`} - nodeId={nodeId} - value={payload.value} - onChange={handleChange('value')} - hasRemove={!readonly && canRemove} - onRemove={onRemove} - placeholder={t(`${i18nPrefix}.value`)!} - readOnly={readonly} - isSupportFile={isSupportFile} - insertVarTipToLeft={insertVarTipToLeft} - /> - )} + <InputItem + instanceId={`http-value-${instanceId}`} + nodeId={nodeId} + value={payload.value} + onChange={handleChange('value')} + hasRemove={!readonly && canRemove} + onRemove={onRemove} + placeholder={t(`${i18nPrefix}.value`)!} + readOnly={readonly} + isSupportFile={isSupportFile} + insertVarTipToLeft={insertVarTipToLeft} + /> + )} </div> </div> diff --git a/web/app/components/workflow/nodes/http/components/timeout/index.tsx b/web/app/components/workflow/nodes/http/components/timeout/index.tsx index bb84091d67..11edfa8c93 100644 --- a/web/app/components/workflow/nodes/http/components/timeout/index.tsx +++ b/web/app/components/workflow/nodes/http/components/timeout/index.tsx @@ -1,8 +1,8 @@ 'use client' import type { FC } from 'react' +import type { Timeout as TimeoutPayloadType } from '../../types' import React from 'react' import { useTranslation } from 'react-i18next' -import type { Timeout as TimeoutPayloadType } from '../../types' import Input from '@/app/components/base/input' import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse' import { useStore } from '@/app/components/workflow/store' @@ -34,7 +34,7 @@ const InputField: FC<{ <span className="text-xs font-normal text-text-tertiary">{description}</span> </div> <Input - type='number' + type="number" value={value} onChange={(e) => { const inputValue = e.target.value @@ -70,7 +70,7 @@ const Timeout: FC<Props> = ({ readonly, payload, onChange }) => { return ( <FieldCollapse title={t(`${i18nPrefix}.timeout.title`)}> - <div className='mt-2 space-y-1'> + <div className="mt-2 space-y-1"> <div className="space-y-3"> <InputField title={t('workflow.nodes.http.timeout.connectLabel')!} diff --git a/web/app/components/workflow/nodes/http/default.ts b/web/app/components/workflow/nodes/http/default.ts index 4a08702d6a..b127f70a1f 100644 --- a/web/app/components/workflow/nodes/http/default.ts +++ b/web/app/components/workflow/nodes/http/default.ts @@ -1,9 +1,9 @@ import type { NodeDefault } from '../../types' -import { AuthorizationType, BodyType, Method } from './types' import type { BodyPayload, HttpNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' +import { AuthorizationType, BodyType, Method } from './types' const metaData = genNodeMetaData({ classification: BlockClassificationEnum.Utilities, @@ -45,10 +45,11 @@ const nodeDefault: NodeDefault<HttpNodeType> = { errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('workflow.nodes.http.api') }) if (!errorMessages - && payload.body.type === BodyType.binary - && ((!(payload.body.data as BodyPayload)[0]?.file) || (payload.body.data as BodyPayload)[0]?.file?.length === 0) - ) + && payload.body.type === BodyType.binary + && ((!(payload.body.data as BodyPayload)[0]?.file) || (payload.body.data as BodyPayload)[0]?.file?.length === 0) + ) { errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('workflow.nodes.http.binaryFileVariable') }) + } return { isValid: !errorMessages, diff --git a/web/app/components/workflow/nodes/http/hooks/use-key-value-list.ts b/web/app/components/workflow/nodes/http/hooks/use-key-value-list.ts index 44774074dc..b174b7e6de 100644 --- a/web/app/components/workflow/nodes/http/hooks/use-key-value-list.ts +++ b/web/app/components/workflow/nodes/http/hooks/use-key-value-list.ts @@ -1,7 +1,7 @@ -import { useCallback, useEffect, useState } from 'react' +import type { KeyValue } from '../types' import { useBoolean } from 'ahooks' import { uniqueId } from 'lodash-es' -import type { KeyValue } from '../types' +import { useCallback, useEffect, useState } from 'react' const UNIQUE_ID_PREFIX = 'key-value-' const strToKeyValueList = (value: string) => { diff --git a/web/app/components/workflow/nodes/http/node.tsx b/web/app/components/workflow/nodes/http/node.tsx index 6002bf737d..332a28c44c 100644 --- a/web/app/components/workflow/nodes/http/node.tsx +++ b/web/app/components/workflow/nodes/http/node.tsx @@ -1,8 +1,9 @@ import type { FC } from 'react' -import React from 'react' -import ReadonlyInputWithSelectVar from '../_base/components/readonly-input-with-select-var' import type { HttpNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' +import React from 'react' +import ReadonlyInputWithSelectVar from '../_base/components/readonly-input-with-select-var' + const Node: FC<NodeProps<HttpNodeType>> = ({ id, data, @@ -12,12 +13,12 @@ const Node: FC<NodeProps<HttpNodeType>> = ({ return null return ( - <div className='mb-1 px-3 py-1'> - <div className='flex items-center justify-start rounded-md bg-workflow-block-parma-bg p-1'> - <div className='flex h-4 shrink-0 items-center rounded bg-components-badge-white-to-dark px-1 text-xs font-semibold uppercase text-text-secondary'>{method}</div> - <div className='w-0 grow pl-1 pt-1'> + <div className="mb-1 px-3 py-1"> + <div className="flex items-center justify-start rounded-md bg-workflow-block-parma-bg p-1"> + <div className="flex h-4 shrink-0 items-center rounded bg-components-badge-white-to-dark px-1 text-xs font-semibold uppercase text-text-secondary">{method}</div> + <div className="w-0 grow pl-1 pt-1"> <ReadonlyInputWithSelectVar - className='text-text-secondary' + className="text-text-secondary" value={url} nodeId={id} /> diff --git a/web/app/components/workflow/nodes/http/panel.tsx b/web/app/components/workflow/nodes/http/panel.tsx index a174ff4742..b46474ab84 100644 --- a/web/app/components/workflow/nodes/http/panel.tsx +++ b/web/app/components/workflow/nodes/http/panel.tsx @@ -1,22 +1,22 @@ import type { FC } from 'react' +import type { HttpNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import { memo } from 'react' import { useTranslation } from 'react-i18next' -import useConfig from './use-config' -import ApiInput from './components/api-input' -import KeyValue from './components/key-value' -import EditBody from './components/edit-body' -import AuthorizationModal from './components/authorization' -import type { HttpNodeType } from './types' -import Timeout from './components/timeout' -import CurlPanel from './components/curl-panel' -import { cn } from '@/utils/classnames' +import { FileArrow01 } from '@/app/components/base/icons/src/vender/line/files' +import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' import Switch from '@/app/components/base/switch' import Field from '@/app/components/workflow/nodes/_base/components/field' -import Split from '@/app/components/workflow/nodes/_base/components/split' import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' -import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' -import { FileArrow01 } from '@/app/components/base/icons/src/vender/line/files' -import type { NodePanelProps } from '@/app/components/workflow/types' +import Split from '@/app/components/workflow/nodes/_base/components/split' +import { cn } from '@/utils/classnames' +import ApiInput from './components/api-input' +import AuthorizationModal from './components/authorization' +import CurlPanel from './components/curl-panel' +import EditBody from './components/edit-body' +import KeyValue from './components/key-value' +import Timeout from './components/timeout' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.http' @@ -55,34 +55,34 @@ const Panel: FC<NodePanelProps<HttpNodeType>> = ({ return null return ( - <div className='pt-2'> - <div className='space-y-4 px-4 pb-4'> + <div className="pt-2"> + <div className="space-y-4 px-4 pb-4"> <Field title={t(`${i18nPrefix}.api`)} required - operations={ - <div className='flex'> + operations={( + <div className="flex"> <div onClick={showAuthorization} className={cn(!readOnly && 'cursor-pointer hover:bg-state-base-hover', 'flex h-6 items-center space-x-1 rounded-md px-2 ')} > - {!readOnly && <Settings01 className='h-3 w-3 text-text-tertiary' />} - <div className='text-xs font-medium text-text-tertiary'> + {!readOnly && <Settings01 className="h-3 w-3 text-text-tertiary" />} + <div className="text-xs font-medium text-text-tertiary"> {t(`${i18nPrefix}.authorization.authorization`)} - <span className='ml-1 text-text-secondary'>{t(`${i18nPrefix}.authorization.${inputs.authorization.type}`)}</span> + <span className="ml-1 text-text-secondary">{t(`${i18nPrefix}.authorization.${inputs.authorization.type}`)}</span> </div> </div> <div onClick={showCurlPanel} className={cn(!readOnly && 'cursor-pointer hover:bg-state-base-hover', 'flex h-6 items-center space-x-1 rounded-md px-2 ')} > - {!readOnly && <FileArrow01 className='h-3 w-3 text-text-tertiary' />} - <div className='text-xs font-medium text-text-tertiary'> + {!readOnly && <FileArrow01 className="h-3 w-3 text-text-tertiary" />} + <div className="text-xs font-medium text-text-tertiary"> {t(`${i18nPrefix}.curl.title`)} </div> </div> </div> - } + )} > <ApiInput nodeId={id} @@ -129,14 +129,15 @@ const Panel: FC<NodePanelProps<HttpNodeType>> = ({ <Field title={t(`${i18nPrefix}.verifySSL.title`)} tooltip={t(`${i18nPrefix}.verifySSL.warningTooltip`)} - operations={ + operations={( <Switch defaultValue={!!inputs.ssl_verify} onChange={handleSSLVerifyChange} - size='md' + size="md" disabled={readOnly} /> - }> + )} + > </Field> </div> <Split /> @@ -156,27 +157,27 @@ const Panel: FC<NodePanelProps<HttpNodeType>> = ({ /> )} <Split /> - <div className=''> + <div className=""> <OutputVars> <> <VarItem - name='body' - type='string' + name="body" + type="string" description={t(`${i18nPrefix}.outputVars.body`)} /> <VarItem - name='status_code' - type='number' + name="status_code" + type="number" description={t(`${i18nPrefix}.outputVars.statusCode`)} /> <VarItem - name='headers' - type='object' + name="headers" + type="object" description={t(`${i18nPrefix}.outputVars.headers`)} /> <VarItem - name='files' - type='Array[File]' + name="files" + type="Array[File]" description={t(`${i18nPrefix}.outputVars.files`)} /> </> diff --git a/web/app/components/workflow/nodes/http/use-config.ts b/web/app/components/workflow/nodes/http/use-config.ts index 45303c3b46..fe8c8ac236 100644 --- a/web/app/components/workflow/nodes/http/use-config.ts +++ b/web/app/components/workflow/nodes/http/use-config.ts @@ -1,17 +1,18 @@ -import { useCallback, useEffect, useState } from 'react' -import { produce } from 'immer' -import { useBoolean } from 'ahooks' -import useVarList from '../_base/hooks/use-var-list' -import { VarType } from '../../types' import type { Var } from '../../types' -import { useStore } from '../../store' -import { type Authorization, type Body, BodyType, type HttpNodeType, type Method, type Timeout } from './types' -import useKeyValueList from './hooks/use-key-value-list' -import { transformToBodyPayload } from './utils' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import type { Authorization, Body, HttpNodeType, Method, Timeout } from './types' +import { useBoolean } from 'ahooks' +import { produce } from 'immer' +import { useCallback, useEffect, useState } from 'react' import { useNodesReadOnly, } from '@/app/components/workflow/hooks' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { useStore } from '../../store' +import { VarType } from '../../types' +import useVarList from '../_base/hooks/use-var-list' +import useKeyValueList from './hooks/use-key-value-list' +import { BodyType } from './types' +import { transformToBodyPayload } from './utils' const useConfig = (id: string, payload: HttpNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() diff --git a/web/app/components/workflow/nodes/http/use-single-run-form-params.ts b/web/app/components/workflow/nodes/http/use-single-run-form-params.ts index 06d4ac3a27..735ed082c3 100644 --- a/web/app/components/workflow/nodes/http/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/http/use-single-run-form-params.ts @@ -1,12 +1,12 @@ import type { RefObject } from 'react' +import type { HttpNodeType } from './types' import type { InputVar, Variable } from '@/app/components/workflow/types' import { useCallback, useMemo } from 'react' import useNodeCrud from '../_base/hooks/use-node-crud' -import type { HttpNodeType } from './types' type Params = { - id: string, - payload: HttpNodeType, + id: string + payload: HttpNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] diff --git a/web/app/components/workflow/nodes/http/utils.ts b/web/app/components/workflow/nodes/http/utils.ts index ffb474f45a..1d07fc5398 100644 --- a/web/app/components/workflow/nodes/http/utils.ts +++ b/web/app/components/workflow/nodes/http/utils.ts @@ -1,4 +1,5 @@ -import { type BodyPayload, BodyPayloadValueType } from './types' +import type { BodyPayload } from './types' +import { BodyPayloadValueType } from './types' export const transformToBodyPayload = (old: string, hasKey: boolean): BodyPayload => { if (!hasKey) { 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 9b6ad2700b..91d7a0f03a 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 @@ -1,10 +1,15 @@ +import type { HandleAddCondition } from '../types' +import type { + NodeOutPutVar, + ValueSelector, + Var, +} from '@/app/components/workflow/types' +import { RiAddLine } from '@remixicon/react' import { useCallback, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { RiAddLine } from '@remixicon/react' -import type { HandleAddCondition } from '../types' import Button from '@/app/components/base/button' import { PortalToFollowElem, @@ -12,11 +17,6 @@ import { PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' -import type { - NodeOutPutVar, - ValueSelector, - Var, -} from '@/app/components/workflow/types' type ConditionAddProps = { className?: string @@ -44,7 +44,7 @@ const ConditionAdd = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 4, crossAxis: 0, @@ -52,16 +52,16 @@ const ConditionAdd = ({ > <PortalToFollowElemTrigger onClick={() => setOpen(!open)}> <Button - size='small' + size="small" className={className} disabled={disabled} > - <RiAddLine className='mr-1 h-3.5 w-3.5' /> + <RiAddLine className="mr-1 h-3.5 w-3.5" /> {t('workflow.nodes.ifElse.addCondition')} </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> - <div className='w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'> + <PortalToFollowElemContent className="z-[1000]"> + <div className="w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg"> <VarReferenceVars vars={variables} isSupportFileVar diff --git a/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx b/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx index 1a2e3e3bc5..53df68c337 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx @@ -1,20 +1,22 @@ +import type { ValueSelector } from '../../../types' +import type { Condition } from '../types' import { memo, useCallback, } from 'react' import { useTranslation } from 'react-i18next' -import { ComparisonOperator, type Condition } from '../types' +import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { + VariableLabelInNode, +} from '@/app/components/workflow/nodes/_base/components/variable/variable-label' +import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../../constants' +import { ComparisonOperator } from '../types' import { comparisonOperatorNotRequireValue, isComparisonOperatorNeedTranslate, isEmptyRelatedOperator, } from '../utils' -import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../../constants' -import type { ValueSelector } from '../../../types' -import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' -import { - VariableLabelInNode, -} from '@/app/components/workflow/nodes/_base/components/variable/variable-label' + const i18nPrefix = 'workflow.nodes.ifElse' type ConditionValueProps = { @@ -39,7 +41,7 @@ const ConditionValue = ({ return '' const value = c.value as string - return value.replace(/{{#([^#]*)#}}/g, (a, b) => { + return value.replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { const arr: string[] = b.split('.') if (isSystemVar(arr)) return `{{${b}}}` @@ -57,41 +59,41 @@ const ConditionValue = ({ if (isSelect) { const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(c.value) ? c.value[0] : c.value))[0] return name - ? t(`workflow.nodes.ifElse.optionName.${name.i18nKey}`).replace(/{{#([^#]*)#}}/g, (a, b) => { - const arr: string[] = b.split('.') - if (isSystemVar(arr)) - return `{{${b}}}` + ? t(`workflow.nodes.ifElse.optionName.${name.i18nKey}`).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { + const arr: string[] = b.split('.') + if (isSystemVar(arr)) + return `{{${b}}}` - return `{{${arr.slice(1).join('.')}}}` - }) + return `{{${arr.slice(1).join('.')}}}` + }) : '' } return '' }, [t]) return ( - <div className='rounded-md bg-workflow-block-parma-bg'> - <div className='flex h-6 items-center px-1 '> + <div className="rounded-md bg-workflow-block-parma-bg"> + <div className="flex h-6 items-center px-1 "> <VariableLabelInNode - className='w-0 grow' + className="w-0 grow" variables={variableSelector} notShowFullPath /> <div - className='mx-1 shrink-0 text-xs font-medium text-text-primary' + className="mx-1 shrink-0 text-xs font-medium text-text-primary" title={operatorName} > {operatorName} </div> </div> - <div className='ml-[10px] border-l border-divider-regular pl-[10px]'> + <div className="ml-[10px] border-l border-divider-regular pl-[10px]"> { sub_variable_condition?.conditions.map((c: Condition, index) => ( - <div className='relative flex h-6 items-center space-x-1' key={c.id}> - <div className='system-xs-medium text-text-accent'>{c.key}</div> - <div className='system-xs-medium text-text-primary'>{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${c.comparison_operator}`) : c.comparison_operator}</div> - {c.comparison_operator && !isEmptyRelatedOperator(c.comparison_operator) && <div className='system-xs-regular text-text-secondary'>{isSelect(c) ? selectName(c) : formatValue(c)}</div>} - {index !== sub_variable_condition.conditions.length - 1 && (<div className='absolute bottom-[-10px] right-1 z-10 text-[10px] font-medium uppercase leading-4 text-text-accent'>{t(`${i18nPrefix}.${sub_variable_condition.logical_operator}`)}</div>)} + <div className="relative flex h-6 items-center space-x-1" key={c.id}> + <div className="system-xs-medium text-text-accent">{c.key}</div> + <div className="system-xs-medium text-text-primary">{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${c.comparison_operator}`) : c.comparison_operator}</div> + {c.comparison_operator && !isEmptyRelatedOperator(c.comparison_operator) && <div className="system-xs-regular text-text-secondary">{isSelect(c) ? selectName(c) : formatValue(c)}</div>} + {index !== sub_variable_condition.conditions.length - 1 && (<div className="absolute bottom-[-10px] right-1 z-10 text-[10px] font-medium uppercase leading-4 text-text-accent">{t(`${i18nPrefix}.${sub_variable_condition.logical_operator}`)}</div>)} </div> )) } diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-input.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-input.tsx index eba7a3bab7..f4961a7156 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-input.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-input.tsx @@ -1,11 +1,11 @@ -import { useTranslation } from 'react-i18next' -import { useStore } from '@/app/components/workflow/store' -import PromptEditor from '@/app/components/base/prompt-editor' -import { BlockEnum } from '@/app/components/workflow/types' import type { Node, NodeOutPutVar, } from '@/app/components/workflow/types' +import { useTranslation } from 'react-i18next' +import PromptEditor from '@/app/components/base/prompt-editor' +import { useStore } from '@/app/components/workflow/store' +import { BlockEnum } from '@/app/components/workflow/types' type ConditionInputProps = { disabled?: boolean diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx index af87b70196..b323e65066 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-item.tsx @@ -1,53 +1,54 @@ -import { - useCallback, - useMemo, - useState, -} from 'react' -import { useTranslation } from 'react-i18next' -import { RiDeleteBinLine } from '@remixicon/react' -import { produce } from 'immer' import type { VarType as NumberVarType } from '../../../tool/types' import type { Condition, HandleAddSubVariableCondition, HandleRemoveCondition, + handleRemoveSubVariableCondition, HandleToggleSubVariableConditionLogicalOperator, HandleUpdateCondition, HandleUpdateSubVariableCondition, - handleRemoveSubVariableCondition, } from '../../types' -import { - ComparisonOperator, -} from '../../types' -import { comparisonOperatorNotRequireValue, getOperators } from '../../utils' -import ConditionNumberInput from '../condition-number-input' -import { FILE_TYPE_OPTIONS, SUB_VARIABLES, TRANSFER_METHOD } from '../../../constants' -import ConditionWrap from '../condition-wrap' -import ConditionOperator from './condition-operator' -import ConditionInput from './condition-input' -import { useWorkflowStore } from '@/app/components/workflow/store' - -import ConditionVarSelector from './condition-var-selector' import type { Node, NodeOutPutVar, ValueSelector, Var, } from '@/app/components/workflow/types' -import { VarType } from '@/app/components/workflow/types' -import { cn } from '@/utils/classnames' -import { SimpleSelect as Select } from '@/app/components/base/select' +import { RiDeleteBinLine } from '@remixicon/react' +import { produce } from 'immer' +import { + useCallback, + useMemo, + useState, +} from 'react' +import { useTranslation } from 'react-i18next' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' -import BoolValue from '@/app/components/workflow/panel/chat-variable-panel/components/bool-value' -import { getVarType } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { SimpleSelect as Select } from '@/app/components/base/select' import { useIsChatMode } from '@/app/components/workflow/hooks/use-workflow' -import useMatchSchemaType from '../../../_base/components/variable/use-match-schema-type' +import { getVarType } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import BoolValue from '@/app/components/workflow/panel/chat-variable-panel/components/bool-value' +import { useWorkflowStore } from '@/app/components/workflow/store' +import { VarType } from '@/app/components/workflow/types' + import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, } from '@/service/use-tools' +import { cn } from '@/utils/classnames' +import useMatchSchemaType from '../../../_base/components/variable/use-match-schema-type' +import { FILE_TYPE_OPTIONS, SUB_VARIABLES, TRANSFER_METHOD } from '../../../constants' +import { + ComparisonOperator, +} from '../../types' +import { comparisonOperatorNotRequireValue, getOperators } from '../../utils' +import ConditionNumberInput from '../condition-number-input' +import ConditionWrap from '../condition-wrap' +import ConditionInput from './condition-input' +import ConditionOperator from './condition-operator' +import ConditionVarSelector from './condition-var-selector' + const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName' type ConditionItemProps = { @@ -249,10 +250,10 @@ const ConditionItem = ({ }, [condition, doUpdateCondition, availableNodes, isChatMode, schemaTypeDefinitions, buildInTools, customTools, mcpTools, workflowTools]) const showBooleanInput = useMemo(() => { - if(condition.varType === VarType.boolean) + if (condition.varType === VarType.boolean) return true - if(condition.varType === VarType.arrayBoolean && [ComparisonOperator.contains, ComparisonOperator.notContains].includes(condition.comparison_operator!)) + if (condition.varType === VarType.arrayBoolean && [ComparisonOperator.contains, ComparisonOperator.notContains].includes(condition.comparison_operator!)) return true return false }, [condition]) @@ -261,45 +262,48 @@ const ConditionItem = ({ <div className={cn( 'grow rounded-lg bg-components-input-bg-normal', isHovered && 'bg-state-destructive-hover', - )}> - <div className='flex items-center p-1'> - <div className='w-0 grow'> + )} + > + <div className="flex items-center p-1"> + <div className="w-0 grow"> {isSubVarSelect ? ( - <Select - wrapperClassName='h-6' - className='pl-0 text-xs' - optionWrapClassName='w-[165px] max-h-none' - defaultValue={condition.key} - items={subVarOptions} - onSelect={item => handleSubVarKeyChange(item.value as string)} - renderTrigger={item => ( - item - ? <div className='flex cursor-pointer justify-start'> - <div className='inline-flex h-6 max-w-full items-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark px-1.5 text-text-accent shadow-xs'> - <Variable02 className='h-3.5 w-3.5 shrink-0 text-text-accent' /> - <div className='system-xs-medium ml-0.5 truncate'>{item?.name}</div> - </div> - </div> - : <div className='system-sm-regular text-left text-components-input-text-placeholder'>{t('common.placeholder.select')}</div> - )} - hideChecked - /> - ) + <Select + wrapperClassName="h-6" + className="pl-0 text-xs" + optionWrapClassName="w-[165px] max-h-none" + defaultValue={condition.key} + items={subVarOptions} + onSelect={item => handleSubVarKeyChange(item.value as string)} + renderTrigger={item => ( + item + ? ( + <div className="flex cursor-pointer justify-start"> + <div className="inline-flex h-6 max-w-full items-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark px-1.5 text-text-accent shadow-xs"> + <Variable02 className="h-3.5 w-3.5 shrink-0 text-text-accent" /> + <div className="system-xs-medium ml-0.5 truncate">{item?.name}</div> + </div> + </div> + ) + : <div className="system-sm-regular text-left text-components-input-text-placeholder">{t('common.placeholder.select')}</div> + )} + hideChecked + /> + ) : ( - <ConditionVarSelector - open={open} - onOpenChange={setOpen} - valueSelector={condition.variable_selector || []} - varType={condition.varType} - availableNodes={availableNodes} - nodesOutputVars={nodesOutputVars} - onChange={handleVarChange} - /> - )} + <ConditionVarSelector + open={open} + onOpenChange={setOpen} + valueSelector={condition.variable_selector || []} + varType={condition.varType} + availableNodes={availableNodes} + nodesOutputVars={nodesOutputVars} + onChange={handleVarChange} + /> + )} </div> - <div className='mx-1 h-3 w-[1px] bg-divider-regular'></div> + <div className="mx-1 h-3 w-[1px] bg-divider-regular"></div> <ConditionOperator disabled={!canChooseOperator} varType={condition.varType} @@ -310,7 +314,7 @@ const ConditionItem = ({ </div> { !comparisonOperatorNotRequireValue(condition.comparison_operator) && !isNotInput && condition.varType !== VarType.number && !showBooleanInput && ( - <div className='max-h-[100px] overflow-y-auto border-t border-t-divider-subtle px-2 py-1'> + <div className="max-h-[100px] overflow-y-auto border-t border-t-divider-subtle px-2 py-1"> <ConditionInput disabled={disabled} value={condition.value as string} @@ -323,7 +327,7 @@ const ConditionItem = ({ } { !comparisonOperatorNotRequireValue(condition.comparison_operator) && !isNotInput && showBooleanInput && ( - <div className='p-1'> + <div className="p-1"> <BoolValue value={condition.value as boolean} onChange={handleUpdateConditionValue} @@ -333,7 +337,7 @@ const ConditionItem = ({ } { !comparisonOperatorNotRequireValue(condition.comparison_operator) && !isNotInput && condition.varType === VarType.number && ( - <div className='border-t border-t-divider-subtle px-2 py-1 pt-[3px]'> + <div className="border-t border-t-divider-subtle px-2 py-1 pt-[3px]"> <ConditionNumberInput numberVarType={condition.numberVarType} onNumberVarTypeChange={handleUpdateConditionNumberVarType} @@ -348,10 +352,10 @@ const ConditionItem = ({ } { !comparisonOperatorNotRequireValue(condition.comparison_operator) && isSelect && ( - <div className='border-t border-t-divider-subtle'> + <div className="border-t border-t-divider-subtle"> <Select - wrapperClassName='h-8' - className='rounded-t-none px-2 text-xs' + wrapperClassName="h-8" + className="rounded-t-none px-2 text-xs" defaultValue={isArrayValue ? (condition.value as string[])?.[0] : (condition.value as string)} items={selectOptions} onSelect={item => handleUpdateConditionValue(item.value as string)} @@ -363,7 +367,7 @@ const ConditionItem = ({ } { !comparisonOperatorNotRequireValue(condition.comparison_operator) && isSubVariable && ( - <div className='p-1'> + <div className="p-1"> <ConditionWrap isSubVariable caseId={caseId} @@ -384,12 +388,12 @@ const ConditionItem = ({ } </div> <div - className='ml-1 mt-1 flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-lg text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive' + className="ml-1 mt-1 flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-lg text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive" onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} onClick={doRemoveCondition} > - <RiDeleteBinLine className='h-4 w-4' /> + <RiDeleteBinLine className="h-4 w-4" /> </div> </div> ) 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 1763afdfd1..e2753ba6e7 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,19 +1,20 @@ +import type { ComparisonOperator } from '../../types' +import type { VarType } from '@/app/components/workflow/types' +import { RiArrowDownSLine } from '@remixicon/react' import { useMemo, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { RiArrowDownSLine } from '@remixicon/react' -import { getOperators, isComparisonOperatorNeedTranslate } from '../../utils' -import type { ComparisonOperator } from '../../types' import Button from '@/app/components/base/button' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import type { VarType } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' +import { getOperators, isComparisonOperatorNeedTranslate } from '../../utils' + const i18nPrefix = 'workflow.nodes.ifElse' type ConditionOperatorProps = { @@ -48,7 +49,7 @@ const ConditionOperator = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-end' + placement="bottom-end" offset={{ mainAxis: 4, crossAxis: 0, @@ -57,8 +58,8 @@ const ConditionOperator = ({ <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}> <Button className={cn('shrink-0', !selectedOption && 'opacity-50', className)} - size='small' - variant='ghost' + size="small" + variant="ghost" disabled={disabled} > { @@ -66,16 +67,16 @@ const ConditionOperator = ({ ? selectedOption.label : t(`${i18nPrefix}.select`) } - <RiArrowDownSLine className='ml-1 h-3.5 w-3.5' /> + <RiArrowDownSLine className="ml-1 h-3.5 w-3.5" /> </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[11]'> - <div className='rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'> + <PortalToFollowElemContent className="z-[11]"> + <div className="rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg"> { options.map(option => ( <div key={option.value} - className='flex h-7 cursor-pointer items-center rounded-lg px-3 py-1.5 text-[13px] font-medium text-text-secondary hover:bg-state-base-hover' + className="flex h-7 cursor-pointer items-center rounded-lg px-3 py-1.5 text-[13px] font-medium text-text-secondary hover:bg-state-base-hover" onClick={() => { onSelect(option.value) setOpen(false) diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-var-selector.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-var-selector.tsx index c05e733c1a..a8146c353d 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-var-selector.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-var-selector.tsx @@ -1,7 +1,7 @@ +import type { Node, NodeOutPutVar, ValueSelector, Var, VarType } from '@/app/components/workflow/types' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' import VariableTag from '@/app/components/workflow/nodes/_base/components/variable-tag' import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' -import type { Node, NodeOutPutVar, ValueSelector, Var, VarType } from '@/app/components/workflow/types' type ConditionVarSelectorProps = { open: boolean @@ -26,7 +26,7 @@ const ConditionVarSelector = ({ <PortalToFollowElem open={open} onOpenChange={onOpenChange} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 4, crossAxis: 0, @@ -42,8 +42,8 @@ const ConditionVarSelector = ({ /> </div> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> - <div className='w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'> + <PortalToFollowElemContent className="z-[1000]"> + <div className="w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg"> <VarReferenceVars vars={nodesOutputVars} isSupportFileVar diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/index.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/index.tsx index 6c03dd8412..bda775e21c 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-list/index.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-list/index.tsx @@ -1,23 +1,18 @@ -import { RiLoopLeftLine } from '@remixicon/react' -import { useCallback, useMemo } from 'react' -import { - type CaseItem, - type HandleAddSubVariableCondition, - type HandleRemoveCondition, - type HandleToggleConditionLogicalOperator, - type HandleToggleSubVariableConditionLogicalOperator, - type HandleUpdateCondition, - type HandleUpdateSubVariableCondition, - LogicalOperator, - type handleRemoveSubVariableCondition, -} from '../../types' -import ConditionItem from './condition-item' +import type { CaseItem, HandleAddSubVariableCondition, HandleRemoveCondition, handleRemoveSubVariableCondition, HandleToggleConditionLogicalOperator, HandleToggleSubVariableConditionLogicalOperator, HandleUpdateCondition, HandleUpdateSubVariableCondition } from '../../types' import type { Node, NodeOutPutVar, Var, } from '@/app/components/workflow/types' +import { RiLoopLeftLine } from '@remixicon/react' +import { useCallback, useMemo } from 'react' import { cn } from '@/utils/classnames' +import { + + LogicalOperator, + +} from '../../types' +import ConditionItem from './condition-item' type ConditionListProps = { isSubVariable?: boolean @@ -90,15 +85,16 @@ const ConditionList = ({ 'absolute bottom-0 left-0 top-0 w-[60px]', isSubVariable && logical_operator === LogicalOperator.and && 'left-[-10px]', isSubVariable && logical_operator === LogicalOperator.or && 'left-[-18px]', - )}> - <div className='absolute bottom-4 left-[46px] top-4 w-2.5 rounded-l-[8px] border border-r-0 border-divider-deep'></div> - <div className='absolute right-0 top-1/2 h-[29px] w-4 -translate-y-1/2 bg-components-panel-bg'></div> + )} + > + <div className="absolute bottom-4 left-[46px] top-4 w-2.5 rounded-l-[8px] border border-r-0 border-divider-deep"></div> + <div className="absolute right-0 top-1/2 h-[29px] w-4 -translate-y-1/2 bg-components-panel-bg"></div> <div - className='absolute right-1 top-1/2 flex h-[21px] -translate-y-1/2 cursor-pointer select-none items-center rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-1 text-[10px] font-semibold text-text-accent-secondary shadow-xs' + className="absolute right-1 top-1/2 flex h-[21px] -translate-y-1/2 cursor-pointer select-none items-center rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-1 text-[10px] font-semibold text-text-accent-secondary shadow-xs" onClick={doToggleConditionLogicalOperator} > {logical_operator.toUpperCase()} - <RiLoopLeftLine className='ml-0.5 h-3 w-3' /> + <RiLoopLeftLine className="ml-0.5 h-3 w-3" /> </div> </div> ) 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 a13fbef011..29419be011 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 @@ -1,29 +1,29 @@ +import type { + NodeOutPutVar, + ValueSelector, +} from '@/app/components/workflow/types' +import { RiArrowDownSLine } from '@remixicon/react' +import { useBoolean } from 'ahooks' +import { capitalize } from 'lodash-es' import { memo, useCallback, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { RiArrowDownSLine } from '@remixicon/react' -import { capitalize } from 'lodash-es' -import { useBoolean } from 'ahooks' -import { VarType as NumberVarType } from '../../tool/types' -import VariableTag from '../../_base/components/variable-tag' +import Button from '@/app/components/base/button' +import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import Button from '@/app/components/base/button' -import { cn } from '@/utils/classnames' import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' -import type { - NodeOutPutVar, - ValueSelector, -} from '@/app/components/workflow/types' import { VarType } from '@/app/components/workflow/types' import { variableTransformer } from '@/app/components/workflow/utils' -import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' +import { cn } from '@/utils/classnames' +import VariableTag from '../../_base/components/variable-tag' +import { VarType as NumberVarType } from '../../tool/types' const options = [ NumberVarType.variable, @@ -62,25 +62,25 @@ const ConditionNumberInput = ({ }, [onValueChange]) return ( - <div className='flex cursor-pointer items-center'> + <div className="flex cursor-pointer items-center"> <PortalToFollowElem open={numberVarTypeVisible} onOpenChange={setNumberVarTypeVisible} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 2, crossAxis: 0 }} > <PortalToFollowElemTrigger onClick={() => setNumberVarTypeVisible(v => !v)}> <Button - className='shrink-0' - variant='ghost' - size='small' + className="shrink-0" + variant="ghost" + size="small" > {capitalize(numberVarType)} - <RiArrowDownSLine className='ml-[1px] h-3.5 w-3.5' /> + <RiArrowDownSLine className="ml-[1px] h-3.5 w-3.5" /> </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> - <div className='w-[112px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'> + <PortalToFollowElemContent className="z-[1000]"> + <div className="w-[112px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg"> { options.map(option => ( <div @@ -102,19 +102,20 @@ const ConditionNumberInput = ({ </div> </PortalToFollowElemContent> </PortalToFollowElem> - <div className='mx-1 h-4 w-[1px] bg-divider-regular'></div> - <div className='ml-0.5 w-0 grow'> + <div className="mx-1 h-4 w-[1px] bg-divider-regular"></div> + <div className="ml-0.5 w-0 grow"> { numberVarType === NumberVarType.variable && ( <PortalToFollowElem open={variableSelectorVisible} onOpenChange={setVariableSelectorVisible} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 2, crossAxis: 0 }} > <PortalToFollowElemTrigger - className='w-full' - onClick={() => setVariableSelectorVisible(v => !v)}> + className="w-full" + onClick={() => setVariableSelectorVisible(v => !v)} + > { value && ( <VariableTag @@ -126,14 +127,14 @@ const ConditionNumberInput = ({ } { !value && ( - <div className='flex h-6 items-center p-1 text-[13px] text-components-input-text-placeholder'> - <Variable02 className='mr-1 h-4 w-4 shrink-0' /> - <div className='w-0 grow truncate'>{t('workflow.nodes.ifElse.selectVariable')}</div> + <div className="flex h-6 items-center p-1 text-[13px] text-components-input-text-placeholder"> + <Variable02 className="mr-1 h-4 w-4 shrink-0" /> + <div className="w-0 grow truncate">{t('workflow.nodes.ifElse.selectVariable')}</div> </div> ) } </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> + <PortalToFollowElemContent className="z-[1000]"> <div className={cn('w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur pt-1 shadow-lg', isShort && 'w-[200px]')}> <VarReferenceVars vars={variables} @@ -146,17 +147,17 @@ const ConditionNumberInput = ({ } { numberVarType === NumberVarType.constant && ( - <div className=' relative'> + <div className=" relative"> <input className={cn('block w-full appearance-none bg-transparent px-2 text-[13px] text-components-input-text-filled outline-none placeholder:text-components-input-text-placeholder', unit && 'pr-6')} - type='number' + type="number" value={value} onChange={e => onValueChange(e.target.value)} placeholder={t('workflow.nodes.ifElse.enterValue') || ''} onFocus={setFocus} onBlur={setBlur} /> - {!isFocus && unit && <div className='system-sm-regular absolute right-2 top-[50%] translate-y-[-50%] text-text-tertiary'>{unit}</div>} + {!isFocus && unit && <div className="system-sm-regular absolute right-2 top-[50%] translate-y-[-50%] text-text-tertiary">{unit}</div>} </div> ) } diff --git a/web/app/components/workflow/nodes/if-else/components/condition-value.tsx b/web/app/components/workflow/nodes/if-else/components/condition-value.tsx index 82db6d15f8..376c3a670f 100644 --- a/web/app/components/workflow/nodes/if-else/components/condition-value.tsx +++ b/web/app/components/workflow/nodes/if-else/components/condition-value.tsx @@ -1,24 +1,24 @@ +import type { + CommonNodeType, + Node, +} from '@/app/components/workflow/types' import { memo, useMemo, } from 'react' import { useTranslation } from 'react-i18next' import { useNodes } from 'reactflow' +import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { + VariableLabelInText, +} from '@/app/components/workflow/nodes/_base/components/variable/variable-label' +import { isExceptionVariable } from '@/app/components/workflow/utils' +import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../../constants' import { ComparisonOperator } from '../types' import { comparisonOperatorNotRequireValue, isComparisonOperatorNeedTranslate, } from '../utils' -import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '../../constants' -import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' -import { isExceptionVariable } from '@/app/components/workflow/utils' -import type { - CommonNodeType, - Node, -} from '@/app/components/workflow/types' -import { - VariableLabelInText, -} from '@/app/components/workflow/nodes/_base/components/variable/variable-label' type ConditionValueProps = { variableSelector: string[] @@ -49,7 +49,7 @@ const ConditionValue = ({ if (value === true || value === false) return value ? 'True' : 'False' - return value.replace(/{{#([^#]*)#}}/g, (a, b) => { + return value.replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { const arr: string[] = b.split('.') if (isSystemVar(arr)) return `{{${b}}}` @@ -63,22 +63,22 @@ const ConditionValue = ({ if (isSelect) { const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(value) ? value[0] : value))[0] return name - ? t(`workflow.nodes.ifElse.optionName.${name.i18nKey}`).replace(/{{#([^#]*)#}}/g, (a, b) => { - const arr: string[] = b.split('.') - if (isSystemVar(arr)) - return `{{${b}}}` + ? t(`workflow.nodes.ifElse.optionName.${name.i18nKey}`).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { + const arr: string[] = b.split('.') + if (isSystemVar(arr)) + return `{{${b}}}` - return `{{${arr.slice(1).join('.')}}}` - }) + return `{{${arr.slice(1).join('.')}}}` + }) : '' } return '' }, [isSelect, t, value]) return ( - <div className='flex h-6 items-center rounded-md bg-workflow-block-parma-bg px-1'> + <div className="flex h-6 items-center rounded-md bg-workflow-block-parma-bg px-1"> <VariableLabelInText - className='w-0 grow' + className="w-0 grow" variables={variableSelector} nodeTitle={node?.data.title} nodeType={node?.data.type} @@ -86,14 +86,14 @@ const ConditionValue = ({ notShowFullPath /> <div - className='mx-1 shrink-0 text-xs font-medium text-text-primary' + className="mx-1 shrink-0 text-xs font-medium text-text-primary" title={operatorName} > {operatorName} </div> { !notHasValue && ( - <div className='shrink-[3] truncate text-xs text-text-secondary' title={formatValue}>{isSelect ? selectName : formatValue}</div> + <div className="shrink-[3] truncate text-xs text-text-secondary" title={formatValue}>{isSelect ? selectName : formatValue}</div> ) } </div> 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 e9352d154e..b9f80cb9f0 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 @@ -1,24 +1,24 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { ReactSortable } from 'react-sortablejs' +import type { Node, NodeOutPutVar, Var } from '../../../types' +import type { CaseItem, HandleAddCondition, HandleAddSubVariableCondition, HandleRemoveCondition, handleRemoveSubVariableCondition, HandleToggleConditionLogicalOperator, HandleToggleSubVariableConditionLogicalOperator, HandleUpdateCondition, HandleUpdateSubVariableCondition } from '../types' import { RiAddLine, RiDeleteBinLine, RiDraggable, } from '@remixicon/react' -import type { CaseItem, HandleAddCondition, HandleAddSubVariableCondition, HandleRemoveCondition, HandleToggleConditionLogicalOperator, HandleToggleSubVariableConditionLogicalOperator, HandleUpdateCondition, HandleUpdateSubVariableCondition, handleRemoveSubVariableCondition } from '../types' -import type { Node, NodeOutPutVar, Var } from '../../../types' -import { VarType } from '../../../types' -import { useGetAvailableVars } from '../../variable-assigner/hooks' -import { SUB_VARIABLES } from '../../constants' -import ConditionList from './condition-list' -import ConditionAdd from './condition-add' -import { cn } from '@/utils/classnames' +import { noop } from 'lodash-es' +import React, { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { ReactSortable } from 'react-sortablejs' import Button from '@/app/components/base/button' import { PortalSelect as Select } from '@/app/components/base/select' -import { noop } from 'lodash-es' +import { cn } from '@/utils/classnames' +import { VarType } from '../../../types' +import { SUB_VARIABLES } from '../../constants' +import { useGetAvailableVars } from '../../variable-assigner/hooks' +import ConditionAdd from './condition-add' +import ConditionList from './condition-list' type Props = { isSubVariable?: boolean @@ -86,8 +86,8 @@ const ConditionWrap: FC<Props> = ({ <ReactSortable list={cases.map(caseItem => ({ ...caseItem, id: caseItem.case_id }))} setList={handleSortCase} - handle='.handle' - ghostClass='bg-components-panel-bg' + handle=".handle" + ghostClass="bg-components-panel-bg" animation={150} disabled={readOnly || isSubVariable} > @@ -107,17 +107,22 @@ const ConditionWrap: FC<Props> = ({ <RiDraggable className={cn( 'handle absolute left-1 top-2 hidden h-3 w-3 cursor-pointer text-text-quaternary', casesLength > 1 && 'group-hover:block', - )} /> + )} + /> <div className={cn( 'absolute left-4 text-[13px] font-semibold leading-4 text-text-secondary', casesLength === 1 ? 'top-2.5' : 'top-1', - )}> + )} + > { index === 0 ? 'IF' : 'ELIF' } { casesLength > 1 && ( - <div className='text-[10px] font-medium text-text-tertiary'>CASE {index + 1}</div> + <div className="text-[10px] font-medium text-text-tertiary"> + CASE + {index + 1} + </div> ) } </div> @@ -126,7 +131,7 @@ const ConditionWrap: FC<Props> = ({ { !!item.conditions.length && ( - <div className='mb-2'> + <div className="mb-2"> <ConditionList disabled={readOnly} caseItem={item} @@ -156,47 +161,48 @@ const ConditionWrap: FC<Props> = ({ !item.conditions.length && !isSubVariable && 'mt-1', !item.conditions.length && isSubVariable && 'mt-2', !isSubVariable && ' pl-[60px]', - )}> + )} + > {isSubVariable ? ( - <Select - popupInnerClassName='w-[165px] max-h-none' - onSelect={value => handleAddSubVariableCondition?.(caseId!, conditionId!, value.value as string)} - items={subVarOptions} - value='' - renderTrigger={() => ( - <Button - size='small' - disabled={readOnly} - > - <RiAddLine className='mr-1 h-3.5 w-3.5' /> - {t('workflow.nodes.ifElse.addSubVariable')} - </Button> - )} - hideChecked - /> - ) + <Select + popupInnerClassName="w-[165px] max-h-none" + onSelect={value => handleAddSubVariableCondition?.(caseId!, conditionId!, value.value as string)} + items={subVarOptions} + value="" + renderTrigger={() => ( + <Button + size="small" + disabled={readOnly} + > + <RiAddLine className="mr-1 h-3.5 w-3.5" /> + {t('workflow.nodes.ifElse.addSubVariable')} + </Button> + )} + hideChecked + /> + ) : ( - <ConditionAdd - disabled={readOnly} - caseId={item.case_id} - variables={getAvailableVars(id, '', filterVar)} - onSelectVariable={handleAddCondition!} - /> - )} + <ConditionAdd + disabled={readOnly} + caseId={item.case_id} + variables={getAvailableVars(id, '', filterVar)} + onSelectVariable={handleAddCondition!} + /> + )} { ((index === 0 && casesLength > 1) || (index > 0)) && ( <Button - className='hover:bg-components-button-destructive-ghost-bg-hover hover:text-components-button-destructive-ghost-text' - size='small' - variant='ghost' + className="hover:bg-components-button-destructive-ghost-bg-hover hover:text-components-button-destructive-ghost-text" + size="small" + variant="ghost" disabled={readOnly} onClick={() => handleRemoveCase?.(item.case_id)} onMouseEnter={() => setWillDeleteCaseId(item.case_id)} onMouseLeave={() => setWillDeleteCaseId('')} > - <RiDeleteBinLine className='mr-1 h-3.5 w-3.5' /> + <RiDeleteBinLine className="mr-1 h-3.5 w-3.5" /> {t('common.operation.remove')} </Button> ) @@ -204,7 +210,7 @@ const ConditionWrap: FC<Props> = ({ </div> </div> {!isSubVariable && ( - <div className='mx-3 my-2 h-px bg-divider-subtle'></div> + <div className="mx-3 my-2 h-px bg-divider-subtle"></div> )} </div> )) @@ -212,11 +218,11 @@ const ConditionWrap: FC<Props> = ({ </ReactSortable> {(cases.length === 0) && ( <Button - size='small' + size="small" disabled={readOnly} onClick={() => handleAddSubVariableCondition?.(caseId!, conditionId!)} > - <RiAddLine className='mr-1 h-3.5 w-3.5' /> + <RiAddLine className="mr-1 h-3.5 w-3.5" /> {t('workflow.nodes.ifElse.addSubVariable')} </Button> )} diff --git a/web/app/components/workflow/nodes/if-else/default.ts b/web/app/components/workflow/nodes/if-else/default.ts index ca3aa0cd2b..155971b454 100644 --- a/web/app/components/workflow/nodes/if-else/default.ts +++ b/web/app/components/workflow/nodes/if-else/default.ts @@ -1,9 +1,12 @@ -import { type NodeDefault, VarType } from '../../types' -import { type IfElseNodeType, LogicalOperator } from './types' -import { isEmptyRelatedOperator } from './utils' -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' +import type { NodeDefault } from '../../types' +import type { IfElseNodeType } from './types' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' +import { VarType } from '../../types' +import { LogicalOperator } from './types' +import { isEmptyRelatedOperator } from './utils' + const i18nPrefix = 'workflow.errorMsg' const metaData = genNodeMetaData({ diff --git a/web/app/components/workflow/nodes/if-else/node.tsx b/web/app/components/workflow/nodes/if-else/node.tsx index e423e95a3f..41a7ec5d46 100644 --- a/web/app/components/workflow/nodes/if-else/node.tsx +++ b/web/app/components/workflow/nodes/if-else/node.tsx @@ -1,13 +1,14 @@ import type { FC } from 'react' +import type { NodeProps } from 'reactflow' +import type { Condition, IfElseNodeType } from './types' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import type { NodeProps } from 'reactflow' -import { NodeSourceHandle } from '../_base/components/node-handle' -import { isEmptyRelatedOperator } from './utils' -import type { Condition, IfElseNodeType } from './types' -import ConditionValue from './components/condition-value' -import ConditionFilesListValue from './components/condition-files-list-value' import { VarType } from '../../types' +import { NodeSourceHandle } from '../_base/components/node-handle' +import ConditionFilesListValue from './components/condition-files-list-value' +import ConditionValue from './components/condition-value' +import { isEmptyRelatedOperator } from './utils' + const i18nPrefix = 'workflow.nodes.ifElse' const IfElseNode: FC<NodeProps<IfElseNodeType>> = (props) => { @@ -34,50 +35,53 @@ const IfElseNode: FC<NodeProps<IfElseNodeType>> = (props) => { return (condition.varType === VarType.boolean || condition.varType === VarType.arrayBoolean) ? true : !!condition.value } }, []) - const conditionNotSet = (<div className='flex h-6 items-center space-x-1 rounded-md bg-workflow-block-parma-bg px-1 text-xs font-normal text-text-secondary'> - {t(`${i18nPrefix}.conditionNotSetup`)} - </div>) + const conditionNotSet = ( + <div className="flex h-6 items-center space-x-1 rounded-md bg-workflow-block-parma-bg px-1 text-xs font-normal text-text-secondary"> + {t(`${i18nPrefix}.conditionNotSetup`)} + </div> + ) return ( - <div className='px-3'> + <div className="px-3"> { cases.map((caseItem, index) => ( <div key={caseItem.case_id}> - <div className='relative flex h-6 items-center px-1'> - <div className='flex w-full items-center justify-between'> - <div className='text-[10px] font-semibold text-text-tertiary'> + <div className="relative flex h-6 items-center px-1"> + <div className="flex w-full items-center justify-between"> + <div className="text-[10px] font-semibold text-text-tertiary"> {casesLength > 1 && `CASE ${index + 1}`} </div> - <div className='text-[12px] font-semibold text-text-secondary'>{index === 0 ? 'IF' : 'ELIF'}</div> + <div className="text-[12px] font-semibold text-text-secondary">{index === 0 ? 'IF' : 'ELIF'}</div> </div> <NodeSourceHandle {...props} handleId={caseItem.case_id} - handleClassName='!top-1/2 !-right-[21px] !-translate-y-1/2' + handleClassName="!top-1/2 !-right-[21px] !-translate-y-1/2" /> </div> - <div className='space-y-0.5'> + <div className="space-y-0.5"> {caseItem.conditions.map((condition, i) => ( - <div key={condition.id} className='relative'> + <div key={condition.id} className="relative"> { checkIsConditionSet(condition) ? ( - (!isEmptyRelatedOperator(condition.comparison_operator!) && condition.sub_variable_condition) - ? ( - <ConditionFilesListValue condition={condition} /> - ) - : ( - <ConditionValue - variableSelector={condition.variable_selector!} - operator={condition.comparison_operator!} - value={condition.varType === VarType.boolean ? (!condition.value ? 'False' : condition.value) : condition.value} - /> - ) + (!isEmptyRelatedOperator(condition.comparison_operator!) && condition.sub_variable_condition) + ? ( + <ConditionFilesListValue condition={condition} /> + ) + : ( + <ConditionValue + variableSelector={condition.variable_selector!} + operator={condition.comparison_operator!} + value={condition.varType === VarType.boolean ? (!condition.value ? 'False' : condition.value) : condition.value} + /> + ) - ) - : conditionNotSet} + ) + : conditionNotSet + } {i !== caseItem.conditions.length - 1 && ( - <div className='absolute bottom-[-10px] right-1 z-10 text-[10px] font-medium uppercase leading-4 text-text-accent'>{t(`${i18nPrefix}.${caseItem.logical_operator}`)}</div> + <div className="absolute bottom-[-10px] right-1 z-10 text-[10px] font-medium uppercase leading-4 text-text-accent">{t(`${i18nPrefix}.${caseItem.logical_operator}`)}</div> )} </div> ))} @@ -85,12 +89,12 @@ const IfElseNode: FC<NodeProps<IfElseNodeType>> = (props) => { </div> )) } - <div className='relative flex h-6 items-center px-1'> - <div className='w-full text-right text-xs font-semibold text-text-secondary'>ELSE</div> + <div className="relative flex h-6 items-center px-1"> + <div className="w-full text-right text-xs font-semibold text-text-secondary">ELSE</div> <NodeSourceHandle {...props} - handleId='false' - handleClassName='!top-1/2 !-right-[21px] !-translate-y-1/2' + handleId="false" + handleClassName="!top-1/2 !-right-[21px] !-translate-y-1/2" /> </div> </div> diff --git a/web/app/components/workflow/nodes/if-else/panel.tsx b/web/app/components/workflow/nodes/if-else/panel.tsx index 4b74132b04..78e2406805 100644 --- a/web/app/components/workflow/nodes/if-else/panel.tsx +++ b/web/app/components/workflow/nodes/if-else/panel.tsx @@ -1,17 +1,17 @@ import type { FC } from 'react' +import type { IfElseNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' +import { + RiAddLine, +} from '@remixicon/react' import { memo, } from 'react' import { useTranslation } from 'react-i18next' -import { - RiAddLine, -} from '@remixicon/react' -import useConfig from './use-config' -import type { IfElseNodeType } from './types' -import ConditionWrap from './components/condition-wrap' import Button from '@/app/components/base/button' -import type { NodePanelProps } from '@/app/components/workflow/types' import Field from '@/app/components/workflow/nodes/_base/components/field' +import ConditionWrap from './components/condition-wrap' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.ifElse' @@ -42,7 +42,7 @@ const Panel: FC<NodePanelProps<IfElseNodeType>> = ({ const cases = inputs.cases || [] return ( - <div className='p-1'> + <div className="p-1"> <ConditionWrap nodeId={id} cases={cases} @@ -62,23 +62,23 @@ const Panel: FC<NodePanelProps<IfElseNodeType>> = ({ varsIsVarFileAttribute={varsIsVarFileAttribute} filterVar={filterVar} /> - <div className='px-4 py-2'> + <div className="px-4 py-2"> <Button - className='w-full' - variant='tertiary' + className="w-full" + variant="tertiary" onClick={() => handleAddCase()} disabled={readOnly} > - <RiAddLine className='mr-1 h-4 w-4' /> + <RiAddLine className="mr-1 h-4 w-4" /> ELIF </Button> </div> - <div className='mx-3 my-2 h-px bg-divider-subtle'></div> + <div className="mx-3 my-2 h-px bg-divider-subtle"></div> <Field title={t(`${i18nPrefix}.else`)} - className='px-4 py-2' + className="px-4 py-2" > - <div className='text-xs font-normal leading-[18px] text-text-tertiary'>{t(`${i18nPrefix}.elseDescription`)}</div> + <div className="text-xs font-normal leading-[18px] text-text-tertiary">{t(`${i18nPrefix}.elseDescription`)}</div> </Field> </div> ) diff --git a/web/app/components/workflow/nodes/if-else/use-config.ts b/web/app/components/workflow/nodes/if-else/use-config.ts index 205715b898..f7384e1d67 100644 --- a/web/app/components/workflow/nodes/if-else/use-config.ts +++ b/web/app/components/workflow/nodes/if-else/use-config.ts @@ -1,12 +1,6 @@ -import { useCallback, useMemo } from 'react' -import { produce } from 'immer' -import { v4 as uuid4 } from 'uuid' -import { useUpdateNodeInternals } from 'reactflow' import type { Var, } from '../../types' -import { VarType } from '../../types' -import { LogicalOperator } from './types' import type { CaseItem, HandleAddCondition, @@ -18,17 +12,23 @@ import type { HandleUpdateSubVariableCondition, IfElseNodeType, } from './types' -import { - branchNameCorrect, - getOperators, -} from './utils' -import useIsVarFileAttribute from './use-is-var-file-attribute' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { produce } from 'immer' +import { useCallback, useMemo } from 'react' +import { useUpdateNodeInternals } from 'reactflow' +import { v4 as uuid4 } from 'uuid' import { useEdgesInteractions, useNodesReadOnly, } from '@/app/components/workflow/hooks' import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { VarType } from '../../types' +import { LogicalOperator } from './types' +import useIsVarFileAttribute from './use-is-var-file-attribute' +import { + branchNameCorrect, + getOperators, +} from './utils' const useConfig = (id: string, payload: IfElseNodeType) => { const updateNodeInternals = useUpdateNodeInternals() diff --git a/web/app/components/workflow/nodes/if-else/use-is-var-file-attribute.ts b/web/app/components/workflow/nodes/if-else/use-is-var-file-attribute.ts index c0cf8cfefe..8bb2b602b8 100644 --- a/web/app/components/workflow/nodes/if-else/use-is-var-file-attribute.ts +++ b/web/app/components/workflow/nodes/if-else/use-is-var-file-attribute.ts @@ -1,7 +1,7 @@ -import { useStoreApi } from 'reactflow' -import { useMemo } from 'react' -import { useIsChatMode, useWorkflow, useWorkflowVariables } from '../../hooks' import type { ValueSelector } from '../../types' +import { useMemo } from 'react' +import { useStoreApi } from 'reactflow' +import { useIsChatMode, useWorkflow, useWorkflowVariables } from '../../hooks' import { VarType } from '../../types' type Params = { diff --git a/web/app/components/workflow/nodes/if-else/use-single-run-form-params.ts b/web/app/components/workflow/nodes/if-else/use-single-run-form-params.ts index 4f07f072cc..8e3d712ded 100644 --- a/web/app/components/workflow/nodes/if-else/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/if-else/use-single-run-form-params.ts @@ -1,11 +1,11 @@ import type { RefObject } from 'react' +import type { CaseItem, Condition, IfElseNodeType } from './types' import type { InputVar, ValueSelector, Variable } from '@/app/components/workflow/types' import { useCallback } from 'react' -import type { CaseItem, Condition, IfElseNodeType } from './types' type Params = { - id: string, - payload: IfElseNodeType, + id: string + payload: IfElseNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] @@ -94,7 +94,7 @@ const useSingleRunFormParams = ({ const existVarsKey: Record<string, boolean> = {} const uniqueVarInputs: InputVar[] = [] varInputs.forEach((input) => { - if(!input) + if (!input) return if (!existVarsKey[input.variable]) { existVarsKey[input.variable] = true @@ -126,7 +126,7 @@ const useSingleRunFormParams = ({ if (condition.variable_selector) vars.push(condition.variable_selector) - if(condition.sub_variable_condition && condition.sub_variable_condition.conditions?.length) + if (condition.sub_variable_condition && condition.sub_variable_condition.conditions?.length) vars.push(...getVarFromCaseItem(condition.sub_variable_condition)) return vars } diff --git a/web/app/components/workflow/nodes/if-else/utils.ts b/web/app/components/workflow/nodes/if-else/utils.ts index 37481c092c..167f26c913 100644 --- a/web/app/components/workflow/nodes/if-else/utils.ts +++ b/web/app/components/workflow/nodes/if-else/utils.ts @@ -1,15 +1,18 @@ -import { ComparisonOperator } from './types' -import { VarType } from '@/app/components/workflow/types' import type { Branch } from '@/app/components/workflow/types' +import { VarType } from '@/app/components/workflow/types' +import { ComparisonOperator } from './types' export const isEmptyRelatedOperator = (operator: ComparisonOperator) => { return [ComparisonOperator.empty, ComparisonOperator.notEmpty, ComparisonOperator.isNull, ComparisonOperator.isNotNull, ComparisonOperator.exists, ComparisonOperator.notExists].includes(operator) } const notTranslateKey = [ - ComparisonOperator.equal, ComparisonOperator.notEqual, - ComparisonOperator.largerThan, ComparisonOperator.largerThanOrEqual, - ComparisonOperator.lessThan, ComparisonOperator.lessThanOrEqual, + ComparisonOperator.equal, + ComparisonOperator.notEqual, + ComparisonOperator.largerThan, + ComparisonOperator.largerThanOrEqual, + ComparisonOperator.lessThan, + ComparisonOperator.lessThanOrEqual, ] export const isComparisonOperatorNeedTranslate = (operator?: ComparisonOperator) => { diff --git a/web/app/components/workflow/nodes/index.tsx b/web/app/components/workflow/nodes/index.tsx index ba880b398b..809c4af284 100644 --- a/web/app/components/workflow/nodes/index.tsx +++ b/web/app/components/workflow/nodes/index.tsx @@ -1,16 +1,16 @@ +import type { NodeProps } from 'reactflow' +import type { Node } from '../types' import { memo, useMemo, } from 'react' -import type { NodeProps } from 'reactflow' -import type { Node } from '../types' import { CUSTOM_NODE } from '../constants' +import BasePanel from './_base/components/workflow-panel' +import BaseNode from './_base/node' import { NodeComponentMap, PanelComponentMap, } from './components' -import BaseNode from './_base/node' -import BasePanel from './_base/components/workflow-panel' const CustomNode = (props: NodeProps) => { const nodeData = props.data diff --git a/web/app/components/workflow/nodes/iteration-start/default.ts b/web/app/components/workflow/nodes/iteration-start/default.ts index 5229229648..70b8b4c45e 100644 --- a/web/app/components/workflow/nodes/iteration-start/default.ts +++ b/web/app/components/workflow/nodes/iteration-start/default.ts @@ -1,7 +1,7 @@ import type { NodeDefault } from '../../types' import type { IterationStartNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' const metaData = genNodeMetaData({ sort: -1, diff --git a/web/app/components/workflow/nodes/iteration-start/index.tsx b/web/app/components/workflow/nodes/iteration-start/index.tsx index 6f880c95e1..36deac04e1 100644 --- a/web/app/components/workflow/nodes/iteration-start/index.tsx +++ b/web/app/components/workflow/nodes/iteration-start/index.tsx @@ -1,7 +1,7 @@ -import { memo } from 'react' -import { useTranslation } from 'react-i18next' import type { NodeProps } from 'reactflow' import { RiHome5Fill } from '@remixicon/react' +import { memo } from 'react' +import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import { NodeSourceHandle } from '@/app/components/workflow/nodes/_base/components/node-handle' @@ -9,17 +9,17 @@ const IterationStartNode = ({ id, data }: NodeProps) => { const { t } = useTranslation() return ( - <div className='nodrag group mt-1 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg shadow-xs'> + <div className="nodrag group mt-1 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg shadow-xs"> <Tooltip popupContent={t('workflow.blocks.iteration-start')} asChild={false}> - <div className='flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500'> - <RiHome5Fill className='h-3 w-3 text-text-primary-on-surface' /> + <div className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500"> + <RiHome5Fill className="h-3 w-3 text-text-primary-on-surface" /> </div> </Tooltip> <NodeSourceHandle id={id} data={data} - handleClassName='!top-1/2 !-right-[9px] !-translate-y-1/2' - handleId='source' + handleClassName="!top-1/2 !-right-[9px] !-translate-y-1/2" + handleId="source" /> </div> ) @@ -29,10 +29,10 @@ export const IterationStartNodeDumb = () => { const { t } = useTranslation() return ( - <div className='nodrag relative left-[17px] top-[21px] z-[11] flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg'> + <div className="nodrag relative left-[17px] top-[21px] z-[11] flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg"> <Tooltip popupContent={t('workflow.blocks.iteration-start')} asChild={false}> - <div className='flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500'> - <RiHome5Fill className='h-3 w-3 text-text-primary-on-surface' /> + <div className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500"> + <RiHome5Fill className="h-3 w-3 text-text-primary-on-surface" /> </div> </Tooltip> </div> diff --git a/web/app/components/workflow/nodes/iteration/add-block.tsx b/web/app/components/workflow/nodes/iteration/add-block.tsx index e838fe560b..fdd183da1e 100644 --- a/web/app/components/workflow/nodes/iteration/add-block.tsx +++ b/web/app/components/workflow/nodes/iteration/add-block.tsx @@ -1,25 +1,25 @@ +import type { IterationNodeType } from './types' +import type { + OnSelectBlock, +} from '@/app/components/workflow/types' +import { + RiAddLine, +} from '@remixicon/react' import { memo, useCallback, } from 'react' -import { - RiAddLine, -} from '@remixicon/react' import { useTranslation } from 'react-i18next' +import BlockSelector from '@/app/components/workflow/block-selector' +import { + BlockEnum, +} from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' import { useAvailableBlocks, useNodesInteractions, useNodesReadOnly, } from '../../hooks' -import type { IterationNodeType } from './types' -import { cn } from '@/utils/classnames' -import BlockSelector from '@/app/components/workflow/block-selector' -import type { - OnSelectBlock, -} from '@/app/components/workflow/types' -import { - BlockEnum, -} from '@/app/components/workflow/types' type AddBlockProps = { iterationNodeId: string @@ -52,24 +52,25 @@ const AddBlock = ({ 'system-sm-medium relative inline-flex h-8 cursor-pointer items-center rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-3 text-components-button-secondary-text shadow-xs backdrop-blur-[5px] hover:bg-components-button-secondary-bg-hover', `${nodesReadOnly && '!cursor-not-allowed bg-components-button-secondary-bg-disabled'}`, open && 'bg-components-button-secondary-bg-hover', - )}> - <RiAddLine className='mr-1 h-4 w-4' /> + )} + > + <RiAddLine className="mr-1 h-4 w-4" /> {t('workflow.common.addBlock')} </div> ) }, [nodesReadOnly, t]) return ( - <div className='absolute left-14 top-7 z-10 flex h-8 items-center'> - <div className='group/insert relative h-0.5 w-16 bg-gray-300'> - <div className='absolute right-0 top-1/2 h-2 w-0.5 -translate-y-1/2 bg-primary-500'></div> + <div className="absolute left-14 top-7 z-10 flex h-8 items-center"> + <div className="group/insert relative h-0.5 w-16 bg-gray-300"> + <div className="absolute right-0 top-1/2 h-2 w-0.5 -translate-y-1/2 bg-primary-500"></div> </div> <BlockSelector disabled={nodesReadOnly} onSelect={handleSelect} trigger={renderTriggerElement} - triggerInnerClassName='inline-flex' - popupClassName='!min-w-[256px]' + triggerInnerClassName="inline-flex" + popupClassName="!min-w-[256px]" availableBlocksTypes={availableNextBlocks} /> </div> diff --git a/web/app/components/workflow/nodes/iteration/default.ts b/web/app/components/workflow/nodes/iteration/default.ts index c375dbdcbf..b708926ba8 100644 --- a/web/app/components/workflow/nodes/iteration/default.ts +++ b/web/app/components/workflow/nodes/iteration/default.ts @@ -1,8 +1,9 @@ -import { BlockEnum, ErrorHandleMode } from '../../types' import type { NodeDefault } from '../../types' import type { IterationNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' +import { BlockEnum, ErrorHandleMode } from '../../types' + const i18nPrefix = 'workflow' const metaData = genNodeMetaData({ diff --git a/web/app/components/workflow/nodes/iteration/node.tsx b/web/app/components/workflow/nodes/iteration/node.tsx index 98ef98e7c4..2d40417df5 100644 --- a/web/app/components/workflow/nodes/iteration/node.tsx +++ b/web/app/components/workflow/nodes/iteration/node.tsx @@ -1,22 +1,22 @@ import type { FC } from 'react' +import type { IterationNodeType } from './types' +import type { NodeProps } from '@/app/components/workflow/types' import { memo, useEffect, useState, } from 'react' +import { useTranslation } from 'react-i18next' import { Background, useNodesInitialized, useViewport, } from 'reactflow' -import { useTranslation } from 'react-i18next' -import { IterationStartNodeDumb } from '../iteration-start' -import { useNodeIterationInteractions } from './use-interactions' -import type { IterationNodeType } from './types' -import AddBlock from './add-block' -import { cn } from '@/utils/classnames' -import type { NodeProps } from '@/app/components/workflow/types' import Toast from '@/app/components/base/toast' +import { cn } from '@/utils/classnames' +import { IterationStartNodeDumb } from '../iteration-start' +import AddBlock from './add-block' +import { useNodeIterationInteractions } from './use-interactions' const i18nPrefix = 'workflow.nodes.iteration' @@ -46,13 +46,14 @@ const Node: FC<NodeProps<IterationNodeType>> = ({ return ( <div className={cn( 'relative h-full min-h-[90px] w-full min-w-[240px] rounded-2xl bg-workflow-canvas-workflow-bg', - )}> + )} + > <Background id={`iteration-background-${id}`} - className='!z-0 rounded-2xl' + className="!z-0 rounded-2xl" gap={[14 / zoom, 14 / zoom]} size={2 / zoom} - color='var(--color-workflow-canvas-workflow-dot-color)' + color="var(--color-workflow-canvas-workflow-dot-color)" /> { data._isCandidate && ( diff --git a/web/app/components/workflow/nodes/iteration/panel.tsx b/web/app/components/workflow/nodes/iteration/panel.tsx index 63e0d5f8cd..5cffb356a2 100644 --- a/web/app/components/workflow/nodes/iteration/panel.tsx +++ b/web/app/components/workflow/nodes/iteration/panel.tsx @@ -1,18 +1,19 @@ import type { FC } from 'react' +import type { IterationNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import VarReferencePicker from '../_base/components/variable/var-reference-picker' -import Split from '../_base/components/split' -import { MIN_ITERATION_PARALLEL_NUM } from '../../constants' -import type { IterationNodeType } from './types' -import useConfig from './use-config' -import { ErrorHandleMode, type NodePanelProps } from '@/app/components/workflow/types' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import Switch from '@/app/components/base/switch' +import Input from '@/app/components/base/input' import Select from '@/app/components/base/select' import Slider from '@/app/components/base/slider' -import Input from '@/app/components/base/input' +import Switch from '@/app/components/base/switch' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import { ErrorHandleMode } from '@/app/components/workflow/types' import { MAX_PARALLEL_LIMIT } from '@/config' +import { MIN_ITERATION_PARALLEL_NUM } from '../../constants' +import Split from '../_base/components/split' +import VarReferencePicker from '../_base/components/variable/var-reference-picker' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.iteration' @@ -50,13 +51,13 @@ const Panel: FC<NodePanelProps<IterationNodeType>> = ({ } = useConfig(id, data) return ( - <div className='pb-2 pt-2'> - <div className='space-y-4 px-4 pb-4'> + <div className="pb-2 pt-2"> + <div className="space-y-4 px-4 pb-4"> <Field title={t(`${i18nPrefix}.input`)} required operations={( - <div className='system-2xs-medium-uppercase flex h-[18px] items-center rounded-[5px] border border-divider-deep px-1 capitalize text-text-tertiary'>Array</div> + <div className="system-2xs-medium-uppercase flex h-[18px] items-center rounded-[5px] border border-divider-deep px-1 capitalize text-text-tertiary">Array</div> )} > <VarReferencePicker @@ -70,12 +71,12 @@ const Panel: FC<NodePanelProps<IterationNodeType>> = ({ </Field> </div> <Split /> - <div className='mt-2 space-y-4 px-4 pb-4'> + <div className="mt-2 space-y-4 px-4 pb-4"> <Field title={t(`${i18nPrefix}.output`)} required operations={( - <div className='system-2xs-medium-uppercase flex h-[18px] items-center rounded-[5px] border border-divider-deep px-1 capitalize text-text-tertiary'>Array</div> + <div className="system-2xs-medium-uppercase flex h-[18px] items-center rounded-[5px] border border-divider-deep px-1 capitalize text-text-tertiary">Array</div> )} > <VarReferencePicker @@ -89,42 +90,44 @@ const Panel: FC<NodePanelProps<IterationNodeType>> = ({ /> </Field> </div> - <div className='px-4 pb-2'> - <Field title={t(`${i18nPrefix}.parallelMode`)} tooltip={<div className='w-[230px]'>{t(`${i18nPrefix}.parallelPanelDesc`)}</div>} inline> + <div className="px-4 pb-2"> + <Field title={t(`${i18nPrefix}.parallelMode`)} tooltip={<div className="w-[230px]">{t(`${i18nPrefix}.parallelPanelDesc`)}</div>} inline> <Switch defaultValue={inputs.is_parallel} onChange={changeParallel} /> </Field> </div> { - inputs.is_parallel && (<div className='px-4 pb-2'> - <Field title={t(`${i18nPrefix}.MaxParallelismTitle`)} isSubTitle tooltip={<div className='w-[230px]'>{t(`${i18nPrefix}.MaxParallelismDesc`)}</div>}> - <div className='row flex'> - <Input type='number' wrapperClassName='w-18 mr-4 ' max={MAX_PARALLEL_LIMIT} min={MIN_ITERATION_PARALLEL_NUM} value={inputs.parallel_nums} onChange={(e) => { changeParallelNums(Number(e.target.value)) }} /> - <Slider - value={inputs.parallel_nums} - onChange={changeParallelNums} - max={MAX_PARALLEL_LIMIT} - min={MIN_ITERATION_PARALLEL_NUM} - className=' mt-4 flex-1 shrink-0' - /> - </div> + inputs.is_parallel && ( + <div className="px-4 pb-2"> + <Field title={t(`${i18nPrefix}.MaxParallelismTitle`)} isSubTitle tooltip={<div className="w-[230px]">{t(`${i18nPrefix}.MaxParallelismDesc`)}</div>}> + <div className="row flex"> + <Input type="number" wrapperClassName="w-18 mr-4 " max={MAX_PARALLEL_LIMIT} min={MIN_ITERATION_PARALLEL_NUM} value={inputs.parallel_nums} onChange={(e) => { changeParallelNums(Number(e.target.value)) }} /> + <Slider + value={inputs.parallel_nums} + onChange={changeParallelNums} + max={MAX_PARALLEL_LIMIT} + min={MIN_ITERATION_PARALLEL_NUM} + className=" mt-4 flex-1 shrink-0" + /> + </div> - </Field> - </div>) + </Field> + </div> + ) } <Split /> - <div className='px-4 py-2'> - <Field title={t(`${i18nPrefix}.errorResponseMethod`)} > + <div className="px-4 py-2"> + <Field title={t(`${i18nPrefix}.errorResponseMethod`)}> <Select items={responseMethod} defaultValue={inputs.error_handle_mode} onSelect={changeErrorResponseMode} allowSearch={false} /> </Field> </div> <Split /> - <div className='px-4 py-2'> + <div className="px-4 py-2"> <Field title={t(`${i18nPrefix}.flattenOutput`)} - tooltip={<div className='w-[230px]'>{t(`${i18nPrefix}.flattenOutputDesc`)}</div>} + tooltip={<div className="w-[230px]">{t(`${i18nPrefix}.flattenOutputDesc`)}</div>} inline > <Switch defaultValue={inputs.flatten_output} onChange={changeFlattenOutput} /> diff --git a/web/app/components/workflow/nodes/iteration/use-config.ts b/web/app/components/workflow/nodes/iteration/use-config.ts index 2e47bb3740..50cee67f81 100644 --- a/web/app/components/workflow/nodes/iteration/use-config.ts +++ b/web/app/components/workflow/nodes/iteration/use-config.ts @@ -1,26 +1,26 @@ -import { useCallback } from 'react' -import { produce } from 'immer' -import { - useIsChatMode, - useNodesReadOnly, - useWorkflow, -} from '../../hooks' -import { VarType } from '../../types' import type { ErrorHandleMode, ValueSelector, Var } from '../../types' -import useNodeCrud from '../_base/hooks/use-node-crud' import type { IterationNodeType } from './types' -import { toNodeOutputVars } from '../_base/components/variable/utils' -import type { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' import type { Item } from '@/app/components/base/select' -import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud' +import type { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' +import { produce } from 'immer' import { isEqual } from 'lodash-es' -import { useStore } from '../../store' +import { useCallback } from 'react' import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTools, } from '@/service/use-tools' +import { + useIsChatMode, + useNodesReadOnly, + useWorkflow, +} from '../../hooks' +import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud' +import { useStore } from '../../store' +import { VarType } from '../../types' +import { toNodeOutputVars } from '../_base/components/variable/utils' +import useNodeCrud from '../_base/hooks/use-node-crud' const useConfig = (id: string, payload: IterationNodeType) => { const { diff --git a/web/app/components/workflow/nodes/iteration/use-interactions.ts b/web/app/components/workflow/nodes/iteration/use-interactions.ts index 3bbbaf252f..c6fddff5ad 100644 --- a/web/app/components/workflow/nodes/iteration/use-interactions.ts +++ b/web/app/components/workflow/nodes/iteration/use-interactions.ts @@ -1,21 +1,21 @@ -import { useCallback } from 'react' -import { produce } from 'immer' -import { useTranslation } from 'react-i18next' -import { useStoreApi } from 'reactflow' import type { BlockEnum, ChildNodeTypeCount, Node, } from '../../types' +import { produce } from 'immer' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import { useStoreApi } from 'reactflow' +import { useNodesMetaData } from '@/app/components/workflow/hooks' +import { + ITERATION_PADDING, +} from '../../constants' import { generateNewNode, getNodeCustomTypeByNodeDataType, } from '../../utils' -import { - ITERATION_PADDING, -} from '../../constants' import { CUSTOM_ITERATION_START_NODE } from '../iteration-start/constants' -import { useNodesMetaData } from '@/app/components/workflow/hooks' export const useNodeIterationInteractions = () => { const { t } = useTranslation() @@ -78,7 +78,7 @@ export const useNodeIterationInteractions = () => { const { getNodes } = store.getState() const nodes = getNodes() - const restrictPosition: { x?: number; y?: number } = { x: undefined, y: undefined } + const restrictPosition: { x?: number, y?: number } = { x: undefined, y: undefined } if (node.data.isInIteration) { const parentNode = nodes.find(n => n.id === node.parentId) @@ -121,7 +121,7 @@ export const useNodeIterationInteractions = () => { const childNodeType = child.data.type as BlockEnum const nodesWithSameType = nodes.filter(node => node.data.type === childNodeType) - if(!childNodeTypeCount[childNodeType]) + if (!childNodeTypeCount[childNodeType]) childNodeTypeCount[childNodeType] = nodesWithSameType.length + 1 else childNodeTypeCount[childNodeType] = childNodeTypeCount[childNodeType] + 1 diff --git a/web/app/components/workflow/nodes/iteration/use-single-run-form-params.ts b/web/app/components/workflow/nodes/iteration/use-single-run-form-params.ts index ba840a472d..fec99d023f 100644 --- a/web/app/components/workflow/nodes/iteration/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/iteration/use-single-run-form-params.ts @@ -1,20 +1,20 @@ import type { RefObject } from 'react' -import type { InputVar, ValueSelector, Variable } from '@/app/components/workflow/types' -import { useCallback, useMemo } from 'react' import type { IterationNodeType } from './types' +import type { InputVar, ValueSelector, Variable } from '@/app/components/workflow/types' +import type { NodeTracing } from '@/types/workflow' +import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' +import formatTracing from '@/app/components/workflow/run/utils/format-log' +import { InputVarType, VarType } from '@/app/components/workflow/types' +import { VALUE_SELECTOR_DELIMITER as DELIMITER } from '@/config' import { useIsNodeInIteration, useWorkflow } from '../../hooks' import { getNodeInfoById, getNodeUsedVarPassToServerKey, getNodeUsedVars, isSystemVar } from '../_base/components/variable/utils' -import { InputVarType, VarType } from '@/app/components/workflow/types' -import formatTracing from '@/app/components/workflow/run/utils/format-log' -import type { NodeTracing } from '@/types/workflow' -import { VALUE_SELECTOR_DELIMITER as DELIMITER } from '@/config' const i18nPrefix = 'workflow.nodes.iteration' type Params = { - id: string, - payload: IterationNodeType, + id: string + payload: IterationNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] @@ -138,7 +138,7 @@ const useSingleRunFormParams = ({ return [payload.iterator_selector] } const getDependentVar = (variable: string) => { - if(variable === iteratorInputKey) + if (variable === iteratorInputKey) return payload.iterator_selector } diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/hooks.tsx b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/hooks.tsx index 876c91862f..4c6ff96d7d 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/hooks.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/hooks.tsx @@ -1,3 +1,4 @@ +import type { Option } from './type' import { useTranslation } from 'react-i18next' import { GeneralChunk, @@ -6,7 +7,6 @@ import { } from '@/app/components/base/icons/src/vender/knowledge' import { cn } from '@/utils/classnames' import { ChunkStructureEnum } from '../../types' -import type { Option } from './type' export const useChunkStructure = () => { const { t } = useTranslation() @@ -17,7 +17,8 @@ export const useChunkStructure = () => { className={cn( 'h-[18px] w-[18px] text-text-tertiary group-hover:text-util-colors-indigo-indigo-600', isActive && 'text-util-colors-indigo-indigo-600', - )} /> + )} + /> ), title: t('datasetCreation.stepTwo.general'), description: t('datasetCreation.stepTwo.generalTip'), 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 60aa3d5590..b9d105ed6e 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,13 +1,13 @@ +import type { ChunkStructureEnum } from '../../types' +import { RiAddLine } from '@remixicon/react' import { memo } from 'react' import { useTranslation } from 'react-i18next' -import { RiAddLine } from '@remixicon/react' -import { Field } from '@/app/components/workflow/nodes/_base/components/layout' -import type { ChunkStructureEnum } from '../../types' -import OptionCard from '../option-card' -import Selector from './selector' -import { useChunkStructure } from './hooks' import Button from '@/app/components/base/button' +import { Field } from '@/app/components/workflow/nodes/_base/components/layout' +import OptionCard from '../option-card' +import { useChunkStructure } from './hooks' import Instruction from './instruction' +import Selector from './selector' type ChunkStructureProps = { chunkStructure?: ChunkStructureEnum @@ -59,15 +59,15 @@ const ChunkStructure = ({ readonly={readonly} trigger={( <Button - className='w-full' - variant='secondary-accent' + className="w-full" + variant="secondary-accent" > - <RiAddLine className='mr-1 h-4 w-4' /> + <RiAddLine className="mr-1 h-4 w-4" /> {t('workflow.nodes.knowledgeBase.chooseChunkStructure')} </Button> )} /> - <Instruction className='mt-2' /> + <Instruction className="mt-2" /> </> ) } diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/index.tsx b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/index.tsx index b621eaf04d..82af5f8d2f 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/index.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/index.tsx @@ -1,9 +1,9 @@ import React from 'react' -import { AddChunks } from '@/app/components/base/icons/src/vender/knowledge' -import Line from './line' -import { cn } from '@/utils/classnames' import { useTranslation } from 'react-i18next' +import { AddChunks } from '@/app/components/base/icons/src/vender/knowledge' import { useDocLink } from '@/context/i18n' +import { cn } from '@/utils/classnames' +import Line from './line' type InstructionProps = { className?: string @@ -17,24 +17,24 @@ const Instruction = ({ return ( <div className={cn('flex flex-col gap-y-2 overflow-hidden rounded-[10px] bg-workflow-process-bg p-4', className)}> - <div className='relative flex size-10 items-center justify-center rounded-[10px] border-[0.5px] border-components-card-border bg-components-card-bg shadow-lg backdrop-blur-[5px]'> - <AddChunks className='size-5 text-text-accent' /> - <Line className='absolute -left-px bottom-[-76px]' type='vertical' /> - <Line className='absolute -right-px bottom-[-76px]' type='vertical' /> - <Line className='absolute -top-px right-[-184px]' type='horizontal' /> - <Line className='absolute -bottom-px right-[-184px]' type='horizontal' /> + <div className="relative flex size-10 items-center justify-center rounded-[10px] border-[0.5px] border-components-card-border bg-components-card-bg shadow-lg backdrop-blur-[5px]"> + <AddChunks className="size-5 text-text-accent" /> + <Line className="absolute -left-px bottom-[-76px]" type="vertical" /> + <Line className="absolute -right-px bottom-[-76px]" type="vertical" /> + <Line className="absolute -top-px right-[-184px]" type="horizontal" /> + <Line className="absolute -bottom-px right-[-184px]" type="horizontal" /> </div> - <div className='flex flex-col gap-y-1'> - <div className='system-sm-medium text-text-secondary'> + <div className="flex flex-col gap-y-1"> + <div className="system-sm-medium text-text-secondary"> {t('workflow.nodes.knowledgeBase.chunkStructureTip.title')} </div> - <div className='system-xs-regular'> - <p className='text-text-tertiary'>{t('workflow.nodes.knowledgeBase.chunkStructureTip.message')}</p> + <div className="system-xs-regular"> + <p className="text-text-tertiary">{t('workflow.nodes.knowledgeBase.chunkStructureTip.message')}</p> <a href={docLink('/guides/knowledge-base/create-knowledge-and-upload-documents/chunking-and-cleaning-text')} - target='_blank' - rel='noopener noreferrer' - className='text-text-accent' + target="_blank" + rel="noopener noreferrer" + className="text-text-accent" > {t('workflow.nodes.knowledgeBase.chunkStructureTip.learnMore')} </a> diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/line.tsx b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/line.tsx index 1177130d2b..a2a3835be6 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/line.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/line.tsx @@ -11,13 +11,13 @@ const Line = ({ }: LineProps) => { if (type === 'vertical') { return ( - <svg xmlns='http://www.w3.org/2000/svg' width='2' height='132' viewBox='0 0 2 132' fill='none' className={className}> - <path d='M1 0L1 132' stroke='url(#paint0_linear_10882_18766)' /> + <svg xmlns="http://www.w3.org/2000/svg" width="2" height="132" viewBox="0 0 2 132" fill="none" className={className}> + <path d="M1 0L1 132" stroke="url(#paint0_linear_10882_18766)" /> <defs> - <linearGradient id='paint0_linear_10882_18766' x1='-7.99584' y1='132' x2='-7.96108' y2='6.4974e-07' gradientUnits='userSpaceOnUse'> - <stop stopColor='var(--color-background-gradient-mask-transparent)' /> - <stop offset='0.877606' stopColor='var(--color-divider-subtle)' /> - <stop offset='1' stopColor='var(--color-background-gradient-mask-transparent)' /> + <linearGradient id="paint0_linear_10882_18766" x1="-7.99584" y1="132" x2="-7.96108" y2="6.4974e-07" gradientUnits="userSpaceOnUse"> + <stop stopColor="var(--color-background-gradient-mask-transparent)" /> + <stop offset="0.877606" stopColor="var(--color-divider-subtle)" /> + <stop offset="1" stopColor="var(--color-background-gradient-mask-transparent)" /> </linearGradient> </defs> </svg> @@ -25,13 +25,13 @@ const Line = ({ } return ( - <svg xmlns='http://www.w3.org/2000/svg' width='240' height='2' viewBox='0 0 240 2' fill='none' className={className}> - <path d='M0 1H240' stroke='url(#paint0_linear_10882_18763)' /> + <svg xmlns="http://www.w3.org/2000/svg" width="240" height="2" viewBox="0 0 240 2" fill="none" className={className}> + <path d="M0 1H240" stroke="url(#paint0_linear_10882_18763)" /> <defs> - <linearGradient id='paint0_linear_10882_18763' x1='240' y1='9.99584' x2='3.95539e-05' y2='9.88094' gradientUnits='userSpaceOnUse'> - <stop stopColor='var(--color-background-gradient-mask-transparent)' /> - <stop offset='0.9031' stopColor='var(--color-divider-subtle)' /> - <stop offset='1' stopColor='var(--color-background-gradient-mask-transparent)' /> + <linearGradient id="paint0_linear_10882_18763" x1="240" y1="9.99584" x2="3.95539e-05" y2="9.88094" gradientUnits="userSpaceOnUse"> + <stop stopColor="var(--color-background-gradient-mask-transparent)" /> + <stop offset="0.9031" stopColor="var(--color-divider-subtle)" /> + <stop offset="1" stopColor="var(--color-background-gradient-mask-transparent)" /> </linearGradient> </defs> </svg> 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 97a5740fce..a349e16aea 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,15 +1,15 @@ import type { ReactNode } from 'react' +import type { ChunkStructureEnum } from '../../types' +import type { Option } from './type' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import Button from '@/app/components/base/button' -import type { ChunkStructureEnum } from '../../types' import OptionCard from '../option-card' -import type { Option } from './type' type SelectorProps = { options: Option[] @@ -35,7 +35,7 @@ const Selector = ({ return ( <PortalToFollowElem - placement='bottom-end' + placement="bottom-end" offset={{ mainAxis: 0, crossAxis: -8, @@ -54,20 +54,20 @@ const Selector = ({ { trigger || ( <Button - size='small' - variant='ghost-accent' + size="small" + variant="ghost-accent" > {t('workflow.panel.change')} </Button> ) } </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-10'> - <div className='w-[404px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-xl backdrop-blur-[5px]'> - <div className='system-sm-semibold px-3 pt-3.5 text-text-primary'> + <PortalToFollowElemContent className="z-10"> + <div className="w-[404px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-xl backdrop-blur-[5px]"> + <div className="system-sm-semibold px-3 pt-3.5 text-text-primary"> {t('workflow.nodes.knowledgeBase.changeChunkStructure')} </div> - <div className='space-y-1 p-3 pt-2'> + <div className="space-y-1 p-3 pt-2"> { options.map(option => ( <OptionCard @@ -80,7 +80,8 @@ const Selector = ({ readonly={readonly} onClick={handleSelect} effectColor={option.effectColor} - ></OptionCard> + > + </OptionCard> )) } </div> diff --git a/web/app/components/workflow/nodes/knowledge-base/components/embedding-model.tsx b/web/app/components/workflow/nodes/knowledge-base/components/embedding-model.tsx index 7709fb49d7..1625f9d332 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/embedding-model.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/embedding-model.tsx @@ -1,14 +1,14 @@ +import type { DefaultModel } from '@/app/components/header/account-setting/model-provider-page/declarations' import { memo, useCallback, useMemo, } from 'react' import { useTranslation } from 'react-i18next' -import { Field } from '@/app/components/workflow/nodes/_base/components/layout' -import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' -import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' -import type { DefaultModel } from '@/app/components/header/account-setting/model-provider-page/declarations' 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 ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' +import { Field } from '@/app/components/workflow/nodes/_base/components/layout' type EmbeddingModelProps = { embeddingModel?: string 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 cf93c4191d..916d25afcb 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,23 +1,23 @@ +import { RiQuestionLine } from '@remixicon/react' import { memo, useCallback, } from 'react' import { useTranslation } from 'react-i18next' -import { RiQuestionLine } from '@remixicon/react' import { Economic, HighQuality, } from '@/app/components/base/icons/src/vender/knowledge' -import Tooltip from '@/app/components/base/tooltip' -import Slider from '@/app/components/base/slider' import Input from '@/app/components/base/input' +import Slider from '@/app/components/base/slider' +import Tooltip from '@/app/components/base/tooltip' import { Field } from '@/app/components/workflow/nodes/_base/components/layout' -import OptionCard from './option-card' import { cn } from '@/utils/classnames' import { ChunkStructureEnum, IndexMethodEnum, } from '../types' +import OptionCard from './option-card' type IndexMethodProps = { chunkStructure: ChunkStructureEnum @@ -55,64 +55,65 @@ const IndexMethod = ({ title: t('datasetCreation.stepTwo.indexMode'), }} > - <div className='space-y-1'> + <div className="space-y-1"> <OptionCard<IndexMethodEnum> id={IndexMethodEnum.QUALIFIED} selectedId={indexMethod} - icon={ + icon={( <HighQuality className={cn( 'h-[15px] w-[15px] text-text-tertiary group-hover:text-util-colors-orange-orange-500', isHighQuality && 'text-util-colors-orange-orange-500', )} /> - } + )} title={t('datasetCreation.stepTwo.qualified')} description={t('datasetSettings.form.indexMethodHighQualityTip')} onClick={handleIndexMethodChange} isRecommended - effectColor='orange' - ></OptionCard> + effectColor="orange" + > + </OptionCard> { chunkStructure === ChunkStructureEnum.general && ( <OptionCard id={IndexMethodEnum.ECONOMICAL} selectedId={indexMethod} - icon={ + icon={( <Economic className={cn( 'h-[15px] w-[15px] text-text-tertiary group-hover:text-util-colors-indigo-indigo-500', isEconomy && 'text-util-colors-indigo-indigo-500', )} /> - } + )} title={t('datasetSettings.form.indexMethodEconomy')} description={t('datasetSettings.form.indexMethodEconomyTip', { count: keywordNumber })} onClick={handleIndexMethodChange} - effectColor='blue' + effectColor="blue" > - <div className='flex items-center'> - <div className='flex grow items-center'> - <div className='system-xs-medium truncate text-text-secondary'> + <div className="flex items-center"> + <div className="flex grow items-center"> + <div className="system-xs-medium truncate text-text-secondary"> {t('datasetSettings.form.numberOfKeywords')} </div> <Tooltip - popupContent='number of keywords' + popupContent="number of keywords" > - <RiQuestionLine className='ml-0.5 h-3.5 w-3.5 text-text-quaternary' /> + <RiQuestionLine className="ml-0.5 h-3.5 w-3.5 text-text-quaternary" /> </Tooltip> </div> <Slider disabled={readonly} - className='mr-3 w-24 shrink-0' + className="mr-3 w-24 shrink-0" value={keywordNumber} onChange={onKeywordNumberChange} /> <Input disabled={readonly} - className='shrink-0' - wrapperClassName='shrink-0 w-[72px]' - type='number' + className="shrink-0" + wrapperClassName="shrink-0 w-[72px]" + type="number" value={keywordNumber} onChange={handleInputChange} /> diff --git a/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx b/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx index fed53f798a..0244d1ebc6 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx @@ -4,7 +4,6 @@ import { useMemo, } from 'react' import { useTranslation } from 'react-i18next' -import { cn } from '@/utils/classnames' import Badge from '@/app/components/base/badge' import { OptionCardEffectBlue, @@ -14,6 +13,7 @@ import { OptionCardEffectTeal, } from '@/app/components/base/icons/src/public/knowledge' import { ArrowShape } from '@/app/components/base/icons/src/vender/knowledge' +import { cn } from '@/utils/classnames' const HEADER_EFFECT_MAP: Record<string, ReactNode> = { 'blue': <OptionCardEffectBlue />, @@ -68,7 +68,8 @@ const OptionCard = memo(({ 'absolute left-[-2px] top-[-2px] hidden h-14 w-14 rounded-full', 'group-hover:block', isActive && 'block', - )}> + )} + > {HEADER_EFFECT_MAP[effectColor]} </div> ) @@ -95,22 +96,23 @@ const OptionCard = memo(({ <div className={cn( 'relative flex rounded-t-xl p-2', className && (typeof className === 'function' ? className(isActive) : className), - )}> + )} + > {effectElement} { icon && ( - <div className='mr-1 flex h-[18px] w-[18px] shrink-0 items-center justify-center'> + <div className="mr-1 flex h-[18px] w-[18px] shrink-0 items-center justify-center"> {typeof icon === 'function' ? icon(isActive) : icon} </div> ) } - <div className='grow py-1 pt-[1px]'> - <div className='flex items-center'> - <div className='system-sm-medium flex grow items-center text-text-secondary'> + <div className="grow py-1 pt-[1px]"> + <div className="flex items-center"> + <div className="system-sm-medium flex grow items-center text-text-secondary"> {title} { isRecommended && ( - <Badge className='ml-1 h-4 border-text-accent-secondary text-text-accent-secondary'> + <Badge className="ml-1 h-4 border-text-accent-secondary text-text-accent-secondary"> {t('datasetCreation.stepTwo.recommend')} </Badge> ) @@ -121,14 +123,15 @@ const OptionCard = memo(({ <div className={cn( 'ml-2 h-4 w-4 shrink-0 rounded-full border border-components-radio-border bg-components-radio-bg', isActive && 'border-[5px] border-components-radio-border-checked', - )}> + )} + > </div> ) } </div> { description && ( - <div className='system-xs-regular mt-1 text-text-tertiary'> + <div className="system-xs-regular mt-1 text-text-tertiary"> {description} </div> ) @@ -137,8 +140,8 @@ const OptionCard = memo(({ </div> { children && isActive && ( - <div className='relative rounded-b-xl bg-components-panel-bg p-3'> - <ArrowShape className='absolute left-[14px] top-[-11px] h-4 w-4 text-components-panel-bg' /> + <div className="relative rounded-b-xl bg-components-panel-bg p-3"> + <ArrowShape className="absolute left-[14px] top-[-11px] h-4 w-4 text-components-panel-bg" /> {children} </div> ) diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/hooks.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/hooks.tsx index 4bcfc4effd..482df34059 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/hooks.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/hooks.tsx @@ -1,3 +1,7 @@ +import type { + HybridSearchModeOption, + Option, +} from './type' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { @@ -10,10 +14,6 @@ import { IndexMethodEnum, RetrievalSearchMethodEnum, } from '../../types' -import type { - HybridSearchModeOption, - Option, -} from './type' export const useRetrievalSetting = (indexMethod?: IndexMethodEnum) => { const { t } = useTranslation() @@ -70,13 +70,15 @@ export const useRetrievalSetting = (indexMethod?: IndexMethodEnum) => { }, [t]) return useMemo(() => ({ - options: indexMethod === IndexMethodEnum.ECONOMICAL ? [ - InvertedIndexOption, - ] : [ - VectorSearchOption, - FullTextSearchOption, - HybridSearchOption, - ], + options: indexMethod === IndexMethodEnum.ECONOMICAL + ? [ + InvertedIndexOption, + ] + : [ + VectorSearchOption, + FullTextSearchOption, + HybridSearchOption, + ], hybridSearchModeOptions: [ WeightedScoreModeOption, RerankModelModeOption, diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/index.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/index.tsx index 5d6b8b8c19..74ef1f8e58 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/index.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/index.tsx @@ -1,19 +1,17 @@ +import type { + HybridSearchModeEnum, + IndexMethodEnum, + RetrievalSearchMethodEnum, + WeightedScore, +} from '../../types' +import type { RerankingModelSelectorProps } from './reranking-model-selector' +import type { TopKAndScoreThresholdProps } from './top-k-and-score-threshold' import { memo, } from 'react' import { useTranslation } from 'react-i18next' import { Field } from '@/app/components/workflow/nodes/_base/components/layout' -import type { - HybridSearchModeEnum, - RetrievalSearchMethodEnum, -} from '../../types' -import type { - IndexMethodEnum, - WeightedScore, -} from '../../types' import { useRetrievalSetting } from './hooks' -import type { TopKAndScoreThresholdProps } from './top-k-and-score-threshold' -import type { RerankingModelSelectorProps } from './reranking-model-selector' import SearchMethodOption from './search-method-option' type RetrievalSettingProps = { @@ -62,14 +60,15 @@ const RetrievalSetting = ({ fieldTitleProps={{ title: t('datasetSettings.form.retrievalSetting.title'), subTitle: ( - <div className='body-xs-regular flex items-center text-text-tertiary'> - <a target='_blank' rel='noopener noreferrer' href='https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings' className='text-text-accent'>{t('datasetSettings.form.retrievalSetting.learnMore')}</a> -  {t('workflow.nodes.knowledgeBase.aboutRetrieval')} + <div className="body-xs-regular flex items-center text-text-tertiary"> + <a target="_blank" rel="noopener noreferrer" href="https://docs.dify.ai/guides/knowledge-base/create-knowledge-and-upload-documents#id-4-retrieval-settings" className="text-text-accent">{t('datasetSettings.form.retrievalSetting.learnMore')}</a> +   + {t('workflow.nodes.knowledgeBase.aboutRetrieval')} </div> ), }} > - <div className='space-y-1'> + <div className="space-y-1"> { options.map(option => ( <SearchMethodOption diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/reranking-model-selector.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/reranking-model-selector.tsx index 19566362a1..3e0bea2b28 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/reranking-model-selector.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/reranking-model-selector.tsx @@ -1,12 +1,12 @@ +import type { RerankingModel } from '../../types' +import type { DefaultModel } from '@/app/components/header/account-setting/model-provider-page/declarations' import { memo, useMemo, } from 'react' -import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' -import { useModelListAndDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' -import type { DefaultModel } from '@/app/components/header/account-setting/model-provider-page/declarations' import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import type { RerankingModel } from '../../types' +import { useModelListAndDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' +import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' export type RerankingModelSelectorProps = { rerankingModel?: RerankingModel 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 70996ddd57..8b85bb576e 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 @@ -1,31 +1,31 @@ +import type { + WeightedScore, +} from '../../types' +import type { RerankingModelSelectorProps } from './reranking-model-selector' +import type { TopKAndScoreThresholdProps } from './top-k-and-score-threshold' +import type { + HybridSearchModeOption, + Option, +} from './type' import { memo, useCallback, useMemo, } from 'react' import { useTranslation } from 'react-i18next' -import { cn } from '@/utils/classnames' import WeightedScoreComponent from '@/app/components/app/configuration/dataset-config/params-config/weighted-score' -import { DEFAULT_WEIGHTED_SCORE } from '@/models/datasets' +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 { cn } from '@/utils/classnames' import { HybridSearchModeEnum, RetrievalSearchMethodEnum, } from '../../types' -import type { - WeightedScore, -} from '../../types' import OptionCard from '../option-card' -import type { - HybridSearchModeOption, - Option, -} from './type' -import type { TopKAndScoreThresholdProps } from './top-k-and-score-threshold' -import TopKAndScoreThreshold from './top-k-and-score-threshold' -import type { RerankingModelSelectorProps } from './reranking-model-selector' import RerankingModelSelector from './reranking-model-selector' -import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' +import TopKAndScoreThreshold from './top-k-and-score-threshold' type SearchMethodOptionProps = { readonly?: boolean @@ -128,10 +128,10 @@ const SearchMethodOption = ({ onClick={onRetrievalSearchMethodChange} readonly={readonly} > - <div className='space-y-3'> + <div className="space-y-3"> { isHybridSearch && ( - <div className='space-y-1'> + <div className="space-y-1"> { hybridSearchModeOptions.map(hybridOption => ( <OptionCard @@ -141,7 +141,7 @@ const SearchMethodOption = ({ enableHighlightBorder={false} enableRadio wrapperClassName={hybridSearchModeWrapperClassName} - className='p-3' + className="p-3" title={hybridOption.title} description={hybridOption.description} onClick={onHybridSearchModeChange} @@ -166,16 +166,16 @@ const SearchMethodOption = ({ <div> { showRerankModelSelectorSwitch && ( - <div className='system-sm-semibold mb-1 flex items-center text-text-secondary'> + <div className="system-sm-semibold mb-1 flex items-center text-text-secondary"> <Switch - className='mr-1' + className="mr-1" defaultValue={rerankingModelEnabled} onChange={onRerankingModelEnabledChange} disabled={readonly} /> {t('common.modelProvider.rerankModel.key')} <Tooltip - triggerClassName='ml-0.5 shrink-0 w-3.5 h-3.5' + triggerClassName="ml-0.5 shrink-0 w-3.5 h-3.5" popupContent={t('common.modelProvider.rerankModel.tip')} /> </div> @@ -187,12 +187,12 @@ const SearchMethodOption = ({ readonly={readonly} /> {showMultiModalTip && ( - <div className='mt-2 flex h-10 items-center gap-x-0.5 overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-2 shadow-xs backdrop-blur-[5px]'> - <div className='absolute bottom-0 left-0 right-0 top-0 bg-dataset-warning-message-bg opacity-40' /> - <div className='p-1'> - <AlertTriangle className='size-4 text-text-warning-secondary' /> + <div className="mt-2 flex h-10 items-center gap-x-0.5 overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-2 shadow-xs backdrop-blur-[5px]"> + <div className="absolute bottom-0 left-0 right-0 top-0 bg-dataset-warning-message-bg opacity-40" /> + <div className="p-1"> + <AlertTriangle className="size-4 text-text-warning-secondary" /> </div> - <span className='system-xs-medium text-text-primary'> + <span className="system-xs-medium text-text-primary"> {t('datasetSettings.form.retrievalSetting.multiModalTip')} </span> </div> 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 9eb1cb39c9..04dff1723f 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,8 +1,8 @@ import { memo, useCallback } from 'react' import { useTranslation } from 'react-i18next' -import Tooltip from '@/app/components/base/tooltip' -import Switch from '@/app/components/base/switch' import { InputNumber } from '@/app/components/base/input-number' +import Switch from '@/app/components/base/switch' +import Tooltip from '@/app/components/base/tooltip' export type TopKAndScoreThresholdProps = { topK: number @@ -58,20 +58,20 @@ const TopKAndScoreThreshold = ({ } return ( - <div className='grid grid-cols-2 gap-4'> + <div className="grid grid-cols-2 gap-4"> <div> - <div className='system-xs-medium mb-0.5 flex h-6 items-center text-text-secondary'> + <div className="system-xs-medium mb-0.5 flex h-6 items-center text-text-secondary"> {t('appDebug.datasetConfig.top_k')} <Tooltip - triggerClassName='ml-0.5 shrink-0 w-3.5 h-3.5' + triggerClassName="ml-0.5 shrink-0 w-3.5 h-3.5" popupContent={t('appDebug.datasetConfig.top_kTip')} /> </div> <InputNumber disabled={readonly} - type='number' + type="number" {...TOP_K_VALUE_LIMIT} - size='regular' + size="regular" value={topK} onChange={handleTopKChange} /> @@ -79,26 +79,26 @@ const TopKAndScoreThreshold = ({ { !hiddenScoreThreshold && ( <div> - <div className='mb-0.5 flex h-6 items-center'> + <div className="mb-0.5 flex h-6 items-center"> <Switch - className='mr-2' + className="mr-2" defaultValue={isScoreThresholdEnabled} onChange={onScoreThresholdEnabledChange} disabled={readonly} /> - <div className='system-sm-medium grow truncate text-text-secondary'> + <div className="system-sm-medium grow truncate text-text-secondary"> {t('appDebug.datasetConfig.score_threshold')} </div> <Tooltip - triggerClassName='shrink-0 ml-0.5 w-3.5 h-3.5' + triggerClassName="shrink-0 ml-0.5 w-3.5 h-3.5" popupContent={t('appDebug.datasetConfig.score_thresholdTip')} /> </div> <InputNumber disabled={readonly || !isScoreThresholdEnabled} - type='number' + type="number" {...SCORE_THRESHOLD_VALUE_LIMIT} - size='regular' + size="regular" value={scoreThreshold} onChange={handleScoreThresholdChange} /> diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/type.ts b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/type.ts index b72a830d4b..73e15fd9d9 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/type.ts +++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/type.ts @@ -10,7 +10,7 @@ export type Option = { title: any description: string effectColor?: string - showEffectColor?: boolean, + showEffectColor?: boolean } export type HybridSearchModeOption = { diff --git a/web/app/components/workflow/nodes/knowledge-base/default.ts b/web/app/components/workflow/nodes/knowledge-base/default.ts index 952eb10fa0..fe9f6ba78e 100644 --- a/web/app/components/workflow/nodes/knowledge-base/default.ts +++ b/web/app/components/workflow/nodes/knowledge-base/default.ts @@ -1,8 +1,8 @@ import type { NodeDefault } from '../../types' import type { KnowledgeBaseNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' import { IndexingType } from '@/app/components/datasets/create/step-two' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' const metaData = genNodeMetaData({ sort: 3.1, diff --git a/web/app/components/workflow/nodes/knowledge-base/hooks/use-config.ts b/web/app/components/workflow/nodes/knowledge-base/hooks/use-config.ts index 8b22704c5a..f2a27d338e 100644 --- a/web/app/components/workflow/nodes/knowledge-base/hooks/use-config.ts +++ b/web/app/components/workflow/nodes/knowledge-base/hooks/use-config.ts @@ -1,25 +1,23 @@ -import { - useCallback, -} from 'react' -import { produce } from 'immer' -import { useStoreApi } from 'reactflow' -import { useNodeDataUpdate } from '@/app/components/workflow/hooks' -import type { ValueSelector } from '@/app/components/workflow/types' -import { - ChunkStructureEnum, - IndexMethodEnum, - RetrievalSearchMethodEnum, - WeightedScoreEnum, -} from '../types' import type { KnowledgeBaseNodeType, RerankingModel, } from '../types' +import type { ValueSelector } from '@/app/components/workflow/types' +import { produce } from 'immer' import { + useCallback, +} from 'react' +import { useStoreApi } from 'reactflow' +import { useNodeDataUpdate } from '@/app/components/workflow/hooks' +import { DEFAULT_WEIGHTED_SCORE, RerankingModeEnum } from '@/models/datasets' +import { + ChunkStructureEnum, HybridSearchModeEnum, + IndexMethodEnum, + RetrievalSearchMethodEnum, + WeightedScoreEnum, } from '../types' import { isHighQualitySearchMethod } from '../utils' -import { DEFAULT_WEIGHTED_SCORE, RerankingModeEnum } from '@/models/datasets' export const useConfig = (id: string) => { const store = useStoreApi() diff --git a/web/app/components/workflow/nodes/knowledge-base/node.tsx b/web/app/components/workflow/nodes/knowledge-base/node.tsx index 29de1bce9e..e69ae56be7 100644 --- a/web/app/components/workflow/nodes/knowledge-base/node.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/node.tsx @@ -1,33 +1,33 @@ import type { FC } from 'react' +import type { KnowledgeBaseNodeType } from './types' +import type { NodeProps } from '@/app/components/workflow/types' import { memo } from 'react' import { useTranslation } from 'react-i18next' -import type { KnowledgeBaseNodeType } from './types' import { useSettingsDisplay } from './hooks/use-settings-display' -import type { NodeProps } from '@/app/components/workflow/types' const Node: FC<NodeProps<KnowledgeBaseNodeType>> = ({ data }) => { const { t } = useTranslation() const settingsDisplay = useSettingsDisplay() return ( - <div className='mb-1 space-y-0.5 px-3 py-1'> - <div className='flex h-6 items-center rounded-md bg-workflow-block-parma-bg px-1.5'> - <div className='system-xs-medium-uppercase mr-2 shrink-0 text-text-tertiary'> + <div className="mb-1 space-y-0.5 px-3 py-1"> + <div className="flex h-6 items-center rounded-md bg-workflow-block-parma-bg px-1.5"> + <div className="system-xs-medium-uppercase mr-2 shrink-0 text-text-tertiary"> {t('datasetCreation.stepTwo.indexMode')} </div> <div - className='system-xs-medium grow truncate text-right text-text-secondary' + className="system-xs-medium grow truncate text-right text-text-secondary" title={data.indexing_technique} > {settingsDisplay[data.indexing_technique as keyof typeof settingsDisplay]} </div> </div> - <div className='flex h-6 items-center rounded-md bg-workflow-block-parma-bg px-1.5'> - <div className='system-xs-medium-uppercase mr-2 shrink-0 text-text-tertiary'> + <div className="flex h-6 items-center rounded-md bg-workflow-block-parma-bg px-1.5"> + <div className="system-xs-medium-uppercase mr-2 shrink-0 text-text-tertiary"> {t('datasetSettings.form.retrievalSetting.title')} </div> <div - className='system-xs-medium grow truncate text-right text-text-secondary' + className="system-xs-medium grow truncate text-right text-text-secondary" title={data.retrieval_model?.search_method} > {settingsDisplay[data.retrieval_model?.search_method as keyof typeof settingsDisplay]} diff --git a/web/app/components/workflow/nodes/knowledge-base/panel.tsx b/web/app/components/workflow/nodes/knowledge-base/panel.tsx index f6448d6fff..0e4bd29def 100644 --- a/web/app/components/workflow/nodes/knowledge-base/panel.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/panel.tsx @@ -1,33 +1,32 @@ import type { FC } from 'react' +import type { KnowledgeBaseNodeType } from './types' +import type { NodePanelProps, Var } from '@/app/components/workflow/types' import { memo, useCallback, useMemo, } from 'react' import { useTranslation } from 'react-i18next' -import type { KnowledgeBaseNodeType } from './types' -import { - ChunkStructureEnum, - IndexMethodEnum, -} from './types' -import ChunkStructure from './components/chunk-structure' -import IndexMethod from './components/index-method' -import RetrievalSetting from './components/retrieval-setting' -import EmbeddingModel from './components/embedding-model' -import { useConfig } from './hooks/use-config' -import type { NodePanelProps } from '@/app/components/workflow/types' +import { checkShowMultiModalTip } from '@/app/components/datasets/settings/utils' +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 { useNodesReadOnly } from '@/app/components/workflow/hooks' import { BoxGroup, BoxGroupField, Group, } from '@/app/components/workflow/nodes/_base/components/layout' -import Split from '../_base/components/split' -import { useNodesReadOnly } from '@/app/components/workflow/hooks' import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' -import type { Var } from '@/app/components/workflow/types' -import { checkShowMultiModalTip } from '@/app/components/datasets/settings/utils' -import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' -import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import Split from '../_base/components/split' +import ChunkStructure from './components/chunk-structure' +import EmbeddingModel from './components/embedding-model' +import IndexMethod from './components/index-method' +import RetrievalSetting from './components/retrieval-setting' +import { useConfig } from './hooks/use-config' +import { + ChunkStructureEnum, + IndexMethodEnum, +} from './types' const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({ id, @@ -55,7 +54,8 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({ } = useConfig(id) const filterVar = useCallback((variable: Var) => { - if (!data.chunk_structure) return false + if (!data.chunk_structure) + return false switch (data.chunk_structure) { case ChunkStructureEnum.general: return variable.schemaType === 'general_structure' || variable.schemaType === 'multimodal_general_structure' @@ -69,7 +69,8 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({ }, [data.chunk_structure]) const chunkTypePlaceHolder = useMemo(() => { - if (!data.chunk_structure) return '' + if (!data.chunk_structure) + return '' let placeholder = '' switch (data.chunk_structure) { case ChunkStructureEnum.general: @@ -107,7 +108,7 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({ return ( <div> <Group - className='py-3' + className="py-3" withBorderBottom={!!data.chunk_structure} > <ChunkStructure @@ -144,7 +145,7 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({ /> </BoxGroupField> <BoxGroup> - <div className='space-y-3'> + <div className="space-y-3"> <IndexMethod chunkStructure={data.chunk_structure} indexMethod={data.indexing_technique} @@ -163,8 +164,8 @@ const Panel: FC<NodePanelProps<KnowledgeBaseNodeType>> = ({ /> ) } - <div className='pt-1'> - <Split className='h-[1px]' /> + <div className="pt-1"> + <Split className="h-[1px]" /> </div> <RetrievalSetting indexMethod={data.indexing_technique} diff --git a/web/app/components/workflow/nodes/knowledge-base/types.ts b/web/app/components/workflow/nodes/knowledge-base/types.ts index 1f484a5c55..b54e8e2b2f 100644 --- a/web/app/components/workflow/nodes/knowledge-base/types.ts +++ b/web/app/components/workflow/nodes/knowledge-base/types.ts @@ -1,13 +1,13 @@ -import type { CommonNodeType } from '@/app/components/workflow/types' import type { IndexingType } from '@/app/components/datasets/create/step-two' -import type { RETRIEVE_METHOD } from '@/types/app' -import type { WeightedScoreEnum } from '@/models/datasets' -import type { RerankingModeEnum } from '@/models/datasets' import type { Model } from '@/app/components/header/account-setting/model-provider-page/declarations' -export { WeightedScoreEnum } from '@/models/datasets' +import type { CommonNodeType } from '@/app/components/workflow/types' +import type { RerankingModeEnum, WeightedScoreEnum } from '@/models/datasets' +import type { RETRIEVE_METHOD } from '@/types/app' + export { IndexingType as IndexMethodEnum } from '@/app/components/datasets/create/step-two' -export { RETRIEVE_METHOD as RetrievalSearchMethodEnum } from '@/types/app' +export { WeightedScoreEnum } from '@/models/datasets' export { RerankingModeEnum as HybridSearchModeEnum } from '@/models/datasets' +export { RETRIEVE_METHOD as RetrievalSearchMethodEnum } from '@/types/app' export enum ChunkStructureEnum { general = 'text_model', diff --git a/web/app/components/workflow/nodes/knowledge-base/use-single-run-form-params.ts b/web/app/components/workflow/nodes/knowledge-base/use-single-run-form-params.ts index 707407a589..f653d7fdc3 100644 --- a/web/app/components/workflow/nodes/knowledge-base/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/knowledge-base/use-single-run-form-params.ts @@ -1,11 +1,11 @@ -import { useTranslation } from 'react-i18next' -import type { InputVar, Variable } from '@/app/components/workflow/types' -import { InputVarType } from '@/app/components/workflow/types' -import { useCallback, useMemo } from 'react' import type { KnowledgeBaseNodeType } from './types' +import type { InputVar, Variable } from '@/app/components/workflow/types' +import { useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { InputVarType } from '@/app/components/workflow/types' type Params = { - id: string, + id: string payload: KnowledgeBaseNodeType runInputData: Record<string, any> getInputVars: (textList: string[]) => InputVar[] @@ -45,7 +45,7 @@ const useSingleRunFormParams = ({ return [payload.index_chunk_variable_selector] } const getDependentVar = (variable: string) => { - if(variable === 'query') + if (variable === 'query') return payload.index_chunk_variable_selector } diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/add-dataset.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/add-dataset.tsx index 010ecbb966..b51d085113 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/add-dataset.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/add-dataset.tsx @@ -1,10 +1,10 @@ 'use client' -import { useBoolean } from 'ahooks' import type { FC } from 'react' -import React, { useCallback } from 'react' -import AddButton from '@/app/components/base/button/add-button' -import SelectDataset from '@/app/components/app/configuration/dataset-config/select-dataset' import type { DataSet } from '@/models/datasets' +import { useBoolean } from 'ahooks' +import React, { useCallback } from 'react' +import SelectDataset from '@/app/components/app/configuration/dataset-config/select-dataset' +import AddButton from '@/app/components/base/button/add-button' type Props = { selectedIds: string[] diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx index e164e4f320..440aa9189a 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx @@ -1,22 +1,22 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useState } from 'react' -import { useBoolean } from 'ahooks' +import type { DataSet } from '@/models/datasets' import { RiDeleteBinLine, RiEditLine, } from '@remixicon/react' +import { useBoolean } from 'ahooks' +import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import type { DataSet } from '@/models/datasets' -import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' import SettingsModal from '@/app/components/app/configuration/dataset-config/settings-modal' -import Drawer from '@/app/components/base/drawer' -import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' -import Badge from '@/app/components/base/badge' -import { useKnowledge } from '@/hooks/use-knowledge' +import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' import AppIcon from '@/app/components/base/app-icon' -import FeatureIcon from '@/app/components/header/account-setting/model-provider-page/model-selector/feature-icon' +import Badge from '@/app/components/base/badge' +import Drawer from '@/app/components/base/drawer' 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 useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' +import { useKnowledge } from '@/hooks/use-knowledge' type Props = { payload: DataSet @@ -67,28 +67,31 @@ const DatasetItem: FC<Props> = ({ ${isDeleteHovered ? 'border-state-destructive-border bg-state-destructive-hover' : 'bg-components-panel-on-panel-item-bg hover:bg-components-panel-on-panel-item-bg-hover' - }`}> - <div className='flex w-0 grow items-center space-x-1.5'> + }`} + > + <div className="flex w-0 grow items-center space-x-1.5"> <AppIcon - size='tiny' + size="tiny" iconType={iconInfo.icon_type} icon={iconInfo.icon} background={iconInfo.icon_type === 'image' ? undefined : iconInfo.icon_background} imageUrl={iconInfo.icon_type === 'image' ? iconInfo.icon_url : undefined} /> - <div className='system-sm-medium w-0 grow truncate text-text-secondary'>{payload.name}</div> + <div className="system-sm-medium w-0 grow truncate text-text-secondary">{payload.name}</div> </div> {!readonly && ( - <div className='ml-2 hidden shrink-0 items-center space-x-1 group-hover/dataset-item:flex'> + <div className="ml-2 hidden shrink-0 items-center space-x-1 group-hover/dataset-item:flex"> { - editable && <ActionButton - onClick={(e) => { - e.stopPropagation() - showSettingsModal() - }} - > - <RiEditLine className='h-4 w-4 shrink-0 text-text-tertiary' /> - </ActionButton> + editable && ( + <ActionButton + onClick={(e) => { + e.stopPropagation() + showSettingsModal() + }} + > + <RiEditLine className="h-4 w-4 shrink-0 text-text-tertiary" /> + </ActionButton> + ) } <ActionButton onClick={handleRemove} @@ -101,25 +104,29 @@ const DatasetItem: FC<Props> = ({ </div> )} {payload.is_multimodal && ( - <div className='mr-1 shrink-0 group-hover/dataset-item:hidden'> + <div className="mr-1 shrink-0 group-hover/dataset-item:hidden"> <FeatureIcon feature={ModelFeatureEnum.vision} /> </div> )} { - payload.indexing_technique && <Badge - className='shrink-0 group-hover/dataset-item:hidden' - text={formatIndexingTechniqueAndMethod(payload.indexing_technique, payload.retrieval_model_dict?.search_method)} - /> + payload.indexing_technique && ( + <Badge + className="shrink-0 group-hover/dataset-item:hidden" + text={formatIndexingTechniqueAndMethod(payload.indexing_technique, payload.retrieval_model_dict?.search_method)} + /> + ) } { - payload.provider === 'external' && <Badge - className='shrink-0 group-hover/dataset-item:hidden' - text={t('dataset.externalTag') as string} - /> + payload.provider === 'external' && ( + <Badge + className="shrink-0 group-hover/dataset-item:hidden" + text={t('dataset.externalTag') as string} + /> + ) } {isShowSettingsModal && ( - <Drawer isOpen={isShowSettingsModal} onClose={hideSettingsModal} footer={null} mask={isMobile} panelClassName='mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl'> + <Drawer isOpen={isShowSettingsModal} onClose={hideSettingsModal} footer={null} mask={isMobile} panelClassName="mt-16 mx-2 sm:mr-2 mb-3 !p-0 !max-w-[640px] rounded-xl"> <SettingsModal currentDataset={payload} onCancel={hideSettingsModal} diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx index 35fd225d82..a804ae8953 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx @@ -1,12 +1,12 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useMemo } from 'react' -import { produce } from 'immer' -import { useTranslation } from 'react-i18next' -import Item from './dataset-item' import type { DataSet } from '@/models/datasets' +import { produce } from 'immer' +import React, { useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' import { useSelector as useAppContextSelector } from '@/context/app-context' import { hasEditPermissionForDataset } from '@/utils/permission' +import Item from './dataset-item' type Props = { list: DataSet[] @@ -55,26 +55,25 @@ const DatasetList: FC<Props> = ({ }, [list, userProfile?.id]) return ( - <div className='space-y-1'> + <div className="space-y-1"> {formattedList.length ? formattedList.map((item, index) => { - return ( - <Item - key={index} - payload={item} - onRemove={handleRemove(index)} - onChange={handleChange(index)} - readonly={readonly} - editable={item.editable} - /> - ) - }) + return ( + <Item + key={index} + payload={item} + onRemove={handleRemove(index)} + onChange={handleChange(index)} + readonly={readonly} + editable={item.editable} + /> + ) + }) : ( - <div className='cursor-default select-none rounded-lg bg-background-section p-3 text-center text-xs text-text-tertiary'> - {t('appDebug.datasetConfig.knowledgeTip')} - </div> - ) - } + <div className="cursor-default select-none rounded-lg bg-background-section p-3 text-center text-xs text-text-tertiary"> + {t('appDebug.datasetConfig.knowledgeTip')} + </div> + )} </div> ) 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 ed6b4c6c78..8044604207 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,22 +1,22 @@ +import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import type { MetadataInDoc } from '@/models/datasets' +import { + RiAddLine, +} from '@remixicon/react' import { useCallback, useMemo, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { - RiAddLine, -} from '@remixicon/react' -import MetadataIcon from './metadata-icon' +import Button from '@/app/components/base/button' +import Input from '@/app/components/base/input' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import Button from '@/app/components/base/button' -import Input from '@/app/components/base/input' -import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' -import type { MetadataInDoc } from '@/models/datasets' +import MetadataIcon from './metadata-icon' const AddCondition = ({ metadataList, @@ -39,7 +39,7 @@ const AddCondition = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 3, crossAxis: 0, @@ -47,16 +47,16 @@ const AddCondition = ({ > <PortalToFollowElemTrigger onClick={() => setOpen(!open)}> <Button - size='small' - variant='secondary' + size="small" + variant="secondary" > - <RiAddLine className='h-3.5 w-3.5' /> + <RiAddLine className="h-3.5 w-3.5" /> {t('workflow.nodes.knowledgeRetrieval.metadata.panel.add')} </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-10'> - <div className='w-[320px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'> - <div className='p-2 pb-1'> + <PortalToFollowElemContent className="z-10"> + <div className="w-[320px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg"> + <div className="p-2 pb-1"> <Input showLeftIcon placeholder={t('workflow.nodes.knowledgeRetrieval.metadata.panel.search')} @@ -64,24 +64,24 @@ const AddCondition = ({ onChange={e => setSearchText(e.target.value)} /> </div> - <div className='p-1'> + <div className="p-1"> { filteredMetadataList?.map(metadata => ( <div key={metadata.name} - className='system-sm-medium flex h-6 cursor-pointer items-center rounded-md px-3 text-text-secondary hover:bg-state-base-hover' + className="system-sm-medium flex h-6 cursor-pointer items-center rounded-md px-3 text-text-secondary hover:bg-state-base-hover" > - <div className='mr-1 p-[1px]'> + <div className="mr-1 p-[1px]"> <MetadataIcon type={metadata.type} /> </div> <div - className='grow truncate' + className="grow truncate" title={metadata.name} onClick={() => handleAddConditionWrapped(metadata)} > {metadata.name} </div> - <div className='system-xs-regular shrink-0 text-text-tertiary'>{metadata.type}</div> + <div className="system-xs-regular shrink-0 text-text-tertiary">{metadata.type}</div> </div> )) } diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-common-variable-selector.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-common-variable-selector.tsx index 0d81d808db..48581fd1d1 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-common-variable-selector.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-common-variable-selector.tsx @@ -1,15 +1,15 @@ +import type { VarType } from '@/app/components/workflow/types' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' +import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import type { VarType } from '@/app/components/workflow/types' -import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' type ConditionCommonVariableSelectorProps = { - variables?: { name: string; type: string; value: string }[] + variables?: { name: string, type: string, value: string }[] value?: string | number varType?: VarType onChange: (v: string) => void @@ -34,21 +34,25 @@ const ConditionCommonVariableSelector = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 4, crossAxis: 0, }} > - <PortalToFollowElemTrigger asChild onClick={() => { - if (!variables.length) return - setOpen(!open) - }}> + <PortalToFollowElemTrigger + asChild + onClick={() => { + if (!variables.length) + return + setOpen(!open) + }} + > <div className="flex h-6 grow cursor-pointer items-center"> { selected && ( - <div className='system-xs-medium inline-flex h-6 items-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark pl-[5px] pr-1.5 text-text-secondary shadow-xs'> - <Variable02 className='mr-1 h-3.5 w-3.5 text-text-accent' /> + <div className="system-xs-medium inline-flex h-6 items-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark pl-[5px] pr-1.5 text-text-secondary shadow-xs"> + <Variable02 className="mr-1 h-3.5 w-3.5 text-text-accent" /> {selected.value} </div> ) @@ -56,11 +60,11 @@ const ConditionCommonVariableSelector = ({ { !selected && ( <> - <div className='system-sm-regular flex grow items-center text-components-input-text-placeholder'> - <Variable02 className='mr-1 h-4 w-4' /> + <div className="system-sm-regular flex grow items-center text-components-input-text-placeholder"> + <Variable02 className="mr-1 h-4 w-4" /> {t('workflow.nodes.knowledgeRetrieval.metadata.panel.select')} </div> - <div className='system-2xs-medium flex h-5 shrink-0 items-center rounded-[5px] border border-divider-deep px-[5px] text-text-tertiary'> + <div className="system-2xs-medium flex h-5 shrink-0 items-center rounded-[5px] border border-divider-deep px-[5px] text-text-tertiary"> {varType} </div> </> @@ -68,16 +72,16 @@ const ConditionCommonVariableSelector = ({ } </div> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> - <div className='w-[200px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'> + <PortalToFollowElemContent className="z-[1000]"> + <div className="w-[200px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg"> { variables.map(v => ( <div key={v.value} - className='system-xs-medium flex h-6 cursor-pointer items-center rounded-md px-2 text-text-secondary hover:bg-state-base-hover' + className="system-xs-medium flex h-6 cursor-pointer items-center rounded-md px-2 text-text-secondary hover:bg-state-base-hover" onClick={() => handleChange(v.value)} > - <Variable02 className='mr-1 h-4 w-4 text-text-accent' /> + <Variable02 className="mr-1 h-4 w-4 text-text-accent" /> {v.value} </div> )) diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-date.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-date.tsx index ed8192239a..f89992a2e4 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-date.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-date.tsx @@ -1,14 +1,14 @@ -import { useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import dayjs from 'dayjs' +import type { TriggerProps } from '@/app/components/base/date-and-time-picker/types' import { RiCalendarLine, RiCloseCircleFill, } from '@remixicon/react' +import dayjs from 'dayjs' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import DatePicker from '@/app/components/base/date-and-time-picker/date-picker' -import type { TriggerProps } from '@/app/components/base/date-and-time-picker/types' -import { cn } from '@/utils/classnames' import { useAppContext } from '@/context/app-context' +import { cn } from '@/utils/classnames' type ConditionDateProps = { value?: number @@ -32,7 +32,7 @@ const ConditionDate = ({ handleClickTrigger, }: TriggerProps) => { return ( - <div className='group flex items-center' onClick={handleClickTrigger}> + <div className="group flex items-center" onClick={handleClickTrigger}> <div className={cn( 'system-sm-regular mr-0.5 flex h-6 grow cursor-pointer items-center px-1', @@ -71,7 +71,7 @@ const ConditionDate = ({ }, [value, handleDateChange, timezone, t]) return ( - <div className='h-8 px-2 py-1'> + <div className="h-8 px-2 py-1"> <DatePicker timezone={timezone} value={value ? dayjs(value * 1000) : undefined} diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-item.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-item.tsx index 42f3a085bc..815844d434 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-item.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-item.tsx @@ -1,21 +1,3 @@ -import { - useCallback, - useMemo, - useState, -} from 'react' -import { - RiDeleteBinLine, -} from '@remixicon/react' -import MetadataIcon from '../metadata-icon' -import { - COMMON_VARIABLE_REGEX, - VARIABLE_REGEX, - comparisonOperatorNotRequireValue, -} from './utils' -import ConditionOperator from './condition-operator' -import ConditionString from './condition-string' -import ConditionNumber from './condition-number' -import ConditionDate from './condition-date' import type { ComparisonOperator, HandleRemoveCondition, @@ -23,8 +5,26 @@ import type { MetadataFilteringCondition, MetadataShape, } from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import { + RiDeleteBinLine, +} from '@remixicon/react' +import { + useCallback, + useMemo, + useState, +} from 'react' import { MetadataFilteringVariableType } from '@/app/components/workflow/nodes/knowledge-retrieval/types' import { cn } from '@/utils/classnames' +import MetadataIcon from '../metadata-icon' +import ConditionDate from './condition-date' +import ConditionNumber from './condition-number' +import ConditionOperator from './condition-operator' +import ConditionString from './condition-string' +import { + COMMON_VARIABLE_REGEX, + comparisonOperatorNotRequireValue, + VARIABLE_REGEX, +} from './utils' type ConditionItemProps = { className?: string @@ -72,14 +72,15 @@ const ConditionItem = ({ ...condition, value: comparisonOperatorNotRequireValue(condition.comparison_operator) ? undefined : condition.value, comparison_operator: operator, - }) + }, + ) }, [onUpdateCondition, condition]) const valueAndValueMethod = useMemo(() => { if ( (currentMetadata?.type === MetadataFilteringVariableType.string - || currentMetadata?.type === MetadataFilteringVariableType.number - || currentMetadata?.type === MetadataFilteringVariableType.select) + || currentMetadata?.type === MetadataFilteringVariableType.number + || currentMetadata?.type === MetadataFilteringVariableType.select) && typeof condition.value === 'string' ) { const regex = isCommonVariable ? COMMON_VARIABLE_REGEX : VARIABLE_REGEX @@ -121,18 +122,19 @@ const ConditionItem = ({ <div className={cn( 'grow rounded-lg bg-components-input-bg-normal', isHovered && 'bg-state-destructive-hover', - )}> - <div className='flex items-center p-1'> - <div className='w-0 grow'> - <div className='flex h-6 min-w-0 items-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark pl-1 pr-1.5 shadow-xs'> - <div className='mr-0.5 p-[1px]'> - <MetadataIcon type={currentMetadata?.type} className='h-3 w-3' /> + )} + > + <div className="flex items-center p-1"> + <div className="w-0 grow"> + <div className="flex h-6 min-w-0 items-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark pl-1 pr-1.5 shadow-xs"> + <div className="mr-0.5 p-[1px]"> + <MetadataIcon type={currentMetadata?.type} className="h-3 w-3" /> </div> - <div className='system-xs-medium mr-0.5 min-w-0 flex-1 truncate text-text-secondary'>{currentMetadata?.name}</div> - <div className='system-xs-regular text-text-tertiary'>{currentMetadata?.type}</div> + <div className="system-xs-medium mr-0.5 min-w-0 flex-1 truncate text-text-secondary">{currentMetadata?.name}</div> + <div className="system-xs-regular text-text-tertiary">{currentMetadata?.type}</div> </div> </div> - <div className='mx-1 h-3 w-[1px] bg-divider-regular'></div> + <div className="mx-1 h-3 w-[1px] bg-divider-regular"></div> <ConditionOperator disabled={!canChooseOperator} variableType={currentMetadata?.type || MetadataFilteringVariableType.string} @@ -140,11 +142,11 @@ const ConditionItem = ({ onSelect={handleConditionOperatorChange} /> </div> - <div className='border-t border-t-divider-subtle'> + <div className="border-t border-t-divider-subtle"> { !comparisonOperatorNotRequireValue(condition.comparison_operator) && (currentMetadata?.type === MetadataFilteringVariableType.string - || currentMetadata?.type === MetadataFilteringVariableType.select) && ( + || currentMetadata?.type === MetadataFilteringVariableType.select) && ( <ConditionString valueMethod={localValueMethod} onValueMethodChange={handleValueMethodChange} @@ -182,12 +184,12 @@ const ConditionItem = ({ </div> </div> <div - className='ml-1 mt-1 flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-lg text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive' + className="ml-1 mt-1 flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-lg text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive" onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} onClick={doRemoveCondition} > - <RiDeleteBinLine className='h-4 w-4' /> + <RiDeleteBinLine className="h-4 w-4" /> </div> </div> ) diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-number.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-number.tsx index 6421401a2a..3b5587c442 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-number.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-number.tsx @@ -1,16 +1,16 @@ -import { useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import ConditionValueMethod from './condition-value-method' import type { ConditionValueMethodProps } from './condition-value-method' -import ConditionVariableSelector from './condition-variable-selector' -import ConditionCommonVariableSelector from './condition-common-variable-selector' import type { Node, NodeOutPutVar, ValueSelector, } from '@/app/components/workflow/types' -import { VarType } from '@/app/components/workflow/types' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' +import { VarType } from '@/app/components/workflow/types' +import ConditionCommonVariableSelector from './condition-common-variable-selector' +import ConditionValueMethod from './condition-value-method' +import ConditionVariableSelector from './condition-variable-selector' type ConditionNumberProps = { value?: string | number @@ -18,7 +18,7 @@ type ConditionNumberProps = { nodesOutputVars: NodeOutPutVar[] availableNodes: Node[] isCommonVariable?: boolean - commonVariables: { name: string; type: string; value: string }[] + commonVariables: { name: string, type: string, value: string }[] } & ConditionValueMethodProps const ConditionNumber = ({ value, @@ -40,12 +40,12 @@ const ConditionNumber = ({ }, [onChange]) return ( - <div className='flex h-8 items-center pl-1 pr-2'> + <div className="flex h-8 items-center pl-1 pr-2"> <ConditionValueMethod valueMethod={valueMethod} onValueMethodChange={onValueMethodChange} /> - <div className='ml-1 mr-1.5 h-4 w-[1px] bg-divider-regular'></div> + <div className="ml-1 mr-1.5 h-4 w-[1px] bg-divider-regular"></div> { valueMethod === 'variable' && !isCommonVariable && ( <ConditionVariableSelector @@ -70,14 +70,14 @@ const ConditionNumber = ({ { valueMethod === 'constant' && ( <Input - className='border-none bg-transparent outline-none hover:bg-transparent focus:bg-transparent focus:shadow-none' + className="border-none bg-transparent outline-none hover:bg-transparent focus:bg-transparent focus:shadow-none" value={value} onChange={(e) => { const v = e.target.value onChange(v ? Number(e.target.value) : undefined) }} placeholder={t('workflow.nodes.knowledgeRetrieval.metadata.panel.placeholder')} - type='number' + type="number" /> ) } 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 93a078ff5d..8f0430b655 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 @@ -1,13 +1,13 @@ +import type { + ComparisonOperator, + MetadataFilteringVariableType, +} from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import { RiArrowDownSLine } from '@remixicon/react' import { useMemo, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { RiArrowDownSLine } from '@remixicon/react' -import { - getOperators, - isComparisonOperatorNeedTranslate, -} from './utils' import Button from '@/app/components/base/button' import { PortalToFollowElem, @@ -15,10 +15,10 @@ import { PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' import { cn } from '@/utils/classnames' -import type { - ComparisonOperator, - MetadataFilteringVariableType, -} from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import { + getOperators, + isComparisonOperatorNeedTranslate, +} from './utils' const i18nPrefix = 'workflow.nodes.ifElse' @@ -52,7 +52,7 @@ const ConditionOperator = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-end' + placement="bottom-end" offset={{ mainAxis: 4, crossAxis: 0, @@ -61,8 +61,8 @@ const ConditionOperator = ({ <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}> <Button className={cn('shrink-0', !selectedOption && 'opacity-50', className)} - size='small' - variant='ghost' + size="small" + variant="ghost" disabled={disabled} > { @@ -70,16 +70,16 @@ const ConditionOperator = ({ ? selectedOption.label : t(`${i18nPrefix}.select`) } - <RiArrowDownSLine className='ml-1 h-3.5 w-3.5' /> + <RiArrowDownSLine className="ml-1 h-3.5 w-3.5" /> </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-10'> - <div className='rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'> + <PortalToFollowElemContent className="z-10"> + <div className="rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg"> { options.map(option => ( <div key={option.value} - className='flex h-7 cursor-pointer items-center rounded-lg px-3 py-1.5 text-[13px] font-medium text-text-secondary hover:bg-state-base-hover' + className="flex h-7 cursor-pointer items-center rounded-lg px-3 py-1.5 text-[13px] font-medium text-text-secondary hover:bg-state-base-hover" onClick={() => { onSelect(option.value) setOpen(false) diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-string.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-string.tsx index d5cb06e690..d2592d7a57 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-string.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-string.tsx @@ -1,16 +1,16 @@ -import { useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import ConditionValueMethod from './condition-value-method' import type { ConditionValueMethodProps } from './condition-value-method' -import ConditionVariableSelector from './condition-variable-selector' -import ConditionCommonVariableSelector from './condition-common-variable-selector' import type { Node, NodeOutPutVar, ValueSelector, } from '@/app/components/workflow/types' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import { VarType } from '@/app/components/workflow/types' +import ConditionCommonVariableSelector from './condition-common-variable-selector' +import ConditionValueMethod from './condition-value-method' +import ConditionVariableSelector from './condition-variable-selector' type ConditionStringProps = { value?: string @@ -18,7 +18,7 @@ type ConditionStringProps = { nodesOutputVars: NodeOutPutVar[] availableNodes: Node[] isCommonVariable?: boolean - commonVariables: { name: string; type: string; value: string }[] + commonVariables: { name: string, type: string, value: string }[] } & ConditionValueMethodProps const ConditionString = ({ value, @@ -40,12 +40,12 @@ const ConditionString = ({ }, [onChange]) return ( - <div className='flex h-8 items-center pl-1 pr-2'> + <div className="flex h-8 items-center pl-1 pr-2"> <ConditionValueMethod valueMethod={valueMethod} onValueMethodChange={onValueMethodChange} /> - <div className='ml-1 mr-1.5 h-4 w-[1px] bg-divider-regular'></div> + <div className="ml-1 mr-1.5 h-4 w-[1px] bg-divider-regular"></div> { valueMethod === 'variable' && !isCommonVariable && ( <ConditionVariableSelector @@ -70,7 +70,7 @@ const ConditionString = ({ { valueMethod === 'constant' && ( <Input - className='border-none bg-transparent outline-none hover:bg-transparent focus:bg-transparent focus:shadow-none' + className="border-none bg-transparent outline-none hover:bg-transparent focus:bg-transparent focus:shadow-none" value={value} onChange={e => onChange(e.target.value)} placeholder={t('workflow.nodes.knowledgeRetrieval.metadata.panel.placeholder')} 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 562cda76e4..c930387c82 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,12 +1,12 @@ -import { useState } from 'react' -import { capitalize } from 'lodash-es' import { RiArrowDownSLine } from '@remixicon/react' +import { capitalize } from 'lodash-es' +import { useState } from 'react' +import Button from '@/app/components/base/button' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import Button from '@/app/components/base/button' import { cn } from '@/utils/classnames' export type ConditionValueMethodProps = { @@ -27,21 +27,21 @@ const ConditionValueMethod = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 4, crossAxis: 0 }} > <PortalToFollowElemTrigger asChild onClick={() => setOpen(v => !v)}> <Button - className='shrink-0' - variant='ghost' - size='small' + className="shrink-0" + variant="ghost" + size="small" > {capitalize(valueMethod)} - <RiArrowDownSLine className='ml-[1px] h-3.5 w-3.5' /> + <RiArrowDownSLine className="ml-[1px] h-3.5 w-3.5" /> </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> - <div className='w-[112px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'> + <PortalToFollowElemContent className="z-[1000]"> + <div className="w-[112px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg"> { options.map(option => ( <div diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-variable-selector.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-variable-selector.tsx index 7908f6a98a..bbd5d9f227 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-variable-selector.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-variable-selector.tsx @@ -1,5 +1,12 @@ +import type { + Node, + NodeOutPutVar, + ValueSelector, + Var, +} from '@/app/components/workflow/types' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' +import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { PortalToFollowElem, PortalToFollowElemContent, @@ -7,14 +14,7 @@ import { } from '@/app/components/base/portal-to-follow-elem' import VariableTag from '@/app/components/workflow/nodes/_base/components/variable-tag' import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' -import type { - Node, - NodeOutPutVar, - ValueSelector, - Var, -} from '@/app/components/workflow/types' import { VarType } from '@/app/components/workflow/types' -import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' type ConditionVariableSelectorProps = { valueSelector?: ValueSelector @@ -43,7 +43,7 @@ const ConditionVariableSelector = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 4, crossAxis: 0, @@ -64,11 +64,11 @@ const ConditionVariableSelector = ({ { !valueSelector.length && ( <> - <div className='system-sm-regular flex grow items-center text-components-input-text-placeholder'> - <Variable02 className='mr-1 h-4 w-4' /> + <div className="system-sm-regular flex grow items-center text-components-input-text-placeholder"> + <Variable02 className="mr-1 h-4 w-4" /> {t('workflow.nodes.knowledgeRetrieval.metadata.panel.select')} </div> - <div className='system-2xs-medium flex h-5 shrink-0 items-center rounded-[5px] border border-divider-deep px-[5px] text-text-tertiary'> + <div className="system-2xs-medium flex h-5 shrink-0 items-center rounded-[5px] border border-divider-deep px-[5px] text-text-tertiary"> {varType} </div> </> @@ -76,8 +76,8 @@ const ConditionVariableSelector = ({ } </div> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> - <div className='w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'> + <PortalToFollowElemContent className="z-[1000]"> + <div className="w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg"> <VarReferenceVars vars={nodesOutputVars} isSupportFileVar diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/index.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/index.tsx index 49da29ce7b..163d1084d2 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/index.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/index.tsx @@ -1,8 +1,8 @@ -import { RiLoopLeftLine } from '@remixicon/react' -import ConditionItem from './condition-item' -import { cn } from '@/utils/classnames' import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import { RiLoopLeftLine } from '@remixicon/react' import { LogicalOperator } from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import { cn } from '@/utils/classnames' +import ConditionItem from './condition-item' type ConditionListProps = { disabled?: boolean @@ -34,15 +34,16 @@ const ConditionList = ({ conditions.length > 1 && ( <div className={cn( 'absolute bottom-0 left-0 top-0 w-[44px]', - )}> - <div className='absolute bottom-4 right-1 top-4 w-2.5 rounded-l-[8px] border border-r-0 border-divider-deep'></div> - <div className='absolute right-0 top-1/2 h-[29px] w-4 -translate-y-1/2 bg-components-panel-bg'></div> + )} + > + <div className="absolute bottom-4 right-1 top-4 w-2.5 rounded-l-[8px] border border-r-0 border-divider-deep"></div> + <div className="absolute right-0 top-1/2 h-[29px] w-4 -translate-y-1/2 bg-components-panel-bg"></div> <div - className='absolute right-1 top-1/2 flex h-[21px] -translate-y-1/2 cursor-pointer select-none items-center rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-1 text-[10px] font-semibold text-text-accent-secondary shadow-xs' + className="absolute right-1 top-1/2 flex h-[21px] -translate-y-1/2 cursor-pointer select-none items-center rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-1 text-[10px] font-semibold text-text-accent-secondary shadow-xs" onClick={() => handleToggleConditionLogicalOperator()} > {logical_operator.toUpperCase()} - <RiLoopLeftLine className='ml-0.5 h-3 w-3' /> + <RiLoopLeftLine className="ml-0.5 h-3 w-3" /> </div> </div> ) diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/utils.ts b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/utils.ts index 10ee1aff1f..ab68275b8d 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/utils.ts +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/utils.ts @@ -8,9 +8,12 @@ export const isEmptyRelatedOperator = (operator: ComparisonOperator) => { } const notTranslateKey = [ - ComparisonOperator.equal, ComparisonOperator.notEqual, - ComparisonOperator.largerThan, ComparisonOperator.largerThanOrEqual, - ComparisonOperator.lessThan, ComparisonOperator.lessThanOrEqual, + ComparisonOperator.equal, + ComparisonOperator.notEqual, + ComparisonOperator.largerThan, + ComparisonOperator.largerThanOrEqual, + ComparisonOperator.lessThan, + ComparisonOperator.lessThanOrEqual, ] export const isComparisonOperatorNeedTranslate = (operator?: ComparisonOperator) => { @@ -64,5 +67,5 @@ export const comparisonOperatorNotRequireValue = (operator?: ComparisonOperator) return [ComparisonOperator.empty, ComparisonOperator.notEmpty, ComparisonOperator.isNull, ComparisonOperator.isNotNull, ComparisonOperator.exists, ComparisonOperator.notExists].includes(operator) } -export const VARIABLE_REGEX = /\{\{(#[a-zA-Z0-9_-]{1,50}(\.[a-zA-Z_]\w{0,29}){1,10}#)\}\}/gi -export const COMMON_VARIABLE_REGEX = /\{\{([a-zA-Z0-9_-]{1,50})\}\}/gi +export const VARIABLE_REGEX = /\{\{(#[\w-]{1,50}(\.[a-z_]\w{0,29}){1,10}#)\}\}/gi +export const COMMON_VARIABLE_REGEX = /\{\{([\w-]{1,50})\}\}/g diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/index.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/index.tsx index 1c6158a60e..9b541b9ea6 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/index.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/index.tsx @@ -1,16 +1,16 @@ +import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import { noop } from 'lodash-es' import { useCallback, useState, } from 'react' import { useTranslation } from 'react-i18next' +import Tooltip from '@/app/components/base/tooltip' +import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' +import Collapse from '@/app/components/workflow/nodes/_base/components/collapse' +import { MetadataFilteringModeEnum } from '@/app/components/workflow/nodes/knowledge-retrieval/types' import MetadataTrigger from '../metadata-trigger' import MetadataFilterSelector from './metadata-filter-selector' -import Collapse from '@/app/components/workflow/nodes/_base/components/collapse' -import Tooltip from '@/app/components/base/tooltip' -import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' -import { MetadataFilteringModeEnum } from '@/app/components/workflow/nodes/knowledge-retrieval/types' -import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' -import { noop } from 'lodash-es' type MetadataFilterProps = { metadataFilterMode?: MetadataFilteringModeEnum @@ -41,28 +41,28 @@ const MetadataFilter = ({ onCollapse={setCollapsed} hideCollapseIcon trigger={collapseIcon => ( - <div className='flex grow items-center justify-between pr-4'> - <div className='flex items-center'> - <div className='system-sm-semibold-uppercase mr-0.5 text-text-secondary'> + <div className="flex grow items-center justify-between pr-4"> + <div className="flex items-center"> + <div className="system-sm-semibold-uppercase mr-0.5 text-text-secondary"> {t('workflow.nodes.knowledgeRetrieval.metadata.title')} </div> <Tooltip popupContent={( - <div className='w-[200px]'> + <div className="w-[200px]"> {t('workflow.nodes.knowledgeRetrieval.metadata.tip')} </div> )} /> {collapseIcon} </div> - <div className='flex items-center'> + <div className="flex items-center"> <MetadataFilterSelector value={metadataFilterMode} onSelect={handleMetadataFilterModeChangeWrapped} /> { metadataFilterMode === MetadataFilteringModeEnum.manual && ( - <div className='ml-1'> + <div className="ml-1"> <MetadataTrigger {...restProps} /> </div> ) @@ -75,13 +75,13 @@ const MetadataFilter = ({ { metadataFilterMode === MetadataFilteringModeEnum.automatic && ( <> - <div className='body-xs-regular px-4 text-text-tertiary'> + <div className="body-xs-regular px-4 text-text-tertiary"> {t('workflow.nodes.knowledgeRetrieval.metadata.options.automatic.desc')} </div> - <div className='mt-1 px-4'> + <div className="mt-1 px-4"> <ModelParameterModal - portalToFollowElemContentClassName='z-[50]' - popupClassName='!w-[387px]' + portalToFollowElemContentClassName="z-[50]" + popupClassName="!w-[387px]" isInWorkflow isAdvancedMode={true} provider={metadataModelConfig?.provider || ''} 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 7183e685f4..4bf52d7b34 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,15 +1,15 @@ -import { useState } from 'react' -import { useTranslation } from 'react-i18next' import { RiArrowDownSLine, RiCheckLine, } from '@remixicon/react' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import Button from '@/app/components/base/button' import { MetadataFilteringModeEnum } from '@/app/components/workflow/nodes/knowledge-retrieval/types' type MetadataFilterSelectorProps = { @@ -44,7 +44,7 @@ const MetadataFilterSelector = ({ return ( <PortalToFollowElem - placement='bottom-end' + placement="bottom-end" offset={{ mainAxis: 4, crossAxis: 0, @@ -60,37 +60,37 @@ const MetadataFilterSelector = ({ asChild > <Button - variant='secondary' - size='small' + variant="secondary" + size="small" > {selectedOption.value} - <RiArrowDownSLine className='h-3.5 w-3.5' /> + <RiArrowDownSLine className="h-3.5 w-3.5" /> </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-10'> - <div className='w-[280px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'> + <PortalToFollowElemContent className="z-10"> + <div className="w-[280px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg"> { options.map(option => ( <div key={option.key} - className='flex cursor-pointer rounded-lg p-2 pr-3 hover:bg-state-base-hover' + className="flex cursor-pointer rounded-lg p-2 pr-3 hover:bg-state-base-hover" onClick={() => { onSelect(option.key) setOpen(false) }} > - <div className='w-4 shrink-0'> + <div className="w-4 shrink-0"> { option.key === value && ( - <RiCheckLine className='h-4 w-4 text-text-accent' /> + <RiCheckLine className="h-4 w-4 text-text-accent" /> ) } </div> - <div className='grow'> - <div className='system-sm-semibold text-text-secondary'> + <div className="grow"> + <div className="system-sm-semibold text-text-secondary"> {option.value} </div> - <div className='system-xs-regular text-text-tertiary'> + <div className="system-xs-regular text-text-tertiary"> {option.desc} </div> </div> diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-icon.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-icon.tsx index 4c327ce882..803a836142 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-icon.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-icon.tsx @@ -1,9 +1,9 @@ -import { memo } from 'react' import { RiHashtag, RiTextSnippet, RiTimeLine, } from '@remixicon/react' +import { memo } from 'react' import { MetadataFilteringVariableType } from '@/app/components/workflow/nodes/knowledge-retrieval/types' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-panel.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-panel.tsx index fd390abe6b..9e0444ac29 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-panel.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-panel.tsx @@ -1,8 +1,8 @@ -import { useTranslation } from 'react-i18next' +import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' import { RiCloseLine } from '@remixicon/react' +import { useTranslation } from 'react-i18next' import AddCondition from './add-condition' import ConditionList from './condition-list' -import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' type MetadataPanelProps = { onCancel: () => void @@ -17,21 +17,21 @@ const MetadataPanel = ({ const { t } = useTranslation() return ( - <div className='w-[420px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl'> - <div className='relative px-3 pt-3.5'> - <div className='system-xl-semibold text-text-primary'> + <div className="w-[420px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl"> + <div className="relative px-3 pt-3.5"> + <div className="system-xl-semibold text-text-primary"> {t('workflow.nodes.knowledgeRetrieval.metadata.panel.title')} </div> <div - className='absolute bottom-0 right-2.5 flex h-8 w-8 cursor-pointer items-center justify-center' + className="absolute bottom-0 right-2.5 flex h-8 w-8 cursor-pointer items-center justify-center" onClick={onCancel} > - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> - <div className='px-1 py-2'> - <div className='px-3 py-1'> - <div className='pb-2'> + <div className="px-1 py-2"> + <div className="px-3 py-1"> + <div className="pb-2"> <ConditionList metadataList={metadataList} metadataFilteringConditions={metadataFilteringConditions} 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 5d11bc7c6c..3a8d96f8f2 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,17 +1,17 @@ +import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import { RiFilter3Line } from '@remixicon/react' import { useEffect, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { RiFilter3Line } from '@remixicon/react' -import MetadataPanel from './metadata-panel' import Button from '@/app/components/base/button' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import MetadataPanel from './metadata-panel' const MetadataTrigger = ({ metadataFilteringConditions, @@ -35,24 +35,24 @@ const MetadataTrigger = ({ return ( <PortalToFollowElem - placement='left' + placement="left" offset={4} open={open} onOpenChange={setOpen} > <PortalToFollowElemTrigger onClick={() => setOpen(!open)}> <Button - variant='secondary-accent' - size='small' + variant="secondary-accent" + size="small" > - <RiFilter3Line className='mr-1 h-3.5 w-3.5' /> + <RiFilter3Line className="mr-1 h-3.5 w-3.5" /> {t('workflow.nodes.knowledgeRetrieval.metadata.panel.conditions')} - <div className='system-2xs-medium-uppercase ml-1 flex items-center rounded-[5px] border border-divider-deep px-1 text-text-tertiary'> + <div className="system-2xs-medium-uppercase ml-1 flex items-center rounded-[5px] border border-divider-deep px-1 text-text-tertiary"> {metadataFilteringConditions?.conditions.length || 0} </div> </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-10'> + <PortalToFollowElemContent className="z-10"> <MetadataPanel metadataFilteringConditions={metadataFilteringConditions} onCancel={() => setOpen(false)} 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 80587b864f..ced1bfcdae 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 @@ -1,22 +1,22 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useMemo } from 'react' -import { RiEqualizer2Line } from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import type { MultipleRetrievalConfig, SingleRetrievalConfig } from '../types' import type { ModelConfig } from '../../../types' -import { cn } from '@/utils/classnames' +import type { MultipleRetrievalConfig, SingleRetrievalConfig } from '../types' +import type { DataSet } from '@/models/datasets' +import type { DatasetConfigs } from '@/models/debug' +import { RiEqualizer2Line } from '@remixicon/react' +import React, { useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import ConfigRetrievalContent from '@/app/components/app/configuration/dataset-config/params-config/config-content' +import Button from '@/app/components/base/button' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import ConfigRetrievalContent from '@/app/components/app/configuration/dataset-config/params-config/config-content' -import { RETRIEVE_TYPE } from '@/types/app' import { DATASET_DEFAULT } from '@/config' -import Button from '@/app/components/base/button' -import type { DatasetConfigs } from '@/models/debug' -import type { DataSet } from '@/models/datasets' +import { RETRIEVE_TYPE } from '@/types/app' +import { cn } from '@/utils/classnames' type Props = { payload: { @@ -68,13 +68,13 @@ const RetrievalConfig: FC<Props> = ({ retrieval_model: retrieval_mode, reranking_model: (reranking_model?.provider && reranking_model?.model) ? { - reranking_provider_name: reranking_model?.provider, - reranking_model_name: reranking_model?.model, - } + reranking_provider_name: reranking_model?.provider, + reranking_model_name: reranking_model?.model, + } : { - reranking_provider_name: '', - reranking_model_name: '', - }, + reranking_provider_name: '', + reranking_model_name: '', + }, top_k: top_k || DATASET_DEFAULT.top_k, score_threshold_enabled: !(score_threshold === undefined || score_threshold === null), score_threshold, @@ -100,11 +100,11 @@ const RetrievalConfig: FC<Props> = ({ ? undefined // eslint-disable-next-line sonarjs/no-nested-conditional : (!configs.reranking_model?.reranking_provider_name - ? undefined - : { - provider: configs.reranking_model?.reranking_provider_name, - model: configs.reranking_model?.reranking_model_name, - }), + ? undefined + : { + provider: configs.reranking_model?.reranking_provider_name, + model: configs.reranking_model?.reranking_model_name, + }), reranking_mode: configs.reranking_mode, weights: configs.weights, reranking_enable: configs.reranking_enable, @@ -115,7 +115,7 @@ const RetrievalConfig: FC<Props> = ({ <PortalToFollowElem open={rerankModalOpen} onOpenChange={handleOpen} - placement='bottom-end' + placement="bottom-end" offset={{ crossAxis: -2, }} @@ -128,17 +128,17 @@ const RetrievalConfig: FC<Props> = ({ }} > <Button - variant='ghost' - size='small' + variant="ghost" + size="small" disabled={readonly} className={cn(rerankModalOpen && 'bg-components-button-ghost-bg-hover')} > - <RiEqualizer2Line className='mr-1 h-3.5 w-3.5' /> + <RiEqualizer2Line className="mr-1 h-3.5 w-3.5" /> {t('dataset.retrievalSettings')} </Button> </PortalToFollowElemTrigger> <PortalToFollowElemContent style={{ zIndex: 1001 }}> - <div className='w-[404px] rounded-2xl border border-components-panel-border bg-components-panel-bg px-4 pb-4 pt-3 shadow-xl'> + <div className="w-[404px] rounded-2xl border border-components-panel-border bg-components-panel-bg px-4 pb-4 pt-3 shadow-xl"> <ConfigRetrievalContent datasetConfigs={datasetConfigs} onChange={handleChange} diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/default.ts b/web/app/components/workflow/nodes/knowledge-retrieval/default.ts index 72d67a1a4f..5e7798f1c6 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/default.ts +++ b/web/app/components/workflow/nodes/knowledge-retrieval/default.ts @@ -1,10 +1,11 @@ import type { NodeDefault } from '../../types' import type { KnowledgeRetrievalNodeType } from './types' -import { checkoutRerankModelConfiguredInRetrievalSettings } from './utils' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' import { DATASET_DEFAULT } from '@/config' import { RETRIEVE_TYPE } from '@/types/app' -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' +import { checkoutRerankModelConfiguredInRetrievalSettings } from './utils' + const i18nPrefix = 'workflow' const metaData = genNodeMetaData({ diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/hooks.ts b/web/app/components/workflow/nodes/knowledge-retrieval/hooks.ts index 139ac87382..00dcb4939b 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/hooks.ts +++ b/web/app/components/workflow/nodes/knowledge-retrieval/hooks.ts @@ -1,9 +1,9 @@ -import { useMemo } from 'react' -import { getSelectedDatasetsMode } from './utils' import type { DataSet, SelectedDatasetsMode, } from '@/models/datasets' +import { useMemo } from 'react' +import { getSelectedDatasetsMode } from './utils' export const useSelectedDatasetsMode = (datasets: DataSet[]) => { const selectedDatasetsMode: SelectedDatasetsMode = useMemo(() => { diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/node.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/node.tsx index da84baf201..55715f2fb0 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/node.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/node.tsx @@ -1,10 +1,10 @@ -import { type FC, useEffect, useState } from 'react' -import React from 'react' +import type { FC } from 'react' import type { KnowledgeRetrievalNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' import type { DataSet } from '@/models/datasets' -import { useDatasetsDetailStore } from '../../datasets-detail-store/store' +import React, { useEffect, useState } from 'react' import AppIcon from '@/app/components/base/app-icon' +import { useDatasetsDetailStore } from '../../datasets-detail-store/store' const Node: FC<NodeProps<KnowledgeRetrievalNodeType>> = ({ data, @@ -30,19 +30,19 @@ const Node: FC<NodeProps<KnowledgeRetrievalNodeType>> = ({ return null return ( - <div className='mb-1 px-3 py-1'> - <div className='space-y-0.5'> + <div className="mb-1 px-3 py-1"> + <div className="space-y-0.5"> {selectedDatasets.map(({ id, name, icon_info }) => ( - <div key={id} className='flex h-[26px] items-center gap-x-1 rounded-md bg-workflow-block-parma-bg px-1'> + <div key={id} className="flex h-[26px] items-center gap-x-1 rounded-md bg-workflow-block-parma-bg px-1"> <AppIcon - size='xs' + size="xs" iconType={icon_info.icon_type} icon={icon_info.icon} background={icon_info.icon_type === 'image' ? undefined : icon_info.icon_background} imageUrl={icon_info.icon_type === 'image' ? icon_info.icon_url : undefined} - className='shrink-0' + className="shrink-0" /> - <div className='system-xs-regular w-0 grow truncate text-text-secondary'> + <div className="system-xs-regular w-0 grow truncate text-text-secondary"> {name} </div> </div> diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/panel.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/panel.tsx index 0d46e2ebac..ff5a9e2292 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/panel.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/panel.tsx @@ -1,21 +1,21 @@ import type { FC } from 'react' +import type { KnowledgeRetrievalNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' +import { intersectionBy } from 'lodash-es' import { memo, useMemo, } from 'react' -import { intersectionBy } from 'lodash-es' import { useTranslation } from 'react-i18next' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' +import Split from '@/app/components/workflow/nodes/_base/components/split' import VarReferencePicker from '../_base/components/variable/var-reference-picker' -import useConfig from './use-config' -import RetrievalConfig from './components/retrieval-config' import AddKnowledge from './components/add-dataset' import DatasetList from './components/dataset-list' import MetadataFilter from './components/metadata/metadata-filter' -import type { KnowledgeRetrievalNodeType } from './types' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import Split from '@/app/components/workflow/nodes/_base/components/split' -import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' -import type { NodePanelProps } from '@/app/components/workflow/types' +import RetrievalConfig from './components/retrieval-config' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.knowledgeRetrieval' @@ -64,8 +64,8 @@ const Panel: FC<NodePanelProps<KnowledgeRetrievalNodeType>> = ({ }, [selectedDatasets]) return ( - <div className='pt-2'> - <div className='space-y-4 px-4 pb-2'> + <div className="pt-2"> + <div className="space-y-4 px-4 pb-2"> <Field title={t(`${i18nPrefix}.queryText`)}> <VarReferencePicker nodeId={id} @@ -93,8 +93,8 @@ const Panel: FC<NodePanelProps<KnowledgeRetrievalNodeType>> = ({ <Field title={t(`${i18nPrefix}.knowledge`)} required - operations={ - <div className='flex items-center space-x-1'> + operations={( + <div className="flex items-center space-x-1"> <RetrievalConfig payload={{ retrieval_mode: inputs.retrieval_mode, @@ -111,7 +111,7 @@ const Panel: FC<NodePanelProps<KnowledgeRetrievalNodeType>> = ({ onRerankModelOpenChange={setRerankModelOpen} selectedDatasets={selectedDatasets} /> - {!readOnly && (<div className='h-3 w-px bg-divider-regular'></div>)} + {!readOnly && (<div className="h-3 w-px bg-divider-regular"></div>)} {!readOnly && ( <AddKnowledge selectedIds={inputs.dataset_ids} @@ -119,7 +119,7 @@ const Panel: FC<NodePanelProps<KnowledgeRetrievalNodeType>> = ({ /> )} </div> - } + )} > <DatasetList list={selectedDatasets} @@ -128,7 +128,7 @@ const Panel: FC<NodePanelProps<KnowledgeRetrievalNodeType>> = ({ /> </Field> </div> - <div className='mb-2 py-2'> + <div className="mb-2 py-2"> <MetadataFilter metadataList={metadataList} selectedDatasetsLoaded={selectedDatasetsLoaded} @@ -153,8 +153,8 @@ const Panel: FC<NodePanelProps<KnowledgeRetrievalNodeType>> = ({ <OutputVars> <> <VarItem - name='result' - type='Array[Object]' + name="result" + type="Array[Object]" description={t(`${i18nPrefix}.outputVars.output`)} subItems={[ { diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/types.ts b/web/app/components/workflow/nodes/knowledge-retrieval/types.ts index 4e9b19dbe2..3b62a1e83f 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/types.ts +++ b/web/app/components/workflow/nodes/knowledge-retrieval/types.ts @@ -5,13 +5,13 @@ import type { NodeOutPutVar, ValueSelector, } from '@/app/components/workflow/types' -import type { RETRIEVE_TYPE } from '@/types/app' import type { DataSet, MetadataInDoc, RerankingModeEnum, WeightedScoreEnum, } from '@/models/datasets' +import type { RETRIEVE_TYPE } from '@/types/app' export type MultipleRetrievalConfig = { top_k: number @@ -122,13 +122,13 @@ export type MetadataShape = { handleToggleConditionLogicalOperator: HandleToggleConditionLogicalOperator handleUpdateCondition: HandleUpdateCondition metadataModelConfig?: ModelConfig - handleMetadataModelChange?: (model: { modelId: string; provider: string; mode?: string; features?: string[] }) => void + handleMetadataModelChange?: (model: { modelId: string, provider: string, mode?: string, features?: string[] }) => void handleMetadataCompletionParamsChange?: (params: Record<string, any>) => void availableStringVars?: NodeOutPutVar[] availableStringNodesWithParent?: Node[] availableNumberVars?: NodeOutPutVar[] availableNumberNodesWithParent?: Node[] isCommonVariable?: boolean - availableCommonStringVars?: { name: string; type: string; value: string }[] - availableCommonNumberVars?: { name: string; type: string; value: string }[] + availableCommonStringVars?: { name: string, type: string, value: string }[] + availableCommonNumberVars?: { name: string, type: string, value: string }[] } diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/use-config.ts b/web/app/components/workflow/nodes/knowledge-retrieval/use-config.ts index 94c28f680b..d0846b3a34 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/use-config.ts +++ b/web/app/components/workflow/nodes/knowledge-retrieval/use-config.ts @@ -1,20 +1,4 @@ -import { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react' -import { produce } from 'immer' -import { isEqual } from 'lodash-es' -import { v4 as uuid4 } from 'uuid' import type { ValueSelector, Var } from '../../types' -import { BlockEnum, VarType } from '../../types' -import { - useIsChatMode, - useNodesReadOnly, - useWorkflow, -} from '../../hooks' import type { HandleAddCondition, HandleRemoveCondition, @@ -24,6 +8,31 @@ import type { MetadataFilteringModeEnum, MultipleRetrievalConfig, } from './types' +import type { DataSet } from '@/models/datasets' +import { produce } from 'immer' +import { isEqual } from 'lodash-es' +import { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react' +import { v4 as uuid4 } from 'uuid' +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 useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { DATASET_DEFAULT } from '@/config' +import { fetchDatasets } from '@/service/datasets' +import { AppModeEnum, RETRIEVE_TYPE } from '@/types/app' +import { useDatasetsDetailStore } from '../../datasets-detail-store/store' +import { + useIsChatMode, + useNodesReadOnly, + useWorkflow, +} from '../../hooks' +import { BlockEnum, VarType } from '../../types' import { ComparisonOperator, LogicalOperator, @@ -33,15 +42,6 @@ import { getMultipleRetrievalConfig, getSelectedDatasetsMode, } from './utils' -import { AppModeEnum, RETRIEVE_TYPE } from '@/types/app' -import { DATASET_DEFAULT } from '@/config' -import type { DataSet } from '@/models/datasets' -import { fetchDatasets } from '@/service/datasets' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' -import { useCurrentProviderAndModel, useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' -import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' -import { useDatasetsDetailStore } from '../../datasets-detail-store/store' const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() @@ -97,13 +97,13 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => { rerankModelList, rerankDefaultModel ? { - ...rerankDefaultModel, - provider: rerankDefaultModel.provider.provider, - } + ...rerankDefaultModel, + provider: rerankDefaultModel.provider.provider, + } : undefined, ) - const handleModelChanged = useCallback((model: { provider: string; modelId: string; mode?: string }) => { + const handleModelChanged = useCallback((model: { provider: string, modelId: string, mode?: string }) => { const newInputs = produce(inputRef.current, (draft) => { if (!draft.single_retrieval_config) { draft.single_retrieval_config = { @@ -282,8 +282,9 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => { (allInternal && (mixtureHighQualityAndEconomic || inconsistentEmbeddingModel)) || mixtureInternalAndExternal || allExternal - ) + ) { setRerankModelOpen(true) + } }, [inputs, setInputs, payload.retrieval_mode, selectedDatasets, currentRerankModel, currentRerankProvider, updateDatasetsDetail]) const filterStringVar = useCallback((varPayload: Var) => { @@ -359,7 +360,7 @@ const useConfig = (id: string, payload: KnowledgeRetrievalNodeType) => { setInputs(newInputs) }, [setInputs]) - const handleMetadataModelChange = useCallback((model: { provider: string; modelId: string; mode?: string }) => { + const handleMetadataModelChange = useCallback((model: { provider: string, modelId: string, mode?: string }) => { const newInputs = produce(inputRef.current, (draft) => { draft.metadata_model_config = { provider: model.provider, diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/use-single-run-form-params.ts b/web/app/components/workflow/nodes/knowledge-retrieval/use-single-run-form-params.ts index 0f079bcee8..3ffd3c87d6 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/knowledge-retrieval/use-single-run-form-params.ts @@ -1,19 +1,19 @@ import type { RefObject } from 'react' -import { useTranslation } from 'react-i18next' -import type { InputVar, Var, Variable } from '@/app/components/workflow/types' -import { InputVarType, VarType } from '@/app/components/workflow/types' -import { useCallback, useMemo } from 'react' import type { KnowledgeRetrievalNodeType } from './types' import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form' -import { useDatasetsDetailStore } from '../../datasets-detail-store/store' +import type { InputVar, Var, Variable } from '@/app/components/workflow/types' import type { DataSet } from '@/models/datasets' +import { useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { InputVarType, VarType } from '@/app/components/workflow/types' +import { useDatasetsDetailStore } from '../../datasets-detail-store/store' import useAvailableVarList from '../_base/hooks/use-available-var-list' import { findVariableWhenOnLLMVision } from '../utils' const i18nPrefix = 'workflow.nodes.knowledgeRetrieval' type Params = { - id: string, + id: string payload: KnowledgeRetrievalNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/utils.ts b/web/app/components/workflow/nodes/knowledge-retrieval/utils.ts index 719aa57f2f..12cf8c053c 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/utils.ts +++ b/web/app/components/workflow/nodes/knowledge-retrieval/utils.ts @@ -1,19 +1,19 @@ -import { - uniq, - xorBy, -} from 'lodash-es' import type { MultipleRetrievalConfig } from './types' import type { DataSet, SelectedDatasetsMode, } from '@/models/datasets' +import { + uniq, + xorBy, +} from 'lodash-es' +import { DATASET_DEFAULT } from '@/config' import { DEFAULT_WEIGHTED_SCORE, RerankingModeEnum, WeightedScoreEnum, } from '@/models/datasets' import { RETRIEVE_METHOD } from '@/types/app' -import { DATASET_DEFAULT } from '@/config' export const checkNodeValid = () => { return true @@ -94,7 +94,7 @@ export const getMultipleRetrievalConfig = ( multipleRetrievalConfig: MultipleRetrievalConfig, selectedDatasets: DataSet[], originalDatasets: DataSet[], - fallbackRerankModel?: { provider?: string; model?: string }, // fallback rerank model + fallbackRerankModel?: { provider?: string, model?: string }, // fallback rerank model ) => { // Check if the selected datasets are different from the original datasets const isDatasetsChanged = xorBy(selectedDatasets, originalDatasets, 'id').length > 0 diff --git a/web/app/components/workflow/nodes/list-operator/components/extract-input.tsx b/web/app/components/workflow/nodes/list-operator/components/extract-input.tsx index 66f23829e8..7d4b472fd3 100644 --- a/web/app/components/workflow/nodes/list-operator/components/extract-input.tsx +++ b/web/app/components/workflow/nodes/list-operator/components/extract-input.tsx @@ -1,12 +1,12 @@ 'use client' import type { FC } from 'react' +import type { Var } from '../../../types' import React, { useState } from 'react' import { useTranslation } from 'react-i18next' -import { VarType } from '../../../types' -import type { Var } from '../../../types' +import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' import { cn } from '@/utils/classnames' -import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' +import { VarType } from '../../../types' type Props = { nodeId: string @@ -32,9 +32,9 @@ const ExtractInput: FC<Props> = ({ }) return ( - <div className='flex items-start space-x-1'> + <div className="flex items-start space-x-1"> <Input - instanceId='http-extract-number' + instanceId="http-extract-number" className={cn(isFocus ? 'border-components-input-border-active bg-components-input-bg-active shadow-xs' : 'border-components-input-border-hover bg-components-input-bg-normal', 'w-0 grow rounded-lg border px-3 py-[6px]')} value={value} onChange={onChange} @@ -43,9 +43,9 @@ const ExtractInput: FC<Props> = ({ availableNodes={availableNodesWithParent} onFocusChange={setIsFocus} placeholder={!readOnly ? t('workflow.nodes.http.extractListPlaceholder')! : ''} - placeholderClassName='!leading-[21px]' + placeholderClassName="!leading-[21px]" /> - </div > + </div> ) } export default React.memo(ExtractInput) diff --git a/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx b/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx index a51aefe9c6..d77a0c3eb3 100644 --- a/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx +++ b/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx @@ -1,22 +1,22 @@ 'use client' import type { FC } from 'react' +import type { Condition } from '../types' import React, { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' +import { SimpleSelect as Select } from '@/app/components/base/select' +import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' +import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' +import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '@/app/components/workflow/nodes/constants' +import { getConditionValueAsString } from '@/app/components/workflow/nodes/utils' +import { cn } from '@/utils/classnames' +import BoolValue from '../../../panel/chat-variable-panel/components/bool-value' +import { VarType } from '../../../types' import ConditionOperator from '../../if-else/components/condition-list/condition-operator' -import type { Condition } from '../types' import { ComparisonOperator } from '../../if-else/types' import { comparisonOperatorNotRequireValue, getOperators } from '../../if-else/utils' import SubVariablePicker from './sub-variable-picker' -import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from '@/app/components/workflow/nodes/constants' -import { SimpleSelect as Select } from '@/app/components/base/select' -import BoolValue from '../../../panel/chat-variable-panel/components/bool-value' -import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' -import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' -import { cn } from '@/utils/classnames' -import { VarType } from '../../../types' const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName' -import { getConditionValueAsString } from '@/app/components/workflow/nodes/utils' const VAR_INPUT_SUPPORTED_KEYS: Record<string, VarType> = { name: VarType.string, @@ -108,22 +108,24 @@ const FilterCondition: FC<Props> = ({ items={selectOptions} defaultValue={isArrayValue ? (condition.value as string[])[0] : condition.value as string} onSelect={item => handleChange('value')(item.value)} - className='!text-[13px]' - wrapperClassName='grow h-8' - placeholder='Select value' + className="!text-[13px]" + wrapperClassName="grow h-8" + placeholder="Select value" /> ) } else if (isBoolean) { - inputElement = (<BoolValue - value={condition.value as boolean} - onChange={handleChange('value')} - />) + inputElement = ( + <BoolValue + value={condition.value as boolean} + onChange={handleChange('value')} + /> + ) } else if (supportVariableInput) { inputElement = ( <Input - instanceId='filter-condition-input' + instanceId="filter-condition-input" className={cn( isFocus ? 'border-components-input-border-active bg-components-input-bg-active shadow-xs' @@ -139,7 +141,7 @@ const FilterCondition: FC<Props> = ({ availableNodes={availableNodesWithParent} onFocusChange={setIsFocus} placeholder={!readOnly ? t('workflow.nodes.http.insertVarPlaceholder')! : ''} - placeholderClassName='!leading-[21px]' + placeholderClassName="!leading-[21px]" /> ) } @@ -147,7 +149,7 @@ const FilterCondition: FC<Props> = ({ inputElement = ( <input type={((hasSubVariable && condition.key === 'size') || (!hasSubVariable && varType === VarType.number)) ? 'number' : 'text'} - className='grow rounded-lg border border-components-input-border-hover bg-components-input-bg-normal px-3 py-[6px]' + className="grow rounded-lg border border-components-input-border-hover bg-components-input-bg-normal px-3 py-[6px]" value={ getConditionValueAsString(condition) } @@ -167,9 +169,9 @@ const FilterCondition: FC<Props> = ({ onChange={handleSubVariableChange} /> )} - <div className='flex space-x-1'> + <div className="flex space-x-1"> <ConditionOperator - className='h-8 bg-components-input-bg-normal' + className="h-8 bg-components-input-bg-normal" varType={expectedVarType ?? varType ?? VarType.string} value={condition.comparison_operator} onSelect={handleChange('comparison_operator')} diff --git a/web/app/components/workflow/nodes/list-operator/components/limit-config.tsx b/web/app/components/workflow/nodes/list-operator/components/limit-config.tsx index 6a54eac8ab..f7356b58aa 100644 --- a/web/app/components/workflow/nodes/list-operator/components/limit-config.tsx +++ b/web/app/components/workflow/nodes/list-operator/components/limit-config.tsx @@ -1,12 +1,12 @@ 'use client' import type { FC } from 'react' +import type { Limit } from '../types' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import type { Limit } from '../types' -import InputNumberWithSlider from '../../_base/components/input-number-with-slider' -import { cn } from '@/utils/classnames' -import Field from '@/app/components/workflow/nodes/_base/components/field' import Switch from '@/app/components/base/switch' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import { cn } from '@/utils/classnames' +import InputNumberWithSlider from '../../_base/components/input-number-with-slider' const i18nPrefix = 'workflow.nodes.listFilter' const LIMIT_SIZE_MIN = 1 @@ -53,25 +53,25 @@ const LimitConfig: FC<Props> = ({ <div className={cn(className)}> <Field title={t(`${i18nPrefix}.limit`)} - operations={ + operations={( <Switch defaultValue={payload.enabled} onChange={handleLimitEnabledChange} - size='md' + size="md" disabled={readonly} /> - } + )} > {payload?.enabled ? ( - <InputNumberWithSlider - value={payload?.size || LIMIT_SIZE_DEFAULT} - min={LIMIT_SIZE_MIN} - max={LIMIT_SIZE_MAX} - onChange={handleLimitSizeChange} - readonly={readonly || !payload?.enabled} - /> - ) + <InputNumberWithSlider + value={payload?.size || LIMIT_SIZE_DEFAULT} + min={LIMIT_SIZE_MIN} + max={LIMIT_SIZE_MAX} + onChange={handleLimitSizeChange} + readonly={readonly || !payload?.enabled} + /> + ) : null} </Field> </div> diff --git a/web/app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx b/web/app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx index 9d835436d9..ee8703ca6c 100644 --- a/web/app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx +++ b/web/app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx @@ -1,12 +1,12 @@ 'use client' import type { FC } from 'react' +import type { Item } from '@/app/components/base/select' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { SUB_VARIABLES } from '../../constants' -import type { Item } from '@/app/components/base/select' -import { SimpleSelect as Select } from '@/app/components/base/select' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' +import { SimpleSelect as Select } from '@/app/components/base/select' import { cn } from '@/utils/classnames' +import { SUB_VARIABLES } from '../../constants' type Props = { value: string @@ -27,12 +27,12 @@ const SubVariablePicker: FC<Props> = ({ const renderOption = ({ item }: { item: Record<string, any> }) => { return ( - <div className='flex h-6 items-center justify-between'> - <div className='flex h-full items-center'> - <Variable02 className='mr-[5px] h-3.5 w-3.5 text-text-accent' /> - <span className='system-sm-medium text-text-secondary'>{item.name}</span> + <div className="flex h-6 items-center justify-between"> + <div className="flex h-full items-center"> + <Variable02 className="mr-[5px] h-3.5 w-3.5 text-text-accent" /> + <span className="system-sm-medium text-text-secondary">{item.name}</span> </div> - <span className='system-xs-regular text-text-tertiary'>{item.type}</span> + <span className="system-xs-regular text-text-tertiary">{item.type}</span> </div> ) } @@ -47,23 +47,27 @@ const SubVariablePicker: FC<Props> = ({ items={subVarOptions} defaultValue={value} onSelect={handleChange} - className='!text-[13px]' + className="!text-[13px]" placeholder={t('workflow.nodes.listFilter.selectVariableKeyPlaceholder')!} - optionClassName='pl-1 pr-5 py-0' + optionClassName="pl-1 pr-5 py-0" renderOption={renderOption} renderTrigger={item => ( - <div className='group/sub-variable-picker flex h-8 items-center rounded-lg bg-components-input-bg-normal pl-1 hover:bg-state-base-hover-alt'> + <div className="group/sub-variable-picker flex h-8 items-center rounded-lg bg-components-input-bg-normal pl-1 hover:bg-state-base-hover-alt"> {item - ? <div className='flex cursor-pointer justify-start'> - <div className='inline-flex h-6 max-w-full items-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark px-1.5 text-text-accent shadow-xs'> - <Variable02 className='h-3.5 w-3.5 shrink-0 text-text-accent' /> - <div className='system-xs-medium ml-0.5 truncate'>{item?.name}</div> - </div> - </div> - : <div className='system-sm-regular flex pl-1 text-components-input-text-placeholder group-hover/sub-variable-picker:text-text-tertiary'> - <Variable02 className='mr-1 h-4 w-4 shrink-0' /> - <span>{t('common.placeholder.select')}</span> - </div>} + ? ( + <div className="flex cursor-pointer justify-start"> + <div className="inline-flex h-6 max-w-full items-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark px-1.5 text-text-accent shadow-xs"> + <Variable02 className="h-3.5 w-3.5 shrink-0 text-text-accent" /> + <div className="system-xs-medium ml-0.5 truncate">{item?.name}</div> + </div> + </div> + ) + : ( + <div className="system-sm-regular flex pl-1 text-components-input-text-placeholder group-hover/sub-variable-picker:text-text-tertiary"> + <Variable02 className="mr-1 h-4 w-4 shrink-0" /> + <span>{t('common.placeholder.select')}</span> + </div> + )} </div> )} /> diff --git a/web/app/components/workflow/nodes/list-operator/default.ts b/web/app/components/workflow/nodes/list-operator/default.ts index 816e225046..3ab7ce0d31 100644 --- a/web/app/components/workflow/nodes/list-operator/default.ts +++ b/web/app/components/workflow/nodes/list-operator/default.ts @@ -1,9 +1,11 @@ -import { BlockEnum, VarType } from '../../types' import type { NodeDefault } from '../../types' -import { comparisonOperatorNotRequireValue } from '../if-else/utils' -import { type ListFilterNodeType, OrderBy } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' +import type { ListFilterNodeType } from './types' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' +import { BlockEnum, VarType } from '../../types' +import { comparisonOperatorNotRequireValue } from '../if-else/utils' +import { OrderBy } from './types' + const i18nPrefix = 'workflow.errorMsg' const metaData = genNodeMetaData({ diff --git a/web/app/components/workflow/nodes/list-operator/node.tsx b/web/app/components/workflow/nodes/list-operator/node.tsx index 3c59f36587..4d02595fcf 100644 --- a/web/app/components/workflow/nodes/list-operator/node.tsx +++ b/web/app/components/workflow/nodes/list-operator/node.tsx @@ -1,13 +1,14 @@ import type { FC } from 'react' -import React from 'react' -import { useNodes } from 'reactflow' -import { useTranslation } from 'react-i18next' import type { ListFilterNodeType } from './types' +import type { Node, NodeProps } from '@/app/components/workflow/types' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { useNodes } from 'reactflow' import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' -import { BlockEnum, type Node, type NodeProps } from '@/app/components/workflow/types' import { VariableLabelInNode, } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' +import { BlockEnum } from '@/app/components/workflow/types' const i18nPrefix = 'workflow.nodes.listFilter' @@ -25,8 +26,8 @@ const NodeComponent: FC<NodeProps<ListFilterNodeType>> = ({ const isSystem = isSystemVar(variable) const node = isSystem ? nodes.find(node => node.data.type === BlockEnum.Start) : nodes.find(node => node.id === variable[0]) return ( - <div className='relative px-3'> - <div className='system-2xs-medium-uppercase mb-1 text-text-tertiary'>{t(`${i18nPrefix}.inputVar`)}</div> + <div className="relative px-3"> + <div className="system-2xs-medium-uppercase mb-1 text-text-tertiary">{t(`${i18nPrefix}.inputVar`)}</div> <VariableLabelInNode variables={variable} nodeType={node?.data.type} diff --git a/web/app/components/workflow/nodes/list-operator/panel.tsx b/web/app/components/workflow/nodes/list-operator/panel.tsx index e76befcac0..be1d79dcad 100644 --- a/web/app/components/workflow/nodes/list-operator/panel.tsx +++ b/web/app/components/workflow/nodes/list-operator/panel.tsx @@ -1,19 +1,20 @@ import type { FC } from 'react' +import type { ListFilterNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import VarReferencePicker from '../_base/components/variable/var-reference-picker' -import OutputVars, { VarItem } from '../_base/components/output-vars' -import OptionCard from '../_base/components/option-card' -import Split from '../_base/components/split' -import useConfig from './use-config' -import SubVariablePicker from './components/sub-variable-picker' -import { type ListFilterNodeType, OrderBy } from './types' -import LimitConfig from './components/limit-config' -import FilterCondition from './components/filter-condition' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import type { NodePanelProps } from '@/app/components/workflow/types' import Switch from '@/app/components/base/switch' +import Field from '@/app/components/workflow/nodes/_base/components/field' import ExtractInput from '@/app/components/workflow/nodes/list-operator/components/extract-input' +import OptionCard from '../_base/components/option-card' +import OutputVars, { VarItem } from '../_base/components/output-vars' +import Split from '../_base/components/split' +import VarReferencePicker from '../_base/components/variable/var-reference-picker' +import FilterCondition from './components/filter-condition' +import LimitConfig from './components/limit-config' +import SubVariablePicker from './components/sub-variable-picker' +import { OrderBy } from './types' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.listFilter' @@ -42,8 +43,8 @@ const Panel: FC<NodePanelProps<ListFilterNodeType>> = ({ } = useConfig(id, data) return ( - <div className='pt-2'> - <div className='space-y-4 px-4'> + <div className="pt-2"> + <div className="space-y-4 px-4"> <Field title={t(`${i18nPrefix}.inputVar`)} required @@ -56,59 +57,59 @@ const Panel: FC<NodePanelProps<ListFilterNodeType>> = ({ onChange={handleVarChanges} filterVar={filterVar} isSupportFileVar={false} - typePlaceHolder='Array' + typePlaceHolder="Array" /> </Field> <Field title={t(`${i18nPrefix}.filterCondition`)} - operations={ + operations={( <Switch defaultValue={inputs.filter_by?.enabled} onChange={handleFilterEnabledChange} - size='md' + size="md" disabled={readOnly} /> - } + )} > {inputs.filter_by?.enabled ? ( - <FilterCondition - condition={inputs.filter_by.conditions[0]} - onChange={handleFilterChange} - varType={itemVarType} - hasSubVariable={hasSubVariable} - readOnly={readOnly} - nodeId={id} - /> - ) + <FilterCondition + condition={inputs.filter_by.conditions[0]} + onChange={handleFilterChange} + varType={itemVarType} + hasSubVariable={hasSubVariable} + readOnly={readOnly} + nodeId={id} + /> + ) : null} </Field> <Split /> <Field title={t(`${i18nPrefix}.extractsCondition`)} - operations={ + operations={( <Switch defaultValue={inputs.extract_by?.enabled} onChange={handleExtractsEnabledChange} - size='md' + size="md" disabled={readOnly} /> - } + )} > {inputs.extract_by?.enabled ? ( - <div className='flex items-center justify-between'> - <div className='mr-2 grow'> - <ExtractInput - value={inputs.extract_by.serial as string} - onChange={handleExtractsChange} - readOnly={readOnly} - nodeId={id} - /> + <div className="flex items-center justify-between"> + <div className="mr-2 grow"> + <ExtractInput + value={inputs.extract_by.serial as string} + onChange={handleExtractsChange} + readOnly={readOnly} + nodeId={id} + /> + </div> </div> - </div> - ) + ) : null} </Field> <Split /> @@ -120,40 +121,40 @@ const Panel: FC<NodePanelProps<ListFilterNodeType>> = ({ <Split /> <Field title={t(`${i18nPrefix}.orderBy`)} - operations={ + operations={( <Switch defaultValue={inputs.order_by?.enabled} onChange={handleOrderByEnabledChange} - size='md' + size="md" disabled={readOnly} /> - } + )} > {inputs.order_by?.enabled ? ( - <div className='flex items-center justify-between'> - {hasSubVariable && ( - <div className='mr-2 grow'> - <SubVariablePicker - value={inputs.order_by.key as string} - onChange={handleOrderByKeyChange} + <div className="flex items-center justify-between"> + {hasSubVariable && ( + <div className="mr-2 grow"> + <SubVariablePicker + value={inputs.order_by.key as string} + onChange={handleOrderByKeyChange} + /> + </div> + )} + <div className={!hasSubVariable ? 'grid w-full grid-cols-2 gap-1' : 'flex shrink-0 space-x-1'}> + <OptionCard + title={t(`${i18nPrefix}.asc`)} + onSelect={handleOrderByTypeChange(OrderBy.ASC)} + selected={inputs.order_by.value === OrderBy.ASC} + /> + <OptionCard + title={t(`${i18nPrefix}.desc`)} + onSelect={handleOrderByTypeChange(OrderBy.DESC)} + selected={inputs.order_by.value === OrderBy.DESC} /> </div> - )} - <div className={!hasSubVariable ? 'grid w-full grid-cols-2 gap-1' : 'flex shrink-0 space-x-1'}> - <OptionCard - title={t(`${i18nPrefix}.asc`)} - onSelect={handleOrderByTypeChange(OrderBy.ASC)} - selected={inputs.order_by.value === OrderBy.ASC} - /> - <OptionCard - title={t(`${i18nPrefix}.desc`)} - onSelect={handleOrderByTypeChange(OrderBy.DESC)} - selected={inputs.order_by.value === OrderBy.DESC} - /> </div> - </div> - ) + ) : null} </Field> <Split /> @@ -162,17 +163,17 @@ const Panel: FC<NodePanelProps<ListFilterNodeType>> = ({ <OutputVars> <> <VarItem - name='result' + name="result" type={`Array[${itemVarTypeShowName}]`} description={t(`${i18nPrefix}.outputVars.result`)} /> <VarItem - name='first_record' + name="first_record" type={itemVarTypeShowName} description={t(`${i18nPrefix}.outputVars.first_record`)} /> <VarItem - name='last_record' + name="last_record" type={itemVarTypeShowName} description={t(`${i18nPrefix}.outputVars.last_record`)} /> diff --git a/web/app/components/workflow/nodes/list-operator/use-config.ts b/web/app/components/workflow/nodes/list-operator/use-config.ts index eff249b717..72f92bfea4 100644 --- a/web/app/components/workflow/nodes/list-operator/use-config.ts +++ b/web/app/components/workflow/nodes/list-operator/use-config.ts @@ -1,18 +1,18 @@ -import { useCallback, useMemo } from 'react' -import { produce } from 'immer' -import { useStoreApi } from 'reactflow' import type { ValueSelector, Var } from '../../types' -import { VarType } from '../../types' -import { getOperators } from '../if-else/utils' -import { OrderBy } from './types' import type { Condition, Limit, ListFilterNodeType } from './types' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { produce } from 'immer' +import { useCallback, useMemo } from 'react' +import { useStoreApi } from 'reactflow' import { useIsChatMode, useNodesReadOnly, useWorkflow, useWorkflowVariables, } from '@/app/components/workflow/hooks' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { VarType } from '../../types' +import { getOperators } from '../if-else/utils' +import { OrderBy } from './types' const useConfig = (id: string, payload: ListFilterNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() diff --git a/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx b/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx index 88ba95f76d..a712d6e408 100644 --- a/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx +++ b/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx @@ -1,14 +1,14 @@ 'use client' import type { FC } from 'react' +import type { ModelConfig, PromptItem, Variable } from '../../../types' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import type { ModelConfig, PromptItem, Variable } from '../../../types' -import { EditionType } from '../../../types' -import { useWorkflowStore } from '../../../store' +import Tooltip from '@/app/components/base/tooltip' import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' import TypeSelector from '@/app/components/workflow/nodes/_base/components/selector' -import Tooltip from '@/app/components/base/tooltip' import { PromptRole } from '@/models/debug' +import { useWorkflowStore } from '../../../store' +import { EditionType } from '../../../types' const i18nPrefix = 'workflow.nodes.llm' @@ -99,31 +99,33 @@ const ConfigPromptItem: FC<Props> = ({ headerClassName={headerClassName} instanceId={instanceId} key={instanceId} - title={ - <div className='relative left-1 flex items-center'> + title={( + <div className="relative left-1 flex items-center"> {payload.role === PromptRole.system - ? (<div className='relative left-[-4px] text-xs font-semibold uppercase text-text-secondary'> - SYSTEM - </div>) + ? ( + <div className="relative left-[-4px] text-xs font-semibold uppercase text-text-secondary"> + SYSTEM + </div> + ) : ( - <TypeSelector - value={payload.role as string} - allOptions={roleOptions} - options={canNotChooseSystemRole ? roleOptionsWithoutSystemRole : roleOptions} - onChange={handleChatModeMessageRoleChange} - triggerClassName='text-xs font-semibold text-text-secondary uppercase' - itemClassName='text-[13px] font-medium text-text-secondary' - /> - )} + <TypeSelector + value={payload.role as string} + allOptions={roleOptions} + options={canNotChooseSystemRole ? roleOptionsWithoutSystemRole : roleOptions} + onChange={handleChatModeMessageRoleChange} + triggerClassName="text-xs font-semibold text-text-secondary uppercase" + itemClassName="text-[13px] font-medium text-text-secondary" + /> + )} <Tooltip popupContent={ - <div className='max-w-[180px]'>{t(`${i18nPrefix}.roleDescription.${payload.role}`)}</div> + <div className="max-w-[180px]">{t(`${i18nPrefix}.roleDescription.${payload.role}`)}</div> } - triggerClassName='w-4 h-4' + triggerClassName="w-4 h-4" /> </div> - } + )} value={payload.edition_type === EditionType.jinja2 ? (payload.jinja2_text || '') : payload.text} onChange={onPromptChange} readOnly={readOnly} diff --git a/web/app/components/workflow/nodes/llm/components/config-prompt.tsx b/web/app/components/workflow/nodes/llm/components/config-prompt.tsx index d44d299bc4..856b88ac00 100644 --- a/web/app/components/workflow/nodes/llm/components/config-prompt.tsx +++ b/web/app/components/workflow/nodes/llm/components/config-prompt.tsx @@ -1,19 +1,19 @@ 'use client' import type { FC } from 'react' +import type { ModelConfig, PromptItem, ValueSelector, Var, Variable } from '../../../types' +import { produce } from 'immer' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { produce } from 'immer' import { ReactSortable } from 'react-sortablejs' import { v4 as uuid4 } from 'uuid' -import type { ModelConfig, PromptItem, ValueSelector, Var, Variable } from '../../../types' +import { DragHandle } from '@/app/components/base/icons/src/vender/line/others' +import AddButton from '@/app/components/workflow/nodes/_base/components/add-button' +import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' +import { cn } from '@/utils/classnames' +import { useWorkflowStore } from '../../../store' import { EditionType, PromptRole } from '../../../types' import useAvailableVarList from '../../_base/hooks/use-available-var-list' -import { useWorkflowStore } from '../../../store' import ConfigPromptItem from './config-prompt-item' -import { cn } from '@/utils/classnames' -import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' -import AddButton from '@/app/components/workflow/nodes/_base/components/add-button' -import { DragHandle } from '@/app/components/base/icons/src/vender/line/others' const i18nPrefix = 'workflow.nodes.llm' @@ -57,15 +57,15 @@ const ConfigPrompt: FC<Props> = ({ } = workflowStore.getState() const payloadWithIds = (isChatModel && Array.isArray(payload)) ? payload.map((item) => { - const id = uuid4() - return { - id: item.id || id, - p: { - ...item, + const id = uuid4() + return { id: item.id || id, - }, - } - }) + p: { + ...item, + id: item.id || id, + }, + } + }) : [] const { availableVars, @@ -153,96 +153,97 @@ const ConfigPrompt: FC<Props> = ({ <div> {(isChatModel && Array.isArray(payload)) ? ( - <div> - <div className='space-y-2'> - <ReactSortable className="space-y-1" - list={payloadWithIds} - setList={(list) => { - if ((payload as PromptItem[])?.[0]?.role === PromptRole.system && list[0].p?.role !== PromptRole.system) - return + <div> + <div className="space-y-2"> + <ReactSortable + className="space-y-1" + list={payloadWithIds} + setList={(list) => { + if ((payload as PromptItem[])?.[0]?.role === PromptRole.system && list[0].p?.role !== PromptRole.system) + return - onChange(list.map(item => item.p)) - }} - handle='.handle' - ghostClass="opacity-50" - animation={150} - > - { - (payload as PromptItem[]).map((item, index) => { - const canDrag = (() => { - if (readOnly) - return false + onChange(list.map(item => item.p)) + }} + handle=".handle" + ghostClass="opacity-50" + animation={150} + > + { + (payload as PromptItem[]).map((item, index) => { + const canDrag = (() => { + if (readOnly) + return false - if (index === 0 && item.role === PromptRole.system) - return false + if (index === 0 && item.role === PromptRole.system) + return false - return true - })() - return ( - <div key={item.id || index} className='group relative'> - {canDrag && <DragHandle className='absolute left-[-14px] top-2 hidden h-3.5 w-3.5 text-text-quaternary group-hover:block' />} - <ConfigPromptItem - instanceId={item.role === PromptRole.system ? `${nodeId}-chat-workflow-llm-prompt-editor` : `${nodeId}-chat-workflow-llm-prompt-editor-${index}`} - className={cn(canDrag && 'handle')} - headerClassName={cn(canDrag && 'cursor-grab')} - canNotChooseSystemRole={!canChooseSystemRole} - canRemove={payload.length > 1 && !(index === 0 && item.role === PromptRole.system)} - readOnly={readOnly} - id={item.id!} - nodeId={nodeId} - handleChatModeMessageRoleChange={handleChatModeMessageRoleChange(index)} - isChatModel={isChatModel} - isChatApp={isChatApp} - payload={item} - onPromptChange={handleChatModePromptChange(index)} - onEditionTypeChange={handleChatModeEditionTypeChange(index)} - onRemove={handleRemove(index)} - isShowContext={isShowContext} - hasSetBlockStatus={hasSetBlockStatus} - availableVars={availableVars} - availableNodes={availableNodesWithParent} - varList={varList} - handleAddVariable={handleAddVariable} - modelConfig={modelConfig} - /> - </div> - ) - }) - } - </ReactSortable> + return true + })() + return ( + <div key={item.id || index} className="group relative"> + {canDrag && <DragHandle className="absolute left-[-14px] top-2 hidden h-3.5 w-3.5 text-text-quaternary group-hover:block" />} + <ConfigPromptItem + instanceId={item.role === PromptRole.system ? `${nodeId}-chat-workflow-llm-prompt-editor` : `${nodeId}-chat-workflow-llm-prompt-editor-${index}`} + className={cn(canDrag && 'handle')} + headerClassName={cn(canDrag && 'cursor-grab')} + canNotChooseSystemRole={!canChooseSystemRole} + canRemove={payload.length > 1 && !(index === 0 && item.role === PromptRole.system)} + readOnly={readOnly} + id={item.id!} + nodeId={nodeId} + handleChatModeMessageRoleChange={handleChatModeMessageRoleChange(index)} + isChatModel={isChatModel} + isChatApp={isChatApp} + payload={item} + onPromptChange={handleChatModePromptChange(index)} + onEditionTypeChange={handleChatModeEditionTypeChange(index)} + onRemove={handleRemove(index)} + isShowContext={isShowContext} + hasSetBlockStatus={hasSetBlockStatus} + availableVars={availableVars} + availableNodes={availableNodesWithParent} + varList={varList} + handleAddVariable={handleAddVariable} + modelConfig={modelConfig} + /> + </div> + ) + }) + } + </ReactSortable> + </div> + <AddButton + className="mt-2" + text={t(`${i18nPrefix}.addMessage`)} + onClick={handleAddPrompt} + /> </div> - <AddButton - className='mt-2' - text={t(`${i18nPrefix}.addMessage`)} - onClick={handleAddPrompt} - /> - </div> - ) + ) : ( - <div> - <Editor - instanceId={`${nodeId}-chat-workflow-llm-prompt-editor`} - title={<span className='capitalize'>{t(`${i18nPrefix}.prompt`)}</span>} - value={((payload as PromptItem).edition_type === EditionType.basic || !(payload as PromptItem).edition_type) ? (payload as PromptItem).text : ((payload as PromptItem).jinja2_text || '')} - onChange={handleCompletionPromptChange} - readOnly={readOnly} - isChatModel={isChatModel} - isChatApp={isChatApp} - isShowContext={isShowContext} - hasSetBlockStatus={hasSetBlockStatus} - nodesOutputVars={availableVars} - availableNodes={availableNodesWithParent} - isSupportPromptGenerator - isSupportJinja - editionType={(payload as PromptItem).edition_type} - varList={varList} - onEditionTypeChange={handleCompletionEditionTypeChange} - handleAddVariable={handleAddVariable} - onGenerated={handleGenerated} - modelConfig={modelConfig} - /> - </div> - )} + <div> + <Editor + instanceId={`${nodeId}-chat-workflow-llm-prompt-editor`} + title={<span className="capitalize">{t(`${i18nPrefix}.prompt`)}</span>} + value={((payload as PromptItem).edition_type === EditionType.basic || !(payload as PromptItem).edition_type) ? (payload as PromptItem).text : ((payload as PromptItem).jinja2_text || '')} + onChange={handleCompletionPromptChange} + readOnly={readOnly} + isChatModel={isChatModel} + isChatApp={isChatApp} + isShowContext={isShowContext} + hasSetBlockStatus={hasSetBlockStatus} + nodesOutputVars={availableVars} + availableNodes={availableNodesWithParent} + isSupportPromptGenerator + isSupportJinja + editionType={(payload as PromptItem).edition_type} + varList={varList} + onEditionTypeChange={handleCompletionEditionTypeChange} + handleAddVariable={handleAddVariable} + onGenerated={handleGenerated} + modelConfig={modelConfig} + /> + </div> + )} </div> ) } diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor.tsx index 3ca2162206..72620ee233 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor.tsx @@ -1,12 +1,13 @@ -import React, { type FC, useCallback, useEffect, useMemo, useRef } from 'react' -import useTheme from '@/hooks/use-theme' -import { Theme } from '@/types/app' -import { cn } from '@/utils/classnames' +import type { FC } from 'react' import { Editor } from '@monaco-editor/react' import { RiClipboardLine, RiIndentIncrease } from '@remixicon/react' import copy from 'copy-to-clipboard' -import Tooltip from '@/app/components/base/tooltip' +import React, { useCallback, useEffect, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' +import Tooltip from '@/app/components/base/tooltip' +import useTheme from '@/hooks/use-theme' +import { Theme } from '@/types/app' +import { cn } from '@/utils/classnames' type CodeEditorProps = { value: string @@ -114,28 +115,29 @@ const CodeEditor: FC<CodeEditorProps> = ({ return ( <div className={cn('flex h-full flex-col overflow-hidden bg-components-input-bg-normal', hideTopMenu && 'pt-2', className)}> {!hideTopMenu && ( - <div className='flex items-center justify-between pl-2 pr-1 pt-1'> - <div className='system-xs-semibold-uppercase py-0.5 text-text-secondary'> - <span className='px-1 py-0.5'>JSON</span> + <div className="flex items-center justify-between pl-2 pr-1 pt-1"> + <div className="system-xs-semibold-uppercase py-0.5 text-text-secondary"> + <span className="px-1 py-0.5">JSON</span> </div> - <div className='flex items-center gap-x-0.5'> + <div className="flex items-center gap-x-0.5"> {showFormatButton && ( <Tooltip popupContent={t('common.operation.format')}> <button - type='button' - className='flex h-6 w-6 items-center justify-center' + type="button" + className="flex h-6 w-6 items-center justify-center" onClick={formatJsonContent} > - <RiIndentIncrease className='h-4 w-4 text-text-tertiary' /> + <RiIndentIncrease className="h-4 w-4 text-text-tertiary" /> </button> </Tooltip> )} <Tooltip popupContent={t('common.operation.copy')}> <button - type='button' - className='flex h-6 w-6 items-center justify-center' - onClick={() => copy(value)}> - <RiClipboardLine className='h-4 w-4 text-text-tertiary' /> + type="button" + className="flex h-6 w-6 items-center justify-center" + onClick={() => copy(value)} + > + <RiClipboardLine className="h-4 w-4 text-text-tertiary" /> </button> </Tooltip> </div> @@ -144,7 +146,7 @@ const CodeEditor: FC<CodeEditorProps> = ({ {topContent} <div className={cn('relative overflow-hidden', editorWrapperClassName)}> <Editor - defaultLanguage='json' + defaultLanguage="json" theme={isMounted ? editorTheme : 'default-theme'} // sometimes not load the default theme value={value} onChange={handleEditorChange} diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/error-message.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/error-message.tsx index 937165df1c..5cb2a421d5 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/error-message.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/error-message.tsx @@ -1,6 +1,6 @@ -import React from 'react' import type { FC } from 'react' import { RiErrorWarningFill } from '@remixicon/react' +import React from 'react' import { cn } from '@/utils/classnames' type ErrorMessageProps = { @@ -12,10 +12,9 @@ const ErrorMessage: FC<ErrorMessageProps> = ({ className, }) => { return ( - <div className={cn('mt-1 flex gap-x-1 rounded-lg border-[0.5px] border-components-panel-border bg-toast-error-bg p-2', - className)}> - <RiErrorWarningFill className='h-4 w-4 shrink-0 text-text-destructive' /> - <div className='system-xs-medium max-h-12 grow overflow-y-auto whitespace-pre-line break-words text-text-primary'> + <div className={cn('mt-1 flex gap-x-1 rounded-lg border-[0.5px] border-components-panel-border bg-toast-error-bg p-2', className)}> + <RiErrorWarningFill className="h-4 w-4 shrink-0 text-text-destructive" /> + <div className="system-xs-medium max-h-12 grow overflow-y-auto whitespace-pre-line break-words text-text-primary"> {message} </div> </div> diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx index d34836d5b2..b7a0a40f32 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx @@ -1,6 +1,7 @@ -import React, { type FC } from 'react' -import Modal from '../../../../../base/modal' +import type { FC } from 'react' import type { SchemaRoot } from '../../types' +import React from 'react' +import Modal from '../../../../../base/modal' import JsonSchemaConfig from './json-schema-config' type JsonSchemaConfigModalProps = { @@ -20,7 +21,7 @@ const JsonSchemaConfigModal: FC<JsonSchemaConfigModalProps> = ({ <Modal isShow={isShow} onClose={onClose} - className='h-[800px] max-w-[960px] p-0' + className="h-[800px] max-w-[960px] p-0" > <JsonSchemaConfig defaultSchema={defaultSchema} diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx index 41539ec605..0110756b47 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx @@ -1,15 +1,16 @@ -import React, { type FC, useCallback, useEffect, useRef, useState } from 'react' -import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' -import { cn } from '@/utils/classnames' -import { useTranslation } from 'react-i18next' +import type { FC } from 'react' import { RiCloseLine } from '@remixicon/react' +import React, { useCallback, useEffect, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' -import { checkJsonDepth } from '../../utils' +import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' import { JSON_SCHEMA_MAX_DEPTH } from '@/config' +import { cn } from '@/utils/classnames' +import { checkJsonDepth } from '../../utils' import CodeEditor from './code-editor' import ErrorMessage from './error-message' -import { useVisualEditorStore } from './visual-editor/store' import { useMittContext } from './visual-editor/context' +import { useVisualEditorStore } from './visual-editor/store' type JsonImporterProps = { onSubmit: (schema: any) => void @@ -78,7 +79,7 @@ const JsonImporter: FC<JsonImporterProps> = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-end' + placement="bottom-end" offset={{ mainAxis: 4, crossAxis: 16, @@ -86,31 +87,31 @@ const JsonImporter: FC<JsonImporterProps> = ({ > <PortalToFollowElemTrigger ref={importBtnRef} onClick={handleTrigger}> <button - type='button' + type="button" className={cn( 'system-xs-medium flex shrink-0 rounded-md px-1.5 py-1 text-text-tertiary hover:bg-components-button-ghost-bg-hover', open && 'bg-components-button-ghost-bg-hover', )} > - <span className='px-0.5'>{t('workflow.nodes.llm.jsonSchema.import')}</span> + <span className="px-0.5">{t('workflow.nodes.llm.jsonSchema.import')}</span> </button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[100]'> - <div className='flex w-[400px] flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl shadow-shadow-shadow-9'> + <PortalToFollowElemContent className="z-[100]"> + <div className="flex w-[400px] flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl shadow-shadow-shadow-9"> {/* Title */} - <div className='relative px-3 pb-1 pt-3.5'> - <div className='absolute bottom-0 right-2.5 flex h-8 w-8 items-center justify-center' onClick={onClose}> - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <div className="relative px-3 pb-1 pt-3.5"> + <div className="absolute bottom-0 right-2.5 flex h-8 w-8 items-center justify-center" onClick={onClose}> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> - <div className='system-xl-semibold flex pl-1 pr-8 text-text-primary'> + <div className="system-xl-semibold flex pl-1 pr-8 text-text-primary"> {t('workflow.nodes.llm.jsonSchema.import')} </div> </div> {/* Content */} - <div className='px-4 py-2'> + <div className="px-4 py-2"> <CodeEditor - className='rounded-lg' - editorWrapperClassName='h-[340px]' + className="rounded-lg" + editorWrapperClassName="h-[340px]" value={json} onUpdate={setJson} showFormatButton={false} @@ -118,11 +119,11 @@ const JsonImporter: FC<JsonImporterProps> = ({ {parseError && <ErrorMessage message={parseError.message} />} </div> {/* Footer */} - <div className='flex items-center justify-end gap-x-2 p-4 pt-2'> - <Button variant='secondary' onClick={onClose}> + <div className="flex items-center justify-end gap-x-2 p-4 pt-2"> + <Button variant="secondary" onClick={onClose}> {t('common.operation.cancel')} </Button> - <Button variant='primary' onClick={handleSubmit}> + <Button variant="primary" onClick={handleSubmit}> {t('common.operation.submit')} </Button> </div> diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx index be80a8aac7..c5eaea6efd 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx @@ -1,14 +1,15 @@ -import React, { type FC, useCallback, useState } from 'react' -import { type SchemaRoot, Type } from '../../types' +import type { FC } from 'react' +import type { SchemaRoot } from '../../types' import { RiBracesLine, RiCloseLine, RiExternalLinkLine, RiTimelineView } from '@remixicon/react' -import { SegmentedControl } from '../../../../../base/segmented-control' -import JsonSchemaGenerator from './json-schema-generator' -import Divider from '@/app/components/base/divider' -import JsonImporter from './json-importer' +import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' -import VisualEditor from './visual-editor' -import SchemaEditor from './schema-editor' +import Divider from '@/app/components/base/divider' +import Toast from '@/app/components/base/toast' +import { JSON_SCHEMA_MAX_DEPTH } from '@/config' +import { useDocLink } from '@/context/i18n' +import { SegmentedControl } from '../../../../../base/segmented-control' +import { Type } from '../../types' import { checkJsonSchemaDepth, getValidationErrorMessage, @@ -16,12 +17,13 @@ import { preValidateSchema, validateSchemaAgainstDraft7, } from '../../utils' -import { MittProvider, VisualEditorContextProvider, useMittContext } from './visual-editor/context' import ErrorMessage from './error-message' +import JsonImporter from './json-importer' +import JsonSchemaGenerator from './json-schema-generator' +import SchemaEditor from './schema-editor' +import VisualEditor from './visual-editor' +import { MittProvider, useMittContext, VisualEditorContextProvider } from './visual-editor/context' import { useVisualEditorStore } from './visual-editor/store' -import Toast from '@/app/components/base/toast' -import { JSON_SCHEMA_MAX_DEPTH } from '@/config' -import { useDocLink } from '@/context/i18n' type JsonSchemaConfigProps = { defaultSchema?: SchemaRoot @@ -71,7 +73,8 @@ const JsonSchemaConfig: FC<JsonSchemaConfigProps> = ({ }, []) const handleTabChange = useCallback((value: SchemaView) => { - if (currentTab === value) return + if (currentTab === value) + return if (currentTab === SchemaView.JsonSchema) { try { const schema = JSON.parse(json) @@ -199,31 +202,31 @@ const JsonSchemaConfig: FC<JsonSchemaConfigProps> = ({ }, [currentTab, jsonSchema, json, onSave, onClose, advancedEditing, isAddingNewField, t]) return ( - <div className='flex h-full flex-col'> + <div className="flex h-full flex-col"> {/* Header */} - <div className='relative flex p-6 pb-3 pr-14'> - <div className='title-2xl-semi-bold grow truncate text-text-primary'> + <div className="relative flex p-6 pb-3 pr-14"> + <div className="title-2xl-semi-bold grow truncate text-text-primary"> {t('workflow.nodes.llm.jsonSchema.title')} </div> - <div className='absolute right-5 top-5 flex h-8 w-8 items-center justify-center p-1.5' onClick={onClose}> - <RiCloseLine className='h-[18px] w-[18px] text-text-tertiary' /> + <div className="absolute right-5 top-5 flex h-8 w-8 items-center justify-center p-1.5" onClick={onClose}> + <RiCloseLine className="h-[18px] w-[18px] text-text-tertiary" /> </div> </div> {/* Content */} - <div className='flex items-center justify-between px-6 py-2'> + <div className="flex items-center justify-between px-6 py-2"> {/* Tab */} <SegmentedControl<SchemaView> options={VIEW_TABS} value={currentTab} onChange={handleTabChange} /> - <div className='flex items-center gap-x-0.5'> + <div className="flex items-center gap-x-0.5"> {/* JSON Schema Generator */} <JsonSchemaGenerator crossAxisOffset={btnWidth} onApply={handleApplySchema} /> - <Divider type='vertical' className='h-3' /> + <Divider type="vertical" className="h-3" /> {/* JSON Schema Importer */} <JsonImporter updateBtnWidth={updateBtnWidth} @@ -231,7 +234,7 @@ const JsonSchemaConfig: FC<JsonSchemaConfigProps> = ({ /> </div> </div> - <div className='flex grow flex-col gap-y-1 overflow-hidden px-6'> + <div className="flex grow flex-col gap-y-1 overflow-hidden px-6"> {currentTab === SchemaView.VisualEditor && ( <VisualEditor schema={jsonSchema} @@ -248,28 +251,28 @@ const JsonSchemaConfig: FC<JsonSchemaConfigProps> = ({ {validationError && <ErrorMessage message={validationError} />} </div> {/* Footer */} - <div className='flex items-center gap-x-2 p-6 pt-5'> + <div className="flex items-center gap-x-2 p-6 pt-5"> <a - className='flex grow items-center gap-x-1 text-text-accent' + className="flex grow items-center gap-x-1 text-text-accent" href={docLink('/guides/workflow/structured-outputs')} - target='_blank' - rel='noopener noreferrer' + target="_blank" + rel="noopener noreferrer" > - <span className='system-xs-regular'>{t('workflow.nodes.llm.jsonSchema.doc')}</span> - <RiExternalLinkLine className='h-3 w-3' /> + <span className="system-xs-regular">{t('workflow.nodes.llm.jsonSchema.doc')}</span> + <RiExternalLinkLine className="h-3 w-3" /> </a> - <div className='flex items-center gap-x-3'> - <div className='flex items-center gap-x-2'> - <Button variant='secondary' onClick={handleResetDefaults}> + <div className="flex items-center gap-x-3"> + <div className="flex items-center gap-x-2"> + <Button variant="secondary" onClick={handleResetDefaults}> {t('workflow.nodes.llm.jsonSchema.resetDefaults')} </Button> - <Divider type='vertical' className='ml-1 mr-0 h-4' /> + <Divider type="vertical" className="ml-1 mr-0 h-4" /> </div> - <div className='flex items-center gap-x-2'> - <Button variant='secondary' onClick={handleCancel}> + <div className="flex items-center gap-x-2"> + <Button variant="secondary" onClick={handleCancel}> {t('common.operation.cancel')} </Button> - <Button variant='primary' onClick={handleSave}> + <Button variant="primary" onClick={handleSave}> {t('common.operation.save')} </Button> </div> diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/assets/index.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/assets/index.tsx index 5f1f117086..91dc3dfdd5 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/assets/index.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/assets/index.tsx @@ -1,7 +1,7 @@ -import SchemaGeneratorLight from './schema-generator-light' import SchemaGeneratorDark from './schema-generator-dark' +import SchemaGeneratorLight from './schema-generator-light' export { - SchemaGeneratorLight, SchemaGeneratorDark, + SchemaGeneratorLight, } diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/generated-result.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/generated-result.tsx index 00f57237e5..0e7d8c8d0c 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/generated-result.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/generated-result.tsx @@ -1,12 +1,13 @@ -import React, { type FC, useCallback, useMemo, useState } from 'react' +import type { FC } from 'react' import type { SchemaRoot } from '../../../types' import { RiArrowLeftLine, RiCloseLine, RiSparklingLine } from '@remixicon/react' +import React, { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' +import Loading from '@/app/components/base/loading' +import { getValidationErrorMessage, validateSchemaAgainstDraft7 } from '../../../utils' import CodeEditor from '../code-editor' import ErrorMessage from '../error-message' -import { getValidationErrorMessage, validateSchemaAgainstDraft7 } from '../../../utils' -import Loading from '@/app/components/base/loading' type GeneratedResultProps = { schema: SchemaRoot @@ -57,32 +58,32 @@ const GeneratedResult: FC<GeneratedResultProps> = ({ }, [schema, onApply]) return ( - <div className='flex w-[480px] flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl shadow-shadow-shadow-9'> + <div className="flex w-[480px] flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl shadow-shadow-shadow-9"> { isGenerating ? ( - <div className='flex h-[600px] flex-col items-center justify-center gap-y-3'> - <Loading type='area' /> - <div className='system-xs-regular text-text-tertiary'>{t('workflow.nodes.llm.jsonSchema.generating')}</div> + <div className="flex h-[600px] flex-col items-center justify-center gap-y-3"> + <Loading type="area" /> + <div className="system-xs-regular text-text-tertiary">{t('workflow.nodes.llm.jsonSchema.generating')}</div> </div> ) : ( <> - <div className='absolute right-2.5 top-2.5 flex h-8 w-8 items-center justify-center' onClick={onClose}> - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <div className="absolute right-2.5 top-2.5 flex h-8 w-8 items-center justify-center" onClick={onClose}> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> {/* Title */} - <div className='flex flex-col gap-y-[0.5px] px-3 pb-1 pt-3.5'> - <div className='system-xl-semibold flex pl-1 pr-8 text-text-primary'> + <div className="flex flex-col gap-y-[0.5px] px-3 pb-1 pt-3.5"> + <div className="system-xl-semibold flex pl-1 pr-8 text-text-primary"> {t('workflow.nodes.llm.jsonSchema.generatedResult')} </div> - <div className='system-xs-regular flex px-1 text-text-tertiary'> + <div className="system-xs-regular flex px-1 text-text-tertiary"> {t('workflow.nodes.llm.jsonSchema.resultTip')} </div> </div> {/* Content */} - <div className='px-4 py-2'> + <div className="px-4 py-2"> <CodeEditor - className='rounded-lg' - editorWrapperClassName='h-[424px]' + className="rounded-lg" + editorWrapperClassName="h-[424px]" value={jsonSchema} readOnly showFormatButton={false} @@ -91,21 +92,21 @@ const GeneratedResult: FC<GeneratedResultProps> = ({ {validationError && <ErrorMessage message={validationError} />} </div> {/* Footer */} - <div className='flex items-center justify-between p-4 pt-2'> - <Button variant='secondary' className='flex items-center gap-x-0.5' onClick={onBack}> - <RiArrowLeftLine className='h-4 w-4' /> + <div className="flex items-center justify-between p-4 pt-2"> + <Button variant="secondary" className="flex items-center gap-x-0.5" onClick={onBack}> + <RiArrowLeftLine className="h-4 w-4" /> <span>{t('workflow.nodes.llm.jsonSchema.back')}</span> </Button> - <div className='flex items-center gap-x-2'> + <div className="flex items-center gap-x-2"> <Button - variant='secondary' - className='flex items-center gap-x-0.5' + variant="secondary" + className="flex items-center gap-x-0.5" onClick={onRegenerate} > - <RiSparklingLine className='h-4 w-4' /> + <RiSparklingLine className="h-4 w-4" /> <span>{t('workflow.nodes.llm.jsonSchema.regenerate')}</span> </Button> - <Button variant='primary' onClick={handleApply}> + <Button variant="primary" onClick={handleApply}> {t('workflow.nodes.llm.jsonSchema.apply')} </Button> </div> diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx index 2671858628..a4da5b69e3 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx @@ -1,24 +1,25 @@ -import React, { type FC, useCallback, useEffect, useState } from 'react' +import type { FC } from 'react' import type { SchemaRoot } from '../../../types' +import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { CompletionParams, Model } from '@/types/app' +import React, { useCallback, useEffect, useState } from 'react' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import useTheme from '@/hooks/use-theme' -import type { CompletionParams, Model } from '@/types/app' -import { ModelModeType } from '@/types/app' -import { Theme } from '@/types/app' -import { SchemaGeneratorDark, SchemaGeneratorLight } from './assets' -import { cn } from '@/utils/classnames' -import PromptEditor from './prompt-editor' -import GeneratedResult from './generated-result' -import { useGenerateStructuredOutputRules } from '@/service/use-common' import Toast from '@/app/components/base/toast' -import { type FormValue, ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +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 { useVisualEditorStore } from '../visual-editor/store' +import useTheme from '@/hooks/use-theme' +import { useGenerateStructuredOutputRules } from '@/service/use-common' +import { ModelModeType, Theme } from '@/types/app' +import { cn } from '@/utils/classnames' import { useMittContext } from '../visual-editor/context' +import { useVisualEditorStore } from '../visual-editor/store' +import { SchemaGeneratorDark, SchemaGeneratorLight } from './assets' +import GeneratedResult from './generated-result' +import PromptEditor from './prompt-editor' type JsonSchemaGeneratorProps = { onApply: (schema: SchemaRoot) => void @@ -85,7 +86,7 @@ const JsonSchemaGenerator: FC<JsonSchemaGeneratorProps> = ({ setOpen(false) }, []) - const handleModelChange = useCallback((newValue: { modelId: string; provider: string; mode?: string; features?: string[] }) => { + const handleModelChange = useCallback((newValue: { modelId: string, provider: string, mode?: string, features?: string[] }) => { const newModel = { ...model, provider: newValue.provider, @@ -124,7 +125,8 @@ const JsonSchemaGenerator: FC<JsonSchemaGeneratorProps> = ({ const handleGenerate = useCallback(async () => { setView(GeneratorView.result) const output = await generateSchema() - if (output === undefined) return + if (output === undefined) + return setSchema(JSON.parse(output)) }, [generateSchema]) @@ -134,7 +136,8 @@ const JsonSchemaGenerator: FC<JsonSchemaGeneratorProps> = ({ const handleRegenerate = useCallback(async () => { const output = await generateSchema() - if (output === undefined) return + if (output === undefined) + return setSchema(JSON.parse(output)) }, [generateSchema]) @@ -147,7 +150,7 @@ const JsonSchemaGenerator: FC<JsonSchemaGeneratorProps> = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-end' + placement="bottom-end" offset={{ mainAxis: 4, crossAxis: crossAxisOffset ?? 0, @@ -155,7 +158,7 @@ const JsonSchemaGenerator: FC<JsonSchemaGeneratorProps> = ({ > <PortalToFollowElemTrigger onClick={handleTrigger}> <button - type='button' + type="button" className={cn( 'flex h-6 w-6 items-center justify-center rounded-md p-0.5 hover:bg-state-accent-hover', open && 'bg-state-accent-active', @@ -164,7 +167,7 @@ const JsonSchemaGenerator: FC<JsonSchemaGeneratorProps> = ({ <SchemaGenerator /> </button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[100]'> + <PortalToFollowElemContent className="z-[100]"> {view === GeneratorView.promptEditor && ( <PromptEditor instruction={instruction} diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/prompt-editor.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/prompt-editor.tsx index 62d156253a..17641fdf37 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/prompt-editor.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/prompt-editor.tsx @@ -1,13 +1,13 @@ -import React, { useCallback } from 'react' import type { FC } from 'react' +import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { Model } from '@/types/app' import { RiCloseLine, RiSparklingFill } from '@remixicon/react' +import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' import Textarea from '@/app/components/base/textarea' import Tooltip from '@/app/components/base/tooltip' -import Button from '@/app/components/base/button' -import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations' import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' -import type { Model } from '@/types/app' export type ModelInfo = { modelId: string @@ -42,27 +42,27 @@ const PromptEditor: FC<PromptEditorProps> = ({ }, [onInstructionChange]) return ( - <div className='relative flex w-[480px] flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl shadow-shadow-shadow-9'> - <div className='absolute right-2.5 top-2.5 flex h-8 w-8 items-center justify-center' onClick={onClose}> - <RiCloseLine className='h-4 w-4 text-text-tertiary'/> + <div className="relative flex w-[480px] flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl shadow-shadow-shadow-9"> + <div className="absolute right-2.5 top-2.5 flex h-8 w-8 items-center justify-center" onClick={onClose}> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> {/* Title */} - <div className='flex flex-col gap-y-[0.5px] px-3 pb-1 pt-3.5'> - <div className='system-xl-semibold flex pl-1 pr-8 text-text-primary'> + <div className="flex flex-col gap-y-[0.5px] px-3 pb-1 pt-3.5"> + <div className="system-xl-semibold flex pl-1 pr-8 text-text-primary"> {t('workflow.nodes.llm.jsonSchema.generateJsonSchema')} </div> - <div className='system-xs-regular flex px-1 text-text-tertiary'> + <div className="system-xs-regular flex px-1 text-text-tertiary"> {t('workflow.nodes.llm.jsonSchema.generationTip')} </div> </div> {/* Content */} - <div className='flex flex-col gap-y-1 px-4 py-2'> - <div className='system-sm-semibold-uppercase flex h-6 items-center text-text-secondary'> + <div className="flex flex-col gap-y-1 px-4 py-2"> + <div className="system-sm-semibold-uppercase flex h-6 items-center text-text-secondary"> {t('common.modelProvider.model')} </div> <ModelParameterModal - popupClassName='!w-[448px]' - portalToFollowElemContentClassName='z-[1000]' + popupClassName="!w-[448px]" + portalToFollowElemContentClassName="z-[1000]" isAdvancedMode={true} provider={model.provider} completionParams={model.completion_params} @@ -72,14 +72,14 @@ const PromptEditor: FC<PromptEditorProps> = ({ hideDebugWithMultipleModel /> </div> - <div className='flex flex-col gap-y-1 px-4 py-2'> - <div className='system-sm-semibold-uppercase flex h-6 items-center text-text-secondary'> + <div className="flex flex-col gap-y-1 px-4 py-2"> + <div className="system-sm-semibold-uppercase flex h-6 items-center text-text-secondary"> <span>{t('workflow.nodes.llm.jsonSchema.instruction')}</span> <Tooltip popupContent={t('workflow.nodes.llm.jsonSchema.promptTooltip')} /> </div> - <div className='flex items-center'> + <div className="flex items-center"> <Textarea - className='h-[364px] resize-none px-2 py-1' + className="h-[364px] resize-none px-2 py-1" value={instruction} placeholder={t('workflow.nodes.llm.jsonSchema.promptPlaceholder')} onChange={handleInstructionChange} @@ -87,16 +87,16 @@ const PromptEditor: FC<PromptEditorProps> = ({ </div> </div> {/* Footer */} - <div className='flex justify-end gap-x-2 p-4 pt-2'> - <Button variant='secondary' onClick={onClose}> + <div className="flex justify-end gap-x-2 p-4 pt-2"> + <Button variant="secondary" onClick={onClose}> {t('common.operation.cancel')} </Button> <Button - variant='primary' - className='flex items-center gap-x-0.5' + variant="primary" + className="flex items-center gap-x-0.5" onClick={onGenerate} > - <RiSparklingFill className='h-4 w-4' /> + <RiSparklingFill className="h-4 w-4" /> <span>{t('workflow.nodes.llm.jsonSchema.generate')}</span> </Button> </div> diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor.tsx index 6ba59320b7..54753f08b4 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor.tsx @@ -1,7 +1,8 @@ -import React, { type FC } from 'react' -import CodeEditor from './code-editor' -import { cn } from '@/utils/classnames' +import type { FC } from 'react' +import React from 'react' import LargeDataAlert from '@/app/components/workflow/variable-inspect/large-data-alert' +import { cn } from '@/utils/classnames' +import CodeEditor from './code-editor' type SchemaEditorProps = { schema: string @@ -28,13 +29,13 @@ const SchemaEditor: FC<SchemaEditorProps> = ({ <CodeEditor readOnly={readonly} className={cn('grow rounded-xl', className)} - editorWrapperClassName='grow' + editorWrapperClassName="grow" value={schema} onUpdate={onUpdate} hideTopMenu={hideTopMenu} onFocus={onFocus} onBlur={onBlur} - topContent={isTruncated && <LargeDataAlert className='mx-1 mb-3 mt-[-4px]' />} + topContent={isTruncated && <LargeDataAlert className="mx-1 mb-3 mt-[-4px]" />} /> ) } diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/add-field.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/add-field.tsx index eba94e85dd..54a3b6bb85 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/add-field.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/add-field.tsx @@ -1,9 +1,9 @@ -import React, { useCallback } from 'react' -import Button from '@/app/components/base/button' import { RiAddCircleFill } from '@remixicon/react' +import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { useVisualEditorStore } from './store' +import Button from '@/app/components/base/button' import { useMittContext } from './context' +import { useVisualEditorStore } from './store' const AddField = () => { const { t } = useTranslation() @@ -19,15 +19,15 @@ const AddField = () => { }, [setIsAddingNewField, emit]) return ( - <div className='py-2 pl-5'> + <div className="py-2 pl-5"> <Button - size='small' - variant='secondary-accent' - className='flex items-center gap-x-[1px]' + size="small" + variant="secondary-accent" + className="flex items-center gap-x-[1px]" onClick={handleAddField} > - <RiAddCircleFill className='h-3.5 w-3.5'/> - <span className='px-[3px]'>{t('workflow.nodes.llm.jsonSchema.addField')}</span> + <RiAddCircleFill className="h-3.5 w-3.5" /> + <span className="px-[3px]">{t('workflow.nodes.llm.jsonSchema.addField')}</span> </Button> </div> ) diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/card.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/card.tsx index 4f53f6b163..3510498835 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/card.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/card.tsx @@ -1,4 +1,5 @@ -import React, { type FC } from 'react' +import type { FC } from 'react' +import React from 'react' import { useTranslation } from 'react-i18next' type CardProps = { @@ -17,17 +18,17 @@ const Card: FC<CardProps> = ({ const { t } = useTranslation() return ( - <div className='flex flex-col py-0.5'> - <div className='flex h-6 items-center gap-x-1 pl-1 pr-0.5'> - <div className='system-sm-semibold truncate border border-transparent px-1 py-px text-text-primary'> + <div className="flex flex-col py-0.5"> + <div className="flex h-6 items-center gap-x-1 pl-1 pr-0.5"> + <div className="system-sm-semibold truncate border border-transparent px-1 py-px text-text-primary"> {name} </div> - <div className='system-xs-medium px-1 py-0.5 text-text-tertiary'> + <div className="system-xs-medium px-1 py-0.5 text-text-tertiary"> {type} </div> { required && ( - <div className='system-2xs-medium-uppercase px-1 py-0.5 text-text-warning'> + <div className="system-2xs-medium-uppercase px-1 py-0.5 text-text-warning"> {t('workflow.nodes.llm.jsonSchema.required')} </div> ) @@ -35,7 +36,7 @@ const Card: FC<CardProps> = ({ </div> {description && ( - <div className='system-xs-regular truncate px-2 pb-1 text-text-tertiary'> + <div className="system-xs-regular truncate px-2 pb-1 text-text-tertiary"> {description} </div> )} diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/context.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/context.tsx index 268683aec3..cfe63159d3 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/context.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/context.tsx @@ -1,11 +1,11 @@ +import { noop } from 'lodash-es' import { createContext, useContext, useRef, } from 'react' -import { createVisualEditorStore } from './store' import { useMitt } from '@/hooks/use-mitt' -import { noop } from 'lodash-es' +import { createVisualEditorStore } from './store' type VisualEditorStore = ReturnType<typeof createVisualEditorStore> diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/actions.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/actions.tsx index 3f693c23c7..a612701adc 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/actions.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/actions.tsx @@ -1,8 +1,8 @@ import type { FC } from 'react' -import React from 'react' -import Tooltip from '@/app/components/base/tooltip' import { RiAddCircleLine, RiDeleteBinLine, RiEditLine } from '@remixicon/react' +import React from 'react' import { useTranslation } from 'react-i18next' +import Tooltip from '@/app/components/base/tooltip' type ActionsProps = { disableAddBtn: boolean @@ -20,33 +20,33 @@ const Actions: FC<ActionsProps> = ({ const { t } = useTranslation() return ( - <div className='flex items-center gap-x-0.5'> + <div className="flex items-center gap-x-0.5"> <Tooltip popupContent={t('workflow.nodes.llm.jsonSchema.addChildField')}> <button - type='button' - className='flex h-6 w-6 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled' + type="button" + className="flex h-6 w-6 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary disabled:cursor-not-allowed disabled:text-text-disabled" onClick={onAddChildField} disabled={disableAddBtn} > - <RiAddCircleLine className='h-4 w-4'/> + <RiAddCircleLine className="h-4 w-4" /> </button> </Tooltip> <Tooltip popupContent={t('common.operation.edit')}> <button - type='button' - className='flex h-6 w-6 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary' + type="button" + className="flex h-6 w-6 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary" onClick={onEdit} > - <RiEditLine className='h-4 w-4' /> + <RiEditLine className="h-4 w-4" /> </button> </Tooltip> <Tooltip popupContent={t('common.operation.remove')}> <button - type='button' - className='flex h-6 w-6 items-center justify-center rounded-md text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive' + type="button" + className="flex h-6 w-6 items-center justify-center rounded-md text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive" onClick={onDelete} > - <RiDeleteBinLine className='h-4 w-4' /> + <RiDeleteBinLine className="h-4 w-4" /> </button> </Tooltip> </div> diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-actions.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-actions.tsx index e065406bde..ee60195fdb 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-actions.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-actions.tsx @@ -1,8 +1,9 @@ -import React, { type FC } from 'react' -import Button from '@/app/components/base/button' -import { useTranslation } from 'react-i18next' -import { getKeyboardKeyCodeBySystem, getKeyboardKeyNameBySystem } from '@/app/components/workflow/utils' +import type { FC } from 'react' import { useKeyPress } from 'ahooks' +import React from 'react' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' +import { getKeyboardKeyCodeBySystem, getKeyboardKeyNameBySystem } from '@/app/components/workflow/utils' type AdvancedActionsProps = { isConfirmDisabled: boolean @@ -13,7 +14,7 @@ type AdvancedActionsProps = { const Key = (props: { keyName: string }) => { const { keyName } = props return ( - <kbd className='system-kbd flex h-4 min-w-4 items-center justify-center rounded-[4px] bg-components-kbd-bg-white px-px text-text-primary-on-surface'> + <kbd className="system-kbd flex h-4 min-w-4 items-center justify-center rounded-[4px] bg-components-kbd-bg-white px-px text-text-primary-on-surface"> {keyName} </kbd> ) @@ -35,21 +36,21 @@ const AdvancedActions: FC<AdvancedActionsProps> = ({ }) return ( - <div className='flex items-center gap-x-1'> - <Button size='small' variant='secondary' onClick={onCancel}> + <div className="flex items-center gap-x-1"> + <Button size="small" variant="secondary" onClick={onCancel}> {t('common.operation.cancel')} </Button> <Button - className='flex items-center gap-x-1' + className="flex items-center gap-x-1" disabled={isConfirmDisabled} - size='small' - variant='primary' + size="small" + variant="primary" onClick={onConfirm} > <span>{t('common.operation.confirm')}</span> - <div className='flex items-center gap-x-0.5'> + <div className="flex items-center gap-x-0.5"> <Key keyName={getKeyboardKeyNameBySystem('ctrl')} /> - <Key keyName='⏎' /> + <Key keyName="⏎" /> </div> </Button> </div> diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-options.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-options.tsx index cd06fc8244..28ea12d9a3 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-options.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-options.tsx @@ -1,4 +1,5 @@ -import React, { type FC, useCallback, useState } from 'react' +import type { FC } from 'react' +import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import Textarea from '@/app/components/base/textarea' @@ -33,28 +34,28 @@ const AdvancedOptions: FC<AdvancedOptionsProps> = ({ // }, []) return ( - <div className='border-t border-divider-subtle'> + <div className="border-t border-divider-subtle"> {/* {showAdvancedOptions ? ( */} - <div className='flex flex-col gap-y-1 px-2 py-1.5'> - <div className='flex w-full items-center gap-x-2'> - <span className='system-2xs-medium-uppercase text-text-tertiary'> + <div className="flex flex-col gap-y-1 px-2 py-1.5"> + <div className="flex w-full items-center gap-x-2"> + <span className="system-2xs-medium-uppercase text-text-tertiary"> {t('workflow.nodes.llm.jsonSchema.stringValidations')} </span> - <div className='grow'> - <Divider type='horizontal' className='my-0 h-px bg-line-divider-bg' /> + <div className="grow"> + <Divider type="horizontal" className="my-0 h-px bg-line-divider-bg" /> </div> </div> - <div className='flex flex-col'> - <div className='system-xs-medium flex h-6 items-center text-text-secondary'> + <div className="flex flex-col"> + <div className="system-xs-medium flex h-6 items-center text-text-secondary"> Enum </div> <Textarea - size='small' - className='min-h-6' + size="small" + className="min-h-6" value={enumValue} onChange={handleEnumChange} onBlur={handleEnumBlur} - placeholder={'abcd, 1, 1.5, etc.'} + placeholder="abcd, 1, 1.5, etc." /> </div> </div> diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/auto-width-input.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/auto-width-input.tsx index 19dc478e83..2dfaa88260 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/auto-width-input.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/auto-width-input.tsx @@ -1,5 +1,5 @@ -import React, { useEffect, useState } from 'react' import type { FC } from 'react' +import React, { useEffect, useState } from 'react' import { cn } from '@/utils/classnames' type AutoWidthInputProps = { @@ -43,11 +43,11 @@ const AutoWidthInput: FC<AutoWidthInputProps> = ({ } return ( - <div className='relative inline-flex items-center'> + <div className="relative inline-flex items-center"> {/* Hidden measurement span */} <span ref={textRef} - className='system-sm-semibold invisible absolute left-0 top-0 -z-10 whitespace-pre px-1' + className="system-sm-semibold invisible absolute left-0 top-0 -z-10 whitespace-pre px-1" aria-hidden="true" > {value || placeholder} diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx index 643bc2ef13..e3ae0d16ab 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx @@ -1,20 +1,22 @@ -import React, { type FC, useCallback, useMemo, useRef, useState } from 'react' +import type { FC } from 'react' import type { SchemaEnumType } from '../../../../types' -import { ArrayType, Type } from '../../../../types' +import type { AdvancedOptionsType } from './advanced-options' import type { TypeItem } from './type-selector' -import TypeSelector from './type-selector' -import RequiredSwitch from './required-switch' +import { useUnmount } from 'ahooks' +import React, { useCallback, useMemo, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' +import { JSON_SCHEMA_MAX_DEPTH } from '@/config' +import { cn } from '@/utils/classnames' +import { ArrayType, Type } from '../../../../types' +import { useMittContext } from '../context' +import { useVisualEditorStore } from '../store' import Actions from './actions' import AdvancedActions from './advanced-actions' -import AdvancedOptions, { type AdvancedOptionsType } from './advanced-options' -import { useTranslation } from 'react-i18next' -import { cn } from '@/utils/classnames' -import { useVisualEditorStore } from '../store' -import { useMittContext } from '../context' -import { useUnmount } from 'ahooks' -import { JSON_SCHEMA_MAX_DEPTH } from '@/config' +import AdvancedOptions from './advanced-options' import AutoWidthInput from './auto-width-input' +import RequiredSwitch from './required-switch' +import TypeSelector from './type-selector' export type EditData = { name: string @@ -127,19 +129,22 @@ const EditCard: FC<EditCardProps> = ({ }, []) const handlePropertyNameBlur = useCallback(() => { - if (isAdvancedEditing) return + if (isAdvancedEditing) + return emitPropertyNameChange() }, [isAdvancedEditing, emitPropertyNameChange]) const handleTypeChange = useCallback((item: TypeItem) => { setCurrentFields(prev => ({ ...prev, type: item.value })) - if (isAdvancedEditing) return + if (isAdvancedEditing) + return emitPropertyTypeChange(item.value) }, [isAdvancedEditing, emitPropertyTypeChange]) const toggleRequired = useCallback(() => { setCurrentFields(prev => ({ ...prev, required: !prev.required })) - if (isAdvancedEditing) return + if (isAdvancedEditing) + return emitPropertyRequiredToggle() }, [isAdvancedEditing, emitPropertyRequiredToggle]) @@ -148,7 +153,8 @@ const EditCard: FC<EditCardProps> = ({ }, []) const handleDescriptionBlur = useCallback(() => { - if (isAdvancedEditing) return + if (isAdvancedEditing) + return emitPropertyOptionsChange({ description: currentFields.description, enum: currentFields.enum }) }, [isAdvancedEditing, emitPropertyOptionsChange, currentFields]) @@ -165,7 +171,8 @@ const EditCard: FC<EditCardProps> = ({ enumValue = stringArray } setCurrentFields(prev => ({ ...prev, enum: enumValue })) - if (isAdvancedEditing) return + if (isAdvancedEditing) + return emitPropertyOptionsChange({ description: currentFields.description, enum: enumValue }) }, [isAdvancedEditing, emitPropertyOptionsChange, currentFields]) @@ -203,14 +210,15 @@ const EditCard: FC<EditCardProps> = ({ }, [isAddingNewField, emit, setIsAddingNewField, setAdvancedEditing, backupFields]) useUnmount(() => { - if (isAdvancedEditing || blurWithActions.current) return + if (isAdvancedEditing || blurWithActions.current) + return emitFieldChange() }) return ( - <div className='flex flex-col rounded-lg bg-components-panel-bg py-0.5 shadow-sm shadow-shadow-shadow-4'> - <div className='flex h-6 items-center pl-1 pr-0.5'> - <div className='flex grow items-center gap-x-1'> + <div className="flex flex-col rounded-lg bg-components-panel-bg py-0.5 shadow-sm shadow-shadow-shadow-4"> + <div className="flex h-6 items-center pl-1 pr-0.5"> + <div className="flex grow items-center gap-x-1"> <AutoWidthInput value={currentFields.name} placeholder={t('workflow.nodes.llm.jsonSchema.fieldNamePlaceholder')} @@ -223,11 +231,11 @@ const EditCard: FC<EditCardProps> = ({ currentValue={currentFields.type} items={maximumDepthReached ? MAXIMUM_DEPTH_TYPE_OPTIONS : TYPE_OPTIONS} onSelect={handleTypeChange} - popupClassName={'z-[1000]'} + popupClassName="z-[1000]" /> { currentFields.required && ( - <div className='system-2xs-medium-uppercase px-1 py-0.5 text-text-warning'> + <div className="system-2xs-medium-uppercase px-1 py-0.5 text-text-warning"> {t('workflow.nodes.llm.jsonSchema.required')} </div> ) @@ -237,28 +245,30 @@ const EditCard: FC<EditCardProps> = ({ defaultValue={currentFields.required} toggleRequired={toggleRequired} /> - <Divider type='vertical' className='h-3' /> - {isAdvancedEditing ? ( - <AdvancedActions - isConfirmDisabled={currentFields.name === ''} - onCancel={handleCancel} - onConfirm={handleConfirm} - /> - ) : ( - <Actions - disableAddBtn={disableAddBtn} - onAddChildField={handleAddChildField} - onDelete={handleDelete} - onEdit={handleAdvancedEdit} - /> - )} + <Divider type="vertical" className="h-3" /> + {isAdvancedEditing + ? ( + <AdvancedActions + isConfirmDisabled={currentFields.name === ''} + onCancel={handleCancel} + onConfirm={handleConfirm} + /> + ) + : ( + <Actions + disableAddBtn={disableAddBtn} + onAddChildField={handleAddChildField} + onDelete={handleDelete} + onEdit={handleAdvancedEdit} + /> + )} </div> {(fields.description || isAdvancedEditing) && ( <div className={cn('flex', isAdvancedEditing ? 'p-2 pt-1' : 'px-2 pb-1')}> <input value={currentFields.description} - className='system-xs-regular placeholder:system-xs-regular h-4 w-full p-0 text-text-tertiary caret-[#295EFF] outline-none placeholder:text-text-placeholder' + className="system-xs-regular placeholder:system-xs-regular h-4 w-full p-0 text-text-tertiary caret-[#295EFF] outline-none placeholder:text-text-placeholder" placeholder={t('workflow.nodes.llm.jsonSchema.descriptionPlaceholder')} onChange={handleDescriptionChange} onBlur={handleDescriptionBlur} diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/required-switch.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/required-switch.tsx index c7179408cf..b84bdd0775 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/required-switch.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/required-switch.tsx @@ -1,7 +1,7 @@ -import React from 'react' import type { FC } from 'react' -import Switch from '@/app/components/base/switch' +import React from 'react' import { useTranslation } from 'react-i18next' +import Switch from '@/app/components/base/switch' type RequiredSwitchProps = { defaultValue: boolean @@ -15,9 +15,9 @@ const RequiredSwitch: FC<RequiredSwitchProps> = ({ const { t } = useTranslation() return ( - <div className='flex items-center gap-x-1 rounded-[5px] border border-divider-subtle bg-background-default-lighter px-1.5 py-1'> - <span className='system-2xs-medium-uppercase text-text-secondary'>{t('workflow.nodes.llm.jsonSchema.required')}</span> - <Switch size='xs' defaultValue={defaultValue} onChange={toggleRequired} /> + <div className="flex items-center gap-x-1 rounded-[5px] border border-divider-subtle bg-background-default-lighter px-1.5 py-1"> + <span className="system-2xs-medium-uppercase text-text-secondary">{t('workflow.nodes.llm.jsonSchema.required')}</span> + <Switch size="xs" defaultValue={defaultValue} onChange={toggleRequired} /> </div> ) } diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/type-selector.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/type-selector.tsx index c4d381613d..375cdca09c 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/type-selector.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/type-selector.tsx @@ -1,8 +1,8 @@ -import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' -import type { ArrayType, Type } from '../../../../types' import type { FC } from 'react' -import { useState } from 'react' +import type { ArrayType, Type } from '../../../../types' import { RiArrowDownSLine, RiCheckLine } from '@remixicon/react' +import { useState } from 'react' +import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' import { cn } from '@/utils/classnames' export type TypeItem = { @@ -29,7 +29,7 @@ const TypeSelector: FC<TypeSelectorProps> = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 4, }} @@ -38,26 +38,28 @@ const TypeSelector: FC<TypeSelectorProps> = ({ <div className={cn( 'flex items-center rounded-[5px] p-0.5 pl-1 hover:bg-state-base-hover', open && 'bg-state-base-hover', - )}> - <span className='system-xs-medium text-text-tertiary'>{currentValue}</span> - <RiArrowDownSLine className='h-4 w-4 text-text-tertiary' /> + )} + > + <span className="system-xs-medium text-text-tertiary">{currentValue}</span> + <RiArrowDownSLine className="h-4 w-4 text-text-tertiary" /> </div> </PortalToFollowElemTrigger> <PortalToFollowElemContent className={popupClassName}> - <div className='w-40 rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-1 shadow-lg shadow-shadow-shadow-5'> + <div className="w-40 rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-1 shadow-lg shadow-shadow-shadow-5"> {items.map((item) => { const isSelected = item.value === currentValue - return (<div - key={item.value} - className={'flex items-center gap-x-1 rounded-lg px-2 py-1 hover:bg-state-base-hover'} - onClick={() => { - onSelect(item) - setOpen(false) - }} - > - <span className='system-sm-medium px-1 text-text-secondary'>{item.text}</span> - {isSelected && <RiCheckLine className='h-4 w-4 text-text-accent' />} - </div> + return ( + <div + key={item.value} + className="flex items-center gap-x-1 rounded-lg px-2 py-1 hover:bg-state-base-hover" + onClick={() => { + onSelect(item) + setOpen(false) + }} + > + <span className="system-sm-medium px-1 text-text-secondary">{item.text}</span> + {isSelected && <RiCheckLine className="h-4 w-4 text-text-accent" />} + </div> ) })} </div> diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts index 202bb44638..84c28b236e 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts @@ -1,18 +1,19 @@ -import { produce } from 'immer' import type { VisualEditorProps } from '.' +import type { Field } from '../../../types' +import type { EditData } from './edit-card' +import { produce } from 'immer' +import { noop } from 'lodash-es' +import Toast from '@/app/components/base/toast' +import { ArrayType, Type } from '../../../types' +import { findPropertyWithPath } from '../../../utils' import { useMittContext } from './context' import { useVisualEditorStore } from './store' -import type { EditData } from './edit-card' -import { ArrayType, type Field, Type } from '../../../types' -import Toast from '@/app/components/base/toast' -import { findPropertyWithPath } from '../../../utils' -import { noop } from 'lodash-es' type ChangeEventParams = { - path: string[], - parentPath: string[], - oldFields: EditData, - fields: EditData, + path: string[] + parentPath: string[] + oldFields: EditData + fields: EditData } type AddEventParams = { @@ -57,7 +58,8 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => { const { name: oldName } = oldFields const { name: newName } = fields const newSchema = produce(jsonSchema, (draft) => { - if (oldName === newName) return + if (oldName === newName) + return const schema = findPropertyWithPath(draft, parentPath) as Field if (schema.type === Type.object) { @@ -120,7 +122,8 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => { const { path, oldFields, fields } = params as ChangeEventParams const { type: oldType } = oldFields const { type: newType } = fields - if (oldType === newType) return + if (oldType === newType) + return const newSchema = produce(jsonSchema, (draft) => { const schema = findPropertyWithPath(draft, path) as Field @@ -439,7 +442,8 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => { schema.enum = fields.enum } }) - if (samePropertyNameError) return + if (samePropertyNameError) + return onChange(newSchema) emit('fieldChangeSuccess') }) diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/index.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/index.tsx index f0f8bb2093..fc4d0ec106 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/index.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/index.tsx @@ -1,8 +1,8 @@ import type { FC } from 'react' import type { SchemaRoot } from '../../../types' -import SchemaNode from './schema-node' -import { useSchemaNodeOperations } from './hooks' import { cn } from '@/utils/classnames' +import { useSchemaNodeOperations } from './hooks' +import SchemaNode from './schema-node' export type VisualEditorProps = { className?: string diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/schema-node.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/schema-node.tsx index ec7b355085..23cd1ee477 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/schema-node.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/schema-node.tsx @@ -1,16 +1,17 @@ import type { FC } from 'react' -import React, { useMemo, useState } from 'react' -import { type Field, Type } from '../../../types' -import { cn } from '@/utils/classnames' +import type { Field } from '../../../types' import { RiArrowDropDownLine, RiArrowDropRightLine } from '@remixicon/react' -import { getFieldType, getHasChildren } from '../../../utils' -import Divider from '@/app/components/base/divider' -import EditCard from './edit-card' -import Card from './card' -import { useVisualEditorStore } from './store' import { useDebounceFn } from 'ahooks' -import AddField from './add-field' +import React, { useMemo, useState } from 'react' +import Divider from '@/app/components/base/divider' import { JSON_SCHEMA_MAX_DEPTH } from '@/config' +import { cn } from '@/utils/classnames' +import { Type } from '../../../types' +import { getFieldType, getHasChildren } from '../../../utils' +import AddField from './add-field' +import Card from './card' +import EditCard from './edit-card' +import { useVisualEditorStore } from './store' type SchemaNodeProps = { name: string @@ -79,32 +80,35 @@ const SchemaNode: FC<SchemaNodeProps> = ({ } const handleMouseEnter = () => { - if(readOnly) return - if (advancedEditing || isAddingNewField) return + if (readOnly) + return + if (advancedEditing || isAddingNewField) + return setHoveringPropertyDebounced(path.join('.')) } const handleMouseLeave = () => { - if(readOnly) return - if (advancedEditing || isAddingNewField) return + if (readOnly) + return + if (advancedEditing || isAddingNewField) + return setHoveringPropertyDebounced(null) } return ( - <div className='relative'> + <div className="relative"> <div className={cn('relative z-10', indentPadding[depth])}> {depth > 0 && hasChildren && ( - <div className={cn('absolute top-0 z-10 flex h-7 w-5 items-center bg-background-section-burn px-0.5', - indentLeft[depth - 1])}> + <div className={cn('absolute top-0 z-10 flex h-7 w-5 items-center bg-background-section-burn px-0.5', indentLeft[depth - 1])}> <button type="button" onClick={handleExpand} - className='py-0.5 text-text-tertiary hover:text-text-accent' + className="py-0.5 text-text-tertiary hover:text-text-accent" > { isExpanded - ? <RiArrowDropDownLine className='h-4 w-4' /> - : <RiArrowDropRightLine className='h-4 w-4' /> + ? <RiArrowDropDownLine className="h-4 w-4" /> + : <RiArrowDropRightLine className="h-4 w-4" /> } </button> </div> @@ -114,35 +118,35 @@ const SchemaNode: FC<SchemaNodeProps> = ({ onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} > - {(isHovering && depth > 0) ? ( - <EditCard - fields={{ - name, - type, - required, - description: schema.description, - enum: schema.enum, - }} - path={path} - parentPath={parentPath!} - depth={depth} - /> - ) : ( - <Card - name={name} - type={type} - required={required} - description={schema.description} - /> - )} + {(isHovering && depth > 0) + ? ( + <EditCard + fields={{ + name, + type, + required, + description: schema.description, + enum: schema.enum, + }} + path={path} + parentPath={parentPath!} + depth={depth} + /> + ) + : ( + <Card + name={name} + type={type} + required={required} + description={schema.description} + /> + )} </div> </div> - <div className={cn('absolute z-0 flex w-5 justify-center', - schema.description ? 'top-12 h-[calc(100%-3rem)]' : 'top-7 h-[calc(100%-1.75rem)]', - indentLeft[depth])}> + <div className={cn('absolute z-0 flex w-5 justify-center', schema.description ? 'top-12 h-[calc(100%-3rem)]' : 'top-7 h-[calc(100%-1.75rem)]', indentLeft[depth])}> <Divider - type='vertical' + type="vertical" className={cn('mx-0', isHovering ? 'bg-divider-deep' : 'bg-divider-subtle')} /> </div> diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/store.ts b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/store.ts index 3dbd6676dc..b756c6fea6 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/store.ts +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/store.ts @@ -1,6 +1,6 @@ +import type { SchemaRoot } from '../../../types' import { useContext } from 'react' import { createStore, useStore } from 'zustand' -import type { SchemaRoot } from '../../../types' import { VisualEditorContext } from './context' type VisualEditorStore = { diff --git a/web/app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx b/web/app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx index 60b910dc3e..eb285ee389 100644 --- a/web/app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx +++ b/web/app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx @@ -1,14 +1,14 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' -import { useBoolean } from 'ahooks' -import { cn } from '@/utils/classnames' -import { Generator } from '@/app/components/base/icons/src/vender/other' -import { ActionButton } from '@/app/components/base/action-button' -import GetAutomaticResModal from '@/app/components/app/configuration/config/automatic/get-automatic-res' -import { AppModeEnum } from '@/types/app' -import type { GenRes } from '@/service/debug' import type { ModelConfig } from '@/app/components/workflow/types' +import type { GenRes } from '@/service/debug' +import { useBoolean } from 'ahooks' +import React, { useCallback } from 'react' +import GetAutomaticResModal from '@/app/components/app/configuration/config/automatic/get-automatic-res' +import { ActionButton } from '@/app/components/base/action-button' +import { Generator } from '@/app/components/base/icons/src/vender/other' +import { AppModeEnum } from '@/types/app' +import { cn } from '@/utils/classnames' import { useHooksStore } from '../../../hooks-store' type Props = { @@ -36,9 +36,10 @@ const PromptGeneratorBtn: FC<Props> = ({ return ( <div className={cn(className)}> <ActionButton - className='hover:bg-[#155EFF]/8' - onClick={showAutomaticTrue}> - <Generator className='h-4 w-4 text-primary-600' /> + className="hover:bg-[#155EFF]/8" + onClick={showAutomaticTrue} + > + <Generator className="h-4 w-4 text-primary-600" /> </ActionButton> {showAutomatic && ( <GetAutomaticResModal diff --git a/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx b/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx index 49425ff64c..147981e398 100644 --- a/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx +++ b/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx @@ -1,8 +1,8 @@ import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' -import Field from '@/app/components/workflow/nodes/_base/components/field' import Switch from '@/app/components/base/switch' +import Field from '@/app/components/workflow/nodes/_base/components/field' type ReasoningFormatConfigProps = { value?: 'tagged' | 'separated' @@ -21,16 +21,16 @@ const ReasoningFormatConfig: FC<ReasoningFormatConfigProps> = ({ <Field title={t('workflow.nodes.llm.reasoningFormat.title')} tooltip={t('workflow.nodes.llm.reasoningFormat.tooltip')} - operations={ + operations={( // ON = separated, OFF = tagged <Switch defaultValue={value === 'separated'} onChange={enabled => onChange(enabled ? 'separated' : 'tagged')} - size='md' + size="md" disabled={readonly} key={value} /> - } + )} > <div /> </Field> diff --git a/web/app/components/workflow/nodes/llm/components/resolution-picker.tsx b/web/app/components/workflow/nodes/llm/components/resolution-picker.tsx index 191b9bfa4a..fe078ba6fa 100644 --- a/web/app/components/workflow/nodes/llm/components/resolution-picker.tsx +++ b/web/app/components/workflow/nodes/llm/components/resolution-picker.tsx @@ -24,9 +24,9 @@ const ResolutionPicker: FC<Props> = ({ } }, [onChange]) return ( - <div className='flex items-center justify-between'> - <div className='mr-2 text-xs font-medium uppercase text-text-secondary'>{t(`${i18nPrefix}.resolution.name`)}</div> - <div className='flex items-center space-x-1'> + <div className="flex items-center justify-between"> + <div className="mr-2 text-xs font-medium uppercase text-text-secondary">{t(`${i18nPrefix}.resolution.name`)}</div> + <div className="flex items-center space-x-1"> <OptionCard title={t(`${i18nPrefix}.resolution.high`)} onSelect={handleOnChange(Resolution.high)} diff --git a/web/app/components/workflow/nodes/llm/components/structure-output.tsx b/web/app/components/workflow/nodes/llm/components/structure-output.tsx index ee31a9e5ad..b97d5e20b7 100644 --- a/web/app/components/workflow/nodes/llm/components/structure-output.tsx +++ b/web/app/components/workflow/nodes/llm/components/structure-output.tsx @@ -1,19 +1,20 @@ 'use client' -import Button from '@/app/components/base/button' -import { RiEditLine } from '@remixicon/react' import type { FC } from 'react' -import React, { useCallback } from 'react' -import { type SchemaRoot, type StructuredOutput, Type } from '../types' -import ShowPanel from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show' +import type { SchemaRoot, StructuredOutput } from '../types' +import { RiEditLine } from '@remixicon/react' import { useBoolean } from 'ahooks' -import JsonSchemaConfigModal from './json-schema-config-modal' -import { cn } from '@/utils/classnames' +import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' +import ShowPanel from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show' +import { cn } from '@/utils/classnames' +import { Type } from '../types' +import JsonSchemaConfigModal from './json-schema-config-modal' type Props = { className?: string value?: StructuredOutput - onChange: (value: StructuredOutput) => void, + onChange: (value: StructuredOutput) => void } const StructureOutput: FC<Props> = ({ @@ -34,27 +35,30 @@ const StructureOutput: FC<Props> = ({ }, [onChange]) return ( <div className={cn(className)}> - <div className='flex justify-between'> - <div className='flex items-center leading-[18px]'> - <div className='code-sm-semibold text-text-secondary'>structured_output</div> - <div className='system-xs-regular ml-2 text-text-tertiary'>object</div> + <div className="flex justify-between"> + <div className="flex items-center leading-[18px]"> + <div className="code-sm-semibold text-text-secondary">structured_output</div> + <div className="system-xs-regular ml-2 text-text-tertiary">object</div> </div> <Button - size='small' - variant='secondary' - className='flex' + size="small" + variant="secondary" + className="flex" onClick={showConfigModal} > - <RiEditLine className='mr-1 size-3.5' /> - <div className='system-xs-medium text-components-button-secondary-text'>{t('app.structOutput.configure')}</div> + <RiEditLine className="mr-1 size-3.5" /> + <div className="system-xs-medium text-components-button-secondary-text">{t('app.structOutput.configure')}</div> </Button> </div> - {(value?.schema && value.schema.properties && Object.keys(value.schema.properties).length > 0) ? ( - <ShowPanel - payload={value} - />) : ( - <div className='system-xs-regular mt-1.5 flex h-10 cursor-pointer items-center justify-center rounded-[10px] bg-background-section text-text-tertiary' onClick={showConfigModal}>{t('app.structOutput.notConfiguredTip')}</div> - )} + {(value?.schema && value.schema.properties && Object.keys(value.schema.properties).length > 0) + ? ( + <ShowPanel + payload={value} + /> + ) + : ( + <div className="system-xs-regular mt-1.5 flex h-10 cursor-pointer items-center justify-center rounded-[10px] bg-background-section text-text-tertiary" onClick={showConfigModal}>{t('app.structOutput.notConfiguredTip')}</div> + )} {showConfig && ( <JsonSchemaConfigModal diff --git a/web/app/components/workflow/nodes/llm/default.ts b/web/app/components/workflow/nodes/llm/default.ts index 57033d26a1..2bc49c7f1b 100644 --- a/web/app/components/workflow/nodes/llm/default.ts +++ b/web/app/components/workflow/nodes/llm/default.ts @@ -1,9 +1,9 @@ -// import { RETRIEVAL_OUTPUT_STRUCT } from '../../constants' -import { AppModeEnum } from '@/types/app' -import { BlockEnum, EditionType } from '../../types' -import { type NodeDefault, type PromptItem, PromptRole } from '../../types' +import type { NodeDefault, PromptItem } from '../../types' import type { LLMNodeType } from './types' import { genNodeMetaData } from '@/app/components/workflow/utils' +// import { RETRIEVAL_OUTPUT_STRUCT } from '../../constants' +import { AppModeEnum } from '@/types/app' +import { BlockEnum, EditionType, PromptRole } from '../../types' const RETRIEVAL_OUTPUT_STRUCT = `{ "content": "", @@ -67,11 +67,11 @@ const nodeDefault: NodeDefault<LLMNodeType> = { const isChatModel = payload.model.mode === AppModeEnum.CHAT const isPromptEmpty = isChatModel ? !(payload.prompt_template as PromptItem[]).some((t) => { - if (t.edition_type === EditionType.jinja2) - return t.jinja2_text !== '' + if (t.edition_type === EditionType.jinja2) + return t.jinja2_text !== '' - return t.text !== '' - }) + return t.text !== '' + }) : ((payload.prompt_template as PromptItem).edition_type === EditionType.jinja2 ? (payload.prompt_template as PromptItem).jinja2_text === '' : (payload.prompt_template as PromptItem).text === '') if (isPromptEmpty) errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.llm.prompt') }) diff --git a/web/app/components/workflow/nodes/llm/node.tsx b/web/app/components/workflow/nodes/llm/node.tsx index ce676ba984..9d44d49475 100644 --- a/web/app/components/workflow/nodes/llm/node.tsx +++ b/web/app/components/workflow/nodes/llm/node.tsx @@ -1,11 +1,11 @@ import type { FC } from 'react' -import React from 'react' import type { LLMNodeType } from './types' +import type { NodeProps } from '@/app/components/workflow/types' +import React from 'react' import { useTextGenerationCurrentProviderAndModelAndModelList, } from '@/app/components/header/account-setting/model-provider-page/hooks' import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' -import type { NodeProps } from '@/app/components/workflow/types' const Node: FC<NodeProps<LLMNodeType>> = ({ data, @@ -20,12 +20,12 @@ const Node: FC<NodeProps<LLMNodeType>> = ({ return null return ( - <div className='mb-1 px-3 py-1'> + <div className="mb-1 px-3 py-1"> {hasSetModel && ( <ModelSelector defaultModel={{ provider, model: modelId }} modelList={textGenerationModelList} - triggerClassName='!h-6 !rounded-md' + triggerClassName="!h-6 !rounded-md" readonly /> )} diff --git a/web/app/components/workflow/nodes/llm/panel.tsx b/web/app/components/workflow/nodes/llm/panel.tsx index bb893b0da7..4044989a07 100644 --- a/web/app/components/workflow/nodes/llm/panel.tsx +++ b/web/app/components/workflow/nodes/llm/panel.tsx @@ -1,27 +1,27 @@ import type { FC } from 'react' +import type { LLMNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' +import { RiAlertFill, RiQuestionLine } from '@remixicon/react' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' +import AddButton2 from '@/app/components/base/button/add-button' +import Switch from '@/app/components/base/switch' +import Toast from '@/app/components/base/toast' +import Tooltip from '@/app/components/base/tooltip' +import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' +import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' +import Split from '@/app/components/workflow/nodes/_base/components/split' +import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list' +import { fetchAndMergeValidCompletionParams } from '@/utils/completion-params' +import ConfigVision from '../_base/components/config-vision' import MemoryConfig from '../_base/components/memory-config' import VarReferencePicker from '../_base/components/variable/var-reference-picker' -import ConfigVision from '../_base/components/config-vision' -import useConfig from './use-config' -import type { LLMNodeType } from './types' import ConfigPrompt from './components/config-prompt' -import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list' -import AddButton2 from '@/app/components/base/button/add-button' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import Split from '@/app/components/workflow/nodes/_base/components/split' -import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' -import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' -import type { NodePanelProps } from '@/app/components/workflow/types' -import Tooltip from '@/app/components/base/tooltip' -import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' -import StructureOutput from './components/structure-output' import ReasoningFormatConfig from './components/reasoning-format-config' -import Switch from '@/app/components/base/switch' -import { RiAlertFill, RiQuestionLine } from '@remixicon/react' -import { fetchAndMergeValidCompletionParams } from '@/utils/completion-params' -import Toast from '@/app/components/base/toast' +import StructureOutput from './components/structure-output' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.llm' @@ -95,14 +95,14 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({ })() }, [inputs.model.completion_params]) return ( - <div className='mt-2'> - <div className='space-y-4 px-4 pb-4'> + <div className="mt-2"> + <div className="space-y-4 px-4 pb-4"> <Field title={t(`${i18nPrefix}.model`)} required > <ModelParameterModal - popupClassName='!w-[387px]' + popupClassName="!w-[387px]" isInWorkflow isAdvancedMode={true} provider={model?.provider} @@ -131,7 +131,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({ filterVar={filterVar} /> {shouldShowContextTip && ( - <div className='text-xs font-normal leading-[18px] text-[#DC6803]'>{t(`${i18nPrefix}.notSetContextInPromptTip`)}</div> + <div className="text-xs font-normal leading-[18px] text-[#DC6803]">{t(`${i18nPrefix}.notSetContextInPromptTip`)}</div> )} </> </Field> @@ -175,29 +175,31 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({ {/* Memory put place examples. */} {isChatMode && isChatModel && !!inputs.memory && ( - <div className='mt-4'> - <div className='flex h-8 items-center justify-between rounded-lg bg-components-input-bg-normal pl-3 pr-2'> - <div className='flex items-center space-x-1'> - <div className='text-xs font-semibold uppercase text-text-secondary'>{t('workflow.nodes.common.memories.title')}</div> + <div className="mt-4"> + <div className="flex h-8 items-center justify-between rounded-lg bg-components-input-bg-normal pl-3 pr-2"> + <div className="flex items-center space-x-1"> + <div className="text-xs font-semibold uppercase text-text-secondary">{t('workflow.nodes.common.memories.title')}</div> <Tooltip popupContent={t('workflow.nodes.common.memories.tip')} - triggerClassName='w-4 h-4' + triggerClassName="w-4 h-4" /> </div> - <div className='flex h-[18px] items-center rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-1 text-xs font-semibold uppercase text-text-tertiary'>{t('workflow.nodes.common.memories.builtIn')}</div> + <div className="flex h-[18px] items-center rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-1 text-xs font-semibold uppercase text-text-tertiary">{t('workflow.nodes.common.memories.builtIn')}</div> </div> {/* Readonly User Query */} - <div className='mt-4'> + <div className="mt-4"> <Editor - title={<div className='flex items-center space-x-1'> - <div className='text-xs font-semibold uppercase text-text-secondary'>user</div> - <Tooltip - popupContent={ - <div className='max-w-[180px]'>{t('workflow.nodes.llm.roleDescription.user')}</div> - } - triggerClassName='w-4 h-4' - /> - </div>} + title={( + <div className="flex items-center space-x-1"> + <div className="text-xs font-semibold uppercase text-text-secondary">user</div> + <Tooltip + popupContent={ + <div className="max-w-[180px]">{t('workflow.nodes.llm.roleDescription.user')}</div> + } + triggerClassName="w-4 h-4" + /> + </div> + )} value={inputs.memory.query_prompt_template || '{{#sys.query#}}'} onChange={handleSyeQueryChange} readOnly={readOnly} @@ -211,7 +213,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({ /> {inputs.memory.query_prompt_template && !inputs.memory.query_prompt_template.includes('{{#sys.query#}}') && ( - <div className='text-xs font-normal leading-[18px] text-[#DC6803]'>{t(`${i18nPrefix}.sysQueryInUser`)}</div> + <div className="text-xs font-normal leading-[18px] text-[#DC6803]">{t(`${i18nPrefix}.sysQueryInUser`)}</div> )} </div> </div> @@ -253,59 +255,63 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({ <OutputVars collapsed={structuredOutputCollapsed} onCollapse={setStructuredOutputCollapsed} - operations={ - <div className='mr-4 flex shrink-0 items-center'> + operations={( + <div className="mr-4 flex shrink-0 items-center"> {(!isModelSupportStructuredOutput && !!inputs.structured_output_enabled) && ( - <Tooltip noDecoration popupContent={ - <div className='w-[232px] rounded-xl border-[0.5px] border-components-panel-border bg-components-tooltip-bg px-4 py-3.5 shadow-lg backdrop-blur-[5px]'> - <div className='title-xs-semi-bold text-text-primary'>{t('app.structOutput.modelNotSupported')}</div> - <div className='body-xs-regular mt-1 text-text-secondary'>{t('app.structOutput.modelNotSupportedTip')}</div> - </div> - }> + <Tooltip + noDecoration + popupContent={( + <div className="w-[232px] rounded-xl border-[0.5px] border-components-panel-border bg-components-tooltip-bg px-4 py-3.5 shadow-lg backdrop-blur-[5px]"> + <div className="title-xs-semi-bold text-text-primary">{t('app.structOutput.modelNotSupported')}</div> + <div className="body-xs-regular mt-1 text-text-secondary">{t('app.structOutput.modelNotSupportedTip')}</div> + </div> + )} + > <div> - <RiAlertFill className='mr-1 size-4 text-text-warning-secondary' /> + <RiAlertFill className="mr-1 size-4 text-text-warning-secondary" /> </div> </Tooltip> )} - <div className='system-xs-medium-uppercase mr-0.5 text-text-tertiary'>{t('app.structOutput.structured')}</div> + <div className="system-xs-medium-uppercase mr-0.5 text-text-tertiary">{t('app.structOutput.structured')}</div> <Tooltip popupContent={ - <div className='max-w-[150px]'>{t('app.structOutput.structuredTip')}</div> - }> + <div className="max-w-[150px]">{t('app.structOutput.structuredTip')}</div> + } + > <div> - <RiQuestionLine className='size-3.5 text-text-quaternary' /> + <RiQuestionLine className="size-3.5 text-text-quaternary" /> </div> </Tooltip> <Switch - className='ml-2' + className="ml-2" defaultValue={!!inputs.structured_output_enabled} onChange={handleStructureOutputEnableChange} - size='md' + size="md" disabled={readOnly} /> </div> - } + )} > <> <VarItem - name='text' - type='string' + name="text" + type="string" description={t(`${i18nPrefix}.outputVars.output`)} /> <VarItem - name='reasoning_content' - type='string' + name="reasoning_content" + type="string" description={t(`${i18nPrefix}.outputVars.reasoning_content`)} /> <VarItem - name='usage' - type='object' + name="usage" + type="object" description={t(`${i18nPrefix}.outputVars.usage`)} /> {inputs.structured_output_enabled && ( <> - <Split className='mt-3' /> + <Split className="mt-3" /> <StructureOutput - className='mt-4' + className="mt-4" value={inputs.structured_output} onChange={handleStructureOutputChange} /> diff --git a/web/app/components/workflow/nodes/llm/use-config.ts b/web/app/components/workflow/nodes/llm/use-config.ts index d9b811bb85..e885f108bb 100644 --- a/web/app/components/workflow/nodes/llm/use-config.ts +++ b/web/app/components/workflow/nodes/llm/use-config.ts @@ -1,31 +1,31 @@ -import { useCallback, useEffect, useRef, useState } from 'react' -import { produce } from 'immer' -import { EditionType, VarType } from '../../types' import type { Memory, PromptItem, ValueSelector, Var, Variable } from '../../types' -import { useStore } from '../../store' -import { - useIsChatMode, - useNodesReadOnly, -} from '../../hooks' -import useAvailableVarList from '../_base/hooks/use-available-var-list' -import useConfigVision from '../../hooks/use-config-vision' import type { LLMNodeType, StructuredOutput } from './types' -import { useModelList, useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' +import { produce } from 'immer' +import { useCallback, useEffect, useRef, useState } from 'react' +import { checkHasContextBlock, checkHasHistoryBlock, checkHasQueryBlock } from '@/app/components/base/prompt-editor/constants' import { ModelFeatureEnum, ModelTypeEnum, } from '@/app/components/header/account-setting/model-provider-page/declarations' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' -import { checkHasContextBlock, checkHasHistoryBlock, checkHasQueryBlock } from '@/app/components/base/prompt-editor/constants' +import { useModelList, useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import { AppModeEnum } from '@/types/app' +import { + useIsChatMode, + useNodesReadOnly, +} from '../../hooks' +import useConfigVision from '../../hooks/use-config-vision' +import { useStore } from '../../store' +import { EditionType, VarType } from '../../types' +import useAvailableVarList from '../_base/hooks/use-available-var-list' const useConfig = (id: string, payload: LLMNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() const isChatMode = useIsChatMode() const defaultConfig = useStore(s => s.nodesDefaultConfigs)?.[payload.type] - const [defaultRolePrefix, setDefaultRolePrefix] = useState<{ user: string; assistant: string }>({ user: '', assistant: '' }) + const [defaultRolePrefix, setDefaultRolePrefix] = useState<{ user: string, assistant: string }>({ user: '', assistant: '' }) const { inputs, setInputs: doSetInputs } = useNodeCrud<LLMNodeType>(id, payload) const inputRef = useRef(inputs) useEffect(() => { @@ -128,7 +128,7 @@ const useConfig = (id: string, payload: LLMNodeType) => { }, }) - const handleModelChanged = useCallback((model: { provider: string; modelId: string; mode?: string }) => { + const handleModelChanged = useCallback((model: { provider: string, modelId: string, mode?: string }) => { const newInputs = produce(inputRef.current, (draft) => { draft.model.provider = model.provider draft.model.name = model.modelId @@ -285,8 +285,10 @@ const useConfig = (id: string, payload: LLMNodeType) => { const { data: modelList } = useModelList(ModelTypeEnum.textGeneration) const isModelSupportStructuredOutput = modelList ?.find(provideItem => provideItem.provider === model?.provider) - ?.models.find(modelItem => modelItem.model === model?.name) - ?.features?.includes(ModelFeatureEnum.StructuredOutput) + ?.models + .find(modelItem => modelItem.model === model?.name) + ?.features + ?.includes(ModelFeatureEnum.StructuredOutput) const [structuredOutputCollapsed, setStructuredOutputCollapsed] = useState(true) const handleStructureOutputEnableChange = useCallback((enabled: boolean) => { diff --git a/web/app/components/workflow/nodes/llm/use-single-run-form-params.ts b/web/app/components/workflow/nodes/llm/use-single-run-form-params.ts index 8d539dfc15..ae500074ff 100644 --- a/web/app/components/workflow/nodes/llm/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/llm/use-single-run-form-params.ts @@ -1,23 +1,23 @@ import type { RefObject } from 'react' -import { useTranslation } from 'react-i18next' +import type { LLMNodeType } from './types' import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form' import type { InputVar, PromptItem, Var, Variable } from '@/app/components/workflow/types' -import { InputVarType, VarType } from '@/app/components/workflow/types' -import type { LLMNodeType } from './types' -import { EditionType } from '../../types' -import useNodeCrud from '../_base/hooks/use-node-crud' -import { useIsChatMode } from '../../hooks' -import { useCallback } from 'react' -import useConfigVision from '../../hooks/use-config-vision' import { noop } from 'lodash-es' -import { findVariableWhenOnLLMVision } from '../utils' -import useAvailableVarList from '../_base/hooks/use-available-var-list' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import { InputVarType, VarType } from '@/app/components/workflow/types' import { AppModeEnum } from '@/types/app' +import { useIsChatMode } from '../../hooks' +import useConfigVision from '../../hooks/use-config-vision' +import { EditionType } from '../../types' +import useAvailableVarList from '../_base/hooks/use-available-var-list' +import useNodeCrud from '../_base/hooks/use-node-crud' +import { findVariableWhenOnLLMVision } from '../utils' const i18nPrefix = 'workflow.nodes.llm' type Params = { - id: string, - payload: LLMNodeType, + id: string + payload: LLMNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] @@ -186,10 +186,10 @@ const useSingleRunFormParams = ({ } const getDependentVar = (variable: string) => { - if(variable === '#context#') + if (variable === '#context#') return payload.context.variable_selector - if(variable === '#files#') + if (variable === '#files#') return payload.vision.configs?.variable_selector return false diff --git a/web/app/components/workflow/nodes/llm/utils.ts b/web/app/components/workflow/nodes/llm/utils.ts index 1652d511d0..604a1f8408 100644 --- a/web/app/components/workflow/nodes/llm/utils.ts +++ b/web/app/components/workflow/nodes/llm/utils.ts @@ -1,8 +1,8 @@ -import { z } from 'zod' -import { ArrayType, Type } from './types' -import type { ArrayItems, Field, LLMNodeType } from './types' -import { draft07Validator, forbidBooleanProperties } from '@/utils/validators' import type { ValidationError } from 'jsonschema' +import type { ArrayItems, Field, LLMNodeType } from './types' +import { z } from 'zod' +import { draft07Validator, forbidBooleanProperties } from '@/utils/validators' +import { ArrayType, Type } from './types' export const checkNodeValid = (_payload: LLMNodeType) => { return true @@ -10,8 +10,10 @@ export const checkNodeValid = (_payload: LLMNodeType) => { export const getFieldType = (field: Field) => { const { type, items, enum: enums } = field - if (field.schemaType === 'file') return Type.file - if (enums && enums.length > 0) return Type.enumType + if (field.schemaType === 'file') + return Type.file + if (enums && enums.length > 0) + return Type.enumType if (type !== Type.array || !items) return type @@ -29,7 +31,8 @@ export const getHasChildren = (schema: Field) => { } export const getTypeOf = (target: any) => { - if (target === null) return 'null' + if (target === null) + return 'null' if (typeof target !== 'object') { return typeof target } @@ -43,12 +46,17 @@ export const getTypeOf = (target: any) => { export const inferType = (value: any): Type => { const type = getTypeOf(value) - if (type === 'array') return Type.array + if (type === 'array') + return Type.array // type boolean will be treated as string - if (type === 'boolean') return Type.string - if (type === 'number') return Type.number - if (type === 'string') return Type.string - if (type === 'object') return Type.object + if (type === 'boolean') + return Type.string + if (type === 'number') + return Type.number + if (type === 'string') + return Type.string + if (type === 'object') + return Type.object return Type.string } diff --git a/web/app/components/workflow/nodes/loop-end/default.ts b/web/app/components/workflow/nodes/loop-end/default.ts index bb46ff1166..7a63b0d27f 100644 --- a/web/app/components/workflow/nodes/loop-end/default.ts +++ b/web/app/components/workflow/nodes/loop-end/default.ts @@ -2,9 +2,9 @@ import type { NodeDefault } from '../../types' import type { SimpleNodeType, } from '@/app/components/workflow/simple-node/types' -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' const metaData = genNodeMetaData({ classification: BlockClassificationEnum.Logic, diff --git a/web/app/components/workflow/nodes/loop-start/default.ts b/web/app/components/workflow/nodes/loop-start/default.ts index 86dc5b44bf..7b39eff078 100644 --- a/web/app/components/workflow/nodes/loop-start/default.ts +++ b/web/app/components/workflow/nodes/loop-start/default.ts @@ -1,7 +1,7 @@ import type { NodeDefault } from '../../types' import type { LoopStartNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' const metaData = genNodeMetaData({ sort: -1, diff --git a/web/app/components/workflow/nodes/loop-start/index.tsx b/web/app/components/workflow/nodes/loop-start/index.tsx index a48cc5fc27..34097e2030 100644 --- a/web/app/components/workflow/nodes/loop-start/index.tsx +++ b/web/app/components/workflow/nodes/loop-start/index.tsx @@ -1,7 +1,7 @@ -import { memo } from 'react' -import { useTranslation } from 'react-i18next' import type { NodeProps } from 'reactflow' import { RiHome5Fill } from '@remixicon/react' +import { memo } from 'react' +import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import { NodeSourceHandle } from '@/app/components/workflow/nodes/_base/components/node-handle' @@ -9,17 +9,17 @@ const LoopStartNode = ({ id, data }: NodeProps) => { const { t } = useTranslation() return ( - <div className='nodrag group mt-1 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg'> + <div className="nodrag group mt-1 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg"> <Tooltip popupContent={t('workflow.blocks.loop-start')} asChild={false}> - <div className='flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500'> - <RiHome5Fill className='h-3 w-3 text-text-primary-on-surface' /> + <div className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500"> + <RiHome5Fill className="h-3 w-3 text-text-primary-on-surface" /> </div> </Tooltip> <NodeSourceHandle id={id} data={data} - handleClassName='!top-1/2 !-right-[9px] !-translate-y-1/2' - handleId='source' + handleClassName="!top-1/2 !-right-[9px] !-translate-y-1/2" + handleId="source" /> </div> ) @@ -29,10 +29,10 @@ export const LoopStartNodeDumb = () => { const { t } = useTranslation() return ( - <div className='nodrag relative left-[17px] top-[21px] z-[11] flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg'> + <div className="nodrag relative left-[17px] top-[21px] z-[11] flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg"> <Tooltip popupContent={t('workflow.blocks.loop-start')} asChild={false}> - <div className='flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500'> - <RiHome5Fill className='h-3 w-3 text-text-primary-on-surface' /> + <div className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500"> + <RiHome5Fill className="h-3 w-3 text-text-primary-on-surface" /> </div> </Tooltip> </div> diff --git a/web/app/components/workflow/nodes/loop/add-block.tsx b/web/app/components/workflow/nodes/loop/add-block.tsx index d86d9f83cb..9602f7700a 100644 --- a/web/app/components/workflow/nodes/loop/add-block.tsx +++ b/web/app/components/workflow/nodes/loop/add-block.tsx @@ -1,26 +1,26 @@ +import type { LoopNodeType } from './types' +import type { + OnSelectBlock, +} from '@/app/components/workflow/types' +import { + RiAddLine, +} from '@remixicon/react' import { memo, useCallback, } from 'react' -import { - RiAddLine, -} from '@remixicon/react' import { useTranslation } from 'react-i18next' +import BlockSelector from '@/app/components/workflow/block-selector' +import { + BlockEnum, +} from '@/app/components/workflow/types' + +import { cn } from '@/utils/classnames' import { useAvailableBlocks, useNodesInteractions, useNodesReadOnly, } from '../../hooks' -import type { LoopNodeType } from './types' -import { cn } from '@/utils/classnames' -import BlockSelector from '@/app/components/workflow/block-selector' - -import type { - OnSelectBlock, -} from '@/app/components/workflow/types' -import { - BlockEnum, -} from '@/app/components/workflow/types' type AddBlockProps = { loopNodeId: string @@ -53,24 +53,25 @@ const AddBlock = ({ 'system-sm-medium relative inline-flex h-8 cursor-pointer items-center rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-3 text-components-button-secondary-text shadow-xs backdrop-blur-[5px] hover:bg-components-button-secondary-bg-hover', `${nodesReadOnly && '!cursor-not-allowed bg-components-button-secondary-bg-disabled'}`, open && 'bg-components-button-secondary-bg-hover', - )}> - <RiAddLine className='mr-1 h-4 w-4' /> + )} + > + <RiAddLine className="mr-1 h-4 w-4" /> {t('workflow.common.addBlock')} </div> ) }, [nodesReadOnly, t]) return ( - <div className='absolute left-14 top-7 z-10 flex h-8 items-center'> - <div className='group/insert relative h-0.5 w-16 bg-gray-300'> - <div className='absolute right-0 top-1/2 h-2 w-0.5 -translate-y-1/2 bg-primary-500'></div> + <div className="absolute left-14 top-7 z-10 flex h-8 items-center"> + <div className="group/insert relative h-0.5 w-16 bg-gray-300"> + <div className="absolute right-0 top-1/2 h-2 w-0.5 -translate-y-1/2 bg-primary-500"></div> </div> <BlockSelector disabled={nodesReadOnly} onSelect={handleSelect} trigger={renderTriggerElement} - triggerInnerClassName='inline-flex' - popupClassName='!min-w-[256px]' + triggerInnerClassName="inline-flex" + popupClassName="!min-w-[256px]" availableBlocksTypes={availableNextBlocks} /> </div> diff --git a/web/app/components/workflow/nodes/loop/components/condition-add.tsx b/web/app/components/workflow/nodes/loop/components/condition-add.tsx index cd5be853b6..06af8d3ec8 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-add.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-add.tsx @@ -1,10 +1,15 @@ +import type { HandleAddCondition } from '../types' +import type { + NodeOutPutVar, + ValueSelector, + Var, +} from '@/app/components/workflow/types' +import { RiAddLine } from '@remixicon/react' import { useCallback, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { RiAddLine } from '@remixicon/react' -import type { HandleAddCondition } from '../types' import Button from '@/app/components/base/button' import { PortalToFollowElem, @@ -12,11 +17,6 @@ import { PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' -import type { - NodeOutPutVar, - ValueSelector, - Var, -} from '@/app/components/workflow/types' type ConditionAddProps = { className?: string @@ -42,7 +42,7 @@ const ConditionAdd = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 4, crossAxis: 0, @@ -50,16 +50,16 @@ const ConditionAdd = ({ > <PortalToFollowElemTrigger onClick={() => setOpen(!open)}> <Button - size='small' + size="small" className={className} disabled={disabled} > - <RiAddLine className='mr-1 h-3.5 w-3.5' /> + <RiAddLine className="mr-1 h-3.5 w-3.5" /> {t('workflow.nodes.ifElse.addCondition')} </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> - <div className='w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'> + <PortalToFollowElemContent className="z-[1000]"> + <div className="w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg"> <VarReferenceVars vars={variables} isSupportFileVar diff --git a/web/app/components/workflow/nodes/loop/components/condition-files-list-value.tsx b/web/app/components/workflow/nodes/loop/components/condition-files-list-value.tsx index 00eec93de3..e13832ed46 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-files-list-value.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-files-list-value.tsx @@ -1,20 +1,22 @@ +import type { ValueSelector } from '../../../types' +import type { Condition } from '../types' import { memo, useCallback, } from 'react' import { useTranslation } from 'react-i18next' -import { ComparisonOperator, type Condition } from '../types' +import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { + VariableLabelInNode, +} from '@/app/components/workflow/nodes/_base/components/variable/variable-label' +import { ComparisonOperator } from '../types' import { comparisonOperatorNotRequireValue, isComparisonOperatorNeedTranslate, isEmptyRelatedOperator, } from '../utils' -import type { ValueSelector } from '../../../types' import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from './../default' -import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' -import { - VariableLabelInNode, -} from '@/app/components/workflow/nodes/_base/components/variable/variable-label' + const i18nPrefix = 'workflow.nodes.ifElse' type ConditionValueProps = { @@ -39,7 +41,7 @@ const ConditionValue = ({ return '' const value = c.value as string - return value.replace(/{{#([^#]*)#}}/g, (a, b) => { + return value.replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { const arr: string[] = b.split('.') if (isSystemVar(arr)) return `{{${b}}}` @@ -57,41 +59,41 @@ const ConditionValue = ({ if (isSelect) { const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(c.value) ? c.value[0] : c.value))[0] return name - ? t(`workflow.nodes.ifElse.optionName.${name.i18nKey}`).replace(/{{#([^#]*)#}}/g, (a, b) => { - const arr: string[] = b.split('.') - if (isSystemVar(arr)) - return `{{${b}}}` + ? t(`workflow.nodes.ifElse.optionName.${name.i18nKey}`).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { + const arr: string[] = b.split('.') + if (isSystemVar(arr)) + return `{{${b}}}` - return `{{${arr.slice(1).join('.')}}}` - }) + return `{{${arr.slice(1).join('.')}}}` + }) : '' } return '' }, [t]) return ( - <div className='rounded-md bg-workflow-block-parma-bg'> - <div className='flex h-6 items-center px-1 '> + <div className="rounded-md bg-workflow-block-parma-bg"> + <div className="flex h-6 items-center px-1 "> <VariableLabelInNode - className='w-0 grow' + className="w-0 grow" variables={variableSelector} notShowFullPath /> <div - className='mx-1 shrink-0 text-xs font-medium text-text-primary' + className="mx-1 shrink-0 text-xs font-medium text-text-primary" title={operatorName} > {operatorName} </div> </div> - <div className='ml-[10px] border-l border-divider-regular pl-[10px]'> + <div className="ml-[10px] border-l border-divider-regular pl-[10px]"> { sub_variable_condition?.conditions.map((c: Condition, index) => ( - <div className='relative flex h-6 items-center space-x-1' key={c.id}> - <div className='system-xs-medium text-text-accent'>{c.key}</div> - <div className='system-xs-medium text-text-primary'>{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${c.comparison_operator}`) : c.comparison_operator}</div> - {c.comparison_operator && !isEmptyRelatedOperator(c.comparison_operator) && <div className='system-xs-regular text-text-secondary'>{isSelect(c) ? selectName(c) : formatValue(c)}</div>} - {index !== sub_variable_condition.conditions.length - 1 && (<div className='absolute bottom-[-10px] right-1 z-10 text-[10px] font-medium uppercase leading-4 text-text-accent'>{t(`${i18nPrefix}.${sub_variable_condition.logical_operator}`)}</div>)} + <div className="relative flex h-6 items-center space-x-1" key={c.id}> + <div className="system-xs-medium text-text-accent">{c.key}</div> + <div className="system-xs-medium text-text-primary">{isComparisonOperatorNeedTranslate(c.comparison_operator) ? t(`workflow.nodes.ifElse.comparisonOperator.${c.comparison_operator}`) : c.comparison_operator}</div> + {c.comparison_operator && !isEmptyRelatedOperator(c.comparison_operator) && <div className="system-xs-regular text-text-secondary">{isSelect(c) ? selectName(c) : formatValue(c)}</div>} + {index !== sub_variable_condition.conditions.length - 1 && (<div className="absolute bottom-[-10px] right-1 z-10 text-[10px] font-medium uppercase leading-4 text-text-accent">{t(`${i18nPrefix}.${sub_variable_condition.logical_operator}`)}</div>)} </div> )) } diff --git a/web/app/components/workflow/nodes/loop/components/condition-list/condition-input.tsx b/web/app/components/workflow/nodes/loop/components/condition-list/condition-input.tsx index 683e2884cf..c6560a8a0e 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-list/condition-input.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-list/condition-input.tsx @@ -1,10 +1,10 @@ -import { useTranslation } from 'react-i18next' -import { useStore } from '@/app/components/workflow/store' -import PromptEditor from '@/app/components/base/prompt-editor' -import { BlockEnum } from '@/app/components/workflow/types' import type { Node, } from '@/app/components/workflow/types' +import { useTranslation } from 'react-i18next' +import PromptEditor from '@/app/components/base/prompt-editor' +import { useStore } from '@/app/components/workflow/store' +import { BlockEnum } from '@/app/components/workflow/types' type ConditionInputProps = { disabled?: boolean diff --git a/web/app/components/workflow/nodes/loop/components/condition-list/condition-item.tsx b/web/app/components/workflow/nodes/loop/components/condition-list/condition-item.tsx index 0ecae8f052..ea3e2ef5be 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-list/condition-item.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-list/condition-item.tsx @@ -1,42 +1,42 @@ -import { - useCallback, - useMemo, - useState, -} from 'react' -import { useTranslation } from 'react-i18next' -import { RiDeleteBinLine } from '@remixicon/react' -import { produce } from 'immer' import type { VarType as NumberVarType } from '../../../tool/types' import type { Condition, HandleAddSubVariableCondition, HandleRemoveCondition, + handleRemoveSubVariableCondition, HandleToggleSubVariableConditionLogicalOperator, HandleUpdateCondition, HandleUpdateSubVariableCondition, - handleRemoveSubVariableCondition, } from '../../types' -import { - ComparisonOperator, -} from '../../types' -import ConditionNumberInput from '../condition-number-input' -import ConditionWrap from '../condition-wrap' -import { comparisonOperatorNotRequireValue, getOperators } from './../../utils' -import ConditionOperator from './condition-operator' -import ConditionInput from './condition-input' -import { FILE_TYPE_OPTIONS, SUB_VARIABLES, TRANSFER_METHOD } from './../../default' import type { Node, NodeOutPutVar, ValueSelector, Var, } from '@/app/components/workflow/types' +import { RiDeleteBinLine } from '@remixicon/react' +import { produce } from 'immer' +import { + useCallback, + useMemo, + useState, +} from 'react' +import { useTranslation } from 'react-i18next' +import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' +import { SimpleSelect as Select } from '@/app/components/base/select' +import BoolValue from '@/app/components/workflow/panel/chat-variable-panel/components/bool-value' import { VarType } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' -import { SimpleSelect as Select } from '@/app/components/base/select' -import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' +import { + ComparisonOperator, +} from '../../types' +import ConditionNumberInput from '../condition-number-input' +import ConditionWrap from '../condition-wrap' +import { FILE_TYPE_OPTIONS, SUB_VARIABLES, TRANSFER_METHOD } from './../../default' +import { comparisonOperatorNotRequireValue, getOperators } from './../../utils' +import ConditionInput from './condition-input' +import ConditionOperator from './condition-operator' import ConditionVarSelector from './condition-var-selector' -import BoolValue from '@/app/components/workflow/panel/chat-variable-panel/components/bool-value' const optionNameI18NPrefix = 'workflow.nodes.ifElse.optionName' @@ -209,45 +209,48 @@ const ConditionItem = ({ <div className={cn( 'grow rounded-lg bg-components-input-bg-normal', isHovered && 'bg-state-destructive-hover', - )}> - <div className='flex items-center p-1'> - <div className='w-0 grow'> + )} + > + <div className="flex items-center p-1"> + <div className="w-0 grow"> {isSubVarSelect ? ( - <Select - wrapperClassName='h-6' - className='pl-0 text-xs' - optionWrapClassName='w-[165px] max-h-none' - defaultValue={condition.key} - items={subVarOptions} - onSelect={item => handleSubVarKeyChange(item.value as string)} - renderTrigger={item => ( - item - ? <div className='flex cursor-pointer justify-start'> - <div className='inline-flex h-6 max-w-full items-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark px-1.5 text-text-accent shadow-xs'> - <Variable02 className='h-3.5 w-3.5 shrink-0 text-text-accent' /> - <div className='system-xs-medium ml-0.5 truncate'>{item?.name}</div> - </div> - </div> - : <div className='system-sm-regular text-left text-components-input-text-placeholder'>{t('common.placeholder.select')}</div> - )} - hideChecked - /> - ) + <Select + wrapperClassName="h-6" + className="pl-0 text-xs" + optionWrapClassName="w-[165px] max-h-none" + defaultValue={condition.key} + items={subVarOptions} + onSelect={item => handleSubVarKeyChange(item.value as string)} + renderTrigger={item => ( + item + ? ( + <div className="flex cursor-pointer justify-start"> + <div className="inline-flex h-6 max-w-full items-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-components-badge-white-to-dark px-1.5 text-text-accent shadow-xs"> + <Variable02 className="h-3.5 w-3.5 shrink-0 text-text-accent" /> + <div className="system-xs-medium ml-0.5 truncate">{item?.name}</div> + </div> + </div> + ) + : <div className="system-sm-regular text-left text-components-input-text-placeholder">{t('common.placeholder.select')}</div> + )} + hideChecked + /> + ) : ( - <ConditionVarSelector - open={open} - onOpenChange={setOpen} - valueSelector={condition.variable_selector || []} - varType={condition.varType} - availableNodes={availableNodes} - nodesOutputVars={availableVars} - onChange={handleVarChange} - /> - )} + <ConditionVarSelector + open={open} + onOpenChange={setOpen} + valueSelector={condition.variable_selector || []} + varType={condition.varType} + availableNodes={availableNodes} + nodesOutputVars={availableVars} + onChange={handleVarChange} + /> + )} </div> - <div className='mx-1 h-3 w-[1px] bg-divider-regular'></div> + <div className="mx-1 h-3 w-[1px] bg-divider-regular"></div> <ConditionOperator disabled={!canChooseOperator} varType={condition.varType} @@ -258,7 +261,7 @@ const ConditionItem = ({ </div> { !comparisonOperatorNotRequireValue(condition.comparison_operator) && !isNotInput && condition.varType !== VarType.number && condition.varType !== VarType.boolean && ( - <div className='max-h-[100px] overflow-y-auto border-t border-t-divider-subtle px-2 py-1'> + <div className="max-h-[100px] overflow-y-auto border-t border-t-divider-subtle px-2 py-1"> <ConditionInput disabled={disabled} value={condition.value as string} @@ -269,16 +272,17 @@ const ConditionItem = ({ ) } {!comparisonOperatorNotRequireValue(condition.comparison_operator) && condition.varType === VarType.boolean - && <div className='p-1'> - <BoolValue - value={condition.value as boolean} - onChange={handleUpdateConditionValue} - /> - </div> - } + && ( + <div className="p-1"> + <BoolValue + value={condition.value as boolean} + onChange={handleUpdateConditionValue} + /> + </div> + )} { !comparisonOperatorNotRequireValue(condition.comparison_operator) && !isNotInput && condition.varType === VarType.number && ( - <div className='border-t border-t-divider-subtle px-2 py-1 pt-[3px]'> + <div className="border-t border-t-divider-subtle px-2 py-1 pt-[3px]"> <ConditionNumberInput numberVarType={condition.numberVarType} onNumberVarTypeChange={handleUpdateConditionNumberVarType} @@ -293,10 +297,10 @@ const ConditionItem = ({ } { !comparisonOperatorNotRequireValue(condition.comparison_operator) && isSelect && ( - <div className='border-t border-t-divider-subtle'> + <div className="border-t border-t-divider-subtle"> <Select - wrapperClassName='h-8' - className='rounded-t-none px-2 text-xs' + wrapperClassName="h-8" + className="rounded-t-none px-2 text-xs" defaultValue={isArrayValue ? (condition.value as string[])?.[0] : (condition.value as string)} items={selectOptions} onSelect={item => handleUpdateConditionValue(item.value as string)} @@ -308,7 +312,7 @@ const ConditionItem = ({ } { !comparisonOperatorNotRequireValue(condition.comparison_operator) && isSubVariable && ( - <div className='p-1'> + <div className="p-1"> <ConditionWrap isSubVariable conditions={condition.sub_variable_condition?.conditions || []} @@ -328,12 +332,12 @@ const ConditionItem = ({ } </div> <div - className='ml-1 mt-1 flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-lg text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive' + className="ml-1 mt-1 flex h-6 w-6 shrink-0 cursor-pointer items-center justify-center rounded-lg text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive" onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} onClick={doRemoveCondition} > - <RiDeleteBinLine className='h-4 w-4' /> + <RiDeleteBinLine className="h-4 w-4" /> </div> </div> ) diff --git a/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx b/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx index 49c1d6e64f..a33b2b7727 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx @@ -1,19 +1,20 @@ +import type { ComparisonOperator } from '../../types' +import type { VarType } from '@/app/components/workflow/types' +import { RiArrowDownSLine } from '@remixicon/react' import { useMemo, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { RiArrowDownSLine } from '@remixicon/react' -import { getOperators, isComparisonOperatorNeedTranslate } from '../../utils' -import type { ComparisonOperator } from '../../types' import Button from '@/app/components/base/button' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import type { VarType } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' +import { getOperators, isComparisonOperatorNeedTranslate } from '../../utils' + const i18nPrefix = 'workflow.nodes.ifElse' type ConditionOperatorProps = { @@ -48,7 +49,7 @@ const ConditionOperator = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-end' + placement="bottom-end" offset={{ mainAxis: 4, crossAxis: 0, @@ -57,8 +58,8 @@ const ConditionOperator = ({ <PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}> <Button className={cn('shrink-0', !selectedOption && 'opacity-50', className)} - size='small' - variant='ghost' + size="small" + variant="ghost" disabled={disabled} > { @@ -66,16 +67,16 @@ const ConditionOperator = ({ ? selectedOption.label : t(`${i18nPrefix}.select`) } - <RiArrowDownSLine className='ml-1 h-3.5 w-3.5' /> + <RiArrowDownSLine className="ml-1 h-3.5 w-3.5" /> </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-10'> - <div className='rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'> + <PortalToFollowElemContent className="z-10"> + <div className="rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg"> { options.map(option => ( <div key={option.value} - className='flex h-7 cursor-pointer items-center rounded-lg px-3 py-1.5 text-[13px] font-medium text-text-secondary hover:bg-state-base-hover' + className="flex h-7 cursor-pointer items-center rounded-lg px-3 py-1.5 text-[13px] font-medium text-text-secondary hover:bg-state-base-hover" onClick={() => { onSelect(option.value) setOpen(false) diff --git a/web/app/components/workflow/nodes/loop/components/condition-list/condition-var-selector.tsx b/web/app/components/workflow/nodes/loop/components/condition-list/condition-var-selector.tsx index 4cb82b4ce9..0c219413ee 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-list/condition-var-selector.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-list/condition-var-selector.tsx @@ -1,7 +1,7 @@ +import type { Node, NodeOutPutVar, ValueSelector, Var, VarType } from '@/app/components/workflow/types' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' import VariableTag from '@/app/components/workflow/nodes/_base/components/variable-tag' import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' -import type { Node, NodeOutPutVar, ValueSelector, Var, VarType } from '@/app/components/workflow/types' type ConditionVarSelectorProps = { open: boolean @@ -26,7 +26,7 @@ const ConditionVarSelector = ({ <PortalToFollowElem open={open} onOpenChange={onOpenChange} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 4, crossAxis: 0, @@ -42,8 +42,8 @@ const ConditionVarSelector = ({ /> </div> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> - <div className='w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'> + <PortalToFollowElemContent className="z-[1000]"> + <div className="w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg"> <VarReferenceVars vars={nodesOutputVars} isSupportFileVar diff --git a/web/app/components/workflow/nodes/loop/components/condition-list/index.tsx b/web/app/components/workflow/nodes/loop/components/condition-list/index.tsx index 8cfcc5d811..4c70d9073c 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-list/index.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-list/index.tsx @@ -1,22 +1,17 @@ -import { RiLoopLeftLine } from '@remixicon/react' -import { useCallback, useMemo } from 'react' -import { - type Condition, - type HandleAddSubVariableCondition, - type HandleRemoveCondition, - type HandleToggleConditionLogicalOperator, - type HandleToggleSubVariableConditionLogicalOperator, - type HandleUpdateCondition, - type HandleUpdateSubVariableCondition, - LogicalOperator, - type handleRemoveSubVariableCondition, -} from '../../types' -import ConditionItem from './condition-item' +import type { Condition, HandleAddSubVariableCondition, HandleRemoveCondition, handleRemoveSubVariableCondition, HandleToggleConditionLogicalOperator, HandleToggleSubVariableConditionLogicalOperator, HandleUpdateCondition, HandleUpdateSubVariableCondition } from '../../types' import type { Node, NodeOutPutVar, } from '@/app/components/workflow/types' +import { RiLoopLeftLine } from '@remixicon/react' +import { useCallback, useMemo } from 'react' import { cn } from '@/utils/classnames' +import { + + LogicalOperator, + +} from '../../types' +import ConditionItem from './condition-item' type ConditionListProps = { isSubVariable?: boolean @@ -83,15 +78,16 @@ const ConditionList = ({ 'absolute bottom-0 left-0 top-0 w-[60px]', isSubVariable && logicalOperator === LogicalOperator.and && 'left-[-10px]', isSubVariable && logicalOperator === LogicalOperator.or && 'left-[-18px]', - )}> - <div className='absolute bottom-4 left-[46px] top-4 w-2.5 rounded-l-[8px] border border-r-0 border-divider-deep'></div> - <div className='absolute right-0 top-1/2 h-[29px] w-4 -translate-y-1/2 bg-components-panel-bg'></div> + )} + > + <div className="absolute bottom-4 left-[46px] top-4 w-2.5 rounded-l-[8px] border border-r-0 border-divider-deep"></div> + <div className="absolute right-0 top-1/2 h-[29px] w-4 -translate-y-1/2 bg-components-panel-bg"></div> <div - className='absolute right-1 top-1/2 flex h-[21px] -translate-y-1/2 cursor-pointer select-none items-center rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-1 text-[10px] font-semibold text-text-accent-secondary shadow-xs' + className="absolute right-1 top-1/2 flex h-[21px] -translate-y-1/2 cursor-pointer select-none items-center rounded-md border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg px-1 text-[10px] font-semibold text-text-accent-secondary shadow-xs" onClick={() => doToggleConditionLogicalOperator(conditionId)} > {logicalOperator && logicalOperator.toUpperCase()} - <RiLoopLeftLine className='ml-0.5 h-3 w-3' /> + <RiLoopLeftLine className="ml-0.5 h-3 w-3" /> </div> </div> ) diff --git a/web/app/components/workflow/nodes/loop/components/condition-number-input.tsx b/web/app/components/workflow/nodes/loop/components/condition-number-input.tsx index a13fbef011..29419be011 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-number-input.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-number-input.tsx @@ -1,29 +1,29 @@ +import type { + NodeOutPutVar, + ValueSelector, +} from '@/app/components/workflow/types' +import { RiArrowDownSLine } from '@remixicon/react' +import { useBoolean } from 'ahooks' +import { capitalize } from 'lodash-es' import { memo, useCallback, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { RiArrowDownSLine } from '@remixicon/react' -import { capitalize } from 'lodash-es' -import { useBoolean } from 'ahooks' -import { VarType as NumberVarType } from '../../tool/types' -import VariableTag from '../../_base/components/variable-tag' +import Button from '@/app/components/base/button' +import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import Button from '@/app/components/base/button' -import { cn } from '@/utils/classnames' import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' -import type { - NodeOutPutVar, - ValueSelector, -} from '@/app/components/workflow/types' import { VarType } from '@/app/components/workflow/types' import { variableTransformer } from '@/app/components/workflow/utils' -import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' +import { cn } from '@/utils/classnames' +import VariableTag from '../../_base/components/variable-tag' +import { VarType as NumberVarType } from '../../tool/types' const options = [ NumberVarType.variable, @@ -62,25 +62,25 @@ const ConditionNumberInput = ({ }, [onValueChange]) return ( - <div className='flex cursor-pointer items-center'> + <div className="flex cursor-pointer items-center"> <PortalToFollowElem open={numberVarTypeVisible} onOpenChange={setNumberVarTypeVisible} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 2, crossAxis: 0 }} > <PortalToFollowElemTrigger onClick={() => setNumberVarTypeVisible(v => !v)}> <Button - className='shrink-0' - variant='ghost' - size='small' + className="shrink-0" + variant="ghost" + size="small" > {capitalize(numberVarType)} - <RiArrowDownSLine className='ml-[1px] h-3.5 w-3.5' /> + <RiArrowDownSLine className="ml-[1px] h-3.5 w-3.5" /> </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> - <div className='w-[112px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'> + <PortalToFollowElemContent className="z-[1000]"> + <div className="w-[112px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg"> { options.map(option => ( <div @@ -102,19 +102,20 @@ const ConditionNumberInput = ({ </div> </PortalToFollowElemContent> </PortalToFollowElem> - <div className='mx-1 h-4 w-[1px] bg-divider-regular'></div> - <div className='ml-0.5 w-0 grow'> + <div className="mx-1 h-4 w-[1px] bg-divider-regular"></div> + <div className="ml-0.5 w-0 grow"> { numberVarType === NumberVarType.variable && ( <PortalToFollowElem open={variableSelectorVisible} onOpenChange={setVariableSelectorVisible} - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 2, crossAxis: 0 }} > <PortalToFollowElemTrigger - className='w-full' - onClick={() => setVariableSelectorVisible(v => !v)}> + className="w-full" + onClick={() => setVariableSelectorVisible(v => !v)} + > { value && ( <VariableTag @@ -126,14 +127,14 @@ const ConditionNumberInput = ({ } { !value && ( - <div className='flex h-6 items-center p-1 text-[13px] text-components-input-text-placeholder'> - <Variable02 className='mr-1 h-4 w-4 shrink-0' /> - <div className='w-0 grow truncate'>{t('workflow.nodes.ifElse.selectVariable')}</div> + <div className="flex h-6 items-center p-1 text-[13px] text-components-input-text-placeholder"> + <Variable02 className="mr-1 h-4 w-4 shrink-0" /> + <div className="w-0 grow truncate">{t('workflow.nodes.ifElse.selectVariable')}</div> </div> ) } </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> + <PortalToFollowElemContent className="z-[1000]"> <div className={cn('w-[296px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur pt-1 shadow-lg', isShort && 'w-[200px]')}> <VarReferenceVars vars={variables} @@ -146,17 +147,17 @@ const ConditionNumberInput = ({ } { numberVarType === NumberVarType.constant && ( - <div className=' relative'> + <div className=" relative"> <input className={cn('block w-full appearance-none bg-transparent px-2 text-[13px] text-components-input-text-filled outline-none placeholder:text-components-input-text-placeholder', unit && 'pr-6')} - type='number' + type="number" value={value} onChange={e => onValueChange(e.target.value)} placeholder={t('workflow.nodes.ifElse.enterValue') || ''} onFocus={setFocus} onBlur={setBlur} /> - {!isFocus && unit && <div className='system-sm-regular absolute right-2 top-[50%] translate-y-[-50%] text-text-tertiary'>{unit}</div>} + {!isFocus && unit && <div className="system-sm-regular absolute right-2 top-[50%] translate-y-[-50%] text-text-tertiary">{unit}</div>} </div> ) } diff --git a/web/app/components/workflow/nodes/loop/components/condition-value.tsx b/web/app/components/workflow/nodes/loop/components/condition-value.tsx index 2f011f870a..c24a1a18a6 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-value.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-value.tsx @@ -3,16 +3,16 @@ import { useMemo, } from 'react' import { useTranslation } from 'react-i18next' +import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { + VariableLabelInNode, +} from '@/app/components/workflow/nodes/_base/components/variable/variable-label' import { ComparisonOperator } from '../types' import { comparisonOperatorNotRequireValue, isComparisonOperatorNeedTranslate, } from '../utils' import { FILE_TYPE_OPTIONS, TRANSFER_METHOD } from './../default' -import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' -import { - VariableLabelInNode, -} from '@/app/components/workflow/nodes/_base/components/variable/variable-label' type ConditionValueProps = { variableSelector: string[] @@ -36,7 +36,7 @@ const ConditionValue = ({ if (Array.isArray(value)) // transfer method return value[0] - return value.replace(/{{#([^#]*)#}}/g, (a, b) => { + return value.replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { const arr: string[] = b.split('.') if (isSystemVar(arr)) return `{{${b}}}` @@ -50,34 +50,34 @@ const ConditionValue = ({ if (isSelect) { const name = [...FILE_TYPE_OPTIONS, ...TRANSFER_METHOD].filter(item => item.value === (Array.isArray(value) ? value[0] : value))[0] return name - ? t(`workflow.nodes.ifElse.optionName.${name.i18nKey}`).replace(/{{#([^#]*)#}}/g, (a, b) => { - const arr: string[] = b.split('.') - if (isSystemVar(arr)) - return `{{${b}}}` + ? t(`workflow.nodes.ifElse.optionName.${name.i18nKey}`).replace(/\{\{#([^#]*)#\}\}/g, (a, b) => { + const arr: string[] = b.split('.') + if (isSystemVar(arr)) + return `{{${b}}}` - return `{{${arr.slice(1).join('.')}}}` - }) + return `{{${arr.slice(1).join('.')}}}` + }) : '' } return '' }, [isSelect, t, value]) return ( - <div className='flex h-6 items-center rounded-md bg-workflow-block-parma-bg px-1'> + <div className="flex h-6 items-center rounded-md bg-workflow-block-parma-bg px-1"> <VariableLabelInNode - className='w-0 grow' + className="w-0 grow" variables={variableSelector} notShowFullPath /> <div - className='mx-1 shrink-0 text-xs font-medium text-text-primary' + className="mx-1 shrink-0 text-xs font-medium text-text-primary" title={operatorName} > {operatorName} </div> { !notHasValue && ( - <div className='truncate text-xs text-text-secondary' title={formatValue}>{isSelect ? selectName : formatValue}</div> + <div className="truncate text-xs text-text-secondary" title={formatValue}>{isSelect ? selectName : formatValue}</div> ) } </div> diff --git a/web/app/components/workflow/nodes/loop/components/condition-wrap.tsx b/web/app/components/workflow/nodes/loop/components/condition-wrap.tsx index 6812554767..00f44ab244 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-wrap.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-wrap.tsx @@ -1,20 +1,20 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' -import { useTranslation } from 'react-i18next' +import type { Node, NodeOutPutVar, Var } from '../../../types' +import type { Condition, HandleAddCondition, HandleAddSubVariableCondition, HandleRemoveCondition, handleRemoveSubVariableCondition, HandleToggleConditionLogicalOperator, HandleToggleSubVariableConditionLogicalOperator, HandleUpdateCondition, HandleUpdateSubVariableCondition, LogicalOperator } from '../types' import { RiAddLine, } from '@remixicon/react' -import type { Condition, HandleAddCondition, HandleAddSubVariableCondition, HandleRemoveCondition, HandleToggleConditionLogicalOperator, HandleToggleSubVariableConditionLogicalOperator, HandleUpdateCondition, HandleUpdateSubVariableCondition, LogicalOperator, handleRemoveSubVariableCondition } from '../types' -import type { Node, NodeOutPutVar, Var } from '../../../types' -import { VarType } from '../../../types' -import { useGetAvailableVars } from '../../variable-assigner/hooks' -import ConditionList from './condition-list' -import ConditionAdd from './condition-add' -import { SUB_VARIABLES } from './../default' -import { cn } from '@/utils/classnames' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { PortalSelect as Select } from '@/app/components/base/select' +import { cn } from '@/utils/classnames' +import { VarType } from '../../../types' +import { useGetAvailableVars } from '../../variable-assigner/hooks' +import { SUB_VARIABLES } from './../default' +import ConditionAdd from './condition-add' +import ConditionList from './condition-list' type Props = { isSubVariable?: boolean @@ -81,7 +81,7 @@ const ConditionWrap: FC<Props> = ({ > { conditions && !!conditions.length && ( - <div className='mb-2'> + <div className="mb-2"> <ConditionList disabled={readOnly} conditionId={conditionId} @@ -109,33 +109,34 @@ const ConditionWrap: FC<Props> = ({ !conditions.length && !isSubVariable && 'mt-1', !conditions.length && isSubVariable && 'mt-2', conditions.length > 1 && !isSubVariable && 'ml-[60px]', - )}> + )} + > {isSubVariable ? ( - <Select - popupInnerClassName='w-[165px] max-h-none' - onSelect={value => handleAddSubVariableCondition?.(conditionId!, value.value as string)} - items={subVarOptions} - value='' - renderTrigger={() => ( - <Button - size='small' - disabled={readOnly} - > - <RiAddLine className='mr-1 h-3.5 w-3.5' /> - {t('workflow.nodes.ifElse.addSubVariable')} - </Button> - )} - hideChecked - /> - ) + <Select + popupInnerClassName="w-[165px] max-h-none" + onSelect={value => handleAddSubVariableCondition?.(conditionId!, value.value as string)} + items={subVarOptions} + value="" + renderTrigger={() => ( + <Button + size="small" + disabled={readOnly} + > + <RiAddLine className="mr-1 h-3.5 w-3.5" /> + {t('workflow.nodes.ifElse.addSubVariable')} + </Button> + )} + hideChecked + /> + ) : ( - <ConditionAdd - disabled={readOnly} - variables={availableVars} - onSelectVariable={handleAddCondition!} - /> - )} + <ConditionAdd + disabled={readOnly} + variables={availableVars} + onSelectVariable={handleAddCondition!} + /> + )} </div> </div> </div> diff --git a/web/app/components/workflow/nodes/loop/components/loop-variables/empty.tsx b/web/app/components/workflow/nodes/loop/components/loop-variables/empty.tsx index 6fe4aa0a77..13b5a0ca67 100644 --- a/web/app/components/workflow/nodes/loop/components/loop-variables/empty.tsx +++ b/web/app/components/workflow/nodes/loop/components/loop-variables/empty.tsx @@ -4,7 +4,7 @@ const Empty = () => { const { t } = useTranslation() return ( - <div className='system-xs-regular flex h-10 items-center justify-center rounded-[10px] bg-background-section text-text-tertiary'> + <div className="system-xs-regular flex h-10 items-center justify-center rounded-[10px] bg-background-section text-text-tertiary"> {t('workflow.nodes.loop.setLoopVariables')} </div> ) diff --git a/web/app/components/workflow/nodes/loop/components/loop-variables/form-item.tsx b/web/app/components/workflow/nodes/loop/components/loop-variables/form-item.tsx index e4cc13835f..911b122925 100644 --- a/web/app/components/workflow/nodes/loop/components/loop-variables/form-item.tsx +++ b/web/app/components/workflow/nodes/loop/components/loop-variables/form-item.tsx @@ -1,13 +1,3 @@ -import { - useCallback, - useMemo, -} from 'react' -import { useTranslation } from 'react-i18next' -import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' -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 { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import type { LoopVariable, } from '@/app/components/workflow/nodes/loop/types' @@ -15,9 +5,16 @@ import type { Var, } from '@/app/components/workflow/types' import { - ValueType, - VarType, -} from '@/app/components/workflow/types' + useCallback, + useMemo, +} from 'react' +import { useTranslation } from 'react-i18next' +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 VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' +import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' +import ArrayBoolList from '@/app/components/workflow/panel/chat-variable-panel/components/array-bool-list' import BoolValue from '@/app/components/workflow/panel/chat-variable-panel/components/bool-value' import { @@ -27,7 +24,10 @@ import { arrayStringPlaceholder, objectPlaceholder, } from '@/app/components/workflow/panel/chat-variable-panel/utils' -import ArrayBoolList from '@/app/components/workflow/panel/chat-variable-panel/components/array-bool-list' +import { + ValueType, + VarType, +} from '@/app/components/workflow/types' type FormItemProps = { nodeId: string @@ -91,7 +91,7 @@ const FormItem = ({ <Textarea value={value} onChange={handleInputChange} - className='min-h-12 w-full' + className="min-h-12 w-full" /> ) } @@ -101,7 +101,7 @@ const FormItem = ({ type="number" value={value} onChange={handleInputChange} - className='w-full' + className="w-full" /> ) } @@ -117,15 +117,15 @@ const FormItem = ({ value_type === ValueType.constant && (var_type === VarType.object || var_type === VarType.arrayString || var_type === VarType.arrayNumber || var_type === VarType.arrayObject) && ( - <div className='w-full rounded-[10px] bg-components-input-bg-normal py-2 pl-3 pr-1' style={{ height: editorMinHeight }}> + <div className="w-full rounded-[10px] bg-components-input-bg-normal py-2 pl-3 pr-1" style={{ height: editorMinHeight }}> <CodeEditor value={value} isExpand noWrapper language={CodeLanguage.json} onChange={handleChange} - className='w-full' - placeholder={<div className='whitespace-pre'>{placeholder}</div>} + className="w-full" + placeholder={<div className="whitespace-pre">{placeholder}</div>} /> </div> ) @@ -133,7 +133,7 @@ const FormItem = ({ { value_type === ValueType.constant && var_type === VarType.arrayBoolean && ( <ArrayBoolList - className='mt-2' + className="mt-2" list={value || [false]} onChange={handleChange} /> diff --git a/web/app/components/workflow/nodes/loop/components/loop-variables/index.tsx b/web/app/components/workflow/nodes/loop/components/loop-variables/index.tsx index 26251e394c..ddd34baeae 100644 --- a/web/app/components/workflow/nodes/loop/components/loop-variables/index.tsx +++ b/web/app/components/workflow/nodes/loop/components/loop-variables/index.tsx @@ -1,9 +1,9 @@ -import Empty from './empty' -import Item from './item' import type { LoopVariable, LoopVariablesComponentShape, } from '@/app/components/workflow/nodes/loop/types' +import Empty from './empty' +import Item from './item' type LoopVariableProps = { variables?: LoopVariable[] diff --git a/web/app/components/workflow/nodes/loop/components/loop-variables/item.tsx b/web/app/components/workflow/nodes/loop/components/loop-variables/item.tsx index 7084389be8..973e78ae73 100644 --- a/web/app/components/workflow/nodes/loop/components/loop-variables/item.tsx +++ b/web/app/components/workflow/nodes/loop/components/loop-variables/item.tsx @@ -1,18 +1,18 @@ -import { useCallback } from 'react' -import { RiDeleteBinLine } from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import InputModeSelect from './input-mode-selec' -import VariableTypeSelect from './variable-type-select' -import FormItem from './form-item' -import ActionButton from '@/app/components/base/action-button' -import Input from '@/app/components/base/input' import type { LoopVariable, LoopVariablesComponentShape, } from '@/app/components/workflow/nodes/loop/types' -import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' +import { RiDeleteBinLine } from '@remixicon/react' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import ActionButton from '@/app/components/base/action-button' +import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' import { ValueType, VarType } from '@/app/components/workflow/types' +import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' +import FormItem from './form-item' +import InputModeSelect from './input-mode-selec' +import VariableTypeSelect from './variable-type-select' type ItemProps = { item: LoopVariable @@ -44,7 +44,7 @@ const Item = ({ }, [item.id, handleUpdateLoopVariable]) const getDefaultValue = useCallback((varType: VarType, valueType: ValueType) => { - if(valueType === ValueType.variable) + if (valueType === ValueType.variable) return undefined switch (varType) { case VarType.boolean: @@ -69,9 +69,9 @@ const Item = ({ }, [item.id, handleUpdateLoopVariable]) return ( - <div className='mb-4 flex last-of-type:mb-0'> - <div className='w-0 grow'> - <div className='mb-1 grid grid-cols-3 gap-1'> + <div className="mb-4 flex last-of-type:mb-0"> + <div className="w-0 grow"> + <div className="mb-1 grid grid-cols-3 gap-1"> <Input value={item.label} onChange={handleUpdateItemLabel} @@ -97,11 +97,11 @@ const Item = ({ </div> </div> <ActionButton - className='shrink-0' - size='l' + className="shrink-0" + size="l" onClick={() => handleRemoveLoopVariable(item.id)} > - <RiDeleteBinLine className='h-4 w-4 text-text-tertiary' /> + <RiDeleteBinLine className="h-4 w-4 text-text-tertiary" /> </ActionButton> </div> ) diff --git a/web/app/components/workflow/nodes/loop/default.ts b/web/app/components/workflow/nodes/loop/default.ts index 390545d0e2..cbc79bd54f 100644 --- a/web/app/components/workflow/nodes/loop/default.ts +++ b/web/app/components/workflow/nodes/loop/default.ts @@ -1,12 +1,14 @@ -import { VarType } from '../../types' import type { NodeDefault } from '../../types' -import { ComparisonOperator, LogicalOperator, type LoopNodeType } from './types' -import { isEmptyRelatedOperator } from './utils' -import { TransferMethod } from '@/types/app' -import { LOOP_NODE_MAX_COUNT } from '@/config' -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' +import type { LoopNodeType } from './types' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' +import { LOOP_NODE_MAX_COUNT } from '@/config' +import { TransferMethod } from '@/types/app' +import { VarType } from '../../types' +import { ComparisonOperator, LogicalOperator } from './types' +import { isEmptyRelatedOperator } from './utils' + const i18nPrefix = 'workflow.errorMsg' const metaData = genNodeMetaData({ @@ -66,8 +68,9 @@ const nodeDefault: NodeDefault<LoopNodeType> = { || !Number.isInteger(Number(payload.loop_count)) || payload.loop_count < 1 || payload.loop_count > LOOP_NODE_MAX_COUNT - )) + )) { errorMessages = t('workflow.nodes.loop.loopMaxCountError', { maxCount: LOOP_NODE_MAX_COUNT }) + } return { isValid: !errorMessages, diff --git a/web/app/components/workflow/nodes/loop/insert-block.tsx b/web/app/components/workflow/nodes/loop/insert-block.tsx index 57eef5d17a..7f24ac5078 100644 --- a/web/app/components/workflow/nodes/loop/insert-block.tsx +++ b/web/app/components/workflow/nodes/loop/insert-block.tsx @@ -1,15 +1,15 @@ +import type { + BlockEnum, + OnSelectBlock, +} from '../../types' import { memo, useCallback, useState, } from 'react' import { cn } from '@/utils/classnames' -import { useNodesInteractions } from '../../hooks' -import type { - BlockEnum, - OnSelectBlock, -} from '../../types' import BlockSelector from '../../block-selector' +import { useNodesInteractions } from '../../hooks' type InsertBlockProps = { startNodeId: string diff --git a/web/app/components/workflow/nodes/loop/node.tsx b/web/app/components/workflow/nodes/loop/node.tsx index feacb2ec4d..c8d3843a75 100644 --- a/web/app/components/workflow/nodes/loop/node.tsx +++ b/web/app/components/workflow/nodes/loop/node.tsx @@ -1,4 +1,6 @@ import type { FC } from 'react' +import type { LoopNodeType } from './types' +import type { NodeProps } from '@/app/components/workflow/types' import { memo, useEffect, @@ -8,13 +10,11 @@ import { useNodesInitialized, useViewport, } from 'reactflow' -import { LoopStartNodeDumb } from '../loop-start' -import { useNodeLoopInteractions } from './use-interactions' -import type { LoopNodeType } from './types' -import AddBlock from './add-block' import { cn } from '@/utils/classnames' +import { LoopStartNodeDumb } from '../loop-start' +import AddBlock from './add-block' -import type { NodeProps } from '@/app/components/workflow/types' +import { useNodeLoopInteractions } from './use-interactions' const Node: FC<NodeProps<LoopNodeType>> = ({ id, @@ -32,13 +32,14 @@ const Node: FC<NodeProps<LoopNodeType>> = ({ return ( <div className={cn( 'relative h-full min-h-[90px] w-full min-w-[240px] rounded-2xl bg-workflow-canvas-workflow-bg', - )}> + )} + > <Background id={`loop-background-${id}`} - className='!z-0 rounded-2xl' + className="!z-0 rounded-2xl" gap={[14 / zoom, 14 / zoom]} size={2 / zoom} - color='var(--color-workflow-canvas-workflow-dot-color)' + color="var(--color-workflow-canvas-workflow-dot-color)" /> { data._isCandidate && ( diff --git a/web/app/components/workflow/nodes/loop/panel.tsx b/web/app/components/workflow/nodes/loop/panel.tsx index d3f06482c2..45fec4030f 100644 --- a/web/app/components/workflow/nodes/loop/panel.tsx +++ b/web/app/components/workflow/nodes/loop/panel.tsx @@ -1,17 +1,17 @@ import type { FC } from 'react' +import type { LoopNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' +import { RiAddLine } from '@remixicon/react' import React from 'react' import { useTranslation } from 'react-i18next' -import { RiAddLine } from '@remixicon/react' -import Split from '../_base/components/split' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import { LOOP_NODE_MAX_COUNT } from '@/config' import InputNumberWithSlider from '../_base/components/input-number-with-slider' -import type { LoopNodeType } from './types' -import useConfig from './use-config' +import Split from '../_base/components/split' import ConditionWrap from './components/condition-wrap' import LoopVariable from './components/loop-variables' -import type { NodePanelProps } from '@/app/components/workflow/types' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import { LOOP_NODE_MAX_COUNT } from '@/config' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.loop' @@ -41,20 +41,20 @@ const Panel: FC<NodePanelProps<LoopNodeType>> = ({ } = useConfig(id, data) return ( - <div className='mt-2'> + <div className="mt-2"> <div> <Field - title={<div className='pl-3'>{t('workflow.nodes.loop.loopVariables')}</div>} - operations={ + title={<div className="pl-3">{t('workflow.nodes.loop.loopVariables')}</div>} + operations={( <div - className='mr-4 flex h-5 w-5 cursor-pointer items-center justify-center' + className="mr-4 flex h-5 w-5 cursor-pointer items-center justify-center" onClick={handleAddLoopVariable} > - <RiAddLine className='h-4 w-4 text-text-tertiary' /> + <RiAddLine className="h-4 w-4 text-text-tertiary" /> </div> - } + )} > - <div className='px-4'> + <div className="px-4"> <LoopVariable variables={inputs.loop_variables} nodeId={id} @@ -63,9 +63,9 @@ const Panel: FC<NodePanelProps<LoopNodeType>> = ({ /> </div> </Field> - <Split className='my-2' /> + <Split className="my-2" /> <Field - title={<div className='pl-3'>{t(`${i18nPrefix}.breakCondition`)}</div>} + title={<div className="pl-3">{t(`${i18nPrefix}.breakCondition`)}</div>} tooltip={t(`${i18nPrefix}.breakConditionTip`)} > <ConditionWrap @@ -85,12 +85,12 @@ const Panel: FC<NodePanelProps<LoopNodeType>> = ({ logicalOperator={inputs.logical_operator!} /> </Field> - <Split className='mt-2' /> - <div className='mt-2'> + <Split className="mt-2" /> + <div className="mt-2"> <Field - title={<div className='pl-3'>{t(`${i18nPrefix}.loopMaxCount`)}</div>} + title={<div className="pl-3">{t(`${i18nPrefix}.loopMaxCount`)}</div>} > - <div className='px-3 py-2'> + <div className="px-3 py-2"> <InputNumberWithSlider min={1} max={LOOP_NODE_MAX_COUNT} diff --git a/web/app/components/workflow/nodes/loop/use-config.ts b/web/app/components/workflow/nodes/loop/use-config.ts index e8504fb5e9..d64cd8492e 100644 --- a/web/app/components/workflow/nodes/loop/use-config.ts +++ b/web/app/components/workflow/nodes/loop/use-config.ts @@ -1,20 +1,4 @@ -import { - useCallback, - useRef, -} from 'react' -import { produce } from 'immer' -import { v4 as uuid4 } from 'uuid' -import { - useIsChatMode, - useNodesReadOnly, - useWorkflow, -} from '../../hooks' -import { ValueType, VarType } from '../../types' import type { ErrorHandleMode, Var } from '../../types' -import useNodeCrud from '../_base/hooks/use-node-crud' -import { toNodeOutputVars } from '../_base/components/variable/utils' -import { getOperators } from './utils' -import { LogicalOperator } from './types' import type { HandleAddCondition, HandleAddSubVariableCondition, @@ -25,7 +9,12 @@ import type { HandleUpdateSubVariableCondition, LoopNodeType, } from './types' -import useIsVarFileAttribute from './use-is-var-file-attribute' +import { produce } from 'immer' +import { + useCallback, + useRef, +} from 'react' +import { v4 as uuid4 } from 'uuid' import { useStore } from '@/app/components/workflow/store' import { useAllBuiltInTools, @@ -33,6 +22,17 @@ import { useAllMCPTools, useAllWorkflowTools, } from '@/service/use-tools' +import { + useIsChatMode, + useNodesReadOnly, + useWorkflow, +} from '../../hooks' +import { ValueType, VarType } from '../../types' +import { toNodeOutputVars } from '../_base/components/variable/utils' +import useNodeCrud from '../_base/hooks/use-node-crud' +import { LogicalOperator } from './types' +import useIsVarFileAttribute from './use-is-var-file-attribute' +import { getOperators } from './utils' const useConfig = (id: string, payload: LoopNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() diff --git a/web/app/components/workflow/nodes/loop/use-interactions.ts b/web/app/components/workflow/nodes/loop/use-interactions.ts index 737da404a0..006d8f963b 100644 --- a/web/app/components/workflow/nodes/loop/use-interactions.ts +++ b/web/app/components/workflow/nodes/loop/use-interactions.ts @@ -1,19 +1,19 @@ -import { useCallback } from 'react' -import { produce } from 'immer' -import { useStoreApi } from 'reactflow' import type { BlockEnum, Node, } from '../../types' +import { produce } from 'immer' +import { useCallback } from 'react' +import { useStoreApi } from 'reactflow' +import { useNodesMetaData } from '@/app/components/workflow/hooks' +import { + LOOP_PADDING, +} from '../../constants' import { generateNewNode, getNodeCustomTypeByNodeDataType, } from '../../utils' -import { - LOOP_PADDING, -} from '../../constants' import { CUSTOM_LOOP_START_NODE } from '../loop-start/constants' -import { useNodesMetaData } from '@/app/components/workflow/hooks' export const useNodeLoopInteractions = () => { const store = useStoreApi() @@ -75,7 +75,7 @@ export const useNodeLoopInteractions = () => { const { getNodes } = store.getState() const nodes = getNodes() - const restrictPosition: { x?: number; y?: number } = { x: undefined, y: undefined } + const restrictPosition: { x?: number, y?: number } = { x: undefined, y: undefined } if (node.data.isInLoop) { const parentNode = nodes.find(n => n.id === node.parentId) diff --git a/web/app/components/workflow/nodes/loop/use-is-var-file-attribute.ts b/web/app/components/workflow/nodes/loop/use-is-var-file-attribute.ts index b354d3149e..455d58e605 100644 --- a/web/app/components/workflow/nodes/loop/use-is-var-file-attribute.ts +++ b/web/app/components/workflow/nodes/loop/use-is-var-file-attribute.ts @@ -1,6 +1,6 @@ +import type { ValueSelector } from '../../types' import { useMemo } from 'react' import { useIsChatMode, useWorkflow, useWorkflowVariables } from '../../hooks' -import type { ValueSelector } from '../../types' import { VarType } from '../../types' type Params = { diff --git a/web/app/components/workflow/nodes/loop/use-single-run-form-params.ts b/web/app/components/workflow/nodes/loop/use-single-run-form-params.ts index 6a1b6b20f0..c4123b0e30 100644 --- a/web/app/components/workflow/nodes/loop/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/loop/use-single-run-form-params.ts @@ -1,13 +1,13 @@ -import type { NodeTracing } from '@/types/workflow' -import { useCallback, useMemo } from 'react' -import formatTracing from '@/app/components/workflow/run/utils/format-log' -import { useTranslation } from 'react-i18next' -import { useIsNodeInLoop, useWorkflow } from '../../hooks' -import { getNodeInfoById, getNodeUsedVarPassToServerKey, getNodeUsedVars, isSystemVar } from '../_base/components/variable/utils' import type { InputVar, ValueSelector, Variable } from '../../types' import type { CaseItem, Condition, LoopNodeType } from './types' +import type { NodeTracing } from '@/types/workflow' +import { useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import formatTracing from '@/app/components/workflow/run/utils/format-log' import { ValueType } from '@/app/components/workflow/types' import { VALUE_SELECTOR_DELIMITER as DELIMITER } from '@/config' +import { useIsNodeInLoop, useWorkflow } from '../../hooks' +import { getNodeInfoById, getNodeUsedVarPassToServerKey, getNodeUsedVars, isSystemVar } from '../_base/components/variable/utils' type Params = { id: string diff --git a/web/app/components/workflow/nodes/loop/utils.ts b/web/app/components/workflow/nodes/loop/utils.ts index bc5e6481ca..dba5460824 100644 --- a/web/app/components/workflow/nodes/loop/utils.ts +++ b/web/app/components/workflow/nodes/loop/utils.ts @@ -1,15 +1,18 @@ -import { ComparisonOperator } from './types' -import { VarType } from '@/app/components/workflow/types' import type { Branch } from '@/app/components/workflow/types' +import { VarType } from '@/app/components/workflow/types' +import { ComparisonOperator } from './types' export const isEmptyRelatedOperator = (operator: ComparisonOperator) => { return [ComparisonOperator.empty, ComparisonOperator.notEmpty, ComparisonOperator.isNull, ComparisonOperator.isNotNull, ComparisonOperator.exists, ComparisonOperator.notExists].includes(operator) } const notTranslateKey = [ - ComparisonOperator.equal, ComparisonOperator.notEqual, - ComparisonOperator.largerThan, ComparisonOperator.largerThanOrEqual, - ComparisonOperator.lessThan, ComparisonOperator.lessThanOrEqual, + ComparisonOperator.equal, + ComparisonOperator.notEqual, + ComparisonOperator.largerThan, + ComparisonOperator.largerThanOrEqual, + ComparisonOperator.lessThan, + ComparisonOperator.lessThanOrEqual, ] export const isComparisonOperatorNeedTranslate = (operator?: ComparisonOperator) => { diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/import-from-tool.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/import-from-tool.tsx index af2efad075..e8a4491173 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/import-from-tool.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/import-from-tool.tsx @@ -1,27 +1,27 @@ 'use client' import type { FC } from 'react' +import type { Param, ParamType } from '../../types' +import type { ToolParameter } from '@/app/components/tools/types' +import type { + PluginDefaultValue, + ToolDefaultValue, +} from '@/app/components/workflow/block-selector/types' +import type { BlockEnum } from '@/app/components/workflow/types' import { memo, useCallback, } from 'react' import { useTranslation } from 'react-i18next' -import BlockSelector from '../../../../block-selector' -import type { Param, ParamType } from '../../types' -import { cn } from '@/utils/classnames' -import type { - PluginDefaultValue, - ToolDefaultValue, -} from '@/app/components/workflow/block-selector/types' -import type { ToolParameter } from '@/app/components/tools/types' -import { CollectionType } from '@/app/components/tools/types' -import type { BlockEnum } from '@/app/components/workflow/types' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' -import { canFindTool } from '@/utils' +import { CollectionType } from '@/app/components/tools/types' import { useAllBuiltInTools, useAllCustomTools, useAllWorkflowTools, } from '@/service/use-tools' +import { canFindTool } from '@/utils' +import { cn } from '@/utils/classnames' +import BlockSelector from '../../../../block-selector' const i18nPrefix = 'workflow.nodes.parameterExtractor' @@ -80,7 +80,8 @@ const ImportFromTool: FC<Props> = ({ <div className={cn( 'flex h-6 cursor-pointer items-center rounded-md px-2 text-xs font-medium text-text-tertiary hover:bg-state-base-hover', open && 'bg-state-base-hover', - )}> + )} + > {t(`${i18nPrefix}.importFromTool`)} </div> </div> @@ -89,7 +90,7 @@ const ImportFromTool: FC<Props> = ({ return ( <BlockSelector - placement='bottom-end' + placement="bottom-end" offset={{ mainAxis: 4, crossAxis: 52, diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/item.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/item.tsx index dae43227b0..6382b33154 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/item.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/item.tsx @@ -1,13 +1,14 @@ 'use client' import type { FC } from 'react' -import React from 'react' -import { useTranslation } from 'react-i18next' +import type { Param } from '../../types' import { RiDeleteBinLine, RiEditLine, } from '@remixicon/react' -import type { Param } from '../../types' +import React from 'react' +import { useTranslation } from 'react-i18next' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' + const i18nPrefix = 'workflow.nodes.parameterExtractor' type Props = { @@ -24,33 +25,33 @@ const Item: FC<Props> = ({ const { t } = useTranslation() return ( - <div className='group relative rounded-lg bg-components-input-bg-normal px-2.5 py-2 hover:shadow-xs'> - <div className='flex justify-between'> - <div className='flex items-center'> - <Variable02 className='h-3.5 w-3.5 text-text-accent-secondary' /> - <div className='ml-1 text-[13px] font-medium text-text-primary'>{payload.name}</div> - <div className='ml-2 text-xs font-normal capitalize text-text-tertiary'>{payload.type}</div> + <div className="group relative rounded-lg bg-components-input-bg-normal px-2.5 py-2 hover:shadow-xs"> + <div className="flex justify-between"> + <div className="flex items-center"> + <Variable02 className="h-3.5 w-3.5 text-text-accent-secondary" /> + <div className="ml-1 text-[13px] font-medium text-text-primary">{payload.name}</div> + <div className="ml-2 text-xs font-normal capitalize text-text-tertiary">{payload.type}</div> </div> {payload.required && ( - <div className='text-xs font-normal uppercase leading-4 text-text-tertiary'>{t(`${i18nPrefix}.addExtractParameterContent.required`)}</div> + <div className="text-xs font-normal uppercase leading-4 text-text-tertiary">{t(`${i18nPrefix}.addExtractParameterContent.required`)}</div> )} </div> - <div className='mt-0.5 text-xs font-normal leading-[18px] text-text-tertiary'>{payload.description}</div> + <div className="mt-0.5 text-xs font-normal leading-[18px] text-text-tertiary">{payload.description}</div> <div - className='absolute right-0 top-0 hidden h-full w-[119px] items-center justify-end space-x-1 rounded-lg bg-gradient-to-l from-components-panel-on-panel-item-bg to-background-gradient-mask-transparent pr-1 group-hover:flex' + className="absolute right-0 top-0 hidden h-full w-[119px] items-center justify-end space-x-1 rounded-lg bg-gradient-to-l from-components-panel-on-panel-item-bg to-background-gradient-mask-transparent pr-1 group-hover:flex" > <div - className='cursor-pointer rounded-md p-1 hover:bg-state-base-hover' + className="cursor-pointer rounded-md p-1 hover:bg-state-base-hover" onClick={onEdit} > - <RiEditLine className='h-4 w-4 text-text-tertiary' /> + <RiEditLine className="h-4 w-4 text-text-tertiary" /> </div> <div - className='group shrink-0 cursor-pointer rounded-md p-1 hover:!bg-state-destructive-hover' + className="group shrink-0 cursor-pointer rounded-md p-1 hover:!bg-state-destructive-hover" onClick={onDelete} > - <RiDeleteBinLine className='h-4 w-4 text-text-tertiary group-hover:text-text-destructive' /> + <RiDeleteBinLine className="h-4 w-4 text-text-tertiary group-hover:text-text-destructive" /> </div> </div> </div> diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/list.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/list.tsx index eacb78e7f4..1cd1232983 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/list.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/list.tsx @@ -1,13 +1,13 @@ 'use client' import type { FC } from 'react' +import type { Param } from '../../types' +import type { MoreInfo } from '@/app/components/workflow/types' +import { useBoolean } from 'ahooks' import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useBoolean } from 'ahooks' -import type { Param } from '../../types' import ListNoDataPlaceholder from '../../../_base/components/list-no-data-placeholder' import Item from './item' import EditParam from './update' -import type { MoreInfo } from '@/app/components/workflow/types' const i18nPrefix = 'workflow.nodes.parameterExtractor' @@ -58,11 +58,11 @@ const List: FC<Props> = ({ if (list.length === 0) { return ( - <ListNoDataPlaceholder >{t(`${i18nPrefix}.extractParametersNotSet`)}</ListNoDataPlaceholder> + <ListNoDataPlaceholder>{t(`${i18nPrefix}.extractParametersNotSet`)}</ListNoDataPlaceholder> ) } return ( - <div className='space-y-1'> + <div className="space-y-1"> {list.map((item, index) => ( <Item key={index} @@ -73,7 +73,7 @@ const List: FC<Props> = ({ ))} {isShowEditModal && ( <EditParam - type='edit' + type="edit" payload={list[currEditItemIndex]} onSave={handleItemChange(currEditItemIndex)} onCancel={hideEditModal} diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx index 165ace458f..3048a78118 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx @@ -1,22 +1,23 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useState } from 'react' -import { useBoolean } from 'ahooks' -import { useTranslation } from 'react-i18next' import type { Param } from '../../types' -import { ParamType } from '../../types' -import AddButton from '@/app/components/base/button/add-button' -import Modal from '@/app/components/base/modal' -import Button from '@/app/components/base/button' +import type { MoreInfo } from '@/app/components/workflow/types' +import { useBoolean } from 'ahooks' +import React, { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' import Field from '@/app/components/app/configuration/config-var/config-modal/field' +import ConfigSelect from '@/app/components/app/configuration/config-var/config-select' +import Button from '@/app/components/base/button' +import AddButton from '@/app/components/base/button/add-button' import Input from '@/app/components/base/input' -import Textarea from '@/app/components/base/textarea' +import Modal from '@/app/components/base/modal' import Select from '@/app/components/base/select' import Switch from '@/app/components/base/switch' +import Textarea from '@/app/components/base/textarea' import Toast from '@/app/components/base/toast' -import ConfigSelect from '@/app/components/app/configuration/config-var/config-select' -import { ChangeType, type MoreInfo } from '@/app/components/workflow/types' +import { ChangeType } from '@/app/components/workflow/types' import { checkKeys } from '@/utils/var' +import { ParamType } from '../../types' const i18nPrefix = 'workflow.nodes.parameterExtractor' const errorI18nPrefix = 'workflow.errorMsg' @@ -61,12 +62,12 @@ const AddExtractParameter: FC<Props> = ({ } setRenameInfo(key === 'name' ? { - type: ChangeType.changeVarName, - payload: { - beforeKey: param.name, - afterKey: value, - }, - } + type: ChangeType.changeVarName, + payload: { + beforeKey: param.name, + afterKey: value, + }, + } : undefined) setParam((prev) => { return { @@ -124,17 +125,17 @@ const AddExtractParameter: FC<Props> = ({ return ( <div> {isAdd && ( - <AddButton className='mx-1' onClick={showAddModal} /> + <AddButton className="mx-1" onClick={showAddModal} /> )} {isShowModal && ( <Modal title={t(`${i18nPrefix}.addExtractParameter`)} isShow onClose={hideModal} - className='!w-[400px] !max-w-[400px] !p-4' + className="!w-[400px] !max-w-[400px] !p-4" > <div> - <div className='space-y-2'> + <div className="space-y-2"> <Field title={t(`${i18nPrefix}.addExtractParameterContent.name`)}> <Input value={param.name} @@ -148,7 +149,7 @@ const AddExtractParameter: FC<Props> = ({ allowSearch={false} // bgClassName='bg-gray-100' onSelect={v => handleParamChange('type')(v.value)} - optionClassName='capitalize' + optionClassName="capitalize" items={ TYPES.map(type => ({ value: type, @@ -171,14 +172,14 @@ const AddExtractParameter: FC<Props> = ({ </Field> <Field title={t(`${i18nPrefix}.addExtractParameterContent.required`)}> <> - <div className='mb-1.5 text-xs font-normal leading-[18px] text-text-tertiary'>{t(`${i18nPrefix}.addExtractParameterContent.requiredContent`)}</div> - <Switch size='l' defaultValue={param.required} onChange={handleParamChange('required')} /> + <div className="mb-1.5 text-xs font-normal leading-[18px] text-text-tertiary">{t(`${i18nPrefix}.addExtractParameterContent.requiredContent`)}</div> + <Switch size="l" defaultValue={param.required} onChange={handleParamChange('required')} /> </> </Field> </div> - <div className='mt-4 flex justify-end space-x-2'> - <Button className='!w-[95px]' onClick={hideModal} >{t('common.operation.cancel')}</Button> - <Button className='!w-[95px]' variant='primary' onClick={handleSave} >{isAdd ? t('common.operation.add') : t('common.operation.save')}</Button> + <div className="mt-4 flex justify-end space-x-2"> + <Button className="!w-[95px]" onClick={hideModal}>{t('common.operation.cancel')}</Button> + <Button className="!w-[95px]" variant="primary" onClick={handleSave}>{isAdd ? t('common.operation.add') : t('common.operation.save')}</Button> </div> </div> </Modal> diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/reasoning-mode-picker.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/reasoning-mode-picker.tsx index f4fd6e85a6..dc5354a21a 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/components/reasoning-mode-picker.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/components/reasoning-mode-picker.tsx @@ -2,9 +2,9 @@ import type { FC } from 'react' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { ReasoningModeType } from '../types' import Field from '../../_base/components/field' import OptionCard from '../../_base/components/option-card' +import { ReasoningModeType } from '../types' const i18nPrefix = 'workflow.nodes.parameterExtractor' @@ -30,14 +30,14 @@ const ReasoningModePicker: FC<Props> = ({ title={t(`${i18nPrefix}.reasoningMode`)} tooltip={t(`${i18nPrefix}.reasoningModeTip`)!} > - <div className='grid grid-cols-2 gap-x-1'> + <div className="grid grid-cols-2 gap-x-1"> <OptionCard - title='Function/Tool Calling' + title="Function/Tool Calling" onSelect={handleChange(ReasoningModeType.functionCall)} selected={type === ReasoningModeType.functionCall} /> <OptionCard - title='Prompt' + title="Prompt" selected={type === ReasoningModeType.prompt} onSelect={handleChange(ReasoningModeType.prompt)} /> diff --git a/web/app/components/workflow/nodes/parameter-extractor/default.ts b/web/app/components/workflow/nodes/parameter-extractor/default.ts index 5d2010122d..cfb317487e 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/default.ts +++ b/web/app/components/workflow/nodes/parameter-extractor/default.ts @@ -1,9 +1,11 @@ import type { NodeDefault } from '../../types' -import { type ParameterExtractorNodeType, ReasoningModeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' +import type { ParameterExtractorNodeType } from './types' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' import { AppModeEnum } from '@/types/app' +import { ReasoningModeType } from './types' + const i18nPrefix = 'workflow' const metaData = genNodeMetaData({ diff --git a/web/app/components/workflow/nodes/parameter-extractor/node.tsx b/web/app/components/workflow/nodes/parameter-extractor/node.tsx index d79ae717d9..014706810f 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/node.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/node.tsx @@ -1,11 +1,11 @@ import type { FC } from 'react' -import React from 'react' import type { ParameterExtractorNodeType } from './types' +import type { NodeProps } from '@/app/components/workflow/types' +import React from 'react' import { useTextGenerationCurrentProviderAndModelAndModelList, } from '@/app/components/header/account-setting/model-provider-page/hooks' import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' -import type { NodeProps } from '@/app/components/workflow/types' const Node: FC<NodeProps<ParameterExtractorNodeType>> = ({ data, @@ -16,12 +16,12 @@ const Node: FC<NodeProps<ParameterExtractorNodeType>> = ({ } = useTextGenerationCurrentProviderAndModelAndModelList() const hasSetModel = provider && modelId return ( - <div className='mb-1 px-3 py-1'> + <div className="mb-1 px-3 py-1"> {hasSetModel && ( <ModelSelector defaultModel={{ provider, model: modelId }} modelList={textGenerationModelList} - triggerClassName='!h-6 !rounded-md' + triggerClassName="!h-6 !rounded-md" readonly /> )} diff --git a/web/app/components/workflow/nodes/parameter-extractor/panel.tsx b/web/app/components/workflow/nodes/parameter-extractor/panel.tsx index 8faebfa547..563c124102 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/panel.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/panel.tsx @@ -1,24 +1,24 @@ import type { FC } from 'react' +import type { ParameterExtractorNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import MemoryConfig from '../_base/components/memory-config' -import VarReferencePicker from '../_base/components/variable/var-reference-picker' -import Editor from '../_base/components/prompt/editor' +import Tooltip from '@/app/components/base/tooltip' +import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' +import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' +import Split from '@/app/components/workflow/nodes/_base/components/split' +import { VarType } from '@/app/components/workflow/types' import ConfigVision from '../_base/components/config-vision' -import useConfig from './use-config' -import type { ParameterExtractorNodeType } from './types' -import ExtractParameter from './components/extract-parameter/list' +import MemoryConfig from '../_base/components/memory-config' +import Editor from '../_base/components/prompt/editor' +import VarReferencePicker from '../_base/components/variable/var-reference-picker' import ImportFromTool from './components/extract-parameter/import-from-tool' +import ExtractParameter from './components/extract-parameter/list' import AddExtractParameter from './components/extract-parameter/update' import ReasoningModePicker from './components/reasoning-mode-picker' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import Split from '@/app/components/workflow/nodes/_base/components/split' -import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' -import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' -import type { NodePanelProps } from '@/app/components/workflow/types' -import Tooltip from '@/app/components/base/tooltip' -import { VarType } from '@/app/components/workflow/types' -import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.parameterExtractor' const i18nCommonPrefix = 'workflow.common' @@ -57,14 +57,14 @@ const Panel: FC<NodePanelProps<ParameterExtractorNodeType>> = ({ const model = inputs.model return ( - <div className='pt-2'> - <div className='space-y-4 px-4'> + <div className="pt-2"> + <div className="space-y-4 px-4"> <Field title={t(`${i18nCommonPrefix}.model`)} required > <ModelParameterModal - popupClassName='!w-[387px]' + popupClassName="!w-[387px]" isInWorkflow isAdvancedMode={true} provider={model?.provider} @@ -108,14 +108,14 @@ const Panel: FC<NodePanelProps<ParameterExtractorNodeType>> = ({ operations={ !readOnly ? ( - <div className='flex items-center space-x-1'> - {!readOnly && ( - <ImportFromTool onImport={handleImportFromTool} /> - )} - {!readOnly && (<div className='h-3 w-px bg-divider-regular'></div>)} - <AddExtractParameter type='add' onSave={addExtractParameter} /> - </div> - ) + <div className="flex items-center space-x-1"> + {!readOnly && ( + <ImportFromTool onImport={handleImportFromTool} /> + )} + {!readOnly && (<div className="h-3 w-px bg-divider-regular"></div>)} + <AddExtractParameter type="add" onSave={addExtractParameter} /> + </div> + ) : undefined } > @@ -126,19 +126,19 @@ const Panel: FC<NodePanelProps<ParameterExtractorNodeType>> = ({ /> </Field> <Editor - title={ - <div className='flex items-center space-x-1'> - <span className='uppercase'>{t(`${i18nPrefix}.instruction`)}</span> + title={( + <div className="flex items-center space-x-1"> + <span className="uppercase">{t(`${i18nPrefix}.instruction`)}</span> <Tooltip - popupContent={ - <div className='w-[120px]'> + popupContent={( + <div className="w-[120px]"> {t(`${i18nPrefix}.instructionTip`)} </div> - } - triggerClassName='w-3.5 h-3.5 ml-0.5' + )} + triggerClassName="w-3.5 h-3.5 ml-0.5" /> </div> - } + )} value={inputs.instruction} onChange={handleInstructionChange} readOnly={readOnly} @@ -154,7 +154,7 @@ const Panel: FC<NodePanelProps<ParameterExtractorNodeType>> = ({ <> {/* Memory */} {isChatMode && ( - <div className='mt-4'> + <div className="mt-4"> <MemoryConfig readonly={readOnly} config={{ data: inputs.memory }} @@ -164,7 +164,7 @@ const Panel: FC<NodePanelProps<ParameterExtractorNodeType>> = ({ </div> )} {isSupportFunctionCall && ( - <div className='mt-2'> + <div className="mt-2"> <ReasoningModePicker type={inputs.reasoning_mode} onChange={handleReasoningModeChange} @@ -173,38 +173,40 @@ const Panel: FC<NodePanelProps<ParameterExtractorNodeType>> = ({ )} </> </FieldCollapse> - {inputs.parameters?.length > 0 && (<> - <Split /> - <div> - <OutputVars> - <> - {inputs.parameters.map((param, index) => ( + {inputs.parameters?.length > 0 && ( + <> + <Split /> + <div> + <OutputVars> + <> + {inputs.parameters.map((param, index) => ( + <VarItem + key={index} + name={param.name} + type={param.type} + description={param.description} + /> + ))} <VarItem - key={index} - name={param.name} - type={param.type} - description={param.description} + name="__is_success" + type={VarType.number} + description={t(`${i18nPrefix}.outputVars.isSuccess`)} /> - ))} - <VarItem - name='__is_success' - type={VarType.number} - description={t(`${i18nPrefix}.outputVars.isSuccess`)} - /> - <VarItem - name='__reason' - type={VarType.string} - description={t(`${i18nPrefix}.outputVars.errorReason`)} - /> - <VarItem - name='__usage' - type='object' - description={t(`${i18nPrefix}.outputVars.usage`)} - /> - </> - </OutputVars> - </div> - </>)} + <VarItem + name="__reason" + type={VarType.string} + description={t(`${i18nPrefix}.outputVars.errorReason`)} + /> + <VarItem + name="__usage" + type="object" + description={t(`${i18nPrefix}.outputVars.usage`)} + /> + </> + </OutputVars> + </div> + </> + )} </div> ) } diff --git a/web/app/components/workflow/nodes/parameter-extractor/use-config.ts b/web/app/components/workflow/nodes/parameter-extractor/use-config.ts index 676d631a8a..71e94e95db 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/use-config.ts +++ b/web/app/components/workflow/nodes/parameter-extractor/use-config.ts @@ -1,23 +1,23 @@ -import { useCallback, useEffect, useRef, useState } from 'react' -import { produce } from 'immer' import type { Memory, MoreInfo, ValueSelector, Var } from '../../types' -import { ChangeType, VarType } from '../../types' -import { useStore } from '../../store' +import type { Param, ParameterExtractorNodeType, ReasoningModeType } from './types' +import { produce } from 'immer' +import { useCallback, useEffect, useRef, useState } from 'react' +import { checkHasQueryBlock } from '@/app/components/base/prompt-editor/constants' +import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { useModelListAndDefaultModelAndCurrentProviderAndModel, useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' +import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { AppModeEnum } from '@/types/app' +import { supportFunctionCall } from '@/utils/tool-call' import { useIsChatMode, useNodesReadOnly, useWorkflow, } from '../../hooks' import useConfigVision from '../../hooks/use-config-vision' -import type { Param, ParameterExtractorNodeType, ReasoningModeType } from './types' -import { useModelListAndDefaultModelAndCurrentProviderAndModel, useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks' -import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' -import { checkHasQueryBlock } from '@/app/components/base/prompt-editor/constants' -import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' -import { supportFunctionCall } from '@/utils/tool-call' import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud' -import { AppModeEnum } from '@/types/app' +import { useStore } from '../../store' +import { ChangeType, VarType } from '../../types' const useConfig = (id: string, payload: ParameterExtractorNodeType) => { const { @@ -30,7 +30,7 @@ const useConfig = (id: string, payload: ParameterExtractorNodeType) => { const defaultConfig = useStore(s => s.nodesDefaultConfigs)?.[payload.type] - const [defaultRolePrefix, setDefaultRolePrefix] = useState<{ user: string; assistant: string }>({ user: '', assistant: '' }) + const [defaultRolePrefix, setDefaultRolePrefix] = useState<{ user: string, assistant: string }>({ user: '', assistant: '' }) const { inputs, setInputs: doSetInputs } = useNodeCrud<ParameterExtractorNodeType>(id, payload) const inputRef = useRef(inputs) @@ -127,7 +127,7 @@ const useConfig = (id: string, payload: ParameterExtractorNodeType) => { currentModel, } = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.textGeneration) - const handleModelChanged = useCallback((model: { provider: string; modelId: string; mode?: string }) => { + const handleModelChanged = useCallback((model: { provider: string, modelId: string, mode?: string }) => { const newInputs = produce(inputRef.current, (draft) => { draft.model.provider = model.provider draft.model.name = model.modelId diff --git a/web/app/components/workflow/nodes/parameter-extractor/use-single-run-form-params.ts b/web/app/components/workflow/nodes/parameter-extractor/use-single-run-form-params.ts index 68a6f4992b..abf187d6e5 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/parameter-extractor/use-single-run-form-params.ts @@ -1,21 +1,21 @@ import type { RefObject } from 'react' -import { useTranslation } from 'react-i18next' +import type { ParameterExtractorNodeType } from './types' import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form' import type { InputVar, Var, Variable } from '@/app/components/workflow/types' -import { InputVarType, VarType } from '@/app/components/workflow/types' -import type { ParameterExtractorNodeType } from './types' -import useNodeCrud from '../_base/hooks/use-node-crud' -import { useCallback } from 'react' -import useConfigVision from '../../hooks/use-config-vision' import { noop } from 'lodash-es' -import { findVariableWhenOnLLMVision } from '../utils' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import { InputVarType, VarType } from '@/app/components/workflow/types' +import useConfigVision from '../../hooks/use-config-vision' import useAvailableVarList from '../_base/hooks/use-available-var-list' +import useNodeCrud from '../_base/hooks/use-node-crud' +import { findVariableWhenOnLLMVision } from '../utils' const i18nPrefix = 'workflow.nodes.parameterExtractor' type Params = { - id: string, - payload: ParameterExtractorNodeType, + id: string + payload: ParameterExtractorNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] @@ -136,9 +136,9 @@ const useSingleRunFormParams = ({ } const getDependentVar = (variable: string) => { - if(variable === 'query') + if (variable === 'query') return payload.query - if(variable === '#files#') + if (variable === '#files#') return payload.vision.configs?.variable_selector return false diff --git a/web/app/components/workflow/nodes/question-classifier/components/advanced-setting.tsx b/web/app/components/workflow/nodes/question-classifier/components/advanced-setting.tsx index dc654607f7..336bd3463a 100644 --- a/web/app/components/workflow/nodes/question-classifier/components/advanced-setting.tsx +++ b/web/app/components/workflow/nodes/question-classifier/components/advanced-setting.tsx @@ -1,11 +1,12 @@ 'use client' import type { FC } from 'react' +import type { Memory, Node, NodeOutPutVar } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import MemoryConfig from '../../_base/components/memory-config' -import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' -import type { Memory, Node, NodeOutPutVar } from '@/app/components/workflow/types' import Tooltip from '@/app/components/base/tooltip' +import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' +import MemoryConfig from '../../_base/components/memory-config' + const i18nPrefix = 'workflow.nodes.questionClassifiers' type Props = { @@ -44,19 +45,19 @@ const AdvancedSetting: FC<Props> = ({ return ( <> <Editor - title={ - <div className='flex items-center space-x-1'> - <span className='uppercase'>{t(`${i18nPrefix}.instruction`)}</span> + title={( + <div className="flex items-center space-x-1"> + <span className="uppercase">{t(`${i18nPrefix}.instruction`)}</span> <Tooltip - popupContent={ - <div className='w-[120px]'> + popupContent={( + <div className="w-[120px]"> {t(`${i18nPrefix}.instructionTip`)} </div> - } - triggerClassName='w-3.5 h-3.5 ml-0.5' + )} + triggerClassName="w-3.5 h-3.5 ml-0.5" /> </div> - } + )} value={instruction} onChange={onInstructionChange} readOnly={readonly} @@ -69,7 +70,7 @@ const AdvancedSetting: FC<Props> = ({ /> {!hideMemorySetting && ( <MemoryConfig - className='mt-4' + className="mt-4" readonly={false} config={{ data: memory }} onChange={onMemoryChange} diff --git a/web/app/components/workflow/nodes/question-classifier/components/class-item.tsx b/web/app/components/workflow/nodes/question-classifier/components/class-item.tsx index 8e6865f557..eb629a857c 100644 --- a/web/app/components/workflow/nodes/question-classifier/components/class-item.tsx +++ b/web/app/components/workflow/nodes/question-classifier/components/class-item.tsx @@ -1,12 +1,12 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' import type { Topic } from '../types' -import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' -import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' import type { ValueSelector, Var } from '@/app/components/workflow/types' import { uniqueId } from 'lodash-es' +import React, { useCallback, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' +import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' const i18nPrefix = 'workflow.nodes.questionClassifiers' diff --git a/web/app/components/workflow/nodes/question-classifier/components/class-list.tsx b/web/app/components/workflow/nodes/question-classifier/components/class-list.tsx index e4300008ec..387d60d671 100644 --- a/web/app/components/workflow/nodes/question-classifier/components/class-list.tsx +++ b/web/app/components/workflow/nodes/question-classifier/components/class-list.tsx @@ -1,18 +1,18 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import type { Topic } from '@/app/components/workflow/nodes/question-classifier/types' +import type { ValueSelector, Var } from '@/app/components/workflow/types' +import { RiDraggable } from '@remixicon/react' import { produce } from 'immer' +import { noop } from 'lodash-es' +import React, { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' +import { ReactSortable } from 'react-sortablejs' +import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/general' +import { cn } from '@/utils/classnames' import { useEdgesInteractions } from '../../../hooks' import AddButton from '../../_base/components/add-button' import Item from './class-item' -import type { Topic } from '@/app/components/workflow/nodes/question-classifier/types' -import type { ValueSelector, Var } from '@/app/components/workflow/types' -import { ReactSortable } from 'react-sortablejs' -import { noop } from 'lodash-es' -import { cn } from '@/utils/classnames' -import { RiDraggable } from '@remixicon/react' -import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/general' const i18nPrefix = 'workflow.nodes.questionClassifiers' @@ -87,9 +87,11 @@ const ClassList: FC<Props> = ({ return ( <> - <div className='mb-2 flex items-center justify-between' onClick={handleCollapse}> - <div className='flex cursor-pointer items-center text-xs font-semibold uppercase text-text-secondary'> - {t(`${i18nPrefix}.class`)} <span className='text-text-destructive'>*</span> + <div className="mb-2 flex items-center justify-between" onClick={handleCollapse}> + <div className="flex cursor-pointer items-center text-xs font-semibold uppercase text-text-secondary"> + {t(`${i18nPrefix}.class`)} + {' '} + <span className="text-text-destructive">*</span> {list.length > 0 && ( <ArrowDownRoundFill className={cn( @@ -109,11 +111,11 @@ const ClassList: FC<Props> = ({ <ReactSortable list={list.map(item => ({ ...item }))} setList={handleSortTopic} - handle='.handle' - ghostClass='bg-components-panel-bg' + handle=".handle" + ghostClass="bg-components-panel-bg" animation={150} disabled={readonly} - className='space-y-2' + className="space-y-2" > { list.map((item, index) => { @@ -136,10 +138,13 @@ const ClassList: FC<Props> = ({ }} > <div> - {canDrag && <RiDraggable className={cn( - 'handle absolute left-2 top-3 hidden h-3 w-3 cursor-pointer text-text-tertiary', - 'group-hover:block', - )} />} + {canDrag && ( + <RiDraggable className={cn( + 'handle absolute left-2 top-3 hidden h-3 w-3 cursor-pointer text-text-tertiary', + 'group-hover:block', + )} + /> + )} <Item className={cn(canDrag && 'handle')} headerClassName={cn(canDrag && 'cursor-grab group-hover:pl-5')} @@ -161,7 +166,7 @@ const ClassList: FC<Props> = ({ </div> )} {!readonly && !collapsed && ( - <div className='mt-2'> + <div className="mt-2"> <AddButton onClick={handleAddClass} text={t(`${i18nPrefix}.addClass`)} diff --git a/web/app/components/workflow/nodes/question-classifier/default.ts b/web/app/components/workflow/nodes/question-classifier/default.ts index 90ae3fd586..88a4b32d95 100644 --- a/web/app/components/workflow/nodes/question-classifier/default.ts +++ b/web/app/components/workflow/nodes/question-classifier/default.ts @@ -1,8 +1,8 @@ import type { NodeDefault } from '../../types' import type { QuestionClassifierNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' import { AppModeEnum } from '@/types/app' const i18nPrefix = 'workflow' diff --git a/web/app/components/workflow/nodes/question-classifier/node.tsx b/web/app/components/workflow/nodes/question-classifier/node.tsx index 2da37929c8..e0a330e110 100644 --- a/web/app/components/workflow/nodes/question-classifier/node.tsx +++ b/web/app/components/workflow/nodes/question-classifier/node.tsx @@ -1,23 +1,23 @@ +import type { TFunction } from 'i18next' import type { FC } from 'react' +import type { NodeProps } from 'reactflow' +import type { QuestionClassifierNodeType } from './types' import React from 'react' import { useTranslation } from 'react-i18next' -import type { TFunction } from 'i18next' -import type { NodeProps } from 'reactflow' -import { NodeSourceHandle } from '../_base/components/node-handle' -import type { QuestionClassifierNodeType } from './types' +import Tooltip from '@/app/components/base/tooltip' import { useTextGenerationCurrentProviderAndModelAndModelList, } from '@/app/components/header/account-setting/model-provider-page/hooks' import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector' +import { NodeSourceHandle } from '../_base/components/node-handle' import ReadonlyInputWithSelectVar from '../_base/components/readonly-input-with-select-var' -import Tooltip from '@/app/components/base/tooltip' const i18nPrefix = 'workflow.nodes.questionClassifiers' const MAX_CLASS_TEXT_LENGTH = 50 type TruncatedClassItemProps = { - topic: { id: string; name: string } + topic: { id: string, name: string } index: number nodeId: string t: TFunction @@ -31,31 +31,32 @@ const TruncatedClassItem: FC<TruncatedClassItemProps> = ({ topic, index, nodeId, const shouldShowTooltip = topic.name.length > MAX_CLASS_TEXT_LENGTH const content = ( - <div className='system-xs-regular truncate text-text-tertiary'> + <div className="system-xs-regular truncate text-text-tertiary"> <ReadonlyInputWithSelectVar value={truncatedText} nodeId={nodeId} - className='truncate' + className="truncate" /> </div> ) return ( - <div className='flex flex-col gap-y-0.5 rounded-md bg-workflow-block-parma-bg px-[5px] py-[3px]'> - <div className='system-2xs-semibold-uppercase uppercase text-text-secondary'> + <div className="flex flex-col gap-y-0.5 rounded-md bg-workflow-block-parma-bg px-[5px] py-[3px]"> + <div className="system-2xs-semibold-uppercase uppercase text-text-secondary"> {`${t(`${i18nPrefix}.class`)} ${index + 1}`} </div> {shouldShowTooltip - ? (<Tooltip - popupContent={ - <div className='max-w-[300px] break-words'> - <ReadonlyInputWithSelectVar value={topic.name} nodeId={nodeId}/> - </div> - } - > - {content} - </Tooltip> - ) + ? ( + <Tooltip + popupContent={( + <div className="max-w-[300px] break-words"> + <ReadonlyInputWithSelectVar value={topic.name} nodeId={nodeId} /> + </div> + )} + > + {content} + </Tooltip> + ) : content} </div> ) @@ -77,23 +78,23 @@ const Node: FC<NodeProps<QuestionClassifierNodeType>> = (props) => { return null return ( - <div className='mb-1 px-3 py-1'> + <div className="mb-1 px-3 py-1"> {hasSetModel && ( <ModelSelector defaultModel={{ provider, model: modelId }} - triggerClassName='!h-6 !rounded-md' + triggerClassName="!h-6 !rounded-md" modelList={textGenerationModelList} readonly /> )} { !!topics.length && ( - <div className='mt-2 space-y-0.5'> - <div className='space-y-0.5'> + <div className="mt-2 space-y-0.5"> + <div className="space-y-0.5"> {topics.map((topic, index) => ( <div key={topic.id} - className='relative' + className="relative" > <TruncatedClassItem topic={topic} @@ -104,7 +105,7 @@ const Node: FC<NodeProps<QuestionClassifierNodeType>> = (props) => { <NodeSourceHandle {...props} handleId={topic.id} - handleClassName='!top-1/2 !-translate-y-1/2 !-right-[21px]' + handleClassName="!top-1/2 !-translate-y-1/2 !-right-[21px]" /> </div> ))} diff --git a/web/app/components/workflow/nodes/question-classifier/panel.tsx b/web/app/components/workflow/nodes/question-classifier/panel.tsx index 0e54d2712b..9496f90915 100644 --- a/web/app/components/workflow/nodes/question-classifier/panel.tsx +++ b/web/app/components/workflow/nodes/question-classifier/panel.tsx @@ -1,18 +1,18 @@ import type { FC } from 'react' +import type { QuestionClassifierNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import VarReferencePicker from '../_base/components/variable/var-reference-picker' -import ConfigVision from '../_base/components/config-vision' -import useConfig from './use-config' -import ClassList from './components/class-list' -import AdvancedSetting from './components/advanced-setting' -import type { QuestionClassifierNodeType } from './types' -import Field from '@/app/components/workflow/nodes/_base/components/field' import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' -import type { NodePanelProps } from '@/app/components/workflow/types' -import Split from '@/app/components/workflow/nodes/_base/components/split' -import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' +import Split from '@/app/components/workflow/nodes/_base/components/split' +import ConfigVision from '../_base/components/config-vision' +import VarReferencePicker from '../_base/components/variable/var-reference-picker' +import AdvancedSetting from './components/advanced-setting' +import ClassList from './components/class-list' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.questionClassifiers' @@ -46,14 +46,14 @@ const Panel: FC<NodePanelProps<QuestionClassifierNodeType>> = ({ const model = inputs.model return ( - <div className='pt-2'> - <div className='space-y-4 px-4'> + <div className="pt-2"> + <div className="space-y-4 px-4"> <Field title={t(`${i18nPrefix}.model`)} required > <ModelParameterModal - popupClassName='!w-[387px]' + popupClassName="!w-[387px]" isInWorkflow isAdvancedMode={true} provider={model?.provider} @@ -121,13 +121,13 @@ const Panel: FC<NodePanelProps<QuestionClassifierNodeType>> = ({ <OutputVars> <> <VarItem - name='class_name' - type='string' + name="class_name" + type="string" description={t(`${i18nPrefix}.outputVars.className`)} /> <VarItem - name='usage' - type='object' + name="usage" + type="object" description={t(`${i18nPrefix}.outputVars.usage`)} /> </> diff --git a/web/app/components/workflow/nodes/question-classifier/use-config.ts b/web/app/components/workflow/nodes/question-classifier/use-config.ts index 28a6fa0314..5a46897de5 100644 --- a/web/app/components/workflow/nodes/question-classifier/use-config.ts +++ b/web/app/components/workflow/nodes/question-classifier/use-config.ts @@ -1,21 +1,22 @@ -import { useCallback, useEffect, useRef, useState } from 'react' -import { produce } from 'immer' -import { BlockEnum, VarType } from '../../types' import type { Memory, ValueSelector, Var } from '../../types' +import type { QuestionClassifierNodeType, Topic } from './types' +import { produce } from 'immer' +import { useCallback, useEffect, useRef, useState } from 'react' +import { useUpdateNodeInternals } from 'reactflow' +import { checkHasQueryBlock } from '@/app/components/base/prompt-editor/constants' +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 useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { AppModeEnum } from '@/types/app' import { - useIsChatMode, useNodesReadOnly, + useIsChatMode, + useNodesReadOnly, useWorkflow, } from '../../hooks' -import { useStore } from '../../store' -import useAvailableVarList from '../_base/hooks/use-available-var-list' import useConfigVision from '../../hooks/use-config-vision' -import type { QuestionClassifierNodeType, Topic } from './types' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' -import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' -import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { checkHasQueryBlock } from '@/app/components/base/prompt-editor/constants' -import { useUpdateNodeInternals } from 'reactflow' -import { AppModeEnum } from '@/types/app' +import { useStore } from '../../store' +import { BlockEnum, VarType } from '../../types' +import useAvailableVarList from '../_base/hooks/use-available-var-list' const useConfig = (id: string, payload: QuestionClassifierNodeType) => { const updateNodeInternals = useUpdateNodeInternals() @@ -56,7 +57,7 @@ const useConfig = (id: string, payload: QuestionClassifierNodeType) => { }, }) - const handleModelChanged = useCallback((model: { provider: string; modelId: string; mode?: string }) => { + const handleModelChanged = useCallback((model: { provider: string, modelId: string, mode?: string }) => { const newInputs = produce(inputRef.current, (draft) => { draft.model.provider = model.provider draft.model.name = model.modelId diff --git a/web/app/components/workflow/nodes/question-classifier/use-single-run-form-params.ts b/web/app/components/workflow/nodes/question-classifier/use-single-run-form-params.ts index 79c63cf1da..095809eba2 100644 --- a/web/app/components/workflow/nodes/question-classifier/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/question-classifier/use-single-run-form-params.ts @@ -1,21 +1,21 @@ import type { RefObject } from 'react' -import { useTranslation } from 'react-i18next' +import type { QuestionClassifierNodeType } from './types' import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form' import type { InputVar, Var, Variable } from '@/app/components/workflow/types' -import { InputVarType, VarType } from '@/app/components/workflow/types' -import type { QuestionClassifierNodeType } from './types' -import useNodeCrud from '../_base/hooks/use-node-crud' -import { useCallback } from 'react' -import useConfigVision from '../../hooks/use-config-vision' import { noop } from 'lodash-es' -import { findVariableWhenOnLLMVision } from '../utils' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import { InputVarType, VarType } from '@/app/components/workflow/types' +import useConfigVision from '../../hooks/use-config-vision' import useAvailableVarList from '../_base/hooks/use-available-var-list' +import useNodeCrud from '../_base/hooks/use-node-crud' +import { findVariableWhenOnLLMVision } from '../utils' const i18nPrefix = 'workflow.nodes.questionClassifiers' type Params = { - id: string, - payload: QuestionClassifierNodeType, + id: string + payload: QuestionClassifierNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] @@ -134,9 +134,9 @@ const useSingleRunFormParams = ({ } const getDependentVar = (variable: string) => { - if(variable === 'query') + if (variable === 'query') return payload.query_variable_selector - if(variable === '#files#') + if (variable === '#files#') return payload.vision.configs?.variable_selector return false diff --git a/web/app/components/workflow/nodes/start/components/var-item.tsx b/web/app/components/workflow/nodes/start/components/var-item.tsx index 6bce5d0f0f..317a733d9b 100644 --- a/web/app/components/workflow/nodes/start/components/var-item.tsx +++ b/web/app/components/workflow/nodes/start/components/var-item.tsx @@ -1,19 +1,19 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useRef } from 'react' -import { useBoolean, useHover } from 'ahooks' -import { useTranslation } from 'react-i18next' +import type { InputVar, MoreInfo } from '@/app/components/workflow/types' import { RiDeleteBinLine, } from '@remixicon/react' -import InputVarTypeIcon from '../../_base/components/input-var-type-icon' -import type { InputVar, MoreInfo } from '@/app/components/workflow/types' +import { useBoolean, useHover } from 'ahooks' +import { noop } from 'lodash-es' +import React, { useCallback, useRef } from 'react' +import { useTranslation } from 'react-i18next' +import ConfigVarModal from '@/app/components/app/configuration/config-var/config-modal' +import Badge from '@/app/components/base/badge' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { Edit03 } from '@/app/components/base/icons/src/vender/solid/general' -import Badge from '@/app/components/base/badge' -import ConfigVarModal from '@/app/components/app/configuration/config-var/config-modal' -import { noop } from 'lodash-es' import { cn } from '@/utils/classnames' +import InputVarTypeIcon from '../../_base/components/input-var-type-icon' type Props = { className?: string @@ -23,8 +23,8 @@ type Props = { onRemove?: () => void rightContent?: React.JSX.Element varKeys?: string[] - showLegacyBadge?: boolean, - canDrag?: boolean, + showLegacyBadge?: boolean + canDrag?: boolean } const VarItem: FC<Props> = ({ @@ -49,47 +49,52 @@ const VarItem: FC<Props> = ({ const handlePayloadChange = useCallback((payload: InputVar, moreInfo?: MoreInfo) => { const isValid = onChange(payload, moreInfo) - if(!isValid) + if (!isValid) return hideEditVarModal() }, [onChange, hideEditVarModal]) return ( <div ref={ref} className={cn('flex h-8 cursor-pointer items-center justify-between rounded-lg border border-components-panel-border-subtle bg-components-panel-on-panel-item-bg px-2.5 shadow-xs hover:shadow-md', className)}> - <div className='flex w-0 grow items-center space-x-1'> + <div className="flex w-0 grow items-center space-x-1"> <Variable02 className={cn('h-3.5 w-3.5 text-text-accent', canDrag && 'group-hover:opacity-0')} /> - <div title={payload.variable} className='max-w-[130px] shrink-0 truncate text-[13px] font-medium text-text-secondary'>{payload.variable}</div> - {payload.label && (<><div className='shrink-0 text-xs font-medium text-text-quaternary'>·</div> - <div title={payload.label as string} className='max-w-[130px] truncate text-[13px] font-medium text-text-tertiary'>{payload.label as string}</div> - </>)} + <div title={payload.variable} className="max-w-[130px] shrink-0 truncate text-[13px] font-medium text-text-secondary">{payload.variable}</div> + {payload.label && ( + <> + <div className="shrink-0 text-xs font-medium text-text-quaternary">·</div> + <div title={payload.label as string} className="max-w-[130px] truncate text-[13px] font-medium text-text-tertiary">{payload.label as string}</div> + </> + )} {showLegacyBadge && ( <Badge - text='LEGACY' - className='shrink-0 border-text-accent-secondary text-text-accent-secondary' + text="LEGACY" + className="shrink-0 border-text-accent-secondary text-text-accent-secondary" /> )} </div> - <div className='ml-2 flex shrink-0 items-center'> - {rightContent || (<> - {(!isHovering || readonly) - ? ( - <> - {payload.required && ( - <div className='mr-2 text-xs font-normal text-text-tertiary'>{t('workflow.nodes.start.required')}</div> - )} - <InputVarTypeIcon type={payload.type} className='h-3.5 w-3.5 text-text-tertiary' /> - </> - ) - : (!readonly && ( - <> - <div onClick={showEditVarModal} className='mr-1 cursor-pointer rounded-md p-1 hover:bg-state-base-hover'> - <Edit03 className='h-4 w-4 text-text-tertiary' /> - </div> - <div onClick={onRemove} className='group cursor-pointer rounded-md p-1 hover:bg-state-destructive-hover'> - <RiDeleteBinLine className='h-4 w-4 text-text-tertiary group-hover:text-text-destructive' /> - </div> - </> - ))} - </>)} + <div className="ml-2 flex shrink-0 items-center"> + {rightContent || ( + <> + {(!isHovering || readonly) + ? ( + <> + {payload.required && ( + <div className="mr-2 text-xs font-normal text-text-tertiary">{t('workflow.nodes.start.required')}</div> + )} + <InputVarTypeIcon type={payload.type} className="h-3.5 w-3.5 text-text-tertiary" /> + </> + ) + : (!readonly && ( + <> + <div onClick={showEditVarModal} className="mr-1 cursor-pointer rounded-md p-1 hover:bg-state-base-hover"> + <Edit03 className="h-4 w-4 text-text-tertiary" /> + </div> + <div onClick={onRemove} className="group cursor-pointer rounded-md p-1 hover:bg-state-destructive-hover"> + <RiDeleteBinLine className="h-4 w-4 text-text-tertiary group-hover:text-text-destructive" /> + </div> + </> + ))} + </> + )} </div> { diff --git a/web/app/components/workflow/nodes/start/components/var-list.tsx b/web/app/components/workflow/nodes/start/components/var-list.tsx index 4b5177bb3e..5ae45c7192 100644 --- a/web/app/components/workflow/nodes/start/components/var-list.tsx +++ b/web/app/components/workflow/nodes/start/components/var-list.tsx @@ -1,20 +1,21 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useMemo } from 'react' -import { produce } from 'immer' -import { useTranslation } from 'react-i18next' -import VarItem from './var-item' -import { ChangeType, type InputVar, type MoreInfo } from '@/app/components/workflow/types' -import { ReactSortable } from 'react-sortablejs' +import type { InputVar, MoreInfo } from '@/app/components/workflow/types' import { RiDraggable } from '@remixicon/react' +import { produce } from 'immer' +import React, { useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { ReactSortable } from 'react-sortablejs' +import Toast from '@/app/components/base/toast' +import { ChangeType } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' import { hasDuplicateStr } from '@/utils/var' -import Toast from '@/app/components/base/toast' +import VarItem from './var-item' type Props = { readonly: boolean list: InputVar[] - onChange: (list: InputVar[], moreInfo?: { index: number; payload: MoreInfo }) => void + onChange: (list: InputVar[], moreInfo?: { index: number, payload: MoreInfo }) => void } const VarList: FC<Props> = ({ @@ -80,7 +81,7 @@ const VarList: FC<Props> = ({ if (list.length === 0) { return ( - <div className='flex h-[42px] items-center justify-center rounded-md bg-components-panel-bg text-xs font-normal leading-[18px] text-text-tertiary'> + <div className="flex h-[42px] items-center justify-center rounded-md bg-components-panel-bg text-xs font-normal leading-[18px] text-text-tertiary"> {t('workflow.nodes.start.noVarTip')} </div> ) @@ -90,15 +91,15 @@ const VarList: FC<Props> = ({ return ( <ReactSortable - className='space-y-1' + className="space-y-1" list={listWithIds} setList={(list) => { onChange(list.map(item => item.variable)) }} - handle='.handle' - ghostClass='opacity-50' + handle=".handle" + ghostClass="opacity-50" animation={150} > {listWithIds.map((itemWithId, index) => ( - <div key={itemWithId.id} className='group relative'> + <div key={itemWithId.id} className="group relative"> <VarItem className={cn(canDrag && 'handle')} readonly={readonly} @@ -108,10 +109,13 @@ const VarList: FC<Props> = ({ varKeys={list.map(item => item.variable)} canDrag={canDrag} /> - {canDrag && <RiDraggable className={cn( - 'handle absolute left-3 top-2.5 hidden h-3 w-3 cursor-pointer text-text-tertiary', - 'group-hover:block', - )} />} + {canDrag && ( + <RiDraggable className={cn( + 'handle absolute left-3 top-2.5 hidden h-3 w-3 cursor-pointer text-text-tertiary', + 'group-hover:block', + )} + /> + )} </div> ))} </ReactSortable> diff --git a/web/app/components/workflow/nodes/start/default.ts b/web/app/components/workflow/nodes/start/default.ts index 60584b5144..aead27c139 100644 --- a/web/app/components/workflow/nodes/start/default.ts +++ b/web/app/components/workflow/nodes/start/default.ts @@ -1,7 +1,7 @@ import type { NodeDefault } from '../../types' import type { StartNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' const metaData = genNodeMetaData({ sort: 0.1, diff --git a/web/app/components/workflow/nodes/start/node.tsx b/web/app/components/workflow/nodes/start/node.tsx index 7c02858d1b..e8642ff616 100644 --- a/web/app/components/workflow/nodes/start/node.tsx +++ b/web/app/components/workflow/nodes/start/node.tsx @@ -1,10 +1,11 @@ import type { FC } from 'react' +import type { StartNodeType } from './types' +import type { NodeProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import InputVarTypeIcon from '../_base/components/input-var-type-icon' -import type { StartNodeType } from './types' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' -import type { NodeProps } from '@/app/components/workflow/types' +import InputVarTypeIcon from '../_base/components/input-var-type-icon' + const i18nPrefix = 'workflow.nodes.start' const Node: FC<NodeProps<StartNodeType>> = ({ @@ -17,18 +18,18 @@ const Node: FC<NodeProps<StartNodeType>> = ({ return null return ( - <div className='mb-1 px-3 py-1'> - <div className='space-y-0.5'> + <div className="mb-1 px-3 py-1"> + <div className="space-y-0.5"> {variables.map(variable => ( - <div key={variable.variable} className='flex h-6 items-center justify-between space-x-1 rounded-md bg-workflow-block-parma-bg px-1'> - <div className='flex w-0 grow items-center space-x-1'> - <Variable02 className='h-3.5 w-3.5 shrink-0 text-text-accent' /> - <span className='system-xs-regular w-0 grow truncate text-text-secondary'>{variable.variable}</span> + <div key={variable.variable} className="flex h-6 items-center justify-between space-x-1 rounded-md bg-workflow-block-parma-bg px-1"> + <div className="flex w-0 grow items-center space-x-1"> + <Variable02 className="h-3.5 w-3.5 shrink-0 text-text-accent" /> + <span className="system-xs-regular w-0 grow truncate text-text-secondary">{variable.variable}</span> </div> - <div className='ml-1 flex items-center space-x-1'> - {variable.required && <span className='system-2xs-regular-uppercase text-text-tertiary'>{t(`${i18nPrefix}.required`)}</span>} - <InputVarTypeIcon type={variable.type} className='h-3 w-3 text-text-tertiary' /> + <div className="ml-1 flex items-center space-x-1"> + {variable.required && <span className="system-2xs-regular-uppercase text-text-tertiary">{t(`${i18nPrefix}.required`)}</span>} + <InputVarTypeIcon type={variable.type} className="h-3 w-3 text-text-tertiary" /> </div> </div> ))} diff --git a/web/app/components/workflow/nodes/start/panel.tsx b/web/app/components/workflow/nodes/start/panel.tsx index a560bd2e63..5871ab1852 100644 --- a/web/app/components/workflow/nodes/start/panel.tsx +++ b/web/app/components/workflow/nodes/start/panel.tsx @@ -1,16 +1,16 @@ import type { FC } from 'react' +import type { StartNodeType } from './types' +import type { InputVar, NodePanelProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import RemoveEffectVarConfirm from '../_base/components/remove-effect-var-confirm' -import VarList from './components/var-list' -import VarItem from './components/var-item' -import useConfig from './use-config' -import type { StartNodeType } from './types' -import Split from '@/app/components/workflow/nodes/_base/components/split' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import AddButton from '@/app/components/base/button/add-button' import ConfigVarModal from '@/app/components/app/configuration/config-var/config-modal' -import type { InputVar, NodePanelProps } from '@/app/components/workflow/types' +import AddButton from '@/app/components/base/button/add-button' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import Split from '@/app/components/workflow/nodes/_base/components/split' +import RemoveEffectVarConfirm from '../_base/components/remove-effect-var-confirm' +import VarItem from './components/var-item' +import VarList from './components/var-list' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.start' @@ -35,13 +35,14 @@ const Panel: FC<NodePanelProps<StartNodeType>> = ({ const handleAddVarConfirm = (payload: InputVar) => { const isValid = handleAddVariable(payload) - if (!isValid) return + if (!isValid) + return hideAddVarModal() } return ( - <div className='mt-2'> - <div className='space-y-4 px-4 pb-2'> + <div className="mt-2"> + <div className="space-y-4 px-4 pb-2"> <Field title={t(`${i18nPrefix}.inputField`)} operations={ @@ -55,8 +56,8 @@ const Panel: FC<NodePanelProps<StartNodeType>> = ({ onChange={handleVarListChange} /> - <div className='mt-1 space-y-1'> - <Split className='my-2' /> + <div className="mt-1 space-y-1"> + <Split className="my-2" /> { isChatMode && ( <VarItem @@ -64,12 +65,13 @@ const Panel: FC<NodePanelProps<StartNodeType>> = ({ payload={{ variable: 'userinput.query', } as any} - rightContent={ - <div className='text-xs font-normal text-text-tertiary'> + rightContent={( + <div className="text-xs font-normal text-text-tertiary"> String </div> - } - />) + )} + /> + ) } <VarItem @@ -78,11 +80,11 @@ const Panel: FC<NodePanelProps<StartNodeType>> = ({ payload={{ variable: 'userinput.files', } as any} - rightContent={ - <div className='text-xs font-normal text-text-tertiary'> + rightContent={( + <div className="text-xs font-normal text-text-tertiary"> Array[File] </div> - } + )} /> </div> </> diff --git a/web/app/components/workflow/nodes/start/use-config.ts b/web/app/components/workflow/nodes/start/use-config.ts index 5e4d1b01c6..8eed650f98 100644 --- a/web/app/components/workflow/nodes/start/use-config.ts +++ b/web/app/components/workflow/nodes/start/use-config.ts @@ -1,19 +1,19 @@ -import { useCallback, useState } from 'react' -import { produce } from 'immer' -import { useBoolean } from 'ahooks' import type { StartNodeType } from './types' -import { ChangeType } from '@/app/components/workflow/types' import type { InputVar, MoreInfo, ValueSelector } from '@/app/components/workflow/types' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { useBoolean } from 'ahooks' +import { produce } from 'immer' +import { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Toast from '@/app/components/base/toast' import { useIsChatMode, useNodesReadOnly, useWorkflow, } from '@/app/components/workflow/hooks' -import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { ChangeType } from '@/app/components/workflow/types' import { hasDuplicateStr } from '@/utils/var' -import Toast from '@/app/components/base/toast' -import { useTranslation } from 'react-i18next' +import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud' const useConfig = (id: string, payload: StartNodeType) => { const { t } = useTranslation() @@ -41,12 +41,12 @@ const useConfig = (id: string, payload: StartNodeType) => { }] = useBoolean(false) const [removedVar, setRemovedVar] = useState<ValueSelector>([]) const [removedIndex, setRemoveIndex] = useState(0) - const handleVarListChange = useCallback((newList: InputVar[], moreInfo?: { index: number; payload: MoreInfo }) => { + const handleVarListChange = useCallback((newList: InputVar[], moreInfo?: { index: number, payload: MoreInfo }) => { if (moreInfo?.payload?.type === ChangeType.remove) { const varId = nodesWithInspectVars.find(node => node.nodeId === id)?.vars.find((varItem) => { return varItem.name === moreInfo?.payload?.payload?.beforeKey })?.id - if(varId) + if (varId) deleteInspectVar(id, varId) if (isVarUsedInNodes([id, moreInfo?.payload?.payload?.beforeKey || ''])) { @@ -66,7 +66,7 @@ const useConfig = (id: string, payload: StartNodeType) => { handleOutVarRenameChange(id, [id, inputs.variables[moreInfo.index].variable], [id, changedVar.variable]) renameInspectVarName(id, inputs.variables[moreInfo.index].variable, changedVar.variable) } - else if(moreInfo?.payload?.type !== ChangeType.remove) { // edit var type + else if (moreInfo?.payload?.type !== ChangeType.remove) { // edit var type deleteNodeInspectorVars(id) } }, [deleteInspectVar, deleteNodeInspectorVars, handleOutVarRenameChange, id, inputs, isVarUsedInNodes, nodesWithInspectVars, renameInspectVarName, setInputs, showRemoveVarConfirm]) @@ -87,11 +87,11 @@ const useConfig = (id: string, payload: StartNodeType) => { const newList = newInputs.variables let errorMsgKey = '' let typeName = '' - if(hasDuplicateStr(newList.map(item => item.variable))) { + if (hasDuplicateStr(newList.map(item => item.variable))) { errorMsgKey = 'appDebug.varKeyError.keyAlreadyExists' typeName = 'appDebug.variableConfig.varName' } - else if(hasDuplicateStr(newList.map(item => item.label as string))) { + else if (hasDuplicateStr(newList.map(item => item.label as string))) { errorMsgKey = 'appDebug.varKeyError.keyAlreadyExists' typeName = 'appDebug.variableConfig.labelName' } diff --git a/web/app/components/workflow/nodes/start/use-single-run-form-params.ts b/web/app/components/workflow/nodes/start/use-single-run-form-params.ts index ed2b3900d2..a2d381ffbc 100644 --- a/web/app/components/workflow/nodes/start/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/start/use-single-run-form-params.ts @@ -1,14 +1,14 @@ import type { RefObject } from 'react' -import { useTranslation } from 'react-i18next' -import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form' -import type { ValueSelector } from '@/app/components/workflow/types' -import { type InputVar, InputVarType, type Variable } from '@/app/components/workflow/types' import type { StartNodeType } from './types' +import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form' +import type { InputVar, ValueSelector, Variable } from '@/app/components/workflow/types' +import { useTranslation } from 'react-i18next' +import { InputVarType } from '@/app/components/workflow/types' import { useIsChatMode } from '../../hooks' type Params = { - id: string, - payload: StartNodeType, + id: string + payload: StartNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] diff --git a/web/app/components/workflow/nodes/template-transform/default.ts b/web/app/components/workflow/nodes/template-transform/default.ts index 1a3c092c0b..90e0f60184 100644 --- a/web/app/components/workflow/nodes/template-transform/default.ts +++ b/web/app/components/workflow/nodes/template-transform/default.ts @@ -1,8 +1,9 @@ import type { NodeDefault } from '../../types' import type { TemplateTransformNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' + const i18nPrefix = 'workflow.errorMsg' const metaData = genNodeMetaData({ diff --git a/web/app/components/workflow/nodes/template-transform/node.tsx b/web/app/components/workflow/nodes/template-transform/node.tsx index e6925c488f..3a4c5c3319 100644 --- a/web/app/components/workflow/nodes/template-transform/node.tsx +++ b/web/app/components/workflow/nodes/template-transform/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' -import React from 'react' import type { TemplateTransformNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' +import React from 'react' const Node: FC<NodeProps<TemplateTransformNodeType>> = () => { return ( diff --git a/web/app/components/workflow/nodes/template-transform/panel.tsx b/web/app/components/workflow/nodes/template-transform/panel.tsx index 29c34ee663..c8fc293329 100644 --- a/web/app/components/workflow/nodes/template-transform/panel.tsx +++ b/web/app/components/workflow/nodes/template-transform/panel.tsx @@ -1,19 +1,19 @@ import type { FC } from 'react' -import React from 'react' -import { useTranslation } from 'react-i18next' +import type { TemplateTransformNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import { RiQuestionLine, } from '@remixicon/react' +import React from 'react' +import { useTranslation } from 'react-i18next' +import AddButton from '@/app/components/base/button/add-button' +import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' +import Split from '@/app/components/workflow/nodes/_base/components/split' +import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list' import { CodeLanguage } from '../code/types' import useConfig from './use-config' -import type { TemplateTransformNodeType } from './types' -import VarList from '@/app/components/workflow/nodes/_base/components/variable/var-list' -import AddButton from '@/app/components/base/button/add-button' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import Split from '@/app/components/workflow/nodes/_base/components/split' -import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars' -import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' -import type { NodePanelProps } from '@/app/components/workflow/types' const i18nPrefix = 'workflow.nodes.templateTransform' @@ -36,8 +36,8 @@ const Panel: FC<NodePanelProps<TemplateTransformNodeType>> = ({ } = useConfig(id, data) return ( - <div className='mt-2'> - <div className='space-y-4 px-4 pb-4'> + <div className="mt-2"> + <div className="space-y-4 px-4 pb-4"> <Field title={t(`${i18nPrefix}.inputVars`)} @@ -64,20 +64,21 @@ const Panel: FC<NodePanelProps<TemplateTransformNodeType>> = ({ readOnly={readOnly} language={CodeLanguage.python3} title={ - <div className='uppercase'>{t(`${i18nPrefix}.code`)}</div> + <div className="uppercase">{t(`${i18nPrefix}.code`)}</div> } - headerRight={ - <div className='flex items-center'> + headerRight={( + <div className="flex items-center"> <a - className='flex h-[18px] items-center space-x-0.5 text-xs font-normal text-text-tertiary' + className="flex h-[18px] items-center space-x-0.5 text-xs font-normal text-text-tertiary" href="https://jinja.palletsprojects.com/en/3.1.x/templates/" - target='_blank'> + target="_blank" + > <span>{t(`${i18nPrefix}.codeSupportTip`)}</span> - <RiQuestionLine className='h-3 w-3' /> + <RiQuestionLine className="h-3 w-3" /> </a> - <div className='mx-1.5 h-3 w-px bg-divider-regular'></div> + <div className="mx-1.5 h-3 w-px bg-divider-regular"></div> </div> - } + )} value={inputs.template} onChange={handleCodeChange} /> @@ -87,8 +88,8 @@ const Panel: FC<NodePanelProps<TemplateTransformNodeType>> = ({ <OutputVars> <> <VarItem - name='output' - type='string' + name="output" + type="string" description={t(`${i18nPrefix}.outputVars.output`)} /> </> diff --git a/web/app/components/workflow/nodes/template-transform/use-config.ts b/web/app/components/workflow/nodes/template-transform/use-config.ts index 3e5b0f3d31..ed62ea2fa0 100644 --- a/web/app/components/workflow/nodes/template-transform/use-config.ts +++ b/web/app/components/workflow/nodes/template-transform/use-config.ts @@ -1,15 +1,15 @@ -import { useCallback, useEffect, useRef } from 'react' -import { produce } from 'immer' -import useVarList from '../_base/hooks/use-var-list' import type { Var, Variable } from '../../types' -import { VarType } from '../../types' -import { useStore } from '../../store' import type { TemplateTransformNodeType } from './types' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { produce } from 'immer' +import { useCallback, useEffect, useRef } from 'react' import { useNodesReadOnly, } from '@/app/components/workflow/hooks' import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { useStore } from '../../store' +import { VarType } from '../../types' +import useVarList from '../_base/hooks/use-var-list' const useConfig = (id: string, payload: TemplateTransformNodeType) => { const { nodesReadOnly: readOnly } = useNodesReadOnly() diff --git a/web/app/components/workflow/nodes/template-transform/use-single-run-form-params.ts b/web/app/components/workflow/nodes/template-transform/use-single-run-form-params.ts index 172ece6ce6..d8dcfaea92 100644 --- a/web/app/components/workflow/nodes/template-transform/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/template-transform/use-single-run-form-params.ts @@ -1,12 +1,12 @@ import type { RefObject } from 'react' +import type { TemplateTransformNodeType } from './types' import type { InputVar, Variable } from '@/app/components/workflow/types' import { useCallback, useMemo } from 'react' import useNodeCrud from '../_base/hooks/use-node-crud' -import type { TemplateTransformNodeType } from './types' type Params = { - id: string, - payload: TemplateTransformNodeType, + id: string + payload: TemplateTransformNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] diff --git a/web/app/components/workflow/nodes/tool/components/copy-id.tsx b/web/app/components/workflow/nodes/tool/components/copy-id.tsx index 977db5f234..39ffd7edd1 100644 --- a/web/app/components/workflow/nodes/tool/components/copy-id.tsx +++ b/web/app/components/workflow/nodes/tool/components/copy-id.tsx @@ -1,9 +1,9 @@ 'use client' -import React, { useState } from 'react' -import { useTranslation } from 'react-i18next' import { RiFileCopyLine } from '@remixicon/react' import copy from 'copy-to-clipboard' import { debounce } from 'lodash-es' +import React, { useState } from 'react' +import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' type Props = { @@ -26,7 +26,7 @@ const CopyFeedbackNew = ({ content }: Props) => { }, 100) return ( - <div className='inline-flex w-full pb-0.5' onClick={e => e.stopPropagation()} onMouseLeave={onMouseLeave}> + <div className="inline-flex w-full pb-0.5" onClick={e => e.stopPropagation()} onMouseLeave={onMouseLeave}> <Tooltip popupContent={ (isCopied @@ -35,13 +35,15 @@ const CopyFeedbackNew = ({ content }: Props) => { } > <div - className='group/copy flex w-full items-center gap-0.5 ' + className="group/copy flex w-full items-center gap-0.5 " onClick={onClickCopy} > <div - className='system-2xs-regular w-0 grow cursor-pointer truncate text-text-quaternary group-hover:text-text-tertiary' - >{content}</div> - <RiFileCopyLine className='h-3 w-3 shrink-0 text-text-tertiary opacity-0 group-hover/copy:opacity-100' /> + className="system-2xs-regular w-0 grow cursor-pointer truncate text-text-quaternary group-hover:text-text-tertiary" + > + {content} + </div> + <RiFileCopyLine className="h-3 w-3 shrink-0 text-text-tertiary opacity-0 group-hover/copy:opacity-100" /> </div> </Tooltip> </div> diff --git a/web/app/components/workflow/nodes/tool/components/input-var-list.tsx b/web/app/components/workflow/nodes/tool/components/input-var-list.tsx index 605cbab75e..4f5d5f24bc 100644 --- a/web/app/components/workflow/nodes/tool/components/input-var-list.tsx +++ b/web/app/components/workflow/nodes/tool/components/input-var-list.tsx @@ -1,23 +1,23 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useState } from 'react' -import { produce } from 'immer' -import { useTranslation } from 'react-i18next' import type { ToolVarInputs } from '../types' -import { VarType as VarKindType } from '../types' -import { cn } from '@/utils/classnames' -import type { ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types' import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { Tool } from '@/app/components/tools/types' +import type { ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types' +import { produce } from 'immer' +import { noop } from 'lodash-es' +import React, { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' -import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' -import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' -import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' -import { VarType } from '@/app/components/workflow/types' import AppSelector from '@/app/components/plugins/plugin-detail-panel/app-selector' import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector' -import { noop } from 'lodash-es' -import type { Tool } from '@/app/components/tools/types' +import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' +import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' +import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' +import { VarType } from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' +import { VarType as VarKindType } from '../types' type Props = { readOnly: boolean @@ -158,7 +158,7 @@ const InputVarList: FC<Props> = ({ }, [onOpen]) return ( - <div className='space-y-3'> + <div className="space-y-3"> { schema.map((schema, index) => { const { @@ -180,11 +180,11 @@ const InputVarList: FC<Props> = ({ const isString = !isNumber && !isSelect && !isFile && !isAppSelector && !isModelSelector return ( - <div key={variable} className='space-y-1'> - <div className='flex items-center space-x-2 leading-[18px]'> - <span className='code-sm-semibold text-text-secondary'>{label[language] || label.en_US}</span> - <span className='system-xs-regular text-text-tertiary'>{paramType(type)}</span> - {required && <span className='system-xs-regular text-util-colors-orange-dark-orange-dark-600'>Required</span>} + <div key={variable} className="space-y-1"> + <div className="flex items-center space-x-2 leading-[18px]"> + <span className="code-sm-semibold text-text-secondary">{label[language] || label.en_US}</span> + <span className="system-xs-regular text-text-tertiary">{paramType(type)}</span> + {required && <span className="system-xs-regular text-util-colors-orange-dark-orange-dark-600">Required</span>} </div> {isString && ( <Input @@ -196,7 +196,7 @@ const InputVarList: FC<Props> = ({ availableNodes={availableNodesWithParent} onFocusChange={handleInputFocus(variable)} placeholder={t('workflow.nodes.http.insertVarPlaceholder')!} - placeholderClassName='!leading-[21px]' + placeholderClassName="!leading-[21px]" /> )} {(isNumber || isSelect) && ( @@ -238,7 +238,7 @@ const InputVarList: FC<Props> = ({ )} {isModelSelector && ( <ModelParameterModal - popupClassName='!w-[387px]' + popupClassName="!w-[387px]" isAdvancedMode isInWorkflow value={varInput as any} @@ -247,7 +247,7 @@ const InputVarList: FC<Props> = ({ scope={scope} /> )} - {tooltip && <div className='body-xs-regular text-text-tertiary'>{tooltip[language] || tooltip.en_US}</div>} + {tooltip && <div className="body-xs-regular text-text-tertiary">{tooltip[language] || tooltip.en_US}</div>} </div> ) }) diff --git a/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/index.tsx b/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/index.tsx index 98a88e5e0f..e0191ce2d8 100644 --- a/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/index.tsx +++ b/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/index.tsx @@ -1,16 +1,16 @@ +import type { + Node, + NodeOutPutVar, +} from '@/app/components/workflow/types' import { memo, } from 'react' import { useTranslation } from 'react-i18next' import PromptEditor from '@/app/components/base/prompt-editor' -import Placeholder from './placeholder' -import type { - Node, - NodeOutPutVar, -} from '@/app/components/workflow/types' +import { useStore } from '@/app/components/workflow/store' import { BlockEnum } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' -import { useStore } from '@/app/components/workflow/store' +import Placeholder from './placeholder' type MixedVariableTextInputProps = { readOnly?: boolean @@ -43,7 +43,7 @@ const MixedVariableTextInput = ({ 'hover:border-components-input-border-hover hover:bg-components-input-bg-hover', 'focus-within:border-components-input-border-active focus-within:bg-components-input-bg-active focus-within:shadow-xs', )} - className='caret:text-text-accent' + className="caret:text-text-accent" editable={!readOnly} value={value} workflowVariableBlock={{ diff --git a/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/placeholder.tsx b/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/placeholder.tsx index d6e0bbc059..5c599dd059 100644 --- a/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/placeholder.tsx +++ b/web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/placeholder.tsx @@ -1,10 +1,9 @@ +import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' +import { $insertNodes, FOCUS_COMMAND } from 'lexical' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' -import { FOCUS_COMMAND } from 'lexical' -import { $insertNodes } from 'lexical' -import { CustomTextNode } from '@/app/components/base/prompt-editor/plugins/custom-text/node' import Badge from '@/app/components/base/badge' +import { CustomTextNode } from '@/app/components/base/prompt-editor/plugins/custom-text/node' type PlaceholderProps = { disableVariableInsertion?: boolean @@ -24,19 +23,19 @@ const Placeholder = ({ disableVariableInsertion = false }: PlaceholderProps) => return ( <div - className='pointer-events-auto flex h-full w-full cursor-text items-center px-2' + className="pointer-events-auto flex h-full w-full cursor-text items-center px-2" onClick={(e) => { e.stopPropagation() handleInsert('') }} > - <div className='flex grow items-center'> + <div className="flex grow items-center"> {t('workflow.nodes.tool.insertPlaceholder1')} {(!disableVariableInsertion) && ( <> - <div className='system-kbd mx-0.5 flex h-4 w-4 items-center justify-center rounded bg-components-kbd-bg-gray text-text-placeholder'>/</div> + <div className="system-kbd mx-0.5 flex h-4 w-4 items-center justify-center rounded bg-components-kbd-bg-gray text-text-placeholder">/</div> <div - className='system-sm-regular cursor-pointer text-components-input-text-placeholder underline decoration-dotted decoration-auto underline-offset-auto hover:text-text-tertiary' + className="system-sm-regular cursor-pointer text-components-input-text-placeholder underline decoration-dotted decoration-auto underline-offset-auto hover:text-text-tertiary" onMouseDown={((e) => { e.preventDefault() e.stopPropagation() @@ -49,8 +48,8 @@ const Placeholder = ({ disableVariableInsertion = false }: PlaceholderProps) => )} </div> <Badge - className='shrink-0' - text='String' + className="shrink-0" + text="String" uppercase={false} /> </div> diff --git a/web/app/components/workflow/nodes/tool/components/tool-form/index.tsx b/web/app/components/workflow/nodes/tool/components/tool-form/index.tsx index ade29beddb..c8d60be789 100644 --- a/web/app/components/workflow/nodes/tool/components/tool-form/index.tsx +++ b/web/app/components/workflow/nodes/tool/components/tool-form/index.tsx @@ -2,9 +2,9 @@ import type { FC } from 'react' import type { ToolVarInputs } from '../../types' import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations' -import ToolFormItem from './item' -import type { ToolWithProvider } from '@/app/components/workflow/types' import type { Tool } from '@/app/components/tools/types' +import type { ToolWithProvider } from '@/app/components/workflow/types' +import ToolFormItem from './item' type Props = { readOnly: boolean @@ -35,7 +35,7 @@ const ToolForm: FC<Props> = ({ extraParams, }) => { return ( - <div className='space-y-1'> + <div className="space-y-1"> { schema.map((schema, index) => ( <ToolFormItem @@ -51,7 +51,7 @@ const ToolForm: FC<Props> = ({ showManageInputField={showManageInputField} onManageInputField={onManageInputField} extraParams={extraParams} - providerType='tool' + providerType="tool" /> )) } diff --git a/web/app/components/workflow/nodes/tool/components/tool-form/item.tsx b/web/app/components/workflow/nodes/tool/components/tool-form/item.tsx index 567266abde..83d4ee9eef 100644 --- a/web/app/components/workflow/nodes/tool/components/tool-form/item.tsx +++ b/web/app/components/workflow/nodes/tool/components/tool-form/item.tsx @@ -1,19 +1,19 @@ 'use client' import type { FC } from 'react' +import type { ToolVarInputs } from '../../types' +import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { Tool } from '@/app/components/tools/types' +import type { ToolWithProvider } from '@/app/components/workflow/types' import { RiBracesLine, } from '@remixicon/react' -import type { ToolVarInputs } from '../../types' -import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' +import { useBoolean } from 'ahooks' import Button from '@/app/components/base/button' import Tooltip from '@/app/components/base/tooltip' -import FormInputItem from '@/app/components/workflow/nodes/_base/components/form-input-item' -import { useBoolean } from 'ahooks' +import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import SchemaModal from '@/app/components/plugins/plugin-detail-panel/tool-selector/schema-modal' -import type { ToolWithProvider } from '@/app/components/workflow/types' -import type { Tool } from '@/app/components/tools/types' +import FormInputItem from '@/app/components/workflow/nodes/_base/components/form-input-item' type Props = { readOnly: boolean @@ -53,39 +53,41 @@ const ToolFormItem: FC<Props> = ({ setFalse: hideSchema, }] = useBoolean(false) return ( - <div className='space-y-0.5 py-1'> + <div className="space-y-0.5 py-1"> <div> - <div className='flex h-6 items-center'> - <div className='system-sm-medium text-text-secondary'>{label[language] || label.en_US}</div> + <div className="flex h-6 items-center"> + <div className="system-sm-medium text-text-secondary">{label[language] || label.en_US}</div> {required && ( - <div className='system-xs-regular ml-1 text-text-destructive-secondary'>*</div> + <div className="system-xs-regular ml-1 text-text-destructive-secondary">*</div> )} {!showDescription && tooltip && ( <Tooltip - popupContent={<div className='w-[200px]'> - {tooltip[language] || tooltip.en_US} - </div>} - triggerClassName='ml-1 w-4 h-4' + popupContent={( + <div className="w-[200px]"> + {tooltip[language] || tooltip.en_US} + </div> + )} + triggerClassName="ml-1 w-4 h-4" asChild={false} /> )} {showSchemaButton && ( <> - <div className='system-xs-regular ml-1 mr-0.5 text-text-quaternary'>·</div> + <div className="system-xs-regular ml-1 mr-0.5 text-text-quaternary">·</div> <Button - variant='ghost' - size='small' + variant="ghost" + size="small" onClick={showSchema} - className='system-xs-regular px-1 text-text-tertiary' + className="system-xs-regular px-1 text-text-tertiary" > - <RiBracesLine className='mr-1 size-3.5' /> + <RiBracesLine className="mr-1 size-3.5" /> <span>JSON Schema</span> </Button> </> )} </div> {showDescription && tooltip && ( - <div className='body-xs-regular pb-0.5 text-text-tertiary'>{tooltip[language] || tooltip.en_US}</div> + <div className="body-xs-regular pb-0.5 text-text-tertiary">{tooltip[language] || tooltip.en_US}</div> )} </div> <FormInputItem diff --git a/web/app/components/workflow/nodes/tool/default.ts b/web/app/components/workflow/nodes/tool/default.ts index 0fcd321a29..4d8046ef1c 100644 --- a/web/app/components/workflow/nodes/tool/default.ts +++ b/web/app/components/workflow/nodes/tool/default.ts @@ -1,11 +1,11 @@ -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' import type { NodeDefault, ToolWithProvider, Var } from '../../types' import type { ToolNodeType } from './types' -import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' -import { TOOL_OUTPUT_STRUCT } from '../../constants' import { CollectionType } from '@/app/components/tools/types' +import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' import { canFindTool } from '@/utils' +import { TOOL_OUTPUT_STRUCT } from '../../constants' import { Type } from '../llm/types' import { resolveVarType } from './output-schema-utils' @@ -104,13 +104,15 @@ const nodeDefault: NodeDefault<ToolNodeType> = { type, des: output.description, schemaType, - children: output.type === 'object' ? { - schema: { - type: Type.object, - properties: output.properties, - additionalProperties: false, - }, - } : undefined, + children: output.type === 'object' + ? { + schema: { + type: Type.object, + properties: output.properties, + additionalProperties: false, + }, + } + : undefined, }) }) res = [ diff --git a/web/app/components/workflow/nodes/tool/node.tsx b/web/app/components/workflow/nodes/tool/node.tsx index 6aa483e8b0..e2bcd26bd2 100644 --- a/web/app/components/workflow/nodes/tool/node.tsx +++ b/web/app/components/workflow/nodes/tool/node.tsx @@ -1,11 +1,11 @@ import type { FC } from 'react' -import React, { useEffect } from 'react' -import type { NodeProps } from '@/app/components/workflow/types' -import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button' -import { useNodePluginInstallation } from '@/app/components/workflow/hooks/use-node-plugin-installation' -import { useNodeDataUpdate } from '@/app/components/workflow/hooks/use-node-data-update' import type { ToolNodeType } from './types' +import type { NodeProps } from '@/app/components/workflow/types' +import React, { useEffect } from 'react' +import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { useNodeDataUpdate } from '@/app/components/workflow/hooks/use-node-data-update' +import { useNodePluginInstallation } from '@/app/components/workflow/hooks/use-node-plugin-installation' +import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button' const Node: FC<NodeProps<ToolNodeType>> = ({ id, @@ -43,12 +43,12 @@ const Node: FC<NodeProps<ToolNodeType>> = ({ return null return ( - <div className='relative mb-1 px-3 py-1'> + <div className="relative mb-1 px-3 py-1"> {showInstallButton && ( - <div className='pointer-events-auto absolute right-3 top-[-32px] z-40'> + <div className="pointer-events-auto absolute right-3 top-[-32px] z-40"> <InstallPluginButton - size='small' - className='!font-medium !text-text-accent' + size="small" + className="!font-medium !text-text-accent" extraIdentifiers={[ data.plugin_id, data.provider_id, @@ -60,24 +60,24 @@ const Node: FC<NodeProps<ToolNodeType>> = ({ </div> )} {hasConfigs && ( - <div className='space-y-0.5' aria-disabled={shouldDim}> + <div className="space-y-0.5" aria-disabled={shouldDim}> {toolConfigs.map((key, index) => ( - <div key={index} className='flex h-6 items-center justify-between space-x-1 rounded-md bg-workflow-block-parma-bg px-1 text-xs font-normal text-text-secondary'> - <div title={key} className='max-w-[100px] shrink-0 truncate text-xs font-medium uppercase text-text-tertiary'> + <div key={index} className="flex h-6 items-center justify-between space-x-1 rounded-md bg-workflow-block-parma-bg px-1 text-xs font-normal text-text-secondary"> + <div title={key} className="max-w-[100px] shrink-0 truncate text-xs font-medium uppercase text-text-tertiary"> {key} </div> {typeof tool_configurations[key].value === 'string' && ( - <div title={tool_configurations[key].value} className='w-0 shrink-0 grow truncate text-right text-xs font-normal text-text-secondary'> + <div title={tool_configurations[key].value} className="w-0 shrink-0 grow truncate text-right text-xs font-normal text-text-secondary"> {paramSchemas?.find(i => i.name === key)?.type === FormTypeEnum.secretInput ? '********' : tool_configurations[key].value} </div> )} {typeof tool_configurations[key].value === 'number' && ( - <div title={Number.isNaN(tool_configurations[key].value) ? '' : tool_configurations[key].value} className='w-0 shrink-0 grow truncate text-right text-xs font-normal text-text-secondary'> + <div title={Number.isNaN(tool_configurations[key].value) ? '' : tool_configurations[key].value} className="w-0 shrink-0 grow truncate text-right text-xs font-normal text-text-secondary"> {Number.isNaN(tool_configurations[key].value) ? '' : tool_configurations[key].value} </div> )} {typeof tool_configurations[key] !== 'string' && tool_configurations[key]?.type === FormTypeEnum.modelSelector && ( - <div title={tool_configurations[key].model} className='w-0 shrink-0 grow truncate text-right text-xs font-normal text-text-secondary'> + <div title={tool_configurations[key].model} className="w-0 shrink-0 grow truncate text-right text-xs font-normal text-text-secondary"> {tool_configurations[key].model} </div> )} diff --git a/web/app/components/workflow/nodes/tool/output-schema-utils.ts b/web/app/components/workflow/nodes/tool/output-schema-utils.ts index 684ff0b29f..141c679da0 100644 --- a/web/app/components/workflow/nodes/tool/output-schema-utils.ts +++ b/web/app/components/workflow/nodes/tool/output-schema-utils.ts @@ -1,13 +1,14 @@ +import type { SchemaTypeDefinition } from '@/service/use-common' import { VarType } from '@/app/components/workflow/types' import { getMatchedSchemaType } from '../_base/components/variable/use-match-schema-type' -import type { SchemaTypeDefinition } from '@/service/use-common' /** * Normalizes a JSON Schema type to a simple string type. * Handles complex schemas with oneOf, anyOf, allOf. */ export const normalizeJsonSchemaType = (schema: any): string | undefined => { - if (!schema) return undefined + if (!schema) + return undefined const { type, properties, items, oneOf, anyOf, allOf } = schema if (Array.isArray(type)) @@ -51,7 +52,7 @@ export const pickItemSchema = (schema: any) => { export const resolveVarType = ( schema: any, schemaTypeDefinitions?: SchemaTypeDefinition[], -): { type: VarType; schemaType?: string } => { +): { type: VarType, schemaType?: string } => { const schemaType = getMatchedSchemaType(schema, schemaTypeDefinitions) const normalizedType = normalizeJsonSchemaType(schema) diff --git a/web/app/components/workflow/nodes/tool/panel.tsx b/web/app/components/workflow/nodes/tool/panel.tsx index 3a623208e5..3e1b778a7a 100644 --- a/web/app/components/workflow/nodes/tool/panel.tsx +++ b/web/app/components/workflow/nodes/tool/panel.tsx @@ -1,18 +1,18 @@ import type { FC } from 'react' +import type { ToolNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import Split from '../_base/components/split' -import type { ToolNodeType } from './types' -import useConfig from './use-config' -import ToolForm from './components/tool-form' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import type { NodePanelProps } from '@/app/components/workflow/types' import Loading from '@/app/components/base/loading' +import Field from '@/app/components/workflow/nodes/_base/components/field' import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' import StructureOutputItem from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show' import { useStore } from '@/app/components/workflow/store' import { wrapStructuredVarItem } from '@/app/components/workflow/utils/tool' +import Split from '../_base/components/split' import useMatchSchemaType, { getMatchedSchemaType } from '../_base/components/variable/use-match-schema-type' +import ToolForm from './components/tool-form' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.tool' @@ -44,19 +44,19 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({ if (isLoading) { return ( - <div className='flex h-[200px] items-center justify-center'> + <div className="flex h-[200px] items-center justify-center"> <Loading /> </div> ) } return ( - <div className='pt-2'> + <div className="pt-2"> {!isShowAuthBtn && ( - <div className='relative'> + <div className="relative"> {toolInputVarSchema.length > 0 && ( <Field - className='px-4' + className="px-4" title={t(`${i18nPrefix}.inputVars`)} > <ToolForm @@ -74,7 +74,7 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({ )} {toolInputVarSchema.length > 0 && toolSettingSchema.length > 0 && ( - <Split className='mt-1' /> + <Split className="mt-1" /> )} {toolSettingSchema.length > 0 && ( @@ -102,20 +102,20 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({ <OutputVars> <> <VarItem - name='text' - type='string' + name="text" + type="string" description={t(`${i18nPrefix}.outputVars.text`)} isIndent={hasObjectOutput} /> <VarItem - name='files' - type='array[file]' + name="files" + type="array[file]" description={t(`${i18nPrefix}.outputVars.files.title`)} isIndent={hasObjectOutput} /> <VarItem - name='json' - type='array[object]' + name="json" + type="array[object]" description={t(`${i18nPrefix}.outputVars.json`)} isIndent={hasObjectOutput} /> @@ -126,7 +126,7 @@ const Panel: FC<NodePanelProps<ToolNodeType>> = ({ <div key={outputItem.name}> {outputItem.value?.type === 'object' ? ( <StructureOutputItem - rootClassName='code-sm-semibold text-text-secondary' + rootClassName="code-sm-semibold text-text-secondary" payload={wrapStructuredVarItem(outputItem, schemaType)} /> ) : ( diff --git a/web/app/components/workflow/nodes/tool/types.ts b/web/app/components/workflow/nodes/tool/types.ts index da3b7f7b31..9a58e8a299 100644 --- a/web/app/components/workflow/nodes/tool/types.ts +++ b/web/app/components/workflow/nodes/tool/types.ts @@ -1,6 +1,6 @@ +import type { ResourceVarInputs } from '../_base/types' import type { Collection, CollectionType } from '@/app/components/tools/types' import type { CommonNodeType } from '@/app/components/workflow/types' -import type { ResourceVarInputs } from '../_base/types' // Use base types directly export { VarKindType as VarType } from '../_base/types' diff --git a/web/app/components/workflow/nodes/tool/use-config.ts b/web/app/components/workflow/nodes/tool/use-config.ts index fe3fe543e9..6e1924dd43 100644 --- a/web/app/components/workflow/nodes/tool/use-config.ts +++ b/web/app/components/workflow/nodes/tool/use-config.ts @@ -1,23 +1,21 @@ +import type { ToolNodeType, ToolVarInputs } from './types' +import type { InputVar } from '@/app/components/workflow/types' +import { useBoolean } from 'ahooks' +import { produce } from 'immer' import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { produce } from 'immer' -import { useBoolean } from 'ahooks' -import { useWorkflowStore } from '../../store' -import type { ToolNodeType, ToolVarInputs } from './types' +import Toast from '@/app/components/base/toast' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import { CollectionType } from '@/app/components/tools/types' -import { updateBuiltInToolCredential } from '@/service/tools' import { getConfiguredValue, toolParametersToFormSchemas, } from '@/app/components/tools/utils/to-form-schema' -import Toast from '@/app/components/base/toast' -import type { InputVar } from '@/app/components/workflow/types' import { useNodesReadOnly, } from '@/app/components/workflow/hooks' -import { canFindTool } from '@/utils' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { updateBuiltInToolCredential } from '@/service/tools' import { useAllBuiltInTools, useAllCustomTools, @@ -25,6 +23,8 @@ import { useAllWorkflowTools, useInvalidToolsByType, } from '@/service/use-tools' +import { canFindTool } from '@/utils' +import { useWorkflowStore } from '../../store' const useConfig = (id: string, payload: ToolNodeType) => { const workflowStore = useWorkflowStore() @@ -133,7 +133,8 @@ const useConfig = (id: string, payload: ToolNodeType) => { if (typeof value === 'string') newConfig[key] = value === 'true' || value === '1' - if (typeof value === 'number') newConfig[key] = value === 1 + if (typeof value === 'number') + newConfig[key] = value === 1 } if (schema?.type === 'number-input') { @@ -149,7 +150,8 @@ const useConfig = (id: string, payload: ToolNodeType) => { ) const [notSetDefaultValue, setNotSetDefaultValue] = useState(false) const toolSettingValue = useMemo(() => { - if (notSetDefaultValue) return tool_configurations + if (notSetDefaultValue) + return tool_configurations return getConfiguredValue(tool_configurations, toolSettingSchema) }, [notSetDefaultValue, toolSettingSchema, tool_configurations]) const setToolSettingValue = useCallback( @@ -188,7 +190,8 @@ const useConfig = (id: string, payload: ToolNodeType) => { } useEffect(() => { - if (!currTool) return + if (!currTool) + return const inputsWithDefaultValue = formattingParameters() const { setControlPromptEditorRerenderKey } = workflowStore.getState() setInputs(inputsWithDefaultValue) @@ -231,7 +234,8 @@ const useConfig = (id: string, payload: ToolNodeType) => { const outputSchema = useMemo(() => { const res: any[] = [] const output_schema = currTool?.output_schema - if (!output_schema || !output_schema.properties) return res + if (!output_schema || !output_schema.properties) + return res Object.keys(output_schema.properties).forEach((outputKey) => { const output = output_schema.properties[outputKey] @@ -266,7 +270,8 @@ const useConfig = (id: string, payload: ToolNodeType) => { const hasObjectOutput = useMemo(() => { const output_schema = currTool?.output_schema - if (!output_schema || !output_schema.properties) return false + if (!output_schema || !output_schema.properties) + return false const properties = output_schema.properties return Object.keys(properties).some( key => properties[key].type === 'object', diff --git a/web/app/components/workflow/nodes/tool/use-get-data-for-check-more.ts b/web/app/components/workflow/nodes/tool/use-get-data-for-check-more.ts index a68f12fc37..78ebab7bff 100644 --- a/web/app/components/workflow/nodes/tool/use-get-data-for-check-more.ts +++ b/web/app/components/workflow/nodes/tool/use-get-data-for-check-more.ts @@ -3,7 +3,7 @@ import useConfig from './use-config' type Params = { id: string - payload: ToolNodeType, + payload: ToolNodeType } const useGetDataForCheckMore = ({ diff --git a/web/app/components/workflow/nodes/tool/use-single-run-form-params.ts b/web/app/components/workflow/nodes/tool/use-single-run-form-params.ts index b393711e98..ca1e61be37 100644 --- a/web/app/components/workflow/nodes/tool/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/tool/use-single-run-form-params.ts @@ -1,19 +1,19 @@ import type { RefObject } from 'react' -import type { InputVar, Variable } from '@/app/components/workflow/types' -import { useCallback, useMemo, useState } from 'react' -import useNodeCrud from '../_base/hooks/use-node-crud' -import { type ToolNodeType, VarType } from './types' -import type { ValueSelector } from '@/app/components/workflow/types' +import type { ToolNodeType } from './types' import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form/form' -import { produce } from 'immer' +import type { InputVar, ValueSelector, Variable } from '@/app/components/workflow/types' import type { NodeTracing } from '@/types/workflow' +import { produce } from 'immer' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import formatToTracingNodeList from '@/app/components/workflow/run/utils/format-log' import { useToolIcon } from '../../hooks' +import useNodeCrud from '../_base/hooks/use-node-crud' +import { VarType } from './types' type Params = { - id: string, - payload: ToolNodeType, + id: string + payload: ToolNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] @@ -61,7 +61,7 @@ const useSingleRunFormParams = ({ Object.keys(inputs.tool_parameters).forEach((key: string) => { const { type, value } = inputs.tool_parameters[key] if (type === VarType.constant && (value === undefined || value === null)) { - if(!draft.tool_parameters || !draft.tool_parameters[key]) + if (!draft.tool_parameters || !draft.tool_parameters[key]) return draft[key] = value } diff --git a/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/index.tsx b/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/index.tsx index 93bf788c34..7ed42ea019 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/index.tsx +++ b/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/index.tsx @@ -1,10 +1,10 @@ 'use client' +import type { FC } from 'react' import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { Event } from '@/app/components/tools/types' -import type { FC } from 'react' +import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' import type { PluginTriggerVarInputs } from '@/app/components/workflow/nodes/trigger-plugin/types' import TriggerFormItem from './item' -import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' type Props = { readOnly: boolean @@ -33,7 +33,7 @@ const TriggerForm: FC<Props> = ({ disableVariableInsertion = false, }) => { return ( - <div className='space-y-1'> + <div className="space-y-1"> { schema.map((schema, index) => ( <TriggerFormItem diff --git a/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/item.tsx b/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/item.tsx index 678c12f02a..ad1e7747d4 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/item.tsx +++ b/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/item.tsx @@ -1,19 +1,19 @@ 'use client' import type { FC } from 'react' +import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { Event } from '@/app/components/tools/types' +import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' +import type { PluginTriggerVarInputs } from '@/app/components/workflow/nodes/trigger-plugin/types' import { RiBracesLine, } from '@remixicon/react' -import type { PluginTriggerVarInputs } from '@/app/components/workflow/nodes/trigger-plugin/types' -import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' +import { useBoolean } from 'ahooks' import Button from '@/app/components/base/button' import Tooltip from '@/app/components/base/tooltip' -import FormInputItem from '@/app/components/workflow/nodes/_base/components/form-input-item' -import { useBoolean } from 'ahooks' +import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import SchemaModal from '@/app/components/plugins/plugin-detail-panel/tool-selector/schema-modal' -import type { Event } from '@/app/components/tools/types' -import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' +import FormInputItem from '@/app/components/workflow/nodes/_base/components/form-input-item' type Props = { readOnly: boolean @@ -49,39 +49,41 @@ const TriggerFormItem: FC<Props> = ({ setFalse: hideSchema, }] = useBoolean(false) return ( - <div className='space-y-0.5 py-1'> + <div className="space-y-0.5 py-1"> <div> - <div className='flex h-6 items-center'> - <div className='system-sm-medium text-text-secondary'>{label[language] || label.en_US}</div> + <div className="flex h-6 items-center"> + <div className="system-sm-medium text-text-secondary">{label[language] || label.en_US}</div> {required && ( - <div className='system-xs-regular ml-1 text-text-destructive-secondary'>*</div> + <div className="system-xs-regular ml-1 text-text-destructive-secondary">*</div> )} {!showDescription && tooltip && ( <Tooltip - popupContent={<div className='w-[200px]'> - {tooltip[language] || tooltip.en_US} - </div>} - triggerClassName='ml-1 w-4 h-4' + popupContent={( + <div className="w-[200px]"> + {tooltip[language] || tooltip.en_US} + </div> + )} + triggerClassName="ml-1 w-4 h-4" asChild={false} /> )} {showSchemaButton && ( <> - <div className='system-xs-regular ml-1 mr-0.5 text-text-quaternary'>·</div> + <div className="system-xs-regular ml-1 mr-0.5 text-text-quaternary">·</div> <Button - variant='ghost' - size='small' + variant="ghost" + size="small" onClick={showSchema} - className='system-xs-regular px-1 text-text-tertiary' + className="system-xs-regular px-1 text-text-tertiary" > - <RiBracesLine className='mr-1 size-3.5' /> + <RiBracesLine className="mr-1 size-3.5" /> <span>JSON Schema</span> </Button> </> )} </div> {showDescription && tooltip && ( - <div className='body-xs-regular pb-0.5 text-text-tertiary'>{tooltip[language] || tooltip.en_US}</div> + <div className="body-xs-regular pb-0.5 text-text-tertiary">{tooltip[language] || tooltip.en_US}</div> )} </div> <FormInputItem @@ -93,7 +95,7 @@ const TriggerFormItem: FC<Props> = ({ inPanel={inPanel} currentTool={currentEvent} currentProvider={currentProvider} - providerType='trigger' + providerType="trigger" extraParams={extraParams} disableVariableInsertion={disableVariableInsertion} /> diff --git a/web/app/components/workflow/nodes/trigger-plugin/default.ts b/web/app/components/workflow/nodes/trigger-plugin/default.ts index 928534e07c..6e3aac1339 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/default.ts +++ b/web/app/components/workflow/nodes/trigger-plugin/default.ts @@ -1,13 +1,15 @@ -import type { SchemaTypeDefinition } from '@/service/use-common' import type { NodeDefault, Var } from '../../types' +import type { Field, StructuredOutput } from '../llm/types' +import type { PluginTriggerNodeType } from './types' +import type { SchemaTypeDefinition } from '@/service/use-common' import { BlockEnum, VarType } from '../../types' import { genNodeMetaData } from '../../utils' import { VarKindType } from '../_base/types' -import { type Field, type StructuredOutput, Type } from '../llm/types' -import type { PluginTriggerNodeType } from './types' +import { Type } from '../llm/types' const normalizeJsonSchemaType = (schema: any): string | undefined => { - if (!schema) return undefined + if (!schema) + return undefined const { type, properties, items, oneOf, anyOf, allOf } = schema if (Array.isArray(type)) @@ -55,7 +57,7 @@ const extractSchemaType = (schema: any, _schemaTypeDefinitions?: SchemaTypeDefin const resolveVarType = ( schema: any, schemaTypeDefinitions?: SchemaTypeDefinition[], -): { type: VarType; schemaType?: string } => { +): { type: VarType, schemaType?: string } => { const schemaType = extractSchemaType(schema, schemaTypeDefinitions) const normalizedType = normalizeJsonSchemaType(schema) @@ -195,9 +197,9 @@ const buildOutputVars = (schema: Record<string, any>, schemaTypeDefinitions?: Sc if (normalizedType === 'object') { const childProperties = propertySchema?.properties ? Object.entries(propertySchema.properties).reduce((acc, [key, value]) => { - acc[key] = convertJsonSchemaToField(value, schemaTypeDefinitions) - return acc - }, {} as Record<string, Field>) + acc[key] = convertJsonSchemaToField(value, schemaTypeDefinitions) + return acc + }, {} as Record<string, Field>) : {} const required = Array.isArray(propertySchema?.required) ? propertySchema.required.filter(Boolean) : undefined @@ -263,7 +265,7 @@ const nodeDefault: NodeDefault<PluginTriggerNodeType> = { } const targetParam = typeof rawParam === 'object' && rawParam !== null && 'type' in rawParam - ? rawParam as { type: VarKindType; value: any } + ? rawParam as { type: VarKindType, value: any } : { type: VarKindType.constant, value: rawParam } const { type, value } = targetParam @@ -277,8 +279,9 @@ const nodeDefault: NodeDefault<PluginTriggerNodeType> = { || value === null || value === '' || (Array.isArray(value) && value.length === 0) - ) + ) { errorMessage = t('workflow.errorMsg.fieldRequired', { field: field.label }) + } } }) } diff --git a/web/app/components/workflow/nodes/trigger-plugin/hooks/use-trigger-auth-flow.ts b/web/app/components/workflow/nodes/trigger-plugin/hooks/use-trigger-auth-flow.ts index 983b8512de..36bcbf1cc7 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/hooks/use-trigger-auth-flow.ts +++ b/web/app/components/workflow/nodes/trigger-plugin/hooks/use-trigger-auth-flow.ts @@ -1,3 +1,4 @@ +import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' import { useCallback, useState } from 'react' import { useBuildTriggerSubscription, @@ -5,7 +6,6 @@ import { useUpdateTriggerSubscriptionBuilder, useVerifyTriggerSubscriptionBuilder, } from '@/service/use-triggers' -import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' // Helper function to serialize complex values to strings for backend encryption const serializeFormValues = (values: Record<string, any>): Record<string, string> => { @@ -51,7 +51,8 @@ export const useTriggerAuthFlow = (provider: TriggerWithProvider): AuthFlowState const buildSubscription = useBuildTriggerSubscription() const startAuth = useCallback(async () => { - if (builderId) return // Prevent multiple calls if already started + if (builderId) + return // Prevent multiple calls if already started setIsLoading(true) setError(null) diff --git a/web/app/components/workflow/nodes/trigger-plugin/node.tsx b/web/app/components/workflow/nodes/trigger-plugin/node.tsx index 0eee4cb8b4..da4dc83d34 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/node.tsx +++ b/web/app/components/workflow/nodes/trigger-plugin/node.tsx @@ -1,12 +1,12 @@ -import NodeStatus, { NodeStatusEnum } from '@/app/components/base/node-status' -import type { NodeProps } from '@/app/components/workflow/types' import type { FC } from 'react' +import type { PluginTriggerNodeType } from './types' +import type { NodeProps } from '@/app/components/workflow/types' import React, { useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button' -import { useNodePluginInstallation } from '@/app/components/workflow/hooks/use-node-plugin-installation' +import NodeStatus, { NodeStatusEnum } from '@/app/components/base/node-status' import { useNodeDataUpdate } from '@/app/components/workflow/hooks/use-node-data-update' -import type { PluginTriggerNodeType } from './types' +import { useNodePluginInstallation } from '@/app/components/workflow/hooks/use-node-plugin-installation' +import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button' import useConfig from './use-config' const formatConfigValue = (rawValue: any): string => { diff --git a/web/app/components/workflow/nodes/trigger-plugin/panel.tsx b/web/app/components/workflow/nodes/trigger-plugin/panel.tsx index 9b4d8058b1..ffa3a5503c 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/panel.tsx +++ b/web/app/components/workflow/nodes/trigger-plugin/panel.tsx @@ -1,14 +1,14 @@ import type { FC } from 'react' -import React from 'react' import type { PluginTriggerNodeType } from './types' -import Split from '@/app/components/workflow/nodes/_base/components/split' -import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' import type { NodePanelProps } from '@/app/components/workflow/types' -import useConfig from './use-config' -import TriggerForm from './components/trigger-form' +import React from 'react' +import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' +import Split from '@/app/components/workflow/nodes/_base/components/split' import StructureOutputItem from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show' -import { Type } from '../llm/types' import { BlockEnum } from '@/app/components/workflow/types' +import { Type } from '../llm/types' +import TriggerForm from './components/trigger-form' +import useConfig from './use-config' const Panel: FC<NodePanelProps<PluginTriggerNodeType>> = ({ id, @@ -35,11 +35,11 @@ const Panel: FC<NodePanelProps<PluginTriggerNodeType>> = ({ })) return ( - <div className='mt-2'> + <div className="mt-2"> {/* Dynamic Parameters Form - Only show when authenticated */} {triggerParameterSchema.length > 0 && subscriptionSelected && ( <> - <div className='px-4 pb-4'> + <div className="px-4 pb-4"> <TriggerForm readOnly={readOnly} nodeId={id} @@ -69,20 +69,22 @@ const Panel: FC<NodePanelProps<PluginTriggerNodeType>> = ({ ))} {Object.entries(outputSchema.properties || {}).map(([name, schema]: [string, any]) => ( <div key={name}> - {schema.type === 'object' ? ( - <StructureOutputItem - rootClassName='code-sm-semibold text-text-secondary' - payload={{ - schema: { - type: Type.object, - properties: { - [name]: schema, - }, - additionalProperties: false, - }, - }} - /> - ) : null} + {schema.type === 'object' + ? ( + <StructureOutputItem + rootClassName="code-sm-semibold text-text-secondary" + payload={{ + schema: { + type: Type.object, + properties: { + [name]: schema, + }, + additionalProperties: false, + }, + }} + /> + ) + : null} </div> ))} </> diff --git a/web/app/components/workflow/nodes/trigger-plugin/types.ts b/web/app/components/workflow/nodes/trigger-plugin/types.ts index 6dba97d795..8dd6aa9657 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/types.ts +++ b/web/app/components/workflow/nodes/trigger-plugin/types.ts @@ -1,6 +1,6 @@ -import type { CommonNodeType } from '@/app/components/workflow/types' -import type { CollectionType } from '@/app/components/tools/types' import type { ResourceVarInputs } from '../_base/types' +import type { CollectionType } from '@/app/components/tools/types' +import type { CommonNodeType } from '@/app/components/workflow/types' export type PluginTriggerNodeType = CommonNodeType & { provider_id: string diff --git a/web/app/components/workflow/nodes/trigger-plugin/use-check-params.ts b/web/app/components/workflow/nodes/trigger-plugin/use-check-params.ts index 16b763f11a..7c23378498 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/use-check-params.ts +++ b/web/app/components/workflow/nodes/trigger-plugin/use-check-params.ts @@ -1,8 +1,8 @@ -import { useCallback } from 'react' import type { PluginTriggerNodeType } from './types' -import { useAllTriggerPlugins } from '@/service/use-triggers' -import { useGetLanguage } from '@/context/i18n' +import { useCallback } from 'react' import { getTriggerCheckParams } from '@/app/components/workflow/utils/trigger' +import { useGetLanguage } from '@/context/i18n' +import { useAllTriggerPlugins } from '@/service/use-triggers' type Params = { id: string diff --git a/web/app/components/workflow/nodes/trigger-plugin/use-config.ts b/web/app/components/workflow/nodes/trigger-plugin/use-config.ts index cf66913e58..a746f1a410 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/use-config.ts +++ b/web/app/components/workflow/nodes/trigger-plugin/use-config.ts @@ -1,20 +1,19 @@ -import { useCallback, useEffect, useMemo } from 'react' +import type { PluginTriggerNodeType, PluginTriggerVarInputs } from './types' +import type { Event } from '@/app/components/tools/types' +import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' +import type { InputVar } from '@/app/components/workflow/types' import { produce } from 'immer' -import type { PluginTriggerNodeType } from './types' -import type { PluginTriggerVarInputs } from './types' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' -import { useNodesReadOnly } from '@/app/components/workflow/hooks' -import { - useAllTriggerPlugins, - useTriggerSubscriptions, -} from '@/service/use-triggers' +import { useCallback, useEffect, useMemo } from 'react' import { getConfiguredValue, toolParametersToFormSchemas, } from '@/app/components/tools/utils/to-form-schema' -import type { InputVar } from '@/app/components/workflow/types' -import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' -import type { Event } from '@/app/components/tools/types' +import { useNodesReadOnly } from '@/app/components/workflow/hooks' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { + useAllTriggerPlugins, + useTriggerSubscriptions, +} from '@/service/use-triggers' import { VarKindType } from '../_base/types' const normalizeEventParameters = ( @@ -121,7 +120,8 @@ const useConfig = (id: string, payload: PluginTriggerNodeType) => { // Dynamic trigger parameters (from specific trigger.parameters) const triggerSpecificParameterSchema = useMemo(() => { - if (!currentEvent) return [] + if (!currentEvent) + return [] return toolParametersToFormSchemas(currentEvent.parameters) }, [currentEvent]) diff --git a/web/app/components/workflow/nodes/trigger-plugin/utils/form-helpers.ts b/web/app/components/workflow/nodes/trigger-plugin/utils/form-helpers.ts index 36090d9771..cd49bd8ffe 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/utils/form-helpers.ts +++ b/web/app/components/workflow/nodes/trigger-plugin/utils/form-helpers.ts @@ -45,8 +45,8 @@ export const deepSanitizeFormValues = (values: Record<string, any>, visited = ne */ export const findMissingRequiredField = ( formData: Record<string, any>, - requiredFields: Array<{ name: string; label: any }>, -): { name: string; label: any } | null => { + requiredFields: Array<{ name: string, label: any }>, +): { name: string, label: any } | null => { for (const field of requiredFields) { if (!formData[field.name] || formData[field.name] === '') return field diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/frequency-selector.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/frequency-selector.tsx index d0de74a6ef..b4f62de436 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/frequency-selector.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/frequency-selector.tsx @@ -1,7 +1,7 @@ +import type { ScheduleFrequency } from '../types' import React, { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { SimpleSelect } from '@/app/components/base/select' -import type { ScheduleFrequency } from '../types' type FrequencySelectorProps = { frequency: ScheduleFrequency diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx index 6dc88c85bf..724fedc7b2 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx @@ -1,8 +1,8 @@ +import type { ScheduleMode } from '../types' +import { RiCalendarLine, RiCodeLine } from '@remixicon/react' import React from 'react' import { useTranslation } from 'react-i18next' -import { RiCalendarLine, RiCodeLine } from '@remixicon/react' import { SegmentedControl } from '@/app/components/base/segmented-control' -import type { ScheduleMode } from '../types' type ModeSwitcherProps = { mode: ScheduleMode diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/mode-toggle.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/mode-toggle.tsx index 6ae5d2cf67..35ffaff939 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/mode-toggle.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/mode-toggle.tsx @@ -1,7 +1,7 @@ +import type { ScheduleMode } from '../types' import React from 'react' import { useTranslation } from 'react-i18next' import { Asterisk, CalendarCheckLine } from '@/app/components/base/icons/src/vender/workflow' -import type { ScheduleMode } from '../types' type ModeToggleProps = { mode: ScheduleMode diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/monthly-days-selector.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/monthly-days-selector.tsx index d7cce79328..e5a50522e1 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/monthly-days-selector.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/monthly-days-selector.tsx @@ -1,6 +1,6 @@ +import { RiQuestionLine } from '@remixicon/react' import React from 'react' import { useTranslation } from 'react-i18next' -import { RiQuestionLine } from '@remixicon/react' import Tooltip from '@/app/components/base/tooltip' type MonthlyDaysSelectorProps = { @@ -53,18 +53,20 @@ const MonthlyDaysSelector = ({ selectedDays, onChange }: MonthlyDaysSelectorProp : 'border-divider-subtle text-text-tertiary hover:border-divider-regular hover:text-text-secondary' }`} > - {day === 'last' ? ( - <div className="flex items-center justify-center gap-1"> - <span>{t('workflow.nodes.triggerSchedule.lastDay')}</span> - <Tooltip - popupContent={t('workflow.nodes.triggerSchedule.lastDayTooltip')} - > - <RiQuestionLine className="h-3 w-3 text-text-quaternary" /> - </Tooltip> - </div> - ) : ( - day - )} + {day === 'last' + ? ( + <div className="flex items-center justify-center gap-1"> + <span>{t('workflow.nodes.triggerSchedule.lastDay')}</span> + <Tooltip + popupContent={t('workflow.nodes.triggerSchedule.lastDayTooltip')} + > + <RiQuestionLine className="h-3 w-3 text-text-quaternary" /> + </Tooltip> + </div> + ) + : ( + day + )} </button> ))} {/* Fill empty cells in the last row (Last day takes 2 cols, so need 1 less) */} diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/next-execution-times.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/next-execution-times.tsx index 02e85e2724..c84bca483c 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/next-execution-times.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/next-execution-times.tsx @@ -1,6 +1,6 @@ +import type { ScheduleTriggerNodeType } from '../types' import React from 'react' import { useTranslation } from 'react-i18next' -import type { ScheduleTriggerNodeType } from '../types' import { getFormattedExecutionTimes } from '../utils/execution-time-calculator' type NextExecutionTimesProps = { diff --git a/web/app/components/workflow/nodes/trigger-schedule/default.ts b/web/app/components/workflow/nodes/trigger-schedule/default.ts index 69f93c33f4..bd7f1976ba 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/default.ts +++ b/web/app/components/workflow/nodes/trigger-schedule/default.ts @@ -1,14 +1,15 @@ -import { BlockEnum } from '../../types' import type { NodeDefault } from '../../types' import type { ScheduleTriggerNodeType } from './types' +import { BlockEnum } from '../../types' +import { genNodeMetaData } from '../../utils' +import { getDefaultScheduleConfig } from './constants' import { isValidCronExpression } from './utils/cron-parser' import { getNextExecutionTimes } from './utils/execution-time-calculator' -import { getDefaultScheduleConfig } from './constants' -import { genNodeMetaData } from '../../utils' const isValidTimeFormat = (time: string): boolean => { const timeRegex = /^(0?\d|1[0-2]):[0-5]\d (AM|PM)$/ - if (!timeRegex.test(time)) return false + if (!timeRegex.test(time)) + return false const [timePart, period] = time.split(' ') const [hour, minute] = timePart.split(':') @@ -16,8 +17,8 @@ const isValidTimeFormat = (time: string): boolean => { const minuteNum = Number.parseInt(minute, 10) return hourNum >= 1 && hourNum <= 12 - && minuteNum >= 0 && minuteNum <= 59 - && ['AM', 'PM'].includes(period) + && minuteNum >= 0 && minuteNum <= 59 + && ['AM', 'PM'].includes(period) } const validateHourlyConfig = (config: any, t: any): string => { @@ -41,7 +42,8 @@ const validateDailyConfig = (config: any, t: any): string => { const validateWeeklyConfig = (config: any, t: any): string => { const dailyError = validateDailyConfig(config, t) - if (dailyError) return dailyError + if (dailyError) + return dailyError const i18nPrefix = 'workflow.errorMsg' @@ -59,7 +61,8 @@ const validateWeeklyConfig = (config: any, t: any): string => { const validateMonthlyConfig = (config: any, t: any): string => { const dailyError = validateDailyConfig(config, t) - if (dailyError) return dailyError + if (dailyError) + return dailyError const i18nPrefix = 'workflow.errorMsg' diff --git a/web/app/components/workflow/nodes/trigger-schedule/node.tsx b/web/app/components/workflow/nodes/trigger-schedule/node.tsx index 9870ef211a..45e9b2afdb 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/node.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/node.tsx @@ -1,8 +1,8 @@ import type { FC } from 'react' -import React from 'react' -import { useTranslation } from 'react-i18next' import type { ScheduleTriggerNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' +import React from 'react' +import { useTranslation } from 'react-i18next' import { getNextExecutionTime } from './utils/execution-time-calculator' const i18nPrefix = 'workflow.nodes.triggerSchedule' diff --git a/web/app/components/workflow/nodes/trigger-schedule/panel.tsx b/web/app/components/workflow/nodes/trigger-schedule/panel.tsx index 2a7c661339..8daedc50a9 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/panel.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/panel.tsx @@ -1,17 +1,17 @@ import type { FC } from 'react' +import type { ScheduleTriggerNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import type { ScheduleTriggerNodeType } from './types' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import type { NodePanelProps } from '@/app/components/workflow/types' -import ModeToggle from './components/mode-toggle' -import FrequencySelector from './components/frequency-selector' -import WeekdaySelector from './components/weekday-selector' import TimePicker from '@/app/components/base/date-and-time-picker/time-picker' -import NextExecutionTimes from './components/next-execution-times' -import MonthlyDaysSelector from './components/monthly-days-selector' -import OnMinuteSelector from './components/on-minute-selector' import Input from '@/app/components/base/input' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import FrequencySelector from './components/frequency-selector' +import ModeToggle from './components/mode-toggle' +import MonthlyDaysSelector from './components/monthly-days-selector' +import NextExecutionTimes from './components/next-execution-times' +import OnMinuteSelector from './components/on-minute-selector' +import WeekdaySelector from './components/weekday-selector' import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.triggerSchedule' @@ -33,16 +33,16 @@ const Panel: FC<NodePanelProps<ScheduleTriggerNodeType>> = ({ } = useConfig(id, data) return ( - <div className='mt-2'> - <div className='space-y-4 px-4 pb-3 pt-2'> + <div className="mt-2"> + <div className="space-y-4 px-4 pb-3 pt-2"> <Field title={t(`${i18nPrefix}.title`)} - operations={ + operations={( <ModeToggle mode={inputs.mode} onChange={handleModeChange} /> - } + )} > <div className="space-y-3"> @@ -59,35 +59,37 @@ const Panel: FC<NodePanelProps<ScheduleTriggerNodeType>> = ({ /> </div> <div className="col-span-2"> - {inputs.frequency === 'hourly' ? ( - <OnMinuteSelector - value={inputs.visual_config?.on_minute} - onChange={handleOnMinuteChange} - /> - ) : ( - <> - <label className="mb-2 block text-xs font-medium text-gray-500"> - {t('workflow.nodes.triggerSchedule.time')} - </label> - <TimePicker - notClearable={true} - timezone={inputs.timezone} - value={inputs.visual_config?.time || '12:00 AM'} - triggerFullWidth={true} - onChange={(time) => { - if (time) { - const timeString = time.format('h:mm A') - handleTimeChange(timeString) - } - }} - onClear={() => { - handleTimeChange('12:00 AM') - }} - placeholder={t('workflow.nodes.triggerSchedule.selectTime')} - showTimezone={true} - /> - </> - )} + {inputs.frequency === 'hourly' + ? ( + <OnMinuteSelector + value={inputs.visual_config?.on_minute} + onChange={handleOnMinuteChange} + /> + ) + : ( + <> + <label className="mb-2 block text-xs font-medium text-gray-500"> + {t('workflow.nodes.triggerSchedule.time')} + </label> + <TimePicker + notClearable={true} + timezone={inputs.timezone} + value={inputs.visual_config?.time || '12:00 AM'} + triggerFullWidth={true} + onChange={(time) => { + if (time) { + const timeString = time.format('h:mm A') + handleTimeChange(timeString) + } + }} + onClear={() => { + handleTimeChange('12:00 AM') + }} + placeholder={t('workflow.nodes.triggerSchedule.selectTime')} + showTimezone={true} + /> + </> + )} </div> </div> diff --git a/web/app/components/workflow/nodes/trigger-schedule/use-config.ts b/web/app/components/workflow/nodes/trigger-schedule/use-config.ts index 06e29ccd84..a3e5959f2e 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/use-config.ts +++ b/web/app/components/workflow/nodes/trigger-schedule/use-config.ts @@ -1,7 +1,7 @@ -import { useCallback, useMemo } from 'react' import type { ScheduleFrequency, ScheduleMode, ScheduleTriggerNodeType } from './types' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { useCallback, useMemo } from 'react' import { useNodesReadOnly } from '@/app/components/workflow/hooks' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import { useAppContext } from '@/context/app-context' import { getDefaultVisualConfig } from './constants' diff --git a/web/app/components/workflow/nodes/trigger-schedule/utils/execution-time-calculator.ts b/web/app/components/workflow/nodes/trigger-schedule/utils/execution-time-calculator.ts index aef122ba25..6bd6bc41dc 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/utils/execution-time-calculator.ts +++ b/web/app/components/workflow/nodes/trigger-schedule/utils/execution-time-calculator.ts @@ -1,6 +1,6 @@ import type { ScheduleTriggerNodeType } from '../types' -import { isValidCronExpression, parseCronExpression } from './cron-parser' import { convertTimezoneToOffsetStr } from '@/app/components/base/date-and-time-picker/utils/dayjs' +import { isValidCronExpression, parseCronExpression } from './cron-parser' const DEFAULT_TIMEZONE = 'UTC' @@ -107,8 +107,10 @@ export const getNextExecutionTimes = (data: ScheduleTriggerNodeType, count: numb const [time, period] = defaultTime.split(' ') const [hour, minute] = time.split(':') let displayHour = Number.parseInt(hour) - if (period === 'PM' && displayHour !== 12) displayHour += 12 - if (period === 'AM' && displayHour === 12) displayHour = 0 + if (period === 'PM' && displayHour !== 12) + displayHour += 12 + if (period === 'AM' && displayHour === 12) + displayHour = 0 // Check if today's configured time has already passed const todayExecution = new Date(userToday) @@ -132,8 +134,10 @@ export const getNextExecutionTimes = (data: ScheduleTriggerNodeType, count: numb const [time, period] = defaultTime.split(' ') const [hour, minute] = time.split(':') let displayHour = Number.parseInt(hour) - if (period === 'PM' && displayHour !== 12) displayHour += 12 - if (period === 'AM' && displayHour === 12) displayHour = 0 + if (period === 'PM' && displayHour !== 12) + displayHour += 12 + if (period === 'AM' && displayHour === 12) + displayHour = 0 // Get current time completely in user timezone const userCurrentTime = getUserTimezoneCurrentTime(timezone) @@ -145,10 +149,12 @@ export const getNextExecutionTimes = (data: ScheduleTriggerNodeType, count: numb let hasValidDays = false for (const selectedDay of selectedDays) { - if (executionCount >= count) break + if (executionCount >= count) + break const targetDay = dayMap[selectedDay as keyof typeof dayMap] - if (targetDay === undefined) continue + if (targetDay === undefined) + continue hasValidDays = true @@ -174,7 +180,8 @@ export const getNextExecutionTimes = (data: ScheduleTriggerNodeType, count: numb } } - if (!hasValidDays) break + if (!hasValidDays) + break weekOffset++ } @@ -192,8 +199,10 @@ export const getNextExecutionTimes = (data: ScheduleTriggerNodeType, count: numb const [time, period] = defaultTime.split(' ') const [hour, minute] = time.split(':') let displayHour = Number.parseInt(hour) - if (period === 'PM' && displayHour !== 12) displayHour += 12 - if (period === 'AM' && displayHour === 12) displayHour = 0 + if (period === 'PM' && displayHour !== 12) + displayHour += 12 + if (period === 'AM' && displayHour === 12) + displayHour = 0 // Get current time completely in user timezone const userCurrentTime = getUserTimezoneCurrentTime(timezone) @@ -237,7 +246,8 @@ export const getNextExecutionTimes = (data: ScheduleTriggerNodeType, count: numb monthlyExecutions.sort((a, b) => a.getTime() - b.getTime()) for (const execution of monthlyExecutions) { - if (executionCount >= count) break + if (executionCount >= count) + break times.push(execution) executionCount++ } diff --git a/web/app/components/workflow/nodes/trigger-schedule/utils/integration.spec.ts b/web/app/components/workflow/nodes/trigger-schedule/utils/integration.spec.ts index a3d28de112..cfc502d141 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/utils/integration.spec.ts +++ b/web/app/components/workflow/nodes/trigger-schedule/utils/integration.spec.ts @@ -1,7 +1,7 @@ -import { isValidCronExpression, parseCronExpression } from './cron-parser' -import { getNextExecutionTime, getNextExecutionTimes } from './execution-time-calculator' import type { ScheduleTriggerNodeType } from '../types' import { BlockEnum } from '../../../types' +import { isValidCronExpression, parseCronExpression } from './cron-parser' +import { getNextExecutionTime, getNextExecutionTimes } from './execution-time-calculator' // Comprehensive integration tests for cron-parser and execution-time-calculator compatibility describe('cron-parser + execution-time-calculator integration', () => { @@ -176,9 +176,12 @@ describe('cron-parser + execution-time-calculator integration', () => { expect(date.getHours()).toBe(hour) expect(date.getMinutes()).toBe(minute) - if (weekday !== undefined) expect(date.getDay()).toBe(weekday) - if (day !== undefined) expect(date.getDate()).toBe(day) - if (month !== undefined) expect(date.getMonth()).toBe(month) + if (weekday !== undefined) + expect(date.getDay()).toBe(weekday) + if (day !== undefined) + expect(date.getDate()).toBe(day) + if (month !== undefined) + expect(date.getMonth()).toBe(month) }) }) }) diff --git a/web/app/components/workflow/nodes/trigger-webhook/components/generic-table.tsx b/web/app/components/workflow/nodes/trigger-webhook/components/generic-table.tsx index eaf3f399d7..a6644d7312 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/components/generic-table.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/components/generic-table.tsx @@ -1,16 +1,17 @@ 'use client' import type { FC, ReactNode } from 'react' -import React, { useCallback, useMemo } from 'react' import { RiDeleteBinLine } from '@remixicon/react' -import Input from '@/app/components/base/input' +import React, { useCallback, useMemo } from 'react' import Checkbox from '@/app/components/base/checkbox' +import Input from '@/app/components/base/input' import { SimpleSelect } from '@/app/components/base/select' -import { replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' import { cn } from '@/utils/classnames' +import { replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' // Tiny utility to judge whether a cell value is effectively present const isPresent = (v: unknown): boolean => { - if (typeof v === 'string') return v.trim() !== '' + if (typeof v === 'string') + return v.trim() !== '' return !(v === '' || v === null || v === undefined || v === false) } // Column configuration types for table components @@ -102,14 +103,17 @@ const GenericTable: FC<GenericTableProps> = ({ }, [data, emptyRowData, readonly]) const removeRow = useCallback((dataIndex: number) => { - if (readonly) return - if (dataIndex < 0 || dataIndex >= data.length) return // ignore virtual rows + if (readonly) + return + if (dataIndex < 0 || dataIndex >= data.length) + return // ignore virtual rows const newData = data.filter((_, i) => i !== dataIndex) onChange(newData) }, [data, readonly, onChange]) const updateRow = useCallback((dataIndex: number | null, key: string, value: unknown) => { - if (readonly) return + if (readonly) + return if (dataIndex !== null && dataIndex < data.length) { // Editing existing configured row @@ -283,13 +287,15 @@ const GenericTable: FC<GenericTableProps> = ({ <h4 className="system-sm-semibold-uppercase text-text-secondary">{title}</h4> </div> - {showPlaceholder ? ( - <div className="flex h-7 items-center justify-center rounded-lg border border-divider-regular bg-components-panel-bg text-xs font-normal leading-[18px] text-text-quaternary"> - {placeholder} - </div> - ) : ( - renderTable() - )} + {showPlaceholder + ? ( + <div className="flex h-7 items-center justify-center rounded-lg border border-divider-regular bg-components-panel-bg text-xs font-normal leading-[18px] text-text-quaternary"> + {placeholder} + </div> + ) + : ( + renderTable() + )} </div> ) } diff --git a/web/app/components/workflow/nodes/trigger-webhook/components/header-table.tsx b/web/app/components/workflow/nodes/trigger-webhook/components/header-table.tsx index 25e3cd4137..da54cac16a 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/components/header-table.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/components/header-table.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' +import type { WebhookHeader } from '../types' +import type { ColumnConfig, GenericTableRow } from './generic-table' import React from 'react' import { useTranslation } from 'react-i18next' import GenericTable from './generic-table' -import type { ColumnConfig, GenericTableRow } from './generic-table' -import type { WebhookHeader } from '../types' type HeaderTableProps = { readonly?: boolean diff --git a/web/app/components/workflow/nodes/trigger-webhook/components/parameter-table.tsx b/web/app/components/workflow/nodes/trigger-webhook/components/parameter-table.tsx index bf030c4340..1fa038ff73 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/components/parameter-table.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/components/parameter-table.tsx @@ -1,12 +1,12 @@ 'use client' import type { FC } from 'react' +import type { WebhookParameter } from '../types' +import type { ColumnConfig, GenericTableRow } from './generic-table' import React, { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import GenericTable from './generic-table' -import type { ColumnConfig, GenericTableRow } from './generic-table' -import type { WebhookParameter } from '../types' -import { createParameterTypeOptions, normalizeParameterType } from '../utils/parameter-type-utils' import { VarType } from '@/app/components/workflow/types' +import { createParameterTypeOptions, normalizeParameterType } from '../utils/parameter-type-utils' +import GenericTable from './generic-table' type ParameterTableProps = { title: string @@ -29,9 +29,7 @@ const ParameterTable: FC<ParameterTableProps> = ({ // Memoize typeOptions to prevent unnecessary re-renders that cause SimpleSelect state resets const typeOptions = useMemo(() => - createParameterTypeOptions(contentType), - [contentType], - ) + createParameterTypeOptions(contentType), [contentType]) // Define columns based on component type - matching prototype design const columns: ColumnConfig[] = [ diff --git a/web/app/components/workflow/nodes/trigger-webhook/default.ts b/web/app/components/workflow/nodes/trigger-webhook/default.ts index 5071a79913..d6c39ca2d8 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/default.ts +++ b/web/app/components/workflow/nodes/trigger-webhook/default.ts @@ -1,7 +1,7 @@ -import { BlockEnum } from '../../types' import type { NodeDefault } from '../../types' -import { genNodeMetaData } from '../../utils' import type { WebhookTriggerNodeType } from './types' +import { BlockEnum } from '../../types' +import { genNodeMetaData } from '../../utils' import { isValidParameterType } from './utils/parameter-type-utils' import { createWebhookRawVariable } from './utils/raw-variable' diff --git a/web/app/components/workflow/nodes/trigger-webhook/node.tsx b/web/app/components/workflow/nodes/trigger-webhook/node.tsx index 40c3b441da..77f42b6db2 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/node.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' -import React from 'react' import type { WebhookTriggerNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' +import React from 'react' const Node: FC<NodeProps<WebhookTriggerNodeType>> = ({ data, diff --git a/web/app/components/workflow/nodes/trigger-webhook/panel.tsx b/web/app/components/workflow/nodes/trigger-webhook/panel.tsx index 1de18bd806..efc541bbb3 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/panel.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/panel.tsx @@ -1,24 +1,24 @@ import type { FC } from 'react' +import type { HttpMethod, WebhookTriggerNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' + +import copy from 'copy-to-clipboard' import React, { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' - -import type { HttpMethod, WebhookTriggerNodeType } from './types' -import useConfig from './use-config' -import ParameterTable from './components/parameter-table' -import HeaderTable from './components/header-table' -import ParagraphInput from './components/paragraph-input' -import { OutputVariablesContent } from './utils/render-output-vars' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import Split from '@/app/components/workflow/nodes/_base/components/split' -import OutputVars from '@/app/components/workflow/nodes/_base/components/output-vars' -import type { NodePanelProps } from '@/app/components/workflow/types' -import InputWithCopy from '@/app/components/base/input-with-copy' import { InputNumber } from '@/app/components/base/input-number' +import InputWithCopy from '@/app/components/base/input-with-copy' import { SimpleSelect } from '@/app/components/base/select' import Toast from '@/app/components/base/toast' import Tooltip from '@/app/components/base/tooltip' -import copy from 'copy-to-clipboard' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import OutputVars from '@/app/components/workflow/nodes/_base/components/output-vars' +import Split from '@/app/components/workflow/nodes/_base/components/split' import { isPrivateOrLocalAddress } from '@/utils/urlValidation' +import HeaderTable from './components/header-table' +import ParagraphInput from './components/paragraph-input' +import ParameterTable from './components/parameter-table' +import useConfig from './use-config' +import { OutputVariablesContent } from './utils/render-output-vars' const i18nPrefix = 'workflow.nodes.triggerWebhook' @@ -70,8 +70,8 @@ const Panel: FC<NodePanelProps<WebhookTriggerNodeType>> = ({ }, [readOnly, inputs.webhook_url, generateWebhookUrl]) return ( - <div className='mt-2'> - <div className='space-y-4 px-4 pb-3 pt-2'> + <div className="mt-2"> + <div className="space-y-4 px-4 pb-3 pt-2"> {/* Webhook URL Section */} <Field title={t(`${i18nPrefix}.webhookUrl`)}> <div className="space-y-1"> @@ -225,7 +225,7 @@ const Panel: FC<NodePanelProps<WebhookTriggerNodeType>> = ({ <Split /> - <div className=''> + <div className=""> <OutputVars collapsed={outputVarsCollapsed} onCollapse={setOutputVarsCollapsed} diff --git a/web/app/components/workflow/nodes/trigger-webhook/types.ts b/web/app/components/workflow/nodes/trigger-webhook/types.ts index 90cfd40434..f5938ed00b 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/types.ts +++ b/web/app/components/workflow/nodes/trigger-webhook/types.ts @@ -1,4 +1,4 @@ -import type { CommonNodeType, VarType, Variable } from '@/app/components/workflow/types' +import type { CommonNodeType, Variable, VarType } from '@/app/components/workflow/types' export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' diff --git a/web/app/components/workflow/nodes/trigger-webhook/use-config.ts b/web/app/components/workflow/nodes/trigger-webhook/use-config.ts index 9b525ec758..dec79b8eaf 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/use-config.ts +++ b/web/app/components/workflow/nodes/trigger-webhook/use-config.ts @@ -1,15 +1,15 @@ -import { useCallback } from 'react' -import { produce } from 'immer' -import { useTranslation } from 'react-i18next' import type { HttpMethod, WebhookHeader, WebhookParameter, WebhookTriggerNodeType } from './types' +import type { Variable } from '@/app/components/workflow/types' +import { produce } from 'immer' +import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import { useStore as useAppStore } from '@/app/components/app/store' +import Toast from '@/app/components/base/toast' import { useNodesReadOnly, useWorkflow } from '@/app/components/workflow/hooks' import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' -import { useStore as useAppStore } from '@/app/components/app/store' -import { fetchWebhookUrl } from '@/service/apps' -import type { Variable } from '@/app/components/workflow/types' import { VarType } from '@/app/components/workflow/types' -import Toast from '@/app/components/base/toast' +import { fetchWebhookUrl } from '@/service/apps' import { checkKeys, hasDuplicateStr } from '@/utils/var' import { WEBHOOK_RAW_VARIABLE_NAME } from './utils/raw-variable' @@ -88,7 +88,7 @@ const useConfig = (id: string, payload: WebhookTriggerNodeType) => { return false } - if(hasDuplicateStr(sanitizedEntries.map(entry => entry.sanitizedName))) { + if (hasDuplicateStr(sanitizedEntries.map(entry => entry.sanitizedName))) { Toast.notify({ type: 'error', message: t('appDebug.varKeyError.keyAlreadyExists', { @@ -126,7 +126,8 @@ const useConfig = (id: string, payload: WebhookTriggerNodeType) => { // Remove variables that no longer exist in newData for this specific source type draft.variables = draft.variables.filter((v) => { // Keep variables from other sources - if (v.label !== sourceType) return true + if (v.label !== sourceType) + return true return newVarNames.has(v.variable) }) diff --git a/web/app/components/workflow/nodes/trigger-webhook/utils/raw-variable.ts b/web/app/components/workflow/nodes/trigger-webhook/utils/raw-variable.ts index 2be7d4c65f..d4975cc597 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/utils/raw-variable.ts +++ b/web/app/components/workflow/nodes/trigger-webhook/utils/raw-variable.ts @@ -1,4 +1,5 @@ -import { VarType, type Variable } from '@/app/components/workflow/types' +import type { Variable } from '@/app/components/workflow/types' +import { VarType } from '@/app/components/workflow/types' export const WEBHOOK_RAW_VARIABLE_NAME = '_webhook_raw' export const WEBHOOK_RAW_VARIABLE_LABEL = 'raw' diff --git a/web/app/components/workflow/nodes/trigger-webhook/utils/render-output-vars.tsx b/web/app/components/workflow/nodes/trigger-webhook/utils/render-output-vars.tsx index 0e9cb8a309..984ffc03dd 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/utils/render-output-vars.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/utils/render-output-vars.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' -import React from 'react' import type { Variable } from '@/app/components/workflow/types' +import React from 'react' type OutputVariablesContentProps = { variables?: Variable[] @@ -27,11 +27,14 @@ type VarItemProps = { const VarItem: FC<VarItemProps> = ({ prefix, name, type }) => { return ( - <div className='py-1'> - <div className='flex items-center leading-[18px]'> - <span className='code-sm-regular text-text-tertiary'>{prefix}.</span> - <span className='code-sm-semibold text-text-secondary'>{name}</span> - <span className='system-xs-regular ml-2 text-text-tertiary'>{type}</span> + <div className="py-1"> + <div className="flex items-center leading-[18px]"> + <span className="code-sm-regular text-text-tertiary"> + {prefix} + . + </span> + <span className="code-sm-semibold text-text-secondary">{name}</span> + <span className="system-xs-regular ml-2 text-text-tertiary">{type}</span> </div> </div> ) @@ -52,7 +55,7 @@ export const OutputVariablesContent: FC<OutputVariablesContentProps> = ({ variab const labelA = typeof a.label === 'string' ? a.label : '' const labelB = typeof b.label === 'string' ? b.label : '' return (LABEL_ORDER[labelA as keyof typeof LABEL_ORDER] || 999) - - (LABEL_ORDER[labelB as keyof typeof LABEL_ORDER] || 999) + - (LABEL_ORDER[labelB as keyof typeof LABEL_ORDER] || 999) }) return ( diff --git a/web/app/components/workflow/nodes/variable-assigner/components/add-variable/index.tsx b/web/app/components/workflow/nodes/variable-assigner/components/add-variable/index.tsx index 2f3ca14b5d..c7c2efa6ba 100644 --- a/web/app/components/workflow/nodes/variable-assigner/components/add-variable/index.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/components/add-variable/index.tsx @@ -1,23 +1,23 @@ -import { - memo, - useCallback, - useState, -} from 'react' -import { useVariableAssigner } from '../../hooks' import type { VariableAssignerNodeType } from '../../types' -import { cn } from '@/utils/classnames' -import { - PortalToFollowElem, - PortalToFollowElemContent, - PortalToFollowElemTrigger, -} from '@/app/components/base/portal-to-follow-elem' -import { Plus02 } from '@/app/components/base/icons/src/vender/line/general' -import AddVariablePopup from '@/app/components/workflow/nodes/_base/components/add-variable-popup' import type { NodeOutPutVar, ValueSelector, Var, } from '@/app/components/workflow/types' +import { + memo, + useCallback, + useState, +} from 'react' +import { Plus02 } from '@/app/components/base/icons/src/vender/line/general' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' +import AddVariablePopup from '@/app/components/workflow/nodes/_base/components/add-variable-popup' +import { cn } from '@/utils/classnames' +import { useVariableAssigner } from '../../hooks' export type AddVariableProps = { variableAssignerNodeId: string @@ -48,9 +48,10 @@ const AddVariable = ({ <div className={cn( open && '!flex', variableAssignerNodeData.selected && '!flex', - )}> + )} + > <PortalToFollowElem - placement={'right'} + placement="right" offset={4} open={open} onOpenChange={setOpen} @@ -75,7 +76,7 @@ const AddVariable = ({ /> </div> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[1000]'> + <PortalToFollowElemContent className="z-[1000]"> <AddVariablePopup onSelect={handleSelectVariable} availableVars={availableVars} diff --git a/web/app/components/workflow/nodes/variable-assigner/components/node-group-item.tsx b/web/app/components/workflow/nodes/variable-assigner/components/node-group-item.tsx index 540dfecfd9..fdffecb8f6 100644 --- a/web/app/components/workflow/nodes/variable-assigner/components/node-group-item.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/components/node-group-item.tsx @@ -1,29 +1,29 @@ -import { - memo, - useMemo, -} from 'react' -import { useTranslation } from 'react-i18next' -import { useNodes } from 'reactflow' -import { useStore } from '../../../store' -import { BlockEnum } from '../../../types' import type { Node, ValueSelector, VarType, } from '../../../types' import type { VariableAssignerNodeType } from '../types' +import { + memo, + useMemo, +} from 'react' +import { useTranslation } from 'react-i18next' +import { useNodes } from 'reactflow' +import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { + VariableLabelInNode, +} from '@/app/components/workflow/nodes/_base/components/variable/variable-label' +import { isExceptionVariable } from '@/app/components/workflow/utils' +import { cn } from '@/utils/classnames' +import { useStore } from '../../../store' +import { BlockEnum } from '../../../types' import { useGetAvailableVars, useVariableAssigner, } from '../hooks' import { filterVar } from '../utils' import AddVariable from './add-variable' -import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' -import { cn } from '@/utils/classnames' -import { isExceptionVariable } from '@/app/components/workflow/utils' -import { - VariableLabelInNode, -} from '@/app/components/workflow/nodes/_base/components/variable/variable-label' const i18nPrefix = 'workflow.nodes.variableAssigner' type GroupItem = { @@ -90,7 +90,7 @@ const NodeGroupItem = ({ onMouseEnter={() => groupEnabled && handleGroupItemMouseEnter(item.targetHandleId)} onMouseLeave={handleGroupItemMouseLeave} > - <div className='flex h-4 items-center justify-between text-[10px] font-medium text-text-tertiary'> + <div className="flex h-4 items-center justify-between text-[10px] font-medium text-text-tertiary"> <span className={cn( 'grow truncate uppercase', @@ -100,9 +100,9 @@ const NodeGroupItem = ({ > {item.title} </span> - <div className='flex items-center'> - <span className='ml-2 shrink-0'>{item.type}</span> - <div className='ml-2 mr-1 h-2.5 w-[1px] bg-divider-regular'></div> + <div className="flex items-center"> + <span className="ml-2 shrink-0">{item.type}</span> + <div className="ml-2 mr-1 h-2.5 w-[1px] bg-divider-regular"></div> <AddVariable availableVars={availableVars} variableAssignerNodeId={item.variableAssignerNodeId} @@ -125,7 +125,7 @@ const NodeGroupItem = ({ } { !!item.variables.length && ( - <div className='space-y-0.5'> + <div className="space-y-0.5"> { item.variables.map((variable = [], index) => { const isSystem = isSystemVar(variable) diff --git a/web/app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx b/web/app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx index f891f44e7a..d722c1d231 100644 --- a/web/app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx @@ -1,17 +1,17 @@ +import type { Node, ValueSelector } from '@/app/components/workflow/types' import { memo, useMemo, } from 'react' import { useTranslation } from 'react-i18next' -import { cn } from '@/utils/classnames' -import { VarBlockIcon } from '@/app/components/workflow/block-icon' -import { Line3 } from '@/app/components/base/icons/src/public/common' -import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' -import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others' import Badge from '@/app/components/base/badge' -import type { Node, ValueSelector } from '@/app/components/workflow/types' -import { isConversationVar, isENV, isRagVariableVar, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { Line3 } from '@/app/components/base/icons/src/public/common' +import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others' import { InputField } from '@/app/components/base/icons/src/vender/pipeline' +import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' +import { VarBlockIcon } from '@/app/components/workflow/block-icon' +import { isConversationVar, isENV, isRagVariableVar, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { cn } from '@/utils/classnames' type NodeVariableItemProps = { node: Node @@ -39,9 +39,9 @@ const NodeVariableItem = ({ const isChatVar = isConversationVar(variable) const isRagVar = isRagVariableVar(variable) const varName = useMemo(() => { - if(isSystem) + if (isSystem) return `sys.${variable[variable.length - 1]}` - if(isRagVar) + if (isRagVar) return variable[variable.length - 1] return variable.slice(1).join('.') }, [isRagVar, isSystem, variable]) @@ -49,19 +49,19 @@ const NodeVariableItem = ({ const VariableIcon = useMemo(() => { if (isEnv) { return ( - <Env className='h-3.5 w-3.5 shrink-0 text-util-colors-violet-violet-600' /> + <Env className="h-3.5 w-3.5 shrink-0 text-util-colors-violet-violet-600" /> ) } if (isChatVar) { return ( - <BubbleX className='h-3.5 w-3.5 shrink-0 text-util-colors-teal-teal-700' /> + <BubbleX className="h-3.5 w-3.5 shrink-0 text-util-colors-teal-teal-700" /> ) } - if(isRagVar) { + if (isRagVar) { return ( - <InputField className='h-3.5 w-3.5 shrink-0 text-text-accent' /> + <InputField className="h-3.5 w-3.5 shrink-0 text-text-accent" /> ) } @@ -95,31 +95,32 @@ const NodeVariableItem = ({ 'relative flex items-center gap-1 self-stretch rounded-md bg-workflow-block-parma-bg p-[3px] pl-[5px]', showBorder && '!bg-state-base-hover', className, - )}> - <div className='flex w-0 grow items-center'> + )} + > + <div className="flex w-0 grow items-center"> { node && ( <> - <div className='shrink-0 p-[1px]'> + <div className="shrink-0 p-[1px]"> <VarBlockIcon - className='!text-text-primary' + className="!text-text-primary" type={node.data.type} /> </div> <div - className='mx-0.5 shrink-[1000] truncate text-xs font-medium text-text-secondary' + className="mx-0.5 shrink-[1000] truncate text-xs font-medium text-text-secondary" title={node?.data.title} > {node?.data.title} </div> - <Line3 className='mr-0.5 shrink-0'></Line3> + <Line3 className="mr-0.5 shrink-0"></Line3> </> ) } {VariableIcon} {VariableName} </div> - {writeMode && <Badge className='shrink-0' text={t(`${i18nPrefix}.operations.${writeMode}`)} />} + {writeMode && <Badge className="shrink-0" text={t(`${i18nPrefix}.operations.${writeMode}`)} />} </div> ) } diff --git a/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx b/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx index 90a30f4845..277c44744b 100644 --- a/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx @@ -1,22 +1,22 @@ 'use client' -import React, { useCallback } from 'react' import type { ChangeEvent, FC } from 'react' -import { useTranslation } from 'react-i18next' -import { produce } from 'immer' -import { useBoolean } from 'ahooks' +import type { VarGroupItem as VarGroupItemType } from '../types' +import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' import { RiDeleteBinLine, } from '@remixicon/react' -import type { VarGroupItem as VarGroupItemType } from '../types' +import { useBoolean } from 'ahooks' +import { produce } from 'immer' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import { Folder } from '@/app/components/base/icons/src/vender/line/files' +import Toast from '@/app/components/base/toast' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' +import { VarType } from '@/app/components/workflow/types' +import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' import VarReferencePicker from '../../_base/components/variable/var-reference-picker' import VarList from '../components/var-list' -import Field from '@/app/components/workflow/nodes/_base/components/field' -import { VarType } from '@/app/components/workflow/types' -import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' -import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' -import { Folder } from '@/app/components/base/icons/src/vender/line/files' -import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' -import Toast from '@/app/components/base/toast' const i18nPrefix = 'workflow.nodes.variableAssigner' @@ -104,67 +104,72 @@ const VarGroupItem: FC<Props> = ({ return ( <Field - className='group' + className="group" title={groupEnabled - ? <div className='flex items-center'> - <div className='flex items-center !normal-case'> - <Folder className='mr-0.5 h-3.5 w-3.5' /> - {(!isEditGroupName) - ? ( - <div className='system-sm-semibold flex h-6 cursor-text items-center rounded-lg px-1 text-text-secondary hover:bg-gray-100' onClick={setEditGroupName}> - {payload.group_name} - </div> - ) - : ( - <input - type='text' - className='h-6 rounded-lg border border-gray-300 bg-white px-1 focus:outline-none' - // style={{ - // width: `${((payload.group_name?.length || 0) + 1) / 2}em`, - // }} - size={payload.group_name?.length} // to fit the input width - autoFocus - value={payload.group_name} - onChange={handleGroupNameChange} - onBlur={setNotEditGroupName} - maxLength={30} - />)} + ? ( + <div className="flex items-center"> + <div className="flex items-center !normal-case"> + <Folder className="mr-0.5 h-3.5 w-3.5" /> + {(!isEditGroupName) + ? ( + <div className="system-sm-semibold flex h-6 cursor-text items-center rounded-lg px-1 text-text-secondary hover:bg-gray-100" onClick={setEditGroupName}> + {payload.group_name} + </div> + ) + : ( + <input + type="text" + className="h-6 rounded-lg border border-gray-300 bg-white px-1 focus:outline-none" + // style={{ + // width: `${((payload.group_name?.length || 0) + 1) / 2}em`, + // }} + size={payload.group_name?.length} // to fit the input width + autoFocus + value={payload.group_name} + onChange={handleGroupNameChange} + onBlur={setNotEditGroupName} + maxLength={30} + /> + )} - </div> - {canRemove && ( - <div - className='ml-0.5 hidden cursor-pointer rounded-md p-1 text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive group-hover:block' - onClick={onRemove} - > - <RiDeleteBinLine - className='h-4 w-4' - /> + </div> + {canRemove && ( + <div + className="ml-0.5 hidden cursor-pointer rounded-md p-1 text-text-tertiary hover:bg-state-destructive-hover hover:text-text-destructive group-hover:block" + onClick={onRemove} + > + <RiDeleteBinLine + className="h-4 w-4" + /> + </div> + )} </div> - )} - </div> + ) : t(`${i18nPrefix}.title`)!} - operations={ - <div className='flex h-6 items-center space-x-2'> + operations={( + <div className="flex h-6 items-center space-x-2"> {payload.variables.length > 0 && ( - <div className='system-2xs-medium-uppercase flex h-[18px] items-center rounded-[5px] border border-divider-deep px-1 text-text-tertiary'>{payload.output_type}</div> + <div className="system-2xs-medium-uppercase flex h-[18px] items-center rounded-[5px] border border-divider-deep px-1 text-text-tertiary">{payload.output_type}</div> )} { !readOnly - ? <VarReferencePicker - isAddBtnTrigger - readonly={false} - nodeId={nodeId} - isShowNodeName - value={[]} - onChange={handleAddVariable} - defaultVarKindType={VarKindType.variable} - filterVar={filterVar} - availableVars={availableVars} - /> + ? ( + <VarReferencePicker + isAddBtnTrigger + readonly={false} + nodeId={nodeId} + isShowNodeName + value={[]} + onChange={handleAddVariable} + defaultVarKindType={VarKindType.variable} + filterVar={filterVar} + availableVars={availableVars} + /> + ) : undefined } </div> - } + )} > <VarList readonly={readOnly} diff --git a/web/app/components/workflow/nodes/variable-assigner/components/var-list/index.tsx b/web/app/components/workflow/nodes/variable-assigner/components/var-list/index.tsx index d38ee51465..a767e704fe 100644 --- a/web/app/components/workflow/nodes/variable-assigner/components/var-list/index.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/components/var-list/index.tsx @@ -1,14 +1,14 @@ 'use client' import type { FC } from 'react' -import { useTranslation } from 'react-i18next' -import React, { useCallback } from 'react' -import { produce } from 'immer' -import RemoveButton from '../../../_base/components/remove-button' -import ListNoDataPlaceholder from '../../../_base/components/list-no-data-placeholder' -import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' import type { ValueSelector, Var } from '@/app/components/workflow/types' -import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' +import { produce } from 'immer' import { noop } from 'lodash-es' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' +import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' +import ListNoDataPlaceholder from '../../../_base/components/list-no-data-placeholder' +import RemoveButton from '../../../_base/components/remove-button' type Props = { readonly: boolean @@ -59,14 +59,14 @@ const VarList: FC<Props> = ({ } return ( - <div className='space-y-2'> + <div className="space-y-2"> {list.map((item, index) => ( - <div className='flex items-center space-x-1' key={index}> + <div className="flex items-center space-x-1" key={index}> <VarReferencePicker readonly={readonly} nodeId={nodeId} isShowNodeName - className='grow' + className="grow" value={item} onChange={handleVarReferenceChange(index)} onOpen={handleOpen(index)} diff --git a/web/app/components/workflow/nodes/variable-assigner/components/var-list/use-var-list.ts b/web/app/components/workflow/nodes/variable-assigner/components/var-list/use-var-list.ts index 7e781a4068..71d0fb72ea 100644 --- a/web/app/components/workflow/nodes/variable-assigner/components/var-list/use-var-list.ts +++ b/web/app/components/workflow/nodes/variable-assigner/components/var-list/use-var-list.ts @@ -1,7 +1,7 @@ -import { useCallback } from 'react' -import { produce } from 'immer' import type { VariableAssignerNodeType } from '../../types' import type { ValueSelector } from '@/app/components/workflow/types' +import { produce } from 'immer' +import { useCallback } from 'react' type Params = { id: string diff --git a/web/app/components/workflow/nodes/variable-assigner/default.ts b/web/app/components/workflow/nodes/variable-assigner/default.ts index 825bad5b9c..7cc723be99 100644 --- a/web/app/components/workflow/nodes/variable-assigner/default.ts +++ b/web/app/components/workflow/nodes/variable-assigner/default.ts @@ -1,8 +1,9 @@ -import { type NodeDefault, VarType } from '../../types' +import type { NodeDefault } from '../../types' import type { VariableAssignerNodeType } from './types' -import { genNodeMetaData } from '@/app/components/workflow/utils' -import { BlockEnum } from '@/app/components/workflow/types' import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { genNodeMetaData } from '@/app/components/workflow/utils' +import { VarType } from '../../types' const i18nPrefix = 'workflow' diff --git a/web/app/components/workflow/nodes/variable-assigner/hooks.ts b/web/app/components/workflow/nodes/variable-assigner/hooks.ts index 29e0ee16d1..b20cee79c7 100644 --- a/web/app/components/workflow/nodes/variable-assigner/hooks.ts +++ b/web/app/components/workflow/nodes/variable-assigner/hooks.ts @@ -1,27 +1,27 @@ -import { useCallback } from 'react' -import { - useStoreApi, -} from 'reactflow' -import { useNodes } from 'reactflow' +import type { + Node, + ValueSelector, + Var, +} from '../../types' +import type { + VarGroupItem, + VariableAssignerNodeType, +} from './types' +import { produce } from 'immer' import { uniqBy } from 'lodash-es' -import { produce } from 'immer' +import { useCallback } from 'react' +import { + useNodes, + useStoreApi, +} from 'reactflow' import { useIsChatMode, useNodeDataUpdate, useWorkflow, useWorkflowVariables, } from '../../hooks' -import type { - Node, - ValueSelector, - Var, -} from '../../types' import { useWorkflowStore } from '../../store' -import type { - VarGroupItem, - VariableAssignerNodeType, -} from './types' export const useVariableAssigner = () => { const store = useStoreApi() diff --git a/web/app/components/workflow/nodes/variable-assigner/node.tsx b/web/app/components/workflow/nodes/variable-assigner/node.tsx index 5246ba2a8a..70223c7c7e 100644 --- a/web/app/components/workflow/nodes/variable-assigner/node.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/node.tsx @@ -1,13 +1,13 @@ import type { FC } from 'react' +import type { NodeProps } from 'reactflow' +import type { VariableAssignerNodeType } from './types' import { memo, useMemo, useRef, } from 'react' -import type { NodeProps } from 'reactflow' import { useTranslation } from 'react-i18next' import NodeGroupItem from './components/node-group-item' -import type { VariableAssignerNodeType } from './types' const i18nPrefix = 'workflow.nodes.variableAssigner' @@ -43,7 +43,7 @@ const Node: FC<NodeProps<VariableAssignerNodeType>> = (props) => { }, [t, advanced_settings, data, id]) return ( - <div className='relative mb-1 space-y-0.5 px-1' ref={ref}> + <div className="relative mb-1 space-y-0.5 px-1" ref={ref}> { groups.map((item) => { return ( @@ -54,7 +54,7 @@ const Node: FC<NodeProps<VariableAssignerNodeType>> = (props) => { ) }) } - </div > + </div> ) } diff --git a/web/app/components/workflow/nodes/variable-assigner/panel.tsx b/web/app/components/workflow/nodes/variable-assigner/panel.tsx index a605808f95..0a6c1c3c84 100644 --- a/web/app/components/workflow/nodes/variable-assigner/panel.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/panel.tsx @@ -1,17 +1,17 @@ import type { FC } from 'react' +import type { VariableAssignerNodeType } from './types' +import type { NodePanelProps } from '@/app/components/workflow/types' import React from 'react' import { useTranslation } from 'react-i18next' -import Field from '../_base/components/field' -import RemoveEffectVarConfirm from '../_base/components/remove-effect-var-confirm' -import useConfig from './use-config' -import type { VariableAssignerNodeType } from './types' -import VarGroupItem from './components/var-group-item' -import { cn } from '@/utils/classnames' -import type { NodePanelProps } from '@/app/components/workflow/types' -import Split from '@/app/components/workflow/nodes/_base/components/split' -import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' import Switch from '@/app/components/base/switch' import AddButton from '@/app/components/workflow/nodes/_base/components/add-button' +import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' +import Split from '@/app/components/workflow/nodes/_base/components/split' +import { cn } from '@/utils/classnames' +import Field from '../_base/components/field' +import RemoveEffectVarConfirm from '../_base/components/remove-effect-var-confirm' +import VarGroupItem from './components/var-group-item' +import useConfig from './use-config' const i18nPrefix = 'workflow.nodes.variableAssigner' const Panel: FC<NodePanelProps<VariableAssignerNodeType>> = ({ @@ -38,62 +38,64 @@ const Panel: FC<NodePanelProps<VariableAssignerNodeType>> = ({ } = useConfig(id, data) return ( - <div className='mt-2'> - <div className='space-y-4 px-4 pb-4'> + <div className="mt-2"> + <div className="space-y-4 px-4 pb-4"> {!isEnableGroup ? ( - <VarGroupItem - readOnly={readOnly} - nodeId={id} - payload={{ - output_type: inputs.output_type, - variables: inputs.variables, - }} - onChange={handleListOrTypeChange} - groupEnabled={false} - availableVars={getAvailableVars(id, 'target', filterVar(inputs.output_type), true)} - /> - ) - : (<div> - <div className='space-y-2'> - {inputs.advanced_settings?.groups.map((item, index) => ( - <div key={item.groupId}> - <VarGroupItem - readOnly={readOnly} - nodeId={id} - payload={item} - onChange={handleListOrTypeChangeInGroup(item.groupId)} - groupEnabled - canRemove={!readOnly && inputs.advanced_settings?.groups.length > 1} - onRemove={handleGroupRemoved(item.groupId)} - onGroupNameChange={handleVarGroupNameChange(item.groupId)} - availableVars={getAvailableVars(id, item.groupId, filterVar(item.output_type), true)} - /> - {index !== inputs.advanced_settings?.groups.length - 1 && <Split className='my-4' />} - </div> + <VarGroupItem + readOnly={readOnly} + nodeId={id} + payload={{ + output_type: inputs.output_type, + variables: inputs.variables, + }} + onChange={handleListOrTypeChange} + groupEnabled={false} + availableVars={getAvailableVars(id, 'target', filterVar(inputs.output_type), true)} + /> + ) + : ( + <div> + <div className="space-y-2"> + {inputs.advanced_settings?.groups.map((item, index) => ( + <div key={item.groupId}> + <VarGroupItem + readOnly={readOnly} + nodeId={id} + payload={item} + onChange={handleListOrTypeChangeInGroup(item.groupId)} + groupEnabled + canRemove={!readOnly && inputs.advanced_settings?.groups.length > 1} + onRemove={handleGroupRemoved(item.groupId)} + onGroupNameChange={handleVarGroupNameChange(item.groupId)} + availableVars={getAvailableVars(id, item.groupId, filterVar(item.output_type), true)} + /> + {index !== inputs.advanced_settings?.groups.length - 1 && <Split className="my-4" />} + </div> - ))} - </div> - <AddButton - className='mt-2' - text={t(`${i18nPrefix}.addGroup`)} - onClick={handleAddGroup} - /> - </div>)} + ))} + </div> + <AddButton + className="mt-2" + text={t(`${i18nPrefix}.addGroup`)} + onClick={handleAddGroup} + /> + </div> + )} </div> <Split /> <div className={cn('px-4 pt-4', isEnableGroup ? 'pb-4' : 'pb-2')}> <Field title={t(`${i18nPrefix}.aggregationGroup`)} tooltip={t(`${i18nPrefix}.aggregationGroupTip`)!} - operations={ + operations={( <Switch defaultValue={isEnableGroup} onChange={handleGroupEnabledChange} - size='md' + size="md" disabled={readOnly} /> - } + )} /> </div> {isEnableGroup && ( diff --git a/web/app/components/workflow/nodes/variable-assigner/use-config.ts b/web/app/components/workflow/nodes/variable-assigner/use-config.ts index 583f779575..286eed523d 100644 --- a/web/app/components/workflow/nodes/variable-assigner/use-config.ts +++ b/web/app/components/workflow/nodes/variable-assigner/use-config.ts @@ -1,19 +1,19 @@ -import { useCallback, useRef, useState } from 'react' -import { produce } from 'immer' -import { useBoolean, useDebounceFn } from 'ahooks' -import { v4 as uuid4 } from 'uuid' import type { ValueSelector, Var } from '../../types' -import { VarType } from '../../types' import type { VarGroupItem, VariableAssignerNodeType } from './types' -import { useGetAvailableVars } from './hooks' -import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' - +import { useBoolean, useDebounceFn } from 'ahooks' +import { produce } from 'immer' +import { useCallback, useRef, useState } from 'react' +import { v4 as uuid4 } from 'uuid' import { useNodesReadOnly, useWorkflow, } from '@/app/components/workflow/hooks' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import useInspectVarsCrud from '../../hooks/use-inspect-vars-crud' +import { VarType } from '../../types' +import { useGetAvailableVars } from './hooks' + const useConfig = (id: string, payload: VariableAssignerNodeType) => { const { deleteNodeInspectorVars, @@ -165,7 +165,7 @@ const useConfig = (id: string, payload: VariableAssignerNodeType) => { }) handleOutVarRenameChange(id, [id, inputs.advanced_settings.groups[index].group_name, 'output'], [id, name, 'output']) setInputs(newInputs) - if(!(id in oldNameRecord.current)) + if (!(id in oldNameRecord.current)) oldNameRecord.current[id] = inputs.advanced_settings.groups[index].group_name renameInspectNameWithDebounce(id, name) } diff --git a/web/app/components/workflow/nodes/variable-assigner/use-single-run-form-params.ts b/web/app/components/workflow/nodes/variable-assigner/use-single-run-form-params.ts index 8e67675d3e..874c546fd8 100644 --- a/web/app/components/workflow/nodes/variable-assigner/use-single-run-form-params.ts +++ b/web/app/components/workflow/nodes/variable-assigner/use-single-run-form-params.ts @@ -1,11 +1,11 @@ import type { RefObject } from 'react' +import type { VariableAssignerNodeType } from './types' import type { InputVar, ValueSelector, Variable } from '@/app/components/workflow/types' import { useCallback } from 'react' -import type { VariableAssignerNodeType } from './types' type Params = { - id: string, - payload: VariableAssignerNodeType, + id: string + payload: VariableAssignerNodeType runInputData: Record<string, any> runInputDataRef: RefObject<Record<string, any>> getInputVars: (textList: string[]) => InputVar[] @@ -52,7 +52,7 @@ const useSingleRunFormParams = ({ const existVarsKey: Record<string, boolean> = {} const uniqueVarInputs: InputVar[] = [] varInputs.forEach((input) => { - if(!input) + if (!input) return if (!existVarsKey[input.variable]) { existVarsKey[input.variable] = true @@ -72,10 +72,10 @@ const useSingleRunFormParams = ({ })() const getDependentVars = () => { - if(payload.advanced_settings?.group_enabled) { + if (payload.advanced_settings?.group_enabled) { const vars: ValueSelector[][] = [] payload.advanced_settings.groups.forEach((group) => { - if(group.variables) + if (group.variables) vars.push([...group.variables]) }) return vars diff --git a/web/app/components/workflow/note-node/constants.ts b/web/app/components/workflow/note-node/constants.ts index 223ce72e77..b2fa223690 100644 --- a/web/app/components/workflow/note-node/constants.ts +++ b/web/app/components/workflow/note-node/constants.ts @@ -2,7 +2,7 @@ import { NoteTheme } from './types' export const CUSTOM_NOTE_NODE = 'custom-note' -export const THEME_MAP: Record<string, { outer: string; title: string; bg: string; border: string }> = { +export const THEME_MAP: Record<string, { outer: string, title: string, bg: string, border: string }> = { [NoteTheme.blue]: { outer: 'border-util-colors-blue-blue-500', title: 'bg-util-colors-blue-blue-100', diff --git a/web/app/components/workflow/note-node/hooks.ts b/web/app/components/workflow/note-node/hooks.ts index 29642f90df..6924f31af5 100644 --- a/web/app/components/workflow/note-node/hooks.ts +++ b/web/app/components/workflow/note-node/hooks.ts @@ -1,7 +1,7 @@ -import { useCallback } from 'react' import type { EditorState } from 'lexical' -import { WorkflowHistoryEvent, useNodeDataUpdate, useWorkflowHistory } from '../hooks' import type { NoteTheme } from './types' +import { useCallback } from 'react' +import { useNodeDataUpdate, useWorkflowHistory, WorkflowHistoryEvent } from '../hooks' export const useNote = (id: string) => { const { handleNodeDataUpdateWithSyncDraft } = useNodeDataUpdate() diff --git a/web/app/components/workflow/note-node/index.tsx b/web/app/components/workflow/note-node/index.tsx index 6b81a0fe1f..e482d240a2 100644 --- a/web/app/components/workflow/note-node/index.tsx +++ b/web/app/components/workflow/note-node/index.tsx @@ -1,26 +1,26 @@ +import type { NodeProps } from 'reactflow' +import type { NoteNodeType } from './types' +import { useClickAway } from 'ahooks' import { memo, useRef, } from 'react' import { useTranslation } from 'react-i18next' -import { useClickAway } from 'ahooks' -import type { NodeProps } from 'reactflow' -import NodeResizer from '../nodes/_base/components/node-resizer' -import { useWorkflowHistoryStore } from '../workflow-history-store' +import { cn } from '@/utils/classnames' import { useNodeDataUpdate, useNodesInteractions, } from '../hooks' +import NodeResizer from '../nodes/_base/components/node-resizer' import { useStore } from '../store' +import { useWorkflowHistoryStore } from '../workflow-history-store' +import { THEME_MAP } from './constants' +import { useNote } from './hooks' import { NoteEditor, NoteEditorContextProvider, NoteEditorToolbar, } from './note-editor' -import { THEME_MAP } from './constants' -import { useNote } from './hooks' -import type { NoteNodeType } from './types' -import { cn } from '@/utils/classnames' const Icon = () => { return ( @@ -90,10 +90,12 @@ const NoteNode = ({ className={cn( 'h-2 shrink-0 rounded-t-md opacity-50', THEME_MAP[theme].title, - )}></div> + )} + > + </div> { data.selected && !data._isTempNode && ( - <div className='absolute left-1/2 top-[-41px] -translate-x-1/2'> + <div className="absolute left-1/2 top-[-41px] -translate-x-1/2"> <NoteEditorToolbar theme={theme} onThemeChange={handleThemeChange} @@ -106,10 +108,11 @@ const NoteNode = ({ </div> ) } - <div className='grow overflow-y-auto px-3 py-2.5'> + <div className="grow overflow-y-auto px-3 py-2.5"> <div className={cn( data.selected && 'nodrag nopan nowheel cursor-text', - )}> + )} + > <NoteEditor containerElement={ref.current} placeholder={t('workflow.nodes.note.editor.placeholder') || ''} @@ -120,7 +123,7 @@ const NoteNode = ({ </div> { data.showAuthor && ( - <div className='p-3 pt-0 text-xs text-text-tertiary'> + <div className="p-3 pt-0 text-xs text-text-tertiary"> {data.author} </div> ) diff --git a/web/app/components/workflow/note-node/note-editor/context.tsx b/web/app/components/workflow/note-node/note-editor/context.tsx index c9e7eb56c8..362693f4a4 100644 --- a/web/app/components/workflow/note-node/note-editor/context.tsx +++ b/web/app/components/workflow/note-node/note-editor/context.tsx @@ -1,16 +1,16 @@ 'use client' -import { - createContext, - memo, - useRef, -} from 'react' -import { LexicalComposer } from '@lexical/react/LexicalComposer' import { LinkNode } from '@lexical/link' import { ListItemNode, ListNode, } from '@lexical/list' +import { LexicalComposer } from '@lexical/react/LexicalComposer' +import { + createContext, + memo, + useRef, +} from 'react' import { createNoteEditorStore } from './store' import theme from './theme' diff --git a/web/app/components/workflow/note-node/note-editor/editor.tsx b/web/app/components/workflow/note-node/note-editor/editor.tsx index c91988c532..bdabd8d2e7 100644 --- a/web/app/components/workflow/note-node/note-editor/editor.tsx +++ b/web/app/components/workflow/note-node/note-editor/editor.tsx @@ -1,22 +1,22 @@ 'use client' +import type { EditorState } from 'lexical' +import { ClickableLinkPlugin } from '@lexical/react/LexicalClickableLinkPlugin' +import { ContentEditable } from '@lexical/react/LexicalContentEditable' +import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary' +import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin' +import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin' +import { ListPlugin } from '@lexical/react/LexicalListPlugin' +import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin' +import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin' import { memo, useCallback, } from 'react' -import type { EditorState } from 'lexical' -import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin' -import { ContentEditable } from '@lexical/react/LexicalContentEditable' -import { ClickableLinkPlugin } from '@lexical/react/LexicalClickableLinkPlugin' -import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin' -import { ListPlugin } from '@lexical/react/LexicalListPlugin' -import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary' -import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin' -import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin' -import LinkEditorPlugin from './plugins/link-editor-plugin' -import FormatDetectorPlugin from './plugins/format-detector-plugin' // import TreeView from '@/app/components/base/prompt-editor/plugins/tree-view' import Placeholder from '@/app/components/base/prompt-editor/plugins/placeholder' +import FormatDetectorPlugin from './plugins/format-detector-plugin' +import LinkEditorPlugin from './plugins/link-editor-plugin' type EditorProps = { placeholder?: string @@ -35,18 +35,18 @@ const Editor = ({ }, [onChange]) return ( - <div className='relative'> + <div className="relative"> <RichTextPlugin - contentEditable={ + contentEditable={( <div> <ContentEditable onFocus={() => setShortcutsEnabled?.(false)} onBlur={() => setShortcutsEnabled?.(true)} spellCheck={false} - className='h-full w-full text-text-secondary caret-primary-600 outline-none' + className="h-full w-full text-text-secondary caret-primary-600 outline-none" /> </div> - } + )} placeholder={<Placeholder value={placeholder} compact />} ErrorBoundary={LexicalErrorBoundary} /> diff --git a/web/app/components/workflow/note-node/note-editor/plugins/format-detector-plugin/hooks.ts b/web/app/components/workflow/note-node/note-editor/plugins/format-detector-plugin/hooks.ts index bc7e855c3b..0ad7e78283 100644 --- a/web/app/components/workflow/note-node/note-editor/plugins/format-detector-plugin/hooks.ts +++ b/web/app/components/workflow/note-node/note-editor/plugins/format-detector-plugin/hooks.ts @@ -1,18 +1,18 @@ -import { - useCallback, - useEffect, -} from 'react' +import type { LinkNode } from '@lexical/link' +import { $isLinkNode } from '@lexical/link' +import { $isListItemNode } from '@lexical/list' +import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' +import { mergeRegister } from '@lexical/utils' import { $getSelection, $isRangeSelection, } from 'lexical' -import { mergeRegister } from '@lexical/utils' -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' -import type { LinkNode } from '@lexical/link' -import { $isLinkNode } from '@lexical/link' -import { $isListItemNode } from '@lexical/list' -import { getSelectedNode } from '../../utils' +import { + useCallback, + useEffect, +} from 'react' import { useNoteEditorStore } from '../../store' +import { getSelectedNode } from '../../utils' export const useFormatDetector = () => { const [editor] = useLexicalComposerContext() diff --git a/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/component.tsx b/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/component.tsx index 556b7409c7..3f5472ad56 100644 --- a/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/component.tsx +++ b/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/component.tsx @@ -1,27 +1,27 @@ import { - memo, - useEffect, - useState, -} from 'react' -import { escape } from 'lodash-es' -import { - FloatingPortal, flip, + FloatingPortal, offset, shift, useFloating, } from '@floating-ui/react' -import { useTranslation } from 'react-i18next' -import { useClickAway } from 'ahooks' import { RiEditLine, RiExternalLinkLine, RiLinkUnlinkM, } from '@remixicon/react' +import { useClickAway } from 'ahooks' +import { escape } from 'lodash-es' +import { + memo, + useEffect, + useState, +} from 'react' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' +import { cn } from '@/utils/classnames' import { useStore } from '../../store' import { useLink } from './hooks' -import { cn } from '@/utils/classnames' -import Button from '@/app/components/base/button' type LinkEditorComponentProps = { containerElement: HTMLDivElement | null @@ -80,15 +80,15 @@ const LinkEditorComponent = ({ !linkOperatorShow && ( <> <input - className='mr-0.5 h-6 w-[196px] appearance-none rounded-sm bg-transparent p-1 text-[13px] text-components-input-text-filled outline-none' + className="mr-0.5 h-6 w-[196px] appearance-none rounded-sm bg-transparent p-1 text-[13px] text-components-input-text-filled outline-none" value={url} onChange={e => setUrl(e.target.value)} placeholder={t('workflow.nodes.note.editor.enterUrl') || ''} autoFocus /> <Button - variant='primary' - size='small' + variant="primary" + size="small" disabled={!url} onClick={() => handleSaveLink(url)} > @@ -101,38 +101,38 @@ const LinkEditorComponent = ({ linkOperatorShow && ( <> <a - className='flex h-6 items-center rounded-md px-2 hover:bg-state-base-hover' + className="flex h-6 items-center rounded-md px-2 hover:bg-state-base-hover" href={escape(url)} - target='_blank' - rel='noreferrer' + target="_blank" + rel="noreferrer" > - <RiExternalLinkLine className='mr-1 h-3 w-3' /> - <div className='mr-1'> + <RiExternalLinkLine className="mr-1 h-3 w-3" /> + <div className="mr-1"> {t('workflow.nodes.note.editor.openLink')} </div> <div title={escape(url)} - className='max-w-[140px] truncate text-text-accent' + className="max-w-[140px] truncate text-text-accent" > {escape(url)} </div> </a> - <div className='mx-1 h-3.5 w-[1px] bg-divider-regular'></div> + <div className="mx-1 h-3.5 w-[1px] bg-divider-regular"></div> <div - className='mr-0.5 flex h-6 cursor-pointer items-center rounded-md px-2 hover:bg-state-base-hover' + className="mr-0.5 flex h-6 cursor-pointer items-center rounded-md px-2 hover:bg-state-base-hover" onClick={(e) => { e.stopPropagation() setLinkOperatorShow(false) }} > - <RiEditLine className='mr-1 h-3 w-3' /> + <RiEditLine className="mr-1 h-3 w-3" /> {t('common.operation.edit')} </div> <div - className='flex h-6 cursor-pointer items-center rounded-md px-2 hover:bg-state-base-hover' + className="flex h-6 cursor-pointer items-center rounded-md px-2 hover:bg-state-base-hover" onClick={handleUnlink} > - <RiLinkUnlinkM className='mr-1 h-3 w-3' /> + <RiLinkUnlinkM className="mr-1 h-3 w-3" /> {t('workflow.nodes.note.editor.unlink')} </div> </> diff --git a/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/hooks.ts b/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/hooks.ts index 8be8b55196..2c6e014b15 100644 --- a/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/hooks.ts +++ b/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/hooks.ts @@ -1,23 +1,23 @@ +import { + TOGGLE_LINK_COMMAND, +} from '@lexical/link' +import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' +import { + mergeRegister, +} from '@lexical/utils' +import { + CLICK_COMMAND, + COMMAND_PRIORITY_LOW, +} from 'lexical' +import { escape } from 'lodash-es' import { useCallback, useEffect, } from 'react' import { useTranslation } from 'react-i18next' -import { - CLICK_COMMAND, - COMMAND_PRIORITY_LOW, -} from 'lexical' -import { - mergeRegister, -} from '@lexical/utils' -import { - TOGGLE_LINK_COMMAND, -} from '@lexical/link' -import { escape } from 'lodash-es' -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' +import { useToastContext } from '@/app/components/base/toast' import { useNoteEditorStore } from '../../store' import { urlRegExp } from '../../utils' -import { useToastContext } from '@/app/components/base/toast' export const useOpenLink = () => { const [editor] = useLexicalComposerContext() diff --git a/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/index.tsx b/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/index.tsx index a5b3df6504..e7d4a99cfb 100644 --- a/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/index.tsx +++ b/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/index.tsx @@ -2,8 +2,8 @@ import { memo, } from 'react' import { useStore } from '../../store' -import { useOpenLink } from './hooks' import LinkEditorComponent from './component' +import { useOpenLink } from './hooks' type LinkEditorPluginProps = { containerElement: HTMLDivElement | null diff --git a/web/app/components/workflow/note-node/note-editor/toolbar/color-picker.tsx b/web/app/components/workflow/note-node/note-editor/toolbar/color-picker.tsx index ccf70fdc90..f88848ad9d 100644 --- a/web/app/components/workflow/note-node/note-editor/toolbar/color-picker.tsx +++ b/web/app/components/workflow/note-node/note-editor/toolbar/color-picker.tsx @@ -2,14 +2,14 @@ import { memo, useState, } from 'react' -import { NoteTheme } from '../../types' -import { THEME_MAP } from '../../constants' -import { cn } from '@/utils/classnames' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' +import { cn } from '@/utils/classnames' +import { THEME_MAP } from '../../constants' +import { NoteTheme } from '../../types' export const COLOR_LIST = [ { @@ -58,29 +58,31 @@ const ColorPicker = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='top' + placement="top" offset={4} > <PortalToFollowElemTrigger onClick={() => setOpen(!open)}> <div className={cn( 'flex h-8 w-8 cursor-pointer items-center justify-center rounded-md hover:bg-black/5', open && 'bg-black/5', - )}> + )} + > <div className={cn( 'h-4 w-4 rounded-full border border-black/5', THEME_MAP[theme].title, )} - ></div> + > + </div> </div> </PortalToFollowElemTrigger> <PortalToFollowElemContent> - <div className='grid grid-cols-3 grid-rows-2 gap-0.5 rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-lg'> + <div className="grid grid-cols-3 grid-rows-2 gap-0.5 rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-lg"> { COLOR_LIST.map(color => ( <div key={color.key} - className='group relative flex h-8 w-8 cursor-pointer items-center justify-center rounded-md' + className="group relative flex h-8 w-8 cursor-pointer items-center justify-center rounded-md" onClick={(e) => { e.stopPropagation() onThemeChange(color.key) @@ -92,13 +94,15 @@ const ColorPicker = ({ 'absolute left-1/2 top-1/2 hidden h-5 w-5 -translate-x-1/2 -translate-y-1/2 rounded-full border-[1.5px] group-hover:block', color.outer, )} - ></div> + > + </div> <div className={cn( 'absolute left-1/2 top-1/2 h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border border-black/5', color.inner, )} - ></div> + > + </div> </div> )) } diff --git a/web/app/components/workflow/note-node/note-editor/toolbar/command.tsx b/web/app/components/workflow/note-node/note-editor/toolbar/command.tsx index 1a1d8f20ec..c0d7eb9d95 100644 --- a/web/app/components/workflow/note-node/note-editor/toolbar/command.tsx +++ b/web/app/components/workflow/note-node/note-editor/toolbar/command.tsx @@ -1,8 +1,3 @@ -import { - memo, - useMemo, -} from 'react' -import { useTranslation } from 'react-i18next' import { RiBold, RiItalic, @@ -10,10 +5,15 @@ import { RiListUnordered, RiStrikethrough, } from '@remixicon/react' +import { + memo, + useMemo, +} from 'react' +import { useTranslation } from 'react-i18next' +import Tooltip from '@/app/components/base/tooltip' +import { cn } from '@/utils/classnames' import { useStore } from '../store' import { useCommand } from './hooks' -import { cn } from '@/utils/classnames' -import Tooltip from '@/app/components/base/tooltip' type CommandProps = { type: 'bold' | 'italic' | 'strikethrough' | 'link' | 'bullet' diff --git a/web/app/components/workflow/note-node/note-editor/toolbar/divider.tsx b/web/app/components/workflow/note-node/note-editor/toolbar/divider.tsx index a6554b3e11..35bbbd6893 100644 --- a/web/app/components/workflow/note-node/note-editor/toolbar/divider.tsx +++ b/web/app/components/workflow/note-node/note-editor/toolbar/divider.tsx @@ -1,6 +1,6 @@ const Divider = () => { return ( - <div className='mx-1 h-3.5 w-[1px] bg-divider-regular'></div> + <div className="mx-1 h-3.5 w-[1px] bg-divider-regular"></div> ) } diff --git a/web/app/components/workflow/note-node/note-editor/toolbar/font-size-selector.tsx b/web/app/components/workflow/note-node/note-editor/toolbar/font-size-selector.tsx index b03e176482..7072c6e64a 100644 --- a/web/app/components/workflow/note-node/note-editor/toolbar/font-size-selector.tsx +++ b/web/app/components/workflow/note-node/note-editor/toolbar/font-size-selector.tsx @@ -1,14 +1,14 @@ -import { memo } from 'react' import { RiFontSize } from '@remixicon/react' +import { memo } from 'react' import { useTranslation } from 'react-i18next' -import { useFontSize } from './hooks' -import { cn } from '@/utils/classnames' +import { Check } from '@/app/components/base/icons/src/vender/line/general' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { Check } from '@/app/components/base/icons/src/vender/line/general' +import { cn } from '@/utils/classnames' +import { useFontSize } from './hooks' const FontSizeSelector = () => { const { t } = useTranslation() @@ -37,25 +37,26 @@ const FontSizeSelector = () => { <PortalToFollowElem open={fontSizeSelectorShow} onOpenChange={handleOpenFontSizeSelector} - placement='bottom-start' + placement="bottom-start" offset={2} > <PortalToFollowElemTrigger onClick={() => handleOpenFontSizeSelector(!fontSizeSelectorShow)}> <div className={cn( 'flex h-8 cursor-pointer items-center rounded-md pl-2 pr-1.5 text-[13px] font-medium text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary', fontSizeSelectorShow && 'bg-state-base-hover text-text-secondary', - )}> - <RiFontSize className='mr-1 h-4 w-4' /> + )} + > + <RiFontSize className="mr-1 h-4 w-4" /> {FONT_SIZE_LIST.find(font => font.key === fontSize)?.value || t('workflow.nodes.note.editor.small')} </div> </PortalToFollowElemTrigger> <PortalToFollowElemContent> - <div className='w-[120px] rounded-md border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 text-text-secondary shadow-xl'> + <div className="w-[120px] rounded-md border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 text-text-secondary shadow-xl"> { FONT_SIZE_LIST.map(font => ( <div key={font.key} - className='flex h-8 cursor-pointer items-center justify-between rounded-md pl-3 pr-2 hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center justify-between rounded-md pl-3 pr-2 hover:bg-state-base-hover" onClick={(e) => { e.stopPropagation() handleFontSize(font.key) @@ -69,7 +70,7 @@ const FontSizeSelector = () => { </div> { fontSize === font.key && ( - <Check className='h-4 w-4 text-text-accent' /> + <Check className="h-4 w-4 text-text-accent" /> ) } </div> diff --git a/web/app/components/workflow/note-node/note-editor/toolbar/hooks.ts b/web/app/components/workflow/note-node/note-editor/toolbar/hooks.ts index 8ed942d8d6..39f6a9dc5c 100644 --- a/web/app/components/workflow/note-node/note-editor/toolbar/hooks.ts +++ b/web/app/components/workflow/note-node/note-editor/toolbar/hooks.ts @@ -1,8 +1,15 @@ import { - useCallback, - useEffect, - useState, -} from 'react' + $isLinkNode, + TOGGLE_LINK_COMMAND, +} from '@lexical/link' +import { INSERT_UNORDERED_LIST_COMMAND } from '@lexical/list' +import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' +import { + $getSelectionStyleValueForProperty, + $patchStyleText, + $setBlocksType, +} from '@lexical/selection' +import { mergeRegister } from '@lexical/utils' import { $createParagraphNode, $getSelection, @@ -13,17 +20,10 @@ import { SELECTION_CHANGE_COMMAND, } from 'lexical' import { - $getSelectionStyleValueForProperty, - $patchStyleText, - $setBlocksType, -} from '@lexical/selection' -import { INSERT_UNORDERED_LIST_COMMAND } from '@lexical/list' -import { mergeRegister } from '@lexical/utils' -import { - $isLinkNode, - TOGGLE_LINK_COMMAND, -} from '@lexical/link' -import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext' + useCallback, + useEffect, + useState, +} from 'react' import { useNoteEditorStore } from '../store' import { getSelectedNode } from '../utils' diff --git a/web/app/components/workflow/note-node/note-editor/toolbar/index.tsx b/web/app/components/workflow/note-node/note-editor/toolbar/index.tsx index fd2613d79d..c7b8fa9787 100644 --- a/web/app/components/workflow/note-node/note-editor/toolbar/index.tsx +++ b/web/app/components/workflow/note-node/note-editor/toolbar/index.tsx @@ -1,10 +1,10 @@ -import { memo } from 'react' -import Divider from './divider' import type { ColorPickerProps } from './color-picker' -import ColorPicker from './color-picker' -import FontSizeSelector from './font-size-selector' -import Command from './command' import type { OperatorProps } from './operator' +import { memo } from 'react' +import ColorPicker from './color-picker' +import Command from './command' +import Divider from './divider' +import FontSizeSelector from './font-size-selector' import Operator from './operator' type ToolbarProps = ColorPickerProps & OperatorProps @@ -18,7 +18,7 @@ const Toolbar = ({ onShowAuthorChange, }: ToolbarProps) => { return ( - <div className='inline-flex items-center rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-sm'> + <div className="inline-flex items-center rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 shadow-sm"> <ColorPicker theme={theme} onThemeChange={onThemeChange} @@ -26,12 +26,12 @@ const Toolbar = ({ <Divider /> <FontSizeSelector /> <Divider /> - <div className='flex items-center space-x-0.5'> - <Command type='bold' /> - <Command type='italic' /> - <Command type='strikethrough' /> - <Command type='link' /> - <Command type='bullet' /> + <div className="flex items-center space-x-0.5"> + <Command type="bold" /> + <Command type="italic" /> + <Command type="strikethrough" /> + <Command type="link" /> + <Command type="bullet" /> </div> <Divider /> <Operator diff --git a/web/app/components/workflow/note-node/note-editor/toolbar/operator.tsx b/web/app/components/workflow/note-node/note-editor/toolbar/operator.tsx index 8ba0ad4caf..643a7240f8 100644 --- a/web/app/components/workflow/note-node/note-editor/toolbar/operator.tsx +++ b/web/app/components/workflow/note-node/note-editor/toolbar/operator.tsx @@ -1,17 +1,17 @@ +import { RiMoreFill } from '@remixicon/react' import { memo, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { RiMoreFill } from '@remixicon/react' -import { cn } from '@/utils/classnames' -import ShortcutsName from '@/app/components/workflow/shortcuts-name' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' import Switch from '@/app/components/base/switch' +import ShortcutsName from '@/app/components/workflow/shortcuts-name' +import { cn } from '@/utils/classnames' export type OperatorProps = { onCopy: () => void @@ -34,7 +34,7 @@ const Operator = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom-end' + placement="bottom-end" offset={4} > <PortalToFollowElemTrigger onClick={() => setOpen(!open)}> @@ -44,14 +44,14 @@ const Operator = ({ open && 'bg-state-base-hover text-text-secondary', )} > - <RiMoreFill className='h-4 w-4' /> + <RiMoreFill className="h-4 w-4" /> </div> </PortalToFollowElemTrigger> <PortalToFollowElemContent> - <div className='min-w-[192px] rounded-md border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-xl'> - <div className='p-1'> + <div className="min-w-[192px] rounded-md border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-xl"> + <div className="p-1"> <div - className='flex h-8 cursor-pointer items-center justify-between rounded-md px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center justify-between rounded-md px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => { onCopy() setOpen(false) @@ -61,7 +61,7 @@ const Operator = ({ <ShortcutsName keys={['ctrl', 'c']} /> </div> <div - className='flex h-8 cursor-pointer items-center justify-between rounded-md px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center justify-between rounded-md px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => { onDuplicate() setOpen(false) @@ -71,24 +71,24 @@ const Operator = ({ <ShortcutsName keys={['ctrl', 'd']} /> </div> </div> - <div className='h-px bg-divider-subtle'></div> - <div className='p-1'> + <div className="h-px bg-divider-subtle"></div> + <div className="p-1"> <div - className='flex h-8 cursor-pointer items-center justify-between rounded-md px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center justify-between rounded-md px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={e => e.stopPropagation()} > <div>{t('workflow.nodes.note.editor.showAuthor')}</div> <Switch - size='l' + size="l" defaultValue={showAuthor} onChange={onShowAuthorChange} /> </div> </div> - <div className='h-px bg-divider-subtle'></div> - <div className='p-1'> + <div className="h-px bg-divider-subtle"></div> + <div className="p-1"> <div - className='flex h-8 cursor-pointer items-center justify-between rounded-md px-3 text-sm text-text-secondary hover:bg-state-destructive-hover hover:text-text-destructive' + className="flex h-8 cursor-pointer items-center justify-between rounded-md px-3 text-sm text-text-secondary hover:bg-state-destructive-hover hover:text-text-destructive" onClick={() => { onDelete() setOpen(false) diff --git a/web/app/components/workflow/note-node/note-editor/utils.ts b/web/app/components/workflow/note-node/note-editor/utils.ts index c241e93bf1..dff98a8301 100644 --- a/web/app/components/workflow/note-node/note-editor/utils.ts +++ b/web/app/components/workflow/note-node/note-editor/utils.ts @@ -1,5 +1,5 @@ -import { $isAtNodeEnd } from '@lexical/selection' import type { ElementNode, RangeSelection, TextNode } from 'lexical' +import { $isAtNodeEnd } from '@lexical/selection' export function getSelectedNode( selection: RangeSelection, @@ -19,4 +19,4 @@ export function getSelectedNode( } // eslint-disable-next-line sonarjs/empty-string-repetition -export const urlRegExp = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[\w]*))?)/ +export const urlRegExp = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-]*)?\??[-+=&;%@.\w]*#?\w*)?)/ diff --git a/web/app/components/workflow/operator/add-block.tsx b/web/app/components/workflow/operator/add-block.tsx index d1c69ba63d..f52e440c98 100644 --- a/web/app/components/workflow/operator/add-block.tsx +++ b/web/app/components/workflow/operator/add-block.tsx @@ -1,16 +1,21 @@ +import type { OffsetOptions } from '@floating-ui/react' +import type { + OnSelectBlock, +} from '@/app/components/workflow/types' +import { RiAddCircleFill } from '@remixicon/react' import { memo, useCallback, useState, } from 'react' -import { RiAddCircleFill } from '@remixicon/react' -import { useStoreApi } from 'reactflow' import { useTranslation } from 'react-i18next' -import type { OffsetOptions } from '@floating-ui/react' +import { useStoreApi } from 'reactflow' +import BlockSelector from '@/app/components/workflow/block-selector' import { - generateNewNode, - getNodeCustomTypeByNodeDataType, -} from '../utils' + BlockEnum, +} from '@/app/components/workflow/types' +import { FlowType } from '@/types/common' +import { cn } from '@/utils/classnames' import { useAvailableBlocks, useIsChatMode, @@ -20,16 +25,11 @@ import { } from '../hooks' import { useHooksStore } from '../hooks-store' import { useWorkflowStore } from '../store' -import TipPopup from './tip-popup' -import { cn } from '@/utils/classnames' -import BlockSelector from '@/app/components/workflow/block-selector' -import type { - OnSelectBlock, -} from '@/app/components/workflow/types' import { - BlockEnum, -} from '@/app/components/workflow/types' -import { FlowType } from '@/types/common' + generateNewNode, + getNodeCustomTypeByNodeDataType, +} from '../utils' +import TipPopup from './tip-popup' type AddBlockProps = { renderTrigger?: (open: boolean) => React.ReactNode @@ -93,8 +93,9 @@ const AddBlock = ({ 'flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary', `${nodesReadOnly && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled'}`, open && 'bg-state-accent-active text-text-accent', - )}> - <RiAddCircleFill className='h-4 w-4' /> + )} + > + <RiAddCircleFill className="h-4 w-4" /> </div> </TipPopup> ) @@ -106,13 +107,13 @@ const AddBlock = ({ onOpenChange={handleOpenChange} disabled={nodesReadOnly} onSelect={handleSelect} - placement='right-start' + placement="right-start" offset={offset ?? { mainAxis: 4, crossAxis: -8, }} trigger={renderTrigger || renderTriggerElement} - popupClassName='!min-w-[256px]' + popupClassName="!min-w-[256px]" availableBlocksTypes={availableNextBlocks} showStartTab={showStartTab} /> diff --git a/web/app/components/workflow/operator/control.tsx b/web/app/components/workflow/operator/control.tsx index 636f83bb2d..394ccbe3dd 100644 --- a/web/app/components/workflow/operator/control.tsx +++ b/web/app/components/workflow/operator/control.tsx @@ -1,8 +1,4 @@ import type { MouseEvent } from 'react' -import { - memo, -} from 'react' -import { useTranslation } from 'react-i18next' import { RiAspectRatioFill, RiAspectRatioLine, @@ -11,22 +7,26 @@ import { RiHand, RiStickyNoteAddLine, } from '@remixicon/react' +import { + memo, +} from 'react' +import { useTranslation } from 'react-i18next' +import { cn } from '@/utils/classnames' +import Divider from '../../base/divider' import { useNodesReadOnly, useWorkflowCanvasMaximize, useWorkflowMoveMode, useWorkflowOrganize, } from '../hooks' +import { useStore } from '../store' import { ControlMode, } from '../types' -import { useStore } from '../store' -import Divider from '../../base/divider' import AddBlock from './add-block' -import TipPopup from './tip-popup' -import MoreActions from './more-actions' import { useOperator } from './hooks' -import { cn } from '@/utils/classnames' +import MoreActions from './more-actions' +import TipPopup from './tip-popup' const Control = () => { const { t } = useTranslation() @@ -50,7 +50,7 @@ const Control = () => { } return ( - <div className='pointer-events-auto flex flex-col items-center rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 text-text-tertiary shadow-lg'> + <div className="pointer-events-auto flex flex-col items-center rounded-lg border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg p-0.5 text-text-tertiary shadow-lg"> <AddBlock /> <TipPopup title={t('workflow.nodes.note.addNote')}> <div @@ -60,10 +60,10 @@ const Control = () => { )} onClick={addNote} > - <RiStickyNoteAddLine className='h-4 w-4' /> + <RiStickyNoteAddLine className="h-4 w-4" /> </div> </TipPopup> - <Divider className='my-1 w-3.5' /> + <Divider className="my-1 w-3.5" /> <TipPopup title={t('workflow.common.pointerMode')} shortcuts={['v']}> <div className={cn( @@ -73,7 +73,7 @@ const Control = () => { )} onClick={handleModePointer} > - <RiCursorLine className='h-4 w-4' /> + <RiCursorLine className="h-4 w-4" /> </div> </TipPopup> <TipPopup title={t('workflow.common.handMode')} shortcuts={['h']}> @@ -85,10 +85,10 @@ const Control = () => { )} onClick={handleModeHand} > - <RiHand className='h-4 w-4' /> + <RiHand className="h-4 w-4" /> </div> </TipPopup> - <Divider className='my-1 w-3.5' /> + <Divider className="my-1 w-3.5" /> <TipPopup title={t('workflow.panel.organizeBlocks')} shortcuts={['ctrl', 'o']}> <div className={cn( @@ -97,7 +97,7 @@ const Control = () => { )} onClick={handleLayout} > - <RiFunctionAddLine className='h-4 w-4' /> + <RiFunctionAddLine className="h-4 w-4" /> </div> </TipPopup> <TipPopup title={maximizeCanvas ? t('workflow.panel.minimize') : t('workflow.panel.maximize')} shortcuts={['f']}> @@ -109,8 +109,8 @@ const Control = () => { )} onClick={handleToggleMaximizeCanvas} > - {maximizeCanvas && <RiAspectRatioFill className='h-4 w-4' />} - {!maximizeCanvas && <RiAspectRatioLine className='h-4 w-4' />} + {maximizeCanvas && <RiAspectRatioFill className="h-4 w-4" />} + {!maximizeCanvas && <RiAspectRatioLine className="h-4 w-4" />} </div> </TipPopup> <MoreActions /> diff --git a/web/app/components/workflow/operator/hooks.ts b/web/app/components/workflow/operator/hooks.ts index edec10bda7..23248a89a3 100644 --- a/web/app/components/workflow/operator/hooks.ts +++ b/web/app/components/workflow/operator/hooks.ts @@ -1,10 +1,10 @@ -import { useCallback } from 'react' -import { generateNewNode } from '../utils' -import { useWorkflowStore } from '../store' import type { NoteNodeType } from '../note-node/types' +import { useCallback } from 'react' +import { useAppContext } from '@/context/app-context' import { CUSTOM_NOTE_NODE } from '../note-node/constants' import { NoteTheme } from '../note-node/types' -import { useAppContext } from '@/context/app-context' +import { useWorkflowStore } from '../store' +import { generateNewNode } from '../utils' export const useOperator = () => { const workflowStore = useWorkflowStore() diff --git a/web/app/components/workflow/operator/index.tsx b/web/app/components/workflow/operator/index.tsx index b4fcf184a7..519eb1fdb7 100644 --- a/web/app/components/workflow/operator/index.tsx +++ b/web/app/components/workflow/operator/index.tsx @@ -1,11 +1,11 @@ -import { memo, useCallback, useEffect, useMemo, useRef } from 'react' import type { Node } from 'reactflow' +import { memo, useCallback, useEffect, useMemo, useRef } from 'react' import { MiniMap } from 'reactflow' import UndoRedo from '../header/undo-redo' -import ZoomInOut from './zoom-in-out' -import VariableTrigger from '../variable-inspect/trigger' -import VariableInspectPanel from '../variable-inspect' import { useStore } from '../store' +import VariableInspectPanel from '../variable-inspect' +import VariableTrigger from '../variable-inspect/trigger' +import ZoomInOut from './zoom-in-out' export type OperatorProps = { handleUndo: () => void @@ -51,19 +51,19 @@ const Operator = ({ handleUndo, handleRedo }: OperatorProps) => { return ( <div ref={bottomPanelRef} - className='absolute bottom-0 left-0 right-0 z-10 px-1' + className="absolute bottom-0 left-0 right-0 z-10 px-1" style={ { width: bottomPanelWidth, } } > - <div className='flex justify-between px-1 pb-2'> - <div className='flex items-center gap-2'> + <div className="flex justify-between px-1 pb-2"> + <div className="flex items-center gap-2"> <UndoRedo handleUndo={handleUndo} handleRedo={handleRedo} /> </div> <VariableTrigger /> - <div className='relative'> + <div className="relative"> <MiniMap pannable zoomable @@ -71,11 +71,11 @@ const Operator = ({ handleUndo, handleRedo }: OperatorProps) => { width: 102, height: 72, }} - maskColor='var(--color-workflow-minimap-bg)' + maskColor="var(--color-workflow-minimap-bg)" nodeClassName={getMiniMapNodeClassName} nodeStrokeWidth={3} - className='!absolute !bottom-10 z-[9] !m-0 !h-[73px] !w-[103px] !rounded-lg !border-[0.5px] - !border-divider-subtle !bg-background-default-subtle !shadow-md !shadow-shadow-shadow-5' + className="!absolute !bottom-10 z-[9] !m-0 !h-[73px] !w-[103px] !rounded-lg !border-[0.5px] + !border-divider-subtle !bg-background-default-subtle !shadow-md !shadow-shadow-shadow-5" /> <ZoomInOut /> </div> diff --git a/web/app/components/workflow/operator/more-actions.tsx b/web/app/components/workflow/operator/more-actions.tsx index 52c81612da..558af4e152 100644 --- a/web/app/components/workflow/operator/more-actions.tsx +++ b/web/app/components/workflow/operator/more-actions.tsx @@ -1,26 +1,26 @@ import type { FC } from 'react' +import { RiExportLine, RiMoreFill } from '@remixicon/react' +import { toJpeg, toPng, toSvg } from 'html-to-image' import { memo, useCallback, useMemo, useState, } from 'react' -import { useShallow } from 'zustand/react/shallow' import { useTranslation } from 'react-i18next' -import { RiExportLine, RiMoreFill } from '@remixicon/react' -import { toJpeg, toPng, toSvg } from 'html-to-image' -import { useNodesReadOnly } from '../hooks' -import TipPopup from './tip-popup' -import { cn } from '@/utils/classnames' +import { getNodesBounds, useReactFlow } from 'reactflow' +import { useShallow } from 'zustand/react/shallow' +import { useStore as useAppStore } from '@/app/components/app/store' +import ImagePreview from '@/app/components/base/image-uploader/image-preview' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import { getNodesBounds, useReactFlow } from 'reactflow' -import ImagePreview from '@/app/components/base/image-uploader/image-preview' import { useStore } from '@/app/components/workflow/store' -import { useStore as useAppStore } from '@/app/components/app/store' +import { cn } from '@/utils/classnames' +import { useNodesReadOnly } from '../hooks' +import TipPopup from './tip-popup' const MoreActions: FC = () => { const { t } = useTranslation() @@ -38,7 +38,8 @@ const MoreActions: FC = () => { }))) const crossAxisOffset = useMemo(() => { - if (maximizeCanvas) return 40 + if (maximizeCanvas) + return 40 return appSidebarExpand === 'expand' ? 188 : 40 }, [appSidebarExpand, maximizeCanvas]) @@ -51,7 +52,8 @@ const MoreActions: FC = () => { setOpen(false) const flowElement = document.querySelector('.react-flow__viewport') as HTMLElement - if (!flowElement) return + if (!flowElement) + return try { let filename = appName || knowledgeName @@ -197,58 +199,58 @@ const MoreActions: FC = () => { )} onClick={handleTrigger} > - <RiMoreFill className='h-4 w-4' /> + <RiMoreFill className="h-4 w-4" /> </div> </TipPopup> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-10'> - <div className='min-w-[180px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur text-text-secondary shadow-lg'> - <div className='p-1'> - <div className='flex items-center gap-2 px-2 py-1 text-xs font-medium text-text-tertiary'> - <RiExportLine className='h-3 w-3' /> + <PortalToFollowElemContent className="z-10"> + <div className="min-w-[180px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur text-text-secondary shadow-lg"> + <div className="p-1"> + <div className="flex items-center gap-2 px-2 py-1 text-xs font-medium text-text-tertiary"> + <RiExportLine className="h-3 w-3" /> {t('workflow.common.exportImage')} </div> - <div className='px-2 py-1 text-xs font-medium text-text-tertiary'> + <div className="px-2 py-1 text-xs font-medium text-text-tertiary"> {t('workflow.common.currentView')} </div> <div - className='system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover' + className="system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover" onClick={() => handleExportImage('png')} > {t('workflow.common.exportPNG')} </div> <div - className='system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover' + className="system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover" onClick={() => handleExportImage('jpeg')} > {t('workflow.common.exportJPEG')} </div> <div - className='system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover' + className="system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover" onClick={() => handleExportImage('svg')} > {t('workflow.common.exportSVG')} </div> - <div className='border-border-divider mx-2 my-1 border-t' /> + <div className="border-border-divider mx-2 my-1 border-t" /> - <div className='px-2 py-1 text-xs font-medium text-text-tertiary'> + <div className="px-2 py-1 text-xs font-medium text-text-tertiary"> {t('workflow.common.currentWorkflow')} </div> <div - className='system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover' + className="system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover" onClick={() => handleExportImage('png', true)} > {t('workflow.common.exportPNG')} </div> <div - className='system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover' + className="system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover" onClick={() => handleExportImage('jpeg', true)} > {t('workflow.common.exportJPEG')} </div> <div - className='system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover' + className="system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 hover:bg-state-base-hover" onClick={() => handleExportImage('svg', true)} > {t('workflow.common.exportSVG')} diff --git a/web/app/components/workflow/operator/tip-popup.tsx b/web/app/components/workflow/operator/tip-popup.tsx index 3721ed8118..226a889359 100644 --- a/web/app/components/workflow/operator/tip-popup.tsx +++ b/web/app/components/workflow/operator/tip-popup.tsx @@ -1,6 +1,6 @@ import { memo } from 'react' -import ShortcutsName from '../shortcuts-name' import Tooltip from '@/app/components/base/tooltip' +import ShortcutsName from '../shortcuts-name' type TipPopupProps = { title: string @@ -16,15 +16,15 @@ const TipPopup = ({ <Tooltip needsDelay={false} offset={4} - popupClassName='p-0 bg-transparent' - popupContent={ - <div className='flex items-center gap-1 rounded-lg border-[0.5px] border-components-panel-border bg-components-tooltip-bg p-1.5 shadow-lg backdrop-blur-[5px]'> - <span className='system-xs-medium text-text-secondary'>{title}</span> + popupClassName="p-0 bg-transparent" + popupContent={( + <div className="flex items-center gap-1 rounded-lg border-[0.5px] border-components-panel-border bg-components-tooltip-bg p-1.5 shadow-lg backdrop-blur-[5px]"> + <span className="system-xs-medium text-text-secondary">{title}</span> { shortcuts && <ShortcutsName keys={shortcuts} /> } </div> - } + )} > {children} </Tooltip> diff --git a/web/app/components/workflow/operator/zoom-in-out.tsx b/web/app/components/workflow/operator/zoom-in-out.tsx index 703304c27c..12d603e9b8 100644 --- a/web/app/components/workflow/operator/zoom-in-out.tsx +++ b/web/app/components/workflow/operator/zoom-in-out.tsx @@ -1,34 +1,34 @@ import type { FC } from 'react' +import { + RiZoomInLine, + RiZoomOutLine, +} from '@remixicon/react' import { Fragment, memo, useCallback, useState, } from 'react' -import { - RiZoomInLine, - RiZoomOutLine, -} from '@remixicon/react' import { useTranslation } from 'react-i18next' import { useReactFlow, useViewport, } from 'reactflow' -import { - useNodesSyncDraft, - useWorkflowReadOnly, -} from '../hooks' - -import ShortcutsName from '../shortcuts-name' -import Divider from '../../base/divider' -import TipPopup from './tip-popup' -import { cn } from '@/utils/classnames' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' +import { cn } from '@/utils/classnames' +import Divider from '../../base/divider' +import { + useNodesSyncDraft, + useWorkflowReadOnly, +} from '../hooks' +import ShortcutsName from '../shortcuts-name' +import TipPopup from './tip-popup' + enum ZoomType { zoomIn = 'zoomIn', zoomOut = 'zoomOut', @@ -121,7 +121,7 @@ const ZoomInOut: FC = () => { return ( <PortalToFollowElem - placement='top-start' + placement="top-start" open={open} onOpenChange={setOpen} offset={{ @@ -135,10 +135,12 @@ const ZoomInOut: FC = () => { p-0.5 text-[13px] shadow-lg backdrop-blur-[5px] hover:bg-state-base-hover ${workflowReadOnly && '!cursor-not-allowed opacity-50'} - `}> + `} + > <div className={cn( 'flex h-8 w-[98px] items-center justify-between rounded-lg', - )}> + )} + > <TipPopup title={t('workflow.operator.zoomOut')} shortcuts={['ctrl', '-']} @@ -153,10 +155,13 @@ const ZoomInOut: FC = () => { zoomOut() }} > - <RiZoomOutLine className='h-4 w-4 text-text-tertiary hover:text-text-secondary' /> + <RiZoomOutLine className="h-4 w-4 text-text-tertiary hover:text-text-secondary" /> </div> </TipPopup> - <div onClick={handleTrigger} className={cn('system-sm-medium w-[34px] text-text-tertiary hover:text-text-secondary')}>{Number.parseFloat(`${zoom * 100}`).toFixed(0)}%</div> + <div onClick={handleTrigger} className={cn('system-sm-medium w-[34px] text-text-tertiary hover:text-text-secondary')}> + {Number.parseFloat(`${zoom * 100}`).toFixed(0)} + % + </div> <TipPopup title={t('workflow.operator.zoomIn')} shortcuts={['ctrl', '+']} @@ -171,32 +176,32 @@ const ZoomInOut: FC = () => { zoomIn() }} > - <RiZoomInLine className='h-4 w-4 text-text-tertiary hover:text-text-secondary' /> + <RiZoomInLine className="h-4 w-4 text-text-tertiary hover:text-text-secondary" /> </div> </TipPopup> </div> </div> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-10'> - <div className='w-[145px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[5px]'> + <PortalToFollowElemContent className="z-10"> + <div className="w-[145px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[5px]"> { ZOOM_IN_OUT_OPTIONS.map((options, i) => ( <Fragment key={i}> { i !== 0 && ( - <Divider className='m-0' /> + <Divider className="m-0" /> ) } - <div className='p-1'> + <div className="p-1"> { options.map(option => ( <div key={option.key} - className='system-md-regular flex h-8 cursor-pointer items-center justify-between space-x-1 rounded-lg py-1.5 pl-3 pr-2 text-text-secondary hover:bg-state-base-hover' + className="system-md-regular flex h-8 cursor-pointer items-center justify-between space-x-1 rounded-lg py-1.5 pl-3 pr-2 text-text-secondary hover:bg-state-base-hover" onClick={() => handleZoom(option.key)} > <span>{option.text}</span> - <div className='flex items-center space-x-0.5'> + <div className="flex items-center space-x-0.5"> { option.key === ZoomType.zoomToFit && ( <ShortcutsName keys={['ctrl', '1']} /> diff --git a/web/app/components/workflow/panel-contextmenu.tsx b/web/app/components/workflow/panel-contextmenu.tsx index 8d0811f853..1ae2653d83 100644 --- a/web/app/components/workflow/panel-contextmenu.tsx +++ b/web/app/components/workflow/panel-contextmenu.tsx @@ -1,13 +1,12 @@ +import { useClickAway } from 'ahooks' import { memo, useEffect, useRef, } from 'react' import { useTranslation } from 'react-i18next' -import { useClickAway } from 'ahooks' +import { cn } from '@/utils/classnames' import Divider from '../base/divider' -import ShortcutsName from './shortcuts-name' -import { useStore } from './store' import { useDSL, useNodesInteractions, @@ -16,7 +15,8 @@ import { } from './hooks' import AddBlock from './operator/add-block' import { useOperator } from './operator/hooks' -import { cn } from '@/utils/classnames' +import ShortcutsName from './shortcuts-name' +import { useStore } from './store' const PanelContextmenu = () => { const { t } = useTranslation() @@ -42,7 +42,7 @@ const PanelContextmenu = () => { const renderTrigger = () => { return ( <div - className='flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" > {t('workflow.common.addBlock')} </div> @@ -54,14 +54,14 @@ const PanelContextmenu = () => { return ( <div - className='absolute z-[9] w-[200px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg' + className="absolute z-[9] w-[200px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg" style={{ left: panelMenu.left, top: panelMenu.top, }} ref={ref} > - <div className='p-1'> + <div className="p-1"> <AddBlock renderTrigger={renderTrigger} offset={{ @@ -70,7 +70,7 @@ const PanelContextmenu = () => { }} /> <div - className='flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={(e) => { e.stopPropagation() handleAddNote() @@ -80,7 +80,7 @@ const PanelContextmenu = () => { {t('workflow.nodes.note.addNote')} </div> <div - className='flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => { handleStartWorkflowRun() handlePaneContextmenuCancel() @@ -90,8 +90,8 @@ const PanelContextmenu = () => { <ShortcutsName keys={['alt', 'r']} /> </div> </div> - <Divider className='m-0' /> - <div className='p-1'> + <Divider className="m-0" /> + <div className="p-1"> <div className={cn( 'flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary', @@ -108,16 +108,16 @@ const PanelContextmenu = () => { <ShortcutsName keys={['ctrl', 'v']} /> </div> </div> - <Divider className='m-0' /> - <div className='p-1'> + <Divider className="m-0" /> + <div className="p-1"> <div - className='flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => exportCheck?.()} > {t('app.export')} </div> <div - className='flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center justify-between rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => setShowImportDSLModal(true)} > {t('workflow.common.importDSL')} diff --git a/web/app/components/workflow/panel/chat-record/index.tsx b/web/app/components/workflow/panel/chat-record/index.tsx index 5ab3b45340..6afe463c30 100644 --- a/web/app/components/workflow/panel/chat-record/index.tsx +++ b/web/app/components/workflow/panel/chat-record/index.tsx @@ -1,25 +1,25 @@ +import type { IChatItem } from '@/app/components/base/chat/chat/type' +import type { ChatItem, ChatItemInTree } from '@/app/components/base/chat/types' +import { RiCloseLine } from '@remixicon/react' import { memo, useCallback, useEffect, useState, } from 'react' -import { RiCloseLine } from '@remixicon/react' +import { useStore as useAppStore } from '@/app/components/app/store' +import Chat from '@/app/components/base/chat/chat' +import { buildChatItemTree, getThreadMessages } from '@/app/components/base/chat/utils' +import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils' +import Loading from '@/app/components/base/loading' +import { fetchConversationMessages } from '@/service/debug' +import { useWorkflowRun } from '../../hooks' import { useStore, useWorkflowStore, } from '../../store' -import { useWorkflowRun } from '../../hooks' import { formatWorkflowRunIdentifier } from '../../utils' import UserInput from './user-input' -import Chat from '@/app/components/base/chat/chat' -import type { ChatItem, ChatItemInTree } from '@/app/components/base/chat/types' -import { fetchConversationMessages } from '@/service/debug' -import { useStore as useAppStore } from '@/app/components/app/store' -import Loading from '@/app/components/base/loading' -import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils' -import type { IChatItem } from '@/app/components/base/chat/chat/type' -import { buildChatItemTree, getThreadMessages } from '@/app/components/base/chat/utils' function getFormattedChatList(messages: any[]) { const res: ChatItem[] = [] @@ -87,48 +87,48 @@ const ChatRecord = () => { return ( <div - className='flex h-full w-[420px] flex-col rounded-l-2xl border border-components-panel-border bg-chatbot-bg shadow-xl' + className="flex h-full w-[420px] flex-col rounded-l-2xl border border-components-panel-border bg-chatbot-bg shadow-xl" // style={{ // background: 'linear-gradient(156deg, rgba(242, 244, 247, 0.80) 0%, rgba(242, 244, 247, 0.00) 99.43%), var(--white, #FFF)', // }} > {!fetched && ( - <div className='flex h-full items-center justify-center'> + <div className="flex h-full items-center justify-center"> <Loading /> </div> )} {fetched && ( <> - <div className='flex shrink-0 items-center justify-between p-4 pb-1 text-base font-semibold text-text-primary'> + <div className="flex shrink-0 items-center justify-between p-4 pb-1 text-base font-semibold text-text-primary"> {`TEST CHAT${formatWorkflowRunIdentifier(historyWorkflowData?.finished_at)}`} <div - className='flex h-6 w-6 cursor-pointer items-center justify-center' + className="flex h-6 w-6 cursor-pointer items-center justify-center" onClick={() => { handleLoadBackupDraft() workflowStore.setState({ historyWorkflowData: undefined }) }} > - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> - <div className='h-0 grow'> + <div className="h-0 grow"> <Chat config={{ supportCitationHitInfo: true, questionEditEnable: false, } as any} chatList={threadChatItems} - chatContainerClassName='px-3' - chatContainerInnerClassName='pt-6 w-full max-w-full mx-auto' - chatFooterClassName='px-4 rounded-b-2xl' - chatFooterInnerClassName='pb-4 w-full max-w-full mx-auto' + chatContainerClassName="px-3" + chatContainerInnerClassName="pt-6 w-full max-w-full mx-auto" + chatFooterClassName="px-4 rounded-b-2xl" + chatFooterInnerClassName="pb-4 w-full max-w-full mx-auto" chatNode={<UserInput />} noChatInput allToolIcons={{}} showPromptLog switchSibling={switchSibling} noSpacing - chatAnswerContainerInner='!pr-2' + chatAnswerContainerInner="!pr-2" /> </div> </> diff --git a/web/app/components/workflow/panel/chat-record/user-input.tsx b/web/app/components/workflow/panel/chat-record/user-input.tsx index 7b90435928..ecc54a5e54 100644 --- a/web/app/components/workflow/panel/chat-record/user-input.tsx +++ b/web/app/components/workflow/panel/chat-record/user-input.tsx @@ -1,9 +1,9 @@ +import { RiArrowDownSLine } from '@remixicon/react' import { memo, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { RiArrowDownSLine } from '@remixicon/react' const UserInput = () => { const { t } = useTranslation() @@ -32,15 +32,15 @@ const UserInput = () => { /> {t('workflow.panel.userInputField').toLocaleUpperCase()} </div> - <div className='px-2 pb-3 pt-1'> + <div className="px-2 pb-3 pt-1"> { expanded && ( - <div className='py-2 text-[13px] text-text-primary'> + <div className="py-2 text-[13px] text-text-primary"> { variables.map((variable: any) => ( <div key={variable.variable} - className='mb-2 last-of-type:mb-0' + className="mb-2 last-of-type:mb-0" > </div> )) diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/array-bool-list.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/array-bool-list.tsx index 810d5ba5d5..3d22437830 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/array-bool-list.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/array-bool-list.tsx @@ -1,13 +1,13 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' -import { useTranslation } from 'react-i18next' import { RiAddLine } from '@remixicon/react' import { produce } from 'immer' -import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' -import BoolValue from './bool-value' +import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button' import { cn } from '@/utils/classnames' +import BoolValue from './bool-value' type Props = { className?: string @@ -50,20 +50,20 @@ const ArrayValueList: FC<Props> = ({ return ( <div className={cn('w-full space-y-2', className)}> {list.map((item, index) => ( - <div className='flex items-center space-x-1' key={index}> + <div className="flex items-center space-x-1" key={index}> <BoolValue value={item} onChange={handleChange(index)} /> <RemoveButton - className='!bg-gray-100 !p-2 hover:!bg-gray-200' + className="!bg-gray-100 !p-2 hover:!bg-gray-200" onClick={handleItemRemove(index)} /> </div> ))} - <Button variant='tertiary' className='w-full' onClick={handleItemAdd}> - <RiAddLine className='mr-1 h-4 w-4' /> + <Button variant="tertiary" className="w-full" onClick={handleItemAdd}> + <RiAddLine className="mr-1 h-4 w-4" /> <span>{t('workflow.chatVariable.modal.addArrayValue')}</span> </Button> </div> diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx index f7d0c69d89..e1025eda6a 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx @@ -1,12 +1,12 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' -import { useTranslation } from 'react-i18next' import { RiAddLine } from '@remixicon/react' import { produce } from 'immer' -import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' +import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button' type Props = { isString: boolean @@ -47,9 +47,9 @@ const ArrayValueList: FC<Props> = ({ }, [list, onChange]) return ( - <div className='w-full space-y-2'> + <div className="w-full space-y-2"> {list.map((item, index) => ( - <div className='flex items-center space-x-1' key={index}> + <div className="flex items-center space-x-1" key={index}> <Input placeholder={t('workflow.chatVariable.modal.arrayValue') || ''} value={list[index]} @@ -57,13 +57,13 @@ const ArrayValueList: FC<Props> = ({ type={isString ? 'text' : 'number'} /> <RemoveButton - className='!bg-gray-100 !p-2 hover:!bg-gray-200' + className="!bg-gray-100 !p-2 hover:!bg-gray-200" onClick={handleItemRemove(index)} /> </div> ))} - <Button variant='tertiary' className='w-full' onClick={handleItemAdd}> - <RiAddLine className='mr-1 h-4 w-4' /> + <Button variant="tertiary" className="w-full" onClick={handleItemAdd}> + <RiAddLine className="mr-1 h-4 w-4" /> <span>{t('workflow.chatVariable.modal.addArrayValue')}</span> </Button> </div> diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/bool-value.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/bool-value.tsx index 864fefd9a2..89b309db58 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/bool-value.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/bool-value.tsx @@ -20,15 +20,17 @@ const BoolValue: FC<Props> = ({ }, [onChange]) return ( - <div className='flex w-full space-x-1'> - <OptionCard className='grow' + <div className="flex w-full space-x-1"> + <OptionCard + className="grow" selected={booleanValue} - title='True' + title="True" onSelect={handleChange(true)} /> - <OptionCard className='grow' + <OptionCard + className="grow" selected={!booleanValue} - title='False' + title="False" onSelect={handleChange(false)} /> </div> diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx index f223aca5eb..1132434122 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx @@ -1,12 +1,12 @@ 'use client' import type { FC } from 'react' +import { produce } from 'immer' import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import { produce } from 'immer' import { useContext } from 'use-context-selector' import { ToastContext } from '@/app/components/base/toast' -import VariableTypeSelector from '@/app/components/workflow/panel/chat-variable-panel/components/variable-type-select' import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button' +import VariableTypeSelector from '@/app/components/workflow/panel/chat-variable-panel/components/variable-type-select' import { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type' type Props = { @@ -91,30 +91,30 @@ const ObjectValueItem: FC<Props> = ({ }, [handleItemAdd, index, list.length]) return ( - <div className='group flex border-t border-gray-200'> + <div className="group flex border-t border-gray-200"> {/* Key */} - <div className='w-[120px] border-r border-gray-200'> + <div className="w-[120px] border-r border-gray-200"> <input - className='system-xs-regular placeholder:system-xs-regular block h-7 w-full appearance-none px-2 text-text-secondary caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:bg-state-base-hover focus:bg-components-input-bg-active' + className="system-xs-regular placeholder:system-xs-regular block h-7 w-full appearance-none px-2 text-text-secondary caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:bg-state-base-hover focus:bg-components-input-bg-active" placeholder={t('workflow.chatVariable.modal.objectKey') || ''} value={list[index].key} onChange={handleKeyChange(index)} /> </div> {/* Type */} - <div className='w-[96px] border-r border-gray-200'> + <div className="w-[96px] border-r border-gray-200"> <VariableTypeSelector inCell value={list[index].type} list={typeList} onSelect={handleTypeChange(index)} - popupClassName='w-[120px]' + popupClassName="w-[120px]" /> </div> {/* Value */} - <div className='relative w-[230px]'> + <div className="relative w-[230px]"> <input - className='system-xs-regular placeholder:system-xs-regular block h-7 w-full appearance-none px-2 text-text-secondary caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:bg-state-base-hover focus:bg-components-input-bg-active' + className="system-xs-regular placeholder:system-xs-regular block h-7 w-full appearance-none px-2 text-text-secondary caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:bg-state-base-hover focus:bg-components-input-bg-active" placeholder={t('workflow.chatVariable.modal.objectValue') || ''} value={list[index].value} onChange={handleValueChange(index)} @@ -124,7 +124,7 @@ const ObjectValueItem: FC<Props> = ({ /> {list.length > 1 && !isFocus && ( <RemoveButton - className='absolute right-1 top-0.5 z-10 hidden group-hover:block' + className="absolute right-1 top-0.5 z-10 hidden group-hover:block" onClick={handleItemRemove(index)} /> )} diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/object-value-list.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/object-value-list.tsx index 830cf94f69..0e39bfcfcc 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/object-value-list.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/object-value-list.tsx @@ -16,11 +16,11 @@ const ObjectValueList: FC<Props> = ({ const { t } = useTranslation() return ( - <div className='w-full overflow-hidden rounded-lg border border-gray-200'> - <div className='system-xs-medium flex h-7 items-center uppercase text-text-tertiary'> - <div className='flex h-full w-[120px] items-center border-r border-gray-200 pl-2'>{t('workflow.chatVariable.modal.objectKey')}</div> - <div className='flex h-full w-[96px] items-center border-r border-gray-200 pl-2'>{t('workflow.chatVariable.modal.objectType')}</div> - <div className='flex h-full w-[230px] items-center pl-2 pr-1'>{t('workflow.chatVariable.modal.objectValue')}</div> + <div className="w-full overflow-hidden rounded-lg border border-gray-200"> + <div className="system-xs-medium flex h-7 items-center uppercase text-text-tertiary"> + <div className="flex h-full w-[120px] items-center border-r border-gray-200 pl-2">{t('workflow.chatVariable.modal.objectKey')}</div> + <div className="flex h-full w-[96px] items-center border-r border-gray-200 pl-2">{t('workflow.chatVariable.modal.objectType')}</div> + <div className="flex h-full w-[230px] items-center pl-2 pr-1">{t('workflow.chatVariable.modal.objectValue')}</div> </div> {list.map((item, index) => ( <ObjectValueItem diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-item.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-item.tsx index 54e8fc4f6c..a43179026e 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-item.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-item.tsx @@ -1,8 +1,8 @@ -import { memo, useState } from 'react' -import { capitalize } from 'lodash-es' -import { RiDeleteBinLine, RiEditLine } from '@remixicon/react' -import { BubbleX } from '@/app/components/base/icons/src/vender/line/others' import type { ConversationVariable } from '@/app/components/workflow/types' +import { RiDeleteBinLine, RiEditLine } from '@remixicon/react' +import { capitalize } from 'lodash-es' +import { memo, useState } from 'react' +import { BubbleX } from '@/app/components/base/icons/src/vender/line/others' import { cn } from '@/utils/classnames' type VariableItemProps = { @@ -21,27 +21,28 @@ const VariableItem = ({ <div className={cn( 'radius-md mb-1 border border-components-panel-border-subtle bg-components-panel-on-panel-item-bg px-2.5 py-2 shadow-xs hover:bg-components-panel-on-panel-item-bg-hover', destructive && 'border-state-destructive-border hover:bg-state-destructive-hover', - )}> - <div className='flex items-center justify-between'> - <div className='flex grow items-center gap-1'> - <BubbleX className='h-4 w-4 text-util-colors-teal-teal-700' /> - <div className='system-sm-medium text-text-primary'>{item.name}</div> - <div className='system-xs-medium text-text-tertiary'>{capitalize(item.value_type)}</div> + )} + > + <div className="flex items-center justify-between"> + <div className="flex grow items-center gap-1"> + <BubbleX className="h-4 w-4 text-util-colors-teal-teal-700" /> + <div className="system-sm-medium text-text-primary">{item.name}</div> + <div className="system-xs-medium text-text-tertiary">{capitalize(item.value_type)}</div> </div> - <div className='flex shrink-0 items-center gap-1 text-text-tertiary'> - <div className='radius-md cursor-pointer p-1 hover:bg-state-base-hover hover:text-text-secondary'> - <RiEditLine className='h-4 w-4' onClick={() => onEdit(item)}/> + <div className="flex shrink-0 items-center gap-1 text-text-tertiary"> + <div className="radius-md cursor-pointer p-1 hover:bg-state-base-hover hover:text-text-secondary"> + <RiEditLine className="h-4 w-4" onClick={() => onEdit(item)} /> </div> <div - className='radius-md cursor-pointer p-1 hover:bg-state-destructive-hover hover:text-text-destructive' + className="radius-md cursor-pointer p-1 hover:bg-state-destructive-hover hover:text-text-destructive" onMouseOver={() => setDestructive(true)} onMouseOut={() => setDestructive(false)} > - <RiDeleteBinLine className='h-4 w-4' onClick={() => onDelete(item)}/> + <RiDeleteBinLine className="h-4 w-4" onClick={() => onDelete(item)} /> </div> </div> </div> - <div className='system-xs-regular truncate text-text-tertiary'>{item.description}</div> + <div className="system-xs-regular truncate text-text-tertiary">{item.description}</div> </div> ) } diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx index 9d19b61093..1fe4e5fe5a 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx @@ -1,15 +1,15 @@ 'use client' +import type { ConversationVariable } from '@/app/components/workflow/types' +import { RiAddLine } from '@remixicon/react' import React from 'react' import { useTranslation } from 'react-i18next' -import { RiAddLine } from '@remixicon/react' import Button from '@/app/components/base/button' -import VariableModal from '@/app/components/workflow/panel/chat-variable-panel/components/variable-modal' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import type { ConversationVariable } from '@/app/components/workflow/types' +import VariableModal from '@/app/components/workflow/panel/chat-variable-panel/components/variable-modal' type Props = { open: boolean @@ -38,7 +38,7 @@ const VariableModalTrigger = ({ if (open) onClose() }} - placement='left-start' + placement="left-start" offset={{ mainAxis: 8, alignmentAxis: showTip ? -278 : -48, @@ -48,13 +48,14 @@ const VariableModalTrigger = ({ setOpen(v => !v) if (open) onClose() - }}> - <Button variant='primary'> - <RiAddLine className='mr-1 h-4 w-4' /> - <span className='system-sm-medium'>{t('workflow.chatVariable.button')}</span> + }} + > + <Button variant="primary"> + <RiAddLine className="mr-1 h-4 w-4" /> + <span className="system-sm-medium">{t('workflow.chatVariable.button')}</span> </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[11]'> + <PortalToFollowElemContent className="z-[11]"> <VariableModal chatVar={chatVar} onSave={onSave} diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx index 15f8a081fb..e30da0fff3 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx @@ -1,23 +1,19 @@ +import type { ConversationVariable } from '@/app/components/workflow/types' +import { RiCloseLine, RiDraftLine, RiInputField } from '@remixicon/react' import React, { useCallback, useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { v4 as uuid4 } from 'uuid' -import { RiCloseLine, RiDraftLine, RiInputField } from '@remixicon/react' -import VariableTypeSelector from '@/app/components/workflow/panel/chat-variable-panel/components/variable-type-select' -import ObjectValueList from '@/app/components/workflow/panel/chat-variable-panel/components/object-value-list' -import { DEFAULT_OBJECT_VALUE } from '@/app/components/workflow/panel/chat-variable-panel/components/object-value-item' -import ArrayValueList from '@/app/components/workflow/panel/chat-variable-panel/components/array-value-list' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' -import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' import { ToastContext } from '@/app/components/base/toast' -import { useStore } from '@/app/components/workflow/store' -import type { ConversationVariable } from '@/app/components/workflow/types' +import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' +import ArrayValueList from '@/app/components/workflow/panel/chat-variable-panel/components/array-value-list' +import { DEFAULT_OBJECT_VALUE } from '@/app/components/workflow/panel/chat-variable-panel/components/object-value-item' +import ObjectValueList from '@/app/components/workflow/panel/chat-variable-panel/components/object-value-list' +import VariableTypeSelector from '@/app/components/workflow/panel/chat-variable-panel/components/variable-type-select' import { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type' -import { cn } from '@/utils/classnames' -import BoolValue from './bool-value' -import ArrayBoolList from './array-bool-list' import { arrayBoolPlaceholder, arrayNumberPlaceholder, @@ -25,7 +21,11 @@ import { arrayStringPlaceholder, objectPlaceholder, } from '@/app/components/workflow/panel/chat-variable-panel/utils' +import { useStore } from '@/app/components/workflow/store' +import { cn } from '@/utils/classnames' import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' +import ArrayBoolList from './array-bool-list' +import BoolValue from './bool-value' export type ModalPropsType = { chatVar?: ConversationVariable @@ -147,7 +147,7 @@ const ChatVariableModal = ({ setEditInJSON(true) if (v === ChatVarType.String || v === ChatVarType.Number || v === ChatVarType.Object) setEditInJSON(false) - if(v === ChatVarType.Boolean) + if (v === ChatVarType.Boolean) setValue(false) if (v === ChatVarType.ArrayBoolean) setValue([false]) @@ -197,8 +197,8 @@ const ChatVariableModal = ({ } } - if(type === ChatVarType.ArrayBoolean) { - if(editInJSON) + if (type === ChatVarType.ArrayBoolean) { + if (editInJSON) setEditorContent(JSON.stringify(value.map((item: boolean) => item ? 'True' : 'False'))) } setEditInJSON(editInJSON) @@ -213,7 +213,7 @@ const ChatVariableModal = ({ setEditorContent(content) try { let newValue = JSON.parse(content) - if(type === ChatVarType.ArrayBoolean) { + if (type === ChatVarType.ArrayBoolean) { newValue = newValue.map((item: string | boolean) => { if (item === 'True' || item === 'true' || item === true) return true @@ -271,75 +271,75 @@ const ChatVariableModal = ({ <div className={cn('flex h-full w-[360px] flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl', type === ChatVarType.Object && 'w-[480px]')} > - <div className='system-xl-semibold mb-3 flex shrink-0 items-center justify-between p-4 pb-0 text-text-primary'> + <div className="system-xl-semibold mb-3 flex shrink-0 items-center justify-between p-4 pb-0 text-text-primary"> {!chatVar ? t('workflow.chatVariable.modal.title') : t('workflow.chatVariable.modal.editTitle')} - <div className='flex items-center'> + <div className="flex items-center"> <div - className='flex h-6 w-6 cursor-pointer items-center justify-center' + className="flex h-6 w-6 cursor-pointer items-center justify-center" onClick={onClose} > - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> </div> - <div className='max-h-[480px] overflow-y-auto px-4 py-2'> + <div className="max-h-[480px] overflow-y-auto px-4 py-2"> {/* name */} - <div className='mb-4'> - <div className='system-sm-semibold mb-1 flex h-6 items-center text-text-secondary'>{t('workflow.chatVariable.modal.name')}</div> - <div className='flex'> + <div className="mb-4"> + <div className="system-sm-semibold mb-1 flex h-6 items-center text-text-secondary">{t('workflow.chatVariable.modal.name')}</div> + <div className="flex"> <Input placeholder={t('workflow.chatVariable.modal.namePlaceholder') || ''} value={name} onChange={handleVarNameChange} onBlur={e => checkVariableName(e.target.value)} - type='text' + type="text" /> </div> </div> {/* type */} - <div className='mb-4'> - <div className='system-sm-semibold mb-1 flex h-6 items-center text-text-secondary'>{t('workflow.chatVariable.modal.type')}</div> - <div className='flex'> + <div className="mb-4"> + <div className="system-sm-semibold mb-1 flex h-6 items-center text-text-secondary">{t('workflow.chatVariable.modal.type')}</div> + <div className="flex"> <VariableTypeSelector value={type} list={typeList} onSelect={handleTypeChange} - popupClassName='w-[327px]' + popupClassName="w-[327px]" /> </div> </div> {/* default value */} - <div className='mb-4'> - <div className='system-sm-semibold mb-1 flex h-6 items-center justify-between text-text-secondary'> + <div className="mb-4"> + <div className="system-sm-semibold mb-1 flex h-6 items-center justify-between text-text-secondary"> <div>{t('workflow.chatVariable.modal.value')}</div> {(type === ChatVarType.ArrayString || type === ChatVarType.ArrayNumber || type === ChatVarType.ArrayBoolean) && ( <Button - variant='ghost' - size='small' - className='text-text-tertiary' + variant="ghost" + size="small" + className="text-text-tertiary" onClick={() => handleEditorChange(!editInJSON)} > - {editInJSON ? <RiInputField className='mr-1 h-3.5 w-3.5' /> : <RiDraftLine className='mr-1 h-3.5 w-3.5' />} + {editInJSON ? <RiInputField className="mr-1 h-3.5 w-3.5" /> : <RiDraftLine className="mr-1 h-3.5 w-3.5" />} {editInJSON ? t('workflow.chatVariable.modal.oneByOne') : t('workflow.chatVariable.modal.editInJSON')} </Button> )} {type === ChatVarType.Object && ( <Button - variant='ghost' - size='small' - className='text-text-tertiary' + variant="ghost" + size="small" + className="text-text-tertiary" onClick={() => handleEditorChange(!editInJSON)} > - {editInJSON ? <RiInputField className='mr-1 h-3.5 w-3.5' /> : <RiDraftLine className='mr-1 h-3.5 w-3.5' />} + {editInJSON ? <RiInputField className="mr-1 h-3.5 w-3.5" /> : <RiDraftLine className="mr-1 h-3.5 w-3.5" />} {editInJSON ? t('workflow.chatVariable.modal.editInForm') : t('workflow.chatVariable.modal.editInJSON')} </Button> )} </div> - <div className='flex'> + <div className="flex"> {type === ChatVarType.String && ( // Input will remove \n\r, so use Textarea just like description area <textarea - className='system-sm-regular placeholder:system-sm-regular block h-20 w-full resize-none appearance-none rounded-lg border border-transparent bg-components-input-bg-normal p-2 text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs' + className="system-sm-regular placeholder:system-sm-regular block h-20 w-full resize-none appearance-none rounded-lg border border-transparent bg-components-input-bg-normal p-2 text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs" value={value} placeholder={t('workflow.chatVariable.modal.valuePlaceholder') || ''} onChange={e => setValue(e.target.value)} @@ -350,7 +350,7 @@ const ChatVariableModal = ({ placeholder={t('workflow.chatVariable.modal.valuePlaceholder') || ''} value={value} onChange={e => setValue(Number(e.target.value))} - type='number' + type="number" /> )} {type === ChatVarType.Boolean && ( @@ -387,13 +387,13 @@ const ChatVariableModal = ({ )} {editInJSON && ( - <div className='w-full rounded-[10px] bg-components-input-bg-normal py-2 pl-3 pr-1' style={{ height: editorMinHeight }}> + <div className="w-full rounded-[10px] bg-components-input-bg-normal py-2 pl-3 pr-1" style={{ height: editorMinHeight }}> <CodeEditor isExpand noWrapper language={CodeLanguage.json} value={editorContent} - placeholder={<div className='whitespace-pre'>{placeholder}</div>} + placeholder={<div className="whitespace-pre">{placeholder}</div>} onChange={handleEditorValueChange} /> </div> @@ -401,11 +401,11 @@ const ChatVariableModal = ({ </div> </div> {/* description */} - <div className=''> - <div className='system-sm-semibold mb-1 flex h-6 items-center text-text-secondary'>{t('workflow.chatVariable.modal.description')}</div> - <div className='flex'> + <div className=""> + <div className="system-sm-semibold mb-1 flex h-6 items-center text-text-secondary">{t('workflow.chatVariable.modal.description')}</div> + <div className="flex"> <textarea - className='system-sm-regular placeholder:system-sm-regular block h-20 w-full resize-none appearance-none rounded-lg border border-transparent bg-components-input-bg-normal p-2 text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs' + className="system-sm-regular placeholder:system-sm-regular block h-20 w-full resize-none appearance-none rounded-lg border border-transparent bg-components-input-bg-normal p-2 text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs" value={description} placeholder={t('workflow.chatVariable.modal.descriptionPlaceholder') || ''} onChange={e => setDescription(e.target.value)} @@ -413,10 +413,10 @@ const ChatVariableModal = ({ </div> </div> </div> - <div className='flex flex-row-reverse rounded-b-2xl p-4 pt-2'> - <div className='flex gap-2'> + <div className="flex flex-row-reverse rounded-b-2xl p-4 pt-2"> + <div className="flex gap-2"> <Button onClick={onClose}>{t('common.operation.cancel')}</Button> - <Button variant='primary' onClick={handleSave}>{t('common.operation.save')}</Button> + <Button variant="primary" onClick={handleSave}>{t('common.operation.save')}</Button> </div> </div> </div> diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx index 360775a06c..1922374941 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx @@ -1,6 +1,6 @@ 'use client' -import React, { useState } from 'react' import { RiArrowDownSLine, RiCheckLine } from '@remixicon/react' +import React, { useState } from 'react' import { PortalToFollowElem, PortalToFollowElemContent, @@ -29,32 +29,40 @@ const VariableTypeSelector = ({ <PortalToFollowElem open={open} onOpenChange={() => setOpen(v => !v)} - placement='bottom' + placement="bottom" > - <PortalToFollowElemTrigger className='w-full' onClick={() => setOpen(v => !v)}> + <PortalToFollowElemTrigger className="w-full" onClick={() => setOpen(v => !v)}> <div className={cn( 'flex w-full cursor-pointer items-center px-2', !inCell && 'radius-md bg-components-input-bg-normal py-1 hover:bg-state-base-hover-alt', inCell && 'py-0.5 hover:bg-state-base-hover', open && !inCell && 'bg-state-base-hover-alt hover:bg-state-base-hover-alt', open && inCell && 'bg-state-base-hover hover:bg-state-base-hover', - )}> + )} + > <div className={cn( 'system-sm-regular grow truncate p-1 text-components-input-text-filled', inCell && 'system-xs-regular text-text-secondary', - )}>{value}</div> - <RiArrowDownSLine className='ml-0.5 h-4 w-4 text-text-quaternary' /> + )} + > + {value} + </div> + <RiArrowDownSLine className="ml-0.5 h-4 w-4 text-text-quaternary" /> </div> </PortalToFollowElemTrigger> <PortalToFollowElemContent className={cn('z-[11] w-full', popupClassName)}> - <div className='radius-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'> + <div className="radius-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg"> {list.map((item: any) => ( - <div key={item} className='radius-md flex cursor-pointer items-center gap-2 py-[6px] pl-3 pr-2 hover:bg-state-base-hover' onClick={() => { - onSelect(item) - setOpen(false) - }}> - <div className='system-md-regular grow truncate text-text-secondary'>{item}</div> - {value === item && <RiCheckLine className='h-4 w-4 text-text-accent' />} + <div + key={item} + className="radius-md flex cursor-pointer items-center gap-2 py-[6px] pl-3 pr-2 hover:bg-state-base-hover" + onClick={() => { + onSelect(item) + setOpen(false) + }} + > + <div className="system-md-regular grow truncate text-text-secondary">{item}</div> + {value === item && <RiCheckLine className="h-4 w-4 text-text-accent" />} </div> ))} </div> diff --git a/web/app/components/workflow/panel/chat-variable-panel/index.tsx b/web/app/components/workflow/panel/chat-variable-panel/index.tsx index b075cabf5d..e0396d4a68 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/index.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/index.tsx @@ -1,25 +1,25 @@ +import type { + ConversationVariable, +} from '@/app/components/workflow/types' +import { RiBookOpenLine, RiCloseLine } from '@remixicon/react' import { memo, useCallback, useState, } from 'react' +import { useTranslation } from 'react-i18next' import { useStoreApi, } from 'reactflow' -import { RiBookOpenLine, RiCloseLine } from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import { useStore } from '@/app/components/workflow/store' import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' import { BubbleX, LongArrowLeft, LongArrowRight } from '@/app/components/base/icons/src/vender/line/others' import BlockIcon from '@/app/components/workflow/block-icon' -import VariableModalTrigger from '@/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger' -import VariableItem from '@/app/components/workflow/panel/chat-variable-panel/components/variable-item' -import RemoveEffectVarConfirm from '@/app/components/workflow/nodes/_base/components/remove-effect-var-confirm' -import type { - ConversationVariable, -} from '@/app/components/workflow/types' -import { findUsedVarNodes, updateNodeVars } from '@/app/components/workflow/nodes/_base/components/variable/utils' import { useNodesSyncDraft } from '@/app/components/workflow/hooks/use-nodes-sync-draft' +import RemoveEffectVarConfirm from '@/app/components/workflow/nodes/_base/components/remove-effect-var-confirm' +import { findUsedVarNodes, updateNodeVars } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import VariableItem from '@/app/components/workflow/panel/chat-variable-panel/components/variable-item' +import VariableModalTrigger from '@/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger' +import { useStore } from '@/app/components/workflow/store' import { BlockEnum } from '@/app/components/workflow/types' import { useDocLink } from '@/context/i18n' import { cn } from '@/utils/classnames' @@ -128,64 +128,68 @@ const ChatVariablePanel = () => { 'relative flex h-full w-[420px] flex-col rounded-l-2xl border border-components-panel-border bg-components-panel-bg-alt', )} > - <div className='system-xl-semibold flex shrink-0 items-center justify-between p-4 pb-0 text-text-primary'> + <div className="system-xl-semibold flex shrink-0 items-center justify-between p-4 pb-0 text-text-primary"> {t('workflow.chatVariable.panelTitle')} - <div className='flex items-center gap-1'> + <div className="flex items-center gap-1"> <ActionButton state={showTip ? ActionButtonState.Active : undefined} onClick={() => setShowTip(!showTip)}> - <RiBookOpenLine className='h-4 w-4' /> + <RiBookOpenLine className="h-4 w-4" /> </ActionButton> <div - className='flex h-6 w-6 cursor-pointer items-center justify-center' + className="flex h-6 w-6 cursor-pointer items-center justify-center" onClick={() => setShowChatVariablePanel(false)} > - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> </div> {showTip && ( - <div className='shrink-0 px-3 pb-2 pt-2.5'> - <div className='radius-2xl relative bg-background-section-burn p-3'> - <div className='system-2xs-medium-uppercase inline-block rounded-[5px] border border-divider-deep px-[5px] py-[3px] text-text-tertiary'>TIPS</div> - <div className='system-sm-regular mb-4 mt-1 text-text-secondary'> + <div className="shrink-0 px-3 pb-2 pt-2.5"> + <div className="radius-2xl relative bg-background-section-burn p-3"> + <div className="system-2xs-medium-uppercase inline-block rounded-[5px] border border-divider-deep px-[5px] py-[3px] text-text-tertiary">TIPS</div> + <div className="system-sm-regular mb-4 mt-1 text-text-secondary"> {t('workflow.chatVariable.panelDescription')} - <a target='_blank' rel='noopener noreferrer' className='text-text-accent' + <a + target="_blank" + rel="noopener noreferrer" + className="text-text-accent" href={docLink('/guides/workflow/variables#conversation-variables', { 'zh-Hans': '/guides/workflow/variables#会话变量', 'ja-JP': '/guides/workflow/variables#会話変数', - })}> + })} + > {t('workflow.chatVariable.docLink')} </a> </div> - <div className='flex items-center gap-2'> - <div className='radius-lg flex flex-col border border-workflow-block-border bg-workflow-block-bg p-3 pb-4 shadow-md'> - <BubbleX className='mb-1 h-4 w-4 shrink-0 text-util-colors-teal-teal-700' /> - <div className='system-xs-semibold text-text-secondary'>conversation_var</div> - <div className='system-2xs-regular text-text-tertiary'>String</div> + <div className="flex items-center gap-2"> + <div className="radius-lg flex flex-col border border-workflow-block-border bg-workflow-block-bg p-3 pb-4 shadow-md"> + <BubbleX className="mb-1 h-4 w-4 shrink-0 text-util-colors-teal-teal-700" /> + <div className="system-xs-semibold text-text-secondary">conversation_var</div> + <div className="system-2xs-regular text-text-tertiary">String</div> </div> - <div className='grow'> - <div className='mb-2 flex items-center gap-2 py-1'> - <div className='flex h-3 w-16 shrink-0 items-center gap-1 px-1'> - <LongArrowLeft className='h-2 grow text-text-quaternary' /> - <div className='system-2xs-medium shrink-0 text-text-tertiary'>WRITE</div> + <div className="grow"> + <div className="mb-2 flex items-center gap-2 py-1"> + <div className="flex h-3 w-16 shrink-0 items-center gap-1 px-1"> + <LongArrowLeft className="h-2 grow text-text-quaternary" /> + <div className="system-2xs-medium shrink-0 text-text-tertiary">WRITE</div> </div> - <BlockIcon className='shrink-0' type={BlockEnum.Assigner} /> - <div className='system-xs-semibold grow truncate text-text-secondary'>{t('workflow.blocks.assigner')}</div> + <BlockIcon className="shrink-0" type={BlockEnum.Assigner} /> + <div className="system-xs-semibold grow truncate text-text-secondary">{t('workflow.blocks.assigner')}</div> </div> - <div className='flex items-center gap-2 py-1'> - <div className='flex h-3 w-16 shrink-0 items-center gap-1 px-1'> - <div className='system-2xs-medium shrink-0 text-text-tertiary'>READ</div> - <LongArrowRight className='h-2 grow text-text-quaternary' /> + <div className="flex items-center gap-2 py-1"> + <div className="flex h-3 w-16 shrink-0 items-center gap-1 px-1"> + <div className="system-2xs-medium shrink-0 text-text-tertiary">READ</div> + <LongArrowRight className="h-2 grow text-text-quaternary" /> </div> - <BlockIcon className='shrink-0' type={BlockEnum.LLM} /> - <div className='system-xs-semibold grow truncate text-text-secondary'>{t('workflow.blocks.llm')}</div> + <BlockIcon className="shrink-0" type={BlockEnum.LLM} /> + <div className="system-xs-semibold grow truncate text-text-secondary">{t('workflow.blocks.llm')}</div> </div> </div> </div> - <div className='absolute right-[38px] top-[-4px] z-10 h-3 w-3 rotate-45 bg-background-section-burn' /> + <div className="absolute right-[38px] top-[-4px] z-10 h-3 w-3 rotate-45 bg-background-section-burn" /> </div> </div> )} - <div className='shrink-0 px-4 pb-3 pt-2'> + <div className="shrink-0 px-4 pb-3 pt-2"> <VariableModalTrigger open={showVariableModal} setOpen={setShowVariableModal} @@ -195,7 +199,7 @@ const ChatVariablePanel = () => { onClose={() => setCurrentVar(undefined)} /> </div> - <div className='grow overflow-y-auto rounded-b-2xl px-4'> + <div className="grow overflow-y-auto rounded-b-2xl px-4"> {varList.map(chatVar => ( <VariableItem key={chatVar.id} diff --git a/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx b/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx index 682e91ea81..8ff356cad7 100644 --- a/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx @@ -1,28 +1,28 @@ +import type { StartNodeType } from '../../nodes/start/types' +import type { ChatWrapperRefType } from './index' +import type { ChatItem, OnSend } from '@/app/components/base/chat/types' +import type { FileEntity } from '@/app/components/base/file-uploader/types' import { memo, useCallback, useEffect, useImperativeHandle, useMemo } from 'react' import { useNodes } from 'reactflow' -import { BlockEnum } from '../../types' -import { - useStore, - useWorkflowStore, -} from '../../store' -import type { StartNodeType } from '../../nodes/start/types' -import Empty from './empty' -import UserInput from './user-input' -import ConversationVariableModal from './conversation-variable-modal' -import { useChat } from './hooks' -import type { ChatWrapperRefType } from './index' +import { useStore as useAppStore } from '@/app/components/app/store' import Chat from '@/app/components/base/chat/chat' -import type { ChatItem, OnSend } from '@/app/components/base/chat/types' +import { getLastAnswer, isValidGeneratedAnswer } from '@/app/components/base/chat/utils' import { useFeatures } from '@/app/components/base/features/hooks' +import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types' +import { useEventEmitterContextContext } from '@/context/event-emitter' import { fetchSuggestedQuestions, stopChatMessageResponding, } from '@/service/debug' -import { useStore as useAppStore } from '@/app/components/app/store' -import { getLastAnswer, isValidGeneratedAnswer } from '@/app/components/base/chat/utils' -import type { FileEntity } from '@/app/components/base/file-uploader/types' -import { useEventEmitterContextContext } from '@/context/event-emitter' -import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types' +import { + useStore, + useWorkflowStore, +} from '../../store' +import { BlockEnum } from '../../types' +import ConversationVariableModal from './conversation-variable-modal' +import Empty from './empty' +import { useChat } from './hooks' +import UserInput from './user-input' type ChatWrapperProps = { showConversationVariableModal: boolean @@ -39,7 +39,7 @@ const ChatWrapper = ( showInputsFieldsPanel, onHide, }: ChatWrapperProps & { - ref: React.RefObject<ChatWrapperRefType>; + ref: React.RefObject<ChatWrapperRefType> }, ) => { const nodes = useNodes<StartNodeType>() @@ -118,11 +118,7 @@ const ChatWrapper = ( const doRegenerate = useCallback((chatItem: ChatItem, editedQuestion?: { message: string, files?: FileEntity[] }) => { const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)! const parentAnswer = chatList.find(item => item.id === question.parentMessageId) - doSend(editedQuestion ? editedQuestion.message : question.content, - editedQuestion ? editedQuestion.files : question.message_files, - true, - isValidGeneratedAnswer(parentAnswer) ? parentAnswer : null, - ) + doSend(editedQuestion ? editedQuestion.message : question.content, editedQuestion ? editedQuestion.files : question.message_files, true, isValidGeneratedAnswer(parentAnswer) ? parentAnswer : null) }, [chatList, doSend]) const { eventEmitter } = useEventEmitterContextContext() @@ -160,10 +156,10 @@ const ChatWrapper = ( } as any} chatList={chatList} isResponding={isResponding} - chatContainerClassName='px-3' - chatContainerInnerClassName='pt-6 w-full max-w-full mx-auto' - chatFooterClassName='px-4 rounded-bl-2xl' - chatFooterInnerClassName='pb-0' + chatContainerClassName="px-3" + chatContainerInnerClassName="pt-6 w-full max-w-full mx-auto" + chatFooterClassName="px-4 rounded-bl-2xl" + chatFooterInnerClassName="pb-0" showFileUpload showFeatureBar onFeatureBarClick={setShowFeaturesPanel} @@ -185,7 +181,7 @@ const ChatWrapper = ( noSpacing suggestedQuestions={suggestedQuestions} showPromptLog - chatAnswerContainerInner='!pr-2' + chatAnswerContainerInner="!pr-2" switchSibling={setTargetMessageId} /> {showConversationVariableModal && ( diff --git a/web/app/components/workflow/panel/debug-and-preview/conversation-variable-modal.tsx b/web/app/components/workflow/panel/debug-and-preview/conversation-variable-modal.tsx index 043a842f23..117247901e 100644 --- a/web/app/components/workflow/panel/debug-and-preview/conversation-variable-modal.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/conversation-variable-modal.tsx @@ -1,27 +1,26 @@ 'use client' -import React, { useCallback } from 'react' -import { useMount } from 'ahooks' -import { useTranslation } from 'react-i18next' -import { capitalize } from 'lodash-es' -import copy from 'copy-to-clipboard' +import type { + ConversationVariable, +} from '@/app/components/workflow/types' import { RiCloseLine } from '@remixicon/react' -import Modal from '@/app/components/base/modal' -import { BubbleX } from '@/app/components/base/icons/src/vender/line/others' -import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' +import { useMount } from 'ahooks' +import copy from 'copy-to-clipboard' +import { capitalize, noop } from 'lodash-es' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import { Copy, CopyCheck, } from '@/app/components/base/icons/src/vender/line/files' -import { useStore } from '@/app/components/workflow/store' -import type { - ConversationVariable, -} from '@/app/components/workflow/types' -import { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type' +import { BubbleX } from '@/app/components/base/icons/src/vender/line/others' +import Modal from '@/app/components/base/modal' +import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' +import { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type' +import { useStore } from '@/app/components/workflow/store' import useTimestamp from '@/hooks/use-timestamp' import { fetchCurrentValueOfConversationVariable } from '@/service/workflow' import { cn } from '@/utils/classnames' -import { noop } from 'lodash-es' export type Props = { conversationID: string @@ -80,14 +79,14 @@ const ConversationVariableModal = ({ onClose={noop} className={cn('h-[640px] w-[920px] max-w-[920px] p-0')} > - <div className='absolute right-4 top-4 cursor-pointer p-2' onClick={onHide}> - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <div className="absolute right-4 top-4 cursor-pointer p-2" onClick={onHide}> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> - <div className='flex h-full w-full'> + <div className="flex h-full w-full"> {/* LEFT */} - <div className='flex h-full w-[224px] shrink-0 flex-col border-r border-divider-burn bg-background-sidenav-bg'> - <div className='system-xl-semibold shrink-0 pb-3 pl-5 pr-4 pt-5 text-text-primary'>{t('workflow.chatVariable.panelTitle')}</div> - <div className='grow overflow-y-auto px-3 py-2'> + <div className="flex h-full w-[224px] shrink-0 flex-col border-r border-divider-burn bg-background-sidenav-bg"> + <div className="system-xl-semibold shrink-0 pb-3 pl-5 pr-4 pt-5 text-text-primary">{t('workflow.chatVariable.panelTitle')}</div> + <div className="grow overflow-y-auto px-3 py-2"> {varList.map(chatVar => ( <div key={chatVar.id} className={cn('radius-md group mb-0.5 flex cursor-pointer items-center p-2 hover:bg-state-base-hover', currentVar.id === chatVar.id && 'bg-state-base-hover')} onClick={() => setCurrentVar(chatVar)}> <BubbleX className={cn('mr-1 h-4 w-4 shrink-0 text-text-tertiary group-hover:text-util-colors-teal-teal-700', currentVar.id === chatVar.id && 'text-util-colors-teal-teal-700')} /> @@ -97,40 +96,46 @@ const ConversationVariableModal = ({ </div> </div> {/* RIGHT */} - <div className='flex h-full w-0 grow flex-col bg-components-panel-bg'> - <div className='shrink-0 p-4 pb-2'> - <div className='flex items-center gap-1 py-1'> - <div className='system-xl-semibold text-text-primary'>{currentVar.name}</div> - <div className='system-xs-medium text-text-tertiary'>{capitalize(currentVar.value_type)}</div> + <div className="flex h-full w-0 grow flex-col bg-components-panel-bg"> + <div className="shrink-0 p-4 pb-2"> + <div className="flex items-center gap-1 py-1"> + <div className="system-xl-semibold text-text-primary">{currentVar.name}</div> + <div className="system-xs-medium text-text-tertiary">{capitalize(currentVar.value_type)}</div> </div> </div> - <div className='flex h-0 grow flex-col p-4 pt-2'> - <div className='mb-2 flex shrink-0 items-center gap-2'> - <div className='system-xs-medium-uppercase shrink-0 text-text-tertiary'>{t('workflow.chatVariable.storedContent').toLocaleUpperCase()}</div> - <div className='h-px grow' style={{ - background: 'linear-gradient(to right, rgba(16, 24, 40, 0.08) 0%, rgba(255, 255, 255) 100%)', - }}></div> + <div className="flex h-0 grow flex-col p-4 pt-2"> + <div className="mb-2 flex shrink-0 items-center gap-2"> + <div className="system-xs-medium-uppercase shrink-0 text-text-tertiary">{t('workflow.chatVariable.storedContent').toLocaleUpperCase()}</div> + <div + className="h-px grow" + style={{ + background: 'linear-gradient(to right, rgba(16, 24, 40, 0.08) 0%, rgba(255, 255, 255) 100%)', + }} + > + </div> {latestValueTimestampMap[currentVar.id] && ( - <div className='system-xs-regular shrink-0 text-text-tertiary'>{t('workflow.chatVariable.updatedAt')}{formatTime(latestValueTimestampMap[currentVar.id], t('appLog.dateTimeFormat') as string)}</div> + <div className="system-xs-regular shrink-0 text-text-tertiary"> + {t('workflow.chatVariable.updatedAt')} + {formatTime(latestValueTimestampMap[currentVar.id], t('appLog.dateTimeFormat') as string)} + </div> )} </div> - <div className='grow overflow-y-auto'> + <div className="grow overflow-y-auto"> {currentVar.value_type !== ChatVarType.Number && currentVar.value_type !== ChatVarType.String && ( - <div className='flex h-full flex-col rounded-lg bg-components-input-bg-normal px-2 pb-2'> - <div className='flex h-7 shrink-0 items-center justify-between pl-3 pr-2 pt-1'> - <div className='system-xs-semibold text-text-secondary'>JSON</div> - <div className='flex items-center p-1'> + <div className="flex h-full flex-col rounded-lg bg-components-input-bg-normal px-2 pb-2"> + <div className="flex h-7 shrink-0 items-center justify-between pl-3 pr-2 pt-1"> + <div className="system-xs-semibold text-text-secondary">JSON</div> + <div className="flex items-center p-1"> {!isCopied ? ( - <Copy className='h-4 w-4 cursor-pointer text-text-tertiary' onClick={handleCopy} /> - ) + <Copy className="h-4 w-4 cursor-pointer text-text-tertiary" onClick={handleCopy} /> + ) : ( - <CopyCheck className='h-4 w-4 text-text-tertiary' /> - ) - } + <CopyCheck className="h-4 w-4 text-text-tertiary" /> + )} </div> </div> - <div className='grow pl-4'> + <div className="grow pl-4"> <CodeEditor readOnly noWrapper @@ -143,7 +148,7 @@ const ConversationVariableModal = ({ </div> )} {(currentVar.value_type === ChatVarType.Number || currentVar.value_type === ChatVarType.String) && ( - <div className='system-md-regular h-full overflow-y-auto overflow-x-hidden rounded-lg bg-components-input-bg-normal px-4 py-3 text-components-input-text-filled'>{latestValueMap[currentVar.id] || ''}</div> + <div className="system-md-regular h-full overflow-y-auto overflow-x-hidden rounded-lg bg-components-input-bg-normal px-4 py-3 text-components-input-text-filled">{latestValueMap[currentVar.id] || ''}</div> )} </div> </div> diff --git a/web/app/components/workflow/panel/debug-and-preview/empty.tsx b/web/app/components/workflow/panel/debug-and-preview/empty.tsx index 7f120c3536..6c5e9ca5c2 100644 --- a/web/app/components/workflow/panel/debug-and-preview/empty.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/empty.tsx @@ -5,11 +5,11 @@ const Empty = () => { const { t } = useTranslation() return ( - <div className='absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'> - <div className='mb-2 flex justify-center'> - <ChatBotSlim className='h-12 w-12 text-gray-300' /> + <div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"> + <div className="mb-2 flex justify-center"> + <ChatBotSlim className="h-12 w-12 text-gray-300" /> </div> - <div className='w-[256px] text-center text-[13px] text-gray-400'> + <div className="w-[256px] text-center text-[13px] text-gray-400"> {t('workflow.common.previewPlaceholder')} </div> </div> 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 4cc6e92ace..6eb1ea0b76 100644 --- a/web/app/components/workflow/panel/debug-and-preview/hooks.ts +++ b/web/app/components/workflow/panel/debug-and-preview/hooks.ts @@ -1,3 +1,12 @@ +import type { InputForm } from '@/app/components/base/chat/chat/type' +import type { + ChatItem, + ChatItemInTree, + Inputs, +} from '@/app/components/base/chat/types' +import type { FileEntity } from '@/app/components/base/file-uploader/types' +import { produce, setAutoFreeze } from 'immer' +import { uniqBy } from 'lodash-es' import { useCallback, useEffect, @@ -6,35 +15,26 @@ import { useState, } from 'react' import { useTranslation } from 'react-i18next' -import { produce, setAutoFreeze } from 'immer' -import { uniqBy } from 'lodash-es' -import { - useSetWorkflowVarsWithValue, - useWorkflowRun, -} from '../../hooks' -import { NodeRunningStatus, WorkflowRunningStatus } from '../../types' -import { useWorkflowStore } from '../../store' -import { DEFAULT_ITER_TIMES, DEFAULT_LOOP_TIMES } from '../../constants' -import type { - ChatItem, - ChatItemInTree, - Inputs, -} from '@/app/components/base/chat/types' -import type { InputForm } from '@/app/components/base/chat/chat/type' import { getProcessedInputs, processOpeningStatement, } from '@/app/components/base/chat/chat/utils' -import { useToastContext } from '@/app/components/base/toast' -import { TransferMethod } from '@/types/app' +import { getThreadMessages } from '@/app/components/base/chat/utils' import { getProcessedFiles, getProcessedFilesFromResponse, } from '@/app/components/base/file-uploader/utils' -import type { FileEntity } from '@/app/components/base/file-uploader/types' -import { getThreadMessages } from '@/app/components/base/chat/utils' +import { useToastContext } from '@/app/components/base/toast' import { useInvalidAllLastRun } from '@/service/use-workflow' +import { TransferMethod } from '@/types/app' +import { DEFAULT_ITER_TIMES, DEFAULT_LOOP_TIMES } from '../../constants' +import { + useSetWorkflowVarsWithValue, + useWorkflowRun, +} from '../../hooks' import { useHooksStore } from '../../hooks-store' +import { useWorkflowStore } from '../../store' +import { NodeRunningStatus, WorkflowRunningStatus } from '../../types' type GetAbortController = (abortController: AbortController) => void type SendCallback = { diff --git a/web/app/components/workflow/panel/debug-and-preview/index.tsx b/web/app/components/workflow/panel/debug-and-preview/index.tsx index 7f8deb3a74..3005b68a9c 100644 --- a/web/app/components/workflow/panel/debug-and-preview/index.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/index.tsx @@ -1,3 +1,7 @@ +import type { StartNodeType } from '../../nodes/start/types' + +import { RiCloseLine, RiEqualizer2Line } from '@remixicon/react' +import { debounce, noop } from 'lodash-es' import { memo, useCallback, @@ -5,25 +9,21 @@ import { useRef, useState, } from 'react' - -import { RiCloseLine, RiEqualizer2Line } from '@remixicon/react' import { useTranslation } from 'react-i18next' import { useNodes } from 'reactflow' +import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' +import { RefreshCcw01 } from '@/app/components/base/icons/src/vender/line/arrows' +import Tooltip from '@/app/components/base/tooltip' +import { useEdgesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-edges-interactions-without-sync' +import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync' +import { useStore } from '@/app/components/workflow/store' +import { cn } from '@/utils/classnames' import { useWorkflowInteractions, } from '../../hooks' -import { useEdgesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-edges-interactions-without-sync' -import { useNodesInteractionsWithoutSync } from '@/app/components/workflow/hooks/use-nodes-interactions-without-sync' -import { BlockEnum } from '../../types' -import type { StartNodeType } from '../../nodes/start/types' import { useResizePanel } from '../../nodes/_base/hooks/use-resize-panel' +import { BlockEnum } from '../../types' import ChatWrapper from './chat-wrapper' -import { cn } from '@/utils/classnames' -import { RefreshCcw01 } from '@/app/components/base/icons/src/vender/line/arrows' -import Tooltip from '@/app/components/base/tooltip' -import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' -import { useStore } from '@/app/components/workflow/store' -import { debounce, noop } from 'lodash-es' export type ChatWrapperRefType = { handleRestart: () => void @@ -81,11 +81,12 @@ const DebugAndPreview = () => { }) return ( - <div className='relative h-full'> + <div className="relative h-full"> <div ref={triggerRef} - className='absolute -left-1 top-0 flex h-full w-1 cursor-col-resize resize-x items-center justify-center'> - <div className='h-10 w-0.5 rounded-sm bg-state-base-handle hover:h-full hover:bg-state-accent-solid active:h-full active:bg-state-accent-solid'></div> + className="absolute -left-1 top-0 flex h-full w-1 cursor-col-resize resize-x items-center justify-center" + > + <div className="h-10 w-0.5 rounded-sm bg-state-base-handle hover:h-full hover:bg-state-accent-solid active:h-full active:bg-state-accent-solid"></div> </div> <div ref={containerRef} @@ -94,38 +95,38 @@ const DebugAndPreview = () => { )} style={{ width: `${panelWidth}px` }} > - <div className='system-xl-semibold flex shrink-0 items-center justify-between px-4 pb-2 pt-3 text-text-primary'> - <div className='h-8'>{t('workflow.common.debugAndPreview').toLocaleUpperCase()}</div> - <div className='flex items-center gap-1'> + <div className="system-xl-semibold flex shrink-0 items-center justify-between px-4 pb-2 pt-3 text-text-primary"> + <div className="h-8">{t('workflow.common.debugAndPreview').toLocaleUpperCase()}</div> + <div className="flex items-center gap-1"> <Tooltip popupContent={t('common.operation.refresh')} > <ActionButton onClick={() => handleRestartChat()}> - <RefreshCcw01 className='h-4 w-4' /> + <RefreshCcw01 className="h-4 w-4" /> </ActionButton> </Tooltip> {visibleVariables.length > 0 && ( - <div className='relative'> + <div className="relative"> <Tooltip popupContent={t('workflow.panel.userInputField')} > <ActionButton state={expanded ? ActionButtonState.Active : undefined} onClick={() => setExpanded(!expanded)}> - <RiEqualizer2Line className='h-4 w-4' /> + <RiEqualizer2Line className="h-4 w-4" /> </ActionButton> </Tooltip> - {expanded && <div className='absolute bottom-[-17px] right-[5px] z-10 h-3 w-3 rotate-45 border-l-[0.5px] border-t-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg' />} + {expanded && <div className="absolute bottom-[-17px] right-[5px] z-10 h-3 w-3 rotate-45 border-l-[0.5px] border-t-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg" />} </div> )} - <div className='mx-3 h-3.5 w-[1px] bg-divider-regular'></div> + <div className="mx-3 h-3.5 w-[1px] bg-divider-regular"></div> <div - className='flex h-6 w-6 cursor-pointer items-center justify-center' + className="flex h-6 w-6 cursor-pointer items-center justify-center" onClick={handleCancelDebugAndPreviewPanel} > - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> </div> - <div className='grow overflow-y-auto rounded-b-2xl'> + <div className="grow overflow-y-auto rounded-b-2xl"> <ChatWrapper ref={chatRef} showConversationVariableModal={showConversationVariableModal} diff --git a/web/app/components/workflow/panel/debug-and-preview/user-input.tsx b/web/app/components/workflow/panel/debug-and-preview/user-input.tsx index 75acfac8b2..8b438d995a 100644 --- a/web/app/components/workflow/panel/debug-and-preview/user-input.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/user-input.tsx @@ -1,15 +1,15 @@ +import type { StartNodeType } from '../../nodes/start/types' import { memo, } from 'react' import { useNodes } from 'reactflow' +import { cn } from '@/utils/classnames' import FormItem from '../../nodes/_base/components/before-run-form/form-item' -import { BlockEnum } from '../../types' import { useStore, useWorkflowStore, } from '../../store' -import type { StartNodeType } from '../../nodes/start/types' -import { cn } from '@/utils/classnames' +import { BlockEnum } from '../../types' const UserInput = () => { const workflowStore = useWorkflowStore() @@ -36,11 +36,11 @@ const UserInput = () => { return ( <div className={cn('relative z-[1] rounded-xl border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg shadow-xs')}> - <div className='px-4 pb-4 pt-3'> + <div className="px-4 pb-4 pt-3"> {visibleVariables.map((variable, index) => ( <div key={variable.variable} - className='mb-4 last-of-type:mb-0' + className="mb-4 last-of-type:mb-0" > <FormItem autoFocus={index === 0} diff --git a/web/app/components/workflow/panel/env-panel/env-item.tsx b/web/app/components/workflow/panel/env-panel/env-item.tsx index f0afe4d6d0..64d6610643 100644 --- a/web/app/components/workflow/panel/env-panel/env-item.tsx +++ b/web/app/components/workflow/panel/env-panel/env-item.tsx @@ -1,9 +1,9 @@ -import { memo, useState } from 'react' -import { capitalize } from 'lodash-es' +import type { EnvironmentVariable } from '@/app/components/workflow/types' import { RiDeleteBinLine, RiEditLine, RiLock2Line } from '@remixicon/react' +import { capitalize } from 'lodash-es' +import { memo, useState } from 'react' import { Env } from '@/app/components/base/icons/src/vender/line/others' import { useStore } from '@/app/components/workflow/store' -import type { EnvironmentVariable } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' type EnvItemProps = { @@ -24,37 +24,36 @@ const EnvItem = ({ <div className={cn( 'radius-md group mb-1 border border-components-panel-border-subtle bg-components-panel-on-panel-item-bg shadow-xs hover:bg-components-panel-on-panel-item-bg-hover', destructive && 'border-state-destructive-border hover:bg-state-destructive-hover', - )}> - <div className='px-2.5 py-2'> - <div className='flex items-center justify-between'> - <div className='flex grow items-center gap-1'> - <Env className='h-4 w-4 text-util-colors-violet-violet-600' /> - <div className='system-sm-medium text-text-primary'>{env.name}</div> - <div className='system-xs-medium text-text-tertiary'>{capitalize(env.value_type)}</div> - {env.value_type === 'secret' && <RiLock2Line className='h-3 w-3 text-text-tertiary' />} + )} + > + <div className="px-2.5 py-2"> + <div className="flex items-center justify-between"> + <div className="flex grow items-center gap-1"> + <Env className="h-4 w-4 text-util-colors-violet-violet-600" /> + <div className="system-sm-medium text-text-primary">{env.name}</div> + <div className="system-xs-medium text-text-tertiary">{capitalize(env.value_type)}</div> + {env.value_type === 'secret' && <RiLock2Line className="h-3 w-3 text-text-tertiary" />} </div> - <div className='flex shrink-0 items-center gap-1 text-text-tertiary'> - <div className='radius-md cursor-pointer p-1 hover:bg-state-base-hover hover:text-text-secondary'> - <RiEditLine className='h-4 w-4' onClick={() => onEdit(env)}/> + <div className="flex shrink-0 items-center gap-1 text-text-tertiary"> + <div className="radius-md cursor-pointer p-1 hover:bg-state-base-hover hover:text-text-secondary"> + <RiEditLine className="h-4 w-4" onClick={() => onEdit(env)} /> </div> <div - className='radius-md cursor-pointer p-1 hover:bg-state-destructive-hover hover:text-text-destructive' + className="radius-md cursor-pointer p-1 hover:bg-state-destructive-hover hover:text-text-destructive" onMouseOver={() => setDestructive(true)} onMouseOut={() => setDestructive(false)} > - <RiDeleteBinLine className='h-4 w-4' onClick={() => onDelete(env)} /> + <RiDeleteBinLine className="h-4 w-4" onClick={() => onDelete(env)} /> </div> </div> </div> - <div className='system-xs-regular truncate text-text-tertiary'>{env.value_type === 'secret' ? envSecrets[env.id] : env.value}</div> + <div className="system-xs-regular truncate text-text-tertiary">{env.value_type === 'secret' ? envSecrets[env.id] : env.value}</div> </div> {env.description && ( <> - <div className={'h-[0.5px] bg-divider-subtle'} /> - <div className={cn('rounded-bl-[8px] rounded-br-[8px] bg-background-default-subtle px-2.5 py-2 group-hover:bg-transparent', - destructive && 'bg-state-destructive-hover hover:bg-state-destructive-hover', - )}> - <div className='system-xs-regular truncate text-text-tertiary'>{env.description}</div> + <div className="h-[0.5px] bg-divider-subtle" /> + <div className={cn('rounded-bl-[8px] rounded-br-[8px] bg-background-default-subtle px-2.5 py-2 group-hover:bg-transparent', destructive && 'bg-state-destructive-hover hover:bg-state-destructive-hover')}> + <div className="system-xs-regular truncate text-text-tertiary">{env.description}</div> </div> </> )} diff --git a/web/app/components/workflow/panel/env-panel/index.tsx b/web/app/components/workflow/panel/env-panel/index.tsx index 47aeb94b47..b11159cfbb 100644 --- a/web/app/components/workflow/panel/env-panel/index.tsx +++ b/web/app/components/workflow/panel/env-panel/index.tsx @@ -1,23 +1,23 @@ +import type { + EnvironmentVariable, +} from '@/app/components/workflow/types' +import { RiCloseLine } from '@remixicon/react' import { memo, useCallback, useState, } from 'react' +import { useTranslation } from 'react-i18next' import { useStoreApi, } from 'reactflow' -import { RiCloseLine } from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import { useStore } from '@/app/components/workflow/store' -import VariableTrigger from '@/app/components/workflow/panel/env-panel/variable-trigger' -import EnvItem from '@/app/components/workflow/panel/env-panel/env-item' -import type { - EnvironmentVariable, -} from '@/app/components/workflow/types' -import { findUsedVarNodes, updateNodeVars } from '@/app/components/workflow/nodes/_base/components/variable/utils' -import RemoveEffectVarConfirm from '@/app/components/workflow/nodes/_base/components/remove-effect-var-confirm' -import { cn } from '@/utils/classnames' import { useNodesSyncDraft } from '@/app/components/workflow/hooks/use-nodes-sync-draft' +import RemoveEffectVarConfirm from '@/app/components/workflow/nodes/_base/components/remove-effect-var-confirm' +import { findUsedVarNodes, updateNodeVars } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import EnvItem from '@/app/components/workflow/panel/env-panel/env-item' +import VariableTrigger from '@/app/components/workflow/panel/env-panel/variable-trigger' +import { useStore } from '@/app/components/workflow/store' +import { cn } from '@/utils/classnames' const EnvPanel = () => { const { t } = useTranslation() @@ -152,19 +152,19 @@ const EnvPanel = () => { 'relative flex h-full w-[420px] flex-col rounded-l-2xl border border-components-panel-border bg-components-panel-bg-alt', )} > - <div className='system-xl-semibold flex shrink-0 items-center justify-between p-4 pb-0 text-text-primary'> + <div className="system-xl-semibold flex shrink-0 items-center justify-between p-4 pb-0 text-text-primary"> {t('workflow.env.envPanelTitle')} - <div className='flex items-center'> + <div className="flex items-center"> <div - className='flex h-6 w-6 cursor-pointer items-center justify-center' + className="flex h-6 w-6 cursor-pointer items-center justify-center" onClick={() => setShowEnvPanel(false)} > - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> </div> - <div className='system-sm-regular shrink-0 px-4 py-1 text-text-tertiary'>{t('workflow.env.envDescription')}</div> - <div className='shrink-0 px-4 pb-3 pt-2'> + <div className="system-sm-regular shrink-0 px-4 py-1 text-text-tertiary">{t('workflow.env.envDescription')}</div> + <div className="shrink-0 px-4 pb-3 pt-2"> <VariableTrigger open={showVariableModal} setOpen={setShowVariableModal} @@ -173,7 +173,7 @@ const EnvPanel = () => { onClose={() => setCurrentVar(undefined)} /> </div> - <div className='grow overflow-y-auto rounded-b-2xl px-4'> + <div className="grow overflow-y-auto rounded-b-2xl px-4"> {envList.map(env => ( <EnvItem key={env.id} 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 acf3ca0a0b..6cf193fe96 100644 --- a/web/app/components/workflow/panel/env-panel/variable-modal.tsx +++ b/web/app/components/workflow/panel/env-panel/variable-modal.tsx @@ -1,14 +1,14 @@ +import type { EnvironmentVariable } from '@/app/components/workflow/types' +import { RiCloseLine } from '@remixicon/react' import React, { useEffect } from 'react' import { useTranslation } from 'react-i18next' -import { v4 as uuid4 } from 'uuid' -import { RiCloseLine } from '@remixicon/react' import { useContext } from 'use-context-selector' +import { v4 as uuid4 } from 'uuid' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' -import Tooltip from '@/app/components/base/tooltip' import { ToastContext } from '@/app/components/base/toast' +import Tooltip from '@/app/components/base/tooltip' import { useStore } from '@/app/components/workflow/store' -import type { EnvironmentVariable } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var' @@ -86,89 +86,107 @@ const VariableModal = ({ <div className={cn('flex h-full w-[360px] flex-col rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-2xl')} > - <div className='system-xl-semibold mb-3 flex shrink-0 items-center justify-between p-4 pb-0 text-text-primary'> + <div className="system-xl-semibold mb-3 flex shrink-0 items-center justify-between p-4 pb-0 text-text-primary"> {!env ? t('workflow.env.modal.title') : t('workflow.env.modal.editTitle')} - <div className='flex items-center'> + <div className="flex items-center"> <div - className='flex h-6 w-6 cursor-pointer items-center justify-center' + className="flex h-6 w-6 cursor-pointer items-center justify-center" onClick={onClose} > - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> </div> - <div className='px-4 py-2'> + <div className="px-4 py-2"> {/* type */} - <div className='mb-4'> - <div className='system-sm-semibold mb-1 flex h-6 items-center text-text-secondary'>{t('workflow.env.modal.type')}</div> - <div className='flex gap-2'> - <div className={cn( - 'radius-md system-sm-regular flex w-[106px] cursor-pointer items-center justify-center border border-components-option-card-option-border bg-components-option-card-option-bg p-2 text-text-secondary hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs', - type === 'string' && 'system-sm-medium border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary shadow-xs hover:border-components-option-card-option-selected-border', - )} onClick={() => setType('string')}>String</div> - <div className={cn( - 'radius-md system-sm-regular flex w-[106px] cursor-pointer items-center justify-center border border-components-option-card-option-border bg-components-option-card-option-bg p-2 text-text-secondary hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs', - type === 'number' && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg font-medium text-text-primary shadow-xs hover:border-components-option-card-option-selected-border', - )} onClick={() => { - setType('number') - if (!(/^\d$/).test(value)) - setValue('') - }}>Number</div> - <div className={cn( - 'radius-md system-sm-regular flex w-[106px] cursor-pointer items-center justify-center border border-components-option-card-option-border bg-components-option-card-option-bg p-2 text-text-secondary hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs', - type === 'secret' && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg font-medium text-text-primary shadow-xs hover:border-components-option-card-option-selected-border', - )} onClick={() => setType('secret')}> + <div className="mb-4"> + <div className="system-sm-semibold mb-1 flex h-6 items-center text-text-secondary">{t('workflow.env.modal.type')}</div> + <div className="flex gap-2"> + <div + className={cn( + 'radius-md system-sm-regular flex w-[106px] cursor-pointer items-center justify-center border border-components-option-card-option-border bg-components-option-card-option-bg p-2 text-text-secondary hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs', + type === 'string' && 'system-sm-medium border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg text-text-primary shadow-xs hover:border-components-option-card-option-selected-border', + )} + onClick={() => setType('string')} + > + String + </div> + <div + className={cn( + 'radius-md system-sm-regular flex w-[106px] cursor-pointer items-center justify-center border border-components-option-card-option-border bg-components-option-card-option-bg p-2 text-text-secondary hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs', + type === 'number' && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg font-medium text-text-primary shadow-xs hover:border-components-option-card-option-selected-border', + )} + onClick={() => { + setType('number') + if (!(/^\d$/).test(value)) + setValue('') + }} + > + Number + </div> + <div + className={cn( + 'radius-md system-sm-regular flex w-[106px] cursor-pointer items-center justify-center border border-components-option-card-option-border bg-components-option-card-option-bg p-2 text-text-secondary hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs', + type === 'secret' && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg font-medium text-text-primary shadow-xs hover:border-components-option-card-option-selected-border', + )} + onClick={() => setType('secret')} + > <span>Secret</span> <Tooltip - popupContent={ - <div className='w-[240px]'> + popupContent={( + <div className="w-[240px]"> {t('workflow.env.modal.secretTip')} </div> - } - triggerClassName='ml-0.5 w-3.5 h-3.5' + )} + triggerClassName="ml-0.5 w-3.5 h-3.5" /> </div> </div> </div> {/* name */} - <div className='mb-4'> - <div className='system-sm-semibold mb-1 flex h-6 items-center text-text-secondary'>{t('workflow.env.modal.name')}</div> - <div className='flex'> + <div className="mb-4"> + <div className="system-sm-semibold mb-1 flex h-6 items-center text-text-secondary">{t('workflow.env.modal.name')}</div> + <div className="flex"> <Input placeholder={t('workflow.env.modal.namePlaceholder') || ''} value={name} onChange={handleVarNameChange} onBlur={e => checkVariableName(e.target.value)} - type='text' + type="text" /> </div> </div> {/* value */} - <div className='mb-4'> - <div className='system-sm-semibold mb-1 flex h-6 items-center text-text-secondary'>{t('workflow.env.modal.value')}</div> - <div className='flex'> + <div className="mb-4"> + <div className="system-sm-semibold mb-1 flex h-6 items-center text-text-secondary">{t('workflow.env.modal.value')}</div> + <div className="flex"> { - type !== 'number' ? <textarea - className='system-sm-regular placeholder:system-sm-regular block h-20 w-full resize-none appearance-none rounded-lg border border-transparent bg-components-input-bg-normal p-2 text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs' - value={value} - placeholder={t('workflow.env.modal.valuePlaceholder') || ''} - onChange={e => setValue(e.target.value)} - /> - : <Input - placeholder={t('workflow.env.modal.valuePlaceholder') || ''} - value={value} - onChange={e => setValue(e.target.value)} - type="number" - /> + type !== 'number' + ? ( + <textarea + className="system-sm-regular placeholder:system-sm-regular block h-20 w-full resize-none appearance-none rounded-lg border border-transparent bg-components-input-bg-normal p-2 text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs" + value={value} + placeholder={t('workflow.env.modal.valuePlaceholder') || ''} + onChange={e => setValue(e.target.value)} + /> + ) + : ( + <Input + placeholder={t('workflow.env.modal.valuePlaceholder') || ''} + value={value} + onChange={e => setValue(e.target.value)} + type="number" + /> + ) } </div> </div> {/* description */} - <div className=''> - <div className='system-sm-semibold mb-1 flex h-6 items-center text-text-secondary'>{t('workflow.env.modal.description')}</div> - <div className='flex'> + <div className=""> + <div className="system-sm-semibold mb-1 flex h-6 items-center text-text-secondary">{t('workflow.env.modal.description')}</div> + <div className="flex"> <textarea - className='system-sm-regular placeholder:system-sm-regular block h-20 w-full resize-none appearance-none rounded-lg border border-transparent bg-components-input-bg-normal p-2 text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs' + className="system-sm-regular placeholder:system-sm-regular block h-20 w-full resize-none appearance-none rounded-lg border border-transparent bg-components-input-bg-normal p-2 text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs" value={description} placeholder={t('workflow.env.modal.descriptionPlaceholder') || ''} onChange={e => setDescription(e.target.value)} @@ -176,10 +194,10 @@ const VariableModal = ({ </div> </div> </div> - <div className='flex flex-row-reverse rounded-b-2xl p-4 pt-2'> - <div className='flex gap-2'> + <div className="flex flex-row-reverse rounded-b-2xl p-4 pt-2"> + <div className="flex gap-2"> <Button onClick={onClose}>{t('common.operation.cancel')}</Button> - <Button variant='primary' onClick={handleSave}>{t('common.operation.save')}</Button> + <Button variant="primary" onClick={handleSave}>{t('common.operation.save')}</Button> </div> </div> </div> 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 604fceef81..30551562a7 100644 --- a/web/app/components/workflow/panel/env-panel/variable-trigger.tsx +++ b/web/app/components/workflow/panel/env-panel/variable-trigger.tsx @@ -1,15 +1,15 @@ 'use client' +import type { EnvironmentVariable } from '@/app/components/workflow/types' +import { RiAddLine } from '@remixicon/react' import React from 'react' import { useTranslation } from 'react-i18next' -import { RiAddLine } from '@remixicon/react' import Button from '@/app/components/base/button' -import VariableModal from '@/app/components/workflow/panel/env-panel/variable-modal' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import type { EnvironmentVariable } from '@/app/components/workflow/types' +import VariableModal from '@/app/components/workflow/panel/env-panel/variable-modal' type Props = { open: boolean @@ -36,7 +36,7 @@ const VariableTrigger = ({ if (open) onClose() }} - placement='left-start' + placement="left-start" offset={{ mainAxis: 8, alignmentAxis: -104, @@ -46,13 +46,14 @@ const VariableTrigger = ({ setOpen(v => !v) if (open) onClose() - }}> - <Button variant='primary'> - <RiAddLine className='mr-1 h-4 w-4' /> - <span className='system-sm-medium'>{t('workflow.env.envPanelButton')}</span> + }} + > + <Button variant="primary"> + <RiAddLine className="mr-1 h-4 w-4" /> + <span className="system-sm-medium">{t('workflow.env.envPanelButton')}</span> </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[11]'> + <PortalToFollowElemContent className="z-[11]"> <VariableModal env={env} onSave={onSave} diff --git a/web/app/components/workflow/panel/global-variable-panel/index.tsx b/web/app/components/workflow/panel/global-variable-panel/index.tsx index ab7cf4708b..947393d95a 100644 --- a/web/app/components/workflow/panel/global-variable-panel/index.tsx +++ b/web/app/components/workflow/panel/global-variable-panel/index.tsx @@ -1,16 +1,16 @@ +import type { GlobalVariable } from '../../types' + +import { RiCloseLine } from '@remixicon/react' import { memo, } from 'react' - -import { RiCloseLine } from '@remixicon/react' -import type { GlobalVariable } from '../../types' -import Item from './item' +import { useTranslation } from 'react-i18next' import { useStore } from '@/app/components/workflow/store' import { cn } from '@/utils/classnames' -import { useTranslation } from 'react-i18next' -import { useIsChatMode } from '../../hooks' import { isInWorkflowPage } from '../../constants' +import { useIsChatMode } from '../../hooks' +import Item from './item' const Panel = () => { const { t } = useTranslation() @@ -19,16 +19,17 @@ const Panel = () => { const isWorkflowPage = isInWorkflowPage() const globalVariableList: GlobalVariable[] = [ - ...(isChatMode ? [{ - name: 'conversation_id', - value_type: 'string' as const, - description: t('workflow.globalVar.fieldsDescription.conversationId'), - }, - { - name: 'dialog_count', - value_type: 'number' as const, - description: t('workflow.globalVar.fieldsDescription.dialogCount'), - }] : []), + ...(isChatMode + ? [{ + name: 'conversation_id', + value_type: 'string' as const, + description: t('workflow.globalVar.fieldsDescription.conversationId'), + }, { + name: 'dialog_count', + value_type: 'number' as const, + description: t('workflow.globalVar.fieldsDescription.dialogCount'), + }] + : []), { name: 'user_id', value_type: 'string', @@ -50,11 +51,13 @@ const Panel = () => { description: t('workflow.globalVar.fieldsDescription.workflowRunId'), }, // is workflow - ...((isWorkflowPage && !isChatMode) ? [{ - name: 'timestamp', - value_type: 'number' as const, - description: t('workflow.globalVar.fieldsDescription.triggerTimestamp'), - }] : []), + ...((isWorkflowPage && !isChatMode) + ? [{ + name: 'timestamp', + value_type: 'number' as const, + description: t('workflow.globalVar.fieldsDescription.triggerTimestamp'), + }] + : []), ] return ( @@ -63,20 +66,20 @@ const Panel = () => { 'relative flex h-full w-[420px] flex-col rounded-l-2xl border border-components-panel-border bg-components-panel-bg-alt', )} > - <div className='system-xl-semibold flex shrink-0 items-center justify-between p-4 pb-0 text-text-primary'> + <div className="system-xl-semibold flex shrink-0 items-center justify-between p-4 pb-0 text-text-primary"> {t('workflow.globalVar.title')} - <div className='flex items-center'> + <div className="flex items-center"> <div - className='flex h-6 w-6 cursor-pointer items-center justify-center' + className="flex h-6 w-6 cursor-pointer items-center justify-center" onClick={() => setShowPanel(false)} > - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> </div> - <div className='system-sm-regular shrink-0 px-4 py-1 text-text-tertiary'>{t('workflow.globalVar.description')}</div> + <div className="system-sm-regular shrink-0 px-4 py-1 text-text-tertiary">{t('workflow.globalVar.description')}</div> - <div className='mt-4 grow overflow-y-auto rounded-b-2xl px-4'> + <div className="mt-4 grow overflow-y-auto rounded-b-2xl px-4"> {globalVariableList.map(item => ( <Item key={item.name} diff --git a/web/app/components/workflow/panel/global-variable-panel/item.tsx b/web/app/components/workflow/panel/global-variable-panel/item.tsx index c88222efb4..f82579dedb 100644 --- a/web/app/components/workflow/panel/global-variable-panel/item.tsx +++ b/web/app/components/workflow/panel/global-variable-panel/item.tsx @@ -1,8 +1,8 @@ -import { memo } from 'react' -import { capitalize } from 'lodash-es' -import { GlobalVariable as GlobalVariableIcon } from '@/app/components/base/icons/src/vender/line/others' - import type { GlobalVariable } from '@/app/components/workflow/types' +import { capitalize } from 'lodash-es' +import { memo } from 'react' + +import { GlobalVariable as GlobalVariableIcon } from '@/app/components/base/icons/src/vender/line/others' import { cn } from '@/utils/classnames' type Props = { @@ -15,18 +15,19 @@ const Item = ({ return ( <div className={cn( 'radius-md mb-1 border border-components-panel-border-subtle bg-components-panel-on-panel-item-bg px-2.5 py-2 shadow-xs hover:bg-components-panel-on-panel-item-bg-hover', - )}> - <div className='flex items-center justify-between'> - <div className='flex grow items-center gap-1'> - <GlobalVariableIcon className='h-4 w-4 text-util-colors-orange-orange-600' /> - <div className='system-sm-medium text-text-primary'> - <span className='text-text-tertiary'>sys.</span> + )} + > + <div className="flex items-center justify-between"> + <div className="flex grow items-center gap-1"> + <GlobalVariableIcon className="h-4 w-4 text-util-colors-orange-orange-600" /> + <div className="system-sm-medium text-text-primary"> + <span className="text-text-tertiary">sys.</span> {payload.name} </div> - <div className='system-xs-medium text-text-tertiary'>{capitalize(payload.value_type)}</div> + <div className="system-xs-medium text-text-tertiary">{capitalize(payload.value_type)}</div> </div> </div> - <div className='system-xs-regular mt-1.5 truncate text-text-tertiary'>{payload.description}</div> + <div className="system-xs-regular mt-1.5 truncate text-text-tertiary">{payload.description}</div> </div> ) } diff --git a/web/app/components/workflow/panel/index.tsx b/web/app/components/workflow/panel/index.tsx index 8b4ba27ec3..88ada8b11e 100644 --- a/web/app/components/workflow/panel/index.tsx +++ b/web/app/components/workflow/panel/index.tsx @@ -1,13 +1,13 @@ import type { FC } from 'react' -import { memo, useCallback, useEffect, useRef } from 'react' import type { VersionHistoryPanelProps } from '@/app/components/workflow/panel/version-history-panel' -import { useShallow } from 'zustand/react/shallow' +import dynamic from 'next/dynamic' +import { memo, useCallback, useEffect, useRef } from 'react' import { useStore as useReactflow } from 'reactflow' +import { useShallow } from 'zustand/react/shallow' +import { cn } from '@/utils/classnames' import { Panel as NodePanel } from '../nodes' import { useStore } from '../store' import EnvPanel from './env-panel' -import { cn } from '@/utils/classnames' -import dynamic from 'next/dynamic' const VersionHistoryPanel = dynamic(() => import('@/app/components/workflow/panel/version-history-panel'), { ssr: false, @@ -44,7 +44,8 @@ const useResizeObserver = ( useEffect(() => { const element = elementRef.current - if (!element) return + if (!element) + return const resizeObserver = new ResizeObserver((entries) => { for (const entry of entries) { diff --git a/web/app/components/workflow/panel/inputs-panel.tsx b/web/app/components/workflow/panel/inputs-panel.tsx index 4c9de03b8a..d1791ebe7e 100644 --- a/web/app/components/workflow/panel/inputs-panel.tsx +++ b/web/app/components/workflow/panel/inputs-panel.tsx @@ -1,3 +1,4 @@ +import type { StartNodeType } from '../nodes/start/types' import { memo, useCallback, @@ -5,25 +6,24 @@ import { } from 'react' import { useTranslation } from 'react-i18next' import { useNodes } from 'reactflow' +import Button from '@/app/components/base/button' +import { useCheckInputsForms } from '@/app/components/base/chat/chat/check-input-forms-hooks' +import { + getProcessedInputs, +} from '@/app/components/base/chat/chat/utils' +import { TransferMethod } from '../../base/text-generation/types' +import { useWorkflowRun } from '../hooks' +import { useHooksStore } from '../hooks-store' import FormItem from '../nodes/_base/components/before-run-form/form-item' +import { + useStore, + useWorkflowStore, +} from '../store' import { BlockEnum, InputVarType, WorkflowRunningStatus, } from '../types' -import { - useStore, - useWorkflowStore, -} from '../store' -import { useWorkflowRun } from '../hooks' -import type { StartNodeType } from '../nodes/start/types' -import { TransferMethod } from '../../base/text-generation/types' -import Button from '@/app/components/base/button' -import { - getProcessedInputs, -} from '@/app/components/base/chat/chat/utils' -import { useCheckInputsForms } from '@/app/components/base/chat/chat/check-input-forms-hooks' -import { useHooksStore } from '../hooks-store' type Props = { onRun: () => void @@ -105,16 +105,16 @@ const InputsPanel = ({ onRun }: Props) => { return ( <> - <div className='px-4 pb-2 pt-3'> + <div className="px-4 pb-2 pt-3"> { variables.map((variable, index) => ( <div key={variable.variable} - className='mb-2 last-of-type:mb-0' + className="mb-2 last-of-type:mb-0" > <FormItem autoFocus={index === 0} - className='!block' + className="!block" payload={variable} value={initialInputs[variable.variable]} onChange={v => handleValueChange(variable.variable, v)} @@ -123,11 +123,11 @@ const InputsPanel = ({ onRun }: Props) => { )) } </div> - <div className='flex items-center justify-between px-4 py-2'> + <div className="flex items-center justify-between px-4 py-2"> <Button - variant='primary' + variant="primary" disabled={!canRun || workflowRunningData?.result?.status === WorkflowRunningStatus.Running} - className='w-full' + className="w-full" onClick={doRun} > {t('workflow.singleRun.startRun')} diff --git a/web/app/components/workflow/panel/record.tsx b/web/app/components/workflow/panel/record.tsx index e9c677d235..ff84c73db9 100644 --- a/web/app/components/workflow/panel/record.tsx +++ b/web/app/components/workflow/panel/record.tsx @@ -1,9 +1,9 @@ -import { memo, useCallback } from 'react' import type { WorkflowRunDetailResponse } from '@/models/log' -import Run from '../run' -import { useStore } from '../store' +import { memo, useCallback } from 'react' import { useWorkflowUpdate } from '../hooks' import { useHooksStore } from '../hooks-store' +import Run from '../run' +import { useStore } from '../store' import { formatWorkflowRunIdentifier } from '../utils' const Record = () => { @@ -21,8 +21,8 @@ const Record = () => { }, [handleUpdateWorkflowCanvas]) return ( - <div className='flex h-full w-[400px] flex-col rounded-l-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl'> - <div className='system-xl-semibold flex items-center justify-between p-4 pb-0 text-text-primary'> + <div className="flex h-full w-[400px] flex-col rounded-l-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl"> + <div className="system-xl-semibold flex items-center justify-between p-4 pb-0 text-text-primary"> {`Test Run${formatWorkflowRunIdentifier(historyWorkflowData?.finished_at)}`} </div> <Run 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 a2b7e1afdd..47dc68687d 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,15 +1,16 @@ -import React, { type FC, useCallback } from 'react' +import type { FC } from 'react' import { RiMoreFill } from '@remixicon/react' -import { VersionHistoryContextMenuOptions } from '../../../types' -import MenuItem from './menu-item' -import useContextMenu from './use-context-menu' +import React, { useCallback } from 'react' +import Button from '@/app/components/base/button' +import Divider from '@/app/components/base/divider' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import Button from '@/app/components/base/button' -import Divider from '@/app/components/base/divider' +import { VersionHistoryContextMenuOptions } from '../../../types' +import MenuItem from './menu-item' +import useContextMenu from './use-context-menu' export type ContextMenuProps = { isShowDelete: boolean @@ -33,7 +34,7 @@ const ContextMenu: FC<ContextMenuProps> = (props: ContextMenuProps) => { return ( <PortalToFollowElem - placement={'bottom-end'} + placement="bottom-end" offset={{ mainAxis: 4, crossAxis: 0, @@ -42,13 +43,13 @@ const ContextMenu: FC<ContextMenuProps> = (props: ContextMenuProps) => { onOpenChange={setOpen} > <PortalToFollowElemTrigger> - <Button size='small' className='px-1' onClick={handleClickTrigger}> - <RiMoreFill className='h-4 w-4' /> + <Button size="small" className="px-1" onClick={handleClickTrigger}> + <RiMoreFill className="h-4 w-4" /> </Button> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-10'> - <div className='flex w-[184px] flex-col rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]'> - <div className='flex flex-col p-1'> + <PortalToFollowElemContent className="z-10"> + <div className="flex w-[184px] flex-col rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]"> + <div className="flex flex-col p-1"> { options.map((option) => { return ( @@ -64,8 +65,8 @@ const ContextMenu: FC<ContextMenuProps> = (props: ContextMenuProps) => { { isShowDelete && ( <> - <Divider type='horizontal' className='my-0 h-px bg-divider-subtle' /> - <div className='p-1'> + <Divider type="horizontal" className="my-0 h-px bg-divider-subtle" /> + <div className="p-1"> <MenuItem item={deleteOperation} isDestructive 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 61916b0200..a307056148 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,5 +1,6 @@ -import React, { type FC } from 'react' +import type { FC } from 'react' import type { VersionHistoryContextMenuOptions } from '../../../types' +import React from 'react' import { cn } from '@/utils/classnames' type MenuItemProps = { @@ -29,7 +30,8 @@ const MenuItem: FC<MenuItemProps> = ({ <div className={cn( 'system-md-regular flex-1 text-text-primary', isDestructive && 'hover:text-text-destructive', - )}> + )} + > {item.name} </div> </div> diff --git a/web/app/components/workflow/panel/version-history-panel/context-menu/use-context-menu.ts b/web/app/components/workflow/panel/version-history-panel/context-menu/use-context-menu.ts index 242b77a5fa..caf7b9a14d 100644 --- a/web/app/components/workflow/panel/version-history-panel/context-menu/use-context-menu.ts +++ b/web/app/components/workflow/panel/version-history-panel/context-menu/use-context-menu.ts @@ -1,8 +1,8 @@ +import type { ContextMenuProps } from './index' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { VersionHistoryContextMenuOptions } from '../../../types' -import type { ContextMenuProps } from './index' import { useStore } from '@/app/components/workflow/store' +import { VersionHistoryContextMenuOptions } from '../../../types' const useContextMenu = (props: ContextMenuProps) => { const { @@ -24,18 +24,20 @@ const useContextMenu = (props: ContextMenuProps) => { }, isNamedVersion ? { - key: VersionHistoryContextMenuOptions.edit, - name: t('workflow.versionHistory.editVersionInfo'), - } + key: VersionHistoryContextMenuOptions.edit, + name: t('workflow.versionHistory.editVersionInfo'), + } : { - key: VersionHistoryContextMenuOptions.edit, - name: t('workflow.versionHistory.nameThisVersion'), - }, + key: VersionHistoryContextMenuOptions.edit, + name: t('workflow.versionHistory.nameThisVersion'), + }, // todo: pipeline support export specific version DSL - ...(!pipelineId ? [{ - key: VersionHistoryContextMenuOptions.exportDSL, - name: t('app.export'), - }] : []), + ...(!pipelineId + ? [{ + key: VersionHistoryContextMenuOptions.exportDSL, + name: t('app.export'), + }] + : []), { key: VersionHistoryContextMenuOptions.copyId, name: t('workflow.versionHistory.copyId'), 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 8ba1494000..3ad7d0dc8a 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,8 +1,9 @@ -import React, { type FC } from 'react' -import Modal from '@/app/components/base/modal' +import type { FC } from 'react' import type { VersionHistory } from '@/types/workflow' +import React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' +import Modal from '@/app/components/base/modal' type DeleteConfirmModalProps = { isOpen: boolean @@ -19,24 +20,26 @@ const DeleteConfirmModal: FC<DeleteConfirmModalProps> = ({ }) => { const { t } = useTranslation() - return <Modal className='p-0' isShow={isOpen} onClose={onClose}> - <div className='flex flex-col gap-y-2 p-6 pb-4 '> - <div className='title-2xl-semi-bold text-text-primary'> - {`${t('common.operation.delete')} ${versionInfo.marked_name || t('workflow.versionHistory.defaultName')}`} + return ( + <Modal className="p-0" isShow={isOpen} onClose={onClose}> + <div className="flex flex-col gap-y-2 p-6 pb-4 "> + <div className="title-2xl-semi-bold text-text-primary"> + {`${t('common.operation.delete')} ${versionInfo.marked_name || t('workflow.versionHistory.defaultName')}`} + </div> + <p className="system-md-regular text-text-secondary"> + {t('workflow.versionHistory.deletionTip')} + </p> </div> - <p className='system-md-regular text-text-secondary'> - {t('workflow.versionHistory.deletionTip')} - </p> - </div> - <div className='flex items-center justify-end gap-x-2 p-6'> - <Button onClick={onClose}> - {t('common.operation.cancel')} - </Button> - <Button variant='warning' onClick={onDelete.bind(null, versionInfo.id)}> - {t('common.operation.delete')} - </Button> - </div> - </Modal> + <div className="flex items-center justify-end gap-x-2 p-6"> + <Button onClick={onClose}> + {t('common.operation.cancel')} + </Button> + <Button variant="warning" onClick={onDelete.bind(null, versionInfo.id)}> + {t('common.operation.delete')} + </Button> + </div> + </Modal> + ) } export default DeleteConfirmModal 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 e3f3a6e910..c020c076ad 100644 --- a/web/app/components/workflow/panel/version-history-panel/empty.tsx +++ b/web/app/components/workflow/panel/version-history-panel/empty.tsx @@ -1,7 +1,8 @@ -import Button from '@/app/components/base/button' +import type { FC } from 'react' import { RiHistoryLine } from '@remixicon/react' -import React, { type FC } from 'react' +import React from 'react' import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' type EmptyProps = { onResetFilter: () => void @@ -12,19 +13,21 @@ const Empty: FC<EmptyProps> = ({ }) => { const { t } = useTranslation() - return <div className='flex h-5/6 w-full flex-col justify-center gap-y-2'> - <div className='flex justify-center'> - <RiHistoryLine className='h-10 w-10 text-text-empty-state-icon' /> + return ( + <div className="flex h-5/6 w-full flex-col justify-center gap-y-2"> + <div className="flex justify-center"> + <RiHistoryLine className="h-10 w-10 text-text-empty-state-icon" /> + </div> + <div className="system-xs-regular flex justify-center text-text-tertiary"> + {t('workflow.versionHistory.filter.empty')} + </div> + <div className="flex justify-center"> + <Button size="small" onClick={onResetFilter}> + {t('workflow.versionHistory.filter.reset')} + </Button> + </div> </div> - <div className='system-xs-regular flex justify-center text-text-tertiary'> - {t('workflow.versionHistory.filter.empty')} - </div> - <div className='flex justify-center'> - <Button size='small' onClick={onResetFilter}> - {t('workflow.versionHistory.filter.reset')} - </Button> - </div> - </div> + ) } export default React.memo(Empty) diff --git a/web/app/components/workflow/panel/version-history-panel/filter/filter-item.tsx b/web/app/components/workflow/panel/version-history-panel/filter/filter-item.tsx index 4301a8e582..c9a7c28112 100644 --- a/web/app/components/workflow/panel/version-history-panel/filter/filter-item.tsx +++ b/web/app/components/workflow/panel/version-history-panel/filter/filter-item.tsx @@ -1,6 +1,7 @@ -import { RiCheckLine } from '@remixicon/react' -import React, { type FC } from 'react' +import type { FC } from 'react' import type { WorkflowVersionFilterOptions } from '../../../types' +import { RiCheckLine } from '@remixicon/react' +import React from 'react' type FilterItemProps = { item: { @@ -18,13 +19,13 @@ const FilterItem: FC<FilterItemProps> = ({ }) => { return ( <div - className='flex cursor-pointer items-center justify-between gap-x-1 rounded-lg px-2 py-1.5 hover:bg-state-base-hover' + className="flex cursor-pointer items-center justify-between gap-x-1 rounded-lg px-2 py-1.5 hover:bg-state-base-hover" onClick={() => { onClick(item.key) }} > - <div className='system-md-regular flex-1 text-text-primary'>{item.name}</div> - {isSelected && <RiCheckLine className='h-4 w-4 shrink-0 text-text-accent' />} + <div className="system-md-regular flex-1 text-text-primary">{item.name}</div> + {isSelected && <RiCheckLine className="h-4 w-4 shrink-0 text-text-accent" />} </div> ) } 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 0eabd50702..6db331338b 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,4 +1,5 @@ -import React, { type FC } from 'react' +import type { FC } from 'react' +import React from 'react' import { useTranslation } from 'react-i18next' import Switch from '@/app/components/base/switch' @@ -14,16 +15,16 @@ const FilterSwitch: FC<FilterSwitchProps> = ({ const { t } = useTranslation() return ( - <div className='flex items-center p-1'> - <div className='flex w-full items-center gap-x-1 px-2 py-1.5'> - <div className='system-md-regular flex-1 px-1 text-text-secondary'> + <div className="flex items-center p-1"> + <div className="flex w-full items-center gap-x-1 px-2 py-1.5"> + <div className="system-md-regular flex-1 px-1 text-text-secondary"> {t('workflow.versionHistory.filter.onlyShowNamedVersions')} </div> <Switch defaultValue={enabled} onChange={v => handleSwitch(v)} - size='md' - className='shrink-0' + size="md" + className="shrink-0" /> </div> </div> diff --git a/web/app/components/workflow/panel/version-history-panel/filter/index.tsx b/web/app/components/workflow/panel/version-history-panel/filter/index.tsx index e37bbe2269..8def221926 100644 --- a/web/app/components/workflow/panel/version-history-panel/filter/index.tsx +++ b/web/app/components/workflow/panel/version-history-panel/filter/index.tsx @@ -1,16 +1,17 @@ -import React, { type FC, useCallback, useState } from 'react' +import type { FC } from 'react' import { RiFilter3Line } from '@remixicon/react' -import { WorkflowVersionFilterOptions } from '../../../types' -import { useFilterOptions } from './use-filter' -import FilterItem from './filter-item' -import FilterSwitch from './filter-switch' +import React, { useCallback, useState } from 'react' +import Divider from '@/app/components/base/divider' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import Divider from '@/app/components/base/divider' import { cn } from '@/utils/classnames' +import { WorkflowVersionFilterOptions } from '../../../types' +import FilterItem from './filter-item' +import FilterSwitch from './filter-switch' +import { useFilterOptions } from './use-filter' type FilterProps = { filterValue: WorkflowVersionFilterOptions @@ -36,7 +37,7 @@ const Filter: FC<FilterProps> = ({ return ( <PortalToFollowElem - placement={'bottom-end'} + placement="bottom-end" offset={{ mainAxis: 4, crossAxis: 55, @@ -54,9 +55,9 @@ const Filter: FC<FilterProps> = ({ <RiFilter3Line className={cn('h-4 w-4', isFiltering ? 'text-text-accent' : ' text-text-tertiary')} /> </div> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[12]'> - <div className='flex w-[248px] flex-col rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]'> - <div className='flex flex-col p-1'> + <PortalToFollowElemContent className="z-[12]"> + <div className="flex w-[248px] flex-col rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg shadow-shadow-shadow-5 backdrop-blur-[5px]"> + <div className="flex flex-col p-1"> { options.map((option) => { return ( @@ -70,7 +71,7 @@ const Filter: FC<FilterProps> = ({ }) } </div> - <Divider type='horizontal' className='my-0 h-px bg-divider-subtle' /> + <Divider type="horizontal" className="my-0 h-px bg-divider-subtle" /> <FilterSwitch enabled={isOnlyShowNamedVersions} handleSwitch={handleSwitch} /> </div> </PortalToFollowElemContent> 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 eeb4f50720..0bdb608d94 100644 --- a/web/app/components/workflow/panel/version-history-panel/index.tsx +++ b/web/app/components/workflow/panel/version-history-panel/index.tsx @@ -1,24 +1,24 @@ 'use client' -import React, { useCallback, useState } from 'react' -import { useTranslation } from 'react-i18next' +import type { VersionHistory } from '@/types/workflow' import { RiArrowDownDoubleLine, RiCloseLine, RiLoader2Line } from '@remixicon/react' import copy from 'copy-to-clipboard' +import React, { 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/toast' +import { useSelector as useAppContextSelector } from '@/context/app-context' +import { useDeleteWorkflow, useInvalidAllLastRun, useResetWorkflowVersionHistory, useUpdateWorkflow, useWorkflowVersionHistory } from '@/service/use-workflow' import { useDSL, useNodesSyncDraft, useWorkflowRun } from '../../hooks' +import { useHooksStore } from '../../hooks-store' import { useStore, useWorkflowStore } from '../../store' import { VersionHistoryContextMenuOptions, WorkflowVersionFilterOptions } from '../../types' -import VersionHistoryItem from './version-history-item' -import Filter from './filter' -import type { VersionHistory } from '@/types/workflow' -import { useDeleteWorkflow, useInvalidAllLastRun, useResetWorkflowVersionHistory, useUpdateWorkflow, useWorkflowVersionHistory } from '@/service/use-workflow' -import Divider from '@/app/components/base/divider' -import Loading from './loading' -import Empty from './empty' -import { useSelector as useAppContextSelector } from '@/context/app-context' -import RestoreConfirmModal from './restore-confirm-modal' import DeleteConfirmModal from './delete-confirm-modal' -import VersionInfoModal from '@/app/components/app/app-publisher/version-info-modal' -import Toast from '@/app/components/base/toast' -import { useHooksStore } from '../../hooks-store' +import Empty from './empty' +import Filter from './filter' +import Loading from './loading' +import RestoreConfirmModal from './restore-confirm-modal' +import VersionHistoryItem from './version-history-item' const HISTORY_PER_PAGE = 10 const INITIAL_PAGE = 1 @@ -222,87 +222,95 @@ export const VersionHistoryPanel = ({ }, [t, updateWorkflow, resetWorkflowVersionHistory, updateVersionUrl]) return ( - <div className='flex h-full w-[268px] flex-col rounded-l-2xl border-y-[0.5px] border-l-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl shadow-shadow-shadow-5'> - <div className='flex items-center gap-x-2 px-4 pt-3'> - <div className='system-xl-semibold flex-1 py-1 text-text-primary'>{t('workflow.versionHistory.title')}</div> + <div className="flex h-full w-[268px] flex-col rounded-l-2xl border-y-[0.5px] border-l-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl shadow-shadow-shadow-5"> + <div className="flex items-center gap-x-2 px-4 pt-3"> + <div className="system-xl-semibold flex-1 py-1 text-text-primary">{t('workflow.versionHistory.title')}</div> <Filter filterValue={filterValue} isOnlyShowNamedVersions={isOnlyShowNamedVersions} onClickFilterItem={handleClickFilterItem} handleSwitch={handleSwitch} /> - <Divider type='vertical' className='mx-1 h-3.5' /> + <Divider type="vertical" className="mx-1 h-3.5" /> <div - className='flex h-6 w-6 cursor-pointer items-center justify-center p-0.5' + className="flex h-6 w-6 cursor-pointer items-center justify-center p-0.5" onClick={handleClose} > - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> <div className="flex h-0 flex-1 flex-col"> <div className="flex-1 overflow-y-auto px-3 py-2"> {(isFetching && !versionHistory?.pages?.length) ? ( - <Loading /> - ) + <Loading /> + ) : ( - <> - {versionHistory?.pages?.map((page, pageNumber) => ( - page.items?.map((item, idx) => { - const isLast = pageNumber === versionHistory.pages.length - 1 && idx === page.items.length - 1 - return <VersionHistoryItem - key={item.id} - item={item} - currentVersion={currentVersion} - latestVersionId={latestVersionId || ''} - onClick={handleVersionClick} - handleClickMenuItem={handleClickMenuItem.bind(null, item)} - isLast={isLast} - /> - }) - ))} - {!isFetching && (!versionHistory?.pages?.length || !versionHistory.pages[0].items.length) && ( - <Empty onResetFilter={handleResetFilter} /> - )} - </> - )} + <> + {versionHistory?.pages?.map((page, pageNumber) => ( + page.items?.map((item, idx) => { + const isLast = pageNumber === versionHistory.pages.length - 1 && idx === page.items.length - 1 + return ( + <VersionHistoryItem + key={item.id} + item={item} + currentVersion={currentVersion} + latestVersionId={latestVersionId || ''} + onClick={handleVersionClick} + handleClickMenuItem={handleClickMenuItem.bind(null, item)} + isLast={isLast} + /> + ) + }) + ))} + {!isFetching && (!versionHistory?.pages?.length || !versionHistory.pages[0].items.length) && ( + <Empty onResetFilter={handleResetFilter} /> + )} + </> + )} </div> {hasNextPage && ( - <div className='p-2'> + <div className="p-2"> <div - className='flex cursor-pointer items-center gap-x-1' + className="flex cursor-pointer items-center gap-x-1" onClick={handleNextPage} > - <div className='item-center flex justify-center p-0.5'> + <div className="item-center flex justify-center p-0.5"> {isFetching - ? <RiLoader2Line className='h-3.5 w-3.5 animate-spin text-text-accent' /> - : <RiArrowDownDoubleLine className='h-3.5 w-3.5 text-text-accent' />} + ? <RiLoader2Line className="h-3.5 w-3.5 animate-spin text-text-accent" /> + : <RiArrowDownDoubleLine className="h-3.5 w-3.5 text-text-accent" />} </div> - <div className='system-xs-medium-uppercase py-[1px] text-text-accent'> + <div className="system-xs-medium-uppercase py-[1px] text-text-accent"> {t('workflow.common.loadMore')} </div> </div> </div> )} </div> - {restoreConfirmOpen && (<RestoreConfirmModal - isOpen={restoreConfirmOpen} - versionInfo={operatedItem!} - onClose={handleCancel.bind(null, VersionHistoryContextMenuOptions.restore)} - onRestore={handleRestore} - />)} - {deleteConfirmOpen && (<DeleteConfirmModal - isOpen={deleteConfirmOpen} - versionInfo={operatedItem!} - onClose={handleCancel.bind(null, VersionHistoryContextMenuOptions.delete)} - onDelete={handleDelete} - />)} - {editModalOpen && (<VersionInfoModal - isOpen={editModalOpen} - versionInfo={operatedItem} - onClose={handleCancel.bind(null, VersionHistoryContextMenuOptions.edit)} - onPublish={handleUpdateWorkflow} - />)} + {restoreConfirmOpen && ( + <RestoreConfirmModal + isOpen={restoreConfirmOpen} + versionInfo={operatedItem!} + onClose={handleCancel.bind(null, VersionHistoryContextMenuOptions.restore)} + onRestore={handleRestore} + /> + )} + {deleteConfirmOpen && ( + <DeleteConfirmModal + isOpen={deleteConfirmOpen} + versionInfo={operatedItem!} + onClose={handleCancel.bind(null, VersionHistoryContextMenuOptions.delete)} + onDelete={handleDelete} + /> + )} + {editModalOpen && ( + <VersionInfoModal + isOpen={editModalOpen} + versionInfo={operatedItem} + onClose={handleCancel.bind(null, VersionHistoryContextMenuOptions.edit)} + onPublish={handleUpdateWorkflow} + /> + )} </div> ) } diff --git a/web/app/components/workflow/panel/version-history-panel/loading/index.tsx b/web/app/components/workflow/panel/version-history-panel/loading/index.tsx index 2c4db667ea..a6dadcd652 100644 --- a/web/app/components/workflow/panel/version-history-panel/loading/index.tsx +++ b/web/app/components/workflow/panel/version-history-panel/loading/index.tsx @@ -10,10 +10,12 @@ const itemConfig = Array.from({ length: 8 }).map((_, index) => { }) const Loading = () => { - return <div className='relative w-full overflow-y-hidden'> - <div className='absolute left-0 top-0 z-10 h-full w-full bg-dataset-chunk-list-mask-bg' /> - {itemConfig.map((config, index) => <Item key={index} {...config} />)} - </div> + return ( + <div className="relative w-full overflow-y-hidden"> + <div className="absolute left-0 top-0 z-10 h-full w-full bg-dataset-chunk-list-mask-bg" /> + {itemConfig.map((config, index) => <Item key={index} {...config} />)} + </div> + ) } export default Loading diff --git a/web/app/components/workflow/panel/version-history-panel/loading/item.tsx b/web/app/components/workflow/panel/version-history-panel/loading/item.tsx index ff2d746801..c17d725fb3 100644 --- a/web/app/components/workflow/panel/version-history-panel/loading/item.tsx +++ b/web/app/components/workflow/panel/version-history-panel/loading/item.tsx @@ -1,4 +1,5 @@ -import React, { type FC } from 'react' +import type { FC } from 'react' +import React from 'react' import { cn } from '@/utils/classnames' type ItemProps = { @@ -15,18 +16,18 @@ const Item: FC<ItemProps> = ({ isLast, }) => { return ( - <div className='relative flex gap-x-1 p-2' > - {!isLast && <div className='absolute left-4 top-6 h-[calc(100%-0.75rem)] w-0.5 bg-divider-subtle' />} - <div className=' flex h-5 w-[18px] shrink-0 items-center justify-center'> - <div className='h-2 w-2 rounded-lg border-[2px] border-text-quaternary' /> + <div className="relative flex gap-x-1 p-2"> + {!isLast && <div className="absolute left-4 top-6 h-[calc(100%-0.75rem)] w-0.5 bg-divider-subtle" />} + <div className=" flex h-5 w-[18px] shrink-0 items-center justify-center"> + <div className="h-2 w-2 rounded-lg border-[2px] border-text-quaternary" /> </div> - <div className='flex grow flex-col gap-y-0.5'> - <div className='flex h-3.5 items-center'> + <div className="flex grow flex-col gap-y-0.5"> + <div className="flex h-3.5 items-center"> <div className={cn('h-2 w-full rounded-sm bg-text-quaternary opacity-20', titleWidth)} /> </div> { !isFirst && ( - <div className='flex h-3 items-center'> + <div className="flex h-3 items-center"> <div className={cn('h-1.5 w-full rounded-sm bg-text-quaternary opacity-20', releaseNotesWidth)} /> </div> ) 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 d8394d20c0..09bc5d79b4 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,8 +1,9 @@ -import React, { type FC } from 'react' -import Modal from '@/app/components/base/modal' +import type { FC } from 'react' import type { VersionHistory } from '@/types/workflow' +import React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' +import Modal from '@/app/components/base/modal' type RestoreConfirmModalProps = { isOpen: boolean @@ -19,24 +20,26 @@ const RestoreConfirmModal: FC<RestoreConfirmModalProps> = ({ }) => { const { t } = useTranslation() - return <Modal className='p-0' isShow={isOpen} onClose={onClose}> - <div className='flex flex-col gap-y-2 p-6 pb-4 '> - <div className='title-2xl-semi-bold text-text-primary'> - {`${t('workflow.common.restore')} ${versionInfo.marked_name || t('workflow.versionHistory.defaultName')}`} + return ( + <Modal className="p-0" isShow={isOpen} onClose={onClose}> + <div className="flex flex-col gap-y-2 p-6 pb-4 "> + <div className="title-2xl-semi-bold text-text-primary"> + {`${t('workflow.common.restore')} ${versionInfo.marked_name || t('workflow.versionHistory.defaultName')}`} + </div> + <p className="system-md-regular text-text-secondary"> + {t('workflow.versionHistory.restorationTip')} + </p> </div> - <p className='system-md-regular text-text-secondary'> - {t('workflow.versionHistory.restorationTip')} - </p> - </div> - <div className='flex items-center justify-end gap-x-2 p-6'> - <Button onClick={onClose}> - {t('common.operation.cancel')} - </Button> - <Button variant='primary' onClick={onRestore.bind(null, versionInfo)}> - {t('workflow.common.restore')} - </Button> - </div> - </Modal> + <div className="flex items-center justify-end gap-x-2 p-6"> + <Button onClick={onClose}> + {t('common.operation.cancel')} + </Button> + <Button variant="primary" onClick={onRestore.bind(null, versionInfo)}> + {t('workflow.common.restore')} + </Button> + </div> + </Modal> + ) } export default RestoreConfirmModal diff --git a/web/app/components/workflow/panel/version-history-panel/version-history-item.tsx b/web/app/components/workflow/panel/version-history-panel/version-history-item.tsx index 558e8ab720..7739d10af2 100644 --- a/web/app/components/workflow/panel/version-history-panel/version-history-item.tsx +++ b/web/app/components/workflow/panel/version-history-panel/version-history-item.tsx @@ -1,10 +1,11 @@ -import React, { useEffect, useState } from 'react' -import dayjs from 'dayjs' -import { useTranslation } from 'react-i18next' -import ContextMenu from './context-menu' -import { cn } from '@/utils/classnames' +import type { VersionHistoryContextMenuOptions } from '../../types' import type { VersionHistory } from '@/types/workflow' -import { type VersionHistoryContextMenuOptions, WorkflowVersion } from '../../types' +import dayjs from 'dayjs' +import React, { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { cn } from '@/utils/classnames' +import { WorkflowVersion } from '../../types' +import ContextMenu from './context-menu' type VersionHistoryItemProps = { item: VersionHistory @@ -80,38 +81,41 @@ const VersionHistoryItem: React.FC<VersionHistoryItemProps> = ({ setOpen(true) }} > - {!isLast && <div className='absolute left-4 top-6 h-[calc(100%-0.75rem)] w-0.5 bg-divider-subtle' />} - <div className=' flex h-5 w-[18px] shrink-0 items-center justify-center'> + {!isLast && <div className="absolute left-4 top-6 h-[calc(100%-0.75rem)] w-0.5 bg-divider-subtle" />} + <div className=" flex h-5 w-[18px] shrink-0 items-center justify-center"> <div className={cn( 'h-2 w-2 rounded-lg border-[2px]', isSelected ? 'border-text-accent' : 'border-text-quaternary', - )}/> + )} + /> </div> - <div className='flex grow flex-col gap-y-0.5 overflow-hidden'> - <div className='mr-6 flex h-5 items-center gap-x-1'> + <div className="flex grow flex-col gap-y-0.5 overflow-hidden"> + <div className="mr-6 flex h-5 items-center gap-x-1"> <div className={cn( 'system-sm-semibold truncate py-[1px]', isSelected ? 'text-text-accent' : 'text-text-secondary', - )}> + )} + > {isDraft ? t('workflow.versionHistory.currentDraft') : item.marked_name || t('workflow.versionHistory.defaultName')} </div> {isLatest && ( - <div className='system-2xs-medium-uppercase flex h-5 shrink-0 items-center rounded-md border border-text-accent-secondary - bg-components-badge-bg-dimm px-[5px] text-text-accent-secondary'> + <div className="system-2xs-medium-uppercase flex h-5 shrink-0 items-center rounded-md border border-text-accent-secondary + bg-components-badge-bg-dimm px-[5px] text-text-accent-secondary" + > {t('workflow.versionHistory.latest')} </div> )} </div> { !isDraft && ( - <div className='system-xs-regular break-words text-text-secondary'> + <div className="system-xs-regular break-words text-text-secondary"> {item.marked_comment || ''} </div> ) } { !isDraft && ( - <div className='system-xs-regular truncate text-text-tertiary'> + <div className="system-xs-regular truncate text-text-tertiary"> {`${formatTime(item.created_at)} · ${item.created_by.name}`} </div> ) @@ -119,7 +123,7 @@ const VersionHistoryItem: React.FC<VersionHistoryItemProps> = ({ </div> {/* Context Menu */} {!isDraft && isHovering && ( - <div className='absolute right-1 top-1'> + <div className="absolute right-1 top-1"> <ContextMenu isShowDelete={!isLatest} isNamedVersion={!!item.marked_name} diff --git a/web/app/components/workflow/panel/workflow-preview.tsx b/web/app/components/workflow/panel/workflow-preview.tsx index 0a702d5ce3..9daf42186f 100644 --- a/web/app/components/workflow/panel/workflow-preview.tsx +++ b/web/app/components/workflow/panel/workflow-preview.tsx @@ -1,31 +1,31 @@ +import { + RiClipboardLine, + RiCloseLine, +} from '@remixicon/react' +import copy from 'copy-to-clipboard' import { memo, useCallback, useEffect, useState, } from 'react' -import { - RiClipboardLine, - RiCloseLine, -} from '@remixicon/react' import { useTranslation } from 'react-i18next' -import copy from 'copy-to-clipboard' -import ResultText from '../run/result-text' -import ResultPanel from '../run/result-panel' -import TracingPanel from '../run/tracing-panel' +import Button from '@/app/components/base/button' +import Loading from '@/app/components/base/loading' +import { cn } from '@/utils/classnames' +import Toast from '../../base/toast' import { useWorkflowInteractions, } from '../hooks' +import ResultPanel from '../run/result-panel' +import ResultText from '../run/result-text' +import TracingPanel from '../run/tracing-panel' import { useStore } from '../store' import { WorkflowRunningStatus, } from '../types' import { formatWorkflowRunIdentifier } from '../utils' -import Toast from '../../base/toast' import InputsPanel from './inputs-panel' -import { cn } from '@/utils/classnames' -import Loading from '@/app/components/base/loading' -import Button from '@/app/components/base/button' const WorkflowPreview = () => { const { t } = useTranslation() @@ -95,23 +95,22 @@ const WorkflowPreview = () => { }, [resize, stopResizing]) return ( - <div className={ - 'relative flex h-full flex-col rounded-l-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl' - } - style={{ width: `${panelWidth}px` }} + <div + className="relative flex h-full flex-col rounded-l-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl" + style={{ width: `${panelWidth}px` }} > <div className="absolute bottom-0 left-[3px] top-1/2 z-50 h-6 w-[3px] cursor-col-resize rounded bg-gray-300" onMouseDown={startResizing} /> - <div className='flex items-center justify-between p-4 pb-1 text-base font-semibold text-text-primary'> + <div className="flex items-center justify-between p-4 pb-1 text-base font-semibold text-text-primary"> {`Test Run${formatWorkflowRunIdentifier(workflowRunningData?.result.finished_at)}`} - <div className='cursor-pointer p-1' onClick={() => handleCancelDebugAndPreviewPanel()}> - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <div className="cursor-pointer p-1" onClick={() => handleCancelDebugAndPreviewPanel()}> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> - <div className='relative flex grow flex-col'> - <div className='flex shrink-0 items-center border-b-[0.5px] border-divider-subtle px-4'> + <div className="relative flex grow flex-col"> + <div className="flex shrink-0 items-center border-b-[0.5px] border-divider-subtle px-4"> {showInputsPanel && ( <div className={cn( @@ -119,7 +118,9 @@ const WorkflowPreview = () => { currentTab === 'INPUT' && '!border-[rgb(21,94,239)] text-text-secondary', )} onClick={() => switchTab('INPUT')} - >{t('runLog.input')}</div> + > + {t('runLog.input')} + </div> )} <div className={cn( @@ -132,7 +133,9 @@ const WorkflowPreview = () => { return switchTab('RESULT') }} - >{t('runLog.result')}</div> + > + {t('runLog.result')} + </div> <div className={cn( 'mr-6 cursor-pointer border-b-2 border-transparent py-3 text-[13px] font-semibold leading-[18px] text-text-tertiary', @@ -144,7 +147,9 @@ const WorkflowPreview = () => { return switchTab('DETAIL') }} - >{t('runLog.detail')}</div> + > + {t('runLog.detail')} + </div> <div className={cn( 'mr-6 cursor-pointer border-b-2 border-transparent py-3 text-[13px] font-semibold leading-[18px] text-text-tertiary', @@ -156,12 +161,15 @@ const WorkflowPreview = () => { return switchTab('TRACING') }} - >{t('runLog.tracing')}</div> + > + {t('runLog.tracing')} + </div> </div> <div className={cn( 'h-0 grow overflow-y-auto rounded-b-2xl bg-components-panel-bg', (currentTab === 'RESULT' || currentTab === 'TRACING') && '!bg-background-section-burn', - )}> + )} + > {currentTab === 'INPUT' && showInputsPanel && ( <InputsPanel onRun={() => switchTab('RESULT')} /> )} @@ -184,8 +192,9 @@ const WorkflowPreview = () => { else copy(JSON.stringify(content)) Toast.notify({ type: 'success', message: t('common.actionMsg.copySuccessfully') }) - }}> - <RiClipboardLine className='h-3.5 w-3.5' /> + }} + > + <RiClipboardLine className="h-3.5 w-3.5" /> <div>{t('common.operation.copy')}</div> </Button> )} @@ -211,18 +220,18 @@ const WorkflowPreview = () => { /> )} {currentTab === 'DETAIL' && !workflowRunningData?.result && ( - <div className='flex h-full items-center justify-center bg-components-panel-bg'> + <div className="flex h-full items-center justify-center bg-components-panel-bg"> <Loading /> </div> )} {currentTab === 'TRACING' && ( <TracingPanel - className='bg-background-section-burn' + className="bg-background-section-burn" list={workflowRunningData?.tracing || []} /> )} {currentTab === 'TRACING' && !workflowRunningData?.tracing?.length && ( - <div className='flex h-full items-center justify-center !bg-background-section-burn'> + <div className="flex h-full items-center justify-center !bg-background-section-burn"> <Loading /> </div> )} diff --git a/web/app/components/workflow/plugin-dependency/hooks.ts b/web/app/components/workflow/plugin-dependency/hooks.ts index 1fa7af3dab..ff7b85fa09 100644 --- a/web/app/components/workflow/plugin-dependency/hooks.ts +++ b/web/app/components/workflow/plugin-dependency/hooks.ts @@ -1,7 +1,7 @@ import { useCallback } from 'react' -import { useStore as usePluginDependenciesStore } from './store' -import { useMutationCheckDependencies } from '@/service/use-plugins' import { useCheckPipelineDependencies } from '@/service/use-pipeline' +import { useMutationCheckDependencies } from '@/service/use-plugins' +import { useStore as usePluginDependenciesStore } from './store' export const usePluginDependencies = () => { const { mutateAsync: checkWorkflowDependencies } = useMutationCheckDependencies() diff --git a/web/app/components/workflow/plugin-dependency/index.tsx b/web/app/components/workflow/plugin-dependency/index.tsx index 185722e1b7..69ee457527 100644 --- a/web/app/components/workflow/plugin-dependency/index.tsx +++ b/web/app/components/workflow/plugin-dependency/index.tsx @@ -1,6 +1,6 @@ import { useCallback } from 'react' -import { useStore } from './store' import InstallBundle from '@/app/components/plugins/install-plugin/install-bundle' +import { useStore } from './store' const PluginDependency = () => { const dependencies = useStore(s => s.dependencies) diff --git a/web/app/components/workflow/plugin-dependency/store.ts b/web/app/components/workflow/plugin-dependency/store.ts index a8e1d8171a..71b8420697 100644 --- a/web/app/components/workflow/plugin-dependency/store.ts +++ b/web/app/components/workflow/plugin-dependency/store.ts @@ -1,5 +1,5 @@ -import { create } from 'zustand' import type { Dependency } from '@/app/components/plugins/types' +import { create } from 'zustand' type Shape = { dependencies: Dependency[] 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 fd375fde45..3e67f2a1b1 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,20 +1,20 @@ -import { - useMemo, - useState, -} from 'react' +import type { AgentLogItemWithChildren } from '@/types/workflow' import { RiArrowRightSLine, RiListView, } from '@remixicon/react' -import { cn } from '@/utils/classnames' +import { + useMemo, + useState, +} from 'react' import Button from '@/app/components/base/button' -import type { AgentLogItemWithChildren } from '@/types/workflow' -import NodeStatusIcon from '@/app/components/workflow/nodes/_base/components/node-status-icon' -import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' -import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' -import BlockIcon from '@/app/components/workflow/block-icon' -import { BlockEnum } from '@/app/components/workflow/types' 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' +import NodeStatusIcon from '@/app/components/workflow/nodes/_base/components/node-status-icon' +import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' +import { BlockEnum } from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' type AgentLogItemProps = { item: AgentLogItemWithChildren @@ -54,7 +54,7 @@ const AgentLogItem = ({ }, [status]) return ( - <div className='rounded-[10px] border-[0.5px] border-components-panel-border bg-background-default'> + <div className="rounded-[10px] border-[0.5px] border-components-panel-border bg-background-default"> <div className={cn( 'flex cursor-pointer items-center pb-2 pl-1.5 pr-3 pt-2', @@ -64,43 +64,46 @@ const AgentLogItem = ({ > { expanded - ? <RiArrowRightSLine className='h-4 w-4 shrink-0 rotate-90 text-text-quaternary' /> - : <RiArrowRightSLine className='h-4 w-4 shrink-0 text-text-quaternary' /> + ? <RiArrowRightSLine className="h-4 w-4 shrink-0 rotate-90 text-text-quaternary" /> + : <RiArrowRightSLine className="h-4 w-4 shrink-0 text-text-quaternary" /> } <BlockIcon - className='mr-1.5 shrink-0' + className="mr-1.5 shrink-0" type={toolIcon ? BlockEnum.Tool : BlockEnum.Agent} toolIcon={toolIcon} /> <div - className='system-sm-semibold-uppercase grow truncate text-text-secondary' + className="system-sm-semibold-uppercase grow truncate text-text-secondary" title={label} > {label} </div> { metadata?.elapsed_time && ( - <div className='system-xs-regular mr-2 shrink-0 text-text-tertiary'>{metadata?.elapsed_time?.toFixed(3)}s</div> + <div className="system-xs-regular mr-2 shrink-0 text-text-tertiary"> + {metadata?.elapsed_time?.toFixed(3)} + s + </div> ) } <NodeStatusIcon status={mergeStatus} /> </div> { expanded && ( - <div className='p-1 pt-0'> + <div className="p-1 pt-0"> { !!children?.length && ( <Button - className='mb-1 flex w-full items-center justify-between' - variant='tertiary' + className="mb-1 flex w-full items-center justify-between" + variant="tertiary" onClick={() => onShowAgentOrToolLog(item)} > - <div className='flex items-center'> - <RiListView className='mr-1 h-4 w-4 shrink-0 text-components-button-tertiary-text' /> + <div className="flex items-center"> + <RiListView className="mr-1 h-4 w-4 shrink-0 text-components-button-tertiary-text" /> {`${children.length} Action Logs`} </div> - <div className='flex'> - <RiArrowRightSLine className='h-4 w-4 shrink-0 text-components-button-tertiary-text' /> + <div className="flex"> + <RiArrowRightSLine className="h-4 w-4 shrink-0 text-components-button-tertiary-text" /> </div> </Button> ) 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 6062946ede..fda802150f 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,12 +1,12 @@ -import { useState } from 'react' +import type { AgentLogItemWithChildren } from '@/types/workflow' import { RiMoreLine } from '@remixicon/react' +import { useState } from 'react' +import Button from '@/app/components/base/button' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' -import Button from '@/app/components/base/button' -import type { AgentLogItemWithChildren } from '@/types/workflow' type AgentLogNavMoreProps = { options: AgentLogItemWithChildren[] @@ -20,7 +20,7 @@ const AgentLogNavMore = ({ return ( <PortalToFollowElem - placement='bottom-start' + placement="bottom-start" offset={{ mainAxis: 2, crossAxis: -54, @@ -30,19 +30,19 @@ const AgentLogNavMore = ({ > <PortalToFollowElemTrigger> <Button - className='h-6 w-6' - variant='ghost-accent' + className="h-6 w-6" + variant="ghost-accent" > - <RiMoreLine className='h-4 w-4' /> + <RiMoreLine className="h-4 w-4" /> </Button> </PortalToFollowElemTrigger> <PortalToFollowElemContent> - <div className='w-[136px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'> + <div className="w-[136px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg"> { options.map(option => ( <div key={option.message_id} - className='system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 text-text-secondary hover:bg-state-base-hover' + className="system-md-regular flex h-8 cursor-pointer items-center rounded-lg px-2 text-text-secondary hover:bg-state-base-hover" onClick={() => { onShowAgentOrToolLog(option) setOpen(false) 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 9307f317e7..91abcdb99a 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,8 +1,8 @@ +import type { AgentLogItemWithChildren } from '@/types/workflow' import { RiArrowLeftLine } from '@remixicon/react' import { useTranslation } from 'react-i18next' -import AgentLogNavMore from './agent-log-nav-more' import Button from '@/app/components/base/button' -import type { AgentLogItemWithChildren } from '@/types/workflow' +import AgentLogNavMore from './agent-log-nav-more' type AgentLogNavProps = { agentOrToolLogItemStack: AgentLogItemWithChildren[] @@ -19,41 +19,41 @@ const AgentLogNav = ({ const end = agentOrToolLogItemStack.at(-1) return ( - <div className='flex h-8 items-center bg-components-panel-bg p-1 pr-3'> + <div className="flex h-8 items-center bg-components-panel-bg p-1 pr-3"> <Button - className='shrink-0 px-[5px]' - size='small' - variant='ghost-accent' + className="shrink-0 px-[5px]" + size="small" + variant="ghost-accent" onClick={() => { onShowAgentOrToolLog() }} > - <RiArrowLeftLine className='mr-1 h-3.5 w-3.5' /> + <RiArrowLeftLine className="mr-1 h-3.5 w-3.5" /> AGENT </Button> - <div className='system-xs-regular mx-0.5 shrink-0 text-divider-deep'>/</div> + <div className="system-xs-regular mx-0.5 shrink-0 text-divider-deep">/</div> { agentOrToolLogItemStackLength > 1 ? ( - <Button - className='shrink-0 px-[5px]' - size='small' - variant='ghost-accent' - onClick={() => onShowAgentOrToolLog(first)} - > - {t('workflow.nodes.agent.strategy.label')} - </Button> - ) + <Button + className="shrink-0 px-[5px]" + size="small" + variant="ghost-accent" + onClick={() => onShowAgentOrToolLog(first)} + > + {t('workflow.nodes.agent.strategy.label')} + </Button> + ) : ( - <div className='system-xs-medium-uppercase flex items-center px-[5px] text-text-tertiary'> - {t('workflow.nodes.agent.strategy.label')} - </div> - ) + <div className="system-xs-medium-uppercase flex items-center px-[5px] text-text-tertiary"> + {t('workflow.nodes.agent.strategy.label')} + </div> + ) } { !!mid.length && ( <> - <div className='system-xs-regular mx-0.5 shrink-0 text-divider-deep'>/</div> + <div className="system-xs-regular mx-0.5 shrink-0 text-divider-deep">/</div> <AgentLogNavMore options={mid} onShowAgentOrToolLog={onShowAgentOrToolLog} @@ -64,8 +64,8 @@ const AgentLogNav = ({ { !!end && agentOrToolLogItemStackLength > 1 && ( <> - <div className='system-xs-regular mx-0.5 shrink-0 text-divider-deep'>/</div> - <div className='system-xs-medium-uppercase flex items-center px-[5px] text-text-tertiary'> + <div className="system-xs-regular mx-0.5 shrink-0 text-divider-deep">/</div> + <div className="system-xs-medium-uppercase flex items-center px-[5px] text-text-tertiary"> {end.label} </div> </> diff --git a/web/app/components/workflow/run/agent-log/agent-log-trigger.tsx b/web/app/components/workflow/run/agent-log/agent-log-trigger.tsx index 85b37d72d6..e5381079dc 100644 --- a/web/app/components/workflow/run/agent-log/agent-log-trigger.tsx +++ b/web/app/components/workflow/run/agent-log/agent-log-trigger.tsx @@ -1,9 +1,9 @@ -import { RiArrowRightLine } from '@remixicon/react' -import { useTranslation } from 'react-i18next' import type { AgentLogItemWithChildren, NodeTracing, } from '@/types/workflow' +import { RiArrowRightLine } from '@remixicon/react' +import { useTranslation } from 'react-i18next' type AgentLogTriggerProps = { nodeInfo: NodeTracing @@ -19,27 +19,27 @@ const AgentLogTrigger = ({ return ( <div - className='cursor-pointer rounded-[10px] bg-components-button-tertiary-bg' + className="cursor-pointer rounded-[10px] bg-components-button-tertiary-bg" onClick={() => { onShowAgentOrToolLog({ message_id: nodeInfo.id, children: agentLog || [] } as AgentLogItemWithChildren) }} > - <div className='system-2xs-medium-uppercase flex items-center px-3 pt-2 text-text-tertiary'> + <div className="system-2xs-medium-uppercase flex items-center px-3 pt-2 text-text-tertiary"> {t('workflow.nodes.agent.strategy.label')} </div> - <div className='flex items-center pb-1.5 pl-3 pr-2 pt-1'> + <div className="flex items-center pb-1.5 pl-3 pr-2 pt-1"> { agentStrategy && ( - <div className='system-xs-medium grow text-text-secondary'> + <div className="system-xs-medium grow text-text-secondary"> {agentStrategy} </div> ) } <div - className='system-xs-regular-uppercase flex shrink-0 cursor-pointer items-center px-[1px] text-text-tertiary' + className="system-xs-regular-uppercase flex shrink-0 cursor-pointer items-center px-[1px] text-text-tertiary" > {t('runLog.detail')} - <RiArrowRightLine className='ml-0.5 h-3.5 w-3.5' /> + <RiArrowRightLine className="ml-0.5 h-3.5 w-3.5" /> </div> </div> </div> diff --git a/web/app/components/workflow/run/agent-log/agent-result-panel.tsx b/web/app/components/workflow/run/agent-log/agent-result-panel.tsx index 933f08b9da..6c9a3c0975 100644 --- a/web/app/components/workflow/run/agent-log/agent-result-panel.tsx +++ b/web/app/components/workflow/run/agent-log/agent-result-panel.tsx @@ -1,8 +1,8 @@ +import type { AgentLogItemWithChildren } from '@/types/workflow' import { RiAlertFill } from '@remixicon/react' import { useTranslation } from 'react-i18next' import AgentLogItem from './agent-log-item' import AgentLogNav from './agent-log-nav' -import type { AgentLogItemWithChildren } from '@/types/workflow' type AgentResultPanelProps = { agentOrToolLogItemStack: AgentLogItemWithChildren[] @@ -19,35 +19,34 @@ const AgentResultPanel = ({ const list = agentOrToolLogListMap[top.message_id] return ( - <div className='overflow-y-auto bg-background-section'> + <div className="overflow-y-auto bg-background-section"> <AgentLogNav agentOrToolLogItemStack={agentOrToolLogItemStack} onShowAgentOrToolLog={onShowAgentOrToolLog} /> - { - <div className='space-y-1 p-2'> - { - list.map(item => ( - <AgentLogItem - key={item.message_id} - item={item} - onShowAgentOrToolLog={onShowAgentOrToolLog} - /> - )) - } - </div> - } + <div className="space-y-1 p-2"> + { + list.map(item => ( + <AgentLogItem + key={item.message_id} + item={item} + onShowAgentOrToolLog={onShowAgentOrToolLog} + /> + )) + } + </div> { top.hasCircle && ( - <div className='mt-1 flex items-center rounded-xl border border-components-panel-border bg-components-panel-bg-blur px-3 pr-2 shadow-md'> + <div className="mt-1 flex items-center rounded-xl border border-components-panel-border bg-components-panel-bg-blur px-3 pr-2 shadow-md"> <div - className='absolute inset-0 rounded-xl opacity-[0.4]' + className="absolute inset-0 rounded-xl opacity-[0.4]" style={{ background: 'linear-gradient(92deg, rgba(247, 144, 9, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%)', }} - ></div> - <RiAlertFill className='mr-1.5 h-4 w-4 text-text-warning-secondary' /> - <div className='system-xs-medium text-text-primary'> + > + </div> + <RiAlertFill className="mr-1.5 h-4 w-4 text-text-warning-secondary" /> + <div className="system-xs-medium text-text-primary"> {t('runLog.circularInvocationTip')} </div> </div> diff --git a/web/app/components/workflow/run/hooks.ts b/web/app/components/workflow/run/hooks.ts index df54aa0240..593836f5b3 100644 --- a/web/app/components/workflow/run/hooks.ts +++ b/web/app/components/workflow/run/hooks.ts @@ -1,9 +1,3 @@ -import { - useCallback, - useRef, - useState, -} from 'react' -import { useBoolean } from 'ahooks' import type { AgentLogItemWithChildren, IterationDurationMap, @@ -11,6 +5,12 @@ import type { LoopVariableMap, NodeTracing, } from '@/types/workflow' +import { useBoolean } from 'ahooks' +import { + useCallback, + useRef, + useState, +} from 'react' export const useLogs = () => { const [showRetryDetail, { diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx index 9162429c4d..1bff24e2cc 100644 --- a/web/app/components/workflow/run/index.tsx +++ b/web/app/components/workflow/run/index.tsx @@ -1,20 +1,20 @@ 'use client' import type { FC } from 'react' +import type { WorkflowRunDetailResponse } from '@/models/log' +import type { NodeTracing } from '@/types/workflow' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { useContext } from 'use-context-selector' import { useTranslation } from 'react-i18next' +import { useContext } from 'use-context-selector' +import Loading from '@/app/components/base/loading' +import { ToastContext } from '@/app/components/base/toast' +import { WorkflowRunningStatus } from '@/app/components/workflow/types' +import { fetchRunDetail, fetchTracingList } from '@/service/log' +import { cn } from '@/utils/classnames' +import { useStore } from '../store' import OutputPanel from './output-panel' import ResultPanel from './result-panel' import StatusPanel from './status' -import { WorkflowRunningStatus } from '@/app/components/workflow/types' import TracingPanel from './tracing-panel' -import { cn } from '@/utils/classnames' -import { ToastContext } from '@/app/components/base/toast' -import Loading from '@/app/components/base/loading' -import { fetchRunDetail, fetchTracingList } from '@/service/log' -import type { NodeTracing } from '@/types/workflow' -import type { WorkflowRunDetailResponse } from '@/models/log' -import { useStore } from '../store' export type RunProps = { hideResult?: boolean @@ -118,9 +118,9 @@ const RunPanel: FC<RunProps> = ({ }, [loading]) return ( - <div className='relative flex grow flex-col'> + <div className="relative flex grow flex-col"> {/* tab */} - <div className='flex shrink-0 items-center border-b-[0.5px] border-divider-subtle px-4'> + <div className="flex shrink-0 items-center border-b-[0.5px] border-divider-subtle px-4"> {!hideResult && ( <div className={cn( @@ -128,7 +128,9 @@ const RunPanel: FC<RunProps> = ({ currentTab === 'RESULT' && '!border-util-colors-blue-brand-blue-brand-600 text-text-primary', )} onClick={() => switchTab('RESULT')} - >{t('runLog.result')}</div> + > + {t('runLog.result')} + </div> )} <div className={cn( @@ -136,19 +138,23 @@ const RunPanel: FC<RunProps> = ({ currentTab === 'DETAIL' && '!border-util-colors-blue-brand-blue-brand-600 text-text-primary', )} onClick={() => switchTab('DETAIL')} - >{t('runLog.detail')}</div> + > + {t('runLog.detail')} + </div> <div className={cn( 'system-sm-semibold-uppercase mr-6 cursor-pointer border-b-2 border-transparent py-3 text-text-tertiary', currentTab === 'TRACING' && '!border-util-colors-blue-brand-blue-brand-600 text-text-primary', )} onClick={() => switchTab('TRACING')} - >{t('runLog.tracing')}</div> + > + {t('runLog.tracing')} + </div> </div> {/* panel detail */} <div ref={ref} className={cn('relative h-0 grow overflow-y-auto rounded-b-xl bg-components-panel-bg')}> {loading && ( - <div className='flex h-full items-center justify-center bg-components-panel-bg'> + <div className="flex h-full items-center justify-center bg-components-panel-bg"> <Loading /> </div> )} @@ -185,7 +191,7 @@ const RunPanel: FC<RunProps> = ({ )} {!loading && currentTab === 'TRACING' && ( <TracingPanel - className='bg-background-section-burn' + className="bg-background-section-burn" list={list} /> )} 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 d1926da15e..a4286d94b0 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 @@ -1,12 +1,12 @@ -import { useTranslation } from 'react-i18next' -import { RiArrowRightSLine } from '@remixicon/react' -import Button from '@/app/components/base/button' import type { IterationDurationMap, NodeTracing, } from '@/types/workflow' -import { NodeRunningStatus } from '@/app/components/workflow/types' +import { RiArrowRightSLine } from '@remixicon/react' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' import { Iteration } from '@/app/components/base/icons/src/vender/workflow' +import { NodeRunningStatus } from '@/app/components/workflow/types' type IterationLogTriggerProps = { nodeInfo: NodeTracing @@ -21,7 +21,8 @@ const IterationLogTrigger = ({ const { t } = useTranslation() const filterNodesForInstance = (key: string): NodeTracing[] => { - if (!allExecutions) return [] + if (!allExecutions) + return [] const parallelNodes = allExecutions.filter(exec => exec.execution_metadata?.parallel_mode_run_id === key, @@ -117,9 +118,10 @@ const IterationLogTrigger = ({ // Find all failed iteration nodes allExecutions.forEach((exec) => { if (exec.execution_metadata?.iteration_id === nodeInfo.node_id - && exec.status === NodeRunningStatus.Failed - && exec.execution_metadata?.iteration_index !== undefined) + && exec.status === NodeRunningStatus.Failed + && exec.execution_metadata?.iteration_index !== undefined) { failedIterationIndices.add(exec.execution_metadata.iteration_index) + } }) } @@ -129,17 +131,20 @@ const IterationLogTrigger = ({ return ( <Button - className='flex w-full cursor-pointer items-center gap-2 self-stretch rounded-lg border-none bg-components-button-tertiary-bg-hover px-3 py-2 hover:bg-components-button-tertiary-bg-hover' + className="flex w-full cursor-pointer items-center gap-2 self-stretch rounded-lg border-none bg-components-button-tertiary-bg-hover px-3 py-2 hover:bg-components-button-tertiary-bg-hover" onClick={handleOnShowIterationDetail} > - <Iteration className='h-4 w-4 shrink-0 text-components-button-tertiary-text' /> - <div className='system-sm-medium flex-1 text-left text-components-button-tertiary-text'>{t('workflow.nodes.iteration.iteration', { count: displayIterationCount })}{errorCount > 0 && ( - <> - {t('workflow.nodes.iteration.comma')} - {t('workflow.nodes.iteration.error', { count: errorCount })} - </> - )}</div> - <RiArrowRightSLine className='h-4 w-4 shrink-0 text-components-button-tertiary-text' /> + <Iteration className="h-4 w-4 shrink-0 text-components-button-tertiary-text" /> + <div className="system-sm-medium flex-1 text-left text-components-button-tertiary-text"> + {t('workflow.nodes.iteration.iteration', { count: displayIterationCount })} + {errorCount > 0 && ( + <> + {t('workflow.nodes.iteration.comma')} + {t('workflow.nodes.iteration.error', { count: errorCount })} + </> + )} + </div> + <RiArrowRightSLine className="h-4 w-4 shrink-0 text-components-button-tertiary-text" /> </Button> ) } diff --git a/web/app/components/workflow/run/iteration-log/iteration-result-panel.tsx b/web/app/components/workflow/run/iteration-log/iteration-result-panel.tsx index 60293dbd2b..5933e897bd 100644 --- a/web/app/components/workflow/run/iteration-log/iteration-result-panel.tsx +++ b/web/app/components/workflow/run/iteration-log/iteration-result-panel.tsx @@ -1,18 +1,19 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useState } from 'react' -import { useTranslation } from 'react-i18next' +import type { IterationDurationMap, NodeTracing } from '@/types/workflow' import { RiArrowLeftLine, RiArrowRightSLine, RiErrorWarningLine, RiLoader2Line, } from '@remixicon/react' -import { NodeRunningStatus } from '@/app/components/workflow/types' -import TracingPanel from '@/app/components/workflow/run/tracing-panel' +import React, { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' import { Iteration } from '@/app/components/base/icons/src/vender/workflow' +import TracingPanel from '@/app/components/workflow/run/tracing-panel' +import { NodeRunningStatus } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' -import type { IterationDurationMap, NodeTracing } from '@/types/workflow' + const i18nPrefix = 'workflow.singleRun' type Props = { @@ -48,15 +49,15 @@ const IterationResultPanel: FC<Props> = ({ const hasDurationMap = iterDurationMap && Object.keys(iterDurationMap).length !== 0 if (hasFailed) - return <RiErrorWarningLine className='h-4 w-4 text-text-destructive' /> + return <RiErrorWarningLine className="h-4 w-4 text-text-destructive" /> if (isRunning) - return <RiLoader2Line className='h-3.5 w-3.5 animate-spin text-primary-600' /> + return <RiLoader2Line className="h-3.5 w-3.5 animate-spin text-primary-600" /> return ( <> {hasDurationMap && ( - <div className='system-xs-regular text-text-tertiary'> + <div className="system-xs-regular text-text-tertiary"> {countIterDuration(iteration, iterDurationMap)} </div> )} @@ -71,20 +72,20 @@ const IterationResultPanel: FC<Props> = ({ } return ( - <div className='bg-components-panel-bg'> + <div className="bg-components-panel-bg"> <div - className='flex h-8 cursor-pointer items-center border-b-[0.5px] border-b-divider-regular px-4 text-text-accent-secondary' + className="flex h-8 cursor-pointer items-center border-b-[0.5px] border-b-divider-regular px-4 text-text-accent-secondary" onClick={(e) => { e.stopPropagation() e.nativeEvent.stopImmediatePropagation() onBack() }} > - <RiArrowLeftLine className='mr-1 h-4 w-4' /> - <div className='system-sm-medium'>{t(`${i18nPrefix}.back`)}</div> + <RiArrowLeftLine className="mr-1 h-4 w-4" /> + <div className="system-sm-medium">{t(`${i18nPrefix}.back`)}</div> </div> {/* List */} - <div className='bg-components-panel-bg p-2'> + <div className="bg-components-panel-bg p-2"> {list.map((iteration, index) => ( <div key={index} className={cn('mb-1 overflow-hidden rounded-xl border-none bg-background-section-burn')}> <div @@ -96,27 +97,33 @@ const IterationResultPanel: FC<Props> = ({ onClick={() => toggleIteration(index)} > <div className={cn('flex grow items-center gap-2')}> - <div className='flex h-4 w-4 shrink-0 items-center justify-center rounded-[5px] border-divider-subtle bg-util-colors-cyan-cyan-500'> - <Iteration className='h-3 w-3 text-text-primary-on-surface' /> + <div className="flex h-4 w-4 shrink-0 items-center justify-center rounded-[5px] border-divider-subtle bg-util-colors-cyan-cyan-500"> + <Iteration className="h-3 w-3 text-text-primary-on-surface" /> </div> - <span className='system-sm-semibold-uppercase grow text-text-primary'> - {t(`${i18nPrefix}.iteration`)} {index + 1} + <span className="system-sm-semibold-uppercase grow text-text-primary"> + {t(`${i18nPrefix}.iteration`)} + {' '} + {index + 1} </span> {iterationStatusShow(index, iteration, iterDurationMap)} </div> </div> - {expandedIterations[index] && <div - className="h-px grow bg-divider-subtle" - ></div>} + {expandedIterations[index] && ( + <div + className="h-px grow bg-divider-subtle" + > + </div> + )} <div className={cn( 'transition-all duration-200', expandedIterations[index] ? 'opacity-100' : 'max-h-0 overflow-hidden opacity-0', - )}> + )} + > <TracingPanel list={iteration} - className='bg-background-section-burn' + className="bg-background-section-burn" /> </div> </div> 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 b086312baf..7280c29d05 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 @@ -1,11 +1,11 @@ -import { useTranslation } from 'react-i18next' -import { RiArrowRightSLine } from '@remixicon/react' -import Button from '@/app/components/base/button' import type { LoopDurationMap, LoopVariableMap, NodeTracing, } from '@/types/workflow' +import { RiArrowRightSLine } from '@remixicon/react' +import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' import { Loop } from '@/app/components/base/icons/src/vender/workflow' type LoopLogTriggerProps = { @@ -21,7 +21,8 @@ const LoopLogTrigger = ({ const { t } = useTranslation() const filterNodesForInstance = (key: string): NodeTracing[] => { - if (!allExecutions) return [] + if (!allExecutions) + return [] const parallelNodes = allExecutions.filter(exec => exec.execution_metadata?.parallel_mode_run_id === key, @@ -90,17 +91,20 @@ const LoopLogTrigger = ({ return ( <Button - className='flex w-full cursor-pointer items-center gap-2 self-stretch rounded-lg border-none bg-components-button-tertiary-bg-hover px-3 py-2 hover:bg-components-button-tertiary-bg-hover' + className="flex w-full cursor-pointer items-center gap-2 self-stretch rounded-lg border-none bg-components-button-tertiary-bg-hover px-3 py-2 hover:bg-components-button-tertiary-bg-hover" onClick={handleOnShowLoopDetail} > - <Loop className='h-4 w-4 shrink-0 text-components-button-tertiary-text' /> - <div className='system-sm-medium flex-1 text-left text-components-button-tertiary-text'>{t('workflow.nodes.loop.loop', { count: displayLoopCount })}{errorCount > 0 && ( - <> - {t('workflow.nodes.loop.comma')} - {t('workflow.nodes.loop.error', { count: errorCount })} - </> - )}</div> - <RiArrowRightSLine className='h-4 w-4 shrink-0 text-components-button-tertiary-text' /> + <Loop className="h-4 w-4 shrink-0 text-components-button-tertiary-text" /> + <div className="system-sm-medium flex-1 text-left text-components-button-tertiary-text"> + {t('workflow.nodes.loop.loop', { count: displayLoopCount })} + {errorCount > 0 && ( + <> + {t('workflow.nodes.loop.comma')} + {t('workflow.nodes.loop.error', { count: errorCount })} + </> + )} + </div> + <RiArrowRightSLine className="h-4 w-4 shrink-0 text-components-button-tertiary-text" /> </Button> ) } diff --git a/web/app/components/workflow/run/loop-log/loop-result-panel.tsx b/web/app/components/workflow/run/loop-log/loop-result-panel.tsx index 5ce2101d63..758148d8c1 100644 --- a/web/app/components/workflow/run/loop-log/loop-result-panel.tsx +++ b/web/app/components/workflow/run/loop-log/loop-result-panel.tsx @@ -1,20 +1,21 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useState } from 'react' -import { useTranslation } from 'react-i18next' +import type { LoopDurationMap, LoopVariableMap, NodeTracing } from '@/types/workflow' import { RiArrowLeftLine, RiArrowRightSLine, RiErrorWarningLine, RiLoader2Line, } from '@remixicon/react' -import { NodeRunningStatus } from '@/app/components/workflow/types' -import TracingPanel from '@/app/components/workflow/run/tracing-panel' +import React, { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' import { Loop } from '@/app/components/base/icons/src/vender/workflow' -import { cn } from '@/utils/classnames' -import type { LoopDurationMap, LoopVariableMap, NodeTracing } from '@/types/workflow' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' +import TracingPanel from '@/app/components/workflow/run/tracing-panel' +import { NodeRunningStatus } from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' + const i18nPrefix = 'workflow.singleRun' type Props = { @@ -54,15 +55,15 @@ const LoopResultPanel: FC<Props> = ({ const hasDurationMap = loopDurationMap && Object.keys(loopDurationMap).length !== 0 if (hasFailed) - return <RiErrorWarningLine className='h-4 w-4 text-text-destructive' /> + return <RiErrorWarningLine className="h-4 w-4 text-text-destructive" /> if (isRunning) - return <RiLoader2Line className='h-3.5 w-3.5 animate-spin text-primary-600' /> + return <RiLoader2Line className="h-3.5 w-3.5 animate-spin text-primary-600" /> return ( <> {hasDurationMap && ( - <div className='system-xs-regular text-text-tertiary'> + <div className="system-xs-regular text-text-tertiary"> {countLoopDuration(loop, loopDurationMap)} </div> )} @@ -77,20 +78,20 @@ const LoopResultPanel: FC<Props> = ({ } return ( - <div className='bg-components-panel-bg'> + <div className="bg-components-panel-bg"> <div - className='flex h-8 cursor-pointer items-center border-b-[0.5px] border-b-divider-regular px-4 text-text-accent-secondary' + className="flex h-8 cursor-pointer items-center border-b-[0.5px] border-b-divider-regular px-4 text-text-accent-secondary" onClick={(e) => { e.stopPropagation() e.nativeEvent.stopImmediatePropagation() onBack() }} > - <RiArrowLeftLine className='mr-1 h-4 w-4' /> - <div className='system-sm-medium'>{t(`${i18nPrefix}.back`)}</div> + <RiArrowLeftLine className="mr-1 h-4 w-4" /> + <div className="system-sm-medium">{t(`${i18nPrefix}.back`)}</div> </div> {/* List */} - <div className='bg-components-panel-bg p-2'> + <div className="bg-components-panel-bg p-2"> {list.map((loop, index) => ( <div key={index} className={cn('mb-1 overflow-hidden rounded-xl border-none bg-background-section-burn')}> <div @@ -102,27 +103,33 @@ const LoopResultPanel: FC<Props> = ({ onClick={() => toggleLoop(index)} > <div className={cn('flex grow items-center gap-2')}> - <div className='flex h-4 w-4 shrink-0 items-center justify-center rounded-[5px] border-divider-subtle bg-util-colors-cyan-cyan-500'> - <Loop className='h-3 w-3 text-text-primary-on-surface' /> + <div className="flex h-4 w-4 shrink-0 items-center justify-center rounded-[5px] border-divider-subtle bg-util-colors-cyan-cyan-500"> + <Loop className="h-3 w-3 text-text-primary-on-surface" /> </div> - <span className='system-sm-semibold-uppercase grow text-text-primary'> - {t(`${i18nPrefix}.loop`)} {index + 1} + <span className="system-sm-semibold-uppercase grow text-text-primary"> + {t(`${i18nPrefix}.loop`)} + {' '} + {index + 1} </span> {loopStatusShow(index, loop, loopDurationMap)} </div> </div> - {expandedLoops[index] && <div - className="h-px grow bg-divider-subtle" - ></div>} + {expandedLoops[index] && ( + <div + className="h-px grow bg-divider-subtle" + > + </div> + )} <div className={cn( 'transition-all duration-200', expandedLoops[index] ? 'opacity-100' : 'max-h-0 overflow-hidden opacity-0', - )}> + )} + > { loopVariableMap?.[index] && ( - <div className='p-2 pb-0'> + <div className="p-2 pb-0"> <CodeEditor readOnly title={<div>{t('workflow.nodes.loop.loopVariables').toLocaleUpperCase()}</div>} @@ -136,7 +143,7 @@ const LoopResultPanel: FC<Props> = ({ } <TracingPanel list={loop} - className='bg-background-section-burn' + className="bg-background-section-burn" /> </div> </div> diff --git a/web/app/components/workflow/run/loop-result-panel.tsx b/web/app/components/workflow/run/loop-result-panel.tsx index e87e8e54f7..61ba6db115 100644 --- a/web/app/components/workflow/run/loop-result-panel.tsx +++ b/web/app/components/workflow/run/loop-result-panel.tsx @@ -1,16 +1,16 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useState } from 'react' -import { useTranslation } from 'react-i18next' +import type { NodeTracing } from '@/types/workflow' import { RiArrowRightSLine, RiCloseLine, } from '@remixicon/react' -import { ArrowNarrowLeft } from '../../base/icons/src/vender/line/arrows' -import TracingPanel from './tracing-panel' +import React, { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' import { Loop } from '@/app/components/base/icons/src/vender/workflow' import { cn } from '@/utils/classnames' -import type { NodeTracing } from '@/types/workflow' +import { ArrowNarrowLeft } from '../../base/icons/src/vender/line/arrows' +import TracingPanel from './tracing-panel' const i18nPrefix = 'workflow.singleRun' @@ -40,17 +40,17 @@ const LoopResultPanel: FC<Props> = ({ const main = ( <> <div className={cn(!noWrap && 'shrink-0 ', 'px-4 pt-3')}> - <div className='flex h-8 shrink-0 items-center justify-between'> - <div className='system-xl-semibold truncate text-text-primary'> + <div className="flex h-8 shrink-0 items-center justify-between"> + <div className="system-xl-semibold truncate text-text-primary"> {t(`${i18nPrefix}.testRunLoop`)} </div> - <div className='ml-2 shrink-0 cursor-pointer p-1' onClick={onHide}> - <RiCloseLine className='h-4 w-4 text-text-tertiary' /> + <div className="ml-2 shrink-0 cursor-pointer p-1" onClick={onHide}> + <RiCloseLine className="h-4 w-4 text-text-tertiary" /> </div> </div> - <div className='flex cursor-pointer items-center space-x-1 py-2 text-text-accent-secondary' onClick={onBack}> - <ArrowNarrowLeft className='h-4 w-4' /> - <div className='system-sm-medium'>{t(`${i18nPrefix}.back`)}</div> + <div className="flex cursor-pointer items-center space-x-1 py-2 text-text-accent-secondary" onClick={onBack}> + <ArrowNarrowLeft className="h-4 w-4" /> + <div className="system-sm-medium">{t(`${i18nPrefix}.back`)}</div> </div> </div> {/* List */} @@ -66,30 +66,37 @@ const LoopResultPanel: FC<Props> = ({ onClick={() => toggleLoop(index)} > <div className={cn('flex grow items-center gap-2')}> - <div className='flex h-4 w-4 shrink-0 items-center justify-center rounded-[5px] border-divider-subtle bg-util-colors-cyan-cyan-500'> - <Loop className='h-3 w-3 text-text-primary-on-surface' /> + <div className="flex h-4 w-4 shrink-0 items-center justify-center rounded-[5px] border-divider-subtle bg-util-colors-cyan-cyan-500"> + <Loop className="h-3 w-3 text-text-primary-on-surface" /> </div> - <span className='system-sm-semibold-uppercase grow text-text-primary'> - {t(`${i18nPrefix}.loop`)} {index + 1} + <span className="system-sm-semibold-uppercase grow text-text-primary"> + {t(`${i18nPrefix}.loop`)} + {' '} + {index + 1} </span> <RiArrowRightSLine className={cn( 'h-4 w-4 shrink-0 text-text-tertiary transition-transform duration-200', expandedLoops[index] && 'rotate-90', - )} /> + )} + /> </div> </div> - {expandedLoops[index] && <div - className="h-px grow bg-divider-subtle" - ></div>} + {expandedLoops[index] && ( + <div + className="h-px grow bg-divider-subtle" + > + </div> + )} <div className={cn( 'transition-all duration-200', expandedLoops[index] ? 'opacity-100' : 'max-h-0 overflow-hidden opacity-0', - )}> + )} + > <TracingPanel list={loop} - className='bg-background-section-burn' + className="bg-background-section-burn" /> </div> @@ -109,16 +116,16 @@ const LoopResultPanel: FC<Props> = ({ return ( <div - className='absolute inset-0 z-10 rounded-2xl pt-10' + className="absolute inset-0 z-10 rounded-2xl pt-10" style={{ backgroundColor: 'rgba(16, 24, 40, 0.20)', }} onClick={handleNotBubble} > - <div className='flex h-full flex-col rounded-2xl bg-components-panel-bg'> + <div className="flex h-full flex-col rounded-2xl bg-components-panel-bg"> {main} </div> - </div > + </div> ) } export default React.memo(LoopResultPanel) diff --git a/web/app/components/workflow/run/meta.tsx b/web/app/components/workflow/run/meta.tsx index 13c35a42b8..20a03a2c5a 100644 --- a/web/app/components/workflow/run/meta.tsx +++ b/web/app/components/workflow/run/meta.tsx @@ -26,14 +26,14 @@ const MetaData: FC<Props> = ({ const { formatTime } = useTimestamp() return ( - <div className='relative'> - <div className='system-xs-medium-uppercase h-6 py-1 text-text-tertiary'>{t('runLog.meta.title')}</div> - <div className='py-1'> - <div className='flex'> - <div className='system-xs-regular w-[104px] shrink-0 truncate px-2 py-1.5 text-text-tertiary'>{t('runLog.meta.status')}</div> - <div className='system-xs-regular grow px-2 py-1.5 text-text-secondary'> + <div className="relative"> + <div className="system-xs-medium-uppercase h-6 py-1 text-text-tertiary">{t('runLog.meta.title')}</div> + <div className="py-1"> + <div className="flex"> + <div className="system-xs-regular w-[104px] shrink-0 truncate px-2 py-1.5 text-text-tertiary">{t('runLog.meta.status')}</div> + <div className="system-xs-regular grow px-2 py-1.5 text-text-secondary"> {status === 'running' && ( - <div className='my-1 h-2 w-16 rounded-sm bg-text-quaternary'/> + <div className="my-1 h-2 w-16 rounded-sm bg-text-quaternary" /> )} {status === 'succeeded' && ( <span>SUCCESS</span> @@ -52,44 +52,44 @@ const MetaData: FC<Props> = ({ )} </div> </div> - <div className='flex'> - <div className='system-xs-regular w-[104px] shrink-0 truncate px-2 py-1.5 text-text-tertiary'>{t('runLog.meta.executor')}</div> - <div className='system-xs-regular grow px-2 py-1.5 text-text-secondary'> + <div className="flex"> + <div className="system-xs-regular w-[104px] shrink-0 truncate px-2 py-1.5 text-text-tertiary">{t('runLog.meta.executor')}</div> + <div className="system-xs-regular grow px-2 py-1.5 text-text-secondary"> {status === 'running' && ( - <div className='my-1 h-2 w-[88px] rounded-sm bg-text-quaternary'/> + <div className="my-1 h-2 w-[88px] rounded-sm bg-text-quaternary" /> )} {status !== 'running' && ( <span>{executor || 'N/A'}</span> )} </div> </div> - <div className='flex'> - <div className='system-xs-regular w-[104px] shrink-0 truncate px-2 py-1.5 text-text-tertiary'>{t('runLog.meta.startTime')}</div> - <div className='system-xs-regular grow px-2 py-1.5 text-text-secondary'> + <div className="flex"> + <div className="system-xs-regular w-[104px] shrink-0 truncate px-2 py-1.5 text-text-tertiary">{t('runLog.meta.startTime')}</div> + <div className="system-xs-regular grow px-2 py-1.5 text-text-secondary"> {status === 'running' && ( - <div className='my-1 h-2 w-[72px] rounded-sm bg-text-quaternary'/> + <div className="my-1 h-2 w-[72px] rounded-sm bg-text-quaternary" /> )} {status !== 'running' && ( <span>{startTime ? formatTime(startTime, t('appLog.dateTimeFormat') as string) : '-'}</span> )} </div> </div> - <div className='flex'> - <div className='system-xs-regular w-[104px] shrink-0 truncate px-2 py-1.5 text-text-tertiary'>{t('runLog.meta.time')}</div> - <div className='system-xs-regular grow px-2 py-1.5 text-text-secondary'> + <div className="flex"> + <div className="system-xs-regular w-[104px] shrink-0 truncate px-2 py-1.5 text-text-tertiary">{t('runLog.meta.time')}</div> + <div className="system-xs-regular grow px-2 py-1.5 text-text-secondary"> {status === 'running' && ( - <div className='my-1 h-2 w-[72px] rounded-sm bg-text-quaternary'/> + <div className="my-1 h-2 w-[72px] rounded-sm bg-text-quaternary" /> )} {status !== 'running' && ( <span>{time ? `${time.toFixed(3)}s` : '-'}</span> )} </div> </div> - <div className='flex'> - <div className='system-xs-regular w-[104px] shrink-0 truncate px-2 py-1.5 text-text-tertiary'>{t('runLog.meta.tokens')}</div> - <div className='system-xs-regular grow px-2 py-1.5 text-text-secondary'> + <div className="flex"> + <div className="system-xs-regular w-[104px] shrink-0 truncate px-2 py-1.5 text-text-tertiary">{t('runLog.meta.tokens')}</div> + <div className="system-xs-regular grow px-2 py-1.5 text-text-secondary"> {status === 'running' && ( - <div className='my-1 h-2 w-[48px] rounded-sm bg-text-quaternary'/> + <div className="my-1 h-2 w-[48px] rounded-sm bg-text-quaternary" /> )} {status !== 'running' && ( <span>{`${tokens || 0} Tokens`}</span> @@ -97,11 +97,11 @@ const MetaData: FC<Props> = ({ </div> </div> {showSteps && ( - <div className='flex'> - <div className='system-xs-regular w-[104px] shrink-0 truncate px-2 py-1.5 text-text-tertiary'>{t('runLog.meta.steps')}</div> - <div className='system-xs-regular grow px-2 py-1.5 text-text-secondary'> + <div className="flex"> + <div className="system-xs-regular w-[104px] shrink-0 truncate px-2 py-1.5 text-text-tertiary">{t('runLog.meta.steps')}</div> + <div className="system-xs-regular grow px-2 py-1.5 text-text-secondary"> {status === 'running' && ( - <div className='my-1 h-2 w-[24px] rounded-sm bg-text-quaternary'/> + <div className="my-1 h-2 w-[24px] rounded-sm bg-text-quaternary" /> )} {status !== 'running' && ( <span>{steps}</span> diff --git a/web/app/components/workflow/run/node.tsx b/web/app/components/workflow/run/node.tsx index 6482433cde..d4afb59a91 100644 --- a/web/app/components/workflow/run/node.tsx +++ b/web/app/components/workflow/run/node.tsx @@ -1,24 +1,5 @@ 'use client' -import { useTranslation } from 'react-i18next' import type { FC } from 'react' -import { useCallback, useEffect, useMemo, useState } from 'react' -import { - RiAlertFill, - RiArrowRightSLine, - RiCheckboxCircleFill, - RiErrorWarningLine, - RiLoader2Line, -} from '@remixicon/react' -import BlockIcon from '../block-icon' -import { BlockEnum } from '../types' -import { RetryLogTrigger } from './retry-log' -import { IterationLogTrigger } from './iteration-log' -import { LoopLogTrigger } from './loop-log' -import { AgentLogTrigger } from './agent-log' -import { cn } from '@/utils/classnames' -import StatusContainer from '@/app/components/workflow/run/status-container' -import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' -import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' import type { AgentLogItemWithChildren, IterationDurationMap, @@ -26,11 +7,30 @@ import type { LoopVariableMap, NodeTracing, } from '@/types/workflow' +import { + RiAlertFill, + RiArrowRightSLine, + RiCheckboxCircleFill, + RiErrorWarningLine, + RiLoader2Line, +} from '@remixicon/react' +import { useCallback, useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Tooltip from '@/app/components/base/tooltip' +import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' import ErrorHandleTip from '@/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip' +import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' +import StatusContainer from '@/app/components/workflow/run/status-container' import { hasRetryNode } from '@/app/components/workflow/utils' import { useDocLink } from '@/context/i18n' -import Tooltip from '@/app/components/base/tooltip' +import { cn } from '@/utils/classnames' +import BlockIcon from '../block-icon' +import { BlockEnum } from '../types' import LargeDataAlert from '../variable-inspect/large-data-alert' +import { AgentLogTrigger } from './agent-log' +import { IterationLogTrigger } from './iteration-log' +import { LoopLogTrigger } from './loop-log' +import { RetryLogTrigger } from './retry-log' type Props = { className?: string @@ -113,7 +113,7 @@ const NodePanel: FC<Props> = ({ return ( <div className={cn('px-2 py-1', className)}> - <div className='group rounded-[10px] border border-components-panel-border bg-background-default shadow-xs transition-all hover:shadow-md'> + <div className="group rounded-[10px] border border-components-panel-border bg-background-default shadow-xs transition-all hover:shadow-md"> <div className={cn( 'flex cursor-pointer items-center pl-1 pr-3', @@ -133,22 +133,28 @@ const NodePanel: FC<Props> = ({ <BlockIcon size={inMessage ? 'xs' : 'sm'} className={cn('mr-2 shrink-0', inMessage && '!mr-1')} type={nodeInfo.node_type} toolIcon={nodeInfo.extras?.icon || nodeInfo.extras} /> <Tooltip popupContent={ - <div className='max-w-xs'>{nodeInfo.title}</div> + <div className="max-w-xs">{nodeInfo.title}</div> } > <div className={cn( 'system-xs-semibold-uppercase grow truncate text-text-secondary', hideInfo && '!text-xs', - )}>{nodeInfo.title}</div> + )} + > + {nodeInfo.title} + </div> </Tooltip> {nodeInfo.status !== 'running' && !hideInfo && ( - <div className='system-xs-regular shrink-0 text-text-tertiary'>{nodeInfo.execution_metadata?.total_tokens ? `${getTokenCount(nodeInfo.execution_metadata?.total_tokens || 0)} tokens · ` : ''}{`${getTime(nodeInfo.elapsed_time || 0)}`}</div> + <div className="system-xs-regular shrink-0 text-text-tertiary"> + {nodeInfo.execution_metadata?.total_tokens ? `${getTokenCount(nodeInfo.execution_metadata?.total_tokens || 0)} tokens · ` : ''} + {`${getTime(nodeInfo.elapsed_time || 0)}`} + </div> )} {nodeInfo.status === 'succeeded' && ( - <RiCheckboxCircleFill className='ml-2 h-3.5 w-3.5 shrink-0 text-text-success' /> + <RiCheckboxCircleFill className="ml-2 h-3.5 w-3.5 shrink-0 text-text-success" /> )} {nodeInfo.status === 'failed' && ( - <RiErrorWarningLine className='ml-2 h-3.5 w-3.5 shrink-0 text-text-warning' /> + <RiErrorWarningLine className="ml-2 h-3.5 w-3.5 shrink-0 text-text-warning" /> )} {nodeInfo.status === 'stopped' && ( <RiAlertFill className={cn('ml-2 h-4 w-4 shrink-0 text-text-warning-secondary', inMessage && 'h-3.5 w-3.5')} /> @@ -157,14 +163,14 @@ const NodePanel: FC<Props> = ({ <RiAlertFill className={cn('ml-2 h-4 w-4 shrink-0 text-text-warning-secondary', inMessage && 'h-3.5 w-3.5')} /> )} {nodeInfo.status === 'running' && ( - <div className='flex shrink-0 items-center text-[13px] font-medium leading-[16px] text-text-accent'> - <span className='mr-2 text-xs font-normal'>Running</span> - <RiLoader2Line className='h-3.5 w-3.5 animate-spin' /> + <div className="flex shrink-0 items-center text-[13px] font-medium leading-[16px] text-text-accent"> + <span className="mr-2 text-xs font-normal">Running</span> + <RiLoader2Line className="h-3.5 w-3.5 animate-spin" /> </div> )} </div> {!collapseState && !hideProcessDetail && ( - <div className='px-1 pb-1'> + <div className="px-1 pb-1"> {/* The nav to the iteration detail */} {isIterationNode && !notShowIterationNav && onShowIterationDetail && ( <IterationLogTrigger @@ -197,29 +203,29 @@ const NodePanel: FC<Props> = ({ } <div className={cn('mb-1', hideInfo && '!px-2 !py-0.5')}> {(nodeInfo.status === 'stopped') && ( - <StatusContainer status='stopped'> + <StatusContainer status="stopped"> {t('workflow.tracing.stopBy', { user: nodeInfo.created_by ? nodeInfo.created_by.name : 'N/A' })} </StatusContainer> )} {(nodeInfo.status === 'exception') && ( - <StatusContainer status='stopped'> + <StatusContainer status="stopped"> {nodeInfo.error} <a href={docLink('/guides/workflow/error-handling/error-type')} - target='_blank' - className='text-text-accent' + target="_blank" + className="text-text-accent" > {t('workflow.common.learnMore')} </a> </StatusContainer> )} {nodeInfo.status === 'failed' && ( - <StatusContainer status='failed'> + <StatusContainer status="failed"> {nodeInfo.error} </StatusContainer> )} {nodeInfo.status === 'retry' && ( - <StatusContainer status='failed'> + <StatusContainer status="failed"> {nodeInfo.error} </StatusContainer> )} @@ -232,7 +238,7 @@ const NodePanel: FC<Props> = ({ language={CodeLanguage.json} value={nodeInfo.inputs} isJSONStringifyBeauty - footer={nodeInfo.inputs_truncated && <LargeDataAlert textHasNoExport className='mx-1 mb-1 mt-2 h-7' />} + footer={nodeInfo.inputs_truncated && <LargeDataAlert textHasNoExport className="mx-1 mb-1 mt-2 h-7" />} /> </div> )} @@ -256,7 +262,7 @@ const NodePanel: FC<Props> = ({ value={nodeInfo.outputs} isJSONStringifyBeauty tip={<ErrorHandleTip type={nodeInfo.execution_metadata?.error_strategy} />} - footer={nodeInfo.outputs_truncated && <LargeDataAlert textHasNoExport downloadUrl={nodeInfo.outputs_full_content?.download_url} className='mx-1 mb-1 mt-2 h-7' />} + footer={nodeInfo.outputs_truncated && <LargeDataAlert textHasNoExport downloadUrl={nodeInfo.outputs_full_content?.download_url} className="mx-1 mb-1 mt-2 h-7" />} /> </div> )} diff --git a/web/app/components/workflow/run/output-panel.tsx b/web/app/components/workflow/run/output-panel.tsx index ad776a1651..9f43299059 100644 --- a/web/app/components/workflow/run/output-panel.tsx +++ b/web/app/components/workflow/run/output-panel.tsx @@ -1,13 +1,13 @@ 'use client' import type { FC } from 'react' import { useMemo } from 'react' -import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' -import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' -import { Markdown } from '@/app/components/base/markdown' import LoadingAnim from '@/app/components/base/chat/chat/loading-anim' import { FileList } from '@/app/components/base/file-uploader' -import StatusContainer from '@/app/components/workflow/run/status-container' import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils' +import { Markdown } from '@/app/components/base/markdown' +import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' +import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' +import StatusContainer from '@/app/components/workflow/run/status-container' type OutputPanelProps = { isRunning?: boolean @@ -54,24 +54,24 @@ const OutputPanel: FC<OutputPanelProps> = ({ return getProcessedFilesFromResponse(fileList) }, [outputs]) return ( - <div className='p-2'> + <div className="p-2"> {isRunning && ( - <div className='pl-[26px] pt-4'> - <LoadingAnim type='text' /> + <div className="pl-[26px] pt-4"> + <LoadingAnim type="text" /> </div> )} {!isRunning && error && ( - <div className='px-4'> - <StatusContainer status='failed'>{error}</StatusContainer> + <div className="px-4"> + <StatusContainer status="failed">{error}</StatusContainer> </div> )} {!isRunning && !outputs && ( - <div className='px-4 py-2'> - <Markdown content='No Output' /> + <div className="px-4 py-2"> + <Markdown content="No Output" /> </div> )} {isTextOutput && ( - <div className='px-4 py-2'> + <div className="px-4 py-2"> <Markdown content={ Array.isArray(outputs[Object.keys(outputs)[0]]) @@ -82,7 +82,7 @@ const OutputPanel: FC<OutputPanelProps> = ({ </div> )} {fileList.length > 0 && ( - <div className='px-4 py-2'> + <div className="px-4 py-2"> <FileList files={fileList} showDeleteAction={false} @@ -92,7 +92,7 @@ const OutputPanel: FC<OutputPanelProps> = ({ </div> )} {!isTextOutput && outputs && Object.keys(outputs).length > 0 && height! > 0 && ( - <div className='flex flex-col gap-2'> + <div className="flex flex-col gap-2"> <CodeEditor showFileList readOnly diff --git a/web/app/components/workflow/run/result-panel.tsx b/web/app/components/workflow/run/result-panel.tsx index a444860231..d8ca879025 100644 --- a/web/app/components/workflow/run/result-panel.tsx +++ b/web/app/components/workflow/run/result-panel.tsx @@ -1,22 +1,22 @@ 'use client' import type { FC } from 'react' -import { useTranslation } from 'react-i18next' -import StatusPanel from './status' -import MetaData from './meta' -import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' -import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' -import ErrorHandleTip from '@/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip' import type { AgentLogItemWithChildren, NodeTracing, } from '@/types/workflow' -import { BlockEnum } from '@/app/components/workflow/types' -import { hasRetryNode } from '@/app/components/workflow/utils' +import { useTranslation } from 'react-i18next' +import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' +import ErrorHandleTip from '@/app/components/workflow/nodes/_base/components/error-handle/error-handle-tip' +import { CodeLanguage } from '@/app/components/workflow/nodes/code/types' +import { AgentLogTrigger } from '@/app/components/workflow/run/agent-log' import { IterationLogTrigger } from '@/app/components/workflow/run/iteration-log' import { LoopLogTrigger } from '@/app/components/workflow/run/loop-log' import { RetryLogTrigger } from '@/app/components/workflow/run/retry-log' -import { AgentLogTrigger } from '@/app/components/workflow/run/agent-log' +import { BlockEnum } from '@/app/components/workflow/types' +import { hasRetryNode } from '@/app/components/workflow/utils' import LargeDataAlert from '../variable-inspect/large-data-alert' +import MetaData from './meta' +import StatusPanel from './status' export type ResultPanelProps = { nodeInfo?: NodeTracing @@ -80,8 +80,8 @@ const ResultPanel: FC<ResultPanelProps> = ({ const isToolNode = nodeInfo?.node_type === BlockEnum.Tool && !!nodeInfo?.agentLog?.length return ( - <div className='bg-components-panel-bg py-2'> - <div className='px-4 py-2'> + <div className="bg-components-panel-bg py-2"> + <div className="px-4 py-2"> <StatusPanel status={status} time={elapsed_time} @@ -91,7 +91,7 @@ const ResultPanel: FC<ResultPanelProps> = ({ isListening={isListening} /> </div> - <div className='px-4'> + <div className="px-4"> { isIterationNode && handleShowIterationResultList && ( <IterationLogTrigger @@ -125,14 +125,14 @@ const ResultPanel: FC<ResultPanelProps> = ({ ) } </div> - <div className='flex flex-col gap-2 px-4 py-2'> + <div className="flex flex-col gap-2 px-4 py-2"> <CodeEditor readOnly title={<div>{t('workflow.common.input').toLocaleUpperCase()}</div>} language={CodeLanguage.json} value={inputs} isJSONStringifyBeauty - footer={inputs_truncated && <LargeDataAlert textHasNoExport className='mx-1 mb-1 mt-2 h-7' />} + footer={inputs_truncated && <LargeDataAlert textHasNoExport className="mx-1 mb-1 mt-2 h-7" />} /> {process_data && ( <CodeEditor @@ -141,7 +141,7 @@ const ResultPanel: FC<ResultPanelProps> = ({ language={CodeLanguage.json} value={process_data} isJSONStringifyBeauty - footer={process_data_truncated && <LargeDataAlert textHasNoExport className='mx-1 mb-1 mt-2 h-7' />} + footer={process_data_truncated && <LargeDataAlert textHasNoExport className="mx-1 mb-1 mt-2 h-7" />} /> )} {(outputs || status === 'running') && ( @@ -152,14 +152,14 @@ const ResultPanel: FC<ResultPanelProps> = ({ value={outputs} isJSONStringifyBeauty tip={<ErrorHandleTip type={execution_metadata?.error_strategy} />} - footer={outputs_truncated && <LargeDataAlert textHasNoExport downloadUrl={outputs_full_content?.download_url} className='mx-1 mb-1 mt-2 h-7' />} + footer={outputs_truncated && <LargeDataAlert textHasNoExport downloadUrl={outputs_full_content?.download_url} className="mx-1 mb-1 mt-2 h-7" />} /> )} </div> - <div className='px-4 py-2'> - <div className='divider-subtle h-[0.5px]' /> + <div className="px-4 py-2"> + <div className="divider-subtle h-[0.5px]" /> </div> - <div className='px-4 py-2'> + <div className="px-4 py-2"> <MetaData status={status} executor={created_by} diff --git a/web/app/components/workflow/run/result-text.tsx b/web/app/components/workflow/run/result-text.tsx index cf99c604d3..2bb1d379b4 100644 --- a/web/app/components/workflow/run/result-text.tsx +++ b/web/app/components/workflow/run/result-text.tsx @@ -1,11 +1,11 @@ 'use client' import type { FC } from 'react' import { useTranslation } from 'react-i18next' +import LoadingAnim from '@/app/components/base/chat/chat/loading-anim' +import { FileList } from '@/app/components/base/file-uploader' import { ImageIndentLeft } from '@/app/components/base/icons/src/vender/line/editor' import { Markdown } from '@/app/components/base/markdown' -import LoadingAnim from '@/app/components/base/chat/chat/loading-anim' import StatusContainer from '@/app/components/workflow/run/status-container' -import { FileList } from '@/app/components/base/file-uploader' type ResultTextProps = { isRunning?: boolean @@ -24,26 +24,26 @@ const ResultText: FC<ResultTextProps> = ({ }) => { const { t } = useTranslation() return ( - <div className='bg-background-section-burn'> + <div className="bg-background-section-burn"> {isRunning && !outputs && ( - <div className='pl-[26px] pt-4'> - <LoadingAnim type='text' /> + <div className="pl-[26px] pt-4"> + <LoadingAnim type="text" /> </div> )} {!isRunning && error && ( - <div className='px-4 py-2'> - <StatusContainer status='failed'> + <div className="px-4 py-2"> + <StatusContainer status="failed"> {error} </StatusContainer> </div> )} {!isRunning && !outputs && !error && !allFiles?.length && ( - <div className='mt-[120px] flex flex-col items-center px-4 py-2 text-[13px] leading-[18px] text-gray-500'> - <ImageIndentLeft className='h-6 w-6 text-gray-400' /> - <div className='mr-2'>{t('runLog.resultEmpty.title')}</div> + <div className="mt-[120px] flex flex-col items-center px-4 py-2 text-[13px] leading-[18px] text-gray-500"> + <ImageIndentLeft className="h-6 w-6 text-gray-400" /> + <div className="mr-2">{t('runLog.resultEmpty.title')}</div> <div> {t('runLog.resultEmpty.tipLeft')} - <span onClick={onClick} className='cursor-pointer text-primary-600'>{t('runLog.resultEmpty.link')}</span> + <span onClick={onClick} className="cursor-pointer text-primary-600">{t('runLog.resultEmpty.link')}</span> {t('runLog.resultEmpty.tipRight')} </div> </div> @@ -51,13 +51,13 @@ const ResultText: FC<ResultTextProps> = ({ {(outputs || !!allFiles?.length) && ( <> {outputs && ( - <div className='px-4 py-2'> + <div className="px-4 py-2"> <Markdown content={outputs} /> </div> )} {!!allFiles?.length && allFiles.map(item => ( - <div key={item.varName} className='system-xs-regular flex flex-col gap-1 px-4 py-2'> - <div className='py-1 text-text-tertiary '>{item.varName}</div> + <div key={item.varName} className="system-xs-regular flex flex-col gap-1 px-4 py-2"> + <div className="py-1 text-text-tertiary ">{item.varName}</div> <FileList files={item.list} showDeleteAction={false} 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 0a39e4fec7..cc6d255310 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 { useTranslation } from 'react-i18next' +import type { NodeTracing } from '@/types/workflow' import { RiArrowRightSLine, RiRestartFill, } from '@remixicon/react' +import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' -import type { NodeTracing } from '@/types/workflow' type RetryLogTriggerProps = { nodeInfo: NodeTracing @@ -25,15 +25,15 @@ const RetryLogTrigger = ({ return ( <Button - className='mb-1 flex w-full items-center justify-between' - variant='tertiary' + className="mb-1 flex w-full items-center justify-between" + variant="tertiary" onClick={handleShowRetryResultList} > - <div className='flex items-center'> - <RiRestartFill className='mr-0.5 h-4 w-4 shrink-0 text-components-button-tertiary-text' /> + <div className="flex items-center"> + <RiRestartFill className="mr-0.5 h-4 w-4 shrink-0 text-components-button-tertiary-text" /> {t('workflow.nodes.common.retry.retries', { num: retryDetail?.length })} </div> - <RiArrowRightSLine className='h-4 w-4 shrink-0 text-components-button-tertiary-text' /> + <RiArrowRightSLine className="h-4 w-4 shrink-0 text-components-button-tertiary-text" /> </Button> ) } diff --git a/web/app/components/workflow/run/retry-log/retry-result-panel.tsx b/web/app/components/workflow/run/retry-log/retry-result-panel.tsx index 3d1eb77c93..6547bfd47f 100644 --- a/web/app/components/workflow/run/retry-log/retry-result-panel.tsx +++ b/web/app/components/workflow/run/retry-log/retry-result-panel.tsx @@ -1,13 +1,13 @@ 'use client' import type { FC } from 'react' -import { memo } from 'react' -import { useTranslation } from 'react-i18next' +import type { NodeTracing } from '@/types/workflow' import { RiArrowLeftLine, } from '@remixicon/react' +import { memo } from 'react' +import { useTranslation } from 'react-i18next' import TracingPanel from '../tracing-panel' -import type { NodeTracing } from '@/types/workflow' type Props = { list: NodeTracing[] @@ -23,14 +23,14 @@ const RetryResultPanel: FC<Props> = ({ return ( <div> <div - className='system-sm-medium flex h-8 cursor-pointer items-center bg-components-panel-bg px-4 text-text-accent-secondary' + className="system-sm-medium flex h-8 cursor-pointer items-center bg-components-panel-bg px-4 text-text-accent-secondary" onClick={(e) => { e.stopPropagation() e.nativeEvent.stopImmediatePropagation() onBack() }} > - <RiArrowLeftLine className='mr-1 h-4 w-4' /> + <RiArrowLeftLine className="mr-1 h-4 w-4" /> {t('workflow.singleRun.back')} </div> <TracingPanel @@ -38,9 +38,9 @@ const RetryResultPanel: FC<Props> = ({ ...item, title: `${t('workflow.nodes.common.retry.retry')} ${index + 1}`, }))} - className='bg-background-section-burn' + className="bg-background-section-burn" /> - </div > + </div> ) } export default memo(RetryResultPanel) diff --git a/web/app/components/workflow/run/special-result-panel.tsx b/web/app/components/workflow/run/special-result-panel.tsx index d985950b7e..34a9e2fb37 100644 --- a/web/app/components/workflow/run/special-result-panel.tsx +++ b/web/app/components/workflow/run/special-result-panel.tsx @@ -1,7 +1,3 @@ -import { RetryResultPanel } from './retry-log' -import { IterationResultPanel } from './iteration-log' -import { LoopResultPanel } from './loop-log' -import { AgentResultPanel } from './agent-log' import type { AgentLogItemWithChildren, IterationDurationMap, @@ -9,6 +5,10 @@ import type { LoopVariableMap, NodeTracing, } from '@/types/workflow' +import { AgentResultPanel } from './agent-log' +import { IterationResultPanel } from './iteration-log' +import { LoopResultPanel } from './loop-log' +import { RetryResultPanel } from './retry-log' export type SpecialResultPanelProps = { showRetryDetail?: boolean @@ -54,7 +54,8 @@ const SpecialResultPanel = ({ <div onClick={(e) => { e.stopPropagation() e.nativeEvent.stopImmediatePropagation() - }}> + }} + > { !!showRetryDetail && !!retryResultList?.length && setShowRetryDetailFalse && ( <RetryResultPanel diff --git a/web/app/components/workflow/run/status-container.tsx b/web/app/components/workflow/run/status-container.tsx index 618c25a3f4..d935135231 100644 --- a/web/app/components/workflow/run/status-container.tsx +++ b/web/app/components/workflow/run/status-container.tsx @@ -1,8 +1,8 @@ 'use client' import type { FC } from 'react' +import useTheme from '@/hooks/use-theme' import { Theme } from '@/types/app' import { cn } from '@/utils/classnames' -import useTheme from '@/hooks/use-theme' type Props = { status: string @@ -43,7 +43,9 @@ const StatusContainer: FC<Props> = ({ 'absolute left-0 top-0 h-[50px] w-[65%] bg-no-repeat', theme === Theme.light && 'bg-[url(~@/app/components/workflow/run/assets/highlight.svg)]', theme === Theme.dark && 'bg-[url(~@/app/components/workflow/run/assets/highlight-dark.svg)]', - )}></div> + )} + > + </div> {children} </div> ) diff --git a/web/app/components/workflow/run/status.tsx b/web/app/components/workflow/run/status.tsx index 7b272ee444..e8dc6136f3 100644 --- a/web/app/components/workflow/run/status.tsx +++ b/web/app/components/workflow/run/status.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' import { useTranslation } from 'react-i18next' -import { cn } from '@/utils/classnames' import Indicator from '@/app/components/header/indicator' import StatusContainer from '@/app/components/workflow/run/status-container' import { useDocLink } from '@/context/i18n' +import { cn } from '@/utils/classnames' type ResultProps = { status: string @@ -28,12 +28,13 @@ const StatusPanel: FC<ResultProps> = ({ return ( <StatusContainer status={status}> - <div className='flex'> + <div className="flex"> <div className={cn( 'max-w-[120px] flex-[33%]', status === 'partial-succeeded' && 'min-w-[140px]', - )}> - <div className='system-2xs-medium-uppercase mb-1 text-text-tertiary'>{t('runLog.resultPanel.status')}</div> + )} + > + <div className="system-2xs-medium-uppercase mb-1 text-text-tertiary">{t('runLog.resultPanel.status')}</div> <div className={cn( 'system-xs-semibold-uppercase flex items-center gap-1', @@ -46,58 +47,58 @@ const StatusPanel: FC<ResultProps> = ({ > {status === 'running' && ( <> - <Indicator color={'blue'} /> + <Indicator color="blue" /> <span>{isListening ? 'Listening' : 'Running'}</span> </> )} {status === 'succeeded' && ( <> - <Indicator color={'green'} /> + <Indicator color="green" /> <span>SUCCESS</span> </> )} {status === 'partial-succeeded' && ( <> - <Indicator color={'green'} /> + <Indicator color="green" /> <span>PARTIAL SUCCESS</span> </> )} {status === 'exception' && ( <> - <Indicator color={'yellow'} /> + <Indicator color="yellow" /> <span>EXCEPTION</span> </> )} {status === 'failed' && ( <> - <Indicator color={'red'} /> + <Indicator color="red" /> <span>FAIL</span> </> )} {status === 'stopped' && ( <> - <Indicator color={'yellow'} /> + <Indicator color="yellow" /> <span>STOP</span> </> )} </div> </div> - <div className='max-w-[152px] flex-[33%]'> - <div className='system-2xs-medium-uppercase mb-1 text-text-tertiary'>{t('runLog.resultPanel.time')}</div> - <div className='system-sm-medium flex items-center gap-1 text-text-secondary'> + <div className="max-w-[152px] flex-[33%]"> + <div className="system-2xs-medium-uppercase mb-1 text-text-tertiary">{t('runLog.resultPanel.time')}</div> + <div className="system-sm-medium flex items-center gap-1 text-text-secondary"> {status === 'running' && ( - <div className='h-2 w-16 rounded-sm bg-text-quaternary' /> + <div className="h-2 w-16 rounded-sm bg-text-quaternary" /> )} {status !== 'running' && ( <span>{time ? `${time?.toFixed(3)}s` : '-'}</span> )} </div> </div> - <div className='flex-[33%]'> - <div className='system-2xs-medium-uppercase mb-1 text-text-tertiary'>{t('runLog.resultPanel.tokens')}</div> - <div className='system-sm-medium flex items-center gap-1 text-text-secondary'> + <div className="flex-[33%]"> + <div className="system-2xs-medium-uppercase mb-1 text-text-tertiary">{t('runLog.resultPanel.tokens')}</div> + <div className="system-sm-medium flex items-center gap-1 text-text-secondary"> {status === 'running' && ( - <div className='h-2 w-20 rounded-sm bg-text-quaternary' /> + <div className="h-2 w-20 rounded-sm bg-text-quaternary" /> )} {status !== 'running' && ( <span>{`${tokens || 0} Tokens`}</span> @@ -107,13 +108,13 @@ const StatusPanel: FC<ResultProps> = ({ </div> {status === 'failed' && error && ( <> - <div className='my-2 h-[0.5px] bg-divider-subtle'/> - <div className='system-xs-regular whitespace-pre-wrap text-text-destructive'>{error}</div> + <div className="my-2 h-[0.5px] bg-divider-subtle" /> + <div className="system-xs-regular whitespace-pre-wrap text-text-destructive">{error}</div> { !!exceptionCounts && ( <> - <div className='my-2 h-[0.5px] bg-divider-subtle'/> - <div className='system-xs-regular text-text-destructive'> + <div className="my-2 h-[0.5px] bg-divider-subtle" /> + <div className="system-xs-regular text-text-destructive"> {t('workflow.nodes.common.errorHandle.partialSucceeded.tip', { num: exceptionCounts })} </div> </> @@ -124,8 +125,8 @@ const StatusPanel: FC<ResultProps> = ({ { status === 'partial-succeeded' && !!exceptionCounts && ( <> - <div className='my-2 h-[0.5px] bg-divider-deep'/> - <div className='system-xs-medium text-text-warning'> + <div className="my-2 h-[0.5px] bg-divider-deep" /> + <div className="system-xs-medium text-text-warning"> {t('workflow.nodes.common.errorHandle.partialSucceeded.tip', { num: exceptionCounts })} </div> </> @@ -134,13 +135,13 @@ const StatusPanel: FC<ResultProps> = ({ { status === 'exception' && ( <> - <div className='my-2 h-[0.5px] bg-divider-deep'/> - <div className='system-xs-medium text-text-warning'> + <div className="my-2 h-[0.5px] bg-divider-deep" /> + <div className="system-xs-medium text-text-warning"> {error} <a href={docLink('/guides/workflow/error-handling/error-type')} - target='_blank' - className='text-text-accent' + target="_blank" + className="text-text-accent" > {t('workflow.common.learnMore')} </a> diff --git a/web/app/components/workflow/run/tracing-panel.tsx b/web/app/components/workflow/run/tracing-panel.tsx index 77f64dfba8..0e1d6578ab 100644 --- a/web/app/components/workflow/run/tracing-panel.tsx +++ b/web/app/components/workflow/run/tracing-panel.tsx @@ -1,22 +1,22 @@ 'use client' import type { FC } from 'react' +import type { NodeTracing } from '@/types/workflow' +import { + RiArrowDownSLine, + RiMenu4Line, +} from '@remixicon/react' import React, { useCallback, useState, } from 'react' -import { cn } from '@/utils/classnames' -import { - RiArrowDownSLine, - RiMenu4Line, -} from '@remixicon/react' import { useTranslation } from 'react-i18next' +import formatNodeList from '@/app/components/workflow/run/utils/format-log' +import { cn } from '@/utils/classnames' import { useLogs } from './hooks' import NodePanel from './node' import SpecialResultPanel from './special-result-panel' -import type { NodeTracing } from '@/types/workflow' -import formatNodeList from '@/app/components/workflow/run/utils/format-log' type TracingPanelProps = { list: NodeTracing[] @@ -109,7 +109,8 @@ const TracingPanel: FC<TracingPanelProps> = ({ onMouseLeave={handleParallelMouseLeave} > <div className="mb-1 flex items-center"> - <button type="button" + <button + type="button" onClick={() => toggleCollapse(node.id)} className={cn( 'mr-2 transition-colors', @@ -124,13 +125,16 @@ const TracingPanel: FC<TracingPanelProps> = ({ <div className="mx-2 h-px grow bg-divider-subtle" style={{ background: 'linear-gradient(to right, rgba(16, 24, 40, 0.08), rgba(255, 255, 255, 0)' }} - ></div> + > + </div> </div> <div className={`relative pl-2 ${isCollapsed ? 'hidden' : ''}`}> <div className={cn( 'absolute bottom-0 left-[5px] top-0 w-[2px]', isHovered ? 'bg-text-accent-secondary' : 'bg-divider-subtle', - )}></div> + )} + > + </div> {parallelDetail.children!.map(renderNode)} </div> </div> diff --git a/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts b/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts index 59cf0ce308..9359e227be 100644 --- a/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts +++ b/web/app/components/workflow/run/utils/format-log/agent/index.spec.ts @@ -2,12 +2,12 @@ import format from '.' import { agentNodeData, multiStepsCircle, oneStepCircle } from './data' describe('agent', () => { - test('list should transform to tree', () => { + it('list should transform to tree', () => { // console.log(format(agentNodeData.in as any)) expect(format(agentNodeData.in as any)).toEqual(agentNodeData.expect) }) - test('list should remove circle log item', () => { + it('list should remove circle log item', () => { // format(oneStepCircle.in as any) expect(format(oneStepCircle.in as any)).toEqual(oneStepCircle.expect) expect(format(multiStepsCircle.in as any)).toEqual(multiStepsCircle.expect) diff --git a/web/app/components/workflow/run/utils/format-log/agent/index.ts b/web/app/components/workflow/run/utils/format-log/agent/index.ts index 311e56a269..a4c1ea5167 100644 --- a/web/app/components/workflow/run/utils/format-log/agent/index.ts +++ b/web/app/components/workflow/run/utils/format-log/agent/index.ts @@ -1,6 +1,6 @@ -import { BlockEnum } from '@/app/components/workflow/types' import type { AgentLogItem, AgentLogItemWithChildren, NodeTracing } from '@/types/workflow' import { cloneDeep } from 'lodash-es' +import { BlockEnum } from '@/app/components/workflow/types' const supportedAgentLogNodes = [BlockEnum.Agent, BlockEnum.Tool] diff --git a/web/app/components/workflow/run/utils/format-log/graph-to-log-struct.ts b/web/app/components/workflow/run/utils/format-log/graph-to-log-struct.ts index 741fa08ebf..55e1c2b406 100644 --- a/web/app/components/workflow/run/utils/format-log/graph-to-log-struct.ts +++ b/web/app/components/workflow/run/utils/format-log/graph-to-log-struct.ts @@ -1,7 +1,7 @@ -type IterationInfo = { iterationId: string; iterationIndex: number } -type LoopInfo = { loopId: string; loopIndex: number } -type NodePlain = { nodeType: 'plain'; nodeId: string; } & (Partial<IterationInfo> & Partial<LoopInfo>) -type NodeComplex = { nodeType: string; nodeId: string; params: (NodePlain | (NodeComplex & (Partial<IterationInfo> & Partial<LoopInfo>)) | Node[] | number)[] } & (Partial<IterationInfo> & Partial<LoopInfo>) +type IterationInfo = { iterationId: string, iterationIndex: number } +type LoopInfo = { loopId: string, loopIndex: number } +type NodePlain = { nodeType: 'plain', nodeId: string } & (Partial<IterationInfo> & Partial<LoopInfo>) +type NodeComplex = { nodeType: string, nodeId: string, params: (NodePlain | (NodeComplex & (Partial<IterationInfo> & Partial<LoopInfo>)) | Node[] | number)[] } & (Partial<IterationInfo> & Partial<LoopInfo>) type Node = NodePlain | NodeComplex /** @@ -25,8 +25,10 @@ function parseTopLevelFlow(dsl: string): string[] { for (let i = 0; i < dsl.length; i++) { const char = dsl[i] - if (char === '(') nested++ - if (char === ')') nested-- + if (char === '(') + nested++ + if (char === ')') + nested-- if (char === '-' && dsl[i + 1] === '>' && nested === 0) { segments.push(buffer.trim()) buffer = '' @@ -61,8 +63,10 @@ function parseNode(nodeStr: string, parentIterationId?: string, parentLoopId?: s // Split the inner content by commas, respecting nested parentheses for (let i = 0; i < innerContent.length; i++) { const char = innerContent[i] - if (char === '(') nested++ - if (char === ')') nested-- + if (char === '(') + nested++ + if (char === ')') + nested-- if (char === ',' && nested === 0) { parts.push(buffer.trim()) @@ -137,12 +141,12 @@ function parseParams(paramParts: string[], parentIteration?: string, parentLoopI } type NodeData = { - id: string; - node_id: string; - title: string; - node_type?: string; - execution_metadata: Record<string, any>; - status: string; + id: string + node_id: string + title: string + node_type?: string + execution_metadata: Record<string, any> + status: string } /** @@ -181,13 +185,17 @@ function convertRetryNode(node: Node): NodeData[] { id: nodeId, node_id: nodeId, title: nodeId, - execution_metadata: iterationId ? { - iteration_id: iterationId, - iteration_index: iterationIndex || 0, - } : loopId ? { - loop_id: loopId, - loop_index: loopIndex || 0, - } : {}, + execution_metadata: iterationId + ? { + iteration_id: iterationId, + iteration_index: iterationIndex || 0, + } + : loopId + ? { + loop_id: loopId, + loop_index: loopIndex || 0, + } + : {}, status: 'retry', }) } diff --git a/web/app/components/workflow/run/utils/format-log/index.ts b/web/app/components/workflow/run/utils/format-log/index.ts index 4f97814e46..2c89e91571 100644 --- a/web/app/components/workflow/run/utils/format-log/index.ts +++ b/web/app/components/workflow/run/utils/format-log/index.ts @@ -1,11 +1,11 @@ import type { NodeTracing } from '@/types/workflow' +import { cloneDeep } from 'lodash-es' +import { BlockEnum } from '../../../types' +import formatAgentNode from './agent' import { addChildrenToIterationNode } from './iteration' import { addChildrenToLoopNode } from './loop' import formatParallelNode from './parallel' import formatRetryNode from './retry' -import formatAgentNode from './agent' -import { cloneDeep } from 'lodash-es' -import { BlockEnum } from '../../../types' const formatIterationAndLoopNode = (list: NodeTracing[], t: any) => { const clonedList = cloneDeep(list) diff --git a/web/app/components/workflow/run/utils/format-log/iteration/index.spec.ts b/web/app/components/workflow/run/utils/format-log/iteration/index.spec.ts index f5feb5c367..855ac4c69d 100644 --- a/web/app/components/workflow/run/utils/format-log/iteration/index.spec.ts +++ b/web/app/components/workflow/run/utils/format-log/iteration/index.spec.ts @@ -1,12 +1,12 @@ +import { noop } from 'lodash-es' import format from '.' import graphToLogStruct from '../graph-to-log-struct' -import { noop } from 'lodash-es' describe('iteration', () => { const list = graphToLogStruct('start -> (iteration, iterationNode, plainNode1 -> plainNode2)') // const [startNode, iterationNode, ...iterations] = list const result = format(list as any, noop) - test('result should have no nodes in iteration node', () => { + it('result should have no nodes in iteration node', () => { expect((result as any).find((item: any) => !!item.execution_metadata?.iteration_id)).toBeUndefined() }) // test('iteration should put nodes in details', () => { diff --git a/web/app/components/workflow/run/utils/format-log/iteration/index.ts b/web/app/components/workflow/run/utils/format-log/iteration/index.ts index d0224d0259..fbb81118a1 100644 --- a/web/app/components/workflow/run/utils/format-log/iteration/index.ts +++ b/web/app/components/workflow/run/utils/format-log/iteration/index.ts @@ -1,11 +1,12 @@ -import { BlockEnum } from '@/app/components/workflow/types' import type { NodeTracing } from '@/types/workflow' +import { BlockEnum } from '@/app/components/workflow/types' import formatParallelNode from '../parallel' export function addChildrenToIterationNode(iterationNode: NodeTracing, childrenNodes: NodeTracing[]): NodeTracing { const details: NodeTracing[][] = [] childrenNodes.forEach((item, index) => { - if (!item.execution_metadata) return + if (!item.execution_metadata) + return const { iteration_index = 0 } = item.execution_metadata const runIndex: number = iteration_index !== undefined ? iteration_index : index if (!details[runIndex]) diff --git a/web/app/components/workflow/run/utils/format-log/loop/index.spec.ts b/web/app/components/workflow/run/utils/format-log/loop/index.spec.ts index 1f70cef02c..aee2a432c3 100644 --- a/web/app/components/workflow/run/utils/format-log/loop/index.spec.ts +++ b/web/app/components/workflow/run/utils/format-log/loop/index.spec.ts @@ -1,15 +1,15 @@ +import { noop } from 'lodash-es' import format from '.' import graphToLogStruct from '../graph-to-log-struct' -import { noop } from 'lodash-es' describe('loop', () => { const list = graphToLogStruct('start -> (loop, loopNode, plainNode1 -> plainNode2)') const [startNode, loopNode, ...loops] = list const result = format(list as any, noop) - test('result should have no nodes in loop node', () => { + it('result should have no nodes in loop node', () => { expect(result.find(item => !!item.execution_metadata?.loop_id)).toBeUndefined() }) - test('loop should put nodes in details', () => { + it('loop should put nodes in details', () => { expect(result).toEqual([ startNode, { diff --git a/web/app/components/workflow/run/utils/format-log/loop/index.ts b/web/app/components/workflow/run/utils/format-log/loop/index.ts index b12e12e48f..fd26c3916e 100644 --- a/web/app/components/workflow/run/utils/format-log/loop/index.ts +++ b/web/app/components/workflow/run/utils/format-log/loop/index.ts @@ -1,11 +1,12 @@ -import { BlockEnum } from '@/app/components/workflow/types' import type { NodeTracing } from '@/types/workflow' +import { BlockEnum } from '@/app/components/workflow/types' import formatParallelNode from '../parallel' export function addChildrenToLoopNode(loopNode: NodeTracing, childrenNodes: NodeTracing[]): NodeTracing { const details: NodeTracing[][] = [] childrenNodes.forEach((item) => { - if (!item.execution_metadata) return + if (!item.execution_metadata) + return const { parallel_mode_run_id, loop_index = 0 } = item.execution_metadata const runIndex: number = (parallel_mode_run_id || loop_index) as number if (!details[runIndex]) diff --git a/web/app/components/workflow/run/utils/format-log/parallel/index.ts b/web/app/components/workflow/run/utils/format-log/parallel/index.ts index 22c96918e9..e2c7592b5d 100644 --- a/web/app/components/workflow/run/utils/format-log/parallel/index.ts +++ b/web/app/components/workflow/run/utils/format-log/parallel/index.ts @@ -1,5 +1,5 @@ -import { BlockEnum } from '@/app/components/workflow/types' import type { NodeTracing } from '@/types/workflow' +import { BlockEnum } from '@/app/components/workflow/types' function printNodeStructure(node: NodeTracing, depth: number) { const indent = ' '.repeat(depth) @@ -12,11 +12,13 @@ function printNodeStructure(node: NodeTracing, depth: number) { } function addTitle({ - list, depth, belongParallelIndexInfo, + list, + depth, + belongParallelIndexInfo, }: { - list: NodeTracing[], - depth: number, - belongParallelIndexInfo?: string, + list: NodeTracing[] + depth: number + belongParallelIndexInfo?: string }, t: any) { let branchIndex = 0 const hasMoreThanOneParallel = list.filter(node => node.parallelDetail?.isParallelStartNode).length > 1 diff --git a/web/app/components/workflow/run/utils/format-log/retry/index.spec.ts b/web/app/components/workflow/run/utils/format-log/retry/index.spec.ts index 2ce9554ba4..cb823a0e91 100644 --- a/web/app/components/workflow/run/utils/format-log/retry/index.spec.ts +++ b/web/app/components/workflow/run/utils/format-log/retry/index.spec.ts @@ -6,10 +6,10 @@ describe('retry', () => { const steps = graphToLogStruct('start -> (retry, retryNode, 3)') const [startNode, retryNode, ...retryDetail] = steps const result = format(steps as any) - test('should have no retry status nodes', () => { + it('should have no retry status nodes', () => { expect(result.find(item => item.status === 'retry')).toBeUndefined() }) - test('should put retry nodes in retryDetail', () => { + it('should put retry nodes in retryDetail', () => { expect(result).toEqual([ startNode, { diff --git a/web/app/components/workflow/selection-contextmenu.tsx b/web/app/components/workflow/selection-contextmenu.tsx index 53392f2cd3..7ea18c94f3 100644 --- a/web/app/components/workflow/selection-contextmenu.tsx +++ b/web/app/components/workflow/selection-contextmenu.tsx @@ -1,13 +1,3 @@ -import { - memo, - useCallback, - useEffect, - useMemo, - useRef, -} from 'react' -import { useTranslation } from 'react-i18next' -import { useClickAway } from 'ahooks' -import { useStore as useReactFlowStore, useStoreApi } from 'reactflow' import { RiAlignBottom, RiAlignCenter, @@ -16,12 +6,21 @@ import { RiAlignRight, RiAlignTop, } from '@remixicon/react' -import { useNodesReadOnly, useNodesSyncDraft } from './hooks' +import { useClickAway } from 'ahooks' import { produce } from 'immer' -import { WorkflowHistoryEvent, useWorkflowHistory } from './hooks/use-workflow-history' -import { useStore } from './store' +import { + memo, + useCallback, + useEffect, + useMemo, + useRef, +} from 'react' +import { useTranslation } from 'react-i18next' +import { useStore as useReactFlowStore, useStoreApi } from 'reactflow' +import { useNodesReadOnly, useNodesSyncDraft } from './hooks' import { useSelectionInteractions } from './hooks/use-selection-interactions' -import { useWorkflowStore } from './store' +import { useWorkflowHistory, WorkflowHistoryEvent } from './hooks/use-workflow-history' +import { useStore, useWorkflowStore } from './store' enum AlignType { Left = 'left', @@ -56,7 +55,8 @@ const SelectionContextmenu = () => { const menuRef = useRef<HTMLDivElement>(null) const menuPosition = useMemo(() => { - if (!selectionMenu) return { left: 0, top: 0 } + if (!selectionMenu) + return { left: 0, top: 0 } let left = selectionMenu.left let top = selectionMenu.top @@ -220,7 +220,8 @@ const SelectionContextmenu = () => { for (let i = 1; i < sortedNodes.length - 1; i++) { const nodeToAlign = sortedNodes[i] const currentNode = draft.find(n => n.id === nodeToAlign.id) - if (!currentNode) continue + if (!currentNode) + continue if (alignType === AlignType.DistributeHorizontal) { // Position = previous right edge + spacing @@ -272,7 +273,7 @@ const SelectionContextmenu = () => { // If container node is selected, add its children to the exclusion set if (selectedNodeIds.includes(node.id)) { // Add all its children to the childNodeIds set - node.data._children.forEach((child: { nodeId: string; nodeType: string }) => { + node.data._children.forEach((child: { nodeId: string, nodeType: string }) => { childNodeIds.add(child.nodeId) }) } @@ -373,78 +374,78 @@ const SelectionContextmenu = () => { return ( <div - className='absolute z-[9]' + className="absolute z-[9]" style={{ left: menuPosition.left, top: menuPosition.top, }} ref={ref} > - <div ref={menuRef} className='w-[240px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl'> - <div className='p-1'> - <div className='system-xs-medium px-2 py-2 text-text-tertiary'> + <div ref={menuRef} className="w-[240px] rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl"> + <div className="p-1"> + <div className="system-xs-medium px-2 py-2 text-text-tertiary"> {t('workflow.operator.vertical')} </div> <div - className='flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => handleAlignNodes(AlignType.Top)} > - <RiAlignTop className='h-4 w-4' /> + <RiAlignTop className="h-4 w-4" /> {t('workflow.operator.alignTop')} </div> <div - className='flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => handleAlignNodes(AlignType.Middle)} > - <RiAlignCenter className='h-4 w-4 rotate-90' /> + <RiAlignCenter className="h-4 w-4 rotate-90" /> {t('workflow.operator.alignMiddle')} </div> <div - className='flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => handleAlignNodes(AlignType.Bottom)} > - <RiAlignBottom className='h-4 w-4' /> + <RiAlignBottom className="h-4 w-4" /> {t('workflow.operator.alignBottom')} </div> <div - className='flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => handleAlignNodes(AlignType.DistributeVertical)} > - <RiAlignJustify className='h-4 w-4 rotate-90' /> + <RiAlignJustify className="h-4 w-4 rotate-90" /> {t('workflow.operator.distributeVertical')} </div> </div> - <div className='h-px bg-divider-regular'></div> - <div className='p-1'> - <div className='system-xs-medium px-2 py-2 text-text-tertiary'> + <div className="h-px bg-divider-regular"></div> + <div className="p-1"> + <div className="system-xs-medium px-2 py-2 text-text-tertiary"> {t('workflow.operator.horizontal')} </div> <div - className='flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => handleAlignNodes(AlignType.Left)} > - <RiAlignLeft className='h-4 w-4' /> + <RiAlignLeft className="h-4 w-4" /> {t('workflow.operator.alignLeft')} </div> <div - className='flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => handleAlignNodes(AlignType.Center)} > - <RiAlignCenter className='h-4 w-4' /> + <RiAlignCenter className="h-4 w-4" /> {t('workflow.operator.alignCenter')} </div> <div - className='flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => handleAlignNodes(AlignType.Right)} > - <RiAlignRight className='h-4 w-4' /> + <RiAlignRight className="h-4 w-4" /> {t('workflow.operator.alignRight')} </div> <div - className='flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover' + className="flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 text-sm text-text-secondary hover:bg-state-base-hover" onClick={() => handleAlignNodes(AlignType.DistributeHorizontal)} > - <RiAlignJustify className='h-4 w-4' /> + <RiAlignJustify className="h-4 w-4" /> {t('workflow.operator.distributeHorizontal')} </div> </div> diff --git a/web/app/components/workflow/shortcuts-name.tsx b/web/app/components/workflow/shortcuts-name.tsx index c901d0c2d1..d0ce007f61 100644 --- a/web/app/components/workflow/shortcuts-name.tsx +++ b/web/app/components/workflow/shortcuts-name.tsx @@ -1,6 +1,6 @@ import { memo } from 'react' -import { getKeyboardKeyNameBySystem } from './utils' import { cn } from '@/utils/classnames' +import { getKeyboardKeyNameBySystem } from './utils' type ShortcutsNameProps = { keys: string[] @@ -16,7 +16,8 @@ const ShortcutsName = ({ <div className={cn( 'flex items-center gap-0.5', className, - )}> + )} + > { keys.map(key => ( <div diff --git a/web/app/components/workflow/simple-node/index.tsx b/web/app/components/workflow/simple-node/index.tsx index d6f0804f34..3da2c71bc5 100644 --- a/web/app/components/workflow/simple-node/index.tsx +++ b/web/app/components/workflow/simple-node/index.tsx @@ -1,10 +1,9 @@ import type { FC, } from 'react' -import { - memo, - useMemo, -} from 'react' +import type { + NodeProps, +} from '@/app/components/workflow/types' import { RiAlertFill, RiCheckboxCircleFill, @@ -12,20 +11,21 @@ import { RiLoader2Line, } from '@remixicon/react' import { - NodeTargetHandle, -} from '@/app/components/workflow/nodes/_base/components/node-handle' -import NodeControl from '@/app/components/workflow/nodes/_base/components/node-control' -import { cn } from '@/utils/classnames' + memo, + useMemo, +} from 'react' import BlockIcon from '@/app/components/workflow/block-icon' -import type { - NodeProps, -} from '@/app/components/workflow/types' -import { - NodeRunningStatus, -} from '@/app/components/workflow/types' import { useNodesReadOnly, } from '@/app/components/workflow/hooks' +import NodeControl from '@/app/components/workflow/nodes/_base/components/node-control' +import { + NodeTargetHandle, +} from '@/app/components/workflow/nodes/_base/components/node-handle' +import { + NodeRunningStatus, +} from '@/app/components/workflow/types' +import { cn } from '@/utils/classnames' type SimpleNodeProps = NodeProps @@ -80,8 +80,8 @@ const SimpleNode: FC<SimpleNodeProps> = ({ <NodeTargetHandle id={id} data={data} - handleClassName='!top-4 !-left-[9px] !translate-y-0' - handleId='target' + handleClassName="!top-4 !-left-[9px] !translate-y-0" + handleId="target" /> ) } @@ -95,15 +95,16 @@ const SimpleNode: FC<SimpleNodeProps> = ({ } <div className={cn( 'flex items-center rounded-t-2xl px-3 pb-2 pt-3', - )}> + )} + > <BlockIcon - className='mr-2 shrink-0' + className="mr-2 shrink-0" type={data.type} - size='md' + size="md" /> <div title={data.title} - className='system-sm-semibold-uppercase mr-1 flex grow items-center truncate text-text-primary' + className="system-sm-semibold-uppercase mr-1 flex grow items-center truncate text-text-primary" > <div> {data.title} @@ -111,22 +112,22 @@ const SimpleNode: FC<SimpleNodeProps> = ({ </div> { (data._runningStatus === NodeRunningStatus.Running || data._singleRunningStatus === NodeRunningStatus.Running) && ( - <RiLoader2Line className='h-3.5 w-3.5 animate-spin text-text-accent' /> + <RiLoader2Line className="h-3.5 w-3.5 animate-spin text-text-accent" /> ) } { data._runningStatus === NodeRunningStatus.Succeeded && ( - <RiCheckboxCircleFill className='h-3.5 w-3.5 text-text-success' /> + <RiCheckboxCircleFill className="h-3.5 w-3.5 text-text-success" /> ) } { data._runningStatus === NodeRunningStatus.Failed && ( - <RiErrorWarningFill className='h-3.5 w-3.5 text-text-destructive' /> + <RiErrorWarningFill className="h-3.5 w-3.5 text-text-destructive" /> ) } { data._runningStatus === NodeRunningStatus.Exception && ( - <RiAlertFill className='h-3.5 w-3.5 text-text-warning-secondary' /> + <RiAlertFill className="h-3.5 w-3.5 text-text-warning-secondary" /> ) } </div> diff --git a/web/app/components/workflow/store/__tests__/trigger-status.test.ts b/web/app/components/workflow/store/__tests__/trigger-status.test.ts index d7e1284487..56ef634547 100644 --- a/web/app/components/workflow/store/__tests__/trigger-status.test.ts +++ b/web/app/components/workflow/store/__tests__/trigger-status.test.ts @@ -1,6 +1,6 @@ +import type { EntryNodeStatus } from '../trigger-status' import { act, renderHook } from '@testing-library/react' import { useTriggerStatusStore } from '../trigger-status' -import type { EntryNodeStatus } from '../trigger-status' describe('useTriggerStatusStore', () => { beforeEach(() => { diff --git a/web/app/components/workflow/store/index.ts b/web/app/components/workflow/store/index.ts index 5ca06d2ec3..1d22a5837f 100644 --- a/web/app/components/workflow/store/index.ts +++ b/web/app/components/workflow/store/index.ts @@ -1,2 +1,2 @@ -export * from './workflow' export * from './trigger-status' +export * from './workflow' diff --git a/web/app/components/workflow/store/workflow/debug/inspect-vars-slice.ts b/web/app/components/workflow/store/workflow/debug/inspect-vars-slice.ts index 7d9797630d..cb1998befd 100644 --- a/web/app/components/workflow/store/workflow/debug/inspect-vars-slice.ts +++ b/web/app/components/workflow/store/workflow/debug/inspect-vars-slice.ts @@ -1,7 +1,7 @@ import type { StateCreator } from 'zustand' -import { produce } from 'immer' -import type { NodeWithVar, VarInInspect } from '@/types/workflow' import type { ValueSelector } from '../../../types' +import type { NodeWithVar, VarInInspect } from '@/types/workflow' +import { produce } from 'immer' type InspectVarsState = { currentFocusNodeId: string | null @@ -79,8 +79,7 @@ export const createInspectVarsSlice: StateCreator<InspectVarsSliceShape> = (set) return targetVar.value = value targetVar.edited = true - }, - ) + }) return { nodesWithInspectVars: nodes, } @@ -97,8 +96,7 @@ export const createInspectVarsSlice: StateCreator<InspectVarsSliceShape> = (set) return targetVar.value = value targetVar.edited = false - }, - ) + }) return { nodesWithInspectVars: nodes, } @@ -115,8 +113,7 @@ export const createInspectVarsSlice: StateCreator<InspectVarsSliceShape> = (set) return targetVar.name = selector[1] targetVar.selector = selector - }, - ) + }) return { nodesWithInspectVars: nodes, } @@ -131,8 +128,7 @@ export const createInspectVarsSlice: StateCreator<InspectVarsSliceShape> = (set) const needChangeVarIndex = targetNode.vars.findIndex(varItem => varItem.id === varId) if (needChangeVarIndex !== -1) targetNode.vars.splice(needChangeVarIndex, 1) - }, - ) + }) return { nodesWithInspectVars: nodes, } diff --git a/web/app/components/workflow/store/workflow/index.ts b/web/app/components/workflow/store/workflow/index.ts index da857fd0f2..c2c0c00201 100644 --- a/web/app/components/workflow/store/workflow/index.ts +++ b/web/app/components/workflow/store/workflow/index.ts @@ -1,61 +1,61 @@ -import { useContext } from 'react' import type { StateCreator, } from 'zustand' +import type { ChatVariableSliceShape } from './chat-variable-slice' +import type { InspectVarsSliceShape } from './debug/inspect-vars-slice' +import type { EnvVariableSliceShape } from './env-variable-slice' +import type { FormSliceShape } from './form-slice' +import type { HelpLineSliceShape } from './help-line-slice' +import type { HistorySliceShape } from './history-slice' +import type { LayoutSliceShape } from './layout-slice' +import type { NodeSliceShape } from './node-slice' +import type { PanelSliceShape } from './panel-slice' +import type { ToolSliceShape } from './tool-slice' +import type { VersionSliceShape } from './version-slice' +import type { WorkflowDraftSliceShape } from './workflow-draft-slice' +import type { WorkflowSliceShape } from './workflow-slice' +import type { RagPipelineSliceShape } from '@/app/components/rag-pipeline/store' +import type { WorkflowSliceShape as WorkflowAppSliceShape } from '@/app/components/workflow-app/store/workflow/workflow-slice' +import { useContext } from 'react' import { useStore as useZustandStore, } from 'zustand' import { createStore } from 'zustand/vanilla' -import type { ChatVariableSliceShape } from './chat-variable-slice' -import { createChatVariableSlice } from './chat-variable-slice' -import type { EnvVariableSliceShape } from './env-variable-slice' -import { createEnvVariableSlice } from './env-variable-slice' -import type { FormSliceShape } from './form-slice' -import { createFormSlice } from './form-slice' -import type { HelpLineSliceShape } from './help-line-slice' -import { createHelpLineSlice } from './help-line-slice' -import type { HistorySliceShape } from './history-slice' -import { createHistorySlice } from './history-slice' -import type { NodeSliceShape } from './node-slice' -import { createNodeSlice } from './node-slice' -import type { PanelSliceShape } from './panel-slice' -import { createPanelSlice } from './panel-slice' -import type { ToolSliceShape } from './tool-slice' -import { createToolSlice } from './tool-slice' -import type { VersionSliceShape } from './version-slice' -import { createVersionSlice } from './version-slice' -import type { WorkflowDraftSliceShape } from './workflow-draft-slice' -import { createWorkflowDraftSlice } from './workflow-draft-slice' -import type { WorkflowSliceShape } from './workflow-slice' -import { createWorkflowSlice } from './workflow-slice' -import type { InspectVarsSliceShape } from './debug/inspect-vars-slice' -import { createInspectVarsSlice } from './debug/inspect-vars-slice' - import { WorkflowContext } from '@/app/components/workflow/context' -import type { LayoutSliceShape } from './layout-slice' +import { createChatVariableSlice } from './chat-variable-slice' +import { createInspectVarsSlice } from './debug/inspect-vars-slice' +import { createEnvVariableSlice } from './env-variable-slice' +import { createFormSlice } from './form-slice' +import { createHelpLineSlice } from './help-line-slice' +import { createHistorySlice } from './history-slice' import { createLayoutSlice } from './layout-slice' -import type { WorkflowSliceShape as WorkflowAppSliceShape } from '@/app/components/workflow-app/store/workflow/workflow-slice' -import type { RagPipelineSliceShape } from '@/app/components/rag-pipeline/store' +import { createNodeSlice } from './node-slice' + +import { createPanelSlice } from './panel-slice' +import { createToolSlice } from './tool-slice' +import { createVersionSlice } from './version-slice' +import { createWorkflowDraftSlice } from './workflow-draft-slice' +import { createWorkflowSlice } from './workflow-slice' export type SliceFromInjection = Partial<WorkflowAppSliceShape> - & Partial<RagPipelineSliceShape> + & Partial<RagPipelineSliceShape> export type Shape = ChatVariableSliceShape - & EnvVariableSliceShape - & FormSliceShape - & HelpLineSliceShape - & HistorySliceShape - & NodeSliceShape - & PanelSliceShape - & ToolSliceShape - & VersionSliceShape - & WorkflowDraftSliceShape - & WorkflowSliceShape - & InspectVarsSliceShape - & LayoutSliceShape - & SliceFromInjection + & EnvVariableSliceShape + & FormSliceShape + & HelpLineSliceShape + & HistorySliceShape + & NodeSliceShape + & PanelSliceShape + & ToolSliceShape + & VersionSliceShape + & WorkflowDraftSliceShape + & WorkflowSliceShape + & InspectVarsSliceShape + & LayoutSliceShape + & SliceFromInjection export type InjectWorkflowStoreSliceFn = StateCreator<SliceFromInjection> diff --git a/web/app/components/workflow/store/workflow/node-slice.ts b/web/app/components/workflow/store/workflow/node-slice.ts index 3463fdee57..b90f23e925 100644 --- a/web/app/components/workflow/store/workflow/node-slice.ts +++ b/web/app/components/workflow/store/workflow/node-slice.ts @@ -1,10 +1,10 @@ import type { StateCreator } from 'zustand' -import type { - Node, -} from '@/app/components/workflow/types' import type { VariableAssignerNodeType, } from '@/app/components/workflow/nodes/variable-assigner/types' +import type { + Node, +} from '@/app/components/workflow/types' import type { NodeTracing, } from '@/types/workflow' @@ -35,7 +35,7 @@ export type NodeSliceShape = { setShowAssignVariablePopup: (showAssignVariablePopup: NodeSliceShape['showAssignVariablePopup']) => void hoveringAssignVariableGroupId?: string setHoveringAssignVariableGroupId: (hoveringAssignVariableGroupId?: string) => void - connectingNodePayload?: { nodeId: string; nodeType: string; handleType: string; handleId: string | null } + connectingNodePayload?: { nodeId: string, nodeType: string, handleType: string, handleId: string | null } setConnectingNodePayload: (startConnectingPayload?: NodeSliceShape['connectingNodePayload']) => void enteringNodePayload?: { nodeId: string diff --git a/web/app/components/workflow/store/workflow/workflow-draft-slice.ts b/web/app/components/workflow/store/workflow/workflow-draft-slice.ts index f20a9453e9..6c08c50e4a 100644 --- a/web/app/components/workflow/store/workflow/workflow-draft-slice.ts +++ b/web/app/components/workflow/store/workflow/workflow-draft-slice.ts @@ -1,11 +1,11 @@ -import type { StateCreator } from 'zustand' -import { debounce } from 'lodash-es' import type { Viewport } from 'reactflow' +import type { StateCreator } from 'zustand' import type { Edge, EnvironmentVariable, Node, } from '@/app/components/workflow/types' +import { debounce } from 'lodash-es' export type WorkflowDraftSliceShape = { backupDraft?: { diff --git a/web/app/components/workflow/store/workflow/workflow-slice.ts b/web/app/components/workflow/store/workflow/workflow-slice.ts index 35eeff07a7..df24058975 100644 --- a/web/app/components/workflow/store/workflow/workflow-slice.ts +++ b/web/app/components/workflow/store/workflow/workflow-slice.ts @@ -26,15 +26,15 @@ export type WorkflowSliceShape = { setListeningTriggerIsAll: (isAll: boolean) => void clipboardElements: Node[] setClipboardElements: (clipboardElements: Node[]) => void - selection: null | { x1: number; y1: number; x2: number; y2: number } + selection: null | { x1: number, y1: number, x2: number, y2: number } setSelection: (selection: WorkflowSliceShape['selection']) => void - bundleNodeSize: { width: number; height: number } | null + bundleNodeSize: { width: number, height: number } | null setBundleNodeSize: (bundleNodeSize: WorkflowSliceShape['bundleNodeSize']) => void controlMode: 'pointer' | 'hand' setControlMode: (controlMode: WorkflowSliceShape['controlMode']) => void - mousePosition: { pageX: number; pageY: number; elementX: number; elementY: number } + mousePosition: { pageX: number, pageY: number, elementX: number, elementY: number } setMousePosition: (mousePosition: WorkflowSliceShape['mousePosition']) => void - showConfirm?: { title: string; desc?: string; onConfirm: () => void } + showConfirm?: { title: string, desc?: string, onConfirm: () => void } setShowConfirm: (showConfirm: WorkflowSliceShape['showConfirm']) => void controlPromptEditorRerenderKey: number setControlPromptEditorRerenderKey: (controlPromptEditorRerenderKey: number) => void diff --git a/web/app/components/workflow/syncing-data-modal.tsx b/web/app/components/workflow/syncing-data-modal.tsx index fe3843d2fc..30c303ffe6 100644 --- a/web/app/components/workflow/syncing-data-modal.tsx +++ b/web/app/components/workflow/syncing-data-modal.tsx @@ -7,7 +7,7 @@ const SyncingDataModal = () => { return null return ( - <div className='absolute inset-0 z-[9999]'> + <div className="absolute inset-0 z-[9999]"> </div> ) } diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index 5ae8d530a8..740f1c1113 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -4,21 +4,20 @@ import type { Viewport, XYPosition, } from 'reactflow' -import type { Resolution, TransferMethod } from '@/types/app' -import type { PluginDefaultValue } from '@/app/components/workflow/block-selector/types' -import type { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' -import type { FileResponse, NodeTracing, PanelProps } from '@/types/workflow' +import type { Plugin, PluginMeta } from '@/app/components/plugins/types' import type { Collection, Tool } from '@/app/components/tools/types' -import type { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type' +import type { BlockClassificationEnum, PluginDefaultValue } from '@/app/components/workflow/block-selector/types' import type { DefaultValueForm, ErrorHandleTypeEnum, } from '@/app/components/workflow/nodes/_base/components/error-handle/types' import type { WorkflowRetryConfig } from '@/app/components/workflow/nodes/_base/components/retry/types' import type { StructuredOutput } from '@/app/components/workflow/nodes/llm/types' -import type { Plugin, PluginMeta } from '@/app/components/plugins/types' -import type { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' +import type { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' +import type { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type' import type { SchemaTypeDefinition } from '@/service/use-common' +import type { Resolution, TransferMethod } from '@/types/app' +import type { FileResponse, NodeTracing, PanelProps } from '@/types/workflow' export enum BlockEnum { Start = 'start', @@ -76,7 +75,7 @@ export type CommonNodeType<T = {}> = { _singleRunningStatus?: NodeRunningStatus _isCandidate?: boolean _isBundled?: boolean - _children?: { nodeId: string; nodeType: BlockEnum }[] + _children?: { nodeId: string, nodeType: BlockEnum }[] _isEntering?: boolean _showAddVariablePopup?: boolean _holdAddVariablePopup?: boolean @@ -122,13 +121,13 @@ export type CommonEdgeType = { isInLoop?: boolean loop_id?: string sourceType: BlockEnum - targetType: BlockEnum, - _isTemp?: boolean, + targetType: BlockEnum + _isTemp?: boolean } export type Node<T = {}> = ReactFlowNode<CommonNodeType<T>> export type SelectedNode = Pick<Node, 'id' | 'data'> -export type NodeProps<T = unknown> = { id: string; data: CommonNodeType<T> } +export type NodeProps<T = unknown> = { id: string, data: CommonNodeType<T> } export type NodePanelProps<T> = { id: string data: CommonNodeType<T> @@ -337,7 +336,7 @@ export type NodeDefault<T = {}> = { } defaultValue: Partial<T> defaultRunInputData?: Record<string, any> - checkValid: (payload: T, t: any, moreDataForCheckValid?: any) => { isValid: boolean; errorMessage?: string } + checkValid: (payload: T, t: any, moreDataForCheckValid?: any) => { isValid: boolean, errorMessage?: string } getOutputVars?: (payload: T, allPluginInfoList: Record<string, ToolWithProvider[]>, ragVariables?: Var[], utils?: { schemaTypeDefinitions?: SchemaTypeDefinition[] }) => Var[] @@ -495,7 +494,7 @@ export enum VersionHistoryContextMenuOptions { } export type ChildNodeTypeCount = { - [key: string]: number; + [key: string]: number } export const TRIGGER_NODE_TYPES = [ diff --git a/web/app/components/workflow/update-dsl-modal.tsx b/web/app/components/workflow/update-dsl-modal.tsx index be2dab7a3d..f5dcce4c9e 100644 --- a/web/app/components/workflow/update-dsl-modal.tsx +++ b/web/app/components/workflow/update-dsl-modal.tsx @@ -1,51 +1,51 @@ 'use client' import type { MouseEventHandler } from 'react' +import type { + CommonNodeType, + Node, +} from './types' +import { + RiAlertFill, + RiCloseLine, + RiFileDownloadLine, +} from '@remixicon/react' +import { load as yamlLoad } from 'js-yaml' import { memo, useCallback, useRef, useState, } from 'react' -import { useContext } from 'use-context-selector' import { useTranslation } from 'react-i18next' -import { load as yamlLoad } from 'js-yaml' +import { useContext } from 'use-context-selector' +import Uploader from '@/app/components/app/create-from-dsl-modal/uploader' +import { useStore as useAppStore } from '@/app/components/app/store' +import Button from '@/app/components/base/button' +import Modal from '@/app/components/base/modal' +import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' +import { ToastContext } from '@/app/components/base/toast' +import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' +import { useEventEmitterContextContext } from '@/context/event-emitter' import { - RiAlertFill, - RiCloseLine, - RiFileDownloadLine, -} from '@remixicon/react' -import { WORKFLOW_DATA_UPDATE } from './constants' -import { - BlockEnum, - SupportUploadFileTypes, -} from './types' -import type { - CommonNodeType, - Node, -} from './types' -import { AppModeEnum } from '@/types/app' -import { - initialEdges, - initialNodes, -} from './utils' + DSLImportMode, + DSLImportStatus, +} from '@/models/app' import { importDSL, importDSLConfirm, } from '@/service/apps' import { fetchWorkflowDraft } from '@/service/workflow' +import { AppModeEnum } from '@/types/app' +import { WORKFLOW_DATA_UPDATE } from './constants' import { - DSLImportMode, - DSLImportStatus, -} from '@/models/app' -import Uploader from '@/app/components/app/create-from-dsl-modal/uploader' -import Button from '@/app/components/base/button' -import Modal from '@/app/components/base/modal' -import { ToastContext } from '@/app/components/base/toast' -import { useEventEmitterContextContext } from '@/context/event-emitter' -import { useStore as useAppStore } from '@/app/components/app/store' -import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' -import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' + BlockEnum, + SupportUploadFileTypes, +} from './types' +import { + initialEdges, + initialNodes, +} from './utils' type UpdateDSLModalProps = { onCancel: () => void @@ -67,7 +67,7 @@ const UpdateDSLModal = ({ const { eventEmitter } = useEventEmitterContextContext() const [show, setShow] = useState(true) const [showErrorModal, setShowErrorModal] = useState(false) - const [versions, setVersions] = useState<{ importedVersion: string; systemVersion: string }>() + const [versions, setVersions] = useState<{ importedVersion: string, systemVersion: string }>() const [importId, setImportId] = useState<string>() const { handleCheckPluginDependencies } = usePluginDependencies() @@ -143,11 +143,11 @@ const UpdateDSLModal = ({ const nodes = data?.workflow?.graph?.nodes ?? [] const invalidNodes = appDetail?.mode === AppModeEnum.ADVANCED_CHAT ? [ - BlockEnum.End, - BlockEnum.TriggerWebhook, - BlockEnum.TriggerSchedule, - BlockEnum.TriggerPlugin, - ] + BlockEnum.End, + BlockEnum.TriggerWebhook, + BlockEnum.TriggerSchedule, + BlockEnum.TriggerPlugin, + ] : [BlockEnum.Answer] const hasInvalidNode = nodes.some((node: Node<CommonNodeType>) => { return invalidNodes.includes(node?.data?.type) @@ -257,32 +257,32 @@ const UpdateDSLModal = ({ return ( <> <Modal - className='w-[520px] rounded-2xl p-6' + className="w-[520px] rounded-2xl p-6" isShow={show} onClose={onCancel} > - <div className='mb-3 flex items-center justify-between'> - <div className='title-2xl-semi-bold text-text-primary'>{t('workflow.common.importDSL')}</div> - <div className='flex h-[22px] w-[22px] cursor-pointer items-center justify-center' onClick={onCancel}> - <RiCloseLine className='h-[18px] w-[18px] text-text-tertiary' /> + <div className="mb-3 flex items-center justify-between"> + <div className="title-2xl-semi-bold text-text-primary">{t('workflow.common.importDSL')}</div> + <div className="flex h-[22px] w-[22px] cursor-pointer items-center justify-center" onClick={onCancel}> + <RiCloseLine className="h-[18px] w-[18px] text-text-tertiary" /> </div> </div> - <div className='relative mb-2 flex grow gap-0.5 overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-2 shadow-xs'> - <div className='absolute left-0 top-0 h-full w-full bg-toast-warning-bg opacity-40' /> - <div className='flex items-start justify-center p-1'> - <RiAlertFill className='h-4 w-4 shrink-0 text-text-warning-secondary' /> + <div className="relative mb-2 flex grow gap-0.5 overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-2 shadow-xs"> + <div className="absolute left-0 top-0 h-full w-full bg-toast-warning-bg opacity-40" /> + <div className="flex items-start justify-center p-1"> + <RiAlertFill className="h-4 w-4 shrink-0 text-text-warning-secondary" /> </div> - <div className='flex grow flex-col items-start gap-0.5 py-1'> - <div className='system-xs-medium whitespace-pre-line text-text-primary'>{t('workflow.common.importDSLTip')}</div> - <div className='flex items-start gap-1 self-stretch pb-0.5 pt-1'> + <div className="flex grow flex-col items-start gap-0.5 py-1"> + <div className="system-xs-medium whitespace-pre-line text-text-primary">{t('workflow.common.importDSLTip')}</div> + <div className="flex items-start gap-1 self-stretch pb-0.5 pt-1"> <Button - size='small' - variant='secondary' - className='z-[1000]' + size="small" + variant="secondary" + className="z-[1000]" onClick={onBackup} > - <RiFileDownloadLine className='h-3.5 w-3.5 text-components-button-secondary-text' /> - <div className='flex items-center justify-center gap-1 px-[3px]'> + <RiFileDownloadLine className="h-3.5 w-3.5 text-components-button-secondary-text" /> + <div className="flex items-center justify-center gap-1 px-[3px]"> {t('workflow.common.backupCurrentDraft')} </div> </Button> @@ -290,22 +290,22 @@ const UpdateDSLModal = ({ </div> </div> <div> - <div className='system-md-semibold pt-2 text-text-primary'> + <div className="system-md-semibold pt-2 text-text-primary"> {t('workflow.common.chooseDSL')} </div> - <div className='flex w-full flex-col items-start justify-center gap-4 self-stretch py-4'> + <div className="flex w-full flex-col items-start justify-center gap-4 self-stretch py-4"> <Uploader file={currentFile} updateFile={handleFile} - className='!mt-0 w-full' + className="!mt-0 w-full" /> </div> </div> - <div className='flex items-center justify-end gap-2 self-stretch pt-5'> + <div className="flex items-center justify-end gap-2 self-stretch pt-5"> <Button onClick={onCancel}>{t('app.newApp.Cancel')}</Button> <Button disabled={!currentFile || loading} - variant='warning' + variant="warning" onClick={handleImport} loading={loading} > @@ -316,21 +316,27 @@ const UpdateDSLModal = ({ <Modal isShow={showErrorModal} onClose={() => setShowErrorModal(false)} - className='w-[480px]' + className="w-[480px]" > - <div className='flex flex-col items-start gap-2 self-stretch pb-4'> - <div className='title-2xl-semi-bold text-text-primary'>{t('app.newApp.appCreateDSLErrorTitle')}</div> - <div className='system-md-regular flex grow flex-col text-text-secondary'> + <div className="flex flex-col items-start gap-2 self-stretch pb-4"> + <div className="title-2xl-semi-bold text-text-primary">{t('app.newApp.appCreateDSLErrorTitle')}</div> + <div className="system-md-regular flex grow flex-col text-text-secondary"> <div>{t('app.newApp.appCreateDSLErrorPart1')}</div> <div>{t('app.newApp.appCreateDSLErrorPart2')}</div> <br /> - <div>{t('app.newApp.appCreateDSLErrorPart3')}<span className='system-md-medium'>{versions?.importedVersion}</span></div> - <div>{t('app.newApp.appCreateDSLErrorPart4')}<span className='system-md-medium'>{versions?.systemVersion}</span></div> + <div> + {t('app.newApp.appCreateDSLErrorPart3')} + <span className="system-md-medium">{versions?.importedVersion}</span> + </div> + <div> + {t('app.newApp.appCreateDSLErrorPart4')} + <span className="system-md-medium">{versions?.systemVersion}</span> + </div> </div> </div> - <div className='flex items-start justify-end gap-2 self-stretch pt-6'> - <Button variant='secondary' onClick={() => setShowErrorModal(false)}>{t('app.newApp.Cancel')}</Button> - <Button variant='primary' destructive onClick={onUpdateDSLConfirm}>{t('app.newApp.Confirm')}</Button> + <div className="flex items-start justify-end gap-2 self-stretch pt-6"> + <Button variant="secondary" onClick={() => setShowErrorModal(false)}>{t('app.newApp.Cancel')}</Button> + <Button variant="primary" destructive onClick={onUpdateDSLConfirm}>{t('app.newApp.Confirm')}</Button> </div> </Modal> </> diff --git a/web/app/components/workflow/utils/data-source.ts b/web/app/components/workflow/utils/data-source.ts index 5b2db5437d..3b349e034e 100644 --- a/web/app/components/workflow/utils/data-source.ts +++ b/web/app/components/workflow/utils/data-source.ts @@ -1,8 +1,8 @@ +import type { DataSourceNodeType } from '../nodes/data-source/types' import type { InputVar, ToolWithProvider, } from '../types' -import type { DataSourceNodeType } from '../nodes/data-source/types' import { CollectionType } from '@/app/components/tools/types' import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema' diff --git a/web/app/components/workflow/utils/elk-layout.ts b/web/app/components/workflow/utils/elk-layout.ts index 69acbf9aff..05f81872bb 100644 --- a/web/app/components/workflow/utils/elk-layout.ts +++ b/web/app/components/workflow/utils/elk-layout.ts @@ -1,13 +1,11 @@ -import ELK from 'elkjs/lib/elk.bundled.js' import type { ElkNode, LayoutOptions } from 'elkjs/lib/elk-api' -import { cloneDeep } from 'lodash-es' +import type { CaseItem, IfElseNodeType } from '@/app/components/workflow/nodes/if-else/types' import type { Edge, Node, } from '@/app/components/workflow/types' -import { - BlockEnum, -} from '@/app/components/workflow/types' +import ELK from 'elkjs/lib/elk.bundled.js' +import { cloneDeep } from 'lodash-es' import { CUSTOM_NODE, NODE_LAYOUT_HORIZONTAL_PADDING, @@ -15,7 +13,9 @@ import { } from '@/app/components/workflow/constants' import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants' import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants' -import type { CaseItem, IfElseNodeType } from '@/app/components/workflow/nodes/if-else/types' +import { + BlockEnum, +} from '@/app/components/workflow/types' // Although the file name refers to Dagre, the implementation now relies on ELK's layered algorithm. // Keep the export signatures unchanged to minimise the blast radius while we migrate the layout stack. @@ -284,7 +284,7 @@ const collectLayout = (graph: ElkNode, predicate: (id: string) => boolean): Layo const buildIfElseWithPorts = ( ifElseNode: Node, edges: Edge[], -): { node: ElkNodeShape; portMap: Map<string, string> } | null => { +): { node: ElkNodeShape, portMap: Map<string, string> } | null => { const childEdges = edges.filter(edge => edge.source === ifElseNode.id) if (childEdges.length <= 1) diff --git a/web/app/components/workflow/utils/gen-node-meta-data.ts b/web/app/components/workflow/utils/gen-node-meta-data.ts index 9c4e9cabca..f45bfcb018 100644 --- a/web/app/components/workflow/utils/gen-node-meta-data.ts +++ b/web/app/components/workflow/utils/gen-node-meta-data.ts @@ -1,5 +1,5 @@ -import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' import type { BlockEnum } from '@/app/components/workflow/types' +import { BlockClassificationEnum } from '@/app/components/workflow/block-selector/types' export type GenNodeMetaDataParams = { classification?: BlockClassificationEnum diff --git a/web/app/components/workflow/utils/index.ts b/web/app/components/workflow/utils/index.ts index 53a423de34..715ce081a3 100644 --- a/web/app/components/workflow/utils/index.ts +++ b/web/app/components/workflow/utils/index.ts @@ -1,10 +1,10 @@ -export * from './node' -export * from './edge' -export * from './workflow-init' -export * from './elk-layout' export * from './common' -export * from './tool' -export * from './workflow' -export * from './variable' -export * from './gen-node-meta-data' export * from './data-source' +export * from './edge' +export * from './elk-layout' +export * from './gen-node-meta-data' +export * from './node' +export * from './tool' +export * from './variable' +export * from './workflow' +export * from './workflow-init' diff --git a/web/app/components/workflow/utils/node-navigation.ts b/web/app/components/workflow/utils/node-navigation.ts index 57106ae6ee..4294a113ab 100644 --- a/web/app/components/workflow/utils/node-navigation.ts +++ b/web/app/components/workflow/utils/node-navigation.ts @@ -7,8 +7,8 @@ * Interface for node selection event detail */ export type NodeSelectionDetail = { - nodeId: string; - focus?: boolean; + nodeId: string + focus?: boolean } /** diff --git a/web/app/components/workflow/utils/node.ts b/web/app/components/workflow/utils/node.ts index 97ca7553e8..b26e350b2a 100644 --- a/web/app/components/workflow/utils/node.ts +++ b/web/app/components/workflow/utils/node.ts @@ -1,12 +1,12 @@ -import { - Position, -} from 'reactflow' +import type { IterationNodeType } from '../nodes/iteration/types' +import type { LoopNodeType } from '../nodes/loop/types' import type { Node, } from '../types' import { - BlockEnum, -} from '../types' + Position, +} from 'reactflow' +import { CUSTOM_SIMPLE_NODE } from '@/app/components/workflow/simple-node/constants' import { CUSTOM_NODE, ITERATION_CHILDREN_Z_INDEX, @@ -16,9 +16,9 @@ import { } from '../constants' import { CUSTOM_ITERATION_START_NODE } from '../nodes/iteration-start/constants' import { CUSTOM_LOOP_START_NODE } from '../nodes/loop-start/constants' -import type { IterationNodeType } from '../nodes/iteration/types' -import type { LoopNodeType } from '../nodes/loop/types' -import { CUSTOM_SIMPLE_NODE } from '@/app/components/workflow/simple-node/constants' +import { + BlockEnum, +} from '../types' export function generateNewNode({ data, position, id, zIndex, type, ...rest }: Omit<Node, 'id'> & { id?: string }): { newNode: Node diff --git a/web/app/components/workflow/utils/tool.ts b/web/app/components/workflow/utils/tool.ts index 1c3b792d9e..6740cf9be4 100644 --- a/web/app/components/workflow/utils/tool.ts +++ b/web/app/components/workflow/utils/tool.ts @@ -1,13 +1,13 @@ +import type { ToolNodeType } from '../nodes/tool/types' import type { InputVar, ToolWithProvider, } from '../types' -import type { ToolNodeType } from '../nodes/tool/types' +import type { StructuredOutput } from '@/app/components/workflow/nodes/llm/types' import { CollectionType } from '@/app/components/tools/types' import { toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema' -import { canFindTool } from '@/utils' -import type { StructuredOutput } from '@/app/components/workflow/nodes/llm/types' import { Type } from '@/app/components/workflow/nodes/llm/types' +import { canFindTool } from '@/utils' export const getToolCheckParams = ( toolData: ToolNodeType, diff --git a/web/app/components/workflow/utils/variable.ts b/web/app/components/workflow/utils/variable.ts index f73f92e371..8c40a469d1 100644 --- a/web/app/components/workflow/utils/variable.ts +++ b/web/app/components/workflow/utils/variable.ts @@ -1,14 +1,12 @@ -import type { - ValueSelector, -} from '../types' import type { BlockEnum, + ValueSelector, } from '../types' import { hasErrorHandleNode } from '.' export const variableTransformer = (v: ValueSelector | string) => { if (typeof v === 'string') - return v.replace(/^{{#|#}}$/g, '').split('.') + return v.replace(/^\{\{#|#\}\}$/g, '').split('.') return `{{#${v.join('.')}#}}` } diff --git a/web/app/components/workflow/utils/workflow-entry.ts b/web/app/components/workflow/utils/workflow-entry.ts index 724a68a85b..bb24fa1466 100644 --- a/web/app/components/workflow/utils/workflow-entry.ts +++ b/web/app/components/workflow/utils/workflow-entry.ts @@ -1,4 +1,5 @@ -import { BlockEnum, type Node, isTriggerNode } from '../types' +import type { Node } from '../types' +import { BlockEnum, isTriggerNode } from '../types' /** * Get the workflow entry node @@ -6,7 +7,8 @@ import { BlockEnum, type Node, isTriggerNode } from '../types' */ export function getWorkflowEntryNode(nodes: Node[]): Node | undefined { const triggerNode = nodes.find(node => isTriggerNode(node.data.type)) - if (triggerNode) return triggerNode + if (triggerNode) + return triggerNode return nodes.find(node => node.data.type === BlockEnum.Start) } diff --git a/web/app/components/workflow/utils/workflow-init.spec.ts b/web/app/components/workflow/utils/workflow-init.spec.ts index 8b7bdfaa92..8dfcbeb30d 100644 --- a/web/app/components/workflow/utils/workflow-init.spec.ts +++ b/web/app/components/workflow/utils/workflow-init.spec.ts @@ -1,9 +1,9 @@ -import { preprocessNodesAndEdges } from './workflow-init' -import { BlockEnum } from '@/app/components/workflow/types' import type { Node, } from '@/app/components/workflow/types' import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants' +import { BlockEnum } from '@/app/components/workflow/types' +import { preprocessNodesAndEdges } from './workflow-init' describe('preprocessNodesAndEdges', () => { it('process nodes without iteration node or loop node should return origin nodes and edges.', () => { diff --git a/web/app/components/workflow/utils/workflow-init.ts b/web/app/components/workflow/utils/workflow-init.ts index 08d0d82e79..18ba643d30 100644 --- a/web/app/components/workflow/utils/workflow-init.ts +++ b/web/app/components/workflow/utils/workflow-init.ts @@ -1,17 +1,23 @@ -import { - getConnectedEdges, -} from 'reactflow' -import { - cloneDeep, -} from 'lodash-es' +import type { IfElseNodeType } from '../nodes/if-else/types' +import type { IterationNodeType } from '../nodes/iteration/types' +import type { LoopNodeType } from '../nodes/loop/types' +import type { QuestionClassifierNodeType } from '../nodes/question-classifier/types' +import type { ToolNodeType } from '../nodes/tool/types' import type { Edge, Node, } from '../types' import { - BlockEnum, - ErrorHandleMode, -} from '../types' + cloneDeep, +} from 'lodash-es' +import { + getConnectedEdges, +} from 'reactflow' +import { correctModelProvider } from '@/utils' +import { + getIterationStartNode, + getLoopStartNode, +} from '.' import { CUSTOM_NODE, DEFAULT_RETRY_INTERVAL, @@ -21,19 +27,13 @@ import { NODE_WIDTH_X_OFFSET, START_INITIAL_POSITION, } from '../constants' +import { branchNameCorrect } from '../nodes/if-else/utils' import { CUSTOM_ITERATION_START_NODE } from '../nodes/iteration-start/constants' import { CUSTOM_LOOP_START_NODE } from '../nodes/loop-start/constants' -import type { QuestionClassifierNodeType } from '../nodes/question-classifier/types' -import type { IfElseNodeType } from '../nodes/if-else/types' -import { branchNameCorrect } from '../nodes/if-else/utils' -import type { IterationNodeType } from '../nodes/iteration/types' -import type { LoopNodeType } from '../nodes/loop/types' -import type { ToolNodeType } from '../nodes/tool/types' import { - getIterationStartNode, - getLoopStartNode, -} from '.' -import { correctModelProvider } from '@/utils' + BlockEnum, + ErrorHandleMode, +} from '../types' const WHITE = 'WHITE' const GRAY = 'GRAY' @@ -216,7 +216,7 @@ export const initialNodes = (originNodes: Node[], originEdges: Edge[]) => { acc[node.parentId] = [{ nodeId: node.id, nodeType: node.data.type }] } return acc - }, {} as Record<string, { nodeId: string; nodeType: BlockEnum }[]>) + }, {} as Record<string, { nodeId: string, nodeType: BlockEnum }[]>) return nodes.map((node) => { if (!node.type) diff --git a/web/app/components/workflow/utils/workflow.ts b/web/app/components/workflow/utils/workflow.ts index 14b1eb87d5..43fbd687c1 100644 --- a/web/app/components/workflow/utils/workflow.ts +++ b/web/app/components/workflow/utils/workflow.ts @@ -1,21 +1,21 @@ -import { - getOutgoers, -} from 'reactflow' -import { v4 as uuid4 } from 'uuid' -import { - uniqBy, -} from 'lodash-es' import type { Edge, Node, } from '../types' +import { + uniqBy, +} from 'lodash-es' +import { + getOutgoers, +} from 'reactflow' +import { v4 as uuid4 } from 'uuid' import { BlockEnum, } from '../types' export const canRunBySingle = (nodeType: BlockEnum, isChildNode: boolean) => { // child node means in iteration or loop. Set value to iteration(or loop) may cause variable not exit problem in backend. - if(isChildNode && nodeType === BlockEnum.Assigner) + if (isChildNode && nodeType === BlockEnum.Assigner) return false return nodeType === BlockEnum.LLM || nodeType === BlockEnum.KnowledgeRetrieval diff --git a/web/app/components/workflow/variable-inspect/display-content.tsx b/web/app/components/workflow/variable-inspect/display-content.tsx index 249f948719..901c0fa6dc 100644 --- a/web/app/components/workflow/variable-inspect/display-content.tsx +++ b/web/app/components/workflow/variable-inspect/display-content.tsx @@ -1,17 +1,17 @@ -import React, { useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { RiBracesLine, RiEyeLine } from '@remixicon/react' -import Textarea from '@/app/components/base/textarea' -import { Markdown } from '@/app/components/base/markdown' -import SchemaEditor from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor' -import { SegmentedControl } from '@/app/components/base/segmented-control' -import { cn } from '@/utils/classnames' -import { ChunkCardList } from '@/app/components/rag-pipeline/components/chunk-card-list' +import type { VarType } from '../types' import type { ChunkInfo } from '@/app/components/rag-pipeline/components/chunk-card-list/types' import type { ParentMode } from '@/models/datasets' +import { RiBracesLine, RiEyeLine } from '@remixicon/react' +import React, { useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { Markdown } from '@/app/components/base/markdown' +import { SegmentedControl } from '@/app/components/base/segmented-control' +import Textarea from '@/app/components/base/textarea' +import { ChunkCardList } from '@/app/components/rag-pipeline/components/chunk-card-list' +import SchemaEditor from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor' import { ChunkingMode } from '@/models/datasets' +import { cn } from '@/utils/classnames' import { PreviewType, ViewMode } from './types' -import type { VarType } from '../types' type DisplayContentProps = { previewType: PreviewType @@ -52,15 +52,16 @@ const DisplayContent = (props: DisplayContentProps) => { return ( <div className={cn('flex h-full flex-col rounded-[10px] bg-components-input-bg-normal', isFocused && 'bg-components-input-bg-active outline outline-1 outline-components-input-border-active', className)}> - <div className='flex shrink-0 items-center justify-end p-1'> + <div className="flex shrink-0 items-center justify-end p-1"> {previewType === PreviewType.Markdown && ( - <div className='system-xs-semibold-uppercase flex grow items-center px-2 py-0.5 text-text-secondary'> + <div className="system-xs-semibold-uppercase flex grow items-center px-2 py-0.5 text-text-secondary"> {previewType.toUpperCase()} </div> )} {previewType === PreviewType.Chunks && ( - <div className='system-xs-semibold-uppercase flex grow items-center px-2 py-0.5 text-text-secondary'> - {varType.toUpperCase()}{schemaType ? `(${schemaType})` : ''} + <div className="system-xs-semibold-uppercase flex grow items-center px-2 py-0.5 text-text-secondary"> + {varType.toUpperCase()} + {schemaType ? `(${schemaType})` : ''} </div> )} <SegmentedControl @@ -70,43 +71,49 @@ const DisplayContent = (props: DisplayContentProps) => { ]} value={viewMode} onChange={setViewMode} - size='small' - padding='with' - activeClassName='!text-text-accent-light-mode-only' - btnClassName='!pl-1.5 !pr-0.5 gap-[3px]' - className='shrink-0' + size="small" + padding="with" + activeClassName="!text-text-accent-light-mode-only" + btnClassName="!pl-1.5 !pr-0.5 gap-[3px]" + className="shrink-0" /> </div> - <div className='flex flex-1 overflow-auto rounded-b-[10px] pl-3 pr-1'> + <div className="flex flex-1 overflow-auto rounded-b-[10px] pl-3 pr-1"> {viewMode === ViewMode.Code && ( previewType === PreviewType.Markdown - ? <Textarea - readOnly={readonly} - disabled={readonly} - className='h-full border-none bg-transparent p-0 text-text-secondary hover:bg-transparent focus:bg-transparent focus:shadow-none' - value={mdString as any} - onChange={e => handleTextChange?.(e.target.value)} - onFocus={() => setIsFocused(true)} - onBlur={() => setIsFocused(false)} - /> - : <SchemaEditor - readonly={readonly} - className='overflow-y-auto bg-transparent' - hideTopMenu - schema={jsonString!} - onUpdate={handleEditorChange!} - onFocus={() => setIsFocused(true)} - onBlur={() => setIsFocused(false)} - /> + ? ( + <Textarea + readOnly={readonly} + disabled={readonly} + className="h-full border-none bg-transparent p-0 text-text-secondary hover:bg-transparent focus:bg-transparent focus:shadow-none" + value={mdString as any} + onChange={e => handleTextChange?.(e.target.value)} + onFocus={() => setIsFocused(true)} + onBlur={() => setIsFocused(false)} + /> + ) + : ( + <SchemaEditor + readonly={readonly} + className="overflow-y-auto bg-transparent" + hideTopMenu + schema={jsonString!} + onUpdate={handleEditorChange!} + onFocus={() => setIsFocused(true)} + onBlur={() => setIsFocused(false)} + /> + ) )} {viewMode === ViewMode.Preview && ( previewType === PreviewType.Markdown - ? <Markdown className='grow overflow-auto rounded-lg px-4 py-3' content={(mdString ?? '') as string} /> - : <ChunkCardList - chunkType={chunkType!} - parentMode={parentMode} - chunkInfo={JSON.parse(jsonString!) as ChunkInfo} - /> + ? <Markdown className="grow overflow-auto rounded-lg px-4 py-3" content={(mdString ?? '') as string} /> + : ( + <ChunkCardList + chunkType={chunkType!} + parentMode={parentMode} + chunkInfo={JSON.parse(jsonString!) as ChunkInfo} + /> + ) )} </div> </div> diff --git a/web/app/components/workflow/variable-inspect/empty.tsx b/web/app/components/workflow/variable-inspect/empty.tsx index 38df10f6e3..e4e4b895f1 100644 --- a/web/app/components/workflow/variable-inspect/empty.tsx +++ b/web/app/components/workflow/variable-inspect/empty.tsx @@ -6,18 +6,19 @@ const Empty: FC = () => { const { t } = useTranslation() return ( - <div className='flex h-full flex-col gap-3 rounded-xl bg-background-section p-8'> - <div className='flex h-10 w-10 items-center justify-center rounded-[10px] border-[0.5px] border-components-card-border bg-components-card-bg shadow-lg backdrop-blur-sm'> - <Variable02 className='h-5 w-5 text-text-accent' /> + <div className="flex h-full flex-col gap-3 rounded-xl bg-background-section p-8"> + <div className="flex h-10 w-10 items-center justify-center rounded-[10px] border-[0.5px] border-components-card-border bg-components-card-bg shadow-lg backdrop-blur-sm"> + <Variable02 className="h-5 w-5 text-text-accent" /> </div> - <div className='flex flex-col gap-1'> - <div className='system-sm-semibold text-text-secondary'>{t('workflow.debug.variableInspect.title')}</div> - <div className='system-xs-regular text-text-tertiary'>{t('workflow.debug.variableInspect.emptyTip')}</div> + <div className="flex flex-col gap-1"> + <div className="system-sm-semibold text-text-secondary">{t('workflow.debug.variableInspect.title')}</div> + <div className="system-xs-regular text-text-tertiary">{t('workflow.debug.variableInspect.emptyTip')}</div> <a - className='system-xs-regular cursor-pointer text-text-accent' - href='https://docs.dify.ai/en/guides/workflow/debug-and-preview/variable-inspect' - target='_blank' - rel='noopener noreferrer'> + className="system-xs-regular cursor-pointer text-text-accent" + href="https://docs.dify.ai/en/guides/workflow/debug-and-preview/variable-inspect" + target="_blank" + rel="noopener noreferrer" + > {t('workflow.debug.variableInspect.emptyLink')} </a> </div> diff --git a/web/app/components/workflow/variable-inspect/group.tsx b/web/app/components/workflow/variable-inspect/group.tsx index 0887a3823d..bcee333e28 100644 --- a/web/app/components/workflow/variable-inspect/group.tsx +++ b/web/app/components/workflow/variable-inspect/group.tsx @@ -1,5 +1,5 @@ -import { useState } from 'react' -import { useTranslation } from 'react-i18next' +import type { currentVarType } from './panel' +import type { NodeWithVar, VarInInspect } from '@/types/workflow' import { RiArrowRightSLine, RiDeleteBinLine, @@ -7,16 +7,16 @@ import { RiLoader2Line, // RiErrorWarningFill, } from '@remixicon/react' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' // import Button from '@/app/components/base/button' import ActionButton from '@/app/components/base/action-button' import Tooltip from '@/app/components/base/tooltip' import BlockIcon from '@/app/components/workflow/block-icon' -import type { currentVarType } from './panel' +import { VariableIconWithColor } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' import { VarInInspectType } from '@/types/workflow' -import type { NodeWithVar, VarInInspect } from '@/types/workflow' import { cn } from '@/utils/classnames' import { useToolIcon } from '../hooks' -import { VariableIconWithColor } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' type Props = { nodeData?: NodeWithVar @@ -86,7 +86,8 @@ const Group = ({ }) return } - if (!nodeData) return + if (!nodeData) + return handleSelect({ nodeId: nodeData.nodeId, nodeType: nodeData.nodeType, @@ -96,31 +97,31 @@ const Group = ({ } return ( - <div className='p-0.5'> + <div className="p-0.5"> {/* node item */} - <div className='group flex h-6 items-center gap-0.5'> - <div className='h-3 w-3 shrink-0'> + <div className="group flex h-6 items-center gap-0.5"> + <div className="h-3 w-3 shrink-0"> {nodeData?.isSingRunRunning && ( - <RiLoader2Line className='h-3 w-3 animate-spin text-text-accent' /> + <RiLoader2Line className="h-3 w-3 animate-spin text-text-accent" /> )} {(!nodeData || !nodeData.isSingRunRunning) && visibleVarList.length > 0 && ( <RiArrowRightSLine className={cn('h-3 w-3 text-text-tertiary', !isCollapsed && 'rotate-90')} onClick={() => setIsCollapsed(!isCollapsed)} /> )} </div> - <div className='flex grow cursor-pointer items-center gap-1' onClick={() => setIsCollapsed(!isCollapsed)}> + <div className="flex grow cursor-pointer items-center gap-1" onClick={() => setIsCollapsed(!isCollapsed)}> {nodeData && ( <> <BlockIcon - className='shrink-0' + className="shrink-0" type={nodeData.nodeType} toolIcon={toolIcon || ''} - size='xs' + size="xs" /> - <div className='system-xs-medium-uppercase truncate text-text-tertiary'>{nodeData.title}</div> + <div className="system-xs-medium-uppercase truncate text-text-tertiary">{nodeData.title}</div> </> )} {!nodeData && ( - <div className='system-xs-medium-uppercase truncate text-text-tertiary'> + <div className="system-xs-medium-uppercase truncate text-text-tertiary"> {isEnv && t('workflow.debug.variableInspect.envNode')} {isChatVar && t('workflow.debug.variableInspect.chatNode')} {isSystem && t('workflow.debug.variableInspect.systemNode')} @@ -128,15 +129,15 @@ const Group = ({ )} </div> {nodeData && !nodeData.isSingRunRunning && ( - <div className='hidden shrink-0 items-center group-hover:flex'> + <div className="hidden shrink-0 items-center group-hover:flex"> <Tooltip popupContent={t('workflow.debug.variableInspect.view')}> <ActionButton onClick={handleView}> - <RiFileList3Line className='h-4 w-4' /> + <RiFileList3Line className="h-4 w-4" /> </ActionButton> </Tooltip> <Tooltip popupContent={t('workflow.debug.variableInspect.clearNode')}> <ActionButton onClick={handleClear}> - <RiDeleteBinLine className='h-4 w-4' /> + <RiDeleteBinLine className="h-4 w-4" /> </ActionButton> </Tooltip> </div> @@ -144,7 +145,7 @@ const Group = ({ </div> {/* var item list */} {!isCollapsed && !nodeData?.isSingRunRunning && ( - <div className='px-0.5'> + <div className="px-0.5"> {visibleVarList.length > 0 && visibleVarList.map(varItem => ( <div key={varItem.id} @@ -157,10 +158,10 @@ const Group = ({ <VariableIconWithColor variableCategory={varType} isExceptionVariable={['error_type', 'error_message'].includes(varItem.name)} - className='size-4' + className="size-4" /> - <div className='system-sm-medium grow truncate text-text-secondary'>{varItem.name}</div> - <div className='system-xs-regular shrink-0 text-text-tertiary'>{varItem.value_type}</div> + <div className="system-sm-medium grow truncate text-text-secondary">{varItem.name}</div> + <div className="system-xs-regular shrink-0 text-text-tertiary">{varItem.value_type}</div> </div> ))} </div> diff --git a/web/app/components/workflow/variable-inspect/index.tsx b/web/app/components/workflow/variable-inspect/index.tsx index 64466ee312..ced7861e00 100644 --- a/web/app/components/workflow/variable-inspect/index.tsx +++ b/web/app/components/workflow/variable-inspect/index.tsx @@ -1,13 +1,13 @@ import type { FC } from 'react' +import { debounce } from 'lodash-es' import { useCallback, useMemo, } from 'react' -import { debounce } from 'lodash-es' -import { useStore } from '../store' -import { useResizePanel } from '../nodes/_base/hooks/use-resize-panel' -import Panel from './panel' import { cn } from '@/utils/classnames' +import { useResizePanel } from '../nodes/_base/hooks/use-resize-panel' +import { useStore } from '../store' +import Panel from './panel' const VariableInspectPanel: FC = () => { const showVariableInspectPanel = useStore(s => s.showVariableInspectPanel) @@ -44,8 +44,9 @@ const VariableInspectPanel: FC = () => { <div className={cn('relative pb-1')}> <div ref={triggerRef} - className='absolute -top-1 left-0 flex h-1 w-full cursor-row-resize resize-y items-center justify-center'> - <div className='h-0.5 w-10 rounded-sm bg-state-base-handle hover:w-full hover:bg-state-accent-solid active:w-full active:bg-state-accent-solid'></div> + className="absolute -top-1 left-0 flex h-1 w-full cursor-row-resize resize-y items-center justify-center" + > + <div className="h-0.5 w-10 rounded-sm bg-state-base-handle hover:w-full hover:bg-state-accent-solid active:w-full active:bg-state-accent-solid"></div> </div> <div ref={containerRef} diff --git a/web/app/components/workflow/variable-inspect/large-data-alert.tsx b/web/app/components/workflow/variable-inspect/large-data-alert.tsx index a6e00bf591..a2750c82e7 100644 --- a/web/app/components/workflow/variable-inspect/large-data-alert.tsx +++ b/web/app/components/workflow/variable-inspect/large-data-alert.tsx @@ -1,9 +1,9 @@ 'use client' -import { RiInformation2Fill } from '@remixicon/react' import type { FC } from 'react' +import { RiInformation2Fill } from '@remixicon/react' import React from 'react' -import { cn } from '@/utils/classnames' import { useTranslation } from 'react-i18next' +import { cn } from '@/utils/classnames' type Props = { textHasNoExport?: boolean @@ -20,12 +20,12 @@ const LargeDataAlert: FC<Props> = ({ const text = textHasNoExport ? t('workflow.debug.variableInspect.largeDataNoExport') : t('workflow.debug.variableInspect.largeData') return ( <div className={cn('flex h-8 items-center justify-between rounded-lg border-[0.5px] border-components-panel-border bg-components-panel-bg-blur px-2 shadow-xs', className)}> - <div className='flex h-full w-0 grow items-center space-x-1'> - <RiInformation2Fill className='size-4 shrink-0 text-text-accent' /> - <div className='system-xs-regular w-0 grow truncate text-text-primary'>{text}</div> + <div className="flex h-full w-0 grow items-center space-x-1"> + <RiInformation2Fill className="size-4 shrink-0 text-text-accent" /> + <div className="system-xs-regular w-0 grow truncate text-text-primary">{text}</div> </div> {downloadUrl && ( - <div className='system-xs-medium-uppercase ml-1 shrink-0 cursor-pointer text-text-accent'>{t('workflow.debug.variableInspect.export')}</div> + <div className="system-xs-medium-uppercase ml-1 shrink-0 cursor-pointer text-text-accent">{t('workflow.debug.variableInspect.export')}</div> )} </div> ) diff --git a/web/app/components/workflow/variable-inspect/left.tsx b/web/app/components/workflow/variable-inspect/left.tsx index b177440b2f..12e0bb8745 100644 --- a/web/app/components/workflow/variable-inspect/left.tsx +++ b/web/app/components/workflow/variable-inspect/left.tsx @@ -1,17 +1,17 @@ +import type { currentVarType } from './panel' + +import type { VarInInspect } from '@/types/workflow' // import { useState } from 'react' import { useTranslation } from 'react-i18next' - -import { useStore } from '../store' import Button from '@/app/components/base/button' +import { VarInInspectType } from '@/types/workflow' +import { cn } from '@/utils/classnames' +import useCurrentVars from '../hooks/use-inspect-vars-crud' +import { useNodesInteractions } from '../hooks/use-nodes-interactions' +import { useStore } from '../store' // import ActionButton from '@/app/components/base/action-button' // import Tooltip from '@/app/components/base/tooltip' import Group from './group' -import useCurrentVars from '../hooks/use-inspect-vars-crud' -import { useNodesInteractions } from '../hooks/use-nodes-interactions' -import type { currentVarType } from './panel' -import type { VarInInspect } from '@/types/workflow' -import { VarInInspectType } from '@/types/workflow' -import { cn } from '@/utils/classnames' type Props = { currentNodeVar?: currentVarType @@ -51,12 +51,12 @@ const Left = ({ return ( <div className={cn('flex h-full flex-col')}> {/* header */} - <div className='flex shrink-0 items-center justify-between gap-1 pl-4 pr-1 pt-2'> - <div className='system-sm-semibold-uppercase truncate text-text-primary'>{t('workflow.debug.variableInspect.title')}</div> - <Button variant='ghost' size='small' className='shrink-0' onClick={handleClearAll}>{t('workflow.debug.variableInspect.clearAll')}</Button> + <div className="flex shrink-0 items-center justify-between gap-1 pl-4 pr-1 pt-2"> + <div className="system-sm-semibold-uppercase truncate text-text-primary">{t('workflow.debug.variableInspect.title')}</div> + <Button variant="ghost" size="small" className="shrink-0" onClick={handleClearAll}>{t('workflow.debug.variableInspect.clearAll')}</Button> </div> {/* content */} - <div className='grow overflow-y-auto py-1'> + <div className="grow overflow-y-auto py-1"> {/* group ENV */} {environmentVariables.length > 0 && ( <Group @@ -86,8 +86,8 @@ const Left = ({ )} {/* divider */} {showDivider && ( - <div className='px-4 py-1'> - <div className='h-px bg-divider-subtle'></div> + <div className="px-4 py-1"> + <div className="h-px bg-divider-subtle"></div> </div> )} {/* group nodes */} diff --git a/web/app/components/workflow/variable-inspect/listening.tsx b/web/app/components/workflow/variable-inspect/listening.tsx index 1f2577f150..8b2c55fcc4 100644 --- a/web/app/components/workflow/variable-inspect/listening.tsx +++ b/web/app/components/workflow/variable-inspect/listening.tsx @@ -1,18 +1,20 @@ -import { type FC, useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { type Node, useStoreApi } from 'reactflow' -import Button from '@/app/components/base/button' -import BlockIcon from '@/app/components/workflow/block-icon' -import { BlockEnum } from '@/app/components/workflow/types' -import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' -import { useStore } from '../store' -import { useGetToolIcon } from '@/app/components/workflow/hooks/use-tool-icon' import type { TFunction } from 'i18next' -import { getNextExecutionTime } from '@/app/components/workflow/nodes/trigger-schedule/utils/execution-time-calculator' +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 Tooltip from '@/app/components/base/tooltip' import copy from 'copy-to-clipboard' +import { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { useStoreApi } from 'reactflow' +import Button from '@/app/components/base/button' +import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' +import Tooltip from '@/app/components/base/tooltip' +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' +import { BlockEnum } from '@/app/components/workflow/types' +import { useStore } from '../store' const resolveListeningDescription = ( message: string | undefined, @@ -32,7 +34,7 @@ const resolveListeningDescription = ( } if (triggerType === BlockEnum.TriggerPlugin) { - const pluginName = (triggerNode?.data as { provider_name?: string; title?: string })?.provider_name + const pluginName = (triggerNode?.data as { provider_name?: string, title?: string })?.provider_name || (triggerNode?.data as { title?: string })?.title || t('workflow.debug.variableInspect.listening.defaultPluginName') return t('workflow.debug.variableInspect.listening.tipPlugin', { pluginName }) @@ -155,8 +157,8 @@ const Listening: FC<ListeningProps> = ({ : resolveListeningDescription(message, triggerNode, triggerType, t) return ( - <div className='flex h-full flex-col gap-4 rounded-xl bg-background-section p-8'> - <div className='flex flex-row flex-wrap items-center gap-3'> + <div className="flex h-full flex-col gap-4 rounded-xl bg-background-section p-8"> + <div className="flex flex-row flex-wrap items-center gap-3"> {iconsToRender.map(icon => ( <BlockIcon key={icon.key} @@ -167,13 +169,13 @@ const Listening: FC<ListeningProps> = ({ /> ))} </div> - <div className='flex flex-col gap-1'> - <div className='system-sm-semibold text-text-secondary'>{t('workflow.debug.variableInspect.listening.title')}</div> - <div className='system-xs-regular whitespace-pre-line text-text-tertiary'>{description}</div> + <div className="flex flex-col gap-1"> + <div className="system-sm-semibold text-text-secondary">{t('workflow.debug.variableInspect.listening.title')}</div> + <div className="system-xs-regular whitespace-pre-line text-text-tertiary">{description}</div> </div> {webhookDebugUrl && ( - <div className='flex items-center gap-2'> - <div className='system-xs-regular shrink-0 whitespace-pre-line text-text-tertiary'> + <div className="flex items-center gap-2"> + <div className="system-xs-regular shrink-0 whitespace-pre-line text-text-tertiary"> {t('workflow.nodes.triggerWebhook.debugUrlTitle')} </div> <Tooltip @@ -186,7 +188,7 @@ const Listening: FC<ListeningProps> = ({ needsDelay={true} > <button - type='button' + type="button" aria-label={t('workflow.nodes.triggerWebhook.debugUrlCopy') || ''} className={`inline-flex items-center rounded-[6px] border border-divider-regular bg-components-badge-white-to-dark px-1.5 py-[2px] font-mono text-[13px] leading-[18px] text-text-secondary transition-colors hover:bg-components-panel-on-panel-item-bg-hover focus:outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-components-panel-border ${debugUrlCopied ? 'bg-components-panel-on-panel-item-bg-hover text-text-primary' : ''}`} onClick={() => { @@ -194,7 +196,7 @@ const Listening: FC<ListeningProps> = ({ setDebugUrlCopied(true) }} > - <span className='whitespace-nowrap text-text-primary'> + <span className="whitespace-nowrap text-text-primary"> {webhookDebugUrl} </span> </button> @@ -203,12 +205,12 @@ const Listening: FC<ListeningProps> = ({ )} <div> <Button - size='medium' - className='px-3' - variant='primary' + size="medium" + className="px-3" + variant="primary" onClick={onStop} > - <StopCircle className='mr-1 size-4' /> + <StopCircle className="mr-1 size-4" /> {t('workflow.debug.variableInspect.listening.stopButton')} </Button> </div> diff --git a/web/app/components/workflow/variable-inspect/panel.tsx b/web/app/components/workflow/variable-inspect/panel.tsx index 047bede826..bccc2ae3e3 100644 --- a/web/app/components/workflow/variable-inspect/panel.tsx +++ b/web/app/components/workflow/variable-inspect/panel.tsx @@ -1,24 +1,24 @@ import type { FC } from 'react' -import { useCallback, useEffect, useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' +import type { NodeProps } from '../types' +import type { VarInInspect } from '@/types/workflow' import { RiCloseLine, } from '@remixicon/react' -import { useStore } from '../store' -import useCurrentVars from '../hooks/use-inspect-vars-crud' -import Empty from './empty' -import Listening from './listening' -import Left from './left' -import Right from './right' +import { useCallback, useEffect, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' -import type { VarInInspect } from '@/types/workflow' -import { VarInInspectType } from '@/types/workflow' - -import { cn } from '@/utils/classnames' -import type { NodeProps } from '../types' -import useMatchSchemaType from '../nodes/_base/components/variable/use-match-schema-type' -import { useEventEmitterContextContext } from '@/context/event-emitter' import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types' +import { useEventEmitterContextContext } from '@/context/event-emitter' +import { VarInInspectType } from '@/types/workflow' +import { cn } from '@/utils/classnames' +import useCurrentVars from '../hooks/use-inspect-vars-crud' +import useMatchSchemaType from '../nodes/_base/components/variable/use-match-schema-type' + +import { useStore } from '../store' +import Empty from './empty' +import Left from './left' +import Listening from './listening' +import Right from './right' export type currentVarType = { nodeId: string @@ -55,7 +55,8 @@ const Panel: FC = () => { }, [environmentVariables, conversationVars, systemVars, nodesWithInspectVars]) const currentNodeInfo = useMemo(() => { - if (!currentFocusNodeId) return + if (!currentFocusNodeId) + return if (currentFocusNodeId === VarInInspectType.environment) { const currentVar = environmentVariables.find(v => v.id === currentVarId) const res = { @@ -113,7 +114,8 @@ const Panel: FC = () => { return res } const targetNode = nodesWithInspectVars.find(node => node.nodeId === currentFocusNodeId) - if (!targetNode) return + if (!targetNode) + return const currentVar = targetNode.vars.find(v => v.id === currentVarId) return { nodeId: targetNode.nodeId, @@ -127,9 +129,11 @@ const Panel: FC = () => { }, [currentFocusNodeId, currentVarId, environmentVariables, conversationVars, systemVars, nodesWithInspectVars]) const isCurrentNodeVarValueFetching = useMemo(() => { - if (!currentNodeInfo) return false + if (!currentNodeInfo) + return false const targetNode = nodesWithInspectVars.find(node => node.nodeId === currentNodeInfo.nodeId) - if (!targetNode) return false + if (!targetNode) + return false return !targetNode.isValueFetched }, [currentNodeInfo, nodesWithInspectVars]) @@ -156,13 +160,13 @@ const Panel: FC = () => { if (isListening) { return ( <div className={cn('flex h-full flex-col')}> - <div className='flex shrink-0 items-center justify-between pl-4 pr-2 pt-2'> - <div className='system-sm-semibold-uppercase text-text-primary'>{t('workflow.debug.variableInspect.title')}</div> + <div className="flex shrink-0 items-center justify-between pl-4 pr-2 pt-2"> + <div className="system-sm-semibold-uppercase text-text-primary">{t('workflow.debug.variableInspect.title')}</div> <ActionButton onClick={() => setShowVariableInspectPanel(false)}> - <RiCloseLine className='h-4 w-4' /> + <RiCloseLine className="h-4 w-4" /> </ActionButton> </div> - <div className='grow p-2'> + <div className="grow p-2"> <Listening onStop={handleStopListening} /> @@ -174,13 +178,13 @@ const Panel: FC = () => { if (isEmpty) { return ( <div className={cn('flex h-full flex-col')}> - <div className='flex shrink-0 items-center justify-between pl-4 pr-2 pt-2'> - <div className='system-sm-semibold-uppercase text-text-primary'>{t('workflow.debug.variableInspect.title')}</div> + <div className="flex shrink-0 items-center justify-between pl-4 pr-2 pt-2"> + <div className="system-sm-semibold-uppercase text-text-primary">{t('workflow.debug.variableInspect.title')}</div> <ActionButton onClick={() => setShowVariableInspectPanel(false)}> - <RiCloseLine className='h-4 w-4' /> + <RiCloseLine className="h-4 w-4" /> </ActionButton> </div> - <div className='grow p-2'> + <div className="grow p-2"> <Empty /> </div> </div> @@ -190,7 +194,7 @@ const Panel: FC = () => { return ( <div className={cn('relative flex h-full')}> {/* left */} - {bottomPanelWidth < 488 && showLeftPanel && <div className='absolute left-0 top-0 h-full w-full' onClick={() => setShowLeftPanel(false)}></div>} + {bottomPanelWidth < 488 && showLeftPanel && <div className="absolute left-0 top-0 h-full w-full" onClick={() => setShowLeftPanel(false)}></div>} <div className={cn( 'w-60 shrink-0 border-r border-divider-burn', @@ -207,7 +211,7 @@ const Panel: FC = () => { /> </div> {/* right */} - <div className='w-0 grow'> + <div className="w-0 grow"> <Right nodeId={currentFocusNodeId!} isValueFetching={isCurrentNodeVarValueFetching} diff --git a/web/app/components/workflow/variable-inspect/right.tsx b/web/app/components/workflow/variable-inspect/right.tsx index 9fbf18dd64..e451837eca 100644 --- a/web/app/components/workflow/variable-inspect/right.tsx +++ b/web/app/components/workflow/variable-inspect/right.tsx @@ -1,4 +1,5 @@ -import { useTranslation } from 'react-i18next' +import type { currentVarType } from './panel' +import type { GenRes } from '@/service/debug' import { RiArrowGoBackLine, RiCloseLine, @@ -6,35 +7,34 @@ import { RiMenuLine, RiSparklingFill, } from '@remixicon/react' -import { useStore } from '../store' -import { BlockEnum } from '../types' -import useCurrentVars from '../hooks/use-inspect-vars-crud' -import Empty from './empty' -import ValueContent from './value-content' +import { useBoolean } from 'ahooks' +import { produce } from 'immer' +import { useCallback, useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import GetAutomaticResModal from '@/app/components/app/configuration/config/automatic/get-automatic-res' import ActionButton from '@/app/components/base/action-button' import Badge from '@/app/components/base/badge' import CopyFeedback from '@/app/components/base/copy-feedback' +import Loading from '@/app/components/base/loading' import Tooltip from '@/app/components/base/tooltip' import BlockIcon from '@/app/components/workflow/block-icon' -import Loading from '@/app/components/base/loading' -import type { currentVarType } from './panel' +import { VariableIconWithColor } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' +import { useEventEmitterContextContext } from '@/context/event-emitter' +import { AppModeEnum } from '@/types/app' import { VarInInspectType } from '@/types/workflow' import { cn } from '@/utils/classnames' -import useNodeInfo from '../nodes/_base/hooks/use-node-info' -import { useBoolean } from 'ahooks' -import GetAutomaticResModal from '@/app/components/app/configuration/config/automatic/get-automatic-res' import GetCodeGeneratorResModal from '../../app/configuration/config/code-generator/get-code-generator-res' -import { AppModeEnum } from '@/types/app' -import { useHooksStore } from '../hooks-store' -import { useCallback, useMemo } from 'react' -import { useNodesInteractions, useToolIcon } from '../hooks' -import { CodeLanguage } from '../nodes/code/types' -import useNodeCrud from '../nodes/_base/hooks/use-node-crud' -import type { GenRes } from '@/service/debug' -import { produce } from 'immer' import { PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER } from '../../base/prompt-editor/plugins/update-block' -import { useEventEmitterContextContext } from '@/context/event-emitter' -import { VariableIconWithColor } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' +import { useNodesInteractions, useToolIcon } from '../hooks' +import { useHooksStore } from '../hooks-store' +import useCurrentVars from '../hooks/use-inspect-vars-crud' +import useNodeCrud from '../nodes/_base/hooks/use-node-crud' +import useNodeInfo from '../nodes/_base/hooks/use-node-info' +import { CodeLanguage } from '../nodes/code/types' +import { useStore } from '../store' +import { BlockEnum } from '../types' +import Empty from './empty' +import ValueContent from './value-content' type Props = { nodeId: string @@ -64,12 +64,14 @@ const Right = ({ } = useCurrentVars() const handleValueChange = (varId: string, value: any) => { - if (!currentNodeVar) return + if (!currentNodeVar) + return editInspectVarValue(currentNodeVar.nodeId, varId, value) } const resetValue = () => { - if (!currentNodeVar) return + if (!currentNodeVar) + return resetToLastRunVar(currentNodeVar.nodeId, currentNodeVar.var.id) } @@ -79,7 +81,8 @@ const Right = ({ } const handleClear = () => { - if (!currentNodeVar) return + if (!currentNodeVar) + return resetConversationVar(currentNodeVar.var.id) } @@ -161,20 +164,20 @@ const Right = ({ return ( <div className={cn('flex h-full flex-col')}> {/* header */} - <div className='flex shrink-0 items-center justify-between gap-1 px-2 pt-2'> + <div className="flex shrink-0 items-center justify-between gap-1 px-2 pt-2"> {bottomPanelWidth < 488 && ( - <ActionButton className='shrink-0' onClick={handleOpenMenu}> - <RiMenuLine className='h-4 w-4' /> + <ActionButton className="shrink-0" onClick={handleOpenMenu}> + <RiMenuLine className="h-4 w-4" /> </ActionButton> )} - <div className='flex w-0 grow items-center gap-1'> + <div className="flex w-0 grow items-center gap-1"> {currentNodeVar?.var && ( <> { [VarInInspectType.environment, VarInInspectType.conversation, VarInInspectType.system].includes(currentNodeVar.nodeType as VarInInspectType) && ( <VariableIconWithColor variableCategory={currentNodeVar.nodeType as VarInInspectType} - className='size-4' + className="size-4" /> ) } @@ -184,22 +187,25 @@ const Right = ({ && ( <> <BlockIcon - className='shrink-0' + className="shrink-0" type={currentNodeVar.nodeType as BlockEnum} - size='xs' + size="xs" toolIcon={toolIcon} /> - <div className='system-sm-regular shrink-0 text-text-secondary'>{currentNodeVar.title}</div> - <div className='system-sm-regular shrink-0 text-text-quaternary'>/</div> + <div className="system-sm-regular shrink-0 text-text-secondary">{currentNodeVar.title}</div> + <div className="system-sm-regular shrink-0 text-text-quaternary">/</div> </> )} - <div title={currentNodeVar.var.name} className='system-sm-semibold truncate text-text-secondary'>{currentNodeVar.var.name}</div> - <div className='system-xs-medium ml-1 shrink-0 space-x-2 text-text-tertiary'> + <div title={currentNodeVar.var.name} className="system-sm-semibold truncate text-text-secondary">{currentNodeVar.var.name}</div> + <div className="system-xs-medium ml-1 shrink-0 space-x-2 text-text-tertiary"> <span>{`${currentNodeVar.var.value_type}${displaySchemaType}`}</span> {isTruncated && ( <> <span>·</span> - <span>{((fullContent?.size_bytes || 0) / 1024 / 1024).toFixed(1)}MB</span> + <span> + {((fullContent?.size_bytes || 0) / 1024 / 1024).toFixed(1)} + MB + </span> </> )} </div> @@ -207,16 +213,16 @@ const Right = ({ </> )} </div> - <div className='flex shrink-0 items-center gap-1'> + <div className="flex shrink-0 items-center gap-1"> {currentNodeVar && ( <> {canShowPromptGenerator && ( <Tooltip popupContent={t('appDebug.generate.optimizePromptTooltip')}> <div - className='cursor-pointer rounded-md p-1 hover:bg-state-accent-active' + className="cursor-pointer rounded-md p-1 hover:bg-state-accent-active" onClick={handleShowPromptGenerator} > - <RiSparklingFill className='size-4 text-components-input-border-active-prompt-1' /> + <RiSparklingFill className="size-4 text-components-input-border-active-prompt-1" /> </div> </Tooltip> )} @@ -225,30 +231,30 @@ const Right = ({ <ActionButton> <a href={fullContent?.download_url} - target='_blank' + target="_blank" > - <RiFileDownloadFill className='size-4' /> + <RiFileDownloadFill className="size-4" /> </a> </ActionButton> </Tooltip> )} {!isTruncated && currentNodeVar.var.edited && ( <Badge> - <span className='ml-[2.5px] mr-[4.5px] h-[3px] w-[3px] rounded bg-text-accent-secondary'></span> - <span className='system-2xs-semibold-uupercase'>{t('workflow.debug.variableInspect.edited')}</span> + <span className="ml-[2.5px] mr-[4.5px] h-[3px] w-[3px] rounded bg-text-accent-secondary"></span> + <span className="system-2xs-semibold-uupercase">{t('workflow.debug.variableInspect.edited')}</span> </Badge> )} {!isTruncated && currentNodeVar.var.edited && currentNodeVar.var.type !== VarInInspectType.conversation && ( <Tooltip popupContent={t('workflow.debug.variableInspect.reset')}> <ActionButton onClick={resetValue}> - <RiArrowGoBackLine className='h-4 w-4' /> + <RiArrowGoBackLine className="h-4 w-4" /> </ActionButton> </Tooltip> )} {!isTruncated && currentNodeVar.var.edited && currentNodeVar.var.type === VarInInspectType.conversation && ( <Tooltip popupContent={t('workflow.debug.variableInspect.resetConversationVar')}> <ActionButton onClick={handleClear}> - <RiArrowGoBackLine className='h-4 w-4' /> + <RiArrowGoBackLine className="h-4 w-4" /> </ActionButton> </Tooltip> )} @@ -258,15 +264,15 @@ const Right = ({ </> )} <ActionButton onClick={handleClose}> - <RiCloseLine className='h-4 w-4' /> + <RiCloseLine className="h-4 w-4" /> </ActionButton> </div> </div> {/* content */} - <div className='grow p-2'> + <div className="grow p-2"> {!currentNodeVar?.var && <Empty />} {isValueFetching && ( - <div className='flex h-full items-center justify-center'> + <div className="flex h-full items-center justify-center"> <Loading /> </div> )} @@ -281,25 +287,29 @@ const Right = ({ </div> {isShowPromptGenerator && ( isCodeBlock - ? <GetCodeGeneratorResModal - isShow - mode={AppModeEnum.CHAT} - onClose={handleHidePromptGenerator} - flowId={configsMap?.flowId || ''} - nodeId={nodeId} - currentCode={currentPrompt} - codeLanguages={node?.data?.code_languages || CodeLanguage.python3} - onFinished={handleUpdatePrompt} - /> - : <GetAutomaticResModal - mode={AppModeEnum.CHAT} - isShow - onClose={handleHidePromptGenerator} - onFinished={handleUpdatePrompt} - flowId={configsMap?.flowId || ''} - nodeId={nodeId} - currentPrompt={currentPrompt} - /> + ? ( + <GetCodeGeneratorResModal + isShow + mode={AppModeEnum.CHAT} + onClose={handleHidePromptGenerator} + flowId={configsMap?.flowId || ''} + nodeId={nodeId} + currentCode={currentPrompt} + codeLanguages={node?.data?.code_languages || CodeLanguage.python3} + onFinished={handleUpdatePrompt} + /> + ) + : ( + <GetAutomaticResModal + mode={AppModeEnum.CHAT} + isShow + onClose={handleHidePromptGenerator} + onFinished={handleUpdatePrompt} + flowId={configsMap?.flowId || ''} + nodeId={nodeId} + currentPrompt={currentPrompt} + /> + ) )} </div> ) diff --git a/web/app/components/workflow/variable-inspect/trigger.tsx b/web/app/components/workflow/variable-inspect/trigger.tsx index 33161a6c5e..e354a6db67 100644 --- a/web/app/components/workflow/variable-inspect/trigger.tsx +++ b/web/app/components/workflow/variable-inspect/trigger.tsx @@ -1,18 +1,17 @@ import type { FC } from 'react' -import { useMemo } from 'react' -import { useNodes } from 'reactflow' -import { useTranslation } from 'react-i18next' -import { RiLoader2Line, RiStopCircleFill } from '@remixicon/react' -import Tooltip from '@/app/components/base/tooltip' -import { useStore } from '../store' -import useCurrentVars from '../hooks/use-inspect-vars-crud' -import { WorkflowRunningStatus } from '@/app/components/workflow/types' -import { NodeRunningStatus } from '@/app/components/workflow/types' import type { CommonNodeType } from '@/app/components/workflow/types' -import { useEventEmitterContextContext } from '@/context/event-emitter' +import { RiLoader2Line, RiStopCircleFill } from '@remixicon/react' +import { useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { useNodes } from 'reactflow' +import Tooltip from '@/app/components/base/tooltip' +import { NodeRunningStatus, WorkflowRunningStatus } from '@/app/components/workflow/types' import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types' +import { useEventEmitterContextContext } from '@/context/event-emitter' import { cn } from '@/utils/classnames' +import useCurrentVars from '../hooks/use-inspect-vars-crud' import { useNodesReadOnly } from '../hooks/use-workflow' +import { useStore } from '../store' const VariableInspectTrigger: FC = () => { const { t } = useTranslation() @@ -65,9 +64,7 @@ const VariableInspectTrigger: FC = () => { <div className={cn('flex items-center gap-1')}> {!isRunning && !currentVars.length && ( <div - className={cn('system-2xs-semibold-uppercase flex h-5 cursor-pointer items-center gap-1 rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-2 text-text-tertiary shadow-lg backdrop-blur-sm hover:bg-background-default-hover', - nodesReadOnly && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled', - )} + className={cn('system-2xs-semibold-uppercase flex h-5 cursor-pointer items-center gap-1 rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-2 text-text-tertiary shadow-lg backdrop-blur-sm hover:bg-background-default-hover', nodesReadOnly && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled')} onClick={() => { if (getNodesReadOnly()) return @@ -80,9 +77,7 @@ const VariableInspectTrigger: FC = () => { {!isRunning && currentVars.length > 0 && ( <> <div - className={cn('system-xs-medium flex h-6 cursor-pointer items-center gap-1 rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-2 text-text-accent shadow-lg backdrop-blur-sm hover:bg-components-actionbar-bg-accent', - nodesReadOnly && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled', - )} + className={cn('system-xs-medium flex h-6 cursor-pointer items-center gap-1 rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-2 text-text-accent shadow-lg backdrop-blur-sm hover:bg-components-actionbar-bg-accent', nodesReadOnly && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled')} onClick={() => { if (getNodesReadOnly()) return @@ -92,9 +87,7 @@ const VariableInspectTrigger: FC = () => { {t('workflow.debug.variableInspect.trigger.cached')} </div> <div - className={cn('system-xs-medium flex h-6 cursor-pointer items-center rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-1 text-text-tertiary shadow-lg backdrop-blur-sm hover:bg-components-actionbar-bg-accent hover:text-text-accent', - nodesReadOnly && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled', - )} + className={cn('system-xs-medium flex h-6 cursor-pointer items-center rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-1 text-text-tertiary shadow-lg backdrop-blur-sm hover:bg-components-actionbar-bg-accent hover:text-text-accent', nodesReadOnly && 'cursor-not-allowed text-text-disabled hover:bg-transparent hover:text-text-disabled')} onClick={() => { if (getNodesReadOnly()) return @@ -108,21 +101,21 @@ const VariableInspectTrigger: FC = () => { {isRunning && ( <> <div - className='system-xs-medium flex h-6 cursor-pointer items-center gap-1 rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-2 text-text-accent shadow-lg backdrop-blur-sm hover:bg-components-actionbar-bg-accent' + className="system-xs-medium flex h-6 cursor-pointer items-center gap-1 rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-2 text-text-accent shadow-lg backdrop-blur-sm hover:bg-components-actionbar-bg-accent" onClick={() => setShowVariableInspectPanel(true)} > - <RiLoader2Line className='h-4 w-4 animate-spin' /> - <span className='text-text-accent'>{t('workflow.debug.variableInspect.trigger.running')}</span> + <RiLoader2Line className="h-4 w-4 animate-spin" /> + <span className="text-text-accent">{t('workflow.debug.variableInspect.trigger.running')}</span> </div> {isPreviewRunning && ( <Tooltip popupContent={t('workflow.debug.variableInspect.trigger.stop')} > <div - className='flex h-6 cursor-pointer items-center rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-1 shadow-lg backdrop-blur-sm hover:bg-components-actionbar-bg-accent' + className="flex h-6 cursor-pointer items-center rounded-md border-[0.5px] border-effects-highlight bg-components-actionbar-bg px-1 shadow-lg backdrop-blur-sm hover:bg-components-actionbar-bg-accent" onClick={handleStop} > - <RiStopCircleFill className='h-4 w-4 text-text-accent' /> + <RiStopCircleFill className="h-4 w-4 text-text-accent" /> </div> </Tooltip> )} diff --git a/web/app/components/workflow/variable-inspect/value-content.tsx b/web/app/components/workflow/variable-inspect/value-content.tsx index 0009fb4580..6d6a04434a 100644 --- a/web/app/components/workflow/variable-inspect/value-content.tsx +++ b/web/app/components/workflow/variable-inspect/value-content.tsx @@ -1,30 +1,30 @@ -import React, { useEffect, useMemo, useRef, useState } from 'react' +import type { VarInInspect } from '@/types/workflow' import { useDebounceFn } from 'ahooks' -import Textarea from '@/app/components/base/textarea' -import SchemaEditor from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor' +import React, { useEffect, useMemo, useRef, useState } from 'react' import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader' +import { getProcessedFiles, getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils' +import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' +import Textarea from '@/app/components/base/textarea' import ErrorMessage from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/error-message' +import SchemaEditor from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor' import { checkJsonSchemaDepth, getValidationErrorMessage, validateSchemaAgainstDraft7, } from '@/app/components/workflow/nodes/llm/utils' +import { useStore } from '@/app/components/workflow/store' +import { SupportUploadFileTypes } from '@/app/components/workflow/types' import { validateJSONSchema, } from '@/app/components/workflow/variable-inspect/utils' -import { getProcessedFiles, getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils' import { JSON_SCHEMA_MAX_DEPTH } from '@/config' import { TransferMethod } from '@/types/app' -import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' -import { SupportUploadFileTypes } from '@/app/components/workflow/types' -import type { VarInInspect } from '@/types/workflow' import { VarInInspectType } from '@/types/workflow' import { cn } from '@/utils/classnames' -import LargeDataAlert from './large-data-alert' -import BoolValue from '../panel/chat-variable-panel/components/bool-value' -import { useStore } from '@/app/components/workflow/store' import { PreviewMode } from '../../base/features/types' +import BoolValue from '../panel/chat-variable-panel/components/bool-value' import DisplayContent from './display-content' +import LargeDataAlert from './large-data-alert' import { CHUNK_SCHEMA_TYPES, PreviewType } from './types' type Props = { @@ -184,36 +184,38 @@ const ValueContent = ({ return ( <div ref={contentContainerRef} - className='flex h-full flex-col' + className="flex h-full flex-col" > <div className={cn('relative grow')} style={{ height: `${editorHeight}px` }}> {showTextEditor && ( <> - {isTruncated && <LargeDataAlert className='absolute left-3 right-3 top-1' />} + {isTruncated && <LargeDataAlert className="absolute left-3 right-3 top-1" />} { - currentVar.value_type === 'string' ? ( - <DisplayContent - previewType={PreviewType.Markdown} - varType={currentVar.value_type} - mdString={value as any} - readonly={textEditorDisabled} - handleTextChange={handleTextChange} - className={cn(isTruncated && 'pt-[36px]')} - /> - ) : ( - <Textarea - readOnly={textEditorDisabled} - disabled={textEditorDisabled || isTruncated} - className={cn('h-full', isTruncated && 'pt-[48px]')} - value={value as any} - onChange={e => handleTextChange(e.target.value)} - /> - ) + currentVar.value_type === 'string' + ? ( + <DisplayContent + previewType={PreviewType.Markdown} + varType={currentVar.value_type} + mdString={value as any} + readonly={textEditorDisabled} + handleTextChange={handleTextChange} + className={cn(isTruncated && 'pt-[36px]')} + /> + ) + : ( + <Textarea + readOnly={textEditorDisabled} + disabled={textEditorDisabled || isTruncated} + className={cn('h-full', isTruncated && 'pt-[48px]')} + value={value as any} + onChange={e => handleTextChange(e.target.value)} + /> + ) } </> )} {showBoolEditor && ( - <div className='w-[295px]'> + <div className="w-[295px]"> <BoolValue value={currentVar.value as boolean} onChange={(newValue) => { @@ -225,7 +227,7 @@ const ValueContent = ({ )} { showBoolArrayEditor && ( - <div className='w-[295px] space-y-1'> + <div className="w-[295px] space-y-1"> {currentVar.value.map((v: boolean, i: number) => ( <BoolValue key={i} @@ -244,28 +246,28 @@ const ValueContent = ({ {showJSONEditor && ( hasChunks ? ( - <DisplayContent - previewType={PreviewType.Chunks} - varType={currentVar.value_type} - schemaType={currentVar.schemaType ?? ''} - jsonString={json ?? '{}'} - readonly={JSONEditorDisabled} - handleEditorChange={handleEditorChange} - /> - ) + <DisplayContent + previewType={PreviewType.Chunks} + varType={currentVar.value_type} + schemaType={currentVar.schemaType ?? ''} + jsonString={json ?? '{}'} + readonly={JSONEditorDisabled} + handleEditorChange={handleEditorChange} + /> + ) : ( - <SchemaEditor - readonly={JSONEditorDisabled || isTruncated} - className='overflow-y-auto' - hideTopMenu - schema={json} - onUpdate={handleEditorChange} - isTruncated={isTruncated} - /> - ) + <SchemaEditor + readonly={JSONEditorDisabled || isTruncated} + className="overflow-y-auto" + hideTopMenu + schema={json} + onUpdate={handleEditorChange} + isTruncated={isTruncated} + /> + ) )} {showFileEditor && ( - <div className='max-w-[460px]'> + <div className="max-w-[460px]"> <FileUploaderInAttachmentWrapper value={fileValue} onChange={files => handleFileChange(getProcessedFiles(files))} @@ -295,11 +297,11 @@ const ValueContent = ({ </div> )} </div> - <div ref={errorMessageRef} className='shrink-0'> - {parseError && <ErrorMessage className='mt-1' message={parseError.message} />} - {validationError && <ErrorMessage className='mt-1' message={validationError} />} + <div ref={errorMessageRef} className="shrink-0"> + {parseError && <ErrorMessage className="mt-1" message={parseError.message} />} + {validationError && <ErrorMessage className="mt-1" message={validationError} />} </div> - </div > + </div> ) } diff --git a/web/app/components/workflow/workflow-history-store.tsx b/web/app/components/workflow/workflow-history-store.tsx index 96e87f4fd4..502cb733cb 100644 --- a/web/app/components/workflow/workflow-history-store.tsx +++ b/web/app/components/workflow/workflow-history-store.tsx @@ -1,10 +1,13 @@ -import { type ReactNode, createContext, useContext, useMemo, useState } from 'react' -import { type StoreApi, create } from 'zustand' -import { type TemporalState, temporal } from 'zundo' -import isDeepEqual from 'fast-deep-equal' -import type { Edge, Node } from './types' +import type { ReactNode } from 'react' +import type { TemporalState } from 'zundo' +import type { StoreApi } from 'zustand' import type { WorkflowHistoryEventT } from './hooks' +import type { Edge, Node } from './types' +import isDeepEqual from 'fast-deep-equal' import { noop } from 'lodash-es' +import { createContext, useContext, useMemo, useState } from 'react' +import { temporal } from 'zundo' +import { create } from 'zustand' export const WorkflowHistoryStoreContext = createContext<WorkflowHistoryStoreContextType>({ store: null, shortcutsEnabled: true, setShortcutsEnabled: noop }) export const Provider = WorkflowHistoryStoreContext.Provider diff --git a/web/app/components/workflow/workflow-preview/components/custom-edge.tsx b/web/app/components/workflow/workflow-preview/components/custom-edge.tsx index eb660fb7b8..6294d3f88a 100644 --- a/web/app/components/workflow/workflow-preview/components/custom-edge.tsx +++ b/web/app/components/workflow/workflow-preview/components/custom-edge.tsx @@ -1,17 +1,17 @@ +import type { EdgeProps } from 'reactflow' import { memo, useMemo, } from 'react' -import type { EdgeProps } from 'reactflow' import { BaseEdge, - Position, getBezierPath, + Position, } from 'reactflow' -import { NodeRunningStatus } from '@/app/components/workflow/types' -import { getEdgeColor } from '@/app/components/workflow/utils' import CustomEdgeLinearGradientRender from '@/app/components/workflow/custom-edge-linear-gradient-render' import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' +import { NodeRunningStatus } from '@/app/components/workflow/types' +import { getEdgeColor } from '@/app/components/workflow/utils' const CustomEdge = ({ id, @@ -51,8 +51,9 @@ const CustomEdge = ({ || _targetRunningStatus === NodeRunningStatus.Exception || _targetRunningStatus === NodeRunningStatus.Running ) - ) + ) { return id + } }, [_sourceRunningStatus, _targetRunningStatus, id]) const stroke = useMemo(() => { diff --git a/web/app/components/workflow/workflow-preview/components/error-handle-on-node.tsx b/web/app/components/workflow/workflow-preview/components/error-handle-on-node.tsx index 03ee1c7f47..73b4d01a2b 100644 --- a/web/app/components/workflow/workflow-preview/components/error-handle-on-node.tsx +++ b/web/app/components/workflow/workflow-preview/components/error-handle-on-node.tsx @@ -1,11 +1,11 @@ +import type { Node } from '@/app/components/workflow/types' import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import { useUpdateNodeInternals } from 'reactflow' -import { NodeSourceHandle } from './node-handle' import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' -import type { Node } from '@/app/components/workflow/types' import { NodeRunningStatus } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' +import { NodeSourceHandle } from './node-handle' type ErrorHandleOnNodeProps = Pick<Node, 'id' | 'data'> const ErrorHandleOnNode = ({ @@ -25,18 +25,20 @@ const ErrorHandleOnNode = ({ return null return ( - <div className='relative px-3 pb-2 pt-1'> + <div className="relative px-3 pb-2 pt-1"> <div className={cn( 'relative flex h-6 items-center justify-between rounded-md bg-workflow-block-parma-bg px-[5px]', data._runningStatus === NodeRunningStatus.Exception && 'border-[0.5px] border-components-badge-status-light-warning-halo bg-state-warning-hover', - )}> - <div className='system-xs-medium-uppercase text-text-tertiary'> + )} + > + <div className="system-xs-medium-uppercase text-text-tertiary"> {t('workflow.common.onFailure')} </div> <div className={cn( 'system-xs-medium text-text-secondary', data._runningStatus === NodeRunningStatus.Exception && 'text-text-warning', - )}> + )} + > { error_strategy === ErrorHandleTypeEnum.defaultValue && ( t('workflow.nodes.common.errorHandle.defaultValue.output') @@ -54,7 +56,7 @@ const ErrorHandleOnNode = ({ id={id} data={data} handleId={ErrorHandleTypeEnum.failBranch} - handleClassName='!top-1/2 !-right-[21px] !-translate-y-1/2 after:!bg-workflow-link-line-failure-button-bg' + handleClassName="!top-1/2 !-right-[21px] !-translate-y-1/2 after:!bg-workflow-link-line-failure-button-bg" /> ) } diff --git a/web/app/components/workflow/workflow-preview/components/node-handle.tsx b/web/app/components/workflow/workflow-preview/components/node-handle.tsx index dd44daf1e4..f63dbba20b 100644 --- a/web/app/components/workflow/workflow-preview/components/node-handle.tsx +++ b/web/app/components/workflow/workflow-preview/components/node-handle.tsx @@ -1,3 +1,4 @@ +import type { Node } from '@/app/components/workflow/types' import { memo, } from 'react' @@ -8,7 +9,6 @@ import { import { BlockEnum, } from '@/app/components/workflow/types' -import type { Node } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' type NodeHandleProps = { @@ -27,7 +27,7 @@ export const NodeTargetHandle = memo(({ <> <Handle id={handleId} - type='target' + type="target" position={Position.Left} className={cn( 'z-[1] !h-4 !w-4 !rounded-none !border-none !bg-transparent !outline-none', @@ -35,9 +35,9 @@ export const NodeTargetHandle = memo(({ 'transition-all hover:scale-125', !connected && 'after:opacity-0', (data.type === BlockEnum.Start - || data.type === BlockEnum.TriggerWebhook - || data.type === BlockEnum.TriggerSchedule - || data.type === BlockEnum.TriggerPlugin) && 'opacity-0', + || data.type === BlockEnum.TriggerWebhook + || data.type === BlockEnum.TriggerSchedule + || data.type === BlockEnum.TriggerPlugin) && 'opacity-0', handleClassName, )} > @@ -57,7 +57,7 @@ export const NodeSourceHandle = memo(({ return ( <Handle id={handleId} - type='source' + type="source" position={Position.Right} className={cn( 'group/handle z-[1] !h-4 !w-4 !rounded-none !border-none !bg-transparent !outline-none', diff --git a/web/app/components/workflow/workflow-preview/components/nodes/base.tsx b/web/app/components/workflow/workflow-preview/components/nodes/base.tsx index 58df273917..07eed87d24 100644 --- a/web/app/components/workflow/workflow-preview/components/nodes/base.tsx +++ b/web/app/components/workflow/workflow-preview/components/nodes/base.tsx @@ -1,27 +1,27 @@ import type { ReactElement, } from 'react' +import type { IterationNodeType } from '@/app/components/workflow/nodes/iteration/types' +import type { + NodeProps, +} from '@/app/components/workflow/types' import { cloneElement, memo, } from 'react' import { useTranslation } from 'react-i18next' -import { cn } from '@/utils/classnames' +import Tooltip from '@/app/components/base/tooltip' import BlockIcon from '@/app/components/workflow/block-icon' -import type { - NodeProps, -} from '@/app/components/workflow/types' import { BlockEnum, } from '@/app/components/workflow/types' import { hasErrorHandleNode } from '@/app/components/workflow/utils' -import Tooltip from '@/app/components/base/tooltip' -import type { IterationNodeType } from '@/app/components/workflow/nodes/iteration/types' +import { cn } from '@/utils/classnames' +import ErrorHandleOnNode from '../error-handle-on-node' import { NodeSourceHandle, NodeTargetHandle, } from '../node-handle' -import ErrorHandleOnNode from '../error-handle-on-node' type NodeChildElement = ReactElement<Partial<NodeProps>> @@ -59,46 +59,48 @@ const BaseCard = ({ > <div className={cn( 'flex items-center rounded-t-2xl px-3 pb-2 pt-3', - )}> + )} + > <NodeTargetHandle id={id} data={data} - handleClassName='!top-4 !-left-[9px] !translate-y-0' - handleId='target' + handleClassName="!top-4 !-left-[9px] !translate-y-0" + handleId="target" /> { data.type !== BlockEnum.IfElse && data.type !== BlockEnum.QuestionClassifier && ( <NodeSourceHandle id={id} data={data} - handleClassName='!top-4 !-right-[9px] !translate-y-0' - handleId='source' + handleClassName="!top-4 !-right-[9px] !translate-y-0" + handleId="source" /> ) } <BlockIcon - className='mr-2 shrink-0' + className="mr-2 shrink-0" type={data.type} - size='md' + size="md" /> <div title={data.title} - className='system-sm-semibold-uppercase mr-1 flex grow items-center truncate text-text-primary' + className="system-sm-semibold-uppercase mr-1 flex grow items-center truncate text-text-primary" > <div> {data.title} </div> { data.type === BlockEnum.Iteration && (data as IterationNodeType).is_parallel && ( - <Tooltip popupContent={ - <div className='w-[180px]'> - <div className='font-extrabold'> + <Tooltip popupContent={( + <div className="w-[180px]"> + <div className="font-extrabold"> {t('workflow.nodes.iteration.parallelModeEnableTitle')} </div> {t('workflow.nodes.iteration.parallelModeEnableDesc')} - </div>} + </div> + )} > - <div className='system-2xs-medium-uppercase ml-1 flex items-center justify-center rounded-[5px] border-[1px] border-text-warning px-[5px] py-[3px] text-text-warning '> + <div className="system-2xs-medium-uppercase ml-1 flex items-center justify-center rounded-[5px] border-[1px] border-text-warning px-[5px] py-[3px] text-text-warning "> {t('workflow.nodes.iteration.parallelModeUpper')} </div> </Tooltip> @@ -113,7 +115,7 @@ const BaseCard = ({ } { (data.type === BlockEnum.Iteration || data.type === BlockEnum.Loop) && children && ( - <div className='h-[calc(100%-42px)] w-full grow pb-1 pl-1 pr-1'> + <div className="h-[calc(100%-42px)] w-full grow pb-1 pl-1 pr-1"> {cloneElement(children, { id, data })} </div> ) @@ -128,7 +130,7 @@ const BaseCard = ({ } { data.desc && data.type !== BlockEnum.Iteration && data.type !== BlockEnum.Loop && ( - <div className='system-xs-regular whitespace-pre-line break-words px-3 pb-2 pt-1 text-text-tertiary'> + <div className="system-xs-regular whitespace-pre-line break-words px-3 pb-2 pt-1 text-text-tertiary"> {data.desc} </div> ) diff --git a/web/app/components/workflow/workflow-preview/components/nodes/constants.ts b/web/app/components/workflow/workflow-preview/components/nodes/constants.ts index 2a6b01d561..dedca308fc 100644 --- a/web/app/components/workflow/workflow-preview/components/nodes/constants.ts +++ b/web/app/components/workflow/workflow-preview/components/nodes/constants.ts @@ -1,8 +1,8 @@ import { BlockEnum } from '@/app/components/workflow/types' -import QuestionClassifierNode from './question-classifier/node' import IfElseNode from './if-else/node' import IterationNode from './iteration/node' import LoopNode from './loop/node' +import QuestionClassifierNode from './question-classifier/node' export const NodeComponentMap: Record<string, any> = { [BlockEnum.QuestionClassifier]: QuestionClassifierNode, diff --git a/web/app/components/workflow/workflow-preview/components/nodes/if-else/node.tsx b/web/app/components/workflow/workflow-preview/components/nodes/if-else/node.tsx index 8d9189fcc1..b0a9e6903f 100644 --- a/web/app/components/workflow/workflow-preview/components/nodes/if-else/node.tsx +++ b/web/app/components/workflow/workflow-preview/components/nodes/if-else/node.tsx @@ -1,12 +1,13 @@ import type { FC } from 'react' +import type { NodeProps } from 'reactflow' +import type { Condition, IfElseNodeType } from '@/app/components/workflow/nodes/if-else/types' import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import type { NodeProps } from 'reactflow' -import { NodeSourceHandle } from '../../node-handle' -import { isEmptyRelatedOperator } from '@/app/components/workflow/nodes/if-else/utils' -import type { Condition, IfElseNodeType } from '@/app/components/workflow/nodes/if-else/types' -import ConditionValue from '@/app/components/workflow/nodes/if-else/components/condition-value' import ConditionFilesListValue from '@/app/components/workflow/nodes/if-else/components/condition-files-list-value' +import ConditionValue from '@/app/components/workflow/nodes/if-else/components/condition-value' +import { isEmptyRelatedOperator } from '@/app/components/workflow/nodes/if-else/utils' +import { NodeSourceHandle } from '../../node-handle' + const i18nPrefix = 'workflow.nodes.ifElse' const IfElseNode: FC<NodeProps<IfElseNodeType>> = (props) => { @@ -37,50 +38,53 @@ const IfElseNode: FC<NodeProps<IfElseNodeType>> = (props) => { return !!condition.value } }, []) - const conditionNotSet = (<div className='flex h-6 items-center space-x-1 rounded-md bg-workflow-block-parma-bg px-1 text-xs font-normal text-text-secondary'> - {t(`${i18nPrefix}.conditionNotSetup`)} - </div>) + const conditionNotSet = ( + <div className="flex h-6 items-center space-x-1 rounded-md bg-workflow-block-parma-bg px-1 text-xs font-normal text-text-secondary"> + {t(`${i18nPrefix}.conditionNotSetup`)} + </div> + ) return ( - <div className='px-3'> + <div className="px-3"> { cases.map((caseItem, index) => ( <div key={caseItem.case_id}> - <div className='relative flex h-6 items-center px-1'> - <div className='flex w-full items-center justify-between'> - <div className='text-[10px] font-semibold text-text-tertiary'> + <div className="relative flex h-6 items-center px-1"> + <div className="flex w-full items-center justify-between"> + <div className="text-[10px] font-semibold text-text-tertiary"> {casesLength > 1 && `CASE ${index + 1}`} </div> - <div className='text-[12px] font-semibold text-text-secondary'>{index === 0 ? 'IF' : 'ELIF'}</div> + <div className="text-[12px] font-semibold text-text-secondary">{index === 0 ? 'IF' : 'ELIF'}</div> </div> <NodeSourceHandle {...props} handleId={caseItem.case_id} - handleClassName='!top-1/2 !-right-[21px] !-translate-y-1/2' + handleClassName="!top-1/2 !-right-[21px] !-translate-y-1/2" /> </div> - <div className='space-y-0.5'> + <div className="space-y-0.5"> {caseItem.conditions.map((condition, i) => ( - <div key={condition.id} className='relative'> + <div key={condition.id} className="relative"> { checkIsConditionSet(condition) ? ( - (!isEmptyRelatedOperator(condition.comparison_operator!) && condition.sub_variable_condition) - ? ( - <ConditionFilesListValue condition={condition} /> - ) - : ( - <ConditionValue - variableSelector={condition.variable_selector!} - operator={condition.comparison_operator!} - value={condition.value} - /> - ) + (!isEmptyRelatedOperator(condition.comparison_operator!) && condition.sub_variable_condition) + ? ( + <ConditionFilesListValue condition={condition} /> + ) + : ( + <ConditionValue + variableSelector={condition.variable_selector!} + operator={condition.comparison_operator!} + value={condition.value} + /> + ) - ) - : conditionNotSet} + ) + : conditionNotSet + } {i !== caseItem.conditions.length - 1 && ( - <div className='absolute bottom-[-10px] right-1 z-10 text-[10px] font-medium uppercase leading-4 text-text-accent'>{t(`${i18nPrefix}.${caseItem.logical_operator}`)}</div> + <div className="absolute bottom-[-10px] right-1 z-10 text-[10px] font-medium uppercase leading-4 text-text-accent">{t(`${i18nPrefix}.${caseItem.logical_operator}`)}</div> )} </div> ))} @@ -88,12 +92,12 @@ const IfElseNode: FC<NodeProps<IfElseNodeType>> = (props) => { </div> )) } - <div className='relative flex h-6 items-center px-1'> - <div className='w-full text-right text-xs font-semibold text-text-secondary'>ELSE</div> + <div className="relative flex h-6 items-center px-1"> + <div className="w-full text-right text-xs font-semibold text-text-secondary">ELSE</div> <NodeSourceHandle {...props} - handleId='false' - handleClassName='!top-1/2 !-right-[21px] !-translate-y-1/2' + handleId="false" + handleClassName="!top-1/2 !-right-[21px] !-translate-y-1/2" /> </div> </div> diff --git a/web/app/components/workflow/workflow-preview/components/nodes/index.tsx b/web/app/components/workflow/workflow-preview/components/nodes/index.tsx index e496d8440d..9521493446 100644 --- a/web/app/components/workflow/workflow-preview/components/nodes/index.tsx +++ b/web/app/components/workflow/workflow-preview/components/nodes/index.tsx @@ -8,7 +8,7 @@ const CustomNode = (props: NodeProps) => { return ( <> - <BaseNode { ...props }> + <BaseNode {...props}> { NodeComponent && <NodeComponent /> } </BaseNode> </> diff --git a/web/app/components/workflow/workflow-preview/components/nodes/iteration-start/index.tsx b/web/app/components/workflow/workflow-preview/components/nodes/iteration-start/index.tsx index e4c7b42fca..5b24fe913c 100644 --- a/web/app/components/workflow/workflow-preview/components/nodes/iteration-start/index.tsx +++ b/web/app/components/workflow/workflow-preview/components/nodes/iteration-start/index.tsx @@ -1,7 +1,7 @@ -import { memo } from 'react' -import { useTranslation } from 'react-i18next' import type { NodeProps } from 'reactflow' import { RiHome5Fill } from '@remixicon/react' +import { memo } from 'react' +import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import { NodeSourceHandle } from '../../node-handle' @@ -9,17 +9,17 @@ const IterationStartNode = ({ id, data }: NodeProps) => { const { t } = useTranslation() return ( - <div className='nodrag group mt-1 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg shadow-xs'> + <div className="nodrag group mt-1 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg shadow-xs"> <Tooltip popupContent={t('workflow.blocks.iteration-start')} asChild={false}> - <div className='flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500'> - <RiHome5Fill className='h-3 w-3 text-text-primary-on-surface' /> + <div className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500"> + <RiHome5Fill className="h-3 w-3 text-text-primary-on-surface" /> </div> </Tooltip> <NodeSourceHandle id={id} data={data} - handleClassName='!top-1/2 !-right-[9px] !-translate-y-1/2' - handleId='source' + handleClassName="!top-1/2 !-right-[9px] !-translate-y-1/2" + handleId="source" /> </div> ) diff --git a/web/app/components/workflow/workflow-preview/components/nodes/iteration/node.tsx b/web/app/components/workflow/workflow-preview/components/nodes/iteration/node.tsx index 5f1ba79810..524bf8aaf7 100644 --- a/web/app/components/workflow/workflow-preview/components/nodes/iteration/node.tsx +++ b/web/app/components/workflow/workflow-preview/components/nodes/iteration/node.tsx @@ -1,4 +1,6 @@ import type { FC } from 'react' +import type { IterationNodeType } from '@/app/components/workflow/nodes/iteration/types' +import type { NodeProps } from '@/app/components/workflow/types' import { memo, } from 'react' @@ -6,9 +8,7 @@ import { Background, useViewport, } from 'reactflow' -import type { IterationNodeType } from '@/app/components/workflow/nodes/iteration/types' import { cn } from '@/utils/classnames' -import type { NodeProps } from '@/app/components/workflow/types' const Node: FC<NodeProps<IterationNodeType>> = ({ id, @@ -18,13 +18,14 @@ const Node: FC<NodeProps<IterationNodeType>> = ({ return ( <div className={cn( 'relative h-full min-h-[90px] w-full min-w-[240px] rounded-2xl bg-workflow-canvas-workflow-bg', - )}> + )} + > <Background id={`iteration-background-${id}`} - className='!z-0 rounded-2xl' + className="!z-0 rounded-2xl" gap={[14 / zoom, 14 / zoom]} size={2 / zoom} - color='var(--color-workflow-canvas-workflow-dot-color)' + color="var(--color-workflow-canvas-workflow-dot-color)" /> </div> ) diff --git a/web/app/components/workflow/workflow-preview/components/nodes/loop-start/index.tsx b/web/app/components/workflow/workflow-preview/components/nodes/loop-start/index.tsx index d9dce9b0ee..9bf5261f2b 100644 --- a/web/app/components/workflow/workflow-preview/components/nodes/loop-start/index.tsx +++ b/web/app/components/workflow/workflow-preview/components/nodes/loop-start/index.tsx @@ -1,7 +1,7 @@ -import { memo } from 'react' -import { useTranslation } from 'react-i18next' import type { NodeProps } from 'reactflow' import { RiHome5Fill } from '@remixicon/react' +import { memo } from 'react' +import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import { NodeSourceHandle } from '../../node-handle' @@ -9,17 +9,17 @@ const LoopStartNode = ({ id, data }: NodeProps) => { const { t } = useTranslation() return ( - <div className='nodrag group mt-1 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg'> + <div className="nodrag group mt-1 flex h-11 w-11 items-center justify-center rounded-2xl border border-workflow-block-border bg-workflow-block-bg"> <Tooltip popupContent={t('workflow.blocks.loop-start')} asChild={false}> - <div className='flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500'> - <RiHome5Fill className='h-3 w-3 text-text-primary-on-surface' /> + <div className="flex h-6 w-6 items-center justify-center rounded-full border-[0.5px] border-components-panel-border-subtle bg-util-colors-blue-brand-blue-brand-500"> + <RiHome5Fill className="h-3 w-3 text-text-primary-on-surface" /> </div> </Tooltip> <NodeSourceHandle id={id} data={data} - handleClassName='!top-1/2 !-right-[9px] !-translate-y-1/2' - handleId='source' + handleClassName="!top-1/2 !-right-[9px] !-translate-y-1/2" + handleId="source" /> </div> ) diff --git a/web/app/components/workflow/workflow-preview/components/nodes/loop/hooks.ts b/web/app/components/workflow/workflow-preview/components/nodes/loop/hooks.ts index af252ce2f3..fd518c72c7 100644 --- a/web/app/components/workflow/workflow-preview/components/nodes/loop/hooks.ts +++ b/web/app/components/workflow/workflow-preview/components/nodes/loop/hooks.ts @@ -1,9 +1,9 @@ -import { useCallback } from 'react' -import { produce } from 'immer' -import { useStoreApi } from 'reactflow' import type { Node, } from '@/app/components/workflow/types' +import { produce } from 'immer' +import { useCallback } from 'react' +import { useStoreApi } from 'reactflow' import { LOOP_PADDING, } from '@/app/components/workflow/constants' diff --git a/web/app/components/workflow/workflow-preview/components/nodes/loop/node.tsx b/web/app/components/workflow/workflow-preview/components/nodes/loop/node.tsx index 9cd3eec716..2c6a05f092 100644 --- a/web/app/components/workflow/workflow-preview/components/nodes/loop/node.tsx +++ b/web/app/components/workflow/workflow-preview/components/nodes/loop/node.tsx @@ -1,4 +1,6 @@ import type { FC } from 'react' +import type { LoopNodeType } from '@/app/components/workflow/nodes/loop/types' +import type { NodeProps } from '@/app/components/workflow/types' import { memo, useEffect, @@ -8,9 +10,7 @@ import { useNodesInitialized, useViewport, } from 'reactflow' -import type { LoopNodeType } from '@/app/components/workflow/nodes/loop/types' import { cn } from '@/utils/classnames' -import type { NodeProps } from '@/app/components/workflow/types' import { useNodeLoopInteractions } from './hooks' const Node: FC<NodeProps<LoopNodeType>> = ({ @@ -36,10 +36,10 @@ const Node: FC<NodeProps<LoopNodeType>> = ({ > <Background id={`loop-background-${id}`} - className='!z-0 rounded-2xl' + className="!z-0 rounded-2xl" gap={[14 / zoom, 14 / zoom]} size={2 / zoom} - color='var(--color-workflow-canvas-workflow-dot-color)' + color="var(--color-workflow-canvas-workflow-dot-color)" /> </div> ) diff --git a/web/app/components/workflow/workflow-preview/components/nodes/question-classifier/node.tsx b/web/app/components/workflow/workflow-preview/components/nodes/question-classifier/node.tsx index 7c24ff54c3..c164d624e4 100644 --- a/web/app/components/workflow/workflow-preview/components/nodes/question-classifier/node.tsx +++ b/web/app/components/workflow/workflow-preview/components/nodes/question-classifier/node.tsx @@ -1,9 +1,9 @@ import type { FC } from 'react' +import type { NodeProps } from 'reactflow' +import type { QuestionClassifierNodeType } from '@/app/components/workflow/nodes/question-classifier/types' import React from 'react' import { useTranslation } from 'react-i18next' -import type { NodeProps } from 'reactflow' import InfoPanel from '@/app/components/workflow/nodes/_base/components/info-panel' -import type { QuestionClassifierNodeType } from '@/app/components/workflow/nodes/question-classifier/types' import { NodeSourceHandle } from '../../node-handle' const i18nPrefix = 'workflow.nodes.questionClassifiers' @@ -14,23 +14,23 @@ const Node: FC<NodeProps<QuestionClassifierNodeType>> = (props) => { const topics = data.classes return ( - <div className='mb-1 px-3 py-1'> + <div className="mb-1 px-3 py-1"> { !!topics.length && ( - <div className='mt-2 space-y-0.5'> + <div className="mt-2 space-y-0.5"> {topics.map((topic, index) => ( <div key={index} - className='relative' + className="relative" > <InfoPanel title={`${t(`${i18nPrefix}.class`)} ${index + 1}`} - content={''} + content="" /> <NodeSourceHandle {...props} handleId={topic.id} - handleClassName='!top-1/2 !-translate-y-1/2 !-right-[21px]' + handleClassName="!top-1/2 !-translate-y-1/2 !-right-[21px]" /> </div> ))} diff --git a/web/app/components/workflow/workflow-preview/components/note-node/index.tsx b/web/app/components/workflow/workflow-preview/components/note-node/index.tsx index 48390efb9c..feee0c7f2c 100644 --- a/web/app/components/workflow/workflow-preview/components/note-node/index.tsx +++ b/web/app/components/workflow/workflow-preview/components/note-node/index.tsx @@ -1,15 +1,15 @@ +import type { NodeProps } from 'reactflow' +import type { NoteNodeType } from '@/app/components/workflow/note-node/types' import { memo, useRef, } from 'react' import { useTranslation } from 'react-i18next' -import type { NodeProps } from 'reactflow' +import { THEME_MAP } from '@/app/components/workflow/note-node/constants' import { NoteEditor, NoteEditorContextProvider, } from '@/app/components/workflow/note-node/note-editor' -import { THEME_MAP } from '@/app/components/workflow/note-node/constants' -import type { NoteNodeType } from '@/app/components/workflow/note-node/types' import { cn } from '@/utils/classnames' const NoteNode = ({ @@ -41,11 +41,14 @@ const NoteNode = ({ className={cn( 'h-2 shrink-0 rounded-t-md opacity-50', THEME_MAP[theme].title, - )}></div> - <div className='grow overflow-y-auto px-3 py-2.5'> + )} + > + </div> + <div className="grow overflow-y-auto px-3 py-2.5"> <div className={cn( data.selected && 'nodrag nopan nowheel cursor-text', - )}> + )} + > <NoteEditor containerElement={ref.current} placeholder={t('workflow.nodes.note.editor.placeholder') || ''} @@ -54,7 +57,7 @@ const NoteNode = ({ </div> { data.showAuthor && ( - <div className='p-3 pt-0 text-xs text-text-tertiary'> + <div className="p-3 pt-0 text-xs text-text-tertiary"> {data.author} </div> ) 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 2322db25ee..089d42422e 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,28 +1,28 @@ import type { FC } from 'react' +import { + RiZoomInLine, + RiZoomOutLine, +} from '@remixicon/react' import { Fragment, memo, useCallback, useState, } from 'react' -import { - RiZoomInLine, - RiZoomOutLine, -} from '@remixicon/react' import { useTranslation } from 'react-i18next' import { useReactFlow, useViewport, } from 'reactflow' -import ShortcutsName from '@/app/components/workflow/shortcuts-name' import Divider from '@/app/components/base/divider' -import TipPopup from '@/app/components/workflow/operator/tip-popup' -import { cn } from '@/utils/classnames' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' +import TipPopup from '@/app/components/workflow/operator/tip-popup' +import ShortcutsName from '@/app/components/workflow/shortcuts-name' +import { cn } from '@/utils/classnames' enum ZoomType { zoomIn = 'zoomIn', @@ -103,7 +103,7 @@ const ZoomInOut: FC = () => { return ( <PortalToFollowElem - placement='top-start' + placement="top-start" open={open} onOpenChange={setOpen} offset={{ @@ -121,7 +121,8 @@ const ZoomInOut: FC = () => { > <div className={cn( 'flex h-8 w-[98px] items-center justify-between rounded-lg', - )}> + )} + > <TipPopup title={t('workflow.operator.zoomOut')} shortcuts={['ctrl', '-']} @@ -136,10 +137,13 @@ const ZoomInOut: FC = () => { zoomOut() }} > - <RiZoomOutLine className='h-4 w-4 text-text-tertiary hover:text-text-secondary' /> + <RiZoomOutLine className="h-4 w-4 text-text-tertiary hover:text-text-secondary" /> </div> </TipPopup> - <div onClick={handleTrigger} className={cn('system-sm-medium w-[34px] text-text-tertiary hover:text-text-secondary')}>{Number.parseFloat(`${zoom * 100}`).toFixed(0)}%</div> + <div onClick={handleTrigger} className={cn('system-sm-medium w-[34px] text-text-tertiary hover:text-text-secondary')}> + {Number.parseFloat(`${zoom * 100}`).toFixed(0)} + % + </div> <TipPopup title={t('workflow.operator.zoomIn')} shortcuts={['ctrl', '+']} @@ -154,32 +158,32 @@ const ZoomInOut: FC = () => { zoomIn() }} > - <RiZoomInLine className='h-4 w-4 text-text-tertiary hover:text-text-secondary' /> + <RiZoomInLine className="h-4 w-4 text-text-tertiary hover:text-text-secondary" /> </div> </TipPopup> </div> </div> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-10'> - <div className='w-[145px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[5px]'> + <PortalToFollowElemContent className="z-10"> + <div className="w-[145px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg backdrop-blur-[5px]"> { ZOOM_IN_OUT_OPTIONS.map((options, i) => ( <Fragment key={i}> { i !== 0 && ( - <Divider className='m-0' /> + <Divider className="m-0" /> ) } - <div className='p-1'> + <div className="p-1"> { options.map(option => ( <div key={option.key} - className='system-md-regular flex h-8 cursor-pointer items-center justify-between space-x-1 rounded-lg py-1.5 pl-3 pr-2 text-text-secondary hover:bg-state-base-hover' + className="system-md-regular flex h-8 cursor-pointer items-center justify-between space-x-1 rounded-lg py-1.5 pl-3 pr-2 text-text-secondary hover:bg-state-base-hover" onClick={() => handleZoom(option.key)} > <span>{option.text}</span> - <div className='flex items-center space-x-0.5'> + <div className="flex items-center space-x-0.5"> { option.key === ZoomType.zoomToFit && ( <ShortcutsName keys={['ctrl', '1']} /> diff --git a/web/app/components/workflow/workflow-preview/index.tsx b/web/app/components/workflow/workflow-preview/index.tsx index 5ce85a8804..8f61c2cfb6 100644 --- a/web/app/components/workflow/workflow-preview/index.tsx +++ b/web/app/components/workflow/workflow-preview/index.tsx @@ -1,49 +1,49 @@ 'use client' -import { - useCallback, - useState, -} from 'react' -import ReactFlow, { - Background, - MiniMap, - ReactFlowProvider, - SelectionMode, - applyEdgeChanges, - applyNodeChanges, -} from 'reactflow' import type { EdgeChange, NodeChange, Viewport, } from 'reactflow' -import 'reactflow/dist/style.css' -import '../style.css' -import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants' -import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants' -import { CUSTOM_SIMPLE_NODE } from '@/app/components/workflow/simple-node/constants' -import CustomConnectionLine from '@/app/components/workflow/custom-connection-line' +import type { + Edge, + Node, +} from '@/app/components/workflow/types' +import { + useCallback, + useState, +} from 'react' +import ReactFlow, { + applyEdgeChanges, + applyNodeChanges, + Background, + MiniMap, + ReactFlowProvider, + SelectionMode, +} from 'reactflow' import { CUSTOM_EDGE, CUSTOM_NODE, ITERATION_CHILDREN_Z_INDEX, } from '@/app/components/workflow/constants' -import { cn } from '@/utils/classnames' +import CustomConnectionLine from '@/app/components/workflow/custom-connection-line' +import { CUSTOM_ITERATION_START_NODE } from '@/app/components/workflow/nodes/iteration-start/constants' +import { CUSTOM_LOOP_START_NODE } from '@/app/components/workflow/nodes/loop-start/constants' +import { CUSTOM_NOTE_NODE } from '@/app/components/workflow/note-node/constants' +import { CUSTOM_SIMPLE_NODE } from '@/app/components/workflow/simple-node/constants' import { initialEdges, initialNodes, } from '@/app/components/workflow/utils/workflow-init' -import type { - Edge, - Node, -} from '@/app/components/workflow/types' -import { CUSTOM_NOTE_NODE } from '@/app/components/workflow/note-node/constants' -import CustomNode from './components/nodes' +import { cn } from '@/utils/classnames' import CustomEdge from './components/custom-edge' -import ZoomInOut from './components/zoom-in-out' +import CustomNode from './components/nodes' import IterationStartNode from './components/nodes/iteration-start' import LoopStartNode from './components/nodes/loop-start' import CustomNoteNode from './components/note-node' +import ZoomInOut from './components/zoom-in-out' +import 'reactflow/dist/style.css' +import '../style.css' const nodeTypes = { [CUSTOM_NODE]: CustomNode, @@ -82,7 +82,7 @@ const WorkflowPreview = ({ return ( <div - id='workflow-container' + id="workflow-container" className={cn( 'relative h-full w-full', className, @@ -96,11 +96,11 @@ const WorkflowPreview = ({ width: 102, height: 72, }} - maskColor='var(--color-workflow-minimap-bg)' - className='!absolute !bottom-14 !left-4 z-[9] !m-0 !h-[72px] !w-[102px] !rounded-lg !border-[0.5px] - !border-divider-subtle !bg-background-default-subtle !shadow-md !shadow-shadow-shadow-5' + maskColor="var(--color-workflow-minimap-bg)" + className="!absolute !bottom-14 !left-4 z-[9] !m-0 !h-[72px] !w-[102px] !rounded-lg !border-[0.5px] + !border-divider-subtle !bg-background-default-subtle !shadow-md !shadow-shadow-shadow-5" /> - <div className='absolute bottom-4 left-4 z-[9] mt-1 flex items-center gap-2'> + <div className="absolute bottom-4 left-4 z-[9] mt-1 flex items-center gap-2"> <ZoomInOut /> </div> </> @@ -128,8 +128,8 @@ const WorkflowPreview = ({ <Background gap={[14, 14]} size={2} - className='bg-workflow-canvas-workflow-bg' - color='var(--color-workflow-canvas-workflow-dot-color)' + className="bg-workflow-canvas-workflow-bg" + color="var(--color-workflow-canvas-workflow-dot-color)" /> </ReactFlow> </div> diff --git a/web/app/dev-preview/page.tsx b/web/app/dev-preview/page.tsx index e06f10d795..90090acdd0 100644 --- a/web/app/dev-preview/page.tsx +++ b/web/app/dev-preview/page.tsx @@ -4,8 +4,8 @@ import { BaseFieldType } from '../components/base/form/form-scenarios/base/types export default function Page() { return ( - <div className='flex h-screen w-full items-center justify-center p-20'> - <div className='w-[400px] rounded-lg border border-components-panel-border bg-components-panel-bg'> + <div className="flex h-screen w-full items-center justify-center p-20"> + <div className="w-[400px] rounded-lg border border-components-panel-border bg-components-panel-bg"> <BaseForm initialData={{ type: 'option_1', diff --git a/web/app/education-apply/education-apply-page.tsx b/web/app/education-apply/education-apply-page.tsx index 208b20278c..5f2446352e 100644 --- a/web/app/education-apply/education-apply-page.tsx +++ b/web/app/education-apply/education-apply-page.tsx @@ -1,30 +1,31 @@ 'use client' -import { - useState, -} from 'react' -import { useTranslation } from 'react-i18next' import { RiExternalLinkLine } from '@remixicon/react' +import { noop } from 'lodash-es' import { useRouter, useSearchParams, } from 'next/navigation' -import UserInfo from './user-info' -import SearchInput from './search-input' -import RoleSelector from './role-selector' -import Confirm from './verify-state-modal' +import { + useState, +} from 'react' +import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Checkbox from '@/app/components/base/checkbox' +import { useToastContext } from '@/app/components/base/toast' +import { EDUCATION_VERIFYING_LOCALSTORAGE_ITEM } from '@/app/education-apply/constants' +import { useDocLink } from '@/context/i18n' +import { useProviderContext } from '@/context/provider-context' import { useEducationAdd, useInvalidateEducationStatus, } from '@/service/use-education' -import { useProviderContext } from '@/context/provider-context' -import { useToastContext } from '@/app/components/base/toast' -import { EDUCATION_VERIFYING_LOCALSTORAGE_ITEM } from '@/app/education-apply/constants' -import { noop } from 'lodash-es' import DifyLogo from '../components/base/logo/dify-logo' -import { useDocLink } from '@/context/i18n' +import RoleSelector from './role-selector' +import SearchInput from './search-input' +import UserInfo from './user-info' +import Confirm from './verify-state-modal' + const EducationApplyAge = () => { const { t } = useTranslation() const [schoolName, setSchoolName] = useState('') @@ -35,7 +36,7 @@ const EducationApplyAge = () => { isPending, mutateAsync: educationAdd, } = useEducationAdd({ onSuccess: noop }) - const [modalShow, setShowModal] = useState<undefined | { title: string; desc: string; onConfirm?: () => void }>(undefined) + const [modalShow, setShowModal] = useState<undefined | { title: string, desc: string, onConfirm?: () => void }>(undefined) const { onPlanInfoChanged } = useProviderContext() const updateEducationStatus = useInvalidateEducationStatus() const { notify } = useToastContext() @@ -75,8 +76,8 @@ const EducationApplyAge = () => { } return ( - <div className='fixed inset-0 z-[31] overflow-y-auto bg-background-body p-6'> - <div className='mx-auto w-full max-w-[1408px] rounded-2xl border border-effects-highlight bg-background-default-subtle'> + <div className="fixed inset-0 z-[31] overflow-y-auto bg-background-body p-6"> + <div className="mx-auto w-full max-w-[1408px] rounded-2xl border border-effects-highlight bg-background-default-subtle"> <div className="h-[349px] w-full overflow-hidden rounded-t-2xl bg-cover bg-center bg-no-repeat" style={{ @@ -84,23 +85,25 @@ const EducationApplyAge = () => { }} > </div> - <div className='mt-[-349px] box-content flex h-7 items-center justify-between p-6'> - <DifyLogo size='large' style='monochromeWhite' /> + <div className="mt-[-349px] box-content flex h-7 items-center justify-between p-6"> + <DifyLogo size="large" style="monochromeWhite" /> </div> - <div className='mx-auto max-w-[720px] px-8 pb-[180px]'> - <div className='mb-2 flex h-[192px] flex-col justify-end pb-4 pt-3 text-text-primary-on-surface'> - <div className='title-5xl-bold mb-2 shadow-xs'>{t('education.toVerified')}</div> - <div className='system-md-medium shadow-xs'> - {t('education.toVerifiedTip.front')}  - <span className='system-md-semibold underline'>{t('education.toVerifiedTip.coupon')}</span>  + <div className="mx-auto max-w-[720px] px-8 pb-[180px]"> + <div className="mb-2 flex h-[192px] flex-col justify-end pb-4 pt-3 text-text-primary-on-surface"> + <div className="title-5xl-bold mb-2 shadow-xs">{t('education.toVerified')}</div> + <div className="system-md-medium shadow-xs"> + {t('education.toVerifiedTip.front')} +  + <span className="system-md-semibold underline">{t('education.toVerifiedTip.coupon')}</span> +  {t('education.toVerifiedTip.end')} </div> </div> - <div className='mb-7'> + <div className="mb-7"> <UserInfo /> </div> - <div className='mb-7'> - <div className='system-md-semibold mb-1 flex h-6 items-center text-text-secondary'> + <div className="mb-7"> + <div className="system-md-semibold mb-1 flex h-6 items-center text-text-secondary"> {t('education.form.schoolName.title')} </div> <SearchInput @@ -108,8 +111,8 @@ const EducationApplyAge = () => { onChange={setSchoolName} /> </div> - <div className='mb-7'> - <div className='system-md-semibold mb-1 flex h-6 items-center text-text-secondary'> + <div className="mb-7"> + <div className="system-md-semibold mb-1 flex h-6 items-center text-text-secondary"> {t('education.form.schoolRole.title')} </div> <RoleSelector @@ -117,29 +120,32 @@ const EducationApplyAge = () => { onChange={setRole} /> </div> - <div className='mb-7'> - <div className='system-md-semibold mb-1 flex h-6 items-center text-text-secondary'> + <div className="mb-7"> + <div className="system-md-semibold mb-1 flex h-6 items-center text-text-secondary"> {t('education.form.terms.title')} </div> - <div className='system-md-regular mb-1 text-text-tertiary'> - {t('education.form.terms.desc.front')}  - <a href='https://dify.ai/terms' target='_blank' className='text-text-secondary hover:underline'>{t('education.form.terms.desc.termsOfService')}</a>  - {t('education.form.terms.desc.and')}  - <a href='https://dify.ai/privacy' target='_blank' className='text-text-secondary hover:underline'>{t('education.form.terms.desc.privacyPolicy')}</a> + <div className="system-md-regular mb-1 text-text-tertiary"> + {t('education.form.terms.desc.front')} +  + <a href="https://dify.ai/terms" target="_blank" className="text-text-secondary hover:underline">{t('education.form.terms.desc.termsOfService')}</a> +  + {t('education.form.terms.desc.and')} +  + <a href="https://dify.ai/privacy" target="_blank" className="text-text-secondary hover:underline">{t('education.form.terms.desc.privacyPolicy')}</a> {t('education.form.terms.desc.end')} </div> - <div className='system-md-regular py-2 text-text-primary'> - <div className='mb-2 flex'> + <div className="system-md-regular py-2 text-text-primary"> + <div className="mb-2 flex"> <Checkbox - className='mr-2 shrink-0' + className="mr-2 shrink-0" checked={ageChecked} onCheck={() => setAgeChecked(!ageChecked)} /> {t('education.form.terms.option.age')} </div> - <div className='flex'> + <div className="flex"> <Checkbox - className='mr-2 shrink-0' + className="mr-2 shrink-0" checked={inSchoolChecked} onCheck={() => setInSchoolChecked(!inSchoolChecked)} /> @@ -148,20 +154,20 @@ const EducationApplyAge = () => { </div> </div> <Button - variant='primary' + variant="primary" disabled={!ageChecked || !inSchoolChecked || !schoolName || !role || isPending} onClick={handleSubmit} > {t('education.submit')} </Button> - <div className='mb-4 mt-5 h-px bg-gradient-to-r from-[rgba(16,24,40,0.08)]'></div> + <div className="mb-4 mt-5 h-px bg-gradient-to-r from-[rgba(16,24,40,0.08)]"></div> <a - className='system-xs-regular flex items-center text-text-accent' + className="system-xs-regular flex items-center text-text-accent" href={docLink('/getting-started/dify-for-education')} - target='_blank' + target="_blank" > {t('education.learn')} - <RiExternalLinkLine className='ml-1 h-3 w-3' /> + <RiExternalLinkLine className="ml-1 h-3 w-3" /> </a> </div> </div> diff --git a/web/app/education-apply/expire-notice-modal.tsx b/web/app/education-apply/expire-notice-modal.tsx index abafd61aa2..51a3ba66b1 100644 --- a/web/app/education-apply/expire-notice-modal.tsx +++ b/web/app/education-apply/expire-notice-modal.tsx @@ -1,16 +1,16 @@ 'use client' +import { RiExternalLinkLine } from '@remixicon/react' +import Link from 'next/link' +import { useRouter } from 'next/navigation' import React from 'react' +import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' import { useDocLink } from '@/context/i18n' -import Link from 'next/link' -import { useTranslation } from 'react-i18next' -import { RiExternalLinkLine } from '@remixicon/react' -import { SparklesSoftAccent } from '../components/base/icons/src/public/common' -import useTimestamp from '@/hooks/use-timestamp' import { useModalContextSelector } from '@/context/modal-context' +import useTimestamp from '@/hooks/use-timestamp' import { useEducationVerify } from '@/service/use-education' -import { useRouter } from 'next/navigation' +import { SparklesSoftAccent } from '../components/base/icons/src/public/common' export type ExpireNoticeModalPayloadProps = { expireAt: number @@ -46,45 +46,53 @@ const ExpireNoticeModal: React.FC<Props> = ({ expireAt, expired, onClose }) => { onClose={onClose} title={expired ? t(`${i18nPrefix}.expired.title`) : t(`${i18nPrefix}.isAboutToExpire.title`, { date: formatTime(expireAt, t(`${i18nPrefix}.dateFormat`) as string), interpolation: { escapeValue: false } })} closable - className='max-w-[600px]' + className="max-w-[600px]" > - <div className='body-md-regular mt-5 space-y-5 text-text-secondary'> + <div className="body-md-regular mt-5 space-y-5 text-text-secondary"> <div> - {expired ? (<> - <div>{t(`${i18nPrefix}.expired.summary.line1`)}</div> - <div>{t(`${i18nPrefix}.expired.summary.line2`)}</div> - </> - ) : t(`${i18nPrefix}.isAboutToExpire.summary`)} + {expired + ? ( + <> + <div>{t(`${i18nPrefix}.expired.summary.line1`)}</div> + <div>{t(`${i18nPrefix}.expired.summary.line2`)}</div> + </> + ) + : t(`${i18nPrefix}.isAboutToExpire.summary`)} </div> <div> - <strong className='title-md-semi-bold block'>{t(`${i18nPrefix}.stillInEducation.title`)}</strong> + <strong className="title-md-semi-bold block">{t(`${i18nPrefix}.stillInEducation.title`)}</strong> {t(`${i18nPrefix}.stillInEducation.${expired ? 'expired' : 'isAboutToExpire'}`)} </div> <div> - <strong className='title-md-semi-bold block'>{t(`${i18nPrefix}.alreadyGraduated.title`)}</strong> + <strong className="title-md-semi-bold block">{t(`${i18nPrefix}.alreadyGraduated.title`)}</strong> {t(`${i18nPrefix}.alreadyGraduated.${expired ? 'expired' : 'isAboutToExpire'}`)} </div> </div> <div className="mt-7 flex items-center justify-between space-x-2"> - <Link className='system-xs-regular flex items-center space-x-1 text-text-accent' href={eduDocLink} target="_blank" rel="noopener noreferrer"> + <Link className="system-xs-regular flex items-center space-x-1 text-text-accent" href={eduDocLink} target="_blank" rel="noopener noreferrer"> <div>{t('education.learn')}</div> - <RiExternalLinkLine className='size-3' /> + <RiExternalLinkLine className="size-3" /> </Link> - <div className='flex space-x-2'> - {expired ? ( - <Button onClick={() => { - onClose() - setShowPricingModal() - }} className='flex items-center space-x-1'> - <SparklesSoftAccent className='size-4' /> - <div className='text-components-button-secondary-accent-text'>{t(`${i18nPrefix}.action.upgrade`)}</div> - </Button> - ) : ( - <Button onClick={onClose}> - {t(`${i18nPrefix}.action.dismiss`)} - </Button> - )} - <Button variant='primary' onClick={handleConfirm}> + <div className="flex space-x-2"> + {expired + ? ( + <Button + onClick={() => { + onClose() + setShowPricingModal() + }} + className="flex items-center space-x-1" + > + <SparklesSoftAccent className="size-4" /> + <div className="text-components-button-secondary-accent-text">{t(`${i18nPrefix}.action.upgrade`)}</div> + </Button> + ) + : ( + <Button onClick={onClose}> + {t(`${i18nPrefix}.action.dismiss`)} + </Button> + )} + <Button variant="primary" onClick={handleConfirm}> {t(`${i18nPrefix}.action.reVerify`)} </Button> </div> diff --git a/web/app/education-apply/hooks.ts b/web/app/education-apply/hooks.ts index 9d45a5ee69..27895b89be 100644 --- a/web/app/education-apply/hooks.ts +++ b/web/app/education-apply/hooks.ts @@ -1,26 +1,25 @@ +import type { SearchParams } from './types' +import { useDebounceFn, useLocalStorageState } from 'ahooks' +import dayjs from 'dayjs' +import timezone from 'dayjs/plugin/timezone' +import utc from 'dayjs/plugin/utc' +import { useRouter, useSearchParams } from 'next/navigation' import { useCallback, useEffect, useState, } from 'react' -import { useDebounceFn, useLocalStorageState } from 'ahooks' -import { useSearchParams } from 'next/navigation' -import type { SearchParams } from './types' +import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' +import { useAppContext } from '@/context/app-context' +import { useModalContextSelector } from '@/context/modal-context' +import { useProviderContext } from '@/context/provider-context' +import { useEducationAutocomplete, useEducationVerify } from '@/service/use-education' import { EDUCATION_PRICING_SHOW_ACTION, EDUCATION_RE_VERIFY_ACTION, - EDUCATION_VERIFYING_LOCALSTORAGE_ITEM, EDUCATION_VERIFY_URL_SEARCHPARAMS_ACTION, + EDUCATION_VERIFYING_LOCALSTORAGE_ITEM, } from './constants' -import { useEducationAutocomplete, useEducationVerify } from '@/service/use-education' -import { useModalContextSelector } from '@/context/modal-context' -import dayjs from 'dayjs' -import utc from 'dayjs/plugin/utc' -import timezone from 'dayjs/plugin/timezone' -import { useAppContext } from '@/context/app-context' -import { useRouter } from 'next/navigation' -import { useProviderContext } from '@/context/provider-context' -import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' dayjs.extend(utc) dayjs.extend(timezone) diff --git a/web/app/education-apply/role-selector.tsx b/web/app/education-apply/role-selector.tsx index e6d6a67b89..1f8b3e3851 100644 --- a/web/app/education-apply/role-selector.tsx +++ b/web/app/education-apply/role-selector.tsx @@ -27,12 +27,12 @@ const RoleSelector = ({ ] return ( - <div className='flex'> + <div className="flex"> { options.map(option => ( <div key={option.key} - className='system-md-regular mr-6 flex h-5 cursor-pointer items-center text-text-primary' + className="system-md-regular mr-6 flex h-5 cursor-pointer items-center text-text-primary" onClick={() => onChange(option.key)} > <div diff --git a/web/app/education-apply/search-input.tsx b/web/app/education-apply/search-input.tsx index 63a393b326..f1be0cca30 100644 --- a/web/app/education-apply/search-input.tsx +++ b/web/app/education-apply/search-input.tsx @@ -5,13 +5,13 @@ import { useState, } from 'react' import { useTranslation } from 'react-i18next' -import { useEducation } from './hooks' import Input from '@/app/components/base/input' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' +import { useEducation } from './hooks' type SearchInputProps = { value?: string @@ -77,30 +77,30 @@ const SearchInput = ({ <PortalToFollowElem open={open} onOpenChange={setOpen} - placement='bottom' + placement="bottom" offset={4} triggerPopupSameWidth > - <PortalToFollowElemTrigger className='block w-full'> + <PortalToFollowElemTrigger className="block w-full"> <Input - className='w-full' + className="w-full" placeholder={t('education.form.schoolName.placeholder')} value={value} onChange={handleValueChange} /> </PortalToFollowElemTrigger> - <PortalToFollowElemContent className='z-[32]'> + <PortalToFollowElemContent className="z-[32]"> { !!schools.length && value && ( <div - className='max-h-[330px] overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1' + className="max-h-[330px] overflow-y-auto rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1" onScroll={handleScroll as any} > { schools.map((school, index) => ( <div key={index} - className='system-md-regular flex h-8 cursor-pointer items-center truncate rounded-lg px-2 py-1.5 text-text-secondary hover:bg-state-base-hover' + className="system-md-regular flex h-8 cursor-pointer items-center truncate rounded-lg px-2 py-1.5 text-text-secondary hover:bg-state-base-hover" title={school} onClick={() => { onChange(school) diff --git a/web/app/education-apply/user-info.tsx b/web/app/education-apply/user-info.tsx index 96ff1aaae6..4baa494c89 100644 --- a/web/app/education-apply/user-info.tsx +++ b/web/app/education-apply/user-info.tsx @@ -1,9 +1,9 @@ -import { useTranslation } from 'react-i18next' import { useRouter } from 'next/navigation' -import Button from '@/app/components/base/button' -import { useAppContext } from '@/context/app-context' +import { useTranslation } from 'react-i18next' import Avatar from '@/app/components/base/avatar' +import Button from '@/app/components/base/button' import { Triangle } from '@/app/components/base/icons/src/public/education' +import { useAppContext } from '@/context/app-context' import { useLogout } from '@/service/use-common' const UserInfo = () => { @@ -22,31 +22,31 @@ const UserInfo = () => { } return ( - <div className='relative flex items-center justify-between rounded-xl border-[4px] border-components-panel-on-panel-item-bg bg-gradient-to-r from-background-gradient-bg-fill-chat-bg-2 to-background-gradient-bg-fill-chat-bg-1 pb-6 pl-6 pr-8 pt-9 shadow-shadow-shadow-5'> - <div className='absolute left-0 top-0 flex items-center'> - <div className='system-2xs-semibold-uppercase flex h-[22px] items-center bg-components-panel-on-panel-item-bg pl-2 pt-1 text-text-accent-light-mode-only'> + <div className="relative flex items-center justify-between rounded-xl border-[4px] border-components-panel-on-panel-item-bg bg-gradient-to-r from-background-gradient-bg-fill-chat-bg-2 to-background-gradient-bg-fill-chat-bg-1 pb-6 pl-6 pr-8 pt-9 shadow-shadow-shadow-5"> + <div className="absolute left-0 top-0 flex items-center"> + <div className="system-2xs-semibold-uppercase flex h-[22px] items-center bg-components-panel-on-panel-item-bg pl-2 pt-1 text-text-accent-light-mode-only"> {t('education.currentSigned')} </div> - <Triangle className='h-[22px] w-4 text-components-panel-on-panel-item-bg' /> + <Triangle className="h-[22px] w-4 text-components-panel-on-panel-item-bg" /> </div> - <div className='flex items-center'> + <div className="flex items-center"> <Avatar - className='mr-4' + className="mr-4" avatar={userProfile.avatar_url} name={userProfile.name} size={48} /> - <div className='pt-1.5'> - <div className='system-md-semibold text-text-primary'> + <div className="pt-1.5"> + <div className="system-md-semibold text-text-primary"> {userProfile.name} </div> - <div className='system-sm-regular text-text-secondary'> + <div className="system-sm-regular text-text-secondary"> {userProfile.email} </div> </div> </div> <Button - variant='secondary' + variant="secondary" onClick={handleLogout} > {t('common.userProfile.logout')} diff --git a/web/app/education-apply/verify-state-modal.tsx b/web/app/education-apply/verify-state-modal.tsx index 2ea2fe5bae..e4a5cd9bbe 100644 --- a/web/app/education-apply/verify-state-modal.tsx +++ b/web/app/education-apply/verify-state-modal.tsx @@ -1,9 +1,9 @@ -import React, { useEffect, useRef, useState } from 'react' -import { createPortal } from 'react-dom' -import { useTranslation } from 'react-i18next' import { RiExternalLinkLine, } from '@remixicon/react' +import React, { useEffect, useRef, useState } from 'react' +import { createPortal } from 'react-dom' +import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { useDocLink } from '@/context/i18n' @@ -77,38 +77,40 @@ function Confirm({ return null return createPortal( - <div className={'fixed inset-0 z-[10000000] flex items-center justify-center bg-background-overlay'} + <div + className="fixed inset-0 z-[10000000] flex items-center justify-center bg-background-overlay" onClick={(e) => { e.preventDefault() e.stopPropagation() }} > - <div ref={dialogRef} className={'relative w-full max-w-[481px] overflow-hidden'}> - <div className='shadows-shadow-lg flex max-w-full flex-col items-start rounded-2xl border-[0.5px] border-solid border-components-panel-border bg-components-panel-bg'> - <div className='flex flex-col items-start gap-2 self-stretch pb-4 pl-6 pr-6 pt-6'> - <div className='title-2xl-semi-bold text-text-primary'>{title}</div> - <div className='system-md-regular w-full text-text-tertiary'>{content}</div> + <div ref={dialogRef} className="relative w-full max-w-[481px] overflow-hidden"> + <div className="shadows-shadow-lg flex max-w-full flex-col items-start rounded-2xl border-[0.5px] border-solid border-components-panel-border bg-components-panel-bg"> + <div className="flex flex-col items-start gap-2 self-stretch pb-4 pl-6 pr-6 pt-6"> + <div className="title-2xl-semi-bold text-text-primary">{title}</div> + <div className="system-md-regular w-full text-text-tertiary">{content}</div> </div> {email && ( - <div className='w-full space-y-1 px-6 py-3'> - <div className='system-sm-semibold py-1 text-text-secondary'>{t('education.emailLabel')}</div> - <div className='system-sm-regular rounded-lg bg-components-input-bg-disabled px-3 py-2 text-components-input-text-filled-disabled'>{email}</div> + <div className="w-full space-y-1 px-6 py-3"> + <div className="system-sm-semibold py-1 text-text-secondary">{t('education.emailLabel')}</div> + <div className="system-sm-regular rounded-lg bg-components-input-bg-disabled px-3 py-2 text-components-input-text-filled-disabled">{email}</div> </div> )} - <div className='flex items-center justify-between gap-2 self-stretch p-6'> - <div className='flex items-center gap-1'> + <div className="flex items-center justify-between gap-2 self-stretch p-6"> + <div className="flex items-center gap-1"> {showLink && ( <> - <a onClick={handleClick} href={eduDocLink} target='_blank' className='system-xs-regular cursor-pointer text-text-accent'>{t('education.learn')}</a> - <RiExternalLinkLine className='h-3 w-3 text-text-accent' /> + <a onClick={handleClick} href={eduDocLink} target="_blank" className="system-xs-regular cursor-pointer text-text-accent">{t('education.learn')}</a> + <RiExternalLinkLine className="h-3 w-3 text-text-accent" /> </> )} </div> - <Button variant='primary' className='!w-20' onClick={onConfirm}>{t('common.operation.ok')}</Button> + <Button variant="primary" className="!w-20" onClick={onConfirm}>{t('common.operation.ok')}</Button> </div> </div> </div> - </div>, document.body, + </div>, + document.body, ) } diff --git a/web/app/forgot-password/ChangePasswordForm.tsx b/web/app/forgot-password/ChangePasswordForm.tsx index 66119ea691..95362ac64b 100644 --- a/web/app/forgot-password/ChangePasswordForm.tsx +++ b/web/app/forgot-password/ChangePasswordForm.tsx @@ -1,17 +1,17 @@ 'use client' +import { CheckCircleIcon } from '@heroicons/react/24/solid' +import { useSearchParams } from 'next/navigation' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useSearchParams } from 'next/navigation' -import { basePath } from '@/utils/var' -import { cn } from '@/utils/classnames' -import { CheckCircleIcon } from '@heroicons/react/24/solid' -import Input from '../components/base/input' import Button from '@/app/components/base/button' -import { changePasswordWithToken } from '@/service/common' -import Toast from '@/app/components/base/toast' import Loading from '@/app/components/base/loading' +import Toast from '@/app/components/base/toast' import { validPassword } from '@/config' +import { changePasswordWithToken } from '@/service/common' import { useVerifyForgotPasswordToken } from '@/service/use-common' +import { cn } from '@/utils/classnames' +import { basePath } from '@/utils/var' +import Input from '../components/base/input' const ChangePasswordForm = () => { const { t } = useTranslation() @@ -79,7 +79,8 @@ const ChangePasswordForm = () => { 'px-6', 'md:px-[108px]', ) - }> + } + > {!isTokenMissing && !verifyTokenRes && <Loading />} {(isTokenMissing || (verifyTokenRes && !verifyTokenRes.is_valid)) && ( <div className="flex flex-col md:w-[400px]"> @@ -88,19 +89,19 @@ const ChangePasswordForm = () => { <h2 className="text-[32px] font-bold text-text-primary">{t('login.invalid')}</h2> </div> <div className="mx-auto mt-6 w-full"> - <Button variant='primary' className='w-full !text-sm'> + <Button variant="primary" className="w-full !text-sm"> <a href="https://dify.ai">{t('login.explore')}</a> </Button> </div> </div> )} {verifyTokenRes && verifyTokenRes.is_valid && !showSuccess && ( - <div className='flex flex-col md:w-[400px]'> + <div className="flex flex-col md:w-[400px]"> <div className="mx-auto w-full"> <h2 className="text-[32px] font-bold text-text-primary"> {t('login.changePassword')} </h2> - <p className='mt-1 text-sm text-text-secondary'> + <p className="mt-1 text-sm text-text-secondary"> {t('login.changePasswordTip')} </p> </div> @@ -108,38 +109,38 @@ const ChangePasswordForm = () => { <div className="mx-auto mt-6 w-full"> <div className="relative"> {/* Password */} - <div className='mb-5'> + <div className="mb-5"> <label htmlFor="password" className="my-2 flex items-center justify-between text-sm font-medium text-text-primary"> {t('common.account.newPassword')} </label> <Input id="password" - type='password' + type="password" value={password} onChange={e => setPassword(e.target.value)} placeholder={t('login.passwordPlaceholder') || ''} - className='mt-1' + className="mt-1" /> - <div className='mt-1 text-xs text-text-secondary'>{t('login.error.passwordInvalid')}</div> + <div className="mt-1 text-xs text-text-secondary">{t('login.error.passwordInvalid')}</div> </div> {/* Confirm Password */} - <div className='mb-5'> + <div className="mb-5"> <label htmlFor="confirmPassword" className="my-2 flex items-center justify-between text-sm font-medium text-text-primary"> {t('common.account.confirmPassword')} </label> <Input id="confirmPassword" - type='password' + type="password" value={confirmPassword} onChange={e => setConfirmPassword(e.target.value)} placeholder={t('login.confirmPasswordPlaceholder') || ''} - className='mt-1' + className="mt-1" /> </div> <div> <Button - variant='primary' - className='w-full !text-sm' + variant="primary" + className="w-full !text-sm" onClick={handleChangePassword} > {t('common.operation.reset')} @@ -153,14 +154,14 @@ const ChangePasswordForm = () => { <div className="flex flex-col md:w-[400px]"> <div className="mx-auto w-full"> <div className="mb-3 flex h-20 w-20 items-center justify-center rounded-[20px] border border-divider-regular bg-components-option-card-option-bg p-5 text-[40px] font-bold shadow-lg"> - <CheckCircleIcon className='h-10 w-10 text-[#039855]' /> + <CheckCircleIcon className="h-10 w-10 text-[#039855]" /> </div> <h2 className="text-[32px] font-bold text-text-primary"> {t('login.passwordChangedTip')} </h2> </div> <div className="mx-auto mt-6 w-full"> - <Button variant='primary' className='w-full'> + <Button variant="primary" className="w-full"> <a href={`${basePath}/signin`}>{t('login.passwordChanged')}</a> </Button> </div> diff --git a/web/app/forgot-password/ForgotPasswordForm.tsx b/web/app/forgot-password/ForgotPasswordForm.tsx index 37e6de7d5d..43aa1006d6 100644 --- a/web/app/forgot-password/ForgotPasswordForm.tsx +++ b/web/app/forgot-password/ForgotPasswordForm.tsx @@ -1,23 +1,23 @@ 'use client' -import React, { useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' +import type { InitValidateStatusResponse } from '@/models/common' +import { zodResolver } from '@hookform/resolvers/zod' import { useRouter } from 'next/navigation' +import React, { useEffect, useState } from 'react' import { useForm } from 'react-hook-form' +import { useTranslation } from 'react-i18next' import { z } from 'zod' -import { zodResolver } from '@hookform/resolvers/zod' -import Loading from '../components/base/loading' -import Input from '../components/base/input' import Button from '@/app/components/base/button' -import { basePath } from '@/utils/var' - import { fetchInitValidateStatus, fetchSetupStatus, sendForgotPasswordEmail, } from '@/service/common' -import type { InitValidateStatusResponse } from '@/models/common' +import { basePath } from '@/utils/var' + +import Input from '../components/base/input' +import Loading from '../components/base/loading' const accountFormSchema = z.object({ email: z @@ -81,42 +81,46 @@ const ForgotPasswordForm = () => { return ( loading ? <Loading /> - : <> - <div className="sm:mx-auto sm:w-full sm:max-w-md"> - <h2 className="text-[32px] font-bold text-text-primary"> - {isEmailSent ? t('login.resetLinkSent') : t('login.forgotPassword')} - </h2> - <p className='mt-1 text-sm text-text-secondary'> - {isEmailSent ? t('login.checkEmailForResetLink') : t('login.forgotPasswordDesc')} - </p> - </div> - <div className="mt-8 grow sm:mx-auto sm:w-full sm:max-w-md"> - <div className="relative"> - <form> - {!isEmailSent && ( - <div className='mb-5'> - <label htmlFor="email" - className="my-2 flex items-center justify-between text-sm font-medium text-text-primary"> - {t('login.email')} - </label> - <div className="mt-1"> - <Input - {...register('email')} - placeholder={t('login.emailPlaceholder') || ''} - /> - {errors.email && <span className='text-sm text-red-400'>{t(`${errors.email?.message}`)}</span>} + : ( + <> + <div className="sm:mx-auto sm:w-full sm:max-w-md"> + <h2 className="text-[32px] font-bold text-text-primary"> + {isEmailSent ? t('login.resetLinkSent') : t('login.forgotPassword')} + </h2> + <p className="mt-1 text-sm text-text-secondary"> + {isEmailSent ? t('login.checkEmailForResetLink') : t('login.forgotPasswordDesc')} + </p> + </div> + <div className="mt-8 grow sm:mx-auto sm:w-full sm:max-w-md"> + <div className="relative"> + <form> + {!isEmailSent && ( + <div className="mb-5"> + <label + htmlFor="email" + className="my-2 flex items-center justify-between text-sm font-medium text-text-primary" + > + {t('login.email')} + </label> + <div className="mt-1"> + <Input + {...register('email')} + placeholder={t('login.emailPlaceholder') || ''} + /> + {errors.email && <span className="text-sm text-red-400">{t(`${errors.email?.message}`)}</span>} + </div> + </div> + )} + <div> + <Button variant="primary" className="w-full" onClick={handleSendResetPasswordClick}> + {isEmailSent ? t('login.backToSignIn') : t('login.sendResetLink')} + </Button> </div> - </div> - )} - <div> - <Button variant='primary' className='w-full' onClick={handleSendResetPasswordClick}> - {isEmailSent ? t('login.backToSignIn') : t('login.sendResetLink')} - </Button> + </form> </div> - </form> - </div> - </div> - </> + </div> + </> + ) ) } diff --git a/web/app/forgot-password/page.tsx b/web/app/forgot-password/page.tsx index 3c78f734ad..4c37e096ca 100644 --- a/web/app/forgot-password/page.tsx +++ b/web/app/forgot-password/page.tsx @@ -1,12 +1,12 @@ 'use client' -import React from 'react' -import { cn } from '@/utils/classnames' import { useSearchParams } from 'next/navigation' +import React from 'react' +import ChangePasswordForm from '@/app/forgot-password/ChangePasswordForm' +import { useGlobalPublicStore } from '@/context/global-public-context' +import useDocumentTitle from '@/hooks/use-document-title' +import { cn } from '@/utils/classnames' import Header from '../signin/_header' import ForgotPasswordForm from './ForgotPasswordForm' -import ChangePasswordForm from '@/app/forgot-password/ChangePasswordForm' -import useDocumentTitle from '@/hooks/use-document-title' -import { useGlobalPublicStore } from '@/context/global-public-context' const ForgotPassword = () => { useDocumentTitle('') @@ -19,9 +19,15 @@ const ForgotPassword = () => { <div className={cn('flex w-full shrink-0 flex-col rounded-2xl border border-effects-highlight bg-background-default-subtle')}> <Header /> {token ? <ChangePasswordForm /> : <ForgotPasswordForm />} - {!systemFeatures.branding.enabled && <div className='px-8 py-6 text-sm font-normal text-text-tertiary'> - © {new Date().getFullYear()} LangGenius, Inc. All rights reserved. - </div>} + {!systemFeatures.branding.enabled && ( + <div className="px-8 py-6 text-sm font-normal text-text-tertiary"> + © + {' '} + {new Date().getFullYear()} + {' '} + LangGenius, Inc. All rights reserved. + </div> + )} </div> </div> ) diff --git a/web/app/init/InitPasswordPopup.tsx b/web/app/init/InitPasswordPopup.tsx index 6c0e9e3078..8ab6dcd0fd 100644 --- a/web/app/init/InitPasswordPopup.tsx +++ b/web/app/init/InitPasswordPopup.tsx @@ -1,14 +1,14 @@ 'use client' +import type { InitValidateStatusResponse } from '@/models/common' +import { useRouter } from 'next/navigation' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useRouter } from 'next/navigation' -import Toast from '../components/base/toast' -import Loading from '../components/base/loading' import Button from '@/app/components/base/button' -import { basePath } from '@/utils/var' -import { fetchInitValidateStatus, initValidate } from '@/service/common' -import type { InitValidateStatusResponse } from '@/models/common' import useDocumentTitle from '@/hooks/use-document-title' +import { fetchInitValidateStatus, initValidate } from '@/service/common' +import { basePath } from '@/utils/var' +import Loading from '../components/base/loading' +import Toast from '../components/base/toast' const InitPasswordPopup = () => { useDocumentTitle('') @@ -53,32 +53,34 @@ const InitPasswordPopup = () => { return ( loading ? <Loading /> - : <div> - {!validated && ( - <div className="mx-12 block min-w-28"> - <div className="mb-4"> - <label htmlFor="password" className="block text-sm font-medium text-text-secondary"> - {t('login.adminInitPassword')} + : ( + <div> + {!validated && ( + <div className="mx-12 block min-w-28"> + <div className="mb-4"> + <label htmlFor="password" className="block text-sm font-medium text-text-secondary"> + {t('login.adminInitPassword')} - </label> - <div className="relative mt-1 rounded-md shadow-sm"> - <input - id="password" - type="password" - value={password} - onChange={e => setPassword(e.target.value)} - className="block w-full appearance-none rounded-md border border-divider-regular px-3 py-2 shadow-sm placeholder:text-text-quaternary focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm" - /> + </label> + <div className="relative mt-1 rounded-md shadow-sm"> + <input + id="password" + type="password" + value={password} + onChange={e => setPassword(e.target.value)} + className="block w-full appearance-none rounded-md border border-divider-regular px-3 py-2 shadow-sm placeholder:text-text-quaternary focus:border-indigo-500 focus:outline-none focus:ring-indigo-500 sm:text-sm" + /> + </div> + </div> + <div className="flex flex-row flex-wrap justify-stretch p-0"> + <Button variant="primary" onClick={handleValidation} className="min-w-28 basis-full"> + {t('login.validate')} + </Button> + </div> </div> - </div> - <div className="flex flex-row flex-wrap justify-stretch p-0"> - <Button variant="primary" onClick={handleValidation} className="min-w-28 basis-full"> - {t('login.validate')} - </Button> - </div> + )} </div> - )} - </div> + ) ) } diff --git a/web/app/init/page.tsx b/web/app/init/page.tsx index 2842f3a739..c61457f984 100644 --- a/web/app/init/page.tsx +++ b/web/app/init/page.tsx @@ -1,6 +1,6 @@ import React from 'react' -import InitPasswordPopup from './InitPasswordPopup' import { cn } from '@/utils/classnames' +import InitPasswordPopup from './InitPasswordPopup' const Install = () => { return ( diff --git a/web/app/install/installForm.tsx b/web/app/install/installForm.tsx index 48956888fc..c3d9c1dfa6 100644 --- a/web/app/install/installForm.tsx +++ b/web/app/install/installForm.tsx @@ -1,25 +1,25 @@ 'use client' -import React, { useCallback, useEffect } from 'react' -import { useTranslation } from 'react-i18next' -import { useDebounceFn } from 'ahooks' - -import Link from 'next/link' -import { useRouter } from 'next/navigation' - import type { SubmitHandler } from 'react-hook-form' -import { useForm } from 'react-hook-form' -import { z } from 'zod' -import { zodResolver } from '@hookform/resolvers/zod' -import Loading from '../components/base/loading' -import { cn } from '@/utils/classnames' -import Button from '@/app/components/base/button' - -import { fetchInitValidateStatus, fetchSetupStatus, login, setup } from '@/service/common' import type { InitValidateStatusResponse, SetupStatusResponse } from '@/models/common' -import useDocumentTitle from '@/hooks/use-document-title' -import { useDocLink } from '@/context/i18n' +import { zodResolver } from '@hookform/resolvers/zod' + +import { useDebounceFn } from 'ahooks' +import Link from 'next/link' + +import { useRouter } from 'next/navigation' +import React, { useCallback, useEffect } from 'react' +import { useForm } from 'react-hook-form' +import { useTranslation } from 'react-i18next' +import { z } from 'zod' +import Button from '@/app/components/base/button' import { validPassword } from '@/config' +import { useDocLink } from '@/context/i18n' +import useDocumentTitle from '@/hooks/use-document-title' +import { fetchInitValidateStatus, fetchSetupStatus, login, setup } from '@/service/common' +import { cn } from '@/utils/classnames' +import Loading from '../components/base/loading' + const accountFormSchema = z.object({ email: z .string() @@ -82,7 +82,8 @@ const InstallForm = () => { } const handleSetting = async () => { - if (isSubmitting) return + if (isSubmitting) + return handleSubmit(onSubmit)() } @@ -117,89 +118,97 @@ const InstallForm = () => { return ( loading ? <Loading /> - : <> - <div className="sm:mx-auto sm:w-full sm:max-w-md"> - <h2 className="text-[32px] font-bold text-text-primary">{t('login.setAdminAccount')}</h2> - <p className='mt-1 text-sm text-text-secondary'>{t('login.setAdminAccountDesc')}</p> - </div> - <div className="mt-8 grow sm:mx-auto sm:w-full sm:max-w-md"> - <div className="relative"> - <form onSubmit={handleSubmit(onSubmit)} onKeyDown={handleKeyDown}> - <div className='mb-5'> - <label htmlFor="email" className="my-2 flex items-center justify-between text-sm font-medium text-text-primary"> - {t('login.email')} - </label> - <div className="mt-1 rounded-md shadow-sm"> - <input - {...register('email')} - placeholder={t('login.emailPlaceholder') || ''} - className={'system-sm-regular w-full appearance-none rounded-md border border-transparent bg-components-input-bg-normal px-3 py-[7px] text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs'} - /> - {errors.email && <span className='text-sm text-red-400'>{t(`${errors.email?.message}`)}</span>} - </div> - - </div> - - <div className='mb-5'> - <label htmlFor="name" className="my-2 flex items-center justify-between text-sm font-medium text-text-primary"> - {t('login.name')} - </label> - <div className="relative mt-1 rounded-md shadow-sm"> - <input - {...register('name')} - placeholder={t('login.namePlaceholder') || ''} - className={'system-sm-regular w-full appearance-none rounded-md border border-transparent bg-components-input-bg-normal px-3 py-[7px] text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs'} - /> - </div> - {errors.name && <span className='text-sm text-red-400'>{t(`${errors.name.message}`)}</span>} - </div> - - <div className='mb-5'> - <label htmlFor="password" className="my-2 flex items-center justify-between text-sm font-medium text-text-primary"> - {t('login.password')} - </label> - <div className="relative mt-1 rounded-md shadow-sm"> - <input - {...register('password')} - type={showPassword ? 'text' : 'password'} - placeholder={t('login.passwordPlaceholder') || ''} - className={'system-sm-regular w-full appearance-none rounded-md border border-transparent bg-components-input-bg-normal px-3 py-[7px] text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs'} - /> - - <div className="absolute inset-y-0 right-0 flex items-center pr-3"> - <button - type="button" - onClick={() => setShowPassword(!showPassword)} - className="text-text-quaternary hover:text-text-tertiary focus:text-text-tertiary focus:outline-none" - > - {showPassword ? '👀' : '😝'} - </button> - </div> - </div> - - <div className={cn('mt-1 text-xs text-text-secondary', { - 'text-red-400 !text-sm': errors.password, - })}>{t('login.error.passwordInvalid')}</div> - </div> - - <div> - <Button variant='primary' className='w-full' onClick={handleSetting}> - {t('login.installBtn')} - </Button> - </div> - </form> - <div className="mt-2 block w-full text-xs text-text-secondary"> - {t('login.license.tip')} -   - <Link - className='text-text-accent' - target='_blank' rel='noopener noreferrer' - href={docLink('/policies/open-source')} - >{t('login.license.link')}</Link> + : ( + <> + <div className="sm:mx-auto sm:w-full sm:max-w-md"> + <h2 className="text-[32px] font-bold text-text-primary">{t('login.setAdminAccount')}</h2> + <p className="mt-1 text-sm text-text-secondary">{t('login.setAdminAccountDesc')}</p> </div> - </div> - </div> - </> + <div className="mt-8 grow sm:mx-auto sm:w-full sm:max-w-md"> + <div className="relative"> + <form onSubmit={handleSubmit(onSubmit)} onKeyDown={handleKeyDown}> + <div className="mb-5"> + <label htmlFor="email" className="my-2 flex items-center justify-between text-sm font-medium text-text-primary"> + {t('login.email')} + </label> + <div className="mt-1 rounded-md shadow-sm"> + <input + {...register('email')} + placeholder={t('login.emailPlaceholder') || ''} + className="system-sm-regular w-full appearance-none rounded-md border border-transparent bg-components-input-bg-normal px-3 py-[7px] text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs" + /> + {errors.email && <span className="text-sm text-red-400">{t(`${errors.email?.message}`)}</span>} + </div> + + </div> + + <div className="mb-5"> + <label htmlFor="name" className="my-2 flex items-center justify-between text-sm font-medium text-text-primary"> + {t('login.name')} + </label> + <div className="relative mt-1 rounded-md shadow-sm"> + <input + {...register('name')} + placeholder={t('login.namePlaceholder') || ''} + className="system-sm-regular w-full appearance-none rounded-md border border-transparent bg-components-input-bg-normal px-3 py-[7px] text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs" + /> + </div> + {errors.name && <span className="text-sm text-red-400">{t(`${errors.name.message}`)}</span>} + </div> + + <div className="mb-5"> + <label htmlFor="password" className="my-2 flex items-center justify-between text-sm font-medium text-text-primary"> + {t('login.password')} + </label> + <div className="relative mt-1 rounded-md shadow-sm"> + <input + {...register('password')} + type={showPassword ? 'text' : 'password'} + placeholder={t('login.passwordPlaceholder') || ''} + className="system-sm-regular w-full appearance-none rounded-md border border-transparent bg-components-input-bg-normal px-3 py-[7px] text-components-input-text-filled caret-primary-600 outline-none placeholder:text-components-input-text-placeholder hover:border-components-input-border-hover hover:bg-components-input-bg-hover focus:border-components-input-border-active focus:bg-components-input-bg-active focus:shadow-xs" + /> + + <div className="absolute inset-y-0 right-0 flex items-center pr-3"> + <button + type="button" + onClick={() => setShowPassword(!showPassword)} + className="text-text-quaternary hover:text-text-tertiary focus:text-text-tertiary focus:outline-none" + > + {showPassword ? '👀' : '😝'} + </button> + </div> + </div> + + <div className={cn('mt-1 text-xs text-text-secondary', { + 'text-red-400 !text-sm': errors.password, + })} + > + {t('login.error.passwordInvalid')} + </div> + </div> + + <div> + <Button variant="primary" className="w-full" onClick={handleSetting}> + {t('login.installBtn')} + </Button> + </div> + </form> + <div className="mt-2 block w-full text-xs text-text-secondary"> + {t('login.license.tip')} +   + <Link + className="text-text-accent" + target="_blank" + rel="noopener noreferrer" + href={docLink('/policies/open-source')} + > + {t('login.license.link')} + </Link> + </div> + </div> + </div> + </> + ) ) } diff --git a/web/app/install/page.tsx b/web/app/install/page.tsx index b3cdeb5ca4..b9a770405f 100644 --- a/web/app/install/page.tsx +++ b/web/app/install/page.tsx @@ -1,9 +1,9 @@ 'use client' import React from 'react' +import { useGlobalPublicStore } from '@/context/global-public-context' +import { cn } from '@/utils/classnames' import Header from '../signin/_header' import InstallForm from './installForm' -import { cn } from '@/utils/classnames' -import { useGlobalPublicStore } from '@/context/global-public-context' const Install = () => { const { systemFeatures } = useGlobalPublicStore() @@ -12,9 +12,15 @@ const Install = () => { <div className={cn('flex w-full shrink-0 flex-col rounded-2xl border border-effects-highlight bg-background-default-subtle')}> <Header /> <InstallForm /> - {!systemFeatures.branding.enabled && <div className='px-8 py-6 text-sm font-normal text-text-tertiary'> - © {new Date().getFullYear()} LangGenius, Inc. All rights reserved. - </div>} + {!systemFeatures.branding.enabled && ( + <div className="px-8 py-6 text-sm font-normal text-text-tertiary"> + © + {' '} + {new Date().getFullYear()} + {' '} + LangGenius, Inc. All rights reserved. + </div> + )} </div> </div> ) diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 94a26eb776..25752c54a5 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -1,18 +1,18 @@ -import { ReactScan } from './components/react-scan' -import RoutePrefixHandle from './routePrefixHandle' import type { Viewport } from 'next' -import I18nServer from './components/i18n-server' -import BrowserInitializer from './components/browser-initializer' -import SentryInitializer from './components/sentry-initializer' -import { getLocaleOnServer } from '@/i18n-config/server' -import { TanstackQueryInitializer } from '@/context/query-client' import { ThemeProvider } from 'next-themes' +import { Instrument_Serif } from 'next/font/google' +import GlobalPublicStoreProvider from '@/context/global-public-context' +import { TanstackQueryInitializer } from '@/context/query-client' +import { getLocaleOnServer } from '@/i18n-config/server' +import { DatasetAttr } from '@/types/feature' +import { cn } from '@/utils/classnames' +import BrowserInitializer from './components/browser-initializer' +import I18nServer from './components/i18n-server' +import { ReactScan } from './components/react-scan' +import SentryInitializer from './components/sentry-initializer' +import RoutePrefixHandle from './routePrefixHandle' import './styles/globals.css' import './styles/markdown.scss' -import GlobalPublicStoreProvider from '@/context/global-public-context' -import { DatasetAttr } from '@/types/feature' -import { Instrument_Serif } from 'next/font/google' -import { cn } from '@/utils/classnames' export const viewport: Viewport = { width: 'device-width', @@ -85,13 +85,13 @@ const LocaleLayout = async ({ <meta name="msapplication-config" content="/browserconfig.xml" /> </head> <body - className='color-scheme h-full select-auto' + className="color-scheme h-full select-auto" {...datasetMap} > <ReactScan /> <ThemeProvider - attribute='data-theme' - defaultTheme='system' + attribute="data-theme" + defaultTheme="system" enableSystem disableTransitionOnChange enableColorScheme={false} diff --git a/web/app/page.tsx b/web/app/page.tsx index c5f5a9580b..117d6c838d 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -6,9 +6,9 @@ const Home = async () => { <div className="flex min-h-screen flex-col justify-center py-12 sm:px-6 lg:px-8"> <div className="sm:mx-auto sm:w-full sm:max-w-md"> - <Loading type='area' /> + <Loading type="area" /> <div className="mt-10 text-center"> - <Link href='/apps'>🚀</Link> + <Link href="/apps">🚀</Link> </div> </div> </div> diff --git a/web/app/repos/[owner]/[repo]/releases/route.ts b/web/app/repos/[owner]/[repo]/releases/route.ts index 29b604d94b..cc62e9d86c 100644 --- a/web/app/repos/[owner]/[repo]/releases/route.ts +++ b/web/app/repos/[owner]/[repo]/releases/route.ts @@ -1,11 +1,12 @@ -import { type NextRequest, NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' import { Octokit } from '@octokit/core' import { RequestError } from '@octokit/request-error' +import { NextResponse } from 'next/server' import { GITHUB_ACCESS_TOKEN } from '@/config' type Params = { - owner: string, - repo: string, + owner: string + repo: string } const octokit = new Octokit({ diff --git a/web/app/reset-password/check-code/page.tsx b/web/app/reset-password/check-code/page.tsx index 1d7597bf2a..fa10aec0c1 100644 --- a/web/app/reset-password/check-code/page.tsx +++ b/web/app/reset-password/check-code/page.tsx @@ -1,15 +1,15 @@ 'use client' import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import { useState } from 'react' import { useRouter, useSearchParams } from 'next/navigation' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' -import Countdown from '@/app/components/signin/countdown' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' -import { sendResetPasswordCode, verifyResetPasswordCode } from '@/service/common' +import Countdown from '@/app/components/signin/countdown' import I18NContext from '@/context/i18n' +import { sendResetPasswordCode, verifyResetPasswordCode } from '@/service/common' export default function CheckCode() { const { t } = useTranslation() @@ -63,37 +63,39 @@ export default function CheckCode() { catch (error) { console.error(error) } } - return <div className='flex flex-col gap-3'> - <div className='inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle bg-background-default-dodge text-text-accent-light-mode-only shadow-lg'> - <RiMailSendFill className='h-6 w-6 text-2xl' /> - </div> - <div className='pb-4 pt-2'> - <h2 className='title-4xl-semi-bold text-text-primary'>{t('login.checkCode.checkYourEmail')}</h2> - <p className='body-md-regular mt-2 text-text-secondary'> - <span> - {t('login.checkCode.tipsPrefix')} - <strong>{email}</strong> - </span> - <br /> - {t('login.checkCode.validTime')} - </p> - </div> - - <form action=""> - <input type='text' className='hidden' /> - <label htmlFor="code" className='system-md-semibold mb-1 text-text-secondary'>{t('login.checkCode.verificationCode')}</label> - <Input value={code} onChange={e => setVerifyCode(e.target.value)} maxLength={6} className='mt-1' placeholder={t('login.checkCode.verificationCodePlaceholder') as string} /> - <Button loading={loading} disabled={loading} className='my-3 w-full' variant='primary' onClick={verify}>{t('login.checkCode.verify')}</Button> - <Countdown onResend={resendCode} /> - </form> - <div className='py-2'> - <div className='h-px bg-gradient-to-r from-background-gradient-mask-transparent via-divider-regular to-background-gradient-mask-transparent'></div> - </div> - <div onClick={() => router.back()} className='flex h-9 cursor-pointer items-center justify-center text-text-tertiary'> - <div className='inline-block rounded-full bg-background-default-dimmed p-1'> - <RiArrowLeftLine size={12} /> + return ( + <div className="flex flex-col gap-3"> + <div className="inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle bg-background-default-dodge text-text-accent-light-mode-only shadow-lg"> + <RiMailSendFill className="h-6 w-6 text-2xl" /> + </div> + <div className="pb-4 pt-2"> + <h2 className="title-4xl-semi-bold text-text-primary">{t('login.checkCode.checkYourEmail')}</h2> + <p className="body-md-regular mt-2 text-text-secondary"> + <span> + {t('login.checkCode.tipsPrefix')} + <strong>{email}</strong> + </span> + <br /> + {t('login.checkCode.validTime')} + </p> + </div> + + <form action=""> + <input type="text" className="hidden" /> + <label htmlFor="code" className="system-md-semibold mb-1 text-text-secondary">{t('login.checkCode.verificationCode')}</label> + <Input value={code} onChange={e => setVerifyCode(e.target.value)} maxLength={6} className="mt-1" placeholder={t('login.checkCode.verificationCodePlaceholder') as string} /> + <Button loading={loading} disabled={loading} className="my-3 w-full" variant="primary" onClick={verify}>{t('login.checkCode.verify')}</Button> + <Countdown onResend={resendCode} /> + </form> + <div className="py-2"> + <div className="h-px bg-gradient-to-r from-background-gradient-mask-transparent via-divider-regular to-background-gradient-mask-transparent"></div> + </div> + <div onClick={() => router.back()} className="flex h-9 cursor-pointer items-center justify-center text-text-tertiary"> + <div className="inline-block rounded-full bg-background-default-dimmed p-1"> + <RiArrowLeftLine size={12} /> + </div> + <span className="system-xs-regular ml-2">{t('login.back')}</span> </div> - <span className='system-xs-regular ml-2'>{t('login.back')}</span> </div> - </div> + ) } diff --git a/web/app/reset-password/layout.tsx b/web/app/reset-password/layout.tsx index 724873f30f..d9b665501d 100644 --- a/web/app/reset-password/layout.tsx +++ b/web/app/reset-password/layout.tsx @@ -1,30 +1,39 @@ 'use client' -import Header from '../signin/_header' +import { useGlobalPublicStore } from '@/context/global-public-context' import { cn } from '@/utils/classnames' -import { useGlobalPublicStore } from '@/context/global-public-context' +import Header from '../signin/_header' export default function SignInLayout({ children }: any) { const { systemFeatures } = useGlobalPublicStore() - return <> - <div className={cn('flex min-h-screen w-full justify-center bg-background-default-burn p-6')}> - <div className={cn('flex w-full shrink-0 flex-col rounded-2xl border border-effects-highlight bg-background-default-subtle')}> - <Header /> - <div className={ - cn( - 'flex w-full grow flex-col items-center justify-center', - 'px-6', - 'md:px-[108px]', - ) - }> - <div className='flex flex-col md:w-[400px]'> - {children} + return ( + <> + <div className={cn('flex min-h-screen w-full justify-center bg-background-default-burn p-6')}> + <div className={cn('flex w-full shrink-0 flex-col rounded-2xl border border-effects-highlight bg-background-default-subtle')}> + <Header /> + <div className={ + cn( + 'flex w-full grow flex-col items-center justify-center', + 'px-6', + 'md:px-[108px]', + ) + } + > + <div className="flex flex-col md:w-[400px]"> + {children} + </div> </div> + {!systemFeatures.branding.enabled && ( + <div className="system-xs-regular px-8 py-6 text-text-tertiary"> + © + {' '} + {new Date().getFullYear()} + {' '} + LangGenius, Inc. All rights reserved. + </div> + )} </div> - {!systemFeatures.branding.enabled && <div className='system-xs-regular px-8 py-6 text-text-tertiary'> - © {new Date().getFullYear()} LangGenius, Inc. All rights reserved. - </div>} </div> - </div> - </> + </> + ) } diff --git a/web/app/reset-password/page.tsx b/web/app/reset-password/page.tsx index 11dfd07e5c..c7e15f8b3f 100644 --- a/web/app/reset-password/page.tsx +++ b/web/app/reset-password/page.tsx @@ -1,19 +1,19 @@ 'use client' -import Link from 'next/link' import { RiArrowLeftLine, RiLockPasswordLine } from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import { useState } from 'react' +import { noop } from 'lodash-es' +import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' -import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '../components/signin/countdown' -import { emailRegex } from '@/config' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' -import { sendResetPasswordCode } from '@/service/common' +import { emailRegex } from '@/config' import I18NContext from '@/context/i18n' -import { noop } from 'lodash-es' import useDocumentTitle from '@/hooks/use-document-title' +import { sendResetPasswordCode } from '@/service/common' +import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '../components/signin/countdown' export default function CheckCode() { const { t } = useTranslation() @@ -62,37 +62,39 @@ export default function CheckCode() { } } - return <div className='flex flex-col gap-3'> - <div className='inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle bg-background-default-dodge shadow-lg'> - <RiLockPasswordLine className='h-6 w-6 text-2xl text-text-accent-light-mode-only' /> - </div> - <div className='pb-4 pt-2'> - <h2 className='title-4xl-semi-bold text-text-primary'>{t('login.resetPassword')}</h2> - <p className='body-md-regular mt-2 text-text-secondary'> - {t('login.resetPasswordDesc')} - </p> - </div> + return ( + <div className="flex flex-col gap-3"> + <div className="inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle bg-background-default-dodge shadow-lg"> + <RiLockPasswordLine className="h-6 w-6 text-2xl text-text-accent-light-mode-only" /> + </div> + <div className="pb-4 pt-2"> + <h2 className="title-4xl-semi-bold text-text-primary">{t('login.resetPassword')}</h2> + <p className="body-md-regular mt-2 text-text-secondary"> + {t('login.resetPasswordDesc')} + </p> + </div> - <form onSubmit={noop}> - <input type='text' className='hidden' /> - <div className='mb-2'> - <label htmlFor="email" className='system-md-semibold my-2 text-text-secondary'>{t('login.email')}</label> - <div className='mt-1'> - <Input id='email' type="email" disabled={loading} value={email} placeholder={t('login.emailPlaceholder') as string} onChange={e => setEmail(e.target.value)} /> - </div> - <div className='mt-3'> - <Button loading={loading} disabled={loading} variant='primary' className='w-full' onClick={handleGetEMailVerificationCode}>{t('login.sendVerificationCode')}</Button> + <form onSubmit={noop}> + <input type="text" className="hidden" /> + <div className="mb-2"> + <label htmlFor="email" className="system-md-semibold my-2 text-text-secondary">{t('login.email')}</label> + <div className="mt-1"> + <Input id="email" type="email" disabled={loading} value={email} placeholder={t('login.emailPlaceholder') as string} onChange={e => setEmail(e.target.value)} /> + </div> + <div className="mt-3"> + <Button loading={loading} disabled={loading} variant="primary" className="w-full" onClick={handleGetEMailVerificationCode}>{t('login.sendVerificationCode')}</Button> + </div> </div> + </form> + <div className="py-2"> + <div className="h-px bg-gradient-to-r from-background-gradient-mask-transparent via-divider-regular to-background-gradient-mask-transparent"></div> </div> - </form> - <div className='py-2'> - <div className='h-px bg-gradient-to-r from-background-gradient-mask-transparent via-divider-regular to-background-gradient-mask-transparent'></div> + <Link href={`/signin?${searchParams.toString()}`} className="flex h-9 items-center justify-center text-text-tertiary hover:text-text-primary"> + <div className="inline-block rounded-full bg-background-default-dimmed p-1"> + <RiArrowLeftLine size={12} /> + </div> + <span className="system-xs-regular ml-2">{t('login.backToLogin')}</span> + </Link> </div> - <Link href={`/signin?${searchParams.toString()}`} className='flex h-9 items-center justify-center text-text-tertiary hover:text-text-primary'> - <div className='inline-block rounded-full bg-background-default-dimmed p-1'> - <RiArrowLeftLine size={12} /> - </div> - <span className='system-xs-regular ml-2'>{t('login.backToLogin')}</span> - </Link> - </div> + ) } diff --git a/web/app/reset-password/set-password/page.tsx b/web/app/reset-password/set-password/page.tsx index 30950a3dff..827bb24f73 100644 --- a/web/app/reset-password/set-password/page.tsx +++ b/web/app/reset-password/set-password/page.tsx @@ -1,15 +1,15 @@ 'use client' -import { useCallback, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { useRouter, useSearchParams } from 'next/navigation' -import { cn } from '@/utils/classnames' import { RiCheckboxCircleFill } from '@remixicon/react' import { useCountDown } from 'ahooks' +import { useRouter, useSearchParams } from 'next/navigation' +import { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' -import { changePasswordWithToken } from '@/service/common' -import Toast from '@/app/components/base/toast' import Input from '@/app/components/base/input' +import Toast from '@/app/components/base/toast' import { validPassword } from '@/config' +import { changePasswordWithToken } from '@/service/common' +import { cn } from '@/utils/classnames' const ChangePasswordForm = () => { const { t } = useTranslation() @@ -91,14 +91,15 @@ const ChangePasswordForm = () => { 'px-6', 'md:px-[108px]', ) - }> + } + > {!showSuccess && ( - <div className='flex flex-col md:w-[400px]'> + <div className="flex flex-col md:w-[400px]"> <div className="mx-auto w-full"> <h2 className="title-4xl-semi-bold text-text-primary"> {t('login.changePassword')} </h2> - <p className='body-md-regular mt-2 text-text-secondary'> + <p className="body-md-regular mt-2 text-text-secondary"> {t('login.changePasswordTip')} </p> </div> @@ -106,13 +107,14 @@ const ChangePasswordForm = () => { <div className="mx-auto mt-6 w-full"> <div> {/* Password */} - <div className='mb-5'> + <div className="mb-5"> <label htmlFor="password" className="system-md-semibold my-2 text-text-secondary"> {t('common.account.newPassword')} </label> - <div className='relative mt-1'> + <div className="relative mt-1"> <Input - id="password" type={showPassword ? 'text' : 'password'} + id="password" + type={showPassword ? 'text' : 'password'} value={password} onChange={e => setPassword(e.target.value)} placeholder={t('login.passwordPlaceholder') || ''} @@ -121,21 +123,21 @@ const ChangePasswordForm = () => { <div className="absolute inset-y-0 right-0 flex items-center"> <Button type="button" - variant='ghost' + variant="ghost" onClick={() => setShowPassword(!showPassword)} > {showPassword ? '👀' : '😝'} </Button> </div> </div> - <div className='body-xs-regular mt-1 text-text-secondary'>{t('login.error.passwordInvalid')}</div> + <div className="body-xs-regular mt-1 text-text-secondary">{t('login.error.passwordInvalid')}</div> </div> {/* Confirm Password */} - <div className='mb-5'> + <div className="mb-5"> <label htmlFor="confirmPassword" className="system-md-semibold my-2 text-text-secondary"> {t('common.account.confirmPassword')} </label> - <div className='relative mt-1'> + <div className="relative mt-1"> <Input id="confirmPassword" type={showConfirmPassword ? 'text' : 'password'} @@ -146,7 +148,7 @@ const ChangePasswordForm = () => { <div className="absolute inset-y-0 right-0 flex items-center"> <Button type="button" - variant='ghost' + variant="ghost" onClick={() => setShowConfirmPassword(!showConfirmPassword)} > {showConfirmPassword ? '👀' : '😝'} @@ -156,8 +158,8 @@ const ChangePasswordForm = () => { </div> <div> <Button - variant='primary' - className='w-full' + variant="primary" + className="w-full" onClick={handleChangePassword} > {t('login.changePasswordBtn')} @@ -171,17 +173,28 @@ const ChangePasswordForm = () => { <div className="flex flex-col md:w-[400px]"> <div className="mx-auto w-full"> <div className="mb-3 flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle font-bold shadow-lg"> - <RiCheckboxCircleFill className='h-6 w-6 text-text-success' /> + <RiCheckboxCircleFill className="h-6 w-6 text-text-success" /> </div> <h2 className="title-4xl-semi-bold text-text-primary"> {t('login.passwordChangedTip')} </h2> </div> <div className="mx-auto mt-6 w-full"> - <Button variant='primary' className='w-full' onClick={() => { - setLeftTime(undefined) - router.replace(getSignInUrl()) - }}>{t('login.passwordChanged')} ({Math.round(countdown / 1000)}) </Button> + <Button + variant="primary" + className="w-full" + onClick={() => { + setLeftTime(undefined) + router.replace(getSignInUrl()) + }} + > + {t('login.passwordChanged')} + {' '} + ( + {Math.round(countdown / 1000)} + ) + {' '} + </Button> </div> </div> )} diff --git a/web/app/routePrefixHandle.tsx b/web/app/routePrefixHandle.tsx index 464910782d..d3a36a51fc 100644 --- a/web/app/routePrefixHandle.tsx +++ b/web/app/routePrefixHandle.tsx @@ -1,8 +1,8 @@ 'use client' -import { basePath } from '@/utils/var' -import { useEffect } from 'react' import { usePathname } from 'next/navigation' +import { useEffect } from 'react' +import { basePath } from '@/utils/var' export default function RoutePrefixHandle() { const pathname = usePathname() diff --git a/web/app/signin/_header.tsx b/web/app/signin/_header.tsx index 731a229b8e..5ef24cd03e 100644 --- a/web/app/signin/_header.tsx +++ b/web/app/signin/_header.tsx @@ -1,22 +1,22 @@ 'use client' +import type { Locale } from '@/i18n-config' +import dynamic from 'next/dynamic' import React from 'react' import { useContext } from 'use-context-selector' -import LocaleSigninSelect from '@/app/components/base/select/locale-signin' import Divider from '@/app/components/base/divider' -import { languages } from '@/i18n-config/language' -import type { Locale } from '@/i18n-config' -import I18n from '@/context/i18n' -import dynamic from 'next/dynamic' +import LocaleSigninSelect from '@/app/components/base/select/locale-signin' import { useGlobalPublicStore } from '@/context/global-public-context' +import I18n from '@/context/i18n' +import { languages } from '@/i18n-config/language' // Avoid rendering the logo and theme selector on the server const DifyLogo = dynamic(() => import('@/app/components/base/logo/dify-logo'), { ssr: false, - loading: () => <div className='h-7 w-16 bg-transparent' />, + loading: () => <div className="h-7 w-16 bg-transparent" />, }) const ThemeSelector = dynamic(() => import('@/app/components/base/theme-selector'), { ssr: false, - loading: () => <div className='size-8 bg-transparent' />, + loading: () => <div className="size-8 bg-transparent" />, }) const Header = () => { @@ -24,15 +24,17 @@ const Header = () => { const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) return ( - <div className='flex w-full items-center justify-between p-6'> + <div className="flex w-full items-center justify-between p-6"> {systemFeatures.branding.enabled && systemFeatures.branding.login_page_logo - ? <img - src={systemFeatures.branding.login_page_logo} - className='block h-7 w-auto object-contain' - alt='logo' - /> - : <DifyLogo size='large' />} - <div className='flex items-center gap-1'> + ? ( + <img + src={systemFeatures.branding.login_page_logo} + className="block h-7 w-auto object-contain" + alt="logo" + /> + ) + : <DifyLogo size="large" />} + <div className="flex items-center gap-1"> <LocaleSigninSelect value={locale} items={languages.filter(item => item.supported)} @@ -40,7 +42,7 @@ const Header = () => { setLocaleOnClient(value as Locale) }} /> - <Divider type='vertical' className='mx-0 ml-2 h-4' /> + <Divider type="vertical" className="mx-0 ml-2 h-4" /> <ThemeSelector /> </div> </div> diff --git a/web/app/signin/check-code/page.tsx b/web/app/signin/check-code/page.tsx index 36c3c67a58..f0842e8c0d 100644 --- a/web/app/signin/check-code/page.tsx +++ b/web/app/signin/check-code/page.tsx @@ -1,18 +1,19 @@ 'use client' +import type { FormEvent } from 'react' import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import { type FormEvent, useEffect, useRef, useState } from 'react' import { useRouter, useSearchParams } from 'next/navigation' +import { useEffect, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' -import Countdown from '@/app/components/signin/countdown' +import { trackEvent } from '@/app/components/base/amplitude' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' -import { emailLoginWithCode, sendEMailLoginCode } from '@/service/common' +import Countdown from '@/app/components/signin/countdown' import I18NContext from '@/context/i18n' -import { resolvePostLoginRedirect } from '../utils/post-login-redirect' -import { trackEvent } from '@/app/components/base/amplitude' +import { emailLoginWithCode, sendEMailLoginCode } from '@/service/common' import { encryptVerificationCode } from '@/utils/encryption' +import { resolvePostLoginRedirect } from '../utils/post-login-redirect' export default function CheckCode() { const { t, i18n } = useTranslation() @@ -88,44 +89,46 @@ export default function CheckCode() { catch (error) { console.error(error) } } - return <div className='flex flex-col gap-3'> - <div className='inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle bg-background-default-dodge shadow-lg'> - <RiMailSendFill className='h-6 w-6 text-2xl text-text-accent-light-mode-only' /> - </div> - <div className='pb-4 pt-2'> - <h2 className='title-4xl-semi-bold text-text-primary'>{t('login.checkCode.checkYourEmail')}</h2> - <p className='body-md-regular mt-2 text-text-secondary'> - <span> - {t('login.checkCode.tipsPrefix')} - <strong>{email}</strong> - </span> - <br /> - {t('login.checkCode.validTime')} - </p> - </div> - - <form onSubmit={handleSubmit}> - <label htmlFor="code" className='system-md-semibold mb-1 text-text-secondary'>{t('login.checkCode.verificationCode')}</label> - <Input - ref={codeInputRef} - id='code' - value={code} - onChange={e => setVerifyCode(e.target.value)} - maxLength={6} - className='mt-1' - placeholder={t('login.checkCode.verificationCodePlaceholder') as string} - /> - <Button type='submit' loading={loading} disabled={loading} className='my-3 w-full' variant='primary'>{t('login.checkCode.verify')}</Button> - <Countdown onResend={resendCode} /> - </form> - <div className='py-2'> - <div className='h-px bg-gradient-to-r from-background-gradient-mask-transparent via-divider-regular to-background-gradient-mask-transparent'></div> - </div> - <div onClick={() => router.back()} className='flex h-9 cursor-pointer items-center justify-center text-text-tertiary'> - <div className='inline-block rounded-full bg-background-default-dimmed p-1'> - <RiArrowLeftLine size={12} /> + return ( + <div className="flex flex-col gap-3"> + <div className="inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle bg-background-default-dodge shadow-lg"> + <RiMailSendFill className="h-6 w-6 text-2xl text-text-accent-light-mode-only" /> + </div> + <div className="pb-4 pt-2"> + <h2 className="title-4xl-semi-bold text-text-primary">{t('login.checkCode.checkYourEmail')}</h2> + <p className="body-md-regular mt-2 text-text-secondary"> + <span> + {t('login.checkCode.tipsPrefix')} + <strong>{email}</strong> + </span> + <br /> + {t('login.checkCode.validTime')} + </p> + </div> + + <form onSubmit={handleSubmit}> + <label htmlFor="code" className="system-md-semibold mb-1 text-text-secondary">{t('login.checkCode.verificationCode')}</label> + <Input + ref={codeInputRef} + id="code" + value={code} + onChange={e => setVerifyCode(e.target.value)} + maxLength={6} + className="mt-1" + placeholder={t('login.checkCode.verificationCodePlaceholder') as string} + /> + <Button type="submit" loading={loading} disabled={loading} className="my-3 w-full" variant="primary">{t('login.checkCode.verify')}</Button> + <Countdown onResend={resendCode} /> + </form> + <div className="py-2"> + <div className="h-px bg-gradient-to-r from-background-gradient-mask-transparent via-divider-regular to-background-gradient-mask-transparent"></div> + </div> + <div onClick={() => router.back()} className="flex h-9 cursor-pointer items-center justify-center text-text-tertiary"> + <div className="inline-block rounded-full bg-background-default-dimmed p-1"> + <RiArrowLeftLine size={12} /> + </div> + <span className="system-xs-regular ml-2">{t('login.back')}</span> </div> - <span className='system-xs-regular ml-2'>{t('login.back')}</span> </div> - </div> + ) } diff --git a/web/app/signin/components/mail-and-code-auth.tsx b/web/app/signin/components/mail-and-code-auth.tsx index 002aaaf4ad..64ebc43a73 100644 --- a/web/app/signin/components/mail-and-code-auth.tsx +++ b/web/app/signin/components/mail-and-code-auth.tsx @@ -1,14 +1,15 @@ -import { type FormEvent, useState } from 'react' -import { useTranslation } from 'react-i18next' +import type { FormEvent } from 'react' import { useRouter, useSearchParams } from 'next/navigation' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' -import Input from '@/app/components/base/input' import Button from '@/app/components/base/button' -import { emailRegex } from '@/config' +import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' -import { sendEMailLoginCode } from '@/service/common' import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown' +import { emailRegex } from '@/config' import I18NContext from '@/context/i18n' +import { sendEMailLoginCode } from '@/service/common' type MailAndCodeAuthProps = { isInvite: boolean @@ -60,17 +61,18 @@ export default function MailAndCodeAuth({ isInvite }: MailAndCodeAuthProps) { handleGetEMailVerificationCode() } - return (<form onSubmit={handleSubmit}> - <input type='text' className='hidden' /> - <div className='mb-2'> - <label htmlFor="email" className='system-md-semibold my-2 text-text-secondary'>{t('login.email')}</label> - <div className='mt-1'> - <Input id='email' type="email" disabled={isInvite} value={email} placeholder={t('login.emailPlaceholder') as string} onChange={e => setEmail(e.target.value)} /> + return ( + <form onSubmit={handleSubmit}> + <input type="text" className="hidden" /> + <div className="mb-2"> + <label htmlFor="email" className="system-md-semibold my-2 text-text-secondary">{t('login.email')}</label> + <div className="mt-1"> + <Input id="email" type="email" disabled={isInvite} value={email} placeholder={t('login.emailPlaceholder') as string} onChange={e => setEmail(e.target.value)} /> + </div> + <div className="mt-3"> + <Button type="submit" loading={loading} disabled={loading || !email} variant="primary" className="w-full">{t('login.signup.verifyMail')}</Button> + </div> </div> - <div className='mt-3'> - <Button type='submit' loading={loading} disabled={loading || !email} variant='primary' className='w-full'>{t('login.signup.verifyMail')}</Button> - </div> - </div> - </form> + </form> ) } diff --git a/web/app/signin/components/mail-and-password-auth.tsx b/web/app/signin/components/mail-and-password-auth.tsx index 27c37e3e26..9ab2d9314c 100644 --- a/web/app/signin/components/mail-and-password-auth.tsx +++ b/web/app/signin/components/mail-and-password-auth.tsx @@ -1,19 +1,19 @@ +import type { ResponseError } from '@/service/fetch' +import { noop } from 'lodash-es' import Link from 'next/link' +import { useRouter, useSearchParams } from 'next/navigation' import { useState } from 'react' import { useTranslation } from 'react-i18next' -import { useRouter, useSearchParams } from 'next/navigation' import { useContext } from 'use-context-selector' +import { trackEvent } from '@/app/components/base/amplitude' import Button from '@/app/components/base/button' +import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' import { emailRegex } from '@/config' -import { login } from '@/service/common' -import Input from '@/app/components/base/input' import I18NContext from '@/context/i18n' -import { noop } from 'lodash-es' -import { resolvePostLoginRedirect } from '../utils/post-login-redirect' -import type { ResponseError } from '@/service/fetch' -import { trackEvent } from '@/app/components/base/amplitude' +import { login } from '@/service/common' import { encryptPassword } from '@/utils/encryption' +import { resolvePostLoginRedirect } from '../utils/post-login-redirect' type MailAndPasswordAuthProps = { isInvite: boolean @@ -99,71 +99,75 @@ export default function MailAndPasswordAuth({ isInvite, isEmailSetup, allowRegis } } - return <form onSubmit={noop}> - <div className='mb-3'> - <label htmlFor="email" className="system-md-semibold my-2 text-text-secondary"> - {t('login.email')} - </label> - <div className="mt-1"> - <Input - value={email} - onChange={e => setEmail(e.target.value)} - disabled={isInvite} - id="email" - type="email" - autoComplete="email" - placeholder={t('login.emailPlaceholder') || ''} - tabIndex={1} - /> - </div> - </div> - - <div className='mb-3'> - <label htmlFor="password" className="my-2 flex items-center justify-between"> - <span className='system-md-semibold text-text-secondary'>{t('login.password')}</span> - <Link - href={`/reset-password?${searchParams.toString()}`} - className={`system-xs-regular ${isEmailSetup ? 'text-components-button-secondary-accent-text' : 'pointer-events-none text-components-button-secondary-accent-text-disabled'}`} - tabIndex={isEmailSetup ? 0 : -1} - aria-disabled={!isEmailSetup} - > - {t('login.forget')} - </Link> - </label> - <div className="relative mt-1"> - <Input - id="password" - value={password} - onChange={e => setPassword(e.target.value)} - onKeyDown={(e) => { - if (e.key === 'Enter') - handleEmailPasswordLogin() - }} - type={showPassword ? 'text' : 'password'} - autoComplete="current-password" - placeholder={t('login.passwordPlaceholder') || ''} - tabIndex={2} - /> - <div className="absolute inset-y-0 right-0 flex items-center"> - <Button - type="button" - variant='ghost' - onClick={() => setShowPassword(!showPassword)} - > - {showPassword ? '👀' : '😝'} - </Button> + return ( + <form onSubmit={noop}> + <div className="mb-3"> + <label htmlFor="email" className="system-md-semibold my-2 text-text-secondary"> + {t('login.email')} + </label> + <div className="mt-1"> + <Input + value={email} + onChange={e => setEmail(e.target.value)} + disabled={isInvite} + id="email" + type="email" + autoComplete="email" + placeholder={t('login.emailPlaceholder') || ''} + tabIndex={1} + /> </div> </div> - </div> - <div className='mb-2'> - <Button - tabIndex={2} - variant='primary' - onClick={handleEmailPasswordLogin} - disabled={isLoading || !email || !password} - className="w-full" - >{t('login.signBtn')}</Button> - </div> - </form> + <div className="mb-3"> + <label htmlFor="password" className="my-2 flex items-center justify-between"> + <span className="system-md-semibold text-text-secondary">{t('login.password')}</span> + <Link + href={`/reset-password?${searchParams.toString()}`} + className={`system-xs-regular ${isEmailSetup ? 'text-components-button-secondary-accent-text' : 'pointer-events-none text-components-button-secondary-accent-text-disabled'}`} + tabIndex={isEmailSetup ? 0 : -1} + aria-disabled={!isEmailSetup} + > + {t('login.forget')} + </Link> + </label> + <div className="relative mt-1"> + <Input + id="password" + value={password} + onChange={e => setPassword(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') + handleEmailPasswordLogin() + }} + type={showPassword ? 'text' : 'password'} + autoComplete="current-password" + placeholder={t('login.passwordPlaceholder') || ''} + tabIndex={2} + /> + <div className="absolute inset-y-0 right-0 flex items-center"> + <Button + type="button" + variant="ghost" + onClick={() => setShowPassword(!showPassword)} + > + {showPassword ? '👀' : '😝'} + </Button> + </div> + </div> + </div> + + <div className="mb-2"> + <Button + tabIndex={2} + variant="primary" + onClick={handleEmailPasswordLogin} + disabled={isLoading || !email || !password} + className="w-full" + > + {t('login.signBtn')} + </Button> + </div> + </form> + ) } diff --git a/web/app/signin/components/social-auth.tsx b/web/app/signin/components/social-auth.tsx index 1afac0809b..a1393e1fa5 100644 --- a/web/app/signin/components/social-auth.tsx +++ b/web/app/signin/components/social-auth.tsx @@ -1,10 +1,10 @@ -import { useTranslation } from 'react-i18next' import { useSearchParams } from 'next/navigation' -import style from '../page.module.css' +import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { API_PREFIX } from '@/config' -import { cn } from '@/utils/classnames' import { getPurifyHref } from '@/utils' +import { cn } from '@/utils/classnames' +import style from '../page.module.css' type SocialAuthProps = { disabled?: boolean @@ -21,38 +21,40 @@ export default function SocialAuth(props: SocialAuthProps) { return url } - return <> - <div className='w-full'> - <a href={getOAuthLink('/oauth/login/github')}> - <Button - disabled={props.disabled} - className='w-full' - > - <> - <span className={ - cn(style.githubIcon, - 'mr-2 h-5 w-5') - } /> - <span className="truncate leading-normal">{t('login.withGitHub')}</span> - </> - </Button> - </a> - </div> - <div className='w-full'> - <a href={getOAuthLink('/oauth/login/google')}> - <Button - disabled={props.disabled} - className='w-full' - > - <> - <span className={ - cn(style.googleIcon, - 'mr-2 h-5 w-5') - } /> - <span className="truncate leading-normal">{t('login.withGoogle')}</span> - </> - </Button> - </a> - </div> - </> + return ( + <> + <div className="w-full"> + <a href={getOAuthLink('/oauth/login/github')}> + <Button + disabled={props.disabled} + className="w-full" + > + <> + <span className={ + cn(style.githubIcon, 'mr-2 h-5 w-5') + } + /> + <span className="truncate leading-normal">{t('login.withGitHub')}</span> + </> + </Button> + </a> + </div> + <div className="w-full"> + <a href={getOAuthLink('/oauth/login/google')}> + <Button + disabled={props.disabled} + className="w-full" + > + <> + <span className={ + cn(style.googleIcon, 'mr-2 h-5 w-5') + } + /> + <span className="truncate leading-normal">{t('login.withGoogle')}</span> + </> + </Button> + </a> + </div> + </> + ) } diff --git a/web/app/signin/components/sso-auth.tsx b/web/app/signin/components/sso-auth.tsx index bb98eb2878..6b4c553f77 100644 --- a/web/app/signin/components/sso-auth.tsx +++ b/web/app/signin/components/sso-auth.tsx @@ -1,12 +1,12 @@ 'use client' -import { useRouter, useSearchParams } from 'next/navigation' import type { FC } from 'react' +import { useRouter, useSearchParams } from 'next/navigation' import { useState } from 'react' import { useTranslation } from 'react-i18next' +import Button from '@/app/components/base/button' import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security' import Toast from '@/app/components/base/toast' import { getUserOAuth2SSOUrl, getUserOIDCSSOUrl, getUserSAMLSSOUrl } from '@/service/sso' -import Button from '@/app/components/base/button' import { SSOProtocol } from '@/types/feature' type SSOAuthProps = { @@ -64,7 +64,7 @@ const SSOAuth: FC<SSOAuthProps> = ({ disabled={isLoading} className="w-full" > - <Lock01 className='mr-2 h-5 w-5 text-text-accent-light-mode-only' /> + <Lock01 className="mr-2 h-5 w-5 text-text-accent-light-mode-only" /> <span className="truncate">{t('login.withSSO')}</span> </Button> ) diff --git a/web/app/signin/invite-settings/page.tsx b/web/app/signin/invite-settings/page.tsx index de8d6c60ea..9abd4366e1 100644 --- a/web/app/signin/invite-settings/page.tsx +++ b/web/app/signin/invite-settings/page.tsx @@ -1,24 +1,23 @@ 'use client' -import { useTranslation } from 'react-i18next' -import { useDocLink } from '@/context/i18n' -import { useCallback, useState } from 'react' -import Link from 'next/link' -import { useContext } from 'use-context-selector' -import { useRouter, useSearchParams } from 'next/navigation' import { RiAccountCircleLine } from '@remixicon/react' -import Input from '@/app/components/base/input' -import { SimpleSelect } from '@/app/components/base/select' -import Button from '@/app/components/base/button' -import { timezones } from '@/utils/timezone' -import { LanguagesSupported, languages } from '@/i18n-config/language' -import I18n from '@/context/i18n' -import { activateMember } from '@/service/common' -import Loading from '@/app/components/base/loading' -import Toast from '@/app/components/base/toast' import { noop } from 'lodash-es' +import Link from 'next/link' +import { useRouter, useSearchParams } from 'next/navigation' +import { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { useContext } from 'use-context-selector' +import Button from '@/app/components/base/button' +import Input from '@/app/components/base/input' +import Loading from '@/app/components/base/loading' +import { SimpleSelect } from '@/app/components/base/select' +import Toast from '@/app/components/base/toast' import { useGlobalPublicStore } from '@/context/global-public-context' -import { resolvePostLoginRedirect } from '../utils/post-login-redirect' +import I18n, { useDocLink } from '@/context/i18n' +import { languages, LanguagesSupported } from '@/i18n-config/language' +import { activateMember } from '@/service/common' import { useInvitationCheck } from '@/service/use-common' +import { timezones } from '@/utils/timezone' +import { resolvePostLoginRedirect } from '../utils/post-login-redirect' export default function InviteSettingsPage() { const { t } = useTranslation() @@ -70,95 +69,104 @@ export default function InviteSettingsPage() { if (!checkRes) return <Loading /> if (!checkRes.is_valid) { - return <div className="flex flex-col md:w-[400px]"> - <div className="mx-auto w-full"> - <div className="mb-3 flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle text-2xl font-bold shadow-lg">🤷‍♂️</div> - <h2 className="title-4xl-semi-bold text-text-primary">{t('login.invalid')}</h2> + return ( + <div className="flex flex-col md:w-[400px]"> + <div className="mx-auto w-full"> + <div className="mb-3 flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle text-2xl font-bold shadow-lg">🤷‍♂️</div> + <h2 className="title-4xl-semi-bold text-text-primary">{t('login.invalid')}</h2> + </div> + <div className="mx-auto mt-6 w-full"> + <Button variant="primary" className="w-full !text-sm"> + <a href="https://dify.ai">{t('login.explore')}</a> + </Button> + </div> </div> - <div className="mx-auto mt-6 w-full"> - <Button variant='primary' className='w-full !text-sm'> - <a href="https://dify.ai">{t('login.explore')}</a> - </Button> - </div> - </div> + ) } - return <div className='flex flex-col gap-3'> - <div className='inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle bg-background-default-dodge shadow-lg'> - <RiAccountCircleLine className='h-6 w-6 text-2xl text-text-accent-light-mode-only' /> - </div> - <div className='pb-4 pt-2'> - <h2 className='title-4xl-semi-bold text-text-primary'>{t('login.setYourAccount')}</h2> - </div> - <form onSubmit={noop}> - <div className='mb-5'> - <label htmlFor="name" className="system-md-semibold my-2 text-text-secondary"> - {t('login.name')} - </label> - <div className="mt-1"> - <Input - id="name" - type="text" - value={name} - onChange={e => setName(e.target.value)} - placeholder={t('login.namePlaceholder') || ''} - onKeyDown={(e) => { - if (e.key === 'Enter') { - e.preventDefault() - e.stopPropagation() - handleActivate() - } - }} - /> + return ( + <div className="flex flex-col gap-3"> + <div className="inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle bg-background-default-dodge shadow-lg"> + <RiAccountCircleLine className="h-6 w-6 text-2xl text-text-accent-light-mode-only" /> + </div> + <div className="pb-4 pt-2"> + <h2 className="title-4xl-semi-bold text-text-primary">{t('login.setYourAccount')}</h2> + </div> + <form onSubmit={noop}> + <div className="mb-5"> + <label htmlFor="name" className="system-md-semibold my-2 text-text-secondary"> + {t('login.name')} + </label> + <div className="mt-1"> + <Input + id="name" + type="text" + value={name} + onChange={e => setName(e.target.value)} + placeholder={t('login.namePlaceholder') || ''} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault() + e.stopPropagation() + handleActivate() + } + }} + /> + </div> </div> - </div> - <div className='mb-5'> - <label htmlFor="name" className="system-md-semibold my-2 text-text-secondary"> - {t('login.interfaceLanguage')} - </label> - <div className="mt-1"> - <SimpleSelect - defaultValue={LanguagesSupported[0]} - items={languages.filter(item => item.supported)} - onSelect={(item) => { - setLanguage(item.value as string) - }} - /> + <div className="mb-5"> + <label htmlFor="name" className="system-md-semibold my-2 text-text-secondary"> + {t('login.interfaceLanguage')} + </label> + <div className="mt-1"> + <SimpleSelect + defaultValue={LanguagesSupported[0]} + items={languages.filter(item => item.supported)} + onSelect={(item) => { + setLanguage(item.value as string) + }} + /> + </div> </div> - </div> - {/* timezone */} - <div className='mb-5'> - <label htmlFor="timezone" className="system-md-semibold text-text-secondary"> - {t('login.timezone')} - </label> - <div className="mt-1"> - <SimpleSelect - defaultValue={timezone} - items={timezones} - onSelect={(item) => { - setTimezone(item.value as string) - }} - /> + {/* timezone */} + <div className="mb-5"> + <label htmlFor="timezone" className="system-md-semibold text-text-secondary"> + {t('login.timezone')} + </label> + <div className="mt-1"> + <SimpleSelect + defaultValue={timezone} + items={timezones} + onSelect={(item) => { + setTimezone(item.value as string) + }} + /> + </div> </div> - </div> - <div> - <Button - variant='primary' - className='w-full' - onClick={handleActivate} - > - {`${t('login.join')} ${checkRes?.data?.workspace_name}`} - </Button> - </div> - </form> - {!systemFeatures.branding.enabled && <div className="system-xs-regular mt-2 block w-full text-text-tertiary"> - {t('login.license.tip')} + <div> + <Button + variant="primary" + className="w-full" + onClick={handleActivate} + > + {`${t('login.join')} ${checkRes?.data?.workspace_name}`} + </Button> + </div> + </form> + {!systemFeatures.branding.enabled && ( + <div className="system-xs-regular mt-2 block w-full text-text-tertiary"> + {t('login.license.tip')}   - <Link - className='system-xs-medium text-text-accent-secondary' - target='_blank' rel='noopener noreferrer' - href={docLink('/policies/open-source')} - >{t('login.license.link')}</Link> - </div>} - </div> + <Link + className="system-xs-medium text-text-accent-secondary" + target="_blank" + rel="noopener noreferrer" + href={docLink('/policies/open-source')} + > + {t('login.license.link')} + </Link> + </div> + )} + </div> + ) } diff --git a/web/app/signin/layout.tsx b/web/app/signin/layout.tsx index 17922f7892..4a1a2f4f58 100644 --- a/web/app/signin/layout.tsx +++ b/web/app/signin/layout.tsx @@ -1,26 +1,34 @@ 'use client' -import Header from './_header' - -import { cn } from '@/utils/classnames' import { useGlobalPublicStore } from '@/context/global-public-context' + import useDocumentTitle from '@/hooks/use-document-title' +import { cn } from '@/utils/classnames' +import Header from './_header' export default function SignInLayout({ children }: any) { const { systemFeatures } = useGlobalPublicStore() useDocumentTitle('') - return <> - <div className={cn('flex min-h-screen w-full justify-center bg-background-default-burn p-6')}> - <div className={cn('flex w-full shrink-0 flex-col items-center rounded-2xl border border-effects-highlight bg-background-default-subtle')}> - <Header /> - <div className={cn('flex w-full grow flex-col items-center justify-center px-6 md:px-[108px]')}> - <div className='flex flex-col md:w-[400px]'> - {children} + return ( + <> + <div className={cn('flex min-h-screen w-full justify-center bg-background-default-burn p-6')}> + <div className={cn('flex w-full shrink-0 flex-col items-center rounded-2xl border border-effects-highlight bg-background-default-subtle')}> + <Header /> + <div className={cn('flex w-full grow flex-col items-center justify-center px-6 md:px-[108px]')}> + <div className="flex flex-col md:w-[400px]"> + {children} + </div> </div> + {systemFeatures.branding.enabled === false && ( + <div className="system-xs-regular px-8 py-6 text-text-tertiary"> + © + {' '} + {new Date().getFullYear()} + {' '} + LangGenius, Inc. All rights reserved. + </div> + )} </div> - {systemFeatures.branding.enabled === false && <div className='system-xs-regular px-8 py-6 text-text-tertiary'> - © {new Date().getFullYear()} LangGenius, Inc. All rights reserved. - </div>} </div> - </div> - </> + </> + ) } diff --git a/web/app/signin/normal-form.tsx b/web/app/signin/normal-form.tsx index 260eb8c0cb..6bc37e6dd3 100644 --- a/web/app/signin/normal-form.tsx +++ b/web/app/signin/normal-form.tsx @@ -1,22 +1,22 @@ -import React, { useCallback, useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' +import { RiContractLine, RiDoorLockLine, RiErrorWarningFill } from '@remixicon/react' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' -import { RiContractLine, RiDoorLockLine, RiErrorWarningFill } from '@remixicon/react' +import React, { useCallback, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Toast from '@/app/components/base/toast' +import { IS_CE_EDITION } from '@/config' +import { useGlobalPublicStore } from '@/context/global-public-context' +import { invitationCheck } from '@/service/common' +import { useIsLogin } from '@/service/use-common' +import { LicenseStatus } from '@/types/feature' +import { cn } from '@/utils/classnames' import Loading from '../components/base/loading' import MailAndCodeAuth from './components/mail-and-code-auth' import MailAndPasswordAuth from './components/mail-and-password-auth' import SocialAuth from './components/social-auth' import SSOAuth from './components/sso-auth' -import { cn } from '@/utils/classnames' -import { invitationCheck } from '@/service/common' -import { LicenseStatus } from '@/types/feature' -import Toast from '@/app/components/base/toast' -import { IS_CE_EDITION } from '@/config' -import { useGlobalPublicStore } from '@/context/global-public-context' -import { resolvePostLoginRedirect } from './utils/post-login-redirect' import Split from './split' -import { useIsLogin } from '@/service/use-common' +import { resolvePostLoginRedirect } from './utils/post-login-redirect' const NormalForm = () => { const { t } = useTranslation() @@ -73,152 +73,204 @@ const NormalForm = () => { init() }, [init]) if (isLoading) { - return <div className={ - cn( - 'flex w-full grow flex-col items-center justify-center', - 'px-6', - 'md:px-[108px]', - ) - }> - <Loading type='area' /> - </div> + return ( + <div className={ + cn( + 'flex w-full grow flex-col items-center justify-center', + 'px-6', + 'md:px-[108px]', + ) + } + > + <Loading type="area" /> + </div> + ) } if (systemFeatures.license?.status === LicenseStatus.LOST) { - return <div className='mx-auto mt-8 w-full'> - <div className='relative'> - <div className="rounded-lg bg-gradient-to-r from-workflow-workflow-progress-bg-1 to-workflow-workflow-progress-bg-2 p-4"> - <div className='shadows-shadow-lg relative mb-2 flex h-10 w-10 items-center justify-center rounded-xl bg-components-card-bg shadow'> - <RiContractLine className='h-5 w-5' /> - <RiErrorWarningFill className='absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary' /> + return ( + <div className="mx-auto mt-8 w-full"> + <div className="relative"> + <div className="rounded-lg bg-gradient-to-r from-workflow-workflow-progress-bg-1 to-workflow-workflow-progress-bg-2 p-4"> + <div className="shadows-shadow-lg relative mb-2 flex h-10 w-10 items-center justify-center rounded-xl bg-components-card-bg shadow"> + <RiContractLine className="h-5 w-5" /> + <RiErrorWarningFill className="absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary" /> + </div> + <p className="system-sm-medium text-text-primary">{t('login.licenseLost')}</p> + <p className="system-xs-regular mt-1 text-text-tertiary">{t('login.licenseLostTip')}</p> </div> - <p className='system-sm-medium text-text-primary'>{t('login.licenseLost')}</p> - <p className='system-xs-regular mt-1 text-text-tertiary'>{t('login.licenseLostTip')}</p> </div> </div> - </div> + ) } if (systemFeatures.license?.status === LicenseStatus.EXPIRED) { - return <div className='mx-auto mt-8 w-full'> - <div className='relative'> - <div className="rounded-lg bg-gradient-to-r from-workflow-workflow-progress-bg-1 to-workflow-workflow-progress-bg-2 p-4"> - <div className='shadows-shadow-lg relative mb-2 flex h-10 w-10 items-center justify-center rounded-xl bg-components-card-bg shadow'> - <RiContractLine className='h-5 w-5' /> - <RiErrorWarningFill className='absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary' /> + return ( + <div className="mx-auto mt-8 w-full"> + <div className="relative"> + <div className="rounded-lg bg-gradient-to-r from-workflow-workflow-progress-bg-1 to-workflow-workflow-progress-bg-2 p-4"> + <div className="shadows-shadow-lg relative mb-2 flex h-10 w-10 items-center justify-center rounded-xl bg-components-card-bg shadow"> + <RiContractLine className="h-5 w-5" /> + <RiErrorWarningFill className="absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary" /> + </div> + <p className="system-sm-medium text-text-primary">{t('login.licenseExpired')}</p> + <p className="system-xs-regular mt-1 text-text-tertiary">{t('login.licenseExpiredTip')}</p> </div> - <p className='system-sm-medium text-text-primary'>{t('login.licenseExpired')}</p> - <p className='system-xs-regular mt-1 text-text-tertiary'>{t('login.licenseExpiredTip')}</p> </div> </div> - </div> + ) } if (systemFeatures.license?.status === LicenseStatus.INACTIVE) { - return <div className='mx-auto mt-8 w-full'> - <div className='relative'> - <div className="rounded-lg bg-gradient-to-r from-workflow-workflow-progress-bg-1 to-workflow-workflow-progress-bg-2 p-4"> - <div className='shadows-shadow-lg relative mb-2 flex h-10 w-10 items-center justify-center rounded-xl bg-components-card-bg shadow'> - <RiContractLine className='h-5 w-5' /> - <RiErrorWarningFill className='absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary' /> + return ( + <div className="mx-auto mt-8 w-full"> + <div className="relative"> + <div className="rounded-lg bg-gradient-to-r from-workflow-workflow-progress-bg-1 to-workflow-workflow-progress-bg-2 p-4"> + <div className="shadows-shadow-lg relative mb-2 flex h-10 w-10 items-center justify-center rounded-xl bg-components-card-bg shadow"> + <RiContractLine className="h-5 w-5" /> + <RiErrorWarningFill className="absolute -right-1 -top-1 h-4 w-4 text-text-warning-secondary" /> + </div> + <p className="system-sm-medium text-text-primary">{t('login.licenseInactive')}</p> + <p className="system-xs-regular mt-1 text-text-tertiary">{t('login.licenseInactiveTip')}</p> </div> - <p className='system-sm-medium text-text-primary'>{t('login.licenseInactive')}</p> - <p className='system-xs-regular mt-1 text-text-tertiary'>{t('login.licenseInactiveTip')}</p> </div> </div> - </div> + ) } return ( <> <div className="mx-auto mt-8 w-full"> {isInviteLink - ? <div className="mx-auto w-full"> - <h2 className="title-4xl-semi-bold text-text-primary">{t('login.join')}{workspaceName}</h2> - {!systemFeatures.branding.enabled && <p className='body-md-regular mt-2 text-text-tertiary'>{t('login.joinTipStart')}{workspaceName}{t('login.joinTipEnd')}</p>} - </div> - : <div className="mx-auto w-full"> - <h2 className="title-4xl-semi-bold text-text-primary">{systemFeatures.branding.enabled ? t('login.pageTitleForE') : t('login.pageTitle')}</h2> - <p className='body-md-regular mt-2 text-text-tertiary'>{t('login.welcome')}</p> - </div>} + ? ( + <div className="mx-auto w-full"> + <h2 className="title-4xl-semi-bold text-text-primary"> + {t('login.join')} + {workspaceName} + </h2> + {!systemFeatures.branding.enabled && ( + <p className="body-md-regular mt-2 text-text-tertiary"> + {t('login.joinTipStart')} + {workspaceName} + {t('login.joinTipEnd')} + </p> + )} + </div> + ) + : ( + <div className="mx-auto w-full"> + <h2 className="title-4xl-semi-bold text-text-primary">{systemFeatures.branding.enabled ? t('login.pageTitleForE') : t('login.pageTitle')}</h2> + <p className="body-md-regular mt-2 text-text-tertiary">{t('login.welcome')}</p> + </div> + )} <div className="relative"> <div className="mt-6 flex flex-col gap-3"> {systemFeatures.enable_social_oauth_login && <SocialAuth />} - {systemFeatures.sso_enforced_for_signin && <div className='w-full'> - <SSOAuth protocol={systemFeatures.sso_enforced_for_signin_protocol} /> - </div>} + {systemFeatures.sso_enforced_for_signin && ( + <div className="w-full"> + <SSOAuth protocol={systemFeatures.sso_enforced_for_signin_protocol} /> + </div> + )} </div> - {showORLine && <div className="relative mt-6"> - <div className="flex items-center"> - <div className="h-px flex-1 bg-gradient-to-r from-background-gradient-mask-transparent to-divider-regular"></div> - <span className="system-xs-medium-uppercase px-3 text-text-tertiary">{t('login.or')}</span> - <div className="h-px flex-1 bg-gradient-to-l from-background-gradient-mask-transparent to-divider-regular"></div> + {showORLine && ( + <div className="relative mt-6"> + <div className="flex items-center"> + <div className="h-px flex-1 bg-gradient-to-r from-background-gradient-mask-transparent to-divider-regular"></div> + <span className="system-xs-medium-uppercase px-3 text-text-tertiary">{t('login.or')}</span> + <div className="h-px flex-1 bg-gradient-to-l from-background-gradient-mask-transparent to-divider-regular"></div> + </div> </div> - </div>} + )} { - (systemFeatures.enable_email_code_login || systemFeatures.enable_email_password_login) && <> - {systemFeatures.enable_email_code_login && authType === 'code' && <> - <MailAndCodeAuth isInvite={isInviteLink} /> - {systemFeatures.enable_email_password_login && <div className='cursor-pointer py-1 text-center' onClick={() => { updateAuthType('password') }}> - <span className='system-xs-medium text-components-button-secondary-accent-text'>{t('login.usePassword')}</span> - </div>} - </>} - {systemFeatures.enable_email_password_login && authType === 'password' && <> - <MailAndPasswordAuth isInvite={isInviteLink} isEmailSetup={systemFeatures.is_email_setup} allowRegistration={systemFeatures.is_allow_register} /> - {systemFeatures.enable_email_code_login && <div className='cursor-pointer py-1 text-center' onClick={() => { updateAuthType('code') }}> - <span className='system-xs-medium text-components-button-secondary-accent-text'>{t('login.useVerificationCode')}</span> - </div>} - </>} - <Split className='mb-5 mt-4' /> - </> + (systemFeatures.enable_email_code_login || systemFeatures.enable_email_password_login) && ( + <> + {systemFeatures.enable_email_code_login && authType === 'code' && ( + <> + <MailAndCodeAuth isInvite={isInviteLink} /> + {systemFeatures.enable_email_password_login && ( + <div className="cursor-pointer py-1 text-center" onClick={() => { updateAuthType('password') }}> + <span className="system-xs-medium text-components-button-secondary-accent-text">{t('login.usePassword')}</span> + </div> + )} + </> + )} + {systemFeatures.enable_email_password_login && authType === 'password' && ( + <> + <MailAndPasswordAuth isInvite={isInviteLink} isEmailSetup={systemFeatures.is_email_setup} allowRegistration={systemFeatures.is_allow_register} /> + {systemFeatures.enable_email_code_login && ( + <div className="cursor-pointer py-1 text-center" onClick={() => { updateAuthType('code') }}> + <span className="system-xs-medium text-components-button-secondary-accent-text">{t('login.useVerificationCode')}</span> + </div> + )} + </> + )} + <Split className="mb-5 mt-4" /> + </> + ) } {systemFeatures.is_allow_register && authType === 'password' && ( - <div className='mb-3 text-[13px] font-medium leading-4 text-text-secondary'> + <div className="mb-3 text-[13px] font-medium leading-4 text-text-secondary"> <span>{t('login.signup.noAccount')}</span> <Link - className='text-text-accent' - href='/signup' - >{t('login.signup.signUp')}</Link> + className="text-text-accent" + href="/signup" + > + {t('login.signup.signUp')} + </Link> </div> )} - {allMethodsAreDisabled && <> - <div className="rounded-lg bg-gradient-to-r from-workflow-workflow-progress-bg-1 to-workflow-workflow-progress-bg-2 p-4"> - <div className='shadows-shadow-lg mb-2 flex h-10 w-10 items-center justify-center rounded-xl bg-components-card-bg shadow'> - <RiDoorLockLine className='h-5 w-5' /> + {allMethodsAreDisabled && ( + <> + <div className="rounded-lg bg-gradient-to-r from-workflow-workflow-progress-bg-1 to-workflow-workflow-progress-bg-2 p-4"> + <div className="shadows-shadow-lg mb-2 flex h-10 w-10 items-center justify-center rounded-xl bg-components-card-bg shadow"> + <RiDoorLockLine className="h-5 w-5" /> + </div> + <p className="system-sm-medium text-text-primary">{t('login.noLoginMethod')}</p> + <p className="system-xs-regular mt-1 text-text-tertiary">{t('login.noLoginMethodTip')}</p> </div> - <p className='system-sm-medium text-text-primary'>{t('login.noLoginMethod')}</p> - <p className='system-xs-regular mt-1 text-text-tertiary'>{t('login.noLoginMethodTip')}</p> - </div> - <div className="relative my-2 py-2"> - <div className="absolute inset-0 flex items-center" aria-hidden="true"> - <div className='h-px w-full bg-gradient-to-r from-background-gradient-mask-transparent via-divider-regular to-background-gradient-mask-transparent'></div> + <div className="relative my-2 py-2"> + <div className="absolute inset-0 flex items-center" aria-hidden="true"> + <div className="h-px w-full bg-gradient-to-r from-background-gradient-mask-transparent via-divider-regular to-background-gradient-mask-transparent"></div> + </div> </div> - </div> - </>} - {!systemFeatures.branding.enabled && <> - <div className="system-xs-regular mt-2 block w-full text-text-tertiary"> - {t('login.tosDesc')} + </> + )} + {!systemFeatures.branding.enabled && ( + <> + <div className="system-xs-regular mt-2 block w-full text-text-tertiary"> + {t('login.tosDesc')}   - <Link - className='system-xs-medium text-text-secondary hover:underline' - target='_blank' rel='noopener noreferrer' - href='https://dify.ai/terms' - >{t('login.tos')}</Link> + <Link + className="system-xs-medium text-text-secondary hover:underline" + target="_blank" + rel="noopener noreferrer" + href="https://dify.ai/terms" + > + {t('login.tos')} + </Link>  &  - <Link - className='system-xs-medium text-text-secondary hover:underline' - target='_blank' rel='noopener noreferrer' - href='https://dify.ai/privacy' - >{t('login.pp')}</Link> - </div> - {IS_CE_EDITION && <div className="w-hull system-xs-regular mt-2 block text-text-tertiary"> - {t('login.goToInit')} + <Link + className="system-xs-medium text-text-secondary hover:underline" + target="_blank" + rel="noopener noreferrer" + href="https://dify.ai/privacy" + > + {t('login.pp')} + </Link> + </div> + {IS_CE_EDITION && ( + <div className="w-hull system-xs-regular mt-2 block text-text-tertiary"> + {t('login.goToInit')}   - <Link - className='system-xs-medium text-text-secondary hover:underline' - href='/install' - >{t('login.setAdminAccount')}</Link> - </div>} - </>} + <Link + className="system-xs-medium text-text-secondary hover:underline" + href="/install" + > + {t('login.setAdminAccount')} + </Link> + </div> + )} + </> + )} </div> </div> </> diff --git a/web/app/signin/one-more-step.tsx b/web/app/signin/one-more-step.tsx index 4b20f85681..80013e622f 100644 --- a/web/app/signin/one-more-step.tsx +++ b/web/app/signin/one-more-step.tsx @@ -1,17 +1,18 @@ 'use client' -import React, { type Reducer, useReducer } from 'react' -import { useTranslation } from 'react-i18next' +import type { Reducer } from 'react' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' -import Input from '../components/base/input' +import React, { useReducer } from 'react' +import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' -import Tooltip from '@/app/components/base/tooltip' import { SimpleSelect } from '@/app/components/base/select' -import { timezones } from '@/utils/timezone' -import { LanguagesSupported, languages } from '@/i18n-config/language' import Toast from '@/app/components/base/toast' +import Tooltip from '@/app/components/base/tooltip' import { useDocLink } from '@/context/i18n' +import { languages, LanguagesSupported } from '@/i18n-config/language' import { useOneMoreStep } from '@/service/use-common' +import { timezones } from '@/utils/timezone' +import Input from '../components/base/input' type IState = { invitation_code: string @@ -21,9 +22,9 @@ type IState = { type IAction = | { type: 'failed', payload: null } - | { type: 'invitation_code', value: string } - | { type: 'interface_language', value: string } - | { type: 'timezone', value: string } + | { type: 'invitation_code', value: string } + | { type: 'interface_language', value: string } + | { type: 'timezone', value: string } const reducer: Reducer<IState, IAction> = (state: IState, action: IAction) => { switch (action.type) { @@ -79,7 +80,7 @@ const OneMoreStep = () => { <> <div className="mx-auto w-full"> <h2 className="title-4xl-semi-bold text-text-secondary">{t('login.oneMoreStep')}</h2> - <p className='body-md-regular mt-1 text-text-tertiary'>{t('login.createSample')}</p> + <p className="body-md-regular mt-1 text-text-tertiary">{t('login.createSample')}</p> </div> <div className="mx-auto mt-6 w-full"> @@ -88,16 +89,16 @@ const OneMoreStep = () => { <label className="system-md-semibold my-2 flex items-center justify-between text-text-secondary"> {t('login.invitationCode')} <Tooltip - popupContent={ - <div className='w-[256px] text-xs font-medium'> - <div className='font-medium'>{t('login.sendUsMail')}</div> - <div className='cursor-pointer text-xs font-medium text-text-accent-secondary'> + popupContent={( + <div className="w-[256px] text-xs font-medium"> + <div className="font-medium">{t('login.sendUsMail')}</div> + <div className="cursor-pointer text-xs font-medium text-text-accent-secondary"> <a href="mailto:request-invitation@langgenius.ai">request-invitation@langgenius.ai</a> </div> </div> - } + )} > - <span className='cursor-pointer text-text-accent-secondary'>{t('login.dontHave')}</span> + <span className="cursor-pointer text-text-accent-secondary">{t('login.dontHave')}</span> </Tooltip> </label> <div className="mt-1"> @@ -112,7 +113,7 @@ const OneMoreStep = () => { /> </div> </div> - <div className='mb-5'> + <div className="mb-5"> <label htmlFor="name" className="system-md-semibold my-2 text-text-secondary"> {t('login.interfaceLanguage')} </label> @@ -126,7 +127,7 @@ const OneMoreStep = () => { /> </div> </div> - <div className='mb-4'> + <div className="mb-4"> <label htmlFor="timezone" className="system-md-semibold text-text-tertiary"> {t('login.timezone')} </label> @@ -142,8 +143,8 @@ const OneMoreStep = () => { </div> <div> <Button - variant='primary' - className='w-full' + variant="primary" + className="w-full" disabled={isPending} onClick={handleSubmit} > @@ -154,10 +155,13 @@ const OneMoreStep = () => { {t('login.license.tip')}   <Link - className='system-xs-medium text-text-accent-secondary' - target='_blank' rel='noopener noreferrer' + className="system-xs-medium text-text-accent-secondary" + target="_blank" + rel="noopener noreferrer" href={docLink('/policies/agreement/README')} - >{t('login.license.link')}</Link> + > + {t('login.license.link')} + </Link> </div> </div> </div> diff --git a/web/app/signin/page.tsx b/web/app/signin/page.tsx index 01c790c760..6f3632393c 100644 --- a/web/app/signin/page.tsx +++ b/web/app/signin/page.tsx @@ -1,9 +1,9 @@ 'use client' import { useSearchParams } from 'next/navigation' -import OneMoreStep from './one-more-step' -import NormalForm from './normal-form' import { useEffect } from 'react' import usePSInfo from '../components/billing/partner-stack/use-ps-info' +import NormalForm from './normal-form' +import OneMoreStep from './one-more-step' const SignIn = () => { const searchParams = useSearchParams() diff --git a/web/app/signin/split.tsx b/web/app/signin/split.tsx index 8fd6fefc15..b6e848357c 100644 --- a/web/app/signin/split.tsx +++ b/web/app/signin/split.tsx @@ -12,7 +12,8 @@ const Split: FC<Props> = ({ }) => { return ( <div - className={cn('h-px w-[400px] bg-[linear-gradient(90deg,rgba(255,255,255,0.01)_0%,rgba(16,24,40,0.08)_50.5%,rgba(255,255,255,0.01)_100%)]', className)}> + className={cn('h-px w-[400px] bg-[linear-gradient(90deg,rgba(255,255,255,0.01)_0%,rgba(16,24,40,0.08)_50.5%,rgba(255,255,255,0.01)_100%)]', className)} + > </div> ) } diff --git a/web/app/signin/utils/post-login-redirect.ts b/web/app/signin/utils/post-login-redirect.ts index 45e2c55941..b548a1bac9 100644 --- a/web/app/signin/utils/post-login-redirect.ts +++ b/web/app/signin/utils/post-login-redirect.ts @@ -1,6 +1,6 @@ -import { OAUTH_AUTHORIZE_PENDING_KEY, REDIRECT_URL_KEY } from '@/app/account/oauth/authorize/constants' -import dayjs from 'dayjs' import type { ReadonlyURLSearchParams } from 'next/navigation' +import dayjs from 'dayjs' +import { OAUTH_AUTHORIZE_PENDING_KEY, REDIRECT_URL_KEY } from '@/app/account/oauth/authorize/constants' function getItemWithExpiry(key: string): string | null { const itemStr = localStorage.getItem(key) @@ -10,7 +10,8 @@ function getItemWithExpiry(key: string): string | null { try { const item = JSON.parse(itemStr) localStorage.removeItem(key) - if (!item?.value) return null + if (!item?.value) + return null return dayjs().unix() > item.expiry ? null : item.value } diff --git a/web/app/signup/check-code/page.tsx b/web/app/signup/check-code/page.tsx index 35c5e78a45..3a4ff403fb 100644 --- a/web/app/signup/check-code/page.tsx +++ b/web/app/signup/check-code/page.tsx @@ -1,15 +1,15 @@ 'use client' +import type { MailSendResponse, MailValidityResponse } from '@/service/use-common' import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import { useState } from 'react' import { useRouter, useSearchParams } from 'next/navigation' +import { useState } from 'react' +import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' -import Countdown from '@/app/components/signin/countdown' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' +import Countdown from '@/app/components/signin/countdown' import I18NContext from '@/context/i18n' -import type { MailSendResponse, MailValidityResponse } from '@/service/use-common' import { useMailValidity, useSendMail } from '@/service/use-common' export default function CheckCode() { @@ -74,36 +74,38 @@ export default function CheckCode() { catch (error) { console.error(error) } } - return <div className='flex flex-col gap-3'> - <div className='inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle bg-background-default-dodge shadow-lg'> - <RiMailSendFill className='h-6 w-6 text-2xl text-text-accent-light-mode-only' /> - </div> - <div className='pb-4 pt-2'> - <h2 className='title-4xl-semi-bold text-text-primary'>{t('login.checkCode.checkYourEmail')}</h2> - <p className='body-md-regular mt-2 text-text-secondary'> - <span> - {t('login.checkCode.tipsPrefix')} - <strong>{email}</strong> - </span> - <br /> - {t('login.checkCode.validTime')} - </p> - </div> - - <form action=""> - <label htmlFor="code" className='system-md-semibold mb-1 text-text-secondary'>{t('login.checkCode.verificationCode')}</label> - <Input value={code} onChange={e => setVerifyCode(e.target.value)} maxLength={6} className='mt-1' placeholder={t('login.checkCode.verificationCodePlaceholder') as string} /> - <Button loading={loading} disabled={loading} className='my-3 w-full' variant='primary' onClick={verify}>{t('login.checkCode.verify')}</Button> - <Countdown onResend={resendCode} /> - </form> - <div className='py-2'> - <div className='h-px bg-gradient-to-r from-background-gradient-mask-transparent via-divider-regular to-background-gradient-mask-transparent'></div> - </div> - <div onClick={() => router.back()} className='flex h-9 cursor-pointer items-center justify-center text-text-tertiary'> - <div className='bg-background-default-dimm inline-block rounded-full p-1'> - <RiArrowLeftLine size={12} /> + return ( + <div className="flex flex-col gap-3"> + <div className="inline-flex h-14 w-14 items-center justify-center rounded-2xl border border-components-panel-border-subtle bg-background-default-dodge shadow-lg"> + <RiMailSendFill className="h-6 w-6 text-2xl text-text-accent-light-mode-only" /> + </div> + <div className="pb-4 pt-2"> + <h2 className="title-4xl-semi-bold text-text-primary">{t('login.checkCode.checkYourEmail')}</h2> + <p className="body-md-regular mt-2 text-text-secondary"> + <span> + {t('login.checkCode.tipsPrefix')} + <strong>{email}</strong> + </span> + <br /> + {t('login.checkCode.validTime')} + </p> + </div> + + <form action=""> + <label htmlFor="code" className="system-md-semibold mb-1 text-text-secondary">{t('login.checkCode.verificationCode')}</label> + <Input value={code} onChange={e => setVerifyCode(e.target.value)} maxLength={6} className="mt-1" placeholder={t('login.checkCode.verificationCodePlaceholder') as string} /> + <Button loading={loading} disabled={loading} className="my-3 w-full" variant="primary" onClick={verify}>{t('login.checkCode.verify')}</Button> + <Countdown onResend={resendCode} /> + </form> + <div className="py-2"> + <div className="h-px bg-gradient-to-r from-background-gradient-mask-transparent via-divider-regular to-background-gradient-mask-transparent"></div> + </div> + <div onClick={() => router.back()} className="flex h-9 cursor-pointer items-center justify-center text-text-tertiary"> + <div className="bg-background-default-dimm inline-block rounded-full p-1"> + <RiArrowLeftLine size={12} /> + </div> + <span className="system-xs-regular ml-2">{t('login.back')}</span> </div> - <span className='system-xs-regular ml-2'>{t('login.back')}</span> </div> - </div> + ) } diff --git a/web/app/signup/components/input-mail.tsx b/web/app/signup/components/input-mail.tsx index d2e7bca65b..b001e1f8b0 100644 --- a/web/app/signup/components/input-mail.tsx +++ b/web/app/signup/components/input-mail.tsx @@ -1,18 +1,18 @@ 'use client' +import type { MailSendResponse } from '@/service/use-common' import { noop } from 'lodash-es' -import Input from '@/app/components/base/input' +import Link from 'next/link' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' -import { useCallback, useState } from 'react' import Button from '@/app/components/base/button' -import { emailRegex } from '@/config' +import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' -import type { MailSendResponse } from '@/service/use-common' -import { useSendMail } from '@/service/use-common' -import I18n from '@/context/i18n' import Split from '@/app/signin/split' -import Link from 'next/link' +import { emailRegex } from '@/config' import { useGlobalPublicStore } from '@/context/global-public-context' +import I18n from '@/context/i18n' +import { useSendMail } from '@/service/use-common' type Props = { onSuccess: (email: string, payload: string) => void @@ -40,63 +40,77 @@ export default function Form({ return } const res = await submitMail({ email, language: locale }) - if((res as MailSendResponse).result === 'success') + if ((res as MailSendResponse).result === 'success') onSuccess(email, (res as MailSendResponse).data) }, [email, locale, submitMail, t]) - return <form onSubmit={noop}> - <div className='mb-3'> - <label htmlFor="email" className="system-md-semibold my-2 text-text-secondary"> - {t('login.email')} - </label> - <div className="mt-1"> - <Input - value={email} - onChange={e => setEmail(e.target.value)} - id="email" - type="email" - autoComplete="email" - placeholder={t('login.emailPlaceholder') || ''} - tabIndex={1} - /> + return ( + <form onSubmit={noop}> + <div className="mb-3"> + <label htmlFor="email" className="system-md-semibold my-2 text-text-secondary"> + {t('login.email')} + </label> + <div className="mt-1"> + <Input + value={email} + onChange={e => setEmail(e.target.value)} + id="email" + type="email" + autoComplete="email" + placeholder={t('login.emailPlaceholder') || ''} + tabIndex={1} + /> + </div> </div> - </div> - <div className='mb-2'> - <Button - tabIndex={2} - variant='primary' - onClick={handleSubmit} - disabled={isPending || !email} - className="w-full" - >{t('login.signup.verifyMail')}</Button> - </div> - <Split className='mb-5 mt-4' /> + <div className="mb-2"> + <Button + tabIndex={2} + variant="primary" + onClick={handleSubmit} + disabled={isPending || !email} + className="w-full" + > + {t('login.signup.verifyMail')} + </Button> + </div> + <Split className="mb-5 mt-4" /> - <div className='text-[13px] font-medium leading-4 text-text-secondary'> - <span>{t('login.signup.haveAccount')}</span> - <Link - className='text-text-accent' - href='/signin' - >{t('login.signup.signIn')}</Link> - </div> + <div className="text-[13px] font-medium leading-4 text-text-secondary"> + <span>{t('login.signup.haveAccount')}</span> + <Link + className="text-text-accent" + href="/signin" + > + {t('login.signup.signIn')} + </Link> + </div> - {!systemFeatures.branding.enabled && <> - <div className="system-xs-regular mt-3 block w-full text-text-tertiary"> - {t('login.tosDesc')} + {!systemFeatures.branding.enabled && ( + <> + <div className="system-xs-regular mt-3 block w-full text-text-tertiary"> + {t('login.tosDesc')}   - <Link - className='system-xs-medium text-text-secondary hover:underline' - target='_blank' rel='noopener noreferrer' - href='https://dify.ai/terms' - >{t('login.tos')}</Link> + <Link + className="system-xs-medium text-text-secondary hover:underline" + target="_blank" + rel="noopener noreferrer" + href="https://dify.ai/terms" + > + {t('login.tos')} + </Link>  &  - <Link - className='system-xs-medium text-text-secondary hover:underline' - target='_blank' rel='noopener noreferrer' - href='https://dify.ai/privacy' - >{t('login.pp')}</Link> - </div> - </>} + <Link + className="system-xs-medium text-text-secondary hover:underline" + target="_blank" + rel="noopener noreferrer" + href="https://dify.ai/privacy" + > + {t('login.pp')} + </Link> + </div> + </> + )} - </form> + </form> + ) } diff --git a/web/app/signup/layout.tsx b/web/app/signup/layout.tsx index e6b9c36411..6728b66115 100644 --- a/web/app/signup/layout.tsx +++ b/web/app/signup/layout.tsx @@ -1,26 +1,34 @@ 'use client' import Header from '@/app/signin/_header' -import { cn } from '@/utils/classnames' import { useGlobalPublicStore } from '@/context/global-public-context' import useDocumentTitle from '@/hooks/use-document-title' +import { cn } from '@/utils/classnames' export default function RegisterLayout({ children }: any) { const { systemFeatures } = useGlobalPublicStore() useDocumentTitle('') - return <> - <div className={cn('flex min-h-screen w-full justify-center bg-background-default-burn p-6')}> - <div className={cn('flex w-full shrink-0 flex-col items-center rounded-2xl border border-effects-highlight bg-background-default-subtle')}> - <Header /> - <div className={cn('flex w-full grow flex-col items-center justify-center px-6 md:px-[108px]')}> - <div className='flex flex-col md:w-[400px]'> - {children} + return ( + <> + <div className={cn('flex min-h-screen w-full justify-center bg-background-default-burn p-6')}> + <div className={cn('flex w-full shrink-0 flex-col items-center rounded-2xl border border-effects-highlight bg-background-default-subtle')}> + <Header /> + <div className={cn('flex w-full grow flex-col items-center justify-center px-6 md:px-[108px]')}> + <div className="flex flex-col md:w-[400px]"> + {children} + </div> </div> + {systemFeatures.branding.enabled === false && ( + <div className="system-xs-regular px-8 py-6 text-text-tertiary"> + © + {' '} + {new Date().getFullYear()} + {' '} + LangGenius, Inc. All rights reserved. + </div> + )} </div> - {systemFeatures.branding.enabled === false && <div className='system-xs-regular px-8 py-6 text-text-tertiary'> - © {new Date().getFullYear()} LangGenius, Inc. All rights reserved. - </div>} </div> - </div> - </> + </> + ) } diff --git a/web/app/signup/page.tsx b/web/app/signup/page.tsx index d410f5c085..4b75445b2c 100644 --- a/web/app/signup/page.tsx +++ b/web/app/signup/page.tsx @@ -1,8 +1,8 @@ 'use client' -import { useCallback } from 'react' -import MailForm from './components/input-mail' import { useRouter, useSearchParams } from 'next/navigation' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' +import MailForm from './components/input-mail' const Signup = () => { const router = useRouter() @@ -20,7 +20,7 @@ const Signup = () => { <div className="mx-auto mt-8 w-full"> <div className="mx-auto mb-10 w-full"> <h2 className="title-4xl-semi-bold text-text-primary">{t('login.signup.createAccount')}</h2> - <p className='body-md-regular mt-2 text-text-tertiary'>{t('login.signup.welcome')}</p> + <p className="body-md-regular mt-2 text-text-tertiary">{t('login.signup.welcome')}</p> </div> <MailForm onSuccess={handleInputMailSubmitted} /> </div> diff --git a/web/app/signup/set-password/page.tsx b/web/app/signup/set-password/page.tsx index f75dff24d9..a4b6708ce8 100644 --- a/web/app/signup/set-password/page.tsx +++ b/web/app/signup/set-password/page.tsx @@ -1,15 +1,15 @@ 'use client' +import type { MailRegisterResponse } from '@/service/use-common' +import { useRouter, useSearchParams } from 'next/navigation' import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import { useRouter, useSearchParams } from 'next/navigation' -import { cn } from '@/utils/classnames' -import Button from '@/app/components/base/button' -import Toast from '@/app/components/base/toast' -import Input from '@/app/components/base/input' -import { validPassword } from '@/config' -import type { MailRegisterResponse } from '@/service/use-common' -import { useMailRegister } from '@/service/use-common' import { trackEvent } from '@/app/components/base/amplitude' +import Button from '@/app/components/base/button' +import Input from '@/app/components/base/input' +import Toast from '@/app/components/base/toast' +import { validPassword } from '@/config' +import { useMailRegister } from '@/service/use-common' +import { cn } from '@/utils/classnames' const ChangePasswordForm = () => { const { t } = useTranslation() @@ -79,13 +79,14 @@ const ChangePasswordForm = () => { 'px-6', 'md:px-[108px]', ) - }> - <div className='flex flex-col md:w-[400px]'> + } + > + <div className="flex flex-col md:w-[400px]"> <div className="mx-auto w-full"> <h2 className="title-4xl-semi-bold text-text-primary"> {t('login.changePassword')} </h2> - <p className='body-md-regular mt-2 text-text-secondary'> + <p className="body-md-regular mt-2 text-text-secondary"> {t('login.changePasswordTip')} </p> </div> @@ -93,31 +94,31 @@ const ChangePasswordForm = () => { <div className="mx-auto mt-6 w-full"> <div> {/* Password */} - <div className='mb-5'> + <div className="mb-5"> <label htmlFor="password" className="system-md-semibold my-2 text-text-secondary"> {t('common.account.newPassword')} </label> - <div className='relative mt-1'> + <div className="relative mt-1"> <Input id="password" - type='password' + type="password" value={password} onChange={e => setPassword(e.target.value)} placeholder={t('login.passwordPlaceholder') || ''} /> </div> - <div className='body-xs-regular mt-1 text-text-secondary'>{t('login.error.passwordInvalid')}</div> + <div className="body-xs-regular mt-1 text-text-secondary">{t('login.error.passwordInvalid')}</div> </div> {/* Confirm Password */} - <div className='mb-5'> + <div className="mb-5"> <label htmlFor="confirmPassword" className="system-md-semibold my-2 text-text-secondary"> {t('common.account.confirmPassword')} </label> - <div className='relative mt-1'> + <div className="relative mt-1"> <Input id="confirmPassword" - type='password' + type="password" value={confirmPassword} onChange={e => setConfirmPassword(e.target.value)} placeholder={t('login.confirmPasswordPlaceholder') || ''} @@ -126,8 +127,8 @@ const ChangePasswordForm = () => { </div> <div> <Button - variant='primary' - className='w-full' + variant="primary" + className="w-full" onClick={handleSubmit} disabled={isPending || !password || !confirmPassword} > diff --git a/web/config/index.spec.ts b/web/config/index.spec.ts index fd6b541006..7b1d91186d 100644 --- a/web/config/index.spec.ts +++ b/web/config/index.spec.ts @@ -1,11 +1,10 @@ -import { validPassword } from './index' -import { VAR_REGEX, resetReg } from './index' +import { resetReg, validPassword, VAR_REGEX } from './index' describe('config test', () => { const passwordRegex = validPassword // Valid passwords - test('Valid passwords: contains letter+digit, length ≥8', () => { + it('Valid passwords: contains letter+digit, length ≥8', () => { expect(passwordRegex.test('password1')).toBe(true) expect(passwordRegex.test('PASSWORD1')).toBe(true) expect(passwordRegex.test('12345678a')).toBe(true) @@ -15,40 +14,40 @@ describe('config test', () => { }) // Missing letter - test('Invalid passwords: missing letter', () => { + it('Invalid passwords: missing letter', () => { expect(passwordRegex.test('12345678')).toBe(false) expect(passwordRegex.test('!@#$%^&*123')).toBe(false) }) // Missing digit - test('Invalid passwords: missing digit', () => { + it('Invalid passwords: missing digit', () => { expect(passwordRegex.test('password')).toBe(false) expect(passwordRegex.test('PASSWORD')).toBe(false) expect(passwordRegex.test('AbCdEfGh')).toBe(false) }) // Too short - test('Invalid passwords: less than 8 characters', () => { + it('Invalid passwords: less than 8 characters', () => { expect(passwordRegex.test('pass1')).toBe(false) expect(passwordRegex.test('abc123')).toBe(false) expect(passwordRegex.test('1a')).toBe(false) }) // Boundary test - test('Boundary test: exactly 8 characters', () => { + it('Boundary test: exactly 8 characters', () => { expect(passwordRegex.test('abc12345')).toBe(true) expect(passwordRegex.test('1abcdefg')).toBe(true) }) // Special characters - test('Special characters: non-whitespace special chars allowed', () => { + it('Special characters: non-whitespace special chars allowed', () => { expect(passwordRegex.test('pass@123')).toBe(true) expect(passwordRegex.test('p@$$w0rd')).toBe(true) expect(passwordRegex.test('!1aBcDeF')).toBe(true) }) // Contains whitespace - test('Invalid passwords: contains whitespace', () => { + it('Invalid passwords: contains whitespace', () => { expect(passwordRegex.test('pass word1')).toBe(false) expect(passwordRegex.test('password1 ')).toBe(false) expect(passwordRegex.test(' password1')).toBe(false) diff --git a/web/config/index.ts b/web/config/index.ts index 508a94f3f0..96e0f7bc4a 100644 --- a/web/config/index.ts +++ b/web/config/index.ts @@ -1,19 +1,21 @@ +import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations' import { InputVarType } from '@/app/components/workflow/types' -import { AgentStrategy } from '@/types/app' import { PromptRole } from '@/models/debug' import { PipelineInputVarType } from '@/models/pipeline' +import { AgentStrategy } from '@/types/app' import { DatasetAttr } from '@/types/feature' import pkg from '../package.json' -import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations' const getBooleanConfig = ( envVar: string | undefined, dataAttrKey: DatasetAttr, defaultValue: boolean = true, ) => { - if (envVar !== undefined && envVar !== '') return envVar === 'true' + if (envVar !== undefined && envVar !== '') + return envVar === 'true' const attrValue = globalThis.document?.body?.getAttribute(dataAttrKey) - if (attrValue !== undefined && attrValue !== '') return attrValue === 'true' + if (attrValue !== undefined && attrValue !== '') + return attrValue === 'true' return defaultValue } @@ -24,13 +26,15 @@ const getNumberConfig = ( ) => { if (envVar) { const parsed = Number.parseInt(envVar) - if (!Number.isNaN(parsed) && parsed > 0) return parsed + if (!Number.isNaN(parsed) && parsed > 0) + return parsed } const attrValue = globalThis.document?.body?.getAttribute(dataAttrKey) if (attrValue) { const parsed = Number.parseInt(attrValue) - if (!Number.isNaN(parsed) && parsed > 0) return parsed + if (!Number.isNaN(parsed) && parsed > 0) + return parsed } return defaultValue } @@ -40,10 +44,12 @@ const getStringConfig = ( dataAttrKey: DatasetAttr, defaultValue: string, ) => { - if (envVar) return envVar + if (envVar) + return envVar const attrValue = globalThis.document?.body?.getAttribute(dataAttrKey) - if (attrValue) return attrValue + if (attrValue) + return attrValue return defaultValue } @@ -159,7 +165,8 @@ const COOKIE_DOMAIN = getStringConfig( '', ).trim() export const CSRF_COOKIE_NAME = () => { - if (COOKIE_DOMAIN) return 'csrf_token' + if (COOKIE_DOMAIN) + return 'csrf_token' const isSecure = API_PREFIX.startsWith('https://') return isSecure ? '__Host-csrf_token' : 'csrf_token' } @@ -179,7 +186,8 @@ export const emailRegex = /^[\w.!#$%&'*+\-/=?^{|}~]+@([\w-]+\.)+[\w-]{2,}$/m const MAX_ZN_VAR_NAME_LENGTH = 8 const MAX_EN_VAR_VALUE_LENGTH = 30 export const getMaxVarNameLength = (value: string) => { - if (zhRegex.test(value)) return MAX_ZN_VAR_NAME_LENGTH + if (zhRegex.test(value)) + return MAX_ZN_VAR_NAME_LENGTH return MAX_EN_VAR_VALUE_LENGTH } @@ -324,7 +332,7 @@ Thought: {{agent_scratchpad}} } export const VAR_REGEX - = /\{\{(#[a-zA-Z0-9_-]{1,50}(\.\d+)?(\.[a-zA-Z_]\w{0,29}){1,10}#)\}\}/gi + = /\{\{(#[\w-]{1,50}(\.\d+)?(\.[a-z_]\w{0,29}){1,10}#)\}\}/gi export const resetReg = () => (VAR_REGEX.lastIndex = 0) @@ -398,7 +406,7 @@ export const ENABLE_SINGLE_DOLLAR_LATEX = getBooleanConfig( export const VALUE_SELECTOR_DELIMITER = '@@@' -export const validPassword = /^(?=.*[a-zA-Z])(?=.*\d)\S{8,}$/ +export const validPassword = /^(?=.*[a-z])(?=.*\d)\S{8,}$/i export const ZENDESK_WIDGET_KEY = getStringConfig( process.env.NEXT_PUBLIC_ZENDESK_WIDGET_KEY, diff --git a/web/context/access-control-store.ts b/web/context/access-control-store.ts index 3a80d7c865..1cb8eb9848 100644 --- a/web/context/access-control-store.ts +++ b/web/context/access-control-store.ts @@ -1,7 +1,7 @@ -import { create } from 'zustand' import type { AccessControlAccount, AccessControlGroup } from '@/models/access-control' -import { AccessMode } from '@/models/access-control' import type { App } from '@/types/app' +import { create } from 'zustand' +import { AccessMode } from '@/models/access-control' type AccessControlStore = { appId: App['id'] diff --git a/web/context/app-context.tsx b/web/context/app-context.tsx index 48d67c3611..b7a47048f3 100644 --- a/web/context/app-context.tsx +++ b/web/context/app-context.tsx @@ -1,21 +1,21 @@ 'use client' +import type { FC, ReactNode } from 'react' +import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common' +import { useQueryClient } from '@tanstack/react-query' +import { noop } from 'lodash-es' import { useCallback, useEffect, useMemo } from 'react' import { createContext, useContext, useContextSelector } from 'use-context-selector' -import type { FC, ReactNode } from 'react' -import { useQueryClient } from '@tanstack/react-query' +import { setUserId, setUserProperties } from '@/app/components/base/amplitude' +import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils' +import MaintenanceNotice from '@/app/components/header/maintenance-notice' +import { ZENDESK_FIELD_IDS } from '@/config' import { useCurrentWorkspace, useLangGeniusVersion, useUserProfile, } from '@/service/use-common' -import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common' -import MaintenanceNotice from '@/app/components/header/maintenance-notice' -import { noop } from 'lodash-es' -import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils' -import { ZENDESK_FIELD_IDS } from '@/config' import { useGlobalPublicStore } from './global-public-context' -import { setUserId, setUserProperties } from '@/app/components/base/amplitude' export type AppContextValue = { userProfile: UserProfileResponse @@ -195,10 +195,11 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) => isCurrentWorkspaceDatasetOperator, mutateCurrentWorkspace, isLoadingCurrentWorkspace, - }}> - <div className='flex h-full flex-col overflow-y-auto'> + }} + > + <div className="flex h-full flex-col overflow-y-auto"> {globalThis.document?.body?.getAttribute('data-public-maintenance-notice') && <MaintenanceNotice />} - <div className='relative flex grow flex-col overflow-y-auto overflow-x-hidden bg-background-body'> + <div className="relative flex grow flex-col overflow-y-auto overflow-x-hidden bg-background-body"> {children} </div> </div> diff --git a/web/context/dataset-detail.ts b/web/context/dataset-detail.ts index 81213bb14b..3aa88c584d 100644 --- a/web/context/dataset-detail.ts +++ b/web/context/dataset-detail.ts @@ -1,7 +1,7 @@ -import { createContext, useContext, useContextSelector } from 'use-context-selector' -import type { DataSet } from '@/models/datasets' -import type { IndexingType } from '@/app/components/datasets/create/step-two' import type { QueryObserverResult, RefetchOptions } from '@tanstack/react-query' +import type { IndexingType } from '@/app/components/datasets/create/step-two' +import type { DataSet } from '@/models/datasets' +import { createContext, useContext, useContextSelector } from 'use-context-selector' type DatasetDetailContextValue = { indexingTechnique?: IndexingType diff --git a/web/context/datasets-context.tsx b/web/context/datasets-context.tsx index e3dc38d78d..4ca7ad311e 100644 --- a/web/context/datasets-context.tsx +++ b/web/context/datasets-context.tsx @@ -1,8 +1,8 @@ 'use client' -import { createContext, useContext } from 'use-context-selector' import type { DataSet } from '@/models/datasets' import { noop } from 'lodash-es' +import { createContext, useContext } from 'use-context-selector' export type DatasetsContextValue = { datasets: DataSet[] diff --git a/web/context/debug-configuration.ts b/web/context/debug-configuration.ts index 5301835f12..51ba4ab626 100644 --- a/web/context/debug-configuration.ts +++ b/web/context/debug-configuration.ts @@ -1,6 +1,8 @@ import type { RefObject } from 'react' -import { createContext, useContext } from 'use-context-selector' -import { PromptMode } from '@/models/debug' +import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { Collection } from '@/app/components/tools/types' +import type { ExternalDataTool } from '@/models/common' +import type { DataSet } from '@/models/datasets' import type { AnnotationReplyConfig, BlockStatus, @@ -19,15 +21,12 @@ import type { SuggestedQuestionsAfterAnswerConfig, TextToSpeechConfig, } from '@/models/debug' -import type { ExternalDataTool } from '@/models/common' -import type { DataSet } from '@/models/datasets' import type { VisionSettings } from '@/types/app' -import { AppModeEnum } from '@/types/app' -import { ModelModeType, RETRIEVE_TYPE, Resolution, TransferMethod } from '@/types/app' -import { ANNOTATION_DEFAULT, DEFAULT_AGENT_SETTING, DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config' -import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations' -import type { Collection } from '@/app/components/tools/types' import { noop } from 'lodash-es' +import { createContext, useContext } from 'use-context-selector' +import { ANNOTATION_DEFAULT, DEFAULT_AGENT_SETTING, DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config' +import { PromptMode } from '@/models/debug' +import { AppModeEnum, ModelModeType, Resolution, RETRIEVE_TYPE, TransferMethod } from '@/types/app' type IDebugConfiguration = { appId: string diff --git a/web/context/event-emitter.tsx b/web/context/event-emitter.tsx index d31e32e8aa..61a605cabf 100644 --- a/web/context/event-emitter.tsx +++ b/web/context/event-emitter.tsx @@ -1,8 +1,8 @@ 'use client' -import { createContext, useContext } from 'use-context-selector' -import { useEventEmitter } from 'ahooks' import type { EventEmitter } from 'ahooks/lib/useEventEmitter' +import { useEventEmitter } from 'ahooks' +import { createContext, useContext } from 'use-context-selector' const EventEmitterContext = createContext<{ eventEmitter: EventEmitter<string> | null }>({ eventEmitter: null, diff --git a/web/context/explore-context.ts b/web/context/explore-context.ts index d8d64fb34c..688b9036f9 100644 --- a/web/context/explore-context.ts +++ b/web/context/explore-context.ts @@ -1,6 +1,6 @@ -import { createContext } from 'use-context-selector' import type { InstalledApp } from '@/models/explore' import { noop } from 'lodash-es' +import { createContext } from 'use-context-selector' type IExplore = { controlUpdateInstalledApps: number diff --git a/web/context/external-knowledge-api-context.tsx b/web/context/external-knowledge-api-context.tsx index 9bf6ece70b..b137e8ca5e 100644 --- a/web/context/external-knowledge-api-context.tsx +++ b/web/context/external-knowledge-api-context.tsx @@ -1,8 +1,8 @@ 'use client' -import { createContext, useCallback, useContext, useMemo } from 'react' import type { FC, ReactNode } from 'react' import type { ExternalAPIItem, ExternalAPIListResponse } from '@/models/datasets' +import { createContext, useCallback, useContext, useMemo } from 'react' import { useExternalKnowledgeApiList } from '@/service/knowledge/use-dataset' type ExternalKnowledgeApiContextType = { diff --git a/web/context/global-public-context.tsx b/web/context/global-public-context.tsx index 324ac019c8..c2742bb7a9 100644 --- a/web/context/global-public-context.tsx +++ b/web/context/global-public-context.tsx @@ -1,12 +1,12 @@ 'use client' -import { create } from 'zustand' -import { useQuery } from '@tanstack/react-query' import type { FC, PropsWithChildren } from 'react' -import { useEffect } from 'react' import type { SystemFeatures } from '@/types/feature' -import { defaultSystemFeatures } from '@/types/feature' -import { getSystemFeatures } from '@/service/common' +import { useQuery } from '@tanstack/react-query' +import { useEffect } from 'react' +import { create } from 'zustand' import Loading from '@/app/components/base/loading' +import { getSystemFeatures } from '@/service/common' +import { defaultSystemFeatures } from '@/types/feature' type GlobalPublicStore = { isGlobalPending: boolean @@ -40,7 +40,7 @@ const GlobalPublicStoreProvider: FC<PropsWithChildren> = ({ }, [isPending, setIsPending]) if (isPending) - return <div className='flex h-screen w-screen items-center justify-center'><Loading /></div> + return <div className="flex h-screen w-screen items-center justify-center"><Loading /></div> return <>{children}</> } export default GlobalPublicStoreProvider diff --git a/web/context/hooks/use-trigger-events-limit-modal.ts b/web/context/hooks/use-trigger-events-limit-modal.ts index ac02acc025..403df58378 100644 --- a/web/context/hooks/use-trigger-events-limit-modal.ts +++ b/web/context/hooks/use-trigger-events-limit-modal.ts @@ -1,9 +1,10 @@ -import { type Dispatch, type SetStateAction, useCallback, useEffect, useRef, useState } from 'react' +import type { Dispatch, SetStateAction } from 'react' +import type { ModalState } from '../modal-context' import dayjs from 'dayjs' +import { useCallback, useEffect, useRef, useState } from 'react' import { NUM_INFINITE } from '@/app/components/billing/config' import { Plan } from '@/app/components/billing/type' import { IS_CLOUD_EDITION } from '@/config' -import type { ModalState } from '../modal-context' export type TriggerEventsLimitModalPayload = { usage: number diff --git a/web/context/i18n.ts b/web/context/i18n.ts index 6364cbf219..773569fa21 100644 --- a/web/context/i18n.ts +++ b/web/context/i18n.ts @@ -1,10 +1,10 @@ +import type { Locale } from '@/i18n-config' +import { noop } from 'lodash-es' import { createContext, useContext, } from 'use-context-selector' -import type { Locale } from '@/i18n-config' import { getDocLanguage, getLanguage, getPricingPageLanguage } from '@/i18n-config/language' -import { noop } from 'lodash-es' type II18NContext = { locale: Locale diff --git a/web/context/mitt-context.tsx b/web/context/mitt-context.tsx index 2b437b0c30..6c6209b5a5 100644 --- a/web/context/mitt-context.tsx +++ b/web/context/mitt-context.tsx @@ -1,6 +1,6 @@ +import { noop } from 'lodash-es' import { createContext, useContext, useContextSelector } from 'use-context-selector' import { useMitt } from '@/hooks/use-mitt' -import { noop } from 'lodash-es' type ContextValueType = ReturnType<typeof useMitt> export const MittContext = createContext<ContextValueType>({ diff --git a/web/context/modal-context.test.tsx b/web/context/modal-context.test.tsx index 5ea8422030..07a82939a0 100644 --- a/web/context/modal-context.test.tsx +++ b/web/context/modal-context.test.tsx @@ -1,8 +1,8 @@ -import React from 'react' import { act, render, screen, waitFor } from '@testing-library/react' -import { ModalContextProvider } from '@/context/modal-context' -import { Plan } from '@/app/components/billing/type' +import React from 'react' import { defaultPlan } from '@/app/components/billing/config' +import { Plan } from '@/app/components/billing/type' +import { ModalContextProvider } from '@/context/modal-context' vi.mock('@/config', async (importOriginal) => { const actual = await importOriginal<typeof import('@/config')>() diff --git a/web/context/modal-context.tsx b/web/context/modal-context.tsx index 7f08045993..2afd1b7b2f 100644 --- a/web/context/modal-context.tsx +++ b/web/context/modal-context.tsx @@ -1,45 +1,46 @@ 'use client' import type { Dispatch, SetStateAction } from 'react' -import { useCallback, useEffect, useState } from 'react' -import { createContext, useContext, useContextSelector } from 'use-context-selector' -import { useSearchParams } from 'next/navigation' +import type { TriggerEventsLimitModalPayload } from './hooks/use-trigger-events-limit-modal' +import type { OpeningStatement } from '@/app/components/base/features/types' +import type { CreateExternalAPIReq } from '@/app/components/datasets/external-api/declarations' +import type { AccountSettingTab } from '@/app/components/header/account-setting/constants' import type { ConfigurationMethodEnum, Credential, CustomConfigurationModelFixedFields, CustomModel, + ModelModalModeEnum, ModelProvider, } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { - EDUCATION_PRICING_SHOW_ACTION, - EDUCATION_VERIFYING_LOCALSTORAGE_ITEM, -} from '@/app/education-apply/constants' -import type { AccountSettingTab } from '@/app/components/header/account-setting/constants' +import type { ModelLoadBalancingModalProps } from '@/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal' +import type { UpdatePluginPayload } from '@/app/components/plugins/types' +import type { InputVar } from '@/app/components/workflow/types' +import type { ExpireNoticeModalPayloadProps } from '@/app/education-apply/expire-notice-modal' +import type { + ApiBasedExtension, + ExternalDataTool, +} from '@/models/common' +import type { ModerationConfig, PromptVariable } from '@/models/debug' +import { noop } from 'lodash-es' +import dynamic from 'next/dynamic' +import { useSearchParams } from 'next/navigation' +import { useCallback, useEffect, useState } from 'react' +import { createContext, useContext, useContextSelector } from 'use-context-selector' import { ACCOUNT_SETTING_MODAL_ACTION, DEFAULT_ACCOUNT_SETTING_TAB, isValidAccountSettingTab, } from '@/app/components/header/account-setting/constants' -import type { ModerationConfig, PromptVariable } from '@/models/debug' -import type { - ApiBasedExtension, - ExternalDataTool, -} from '@/models/common' -import type { CreateExternalAPIReq } from '@/app/components/datasets/external-api/declarations' -import type { ModelLoadBalancingModalProps } from '@/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal' -import type { OpeningStatement } from '@/app/components/base/features/types' -import type { InputVar } from '@/app/components/workflow/types' -import type { UpdatePluginPayload } from '@/app/components/plugins/types' -import { removeSpecificQueryParam } from '@/utils' -import { noop } from 'lodash-es' -import dynamic from 'next/dynamic' -import type { ExpireNoticeModalPayloadProps } from '@/app/education-apply/expire-notice-modal' -import type { ModelModalModeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { useProviderContext } from '@/context/provider-context' -import { useAppContext } from '@/context/app-context' import { - type TriggerEventsLimitModalPayload, + EDUCATION_PRICING_SHOW_ACTION, + EDUCATION_VERIFYING_LOCALSTORAGE_ITEM, +} from '@/app/education-apply/constants' +import { useAppContext } from '@/context/app-context' +import { useProviderContext } from '@/context/provider-context' +import { removeSpecificQueryParam } from '@/utils' +import { + useTriggerEventsLimitModal, } from './hooks/use-trigger-events-limit-modal' @@ -92,7 +93,7 @@ export type ModalState<T> = { onEditCallback?: (newPayload: T) => void onValidateBeforeSaveCallback?: (newPayload: T) => boolean isEditMode?: boolean - datasetBindings?: { id: string; name: string }[] + datasetBindings?: { id: string, name: string }[] } export type ModelModalType = { @@ -358,7 +359,8 @@ export const ModalContextProvider = ({ setShowUpdatePluginModal, setShowEducationExpireNoticeModal, setShowTriggerEventsLimitModal, - }}> + }} + > <> {children} { @@ -410,7 +412,8 @@ export const ModalContextProvider = ({ showAnnotationFullModal && ( <AnnotationFullModal show={showAnnotationFullModal} - onHide={() => setShowAnnotationFullModal(false)} /> + onHide={() => setShowAnnotationFullModal(false)} + /> ) } { @@ -478,7 +481,8 @@ export const ModalContextProvider = ({ {...showEducationExpireNoticeModal.payload} onClose={() => setShowEducationExpireNoticeModal(null)} /> - )} + ) + } { !!showTriggerEventsLimitModal && ( <TriggerEventsLimitModal @@ -496,7 +500,8 @@ export const ModalContextProvider = ({ handleShowPricingModal() }} /> - )} + ) + } </> </ModalContext.Provider> ) diff --git a/web/context/provider-context-mock.spec.tsx b/web/context/provider-context-mock.spec.tsx index ae2d634a5d..5b5f71c972 100644 --- a/web/context/provider-context-mock.spec.tsx +++ b/web/context/provider-context-mock.spec.tsx @@ -1,8 +1,8 @@ -import { render } from '@testing-library/react' import type { UsagePlanInfo } from '@/app/components/billing/type' +import { render } from '@testing-library/react' +import { createMockPlan, createMockPlanReset, createMockPlanTotal, createMockPlanUsage } from '@/__mocks__/provider-context' import { Plan } from '@/app/components/billing/type' import ProviderContextMock from './provider-context-mock' -import { createMockPlan, createMockPlanReset, createMockPlanTotal, createMockPlanUsage } from '@/__mocks__/provider-context' let mockPlan: Plan = Plan.sandbox const usage: UsagePlanInfo = { diff --git a/web/context/provider-context.tsx b/web/context/provider-context.tsx index e1739853c6..eb2a034f3b 100644 --- a/web/context/provider-context.tsx +++ b/web/context/provider-context.tsx @@ -1,34 +1,33 @@ 'use client' -import { createContext, useContext, useContextSelector } from 'use-context-selector' -import { useEffect, useState } from 'react' -import dayjs from 'dayjs' -import { useTranslation } from 'react-i18next' +import type { Plan, UsagePlanInfo, UsageResetInfo } from '@/app/components/billing/type' +import type { Model, ModelProvider } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { RETRIEVE_METHOD } from '@/types/app' import { useQueryClient } from '@tanstack/react-query' +import dayjs from 'dayjs' +import { noop } from 'lodash-es' +import { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { createContext, useContext, useContextSelector } from 'use-context-selector' +import Toast from '@/app/components/base/toast' +import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils' +import { defaultPlan } from '@/app/components/billing/config' +import { parseCurrentPlan } from '@/app/components/billing/utils' +import { + CurrentSystemQuotaTypeEnum, + ModelStatusEnum, + ModelTypeEnum, +} from '@/app/components/header/account-setting/model-provider-page/declarations' +import { ZENDESK_FIELD_IDS } from '@/config' +import { fetchCurrentPlanInfo } from '@/service/billing' import { useModelListByType, useModelProviders, useSupportRetrievalMethods, } from '@/service/use-common' -import { - CurrentSystemQuotaTypeEnum, - ModelStatusEnum, - ModelTypeEnum, -} from '@/app/components/header/account-setting/model-provider-page/declarations' -import type { Model, ModelProvider } from '@/app/components/header/account-setting/model-provider-page/declarations' -import type { RETRIEVE_METHOD } from '@/types/app' -import type { Plan, UsageResetInfo } from '@/app/components/billing/type' -import type { UsagePlanInfo } from '@/app/components/billing/type' -import { fetchCurrentPlanInfo } from '@/service/billing' -import { parseCurrentPlan } from '@/app/components/billing/utils' -import { defaultPlan } from '@/app/components/billing/config' -import Toast from '@/app/components/base/toast' import { useEducationStatus, } from '@/service/use-education' -import { noop } from 'lodash-es' -import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils' -import { ZENDESK_FIELD_IDS } from '@/config' export type ProviderContextState = { modelProviders: ModelProvider[] @@ -61,7 +60,7 @@ export type ProviderContextState = { size: number limit: number } - }, + } refreshLicenseLimit: () => void isAllowTransferWorkspace: boolean isAllowPublishAsCustomKnowledgePipelineTemplate: boolean @@ -251,7 +250,8 @@ export const ProviderContextProvider = ({ refreshLicenseLimit: fetchPlan, isAllowTransferWorkspace, isAllowPublishAsCustomKnowledgePipelineTemplate, - }}> + }} + > {children} </ProviderContext.Provider> ) diff --git a/web/context/query-client.tsx b/web/context/query-client.tsx index 3deccba439..da95491630 100644 --- a/web/context/query-client.tsx +++ b/web/context/query-client.tsx @@ -16,8 +16,10 @@ const client = new QueryClient({ export const TanstackQueryInitializer: FC<PropsWithChildren> = (props) => { const { children } = props - return <QueryClientProvider client={client}> - {children} - <ReactQueryDevtools initialIsOpen={false} /> - </QueryClientProvider> + return ( + <QueryClientProvider client={client}> + {children} + <ReactQueryDevtools initialIsOpen={false} /> + </QueryClientProvider> + ) } diff --git a/web/context/web-app-context.tsx b/web/context/web-app-context.tsx index 1b189cd452..e6680c95a5 100644 --- a/web/context/web-app-context.tsx +++ b/web/context/web-app-context.tsx @@ -1,15 +1,15 @@ 'use client' -import type { ChatConfig } from '@/app/components/base/chat/types' -import Loading from '@/app/components/base/loading' -import { AccessMode } from '@/models/access-control' -import type { AppData, AppMeta } from '@/models/share' -import { useGetWebAppAccessModeByCode } from '@/service/use-share' -import { usePathname, useSearchParams } from 'next/navigation' import type { FC, PropsWithChildren } from 'react' +import type { ChatConfig } from '@/app/components/base/chat/types' +import type { AppData, AppMeta } from '@/models/share' +import { usePathname, useSearchParams } from 'next/navigation' import { useEffect } from 'react' import { create } from 'zustand' import { getProcessedSystemVariablesFromUrlParams } from '@/app/components/base/chat/utils' +import Loading from '@/app/components/base/loading' +import { AccessMode } from '@/models/access-control' +import { useGetWebAppAccessModeByCode } from '@/service/use-share' import { useGlobalPublicStore } from './global-public-context' type WebAppStore = { @@ -112,9 +112,11 @@ const WebAppStoreProvider: FC<PropsWithChildren> = ({ children }) => { }, [accessModeResult, updateWebAppAccessMode, shareCode]) if (isGlobalPending || isLoading) { - return <div className='flex h-full w-full items-center justify-center'> - <Loading /> - </div> + return ( + <div className="flex h-full w-full items-center justify-center"> + <Loading /> + </div> + ) } return ( <> diff --git a/web/context/workspace-context.tsx b/web/context/workspace-context.tsx index da7dcf5a50..3834641bc1 100644 --- a/web/context/workspace-context.tsx +++ b/web/context/workspace-context.tsx @@ -1,8 +1,8 @@ 'use client' +import type { IWorkspace } from '@/models/common' import { createContext, useContext } from 'use-context-selector' import { useWorkspaces } from '@/service/use-common' -import type { IWorkspace } from '@/models/common' export type WorkspacesContextValue = { workspaces: IWorkspace[] @@ -24,7 +24,8 @@ export const WorkspaceProvider = ({ return ( <WorkspacesContext.Provider value={{ workspaces: data?.workspaces || [], - }}> + }} + > {children} </WorkspacesContext.Provider> ) diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index ea2c961ad0..da425efb62 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -1,149 +1,65 @@ -import { - GLOB_TESTS, combine, javascript, node, - stylistic, typescript, unicorn, -} from '@antfu/eslint-config' -import globals from 'globals' -import storybook from 'eslint-plugin-storybook' -// import { fixupConfigRules } from '@eslint/compat' -import tailwind from 'eslint-plugin-tailwindcss' -import reactHooks from 'eslint-plugin-react-hooks' +// @ts-check +import antfu from '@antfu/eslint-config' import sonar from 'eslint-plugin-sonarjs' -import oxlint from 'eslint-plugin-oxlint' -import next from '@next/eslint-plugin-next' +import storybook from 'eslint-plugin-storybook' +import tailwind from 'eslint-plugin-tailwindcss' -// import reactRefresh from 'eslint-plugin-react-refresh' - -export default combine( - stylistic({ - lessOpinionated: true, - // original @antfu/eslint-config does not support jsx - jsx: false, - semi: false, - quotes: 'single', - overrides: { - // original config - 'style/indent': ['error', 2], - 'style/quotes': ['error', 'single'], - 'curly': ['error', 'multi-or-nest', 'consistent'], - 'style/comma-spacing': ['error', { before: false, after: true }], - 'style/quote-props': ['warn', 'consistent-as-needed'], - - // these options does not exist in old version - // maybe useless - 'style/indent-binary-ops': 'off', - 'style/multiline-ternary': 'off', - 'antfu/top-level-function': 'off', - 'antfu/curly': 'off', - 'antfu/consistent-chaining': 'off', - - // copy from eslint-config-antfu 0.36.0 - 'style/brace-style': ['error', 'stroustrup', { allowSingleLine: true }], - 'style/dot-location': ['error', 'property'], - 'style/object-curly-newline': ['error', { consistent: true, multiline: true }], - 'style/template-curly-spacing': ['error', 'never'], - 'style/keyword-spacing': 'off', - - // not exist in old version, and big change - 'style/member-delimiter-style': 'off', - }, - }), - javascript({ - overrides: { - // handled by unused-imports/no-unused-vars - 'no-unused-vars': 'off', - }, - }), - typescript({ - overrides: { - // original config - 'ts/consistent-type-definitions': ['warn', 'type'], - - // useful, but big change - 'ts/no-empty-object-type': 'off', - }, - }), - unicorn(), - node(), - // Next.js configuration +export default antfu( { - plugins: { - '@next/next': next, + react: { + overrides: { + 'react/no-context-provider': 'off', + 'react/no-forward-ref': 'off', + 'react/no-use-context': 'off', + }, }, - rules: { - ...next.configs.recommended.rules, - ...next.configs['core-web-vitals'].rules, - // performance issue, and not used. - '@next/next/no-html-link-for-pages': 'off', + nextjs: true, + ignores: ['public'], + typescript: { + overrides: { + 'ts/consistent-type-definitions': ['error', 'type'], + }, }, - }, - { - ignores: [ - 'storybook-static/**', - '**/node_modules/*', - '**/dist/', - '**/build/', - '**/out/', - '**/.next/', - '**/public/*', - '**/*.json', - '**/*.js', - ], - }, - { - // orignal config - rules: { - // orignal ts/no-var-requires - 'ts/no-require-imports': 'off', - 'no-console': 'off', - 'react/display-name': 'off', - 'array-callback-return': ['error', { - allowImplicit: false, - checkForEach: false, - }], - - // copy from eslint-config-antfu 0.36.0 - 'camelcase': 'off', - 'default-case-last': 'error', - - // antfu use eslint-plugin-perfectionist to replace this - // will cause big change, so keep the original sort-imports - 'sort-imports': [ - 'error', - { - ignoreCase: false, - ignoreDeclarationSort: true, - ignoreMemberSort: false, - memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'], - allowSeparatedGroups: false, - }, - ], - - // antfu migrate to eslint-plugin-unused-imports - 'unused-imports/no-unused-vars': 'warn', - 'unused-imports/no-unused-imports': 'warn', - - // We use `import { noop } from 'lodash-es'` across `web` project - 'no-empty-function': 'error', + test: { + overrides: { + 'test/prefer-lowercase-title': 'off', + }, }, - - languageOptions: { - globals: { - ...globals.browser, - ...globals.es2025, - ...globals.node, - React: 'readable', - JSX: 'readable', + stylistic: { + overrides: { + 'antfu/top-level-function': 'off', }, }, }, - storybook.configs['flat/recommended'], - // reactRefresh.configs.recommended, + // downgrade some rules from error to warn for gradual adoption + // we should fix these in following pull requests { - rules: reactHooks.configs.recommended.rules, - plugins: { - 'react-hooks': reactHooks, + // @keep-sorted + rules: { + 'next/inline-script-id': 'warn', + 'no-console': 'warn', + 'no-irregular-whitespace': 'warn', + 'no-unused-vars': 'warn', + 'node/prefer-global/buffer': 'warn', + 'node/prefer-global/process': 'warn', + 'react/no-create-ref': 'warn', + 'react/no-missing-key': 'warn', + 'react/no-nested-component-definitions': 'warn', + 'regexp/no-dupe-disjunctions': 'warn', + 'regexp/no-super-linear-backtracking': 'warn', + 'regexp/no-unused-capturing-group': 'warn', + 'regexp/no-useless-assertions': 'warn', + 'regexp/no-useless-quantifier': 'warn', + 'style/multiline-ternary': 'warn', + 'test/no-identical-title': 'warn', + 'test/prefer-hooks-in-order': 'warn', + 'ts/no-empty-object-type': 'warn', + 'ts/no-require-imports': 'warn', + 'unicorn/prefer-number-properties': 'warn', + 'unused-imports/no-unused-vars': 'warn', }, }, + storybook.configs['flat/recommended'], // sonar { rules: { @@ -180,6 +96,14 @@ export default combine( // others 'sonarjs/todo-tag': 'warn', 'sonarjs/table-header': 'off', + + // new from this update + 'sonarjs/unused-import': 'off', + 'sonarjs/use-type-alias': 'warn', + 'sonarjs/single-character-alternation': 'warn', + 'sonarjs/no-os-command-from-path': 'warn', + 'sonarjs/class-name': 'off', + 'sonarjs/no-redundant-jump': 'warn', }, plugins: { sonarjs: sonar, @@ -193,38 +117,6 @@ export default combine( 'max-lines': 'off', }, }, - // need further research - { - rules: { - // not exist in old version - 'antfu/consistent-list-newline': 'off', - 'node/prefer-global/process': 'off', - 'node/prefer-global/buffer': 'off', - 'node/no-callback-literal': 'off', - 'eslint-comments/no-unused-disable': 'off', - 'tailwindcss/no-arbitrary-value': 'off', - 'tailwindcss/classnames-order': 'off', - 'style/indent': ['error', 2, { - SwitchCase: 1, - ignoreComments: true, - - }], - // useful, but big change - 'unicorn/prefer-number-properties': 'warn', - 'unicorn/no-new-array': 'warn', - }, - }, - // suppress error for `no-undef` rule - { - files: GLOB_TESTS, - languageOptions: { - globals: { - ...globals.browser, - ...globals.es2021, - ...globals.node, - }, - }, - }, tailwind.configs['flat/recommended'], { settings: { @@ -263,5 +155,4 @@ export default combine( 'tailwindcss/migration-from-tailwind-2': 'warn', }, }, - ...oxlint.buildFromOxlintConfigFile('./.oxlintrc.json'), ) diff --git a/web/hooks/use-app-favicon.ts b/web/hooks/use-app-favicon.ts index e8a0173371..32b8f6893a 100644 --- a/web/hooks/use-app-favicon.ts +++ b/web/hooks/use-app-favicon.ts @@ -1,7 +1,7 @@ +import type { AppIconType } from '@/types/app' import { useAsyncEffect } from 'ahooks' import { appDefaultIconBackground } from '@/config' import { searchEmoji } from '@/utils/emoji' -import type { AppIconType } from '@/types/app' type UseAppFaviconOptions = { enable?: boolean @@ -31,11 +31,11 @@ export function useAppFavicon(options: UseAppFaviconOptions) { link.href = isValidImageIcon ? icon_url : 'data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22>' - + `<rect width=%22100%25%22 height=%22100%25%22 fill=%22${encodeURIComponent(icon_background || appDefaultIconBackground)}%22 rx=%2230%22 ry=%2230%22 />` - + `<text x=%2212.5%22 y=%221em%22 font-size=%2275%22>${ - icon ? await searchEmoji(icon) : '🤖' - }</text>` - + '</svg>' + + `<rect width=%22100%25%22 height=%22100%25%22 fill=%22${encodeURIComponent(icon_background || appDefaultIconBackground)}%22 rx=%2230%22 ry=%2230%22 />` + + `<text x=%2212.5%22 y=%221em%22 font-size=%2275%22>${ + icon ? await searchEmoji(icon) : '🤖' + }</text>` + + '</svg>' link.rel = 'shortcut icon' link.type = 'image/svg' diff --git a/web/hooks/use-document-title.spec.ts b/web/hooks/use-document-title.spec.ts index 27bfe6d3fe..3909978591 100644 --- a/web/hooks/use-document-title.spec.ts +++ b/web/hooks/use-document-title.spec.ts @@ -1,3 +1,5 @@ +import { act, renderHook } from '@testing-library/react' +import { useGlobalPublicStore } from '@/context/global-public-context' /** * Test suite for useDocumentTitle hook * @@ -11,9 +13,7 @@ * If no page title: "[Brand Name]" */ import { defaultSystemFeatures } from '@/types/feature' -import { act, renderHook } from '@testing-library/react' import useDocumentTitle from './use-document-title' -import { useGlobalPublicStore } from '@/context/global-public-context' vi.mock('@/service/common', () => ({ getSystemFeatures: vi.fn(() => ({ ...defaultSystemFeatures })), diff --git a/web/hooks/use-document-title.ts b/web/hooks/use-document-title.ts index 10a167a9ea..bb69aeb20f 100644 --- a/web/hooks/use-document-title.ts +++ b/web/hooks/use-document-title.ts @@ -1,8 +1,8 @@ 'use client' -import { useGlobalPublicStore } from '@/context/global-public-context' import { useFavicon, useTitle } from 'ahooks' -import { basePath } from '@/utils/var' import { useEffect } from 'react' +import { useGlobalPublicStore } from '@/context/global-public-context' +import { basePath } from '@/utils/var' export default function useDocumentTitle(title: string) { const isPending = useGlobalPublicStore(s => s.isGlobalPending) diff --git a/web/hooks/use-format-time-from-now.spec.ts b/web/hooks/use-format-time-from-now.spec.ts index 87e33b6467..c5236dfbe6 100644 --- a/web/hooks/use-format-time-from-now.spec.ts +++ b/web/hooks/use-format-time-from-now.spec.ts @@ -13,6 +13,9 @@ import type { Mock } from 'vitest' * - Returns human-readable relative time strings */ import { renderHook } from '@testing-library/react' +// Import after mock to get the mocked version +import { useI18N } from '@/context/i18n' + import { useFormatTimeFromNow } from './use-format-time-from-now' // Mock the i18n context @@ -22,9 +25,6 @@ vi.mock('@/context/i18n', () => ({ })), })) -// Import after mock to get the mocked version -import { useI18N } from '@/context/i18n' - describe('useFormatTimeFromNow', () => { beforeEach(() => { vi.clearAllMocks() @@ -315,10 +315,27 @@ describe('useFormatTimeFromNow', () => { */ it('should handle all mapped locales', () => { const locales = [ - 'en-US', 'zh-Hans', 'zh-Hant', 'pt-BR', 'es-ES', 'fr-FR', - 'de-DE', 'ja-JP', 'ko-KR', 'ru-RU', 'it-IT', 'th-TH', - 'id-ID', 'uk-UA', 'vi-VN', 'ro-RO', 'pl-PL', 'hi-IN', - 'tr-TR', 'fa-IR', 'sl-SI', + 'en-US', + 'zh-Hans', + 'zh-Hant', + 'pt-BR', + 'es-ES', + 'fr-FR', + 'de-DE', + 'ja-JP', + 'ko-KR', + 'ru-RU', + 'it-IT', + 'th-TH', + 'id-ID', + 'uk-UA', + 'vi-VN', + 'ro-RO', + 'pl-PL', + 'hi-IN', + 'tr-TR', + 'fa-IR', + 'sl-SI', ] const now = Date.now() diff --git a/web/hooks/use-format-time-from-now.ts b/web/hooks/use-format-time-from-now.ts index db3be93df2..09d8db7321 100644 --- a/web/hooks/use-format-time-from-now.ts +++ b/web/hooks/use-format-time-from-now.ts @@ -1,8 +1,8 @@ +import type { Locale } from '@/i18n-config' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import { useCallback } from 'react' import { useI18N } from '@/context/i18n' -import type { Locale } from '@/i18n-config' import 'dayjs/locale/de' import 'dayjs/locale/es' import 'dayjs/locale/fa' diff --git a/web/hooks/use-import-dsl.ts b/web/hooks/use-import-dsl.ts index e5fafb1e75..35aa701fbd 100644 --- a/web/hooks/use-import-dsl.ts +++ b/web/hooks/use-import-dsl.ts @@ -1,25 +1,25 @@ +import type { + DSLImportMode, + DSLImportResponse, +} from '@/models/app' +import type { AppIconType } from '@/types/app' +import { useRouter } from 'next/navigation' import { useCallback, useRef, useState, } from 'react' import { useTranslation } from 'react-i18next' -import { useRouter } from 'next/navigation' -import type { - DSLImportMode, - DSLImportResponse, -} from '@/models/app' +import { useToastContext } from '@/app/components/base/toast' +import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' +import { NEED_REFRESH_APP_LIST_KEY } from '@/config' +import { useSelector } from '@/context/app-context' import { DSLImportStatus } from '@/models/app' import { importDSL, importDSLConfirm, } from '@/service/apps' -import type { AppIconType } from '@/types/app' -import { useToastContext } from '@/app/components/base/toast' -import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' import { getRedirection } from '@/utils/app-redirection' -import { useSelector } from '@/context/app-context' -import { NEED_REFRESH_APP_LIST_KEY } from '@/config' type DSLPayload = { mode: DSLImportMode @@ -43,7 +43,7 @@ export const useImportDSL = () => { const { handleCheckPluginDependencies } = usePluginDependencies() const isCurrentWorkspaceEditor = useSelector(s => s.isCurrentWorkspaceEditor) const { push } = useRouter() - const [versions, setVersions] = useState<{ importedVersion: string; systemVersion: string }>() + const [versions, setVersions] = useState<{ importedVersion: string, systemVersion: string }>() const importIdRef = useRef<string>('') const handleImportDSL = useCallback(async ( diff --git a/web/hooks/use-metadata.ts b/web/hooks/use-metadata.ts index 436747c86e..a51e6b150e 100644 --- a/web/hooks/use-metadata.ts +++ b/web/hooks/use-metadata.ts @@ -1,8 +1,9 @@ 'use client' +import type { DocType } from '@/models/datasets' import { useTranslation } from 'react-i18next' -import { formatFileSize, formatNumber, formatTime } from '@/utils/format' -import { ChunkingMode, type DocType } from '@/models/datasets' import useTimestamp from '@/hooks/use-timestamp' +import { ChunkingMode } from '@/models/datasets' +import { formatFileSize, formatNumber, formatTime } from '@/utils/format' export type inputType = 'input' | 'select' | 'textarea' export type metadataType = DocType | 'originInfo' | 'technicalParameters' diff --git a/web/hooks/use-mitt.ts b/web/hooks/use-mitt.ts index 584636c8a6..e29396aefb 100644 --- a/web/hooks/use-mitt.ts +++ b/web/hooks/use-mitt.ts @@ -12,10 +12,10 @@ export type _Events = Record<EventType, unknown> export type UseSubscribeOption = { /** - * Whether the subscription is enabled. - * @default true - */ - enabled: boolean; + * Whether the subscription is enabled. + * @default true + */ + enabled: boolean } export type ExtendedOn<Events extends _Events> = { @@ -23,17 +23,17 @@ export type ExtendedOn<Events extends _Events> = { type: Key, handler: Handler<Events[Key]>, options?: UseSubscribeOption, - ): void; + ): void ( type: '*', handler: WildcardHandler<Events>, option?: UseSubscribeOption, - ): void; + ): void } export type UseMittReturn<Events extends _Events> = { - useSubscribe: ExtendedOn<Events>; - emit: Emitter<Events>['emit']; + useSubscribe: ExtendedOn<Events> + emit: Emitter<Events>['emit'] } const defaultSubscribeOption: UseSubscribeOption = { diff --git a/web/hooks/use-moderate.ts b/web/hooks/use-moderate.ts index 11b078fbd9..e42441e58e 100644 --- a/web/hooks/use-moderate.ts +++ b/web/hooks/use-moderate.ts @@ -1,5 +1,5 @@ -import { useEffect, useRef, useState } from 'react' import type { ModerationService } from '@/models/common' +import { useEffect, useRef, useState } from 'react' function splitStringByLength(inputString: string, chunkLength: number) { const resultArray = [] diff --git a/web/hooks/use-pay.tsx b/web/hooks/use-pay.tsx index 3812949dec..486fa299a7 100644 --- a/web/hooks/use-pay.tsx +++ b/web/hooks/use-pay.tsx @@ -1,9 +1,9 @@ 'use client' -import { useCallback, useEffect, useState } from 'react' -import { useRouter, useSearchParams } from 'next/navigation' -import { useTranslation } from 'react-i18next' import type { IConfirm } from '@/app/components/base/confirm' +import { useRouter, useSearchParams } from 'next/navigation' +import { useCallback, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import Confirm from '@/app/components/base/confirm' import { useNotionBinding } from '@/service/use-common' @@ -102,7 +102,7 @@ export const CheckModal = () => { onCancel={handleCancelShowPayStatusModal} onConfirm={handleCancelShowPayStatusModal} showCancel={false} - type={confirmInfo.type === 'info' ? 'info' : 'warning' } + type={confirmInfo.type === 'info' ? 'info' : 'warning'} title={confirmInfo.title} content={(confirmInfo as unknown as { desc: string }).desc || ''} confirmText={(confirmInfo.type === 'info' && t('common.operation.ok')) || ''} diff --git a/web/hooks/use-tab-searchparams.spec.ts b/web/hooks/use-tab-searchparams.spec.ts index 424f17d909..e724f323af 100644 --- a/web/hooks/use-tab-searchparams.spec.ts +++ b/web/hooks/use-tab-searchparams.spec.ts @@ -12,6 +12,9 @@ import type { Mock } from 'vitest' * navigation persistent and shareable across sessions. */ import { act, renderHook } from '@testing-library/react' +// Import after mocks +import { usePathname } from 'next/navigation' + import { useTabSearchParams } from './use-tab-searchparams' // Mock Next.js navigation hooks @@ -29,9 +32,6 @@ vi.mock('next/navigation', () => ({ useSearchParams: vi.fn(() => mockSearchParams), })) -// Import after mocks -import { usePathname } from 'next/navigation' - describe('useTabSearchParams', () => { beforeEach(() => { vi.clearAllMocks() diff --git a/web/hooks/use-theme.ts b/web/hooks/use-theme.ts index c814c7d9de..c9c2bdea55 100644 --- a/web/hooks/use-theme.ts +++ b/web/hooks/use-theme.ts @@ -1,5 +1,5 @@ -import { Theme } from '@/types/app' import { useTheme as useBaseTheme } from 'next-themes' +import { Theme } from '@/types/app' const useTheme = () => { const { theme, resolvedTheme, ...rest } = useBaseTheme() diff --git a/web/hooks/use-timestamp.ts b/web/hooks/use-timestamp.ts index 5242eb565a..05afa8e178 100644 --- a/web/hooks/use-timestamp.ts +++ b/web/hooks/use-timestamp.ts @@ -1,8 +1,8 @@ 'use client' -import { useCallback } from 'react' import dayjs from 'dayjs' -import utc from 'dayjs/plugin/utc' import timezone from 'dayjs/plugin/timezone' +import utc from 'dayjs/plugin/utc' +import { useCallback } from 'react' import { useAppContext } from '@/context/app-context' dayjs.extend(utc) diff --git a/web/i18n-config/README.md b/web/i18n-config/README.md index fd4ef30833..0fe8922345 100644 --- a/web/i18n-config/README.md +++ b/web/i18n-config/README.md @@ -79,7 +79,6 @@ export type I18nText = { 4. Add the new language to the `language.json` file. ```typescript - export const languages = [ { value: 'en-US', @@ -172,7 +171,7 @@ export const languages = [ supported: true, }, // Add your language here 👇 - ... + // ... // Add your language here 👆 ] ``` diff --git a/web/i18n-config/auto-gen-i18n.js b/web/i18n-config/auto-gen-i18n.js index 3ab76c8ed4..561fa95869 100644 --- a/web/i18n-config/auto-gen-i18n.js +++ b/web/i18n-config/auto-gen-i18n.js @@ -1,11 +1,11 @@ import fs from 'node:fs' -import path from 'node:path' -import vm from 'node:vm' -import { fileURLToPath } from 'node:url' import { createRequire } from 'node:module' -import { transpile } from 'typescript' -import { parseModule, generateCode, loadFile } from 'magicast' +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import vm from 'node:vm' import { translate } from 'bing-translate-api' +import { generateCode, loadFile, parseModule } from 'magicast' +import { transpile } from 'typescript' const require = createRequire(import.meta.url) const __filename = fileURLToPath(import.meta.url) @@ -42,7 +42,8 @@ function parseArgs(argv) { let cursor = startIndex + 1 while (cursor < argv.length && !argv[cursor].startsWith('--')) { const value = argv[cursor].trim() - if (value) values.push(value) + if (value) + values.push(value) cursor++ } return { values, nextIndex: cursor - 1 } @@ -127,7 +128,7 @@ function protectPlaceholders(text) { const patterns = [ /\{\{[^{}]+\}\}/g, // mustache /\$\{[^{}]+\}/g, // template expressions - /<[^>]+?>/g, // html-like tags + /<[^>]+>/g, // html-like tags ] patterns.forEach((pattern) => { @@ -160,7 +161,7 @@ async function translateText(source, toLanguage) { const { translation } = await translate(safeText, null, languageKeyMap[toLanguage]) return { value: restore(translation), skipped: false } } - catch (error) { + catch (error) { console.error(`❌ Error translating to ${toLanguage}:`, error.message) return { value: source, skipped: true, error: error.message } } @@ -310,7 +311,7 @@ export default translation } const { code } = generateCode(mod) - let res = `const translation =${code.replace('export default', '')} + const res = `const translation =${code.replace('export default', '')} export default translation `.replace(/,\n\n/g, ',\n').replace('};', '}') @@ -319,13 +320,13 @@ export default translation fs.writeFileSync(toGenLanguageFilePath, res) console.log(`💾 Saved translations to ${toGenLanguageFilePath}`) } - else { + else { console.log(`🔍 [DRY RUN] Would save translations to ${toGenLanguageFilePath}`) } return result } - catch (error) { + catch (error) { console.error(`Error processing file ${fullKeyFilePath}:`, error.message) throw error } diff --git a/web/i18n-config/check-i18n-sync.js b/web/i18n-config/check-i18n-sync.js index acf5ca7a20..af00d23875 100644 --- a/web/i18n-config/check-i18n-sync.js +++ b/web/i18n-config/check-i18n-sync.js @@ -1,12 +1,13 @@ #!/usr/bin/env node -import fs from 'fs' -import path from 'path' -import { fileURLToPath } from 'url' +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' import lodash from 'lodash' -const { camelCase } = lodash import ts from 'typescript' +const { camelCase } = lodash + const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) diff --git a/web/i18n-config/check-i18n.js b/web/i18n-config/check-i18n.js index 638a543610..096a4d7afc 100644 --- a/web/i18n-config/check-i18n.js +++ b/web/i18n-config/check-i18n.js @@ -1,8 +1,8 @@ import fs from 'node:fs' -import path from 'node:path' -import vm from 'node:vm' -import { fileURLToPath } from 'node:url' import { createRequire } from 'node:module' +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import vm from 'node:vm' import { transpile } from 'typescript' const require = createRequire(import.meta.url) @@ -11,6 +11,7 @@ const __dirname = path.dirname(__filename) const targetLanguage = 'en-US' const data = require('./languages.json') + const languages = data.languages.filter(language => language.supported).map(language => language.value) function parseArgs(argv) { @@ -27,7 +28,8 @@ function parseArgs(argv) { let cursor = startIndex + 1 while (cursor < argv.length && !argv[cursor].startsWith('--')) { const value = argv[cursor].trim() - if (value) values.push(value) + if (value) + values.push(value) cursor++ } return { values, nextIndex: cursor - 1 } @@ -124,8 +126,7 @@ async function getKeysFromLanguage(language) { const filePath = path.join(folderPath, file) const fileName = file.replace(/\.[^/.]+$/, '') // Remove file extension const camelCaseFileName = fileName.replace(/[-_](.)/g, (_, c) => - c.toUpperCase(), - ) // Convert to camel case + c.toUpperCase()) // Convert to camel case try { const content = fs.readFileSync(filePath, 'utf8') @@ -147,7 +148,7 @@ async function getKeysFromLanguage(language) { // Extract the translation object const translationObj = moduleExports.default || moduleExports - if(!translationObj || typeof translationObj !== 'object') { + if (!translationObj || typeof translationObj !== 'object') { console.error(`Error parsing file: ${filePath}`) reject(new Error(`Error parsing file: ${filePath}`)) return @@ -161,7 +162,7 @@ async function getKeysFromLanguage(language) { // This is an object (but not array), recurse into it but don't add it as a key iterateKeys(obj[key], nestedKey) } - else { + else { // This is a leaf node (string, number, boolean, array, etc.), add it as a key nestedKeys.push(nestedKey) } @@ -173,7 +174,7 @@ async function getKeysFromLanguage(language) { const fileKeys = nestedKeys.map(key => `${camelCaseFileName}.${key}`) allKeys.push(...fileKeys) } - catch (error) { + catch (error) { console.error(`Error processing file ${filePath}:`, error.message) reject(error) } @@ -193,7 +194,7 @@ function removeKeysFromObject(obj, keysToRemove, prefix = '') { modified = true console.log(`🗑️ Removed key: ${fullKey}`) } - else if (typeof obj[key] === 'object' && obj[key] !== null) { + else if (typeof obj[key] === 'object' && obj[key] !== null) { const subModified = removeKeysFromObject(obj[key], keysToRemove, fullKey) modified = modified || subModified } @@ -246,7 +247,7 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) { } } } - else { + else { // Nested key - need to find the exact path const currentPath = [] let braceDepth = 0 @@ -256,12 +257,12 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) { const trimmedLine = line.trim() // Track current object path - const keyMatch = trimmedLine.match(/^(\w+)\s*:\s*{/) + const keyMatch = trimmedLine.match(/^(\w+)\s*:\s*\{/) if (keyMatch) { currentPath.push(keyMatch[1]) braceDepth++ } - else if (trimmedLine === '},' || trimmedLine === '}') { + else if (trimmedLine === '},' || trimmedLine === '}') { if (braceDepth > 0) { braceDepth-- currentPath.pop() @@ -316,11 +317,12 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) { // Check if this line ends the value (ends with quote and comma/no comma) if ((trimmed.endsWith('\',') || trimmed.endsWith('",') || trimmed.endsWith('`,') - || trimmed.endsWith('\'') || trimmed.endsWith('"') || trimmed.endsWith('`')) - && !trimmed.startsWith('//')) + || trimmed.endsWith('\'') || trimmed.endsWith('"') || trimmed.endsWith('`')) + && !trimmed.startsWith('//')) { break + } } - else { + else { break } @@ -332,7 +334,7 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) { console.log(`🗑️ Found key to remove: ${keyToRemove} at line ${targetLineIndex + 1}${linesToRemoveForKey.length > 1 ? ` (multiline, ${linesToRemoveForKey.length} lines)` : ''}`) modified = true } - else { + else { console.log(`⚠️ Could not find key: ${keyToRemove}`) } } @@ -365,7 +367,7 @@ async function removeExtraKeysFromFile(language, fileName, extraKeys) { return false } - catch (error) { + catch (error) { console.error(`Error processing file ${filePath}:`, error.message) return false } @@ -439,7 +441,8 @@ async function main() { let totalRemoved = 0 for (const fileName of files) { const removed = await removeExtraKeysFromFile(language, fileName, extraKeys) - if (removed) totalRemoved++ + if (removed) + totalRemoved++ } console.log(`✅ Auto-removal completed for ${language}. Modified ${totalRemoved} files.`) diff --git a/web/i18n-config/generate-i18n-types.js b/web/i18n-config/generate-i18n-types.js index a4c2234b83..0b3c0195af 100644 --- a/web/i18n-config/generate-i18n-types.js +++ b/web/i18n-config/generate-i18n-types.js @@ -1,8 +1,8 @@ #!/usr/bin/env node -import fs from 'fs' -import path from 'path' -import { fileURLToPath } from 'url' +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' import lodash from 'lodash' import ts from 'typescript' @@ -50,8 +50,8 @@ import 'react-i18next' // Extract types from translation files using typeof import pattern` // Generate individual type definitions - const typeDefinitions = namespaces.map(namespace => { - const typeName = camelCase(namespace).replace(/^\w/, c => c.toUpperCase()) + 'Messages' + const typeDefinitions = namespaces.map((namespace) => { + const typeName = `${camelCase(namespace).replace(/^\w/, c => c.toUpperCase())}Messages` return `type ${typeName} = typeof import('../i18n/en-US/${namespace}').default` }).join('\n') @@ -59,11 +59,11 @@ import 'react-i18next' const messagesInterface = ` // Complete type structure that matches i18next-config.ts camelCase conversion export type Messages = { -${namespaces.map(namespace => { - const camelCased = camelCase(namespace) - const typeName = camelCase(namespace).replace(/^\w/, c => c.toUpperCase()) + 'Messages' - return ` ${camelCased}: ${typeName};` - }).join('\n')} +${namespaces.map((namespace) => { + const camelCased = camelCase(namespace) + const typeName = `${camelCase(namespace).replace(/^\w/, c => c.toUpperCase())}Messages` + return ` ${camelCased}: ${typeName};` +}).join('\n')} }` const utilityTypes = ` @@ -133,13 +133,14 @@ function main() { } console.log('✅ Type definitions are in sync') - } else { + } + else { // Generate mode: write file fs.writeFileSync(outputPath, typeDefinitions) console.log(`✅ Generated type definitions: ${outputPath}`) } - - } catch (error) { + } + catch (error) { console.error('❌ Error:', error.message) process.exit(1) } diff --git a/web/i18n-config/i18next-config.ts b/web/i18n-config/i18next-config.ts index 37651ae191..b310d380e2 100644 --- a/web/i18n-config/i18next-config.ts +++ b/web/i18n-config/i18next-config.ts @@ -3,31 +3,31 @@ import i18n from 'i18next' import { camelCase } from 'lodash-es' import { initReactI18next } from 'react-i18next' +import app from '../i18n/en-US/app' // Static imports for en-US (fallback language) import appAnnotation from '../i18n/en-US/app-annotation' import appApi from '../i18n/en-US/app-api' import appDebug from '../i18n/en-US/app-debug' import appLog from '../i18n/en-US/app-log' import appOverview from '../i18n/en-US/app-overview' -import app from '../i18n/en-US/app' import billing from '../i18n/en-US/billing' import common from '../i18n/en-US/common' import custom from '../i18n/en-US/custom' +import dataset from '../i18n/en-US/dataset' import datasetCreation from '../i18n/en-US/dataset-creation' import datasetDocuments from '../i18n/en-US/dataset-documents' import datasetHitTesting from '../i18n/en-US/dataset-hit-testing' import datasetPipeline from '../i18n/en-US/dataset-pipeline' import datasetSettings from '../i18n/en-US/dataset-settings' -import dataset from '../i18n/en-US/dataset' import education from '../i18n/en-US/education' import explore from '../i18n/en-US/explore' import layout from '../i18n/en-US/layout' import login from '../i18n/en-US/login' import oauth from '../i18n/en-US/oauth' import pipeline from '../i18n/en-US/pipeline' +import plugin from '../i18n/en-US/plugin' import pluginTags from '../i18n/en-US/plugin-tags' import pluginTrigger from '../i18n/en-US/plugin-trigger' -import plugin from '../i18n/en-US/plugin' import register from '../i18n/en-US/register' import runLog from '../i18n/en-US/run-log' import share from '../i18n/en-US/share' @@ -141,7 +141,8 @@ if (!i18n.isInitialized) { } export const changeLanguage = async (lng?: string) => { - if (!lng) return + if (!lng) + return if (!i18n.hasResourceBundle(lng, 'translation')) { const resource = await loadLangResources(lng) i18n.addResourceBundle(lng, 'translation', resource, true, true) diff --git a/web/i18n-config/index.ts b/web/i18n-config/index.ts index b2b83fa76a..8a0f712f2a 100644 --- a/web/i18n-config/index.ts +++ b/web/i18n-config/index.ts @@ -1,7 +1,7 @@ import Cookies from 'js-cookie' -import { changeLanguage } from '@/i18n-config/i18next-config' import { LOCALE_COOKIE_NAME } from '@/config' +import { changeLanguage } from '@/i18n-config/i18next-config' import { LanguagesSupported } from '@/i18n-config/language' export const i18n = { @@ -23,8 +23,11 @@ export const getLocaleOnClient = (): Locale => { } export const renderI18nObject = (obj: Record<string, string>, language: string) => { - if (!obj) return '' - if (obj?.[language]) return obj[language] - if (obj?.en_US) return obj.en_US + if (!obj) + return '' + if (obj?.[language]) + return obj[language] + if (obj?.en_US) + return obj.en_US return Object.values(obj)[0] } diff --git a/web/i18n-config/language.ts b/web/i18n-config/language.ts index 20b3eb3ecc..a1fe6e790f 100644 --- a/web/i18n-config/language.ts +++ b/web/i18n-config/language.ts @@ -1,4 +1,5 @@ import data from './languages.json' + export type Item = { value: number | string name: string diff --git a/web/i18n-config/server.ts b/web/i18n-config/server.ts index 404a71cfaf..c4e008cf84 100644 --- a/web/i18n-config/server.ts +++ b/web/i18n-config/server.ts @@ -1,12 +1,12 @@ -import { cookies, headers } from 'next/headers' -import Negotiator from 'negotiator' +import type { Locale } from '.' import { match } from '@formatjs/intl-localematcher' - import { createInstance } from 'i18next' + import resourcesToBackend from 'i18next-resources-to-backend' +import Negotiator from 'negotiator' +import { cookies, headers } from 'next/headers' import { initReactI18next } from 'react-i18next/initReactI18next' import { i18n } from '.' -import type { Locale } from '.' // https://locize.com/blog/next-13-app-dir-i18n/ const initI18next = async (lng: Locale, ns: string) => { diff --git a/web/models/access-control.ts b/web/models/access-control.ts index 911662b5c4..d0e58d645b 100644 --- a/web/models/access-control.ts +++ b/web/models/access-control.ts @@ -24,7 +24,7 @@ export type AccessControlAccount = { avatarUrl: 'string' } -export type SubjectGroup = { subjectId: string; subjectType: SubjectType; groupData: AccessControlGroup } -export type SubjectAccount = { subjectId: string; subjectType: SubjectType; accountData: AccessControlAccount } +export type SubjectGroup = { subjectId: string, subjectType: SubjectType, groupData: AccessControlGroup } +export type SubjectAccount = { subjectId: string, subjectType: SubjectType, accountData: AccessControlAccount } export type Subject = SubjectGroup | SubjectAccount diff --git a/web/models/app.ts b/web/models/app.ts index fa148511f0..473c68c62b 100644 --- a/web/models/app.ts +++ b/web/models/app.ts @@ -11,8 +11,8 @@ import type { TracingProvider, WeaveConfig, } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type' -import type { App, AppModeEnum, AppTemplate, SiteConfig } from '@/types/app' import type { Dependency } from '@/app/components/plugins/types' +import type { App, AppModeEnum, AppTemplate, SiteConfig } from '@/types/app' export enum DSLImportMode { YAML_CONTENT = 'yaml-content', @@ -56,15 +56,15 @@ export type CreateAppResponse = App export type UpdateAppSiteCodeResponse = { app_id: string } & SiteConfig export type AppDailyMessagesResponse = { - data: Array<{ date: string; message_count: number }> + data: Array<{ date: string, message_count: number }> } export type AppDailyConversationsResponse = { - data: Array<{ date: string; conversation_count: number }> + data: Array<{ date: string, conversation_count: number }> } export type WorkflowDailyConversationsResponse = { - data: Array<{ date: string; runs: number }> + data: Array<{ date: string, runs: number }> } export type AppStatisticsResponse = { @@ -72,11 +72,11 @@ export type AppStatisticsResponse = { } export type AppDailyEndUsersResponse = { - data: Array<{ date: string; terminal_count: number }> + data: Array<{ date: string, terminal_count: number }> } export type AppTokenCostsResponse = { - data: Array<{ date: string; token_count: number; total_price: number; currency: number }> + data: Array<{ date: string, token_count: number, total_price: number, currency: number }> } export type UpdateAppModelConfigResponse = { result: string } diff --git a/web/models/common.ts b/web/models/common.ts index 5d1499dcd5..0e034ffa33 100644 --- a/web/models/common.ts +++ b/web/models/common.ts @@ -268,7 +268,7 @@ export type CodeBasedExtensionForm = { label: I18nText variable: string required: boolean - options: { label: I18nText; value: string }[] + options: { label: I18nText, value: string }[] default: string placeholder: string max_length?: number diff --git a/web/models/datasets.ts b/web/models/datasets.ts index fe4c568e46..ba61c95b64 100644 --- a/web/models/datasets.ts +++ b/web/models/datasets.ts @@ -1,12 +1,12 @@ import type { DataSourceNotionPage, DataSourceProvider } from './common' -import type { AppIconType, AppModeEnum, RetrievalConfig, TransferMethod } from '@/types/app' +import type { DatasourceType } from './pipeline' import type { Tag } from '@/app/components/base/tag-management/constant' import type { IndexingType } from '@/app/components/datasets/create/step-two' -import type { MetadataFilteringVariableType } from '@/app/components/workflow/nodes/knowledge-retrieval/types' import type { MetadataItemWithValue } from '@/app/components/datasets/metadata/types' +import type { MetadataFilteringVariableType } from '@/app/components/workflow/nodes/knowledge-retrieval/types' +import type { AppIconType, AppModeEnum, RetrievalConfig, TransferMethod } from '@/types/app' import { ExternalKnowledgeBase, General, ParentChild, Qa } from '@/app/components/base/icons/src/public/knowledge/dataset-card' import { GeneralChunk, ParentChildChunk, QuestionAndAnswer } from '@/app/components/base/icons/src/vender/knowledge' -import type { DatasourceType } from './pipeline' export enum DataSourceType { FILE = 'upload_file', @@ -98,7 +98,7 @@ export type ExternalAPIItem = { endpoint: string api_key: string } - dataset_bindings: { id: string; name: string }[] + dataset_bindings: { id: string, name: string }[] created_by: string created_at: string } @@ -224,7 +224,7 @@ export type IndexingEstimateResponse = { total_price: number currency: string total_segments: number - preview: Array<{ content: string; child_chunks: string[] }> + preview: Array<{ content: string, child_chunks: string[] }> qa_preview?: QA[] } @@ -360,7 +360,7 @@ export type OnlineDocumentInfo = { page_name: string parent_id: string type: string - }, + } } export type OnlineDriveInfo = { @@ -590,7 +590,7 @@ export type SegmentsResponse = { export type Query = { content: string - content_type: 'text_query' | 'image_query', + content_type: 'text_query' | 'image_query' file_info: Attachment | null } diff --git a/web/models/debug.ts b/web/models/debug.ts index 90f79cbf8d..5290268fe9 100644 --- a/web/models/debug.ts +++ b/web/models/debug.ts @@ -1,8 +1,3 @@ -import type { AgentStrategy, ModelModeType, RETRIEVE_TYPE, ToolItem, TtsAutoPlay } from '@/types/app' -import type { - RerankingModeEnum, - WeightedScoreEnum, -} from '@/models/datasets' import type { FileUpload } from '@/app/components/base/features/types' import type { MetadataFilteringConditions, @@ -10,6 +5,12 @@ import type { } from '@/app/components/workflow/nodes/knowledge-retrieval/types' import type { ModelConfig as NodeModelConfig } from '@/app/components/workflow/types' import type { ExternalDataTool } from '@/models/common' +import type { + RerankingModeEnum, + WeightedScoreEnum, +} from '@/models/datasets' +import type { AgentStrategy, ModelModeType, RETRIEVE_TYPE, ToolItem, TtsAutoPlay } from '@/types/app' + export type Inputs = Record<string, string | number | object | boolean> export enum PromptMode { diff --git a/web/models/explore.ts b/web/models/explore.ts index fbbd01837a..1d513e9b70 100644 --- a/web/models/explore.ts +++ b/web/models/explore.ts @@ -1,4 +1,5 @@ import type { AppIconType, AppModeEnum } from '@/types/app' + export type AppBasicInfo = { id: string mode: AppModeEnum diff --git a/web/models/log.ts b/web/models/log.ts index b9c91a7a3c..8c022ee6b2 100644 --- a/web/models/log.ts +++ b/web/models/log.ts @@ -1,10 +1,10 @@ import type { Viewport } from 'reactflow' -import type { VisionFile } from '@/types/app' +import type { Metadata } from '@/app/components/base/chat/chat/type' import type { Edge, Node, } from '@/app/components/workflow/types' -import type { Metadata } from '@/app/components/base/chat/chat/type' +import type { VisionFile } from '@/types/app' // Log type contains key:string conversation_id:string created_at:string question:string answer:string export type Conversation = { @@ -77,7 +77,7 @@ export type MessageContent = { conversation_id: string query: string inputs: Record<string, any> - message: { role: string; text: string; files?: VisionFile[] }[] + message: { role: string, text: string, files?: VisionFile[] }[] message_tokens: number answer_tokens: number answer: string diff --git a/web/models/pipeline.ts b/web/models/pipeline.ts index 1c2211b6d9..143bc61180 100644 --- a/web/models/pipeline.ts +++ b/web/models/pipeline.ts @@ -1,12 +1,12 @@ -import type { Edge, EnvironmentVariable, Node, SupportUploadFileTypes } from '@/app/components/workflow/types' +import type { Viewport } from 'reactflow' import type { DSLImportMode, DSLImportStatus } from './app' import type { ChunkingMode, DatasetPermission, DocumentIndexingStatus, FileIndexingEstimateResponse, IconInfo } from './datasets' -import type { Dependency } from '@/app/components/plugins/types' import type { AppIconSelection } from '@/app/components/base/app-icon-picker' -import type { Viewport } from 'reactflow' +import type { Dependency } from '@/app/components/plugins/types' +import type { Edge, EnvironmentVariable, Node, SupportUploadFileTypes } from '@/app/components/workflow/types' import type { TransferMethod } from '@/types/app' -import { BaseFieldType } from '@/app/components/base/form/form-scenarios/base/types' import type { NodeRunResult } from '@/types/workflow' +import { BaseFieldType } from '@/app/components/base/form/form-scenarios/base/types' export enum DatasourceType { localFile = 'local_file', @@ -190,7 +190,7 @@ export type PublishedPipelineInfoResponse = { id: string name: string email: string - }, + } environment_variables?: EnvironmentVariable[] rag_pipeline_variables?: RAGPipelineVariables version: string diff --git a/web/next.config.js b/web/next.config.js index b6d6fb5d6c..3414d09021 100644 --- a/web/next.config.js +++ b/web/next.config.js @@ -1,6 +1,6 @@ -import { codeInspectorPlugin } from 'code-inspector-plugin' import withBundleAnalyzerInit from '@next/bundle-analyzer' import createMDX from '@next/mdx' +import { codeInspectorPlugin } from 'code-inspector-plugin' import withPWAInit from 'next-pwa' const isDev = process.env.NODE_ENV === 'development' @@ -21,9 +21,9 @@ const withPWA = withPWAInit({ cacheName: 'google-fonts', expiration: { maxEntries: 4, - maxAgeSeconds: 365 * 24 * 60 * 60 // 1 year - } - } + maxAgeSeconds: 365 * 24 * 60 * 60, // 1 year + }, + }, }, { urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i, @@ -32,9 +32,9 @@ const withPWA = withPWAInit({ cacheName: 'google-fonts-webfonts', expiration: { maxEntries: 4, - maxAgeSeconds: 365 * 24 * 60 * 60 // 1 year - } - } + maxAgeSeconds: 365 * 24 * 60 * 60, // 1 year + }, + }, }, { urlPattern: /\.(?:png|jpg|jpeg|svg|gif|webp|avif)$/i, @@ -43,9 +43,9 @@ const withPWA = withPWAInit({ cacheName: 'images', expiration: { maxEntries: 64, - maxAgeSeconds: 30 * 24 * 60 * 60 // 30 days - } - } + maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days + }, + }, }, { urlPattern: /\.(?:js|css)$/i, @@ -54,9 +54,9 @@ const withPWA = withPWAInit({ cacheName: 'static-resources', expiration: { maxEntries: 32, - maxAgeSeconds: 24 * 60 * 60 // 1 day - } - } + maxAgeSeconds: 24 * 60 * 60, // 1 day + }, + }, }, { urlPattern: /^\/api\/.*/i, @@ -66,11 +66,11 @@ const withPWA = withPWAInit({ networkTimeoutSeconds: 10, expiration: { maxEntries: 16, - maxAgeSeconds: 60 * 60 // 1 hour - } - } - } - ] + maxAgeSeconds: 60 * 60, // 1 hour + }, + }, + }, + ], }) const withMDX = createMDX({ extension: /\.mdx?$/, @@ -100,8 +100,8 @@ const nextConfig = { transpilePackages: ['echarts', 'zrender'], turbopack: { rules: codeInspectorPlugin({ - bundler: 'turbopack' - }) + bundler: 'turbopack', + }), }, productionBrowserSourceMaps: false, // enable browser source map generation during the production build // Configure pageExtensions to include md and mdx @@ -118,7 +118,7 @@ const nextConfig = { }, experimental: { optimizePackageImports: [ - '@heroicons/react' + '@heroicons/react', ], }, // fix all before production. Now it slow the develop speed. @@ -145,7 +145,7 @@ const nextConfig = { output: 'standalone', compiler: { removeConsole: isDev ? false : { exclude: ['warn', 'error'] }, - } + }, } export default withPWA(withBundleAnalyzer(withMDX(nextConfig))) diff --git a/web/package.json b/web/package.json index e75841379d..ce2e59e022 100644 --- a/web/package.json +++ b/web/package.json @@ -1,7 +1,7 @@ { "name": "dify-web", - "version": "1.11.1", "type": "module", + "version": "1.11.1", "private": true, "packageManager": "pnpm@10.26.1+sha512.664074abc367d2c9324fdc18037097ce0a8f126034160f709928e9e9f95d98714347044e5c3164d65bd5da6c59c6be362b107546292a8eecb7999196e5ce58fa", "engines": { @@ -24,11 +24,10 @@ "build": "next build", "build:docker": "next build && node scripts/optimize-standalone.js", "start": "node ./scripts/copy-and-start.mjs", - "lint:oxlint": "oxlint --config .oxlintrc.json .", - "lint": "pnpm run lint:oxlint && eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache", - "lint:fix": "pnpm run lint:oxlint && eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache --fix", - "lint:quiet": "pnpm run lint:oxlint && eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache --quiet", - "lint:complexity": "pnpm run lint:oxlint && eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache --rule 'complexity: [error, {max: 15}]' --quiet", + "lint": "eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache", + "lint:fix": "eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache --fix", + "lint:quiet": "eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache --quiet", + "lint:complexity": "eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache --rule 'complexity: [error, {max: 15}]' --quiet", "type-check": "tsc --noEmit", "type-check:tsgo": "tsgo --noEmit", "prepare": "cd ../ && node -e \"if (process.env.NODE_ENV !== 'production'){process.exit(1)} \" || husky ./web/.husky", @@ -149,10 +148,10 @@ "zustand": "^5.0.9" }, "devDependencies": { - "@antfu/eslint-config": "^5.4.1", + "@antfu/eslint-config": "^6.7.3", "@babel/core": "^7.28.4", "@chromatic-com/storybook": "^4.1.1", - "@eslint-react/eslint-plugin": "^1.53.1", + "@eslint-react/eslint-plugin": "^2.3.13", "@mdx-js/loader": "^3.1.1", "@mdx-js/react": "^3.1.1", "@next/bundle-analyzer": "15.5.9", @@ -183,7 +182,7 @@ "@types/semver": "^7.7.1", "@types/sortablejs": "^1.15.8", "@types/uuid": "^10.0.0", - "@typescript-eslint/parser": "^8.48.0", + "@typescript-eslint/parser": "^8.50.0", "@typescript/native-preview": "^7.0.0-dev", "@vitejs/plugin-react": "^5.1.2", "@vitest/coverage-v8": "4.0.16", @@ -192,14 +191,12 @@ "bing-translate-api": "^4.1.0", "code-inspector-plugin": "1.2.9", "cross-env": "^10.1.0", - "eslint": "^9.38.0", - "eslint-plugin-oxlint": "^1.23.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.24", + "eslint": "^9.39.2", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.26", "eslint-plugin-sonarjs": "^3.0.5", - "eslint-plugin-storybook": "^9.1.13", + "eslint-plugin-storybook": "^10.1.10", "eslint-plugin-tailwindcss": "^3.18.2", - "globals": "^15.15.0", "husky": "^9.1.7", "istanbul-lib-coverage": "^3.2.2", "jsdom": "^27.3.0", @@ -209,7 +206,6 @@ "lodash": "^4.17.21", "magicast": "^0.3.5", "nock": "^14.0.10", - "oxlint": "^1.31.0", "postcss": "^8.5.6", "react-scan": "^0.4.3", "sass": "^1.93.2", @@ -223,36 +219,11 @@ "vitest": "^4.0.16", "vitest-localstorage-mock": "^0.1.2" }, - "resolutions": { - "@types/react": "~19.2.7", - "@types/react-dom": "~19.2.3", - "string-width": "~4.2.3", - "@eslint/plugin-kit": "~0.3", - "canvas": "^3.2.0", - "esbuild": "~0.25.0", - "pbkdf2": "~3.1.3", - "prismjs": "~1.30", - "brace-expansion": "~2.0" - }, - "lint-staged": { - "**/*.js?(x)": [ - "eslint --fix" - ], - "**/*.ts?(x)": [ - "oxlint --config .oxlintrc.json", - "eslint --fix" - ] - }, "pnpm": { "overrides": { - "@monaco-editor/loader": "1.5.0", "@eslint/plugin-kit@<0.3.4": "0.3.4", - "brace-expansion@<2.0.2": "2.0.2", - "devalue@<5.3.2": "5.3.2", - "esbuild@<0.25.0": "0.25.0", - "pbkdf2@<3.1.3": "3.1.3", - "prismjs@<1.30.0": "1.30.0", - "vite@<6.4.1": "6.4.1", + "@monaco-editor/loader": "1.5.0", + "@nolyfill/safe-buffer": "npm:safe-buffer@^5.2.1", "array-includes": "npm:@nolyfill/array-includes@^1", "array.prototype.findlast": "npm:@nolyfill/array.prototype.findlast@^1", "array.prototype.findlastindex": "npm:@nolyfill/array.prototype.findlastindex@^1", @@ -260,7 +231,10 @@ "array.prototype.flatmap": "npm:@nolyfill/array.prototype.flatmap@^1", "array.prototype.tosorted": "npm:@nolyfill/array.prototype.tosorted@^1", "assert": "npm:@nolyfill/assert@^1", + "brace-expansion@<2.0.2": "2.0.2", + "devalue@<5.3.2": "5.3.2", "es-iterator-helpers": "npm:@nolyfill/es-iterator-helpers@^1", + "esbuild@<0.25.0": "0.25.0", "hasown": "npm:@nolyfill/hasown@^1", "is-arguments": "npm:@nolyfill/is-arguments@^1", "is-core-module": "npm:@nolyfill/is-core-module@^1", @@ -272,8 +246,9 @@ "object.fromentries": "npm:@nolyfill/object.fromentries@^1", "object.groupby": "npm:@nolyfill/object.groupby@^1", "object.values": "npm:@nolyfill/object.values@^1", + "pbkdf2@<3.1.3": "3.1.3", + "prismjs@<1.30.0": "1.30.0", "safe-buffer": "^5.2.1", - "@nolyfill/safe-buffer": "npm:safe-buffer@^5.2.1", "safe-regex-test": "npm:@nolyfill/safe-regex-test@^1", "safer-buffer": "npm:@nolyfill/safer-buffer@^1", "side-channel": "npm:@nolyfill/side-channel@^1", @@ -282,6 +257,7 @@ "string.prototype.repeat": "npm:@nolyfill/string.prototype.repeat@^1", "string.prototype.trimend": "npm:@nolyfill/string.prototype.trimend@^1", "typed-array-buffer": "npm:@nolyfill/typed-array-buffer@^1", + "vite@<6.4.1": "6.4.1", "which-typed-array": "npm:@nolyfill/which-typed-array@^1" }, "ignoredBuiltDependencies": [ @@ -293,5 +269,19 @@ "esbuild", "sharp" ] + }, + "resolutions": { + "@eslint/plugin-kit": "~0.3", + "@types/react": "~19.2.7", + "@types/react-dom": "~19.2.3", + "brace-expansion": "~2.0", + "canvas": "^3.2.0", + "esbuild": "~0.25.0", + "pbkdf2": "~3.1.3", + "prismjs": "~1.30", + "string-width": "~4.2.3" + }, + "lint-staged": { + "*": "eslint --fix" } } diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 93db0d0791..95d35c24d8 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -356,8 +356,8 @@ importers: version: 5.0.9(@types/react@19.2.7)(immer@11.1.0)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) devDependencies: '@antfu/eslint-config': - specifier: ^5.4.1 - version: 5.4.1(@eslint-react/eslint-plugin@1.53.1(eslint@9.39.1(jiti@1.21.7))(ts-api-utils@2.1.0(typescript@5.9.3))(typescript@5.9.3))(@next/eslint-plugin-next@15.5.9)(@vue/compiler-sfc@3.5.25)(eslint-plugin-react-hooks@5.2.0(eslint@9.39.1(jiti@1.21.7)))(eslint-plugin-react-refresh@0.4.24(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)(vitest@4.0.16(@types/node@18.15.0)(happy-dom@20.0.11)(jiti@1.21.7)(jsdom@27.3.0(canvas@3.2.0))(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + specifier: ^6.7.3 + version: 6.7.3(@eslint-react/eslint-plugin@2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@next/eslint-plugin-next@15.5.9)(@vue/compiler-sfc@3.5.25)(eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@1.21.7)))(eslint-plugin-react-refresh@0.4.26(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)(vitest@4.0.16(@types/node@18.15.0)(happy-dom@20.0.11)(jiti@1.21.7)(jsdom@27.3.0(canvas@3.2.0))(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) '@babel/core': specifier: ^7.28.4 version: 7.28.5 @@ -365,8 +365,8 @@ importers: specifier: ^4.1.1 version: 4.1.3(storybook@9.1.17(@testing-library/dom@10.4.1)(vite@7.3.0(@types/node@18.15.0)(jiti@1.21.7)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))) '@eslint-react/eslint-plugin': - specifier: ^1.53.1 - version: 1.53.1(eslint@9.39.1(jiti@1.21.7))(ts-api-utils@2.1.0(typescript@5.9.3))(typescript@5.9.3) + specifier: ^2.3.13 + version: 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@mdx-js/loader': specifier: ^3.1.1 version: 3.1.1(webpack@5.103.0(esbuild@0.25.0)(uglify-js@3.19.3)) @@ -458,8 +458,8 @@ importers: specifier: ^10.0.0 version: 10.0.0 '@typescript-eslint/parser': - specifier: ^8.48.0 - version: 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + specifier: ^8.50.0 + version: 8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript/native-preview': specifier: ^7.0.0-dev version: 7.0.0-dev.20251209.1 @@ -485,29 +485,23 @@ importers: specifier: ^10.1.0 version: 10.1.0 eslint: - specifier: ^9.38.0 - version: 9.39.1(jiti@1.21.7) - eslint-plugin-oxlint: - specifier: ^1.23.0 - version: 1.32.0 + specifier: ^9.39.2 + version: 9.39.2(jiti@1.21.7) eslint-plugin-react-hooks: - specifier: ^5.2.0 - version: 5.2.0(eslint@9.39.1(jiti@1.21.7)) + specifier: ^7.0.1 + version: 7.0.1(eslint@9.39.2(jiti@1.21.7)) eslint-plugin-react-refresh: - specifier: ^0.4.24 - version: 0.4.24(eslint@9.39.1(jiti@1.21.7)) + specifier: ^0.4.26 + version: 0.4.26(eslint@9.39.2(jiti@1.21.7)) eslint-plugin-sonarjs: specifier: ^3.0.5 - version: 3.0.5(eslint@9.39.1(jiti@1.21.7)) + version: 3.0.5(eslint@9.39.2(jiti@1.21.7)) eslint-plugin-storybook: - specifier: ^9.1.13 - version: 9.1.16(eslint@9.39.1(jiti@1.21.7))(storybook@9.1.17(@testing-library/dom@10.4.1)(vite@7.3.0(@types/node@18.15.0)(jiti@1.21.7)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(typescript@5.9.3) + specifier: ^10.1.10 + version: 10.1.10(eslint@9.39.2(jiti@1.21.7))(storybook@9.1.17(@testing-library/dom@10.4.1)(vite@7.3.0(@types/node@18.15.0)(jiti@1.21.7)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(typescript@5.9.3) eslint-plugin-tailwindcss: specifier: ^3.18.2 version: 3.18.2(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.2)) - globals: - specifier: ^15.15.0 - version: 15.15.0 husky: specifier: ^9.1.7 version: 9.1.7 @@ -535,9 +529,6 @@ importers: nock: specifier: ^14.0.10 version: 14.0.10 - oxlint: - specifier: ^1.31.0 - version: 1.32.0 postcss: specifier: ^8.5.6 version: 8.5.6 @@ -661,12 +652,12 @@ packages: '@amplitude/targeting@0.2.0': resolution: {integrity: sha512-/50ywTrC4hfcfJVBbh5DFbqMPPfaIOivZeb5Gb+OGM03QrA+lsUqdvtnKLNuWtceD4H6QQ2KFzPJ5aAJLyzVDA==} - '@antfu/eslint-config@5.4.1': - resolution: {integrity: sha512-x7BiNkxJRlXXs8tIvg0CgMuNo5IZVWkGLMJotCtCtzWUHW78Pmm8PvtXhvLBbTc8683GGBK616MMztWLh4RNjA==} + '@antfu/eslint-config@6.7.3': + resolution: {integrity: sha512-0tYYzY59uLnxWgbP9xpuxpvodTcWDacj439kTAJZB3sn7O0BnPfVxTnRvleGYaKCEALBZkzdC/wCho9FD7ICLw==} hasBin: true peerDependencies: - '@eslint-react/eslint-plugin': ^1.38.4 - '@next/eslint-plugin-next': ^15.4.0-canary.115 + '@eslint-react/eslint-plugin': ^2.0.1 + '@next/eslint-plugin-next': '>=15.0.0' '@prettier/plugin-xml': ^3.4.1 '@unocss/eslint-plugin': '>=0.50.0' astro-eslint-parser: ^1.0.2 @@ -674,7 +665,7 @@ packages: eslint-plugin-astro: ^1.2.0 eslint-plugin-format: '>=0.1.0' eslint-plugin-jsx-a11y: '>=6.10.2' - eslint-plugin-react-hooks: ^5.2.0 + eslint-plugin-react-hooks: ^7.0.0 eslint-plugin-react-refresh: ^0.4.19 eslint-plugin-solid: ^0.14.3 eslint-plugin-svelte: '>=2.35.1' @@ -1424,14 +1415,18 @@ packages: '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} - '@es-joy/jsdoccomment@0.50.2': - resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==} - engines: {node: '>=18'} - - '@es-joy/jsdoccomment@0.58.0': - resolution: {integrity: sha512-smMc5pDht/UVsCD3hhw/a/e/p8m0RdRYiluXToVfd+d4yaQQh7nn9bACjkk6nXJvat7EWPAxuFkMEFfrxeGa3Q==} + '@es-joy/jsdoccomment@0.76.0': + resolution: {integrity: sha512-g+RihtzFgGTx2WYCuTHbdOXJeAlGnROws0TeALx9ow/ZmOROOZkVg5wp/B44n0WJgI4SQFP1eWM2iRPlU2Y14w==} engines: {node: '>=20.11.0'} + '@es-joy/jsdoccomment@0.78.0': + resolution: {integrity: sha512-rQkU5u8hNAq2NVRzHnIUUvR6arbO0b6AOlvpTNS48CkiKSn/xtNfOzBK23JE4SiW89DgvU7GtxLVgV4Vn2HBAw==} + engines: {node: '>=20.11.0'} + + '@es-joy/resolve.exports@1.2.0': + resolution: {integrity: sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==} + engines: {node: '>=10'} + '@esbuild/aix-ppc64@0.25.0': resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} engines: {node: '>=18'} @@ -1758,39 +1753,44 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint-react/ast@1.53.1': - resolution: {integrity: sha512-qvUC99ewtriJp9quVEOvZ6+RHcsMLfVQ0OhZ4/LupZUDhjW7GiX1dxJsFaxHdJ9rLNLhQyLSPmbAToeqUrSruQ==} - engines: {node: '>=18.18.0'} - - '@eslint-react/core@1.53.1': - resolution: {integrity: sha512-8prroos5/Uvvh8Tjl1HHCpq4HWD3hV9tYkm7uXgKA6kqj0jHlgRcQzuO6ZPP7feBcK3uOeug7xrq03BuG8QKCA==} - engines: {node: '>=18.18.0'} - - '@eslint-react/eff@1.53.1': - resolution: {integrity: sha512-uq20lPRAmsWRjIZm+mAV/2kZsU2nDqn5IJslxGWe3Vfdw23hoyhEw3S1KKlxbftwbTvsZjKvVP0iw3bZo/NUpg==} - engines: {node: '>=18.18.0'} - - '@eslint-react/eslint-plugin@1.53.1': - resolution: {integrity: sha512-JZ2ciXNCC9CtBBAqYtwWH+Jy/7ZzLw+whei8atP4Fxsbh+Scs30MfEwBzuiEbNw6uF9eZFfPidchpr5RaEhqxg==} - engines: {node: '>=18.18.0'} + '@eslint-react/ast@2.3.13': + resolution: {integrity: sha512-OP2rOhHYLx2nfd9uA9uACKZJN9z9rX9uuAMx4PjT75JNOdYr1GgqWQZcYCepyJ+gmVNCyiXcLXuyhavqxCSM8Q==} + engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@eslint-react/kit@1.53.1': - resolution: {integrity: sha512-zOi2le9V4rMrJvQV4OeedGvMGvDT46OyFPOwXKs7m0tQu5vXVJ8qwIPaVQT1n/WIuvOg49OfmAVaHpGxK++xLQ==} - engines: {node: '>=18.18.0'} + '@eslint-react/core@2.3.13': + resolution: {integrity: sha512-4bWBE+1kApuxJKIrLJH2FuFtCbM4fXfDs6Ou8MNamGoX6hdynlntssvaMZTd/lk/L8dt01H/3btr7xBX4+4BNA==} + engines: {node: '>=20.19.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' - '@eslint-react/shared@1.53.1': - resolution: {integrity: sha512-gomJQmFqQgQVI3Ra4vTMG/s6a4bx3JqeNiTBjxBJt4C9iGaBj458GkP4LJHX7TM6xUzX+fMSKOPX7eV3C/+UCw==} - engines: {node: '>=18.18.0'} + '@eslint-react/eff@2.3.13': + resolution: {integrity: sha512-byXsssozwh3VaiqcOonAKQgLXgpMVNSxBWFjdfbNhW7+NttorSt950qtiw+P7A9JoRab1OuGYk4MDY5UVBno8Q==} + engines: {node: '>=20.19.0'} - '@eslint-react/var@1.53.1': - resolution: {integrity: sha512-yzwopvPntcHU7mmDvWzRo1fb8QhjD8eDRRohD11rTV1u7nWO4QbJi0pOyugQakvte1/W11Y0Vr8Of0Ojk/A6zg==} - engines: {node: '>=18.18.0'} + '@eslint-react/eslint-plugin@2.3.13': + resolution: {integrity: sha512-gq0Z0wADAXvJS8Y/Wk3isK7WIEcfrQGGGdWvorAv0T7MxPd3d32TVwdc1Gx3hVLka3fYq1BBlQ5Fr8e1VgNuIg==} + engines: {node: '>=20.19.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@eslint-react/shared@2.3.13': + resolution: {integrity: sha512-ESE7dVeOXtem3K6BD6k2wJaFt35kPtTT9SWCL99LFk7pym4OEGoMxPcyB2R7PMWiVudwl63BmiOgQOdaFYPONg==} + engines: {node: '>=20.19.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@eslint-react/var@2.3.13': + resolution: {integrity: sha512-BozBfUZkzzobD6x/M8XERAnZQ3UvZPsD49zTGFKKU9M/bgsM78HwzxAPLkiu88W55v3sO/Kqf8fQTXT4VEeZ/g==} + engines: {node: '>=20.19.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' '@eslint/compat@1.4.1': resolution: {integrity: sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w==} @@ -1821,8 +1821,8 @@ packages: resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.1': - resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/markdown@7.5.1': @@ -1833,10 +1833,6 @@ packages: resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.4': - resolution: {integrity: sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.5': resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2577,46 +2573,6 @@ packages: cpu: [x64] os: [win32] - '@oxlint/darwin-arm64@1.32.0': - resolution: {integrity: sha512-yrqPmZYu5Qb+49h0P5EXVIq8VxYkDDM6ZQrWzlh16+UGFcD8HOXs4oF3g9RyfaoAbShLCXooSQsM/Ifwx8E/eQ==} - cpu: [arm64] - os: [darwin] - - '@oxlint/darwin-x64@1.32.0': - resolution: {integrity: sha512-pQRZrJG/2nAKc3IuocFbaFFbTDlQsjz2WfivRsMn0hw65EEsSuM84WMFMiAfLpTGyTICeUtHZLHlrM5lzVr36A==} - cpu: [x64] - os: [darwin] - - '@oxlint/linux-arm64-gnu@1.32.0': - resolution: {integrity: sha512-tyomSmU2DzwcTmbaWFmStHgVfRmJDDvqcIvcw4fRB1YlL2Qg/XaM4NJ0m2bdTap38gxD5FSxSgCo0DkQ8GTolg==} - cpu: [arm64] - os: [linux] - - '@oxlint/linux-arm64-musl@1.32.0': - resolution: {integrity: sha512-0W46dRMaf71OGE4+Rd+GHfS1uF/UODl5Mef6871pMhN7opPGfTI2fKJxh9VzRhXeSYXW/Z1EuCq9yCfmIJq+5Q==} - cpu: [arm64] - os: [linux] - - '@oxlint/linux-x64-gnu@1.32.0': - resolution: {integrity: sha512-5+6myVCBOMvM62rDB9T3CARXUvIwhGqte6E+HoKRwYaqsxGUZ4bh3pItSgSFwHjLGPrvADS11qJUkk39eQQBzQ==} - cpu: [x64] - os: [linux] - - '@oxlint/linux-x64-musl@1.32.0': - resolution: {integrity: sha512-qwQlwYYgVIC6ScjpUwiKKNyVdUlJckrfwPVpIjC9mvglIQeIjKuuyaDxUZWIOc/rEzeCV/tW6tcbehLkfEzqsw==} - cpu: [x64] - os: [linux] - - '@oxlint/win32-arm64@1.32.0': - resolution: {integrity: sha512-7qYZF9CiXGtdv8Z/fBkgB5idD2Zokht67I5DKWH0fZS/2R232sDqW2JpWVkXltk0+9yFvmvJ0ouJgQRl9M3S2g==} - cpu: [arm64] - os: [win32] - - '@oxlint/win32-x64@1.32.0': - resolution: {integrity: sha512-XW1xqCj34MEGJlHteqasTZ/LmBrwYIgluhNW0aP+XWkn90+stKAq3W/40dvJKbMK9F7o09LPCuMVtUW7FIUuiA==} - cpu: [x64] - os: [win32] - '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} engines: {node: '>= 10.0.0'} @@ -3215,6 +3171,10 @@ packages: peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x + '@sindresorhus/base62@1.0.0': + resolution: {integrity: sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==} + engines: {node: '>=18'} + '@sindresorhus/is@4.6.0': resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} @@ -3726,16 +3686,16 @@ packages: '@types/zen-observable@0.8.3': resolution: {integrity: sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==} - '@typescript-eslint/eslint-plugin@8.49.0': - resolution: {integrity: sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==} + '@typescript-eslint/eslint-plugin@8.50.0': + resolution: {integrity: sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.49.0 + '@typescript-eslint/parser': ^8.50.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.49.0': - resolution: {integrity: sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==} + '@typescript-eslint/parser@8.50.0': + resolution: {integrity: sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -3747,16 +3707,32 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/project-service@8.50.0': + resolution: {integrity: sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/scope-manager@8.49.0': resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.50.0': + resolution: {integrity: sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/tsconfig-utils@8.49.0': resolution: {integrity: sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/tsconfig-utils@8.50.0': + resolution: {integrity: sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/type-utils@8.49.0': resolution: {integrity: sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3764,16 +3740,33 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/type-utils@8.50.0': + resolution: {integrity: sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/types@8.49.0': resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.50.0': + resolution: {integrity: sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.49.0': resolution: {integrity: sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/typescript-estree@8.50.0': + resolution: {integrity: sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/utils@8.49.0': resolution: {integrity: sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3781,10 +3774,21 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/utils@8.50.0': + resolution: {integrity: sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/visitor-keys@8.49.0': resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.50.0': + resolution: {integrity: sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20251209.1': resolution: {integrity: sha512-F1cnYi+ZeinYQnaTQKKIsbuoq8vip5iepBkSZXlB8PjbG62LW1edUdktd/nVEc+Q+SEysSQ3jRdk9eU766s5iw==} cpu: [arm64] @@ -3842,8 +3846,8 @@ packages: '@vitest/browser': optional: true - '@vitest/eslint-plugin@1.5.2': - resolution: {integrity: sha512-2t1F2iecXB/b1Ox4U137lhD3chihEE3dRVtu3qMD35tc6UqUjg1VGRJoS1AkFKwpT8zv8OQInzPQO06hrRkeqw==} + '@vitest/eslint-plugin@1.6.1': + resolution: {integrity: sha512-q4ZCihsURDxhJm6bEUtJjciXtT5k3ijWR4U+0f9XdCRAzAfML5NUUSwulsFoK1AFohBieh52akKWJEIFFMLn/g==} engines: {node: '>=18'} peerDependencies: eslint: '>=8.57.0' @@ -5145,8 +5149,8 @@ packages: peerDependencies: eslint: '*' - eslint-plugin-command@3.3.1: - resolution: {integrity: sha512-fBVTXQ2y48TVLT0+4A6PFINp7GcdIailHAXbvPBixE7x+YpYnNQhFZxTdvnb+aWk+COgNebQKen/7m4dmgyWAw==} + eslint-plugin-command@3.4.0: + resolution: {integrity: sha512-EW4eg/a7TKEhG0s5IEti72kh3YOTlnhfFNuctq5WnB1fst37/IHTd5OkD+vnlRf3opTvUcSRihAateP6bT5ZcA==} peerDependencies: eslint: '*' @@ -5156,8 +5160,8 @@ packages: peerDependencies: eslint: '>=8' - eslint-plugin-import-lite@0.3.0: - resolution: {integrity: sha512-dkNBAL6jcoCsXZsQ/Tt2yXmMDoNt5NaBh/U7yvccjiK8cai6Ay+MK77bMykmqQA2bTF6lngaLCDij6MTO3KkvA==} + eslint-plugin-import-lite@0.4.0: + resolution: {integrity: sha512-My0ReAg8WbHXYECIHVJkWB8UxrinZn3m72yonOYH6MFj40ZN1vHYQj16iq2Fd8Wrt/vRZJwDX2xm/BzDk1FzTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: '>=9.0.0' @@ -5166,8 +5170,8 @@ packages: typescript: optional: true - eslint-plugin-jsdoc@59.1.0: - resolution: {integrity: sha512-sg9mzjjzfnMynyY4W8FDiQv3i8eFcKVEHDt4Xh7MLskP3QkMt2z6p7FuzSw7jJSKFues6RaK2GWvmkB1FLPxXg==} + eslint-plugin-jsdoc@61.5.0: + resolution: {integrity: sha512-PR81eOGq4S7diVnV9xzFSBE4CDENRQGP0Lckkek8AdHtbj+6Bm0cItwlFnxsLFriJHspiE3mpu8U20eODyToIg==} engines: {node: '>=20.11.0'} peerDependencies: eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 @@ -5188,93 +5192,62 @@ packages: resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==} engines: {node: '>=5.0.0'} - eslint-plugin-oxlint@1.32.0: - resolution: {integrity: sha512-CodKgz/9q3euGbCYrXVRyFxHfnrxn9Q4EywqE4V/VYegry2pJ9/hPQ0OUDTRzbl3/pPbVndkrUUm5tK8NTSgeg==} - eslint-plugin-perfectionist@4.15.1: resolution: {integrity: sha512-MHF0cBoOG0XyBf7G0EAFCuJJu4I18wy0zAoT1OHfx2o6EOx1EFTIzr2HGeuZa1kDcusoX0xJ9V7oZmaeFd773Q==} engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: eslint: '>=8.45.0' - eslint-plugin-pnpm@1.4.2: - resolution: {integrity: sha512-em/HEUlud5G3G4VZe2dhgsLm2ey6CG+Y+Lq3fS/RsbnmKhi+D+LcLz31GphTJhizCoKl2oAVndMltOHbuBYe+A==} + eslint-plugin-pnpm@1.4.3: + resolution: {integrity: sha512-wdWrkWN5mxRgEADkQvxwv0xA+0++/hYDD5OyXTL6UqPLUPdcCFQJO61NO7IKhEqb3GclWs02OoFs1METN+a3zQ==} peerDependencies: eslint: ^9.0.0 - eslint-plugin-react-debug@1.53.1: - resolution: {integrity: sha512-WNOiQ6jhodJE88VjBU/IVDM+2Zr9gKHlBFDUSA3fQ0dMB5RiBVj5wMtxbxRuipK/GqNJbteqHcZoYEod7nfddg==} - engines: {node: '>=18.18.0'} + eslint-plugin-react-dom@2.3.13: + resolution: {integrity: sha512-O9jglTOnnuyfJcSxjeVc8lqIp5kuS9/0MLLCHlOTH8ZjIifHHxUr6GZ2fd4la9y0FsoEYXEO7DBIMjWx2vCwjg==} + engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - eslint-plugin-react-dom@1.53.1: - resolution: {integrity: sha512-UYrWJ2cS4HpJ1A5XBuf1HfMpPoLdfGil+27g/ldXfGemb4IXqlxHt4ANLyC8l2CWcE3SXGJW7mTslL34MG0qTQ==} - engines: {node: '>=18.18.0'} + eslint-plugin-react-hooks-extra@2.3.13: + resolution: {integrity: sha512-NSnY8yvtrvu2FAALLuvc2xesIAkMqGyJgilpy8wEi1w/Nw6v0IwBEffoNKLq9OHW4v3nikud3aBTqWfWKOx67Q==} + engines: {node: '>=20.0.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - eslint-plugin-react-hooks-extra@1.53.1: - resolution: {integrity: sha512-fshTnMWNn9NjFLIuy7HzkRgGK29vKv4ZBO9UMr+kltVAfKLMeXXP6021qVKk66i/XhQjbktiS+vQsu1Rd3ZKvg==} - engines: {node: '>=18.18.0'} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - typescript: - optional: true - - eslint-plugin-react-hooks@5.2.0: - resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} - engines: {node: '>=10'} + eslint-plugin-react-hooks@7.0.1: + resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + engines: {node: '>=18'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - eslint-plugin-react-naming-convention@1.53.1: - resolution: {integrity: sha512-rvZ/B/CSVF8d34HQ4qIt90LRuxotVx+KUf3i1OMXAyhsagEFMRe4gAlPJiRufZ+h9lnuu279bEdd+NINsXOteA==} - engines: {node: '>=18.18.0'} + eslint-plugin-react-naming-convention@2.3.13: + resolution: {integrity: sha512-2iler1ldFpB/PaNpN8WAVk6dKYKwKcoGm1j0JAAjdCrsfOTJ007ol2xTAyoHKAbMOvkZSi7qq90q+Q//RuhWwA==} + engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - eslint-plugin-react-refresh@0.4.24: - resolution: {integrity: sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==} + eslint-plugin-react-refresh@0.4.26: + resolution: {integrity: sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==} peerDependencies: eslint: '>=8.40' - eslint-plugin-react-web-api@1.53.1: - resolution: {integrity: sha512-INVZ3Cbl9/b+sizyb43ChzEPXXYuDsBGU9BIg7OVTNPyDPloCXdI+dQFAcSlDocZhPrLxhPV3eT6+gXbygzYXg==} - engines: {node: '>=18.18.0'} + eslint-plugin-react-web-api@2.3.13: + resolution: {integrity: sha512-+UypRPHP9GFMulIENpsC/J+TygWywiyz2mb4qyUP6y/IwdcSilk1MyF9WquNYKB/4/FN4Rl1oRm6WMbfkbpMnQ==} + engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - eslint-plugin-react-x@1.53.1: - resolution: {integrity: sha512-MwMNnVwiPem0U6SlejDF/ddA4h/lmP6imL1RDZ2m3pUBrcdcOwOx0gyiRVTA3ENnhRlWfHljHf5y7m8qDSxMEg==} - engines: {node: '>=18.18.0'} + eslint-plugin-react-x@2.3.13: + resolution: {integrity: sha512-+m+V/5VLMxgx0VsFUUyflMNLQG0WFYspsfv0XJFqx7me3A2b3P20QatNDHQCYswz0PRbRFqinTPukPRhZh68ag==} + engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - ts-api-utils: ^2.1.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - ts-api-utils: - optional: true - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' eslint-plugin-regexp@2.10.0: resolution: {integrity: sha512-ovzQT8ESVn5oOe5a7gIDPD5v9bCSjIFJu57sVPDqgPRXicQzOnYfFN21WoQBQF18vrhT5o7UMKFwJQVVjyJ0ng==} @@ -5287,12 +5260,11 @@ packages: peerDependencies: eslint: ^8.0.0 || ^9.0.0 - eslint-plugin-storybook@9.1.16: - resolution: {integrity: sha512-I8f3DXniPxFbcptVgOjtIHNvW6sDu1O2d1zNsxLKmeAvEaRLus1ij8iFHCgkNzMthrU5U2F4Wdo/aaSpz5kHjA==} - engines: {node: '>=20.0.0'} + eslint-plugin-storybook@10.1.10: + resolution: {integrity: sha512-ITr6Aq3buR/DuDATkq1BafUVJLybyo676fY+tj9Zjd1Ak+UXBAMQcQ++tiBVVHm1RqADwM3b1o6bnWHK2fPPKw==} peerDependencies: eslint: '>=8' - storybook: ^9.1.16 + storybook: ^10.1.10 eslint-plugin-tailwindcss@3.18.2: resolution: {integrity: sha512-QbkMLDC/OkkjFQ1iz/5jkMdHfiMu/uwujUHLAJK5iwNHD8RTxVTlsUezE0toTZ6VhybNBsk+gYGPDq2agfeRNA==} @@ -5306,11 +5278,11 @@ packages: peerDependencies: eslint: '>=6.0.0' - eslint-plugin-unicorn@61.0.2: - resolution: {integrity: sha512-zLihukvneYT7f74GNbVJXfWIiNQmkc/a9vYBTE4qPkQZswolWNdu+Wsp9sIXno1JOzdn6OUwLPd19ekXVkahRA==} + eslint-plugin-unicorn@62.0.0: + resolution: {integrity: sha512-HIlIkGLkvf29YEiS/ImuDZQbP12gWyx5i3C6XrRxMvVdqMroCI9qoVYCoIl17ChN+U89pn9sVwLxhIWj5nEc7g==} engines: {node: ^20.10.0 || >=21.0.0} peerDependencies: - eslint: '>=9.29.0' + eslint: '>=9.38.0' eslint-plugin-unused-imports@4.3.0: resolution: {integrity: sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA==} @@ -5335,8 +5307,8 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-yml@1.19.0: - resolution: {integrity: sha512-S+4GbcCWksFKAvFJtf0vpdiCkZZvDJCV4Zsi9ahmYkYOYcf+LRqqzvzkb/ST7vTYV6sFwXOvawzYyL/jFT2nQA==} + eslint-plugin-yml@1.19.1: + resolution: {integrity: sha512-bYkOxyEiXh9WxUhVYPELdSHxGG5pOjCSeJOVkfdIyj6tuiHDxrES2WAW1dBxn3iaZQey57XflwLtCYRcNPOiOg==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: '>=6.0.0' @@ -5363,8 +5335,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.39.1: - resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -5780,6 +5752,12 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} @@ -6133,17 +6111,17 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsdoc-type-pratt-parser@4.1.0: - resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} - engines: {node: '>=12.0.0'} - jsdoc-type-pratt-parser@4.8.0: resolution: {integrity: sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw==} engines: {node: '>=12.0.0'} - jsdoc-type-pratt-parser@5.4.0: - resolution: {integrity: sha512-F9GQ+F1ZU6qvSrZV8fNFpjDNf614YzR2eF6S0+XbDjAcUI28FSoXnYZFjQmb1kFx3rrJb5PnxUH3/Yti6fcM+g==} - engines: {node: '>=12.0.0'} + jsdoc-type-pratt-parser@6.10.0: + resolution: {integrity: sha512-+LexoTRyYui5iOhJGn13N9ZazL23nAHGkXsa1p/C8yeq79WRfLBag6ZZ0FQG2aRoc9yfo59JT9EYCQonOkHKkQ==} + engines: {node: '>=20.0.0'} + + jsdoc-type-pratt-parser@7.0.0: + resolution: {integrity: sha512-c7YbokssPOSHmqTbSAmTtnVgAVa/7lumWNYqomgd5KOMyPrRve2anx6lonfOsXEQacqF9FKVUj7bLg4vRSvdYA==} + engines: {node: '>=20.0.0'} jsdom-testing-mocks@1.16.0: resolution: {integrity: sha512-wLrulXiLpjmcUYOYGEvz4XARkrmdVpyxzdBl9IAMbQ+ib2/UhUTRCn49McdNfXLff2ysGBUms49ZKX0LR1Q0gg==} @@ -6158,11 +6136,6 @@ packages: canvas: optional: true - jsesc@3.0.2: - resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} - engines: {node: '>=6'} - hasBin: true - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -6198,9 +6171,6 @@ packages: resolution: {integrity: sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - jsonc-parser@3.3.1: - resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} - jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} @@ -6803,8 +6773,8 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-deep-merge@1.0.5: - resolution: {integrity: sha512-3DioFgOzetbxbeUq8pB2NunXo8V0n4EvqsWM/cJoI6IA9zghd7cl/2pBOuWRf4dlvA+fcg5ugFMZaN2/RuoaGg==} + object-deep-merge@2.0.0: + resolution: {integrity: sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg==} object-hash@3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} @@ -6848,16 +6818,6 @@ packages: oxc-resolver@11.15.0: resolution: {integrity: sha512-Hk2J8QMYwmIO9XTCUiOH00+Xk2/+aBxRUnhrSlANDyCnLYc32R1WSIq1sU2yEdlqd53FfMpPEpnBYIKQMzliJw==} - oxlint@1.32.0: - resolution: {integrity: sha512-HYDQCga7flsdyLMUIxTgSnEx5KBxpP9VINB8NgO+UjV80xBiTQXyVsvjtneMT3ZBLMbL0SlG/Dm03XQAsEshMA==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - oxlint-tsgolint: '>=0.8.1' - peerDependenciesMeta: - oxlint-tsgolint: - optional: true - p-cancelable@2.1.1: resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} engines: {node: '>=8'} @@ -7065,8 +7025,8 @@ packages: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} - pnpm-workspace-yaml@1.4.2: - resolution: {integrity: sha512-L2EKuOeV8aSt3z0RNtdwkg96BHV4WRN9pN2oTHKkMQQRxVEHFXPTbB+nly6ip1qV+JQM6qBebSiMgPRBx8S0Vw==} + pnpm-workspace-yaml@1.4.3: + resolution: {integrity: sha512-Q8B3SWuuISy/Ciag4DFP7MCrJX07wfaekcqD2o/msdIj4x8Ql3bZ/NEKOXV7mTVh7m1YdiFWiMi9xH+0zuEGHw==} points-on-curve@0.2.0: resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} @@ -7553,10 +7513,6 @@ packages: regjsgen@0.8.0: resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} - regjsparser@0.12.0: - resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} - hasBin: true - regjsparser@0.13.0: resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} hasBin: true @@ -7602,6 +7558,10 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + reserved-identifiers@1.2.0: + resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} + engines: {node: '>=18'} + resize-observer-polyfill@1.5.1: resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} @@ -8146,6 +8106,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + to-valid-identifier@1.0.0: + resolution: {integrity: sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==} + engines: {node: '>=20'} + toggle-selection@1.0.6: resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} @@ -8851,6 +8815,12 @@ packages: zen-observable@0.8.15: resolution: {integrity: sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==} + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -9047,50 +9017,50 @@ snapshots: idb: 8.0.0 tslib: 2.8.1 - '@antfu/eslint-config@5.4.1(@eslint-react/eslint-plugin@1.53.1(eslint@9.39.1(jiti@1.21.7))(ts-api-utils@2.1.0(typescript@5.9.3))(typescript@5.9.3))(@next/eslint-plugin-next@15.5.9)(@vue/compiler-sfc@3.5.25)(eslint-plugin-react-hooks@5.2.0(eslint@9.39.1(jiti@1.21.7)))(eslint-plugin-react-refresh@0.4.24(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)(vitest@4.0.16(@types/node@18.15.0)(happy-dom@20.0.11)(jiti@1.21.7)(jsdom@27.3.0(canvas@3.2.0))(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + '@antfu/eslint-config@6.7.3(@eslint-react/eslint-plugin@2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@next/eslint-plugin-next@15.5.9)(@vue/compiler-sfc@3.5.25)(eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@1.21.7)))(eslint-plugin-react-refresh@0.4.26(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)(vitest@4.0.16(@types/node@18.15.0)(happy-dom@20.0.11)(jiti@1.21.7)(jsdom@27.3.0(canvas@3.2.0))(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@antfu/install-pkg': 1.1.0 '@clack/prompts': 0.11.0 - '@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.39.1(jiti@1.21.7)) + '@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.39.2(jiti@1.21.7)) '@eslint/markdown': 7.5.1 - '@stylistic/eslint-plugin': 5.6.1(eslint@9.39.1(jiti@1.21.7)) - '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/parser': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@vitest/eslint-plugin': 1.5.2(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)(vitest@4.0.16(@types/node@18.15.0)(happy-dom@20.0.11)(jiti@1.21.7)(jsdom@27.3.0(canvas@3.2.0))(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + '@stylistic/eslint-plugin': 5.6.1(eslint@9.39.2(jiti@1.21.7)) + '@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@vitest/eslint-plugin': 1.6.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)(vitest@4.0.16(@types/node@18.15.0)(happy-dom@20.0.11)(jiti@1.21.7)(jsdom@27.3.0(canvas@3.2.0))(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) ansis: 4.2.0 cac: 6.7.14 - eslint: 9.39.1(jiti@1.21.7) - eslint-config-flat-gitignore: 2.1.0(eslint@9.39.1(jiti@1.21.7)) + eslint: 9.39.2(jiti@1.21.7) + eslint-config-flat-gitignore: 2.1.0(eslint@9.39.2(jiti@1.21.7)) eslint-flat-config-utils: 2.1.4 - eslint-merge-processors: 2.0.0(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-antfu: 3.1.1(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-command: 3.3.1(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-import-lite: 0.3.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint-plugin-jsdoc: 59.1.0(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-jsonc: 2.21.0(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-n: 17.23.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + eslint-merge-processors: 2.0.0(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-antfu: 3.1.1(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-command: 3.4.0(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-import-lite: 0.4.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-jsdoc: 61.5.0(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-jsonc: 2.21.0(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-n: 17.23.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) eslint-plugin-no-only-tests: 3.3.0 - eslint-plugin-perfectionist: 4.15.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint-plugin-pnpm: 1.4.2(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-regexp: 2.10.0(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-toml: 0.12.0(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-unicorn: 61.0.2(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-unused-imports: 4.3.0(@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-vue: 10.6.2(@stylistic/eslint-plugin@5.6.1(eslint@9.39.1(jiti@1.21.7)))(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@1.21.7))) - eslint-plugin-yml: 1.19.0(eslint@9.39.1(jiti@1.21.7)) - eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.25)(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-perfectionist: 4.15.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-pnpm: 1.4.3(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-regexp: 2.10.0(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-toml: 0.12.0(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-unicorn: 62.0.0(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-unused-imports: 4.3.0(@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-vue: 10.6.2(@stylistic/eslint-plugin@5.6.1(eslint@9.39.2(jiti@1.21.7)))(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@1.21.7))) + eslint-plugin-yml: 1.19.1(eslint@9.39.2(jiti@1.21.7)) + eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.25)(eslint@9.39.2(jiti@1.21.7)) globals: 16.5.0 jsonc-eslint-parser: 2.4.2 local-pkg: 1.1.2 parse-gitignore: 2.0.0 toml-eslint-parser: 0.10.1 - vue-eslint-parser: 10.2.0(eslint@9.39.1(jiti@1.21.7)) + vue-eslint-parser: 10.2.0(eslint@9.39.2(jiti@1.21.7)) yaml-eslint-parser: 1.3.2 optionalDependencies: - '@eslint-react/eslint-plugin': 1.53.1(eslint@9.39.1(jiti@1.21.7))(ts-api-utils@2.1.0(typescript@5.9.3))(typescript@5.9.3) + '@eslint-react/eslint-plugin': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@next/eslint-plugin-next': 15.5.9 - eslint-plugin-react-hooks: 5.2.0(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-react-refresh: 0.4.24(eslint@9.39.1(jiti@1.21.7)) + eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-react-refresh: 0.4.26(eslint@9.39.2(jiti@1.21.7)) transitivePeerDependencies: - '@eslint/json' - '@vue/compiler-sfc' @@ -10032,21 +10002,23 @@ snapshots: '@epic-web/invariant@1.0.0': {} - '@es-joy/jsdoccomment@0.50.2': + '@es-joy/jsdoccomment@0.76.0': dependencies: '@types/estree': 1.0.8 - '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/types': 8.50.0 comment-parser: 1.4.1 esquery: 1.6.0 - jsdoc-type-pratt-parser: 4.1.0 + jsdoc-type-pratt-parser: 6.10.0 - '@es-joy/jsdoccomment@0.58.0': + '@es-joy/jsdoccomment@0.78.0': dependencies: '@types/estree': 1.0.8 - '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/types': 8.50.0 comment-parser: 1.4.1 esquery: 1.6.0 - jsdoc-type-pratt-parser: 5.4.0 + jsdoc-type-pratt-parser: 7.0.0 + + '@es-joy/resolve.exports@1.2.0': {} '@esbuild/aix-ppc64@0.25.0': optional: true @@ -10201,118 +10173,99 @@ snapshots: '@esbuild/win32-x64@0.25.12': optional: true - '@eslint-community/eslint-plugin-eslint-comments@4.5.0(eslint@9.39.1(jiti@1.21.7))': + '@eslint-community/eslint-plugin-eslint-comments@4.5.0(eslint@9.39.2(jiti@1.21.7))': dependencies: escape-string-regexp: 4.0.0 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) ignore: 5.3.2 - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@1.21.7))': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2(jiti@1.21.7))': dependencies: - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} '@eslint-community/regexpp@4.12.2': {} - '@eslint-react/ast@1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': + '@eslint-react/ast@2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@eslint-react/eff': 1.53.1 - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.3.13 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) string-ts: 2.3.1 - ts-pattern: 5.9.0 - transitivePeerDependencies: - - eslint - - supports-color - - typescript - - '@eslint-react/core@1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@eslint-react/ast': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 1.53.1 - '@eslint-react/kit': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/shared': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/var': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - birecord: 0.1.1 - ts-pattern: 5.9.0 - transitivePeerDependencies: - - eslint - - supports-color - - typescript - - '@eslint-react/eff@1.53.1': {} - - '@eslint-react/eslint-plugin@1.53.1(eslint@9.39.1(jiti@1.21.7))(ts-api-utils@2.1.0(typescript@5.9.3))(typescript@5.9.3)': - dependencies: - '@eslint-react/eff': 1.53.1 - '@eslint-react/kit': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/shared': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) - eslint-plugin-react-debug: 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint-plugin-react-dom: 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint-plugin-react-hooks-extra: 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint-plugin-react-naming-convention: 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint-plugin-react-web-api: 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint-plugin-react-x: 1.53.1(eslint@9.39.1(jiti@1.21.7))(ts-api-utils@2.1.0(typescript@5.9.3))(typescript@5.9.3) - optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - supports-color - - ts-api-utils - '@eslint-react/kit@1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': + '@eslint-react/core@2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@eslint-react/eff': 1.53.1 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/ast': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.3.13 + '@eslint-react/shared': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/var': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + birecord: 0.1.1 + eslint: 9.39.2(jiti@1.21.7) ts-pattern: 5.9.0 - zod: 4.1.13 + typescript: 5.9.3 transitivePeerDependencies: - - eslint - supports-color - - typescript - '@eslint-react/shared@1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@eslint-react/eff': 1.53.1 - '@eslint-react/kit': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - ts-pattern: 5.9.0 - zod: 4.1.13 - transitivePeerDependencies: - - eslint - - supports-color - - typescript + '@eslint-react/eff@2.3.13': {} - '@eslint-react/var@1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': + '@eslint-react/eslint-plugin@2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@eslint-react/ast': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 1.53.1 + '@eslint-react/eff': 2.3.13 + '@eslint-react/shared': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.49.0 + '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - string-ts: 2.3.1 - ts-pattern: 5.9.0 + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) + eslint-plugin-react-dom: 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-react-hooks-extra: 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-react-naming-convention: 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-react-web-api: 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-react-x: 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - - eslint - supports-color - - typescript - '@eslint/compat@1.4.1(eslint@9.39.1(jiti@1.21.7))': + '@eslint-react/shared@2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + dependencies: + '@eslint-react/eff': 2.3.13 + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) + ts-pattern: 5.9.0 + typescript: 5.9.3 + zod: 4.1.13 + transitivePeerDependencies: + - supports-color + + '@eslint-react/var@2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + dependencies: + '@eslint-react/ast': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.3.13 + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) + ts-pattern: 5.9.0 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@eslint/compat@1.4.1(eslint@9.39.2(jiti@1.21.7))': dependencies: '@eslint/core': 0.17.0 optionalDependencies: - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) '@eslint/config-array@0.21.1': dependencies: @@ -10348,7 +10301,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.39.1': {} + '@eslint/js@9.39.2': {} '@eslint/markdown@7.5.1': dependencies: @@ -10366,11 +10319,6 @@ snapshots: '@eslint/object-schema@2.1.7': {} - '@eslint/plugin-kit@0.3.4': - dependencies: - '@eslint/core': 0.15.2 - levn: 0.4.1 - '@eslint/plugin-kit@0.3.5': dependencies: '@eslint/core': 0.15.2 @@ -11114,30 +11062,6 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.15.0': optional: true - '@oxlint/darwin-arm64@1.32.0': - optional: true - - '@oxlint/darwin-x64@1.32.0': - optional: true - - '@oxlint/linux-arm64-gnu@1.32.0': - optional: true - - '@oxlint/linux-arm64-musl@1.32.0': - optional: true - - '@oxlint/linux-x64-gnu@1.32.0': - optional: true - - '@oxlint/linux-x64-musl@1.32.0': - optional: true - - '@oxlint/win32-arm64@1.32.0': - optional: true - - '@oxlint/win32-x64@1.32.0': - optional: true - '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -11679,6 +11603,8 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 19.2.3 + '@sindresorhus/base62@1.0.0': {} + '@sindresorhus/is@4.6.0': {} '@standard-schema/spec@1.1.0': {} @@ -11872,11 +11798,11 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@stylistic/eslint-plugin@5.6.1(eslint@9.39.1(jiti@1.21.7))': + '@stylistic/eslint-plugin@5.6.1(eslint@9.39.2(jiti@1.21.7))': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@1.21.7)) '@typescript-eslint/types': 8.49.0 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 @@ -12306,15 +12232,15 @@ snapshots: '@types/zen-observable@0.8.3': {} - '@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.49.0 - eslint: 9.39.1(jiti@1.21.7) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/type-utils': 8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 + eslint: 9.39.2(jiti@1.21.7) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.1.0(typescript@5.9.3) @@ -12322,14 +12248,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.49.0 + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -12337,7 +12263,16 @@ snapshots: '@typescript-eslint/project-service@8.49.0(typescript@5.9.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.49.0(typescript@5.9.3) - '@typescript-eslint/types': 8.49.0 + '@typescript-eslint/types': 8.50.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.50.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -12348,17 +12283,38 @@ snapshots: '@typescript-eslint/types': 8.49.0 '@typescript-eslint/visitor-keys': 8.49.0 + '@typescript-eslint/scope-manager@8.50.0': + dependencies: + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 + '@typescript-eslint/tsconfig-utils@8.49.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.50.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.49.0 '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.2(jiti@1.21.7) ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -12366,6 +12322,8 @@ snapshots: '@typescript-eslint/types@8.49.0': {} + '@typescript-eslint/types@8.50.0': {} + '@typescript-eslint/typescript-estree@8.49.0(typescript@5.9.3)': dependencies: '@typescript-eslint/project-service': 8.49.0(typescript@5.9.3) @@ -12381,13 +12339,39 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.50.0(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@typescript-eslint/project-service': 8.50.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@1.21.7)) '@typescript-eslint/scope-manager': 8.49.0 '@typescript-eslint/types': 8.49.0 '@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -12397,6 +12381,11 @@ snapshots: '@typescript-eslint/types': 8.49.0 eslint-visitor-keys: 4.2.1 + '@typescript-eslint/visitor-keys@8.50.0': + dependencies: + '@typescript-eslint/types': 8.50.0 + eslint-visitor-keys: 4.2.1 + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20251209.1': optional: true @@ -12459,11 +12448,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/eslint-plugin@1.5.2(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3)(vitest@4.0.16(@types/node@18.15.0)(happy-dom@20.0.11)(jiti@1.21.7)(jsdom@27.3.0(canvas@3.2.0))(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/eslint-plugin@1.6.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)(vitest@4.0.16(@types/node@18.15.0)(happy-dom@20.0.11)(jiti@1.21.7)(jsdom@27.3.0(canvas@3.2.0))(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) optionalDependencies: typescript: 5.9.3 vitest: 4.0.16(@types/node@18.15.0)(happy-dom@20.0.11)(jiti@1.21.7)(jsdom@27.3.0(canvas@3.2.0))(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) @@ -13858,83 +13847,84 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-compat-utils@0.5.1(eslint@9.39.1(jiti@1.21.7)): + eslint-compat-utils@0.5.1(eslint@9.39.2(jiti@1.21.7)): dependencies: - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) semver: 7.7.3 - eslint-compat-utils@0.6.5(eslint@9.39.1(jiti@1.21.7)): + eslint-compat-utils@0.6.5(eslint@9.39.2(jiti@1.21.7)): dependencies: - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) semver: 7.7.3 - eslint-config-flat-gitignore@2.1.0(eslint@9.39.1(jiti@1.21.7)): + eslint-config-flat-gitignore@2.1.0(eslint@9.39.2(jiti@1.21.7)): dependencies: - '@eslint/compat': 1.4.1(eslint@9.39.1(jiti@1.21.7)) - eslint: 9.39.1(jiti@1.21.7) + '@eslint/compat': 1.4.1(eslint@9.39.2(jiti@1.21.7)) + eslint: 9.39.2(jiti@1.21.7) eslint-flat-config-utils@2.1.4: dependencies: pathe: 2.0.3 - eslint-json-compat-utils@0.2.1(eslint@9.39.1(jiti@1.21.7))(jsonc-eslint-parser@2.4.2): + eslint-json-compat-utils@0.2.1(eslint@9.39.2(jiti@1.21.7))(jsonc-eslint-parser@2.4.2): dependencies: - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) esquery: 1.6.0 jsonc-eslint-parser: 2.4.2 - eslint-merge-processors@2.0.0(eslint@9.39.1(jiti@1.21.7)): + eslint-merge-processors@2.0.0(eslint@9.39.2(jiti@1.21.7)): dependencies: - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) - eslint-plugin-antfu@3.1.1(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-antfu@3.1.1(eslint@9.39.2(jiti@1.21.7)): dependencies: - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) - eslint-plugin-command@3.3.1(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-command@3.4.0(eslint@9.39.2(jiti@1.21.7)): dependencies: - '@es-joy/jsdoccomment': 0.50.2 - eslint: 9.39.1(jiti@1.21.7) + '@es-joy/jsdoccomment': 0.78.0 + eslint: 9.39.2(jiti@1.21.7) - eslint-plugin-es-x@7.8.0(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-es-x@7.8.0(eslint@9.39.2(jiti@1.21.7)): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@1.21.7)) '@eslint-community/regexpp': 4.12.2 - eslint: 9.39.1(jiti@1.21.7) - eslint-compat-utils: 0.5.1(eslint@9.39.1(jiti@1.21.7)) + eslint: 9.39.2(jiti@1.21.7) + eslint-compat-utils: 0.5.1(eslint@9.39.2(jiti@1.21.7)) - eslint-plugin-import-lite@0.3.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): + eslint-plugin-import-lite@0.4.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) - '@typescript-eslint/types': 8.49.0 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) optionalDependencies: typescript: 5.9.3 - eslint-plugin-jsdoc@59.1.0(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-jsdoc@61.5.0(eslint@9.39.2(jiti@1.21.7)): dependencies: - '@es-joy/jsdoccomment': 0.58.0 + '@es-joy/jsdoccomment': 0.76.0 + '@es-joy/resolve.exports': 1.2.0 are-docs-informative: 0.0.2 comment-parser: 1.4.1 debug: 4.4.3 escape-string-regexp: 4.0.0 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) espree: 10.4.0 esquery: 1.6.0 - object-deep-merge: 1.0.5 + html-entities: 2.6.0 + object-deep-merge: 2.0.0 parse-imports-exports: 0.2.4 semver: 7.7.3 spdx-expression-parse: 4.0.0 + to-valid-identifier: 1.0.0 transitivePeerDependencies: - supports-color - eslint-plugin-jsonc@2.21.0(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-jsonc@2.21.0(eslint@9.39.2(jiti@1.21.7)): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@1.21.7)) diff-sequences: 27.5.1 - eslint: 9.39.1(jiti@1.21.7) - eslint-compat-utils: 0.6.5(eslint@9.39.1(jiti@1.21.7)) - eslint-json-compat-utils: 0.2.1(eslint@9.39.1(jiti@1.21.7))(jsonc-eslint-parser@2.4.2) + eslint: 9.39.2(jiti@1.21.7) + eslint-compat-utils: 0.6.5(eslint@9.39.2(jiti@1.21.7)) + eslint-json-compat-utils: 0.2.1(eslint@9.39.2(jiti@1.21.7))(jsonc-eslint-parser@2.4.2) espree: 10.4.0 graphemer: 1.4.0 jsonc-eslint-parser: 2.4.2 @@ -13943,12 +13933,12 @@ snapshots: transitivePeerDependencies: - '@eslint/json' - eslint-plugin-n@17.23.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): + eslint-plugin-n@17.23.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@1.21.7)) enhanced-resolve: 5.18.3 - eslint: 9.39.1(jiti@1.21.7) - eslint-plugin-es-x: 7.8.0(eslint@9.39.1(jiti@1.21.7)) + eslint: 9.39.2(jiti@1.21.7) + eslint-plugin-es-x: 7.8.0(eslint@9.39.2(jiti@1.21.7)) get-tsconfig: 4.13.0 globals: 15.15.0 globrex: 0.1.2 @@ -13960,178 +13950,151 @@ snapshots: eslint-plugin-no-only-tests@3.3.0: {} - eslint-plugin-oxlint@1.32.0: - dependencies: - jsonc-parser: 3.3.1 - - eslint-plugin-perfectionist@4.15.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): + eslint-plugin-perfectionist@4.15.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) natural-orderby: 5.0.0 transitivePeerDependencies: - supports-color - typescript - eslint-plugin-pnpm@1.4.2(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-pnpm@1.4.3(eslint@9.39.2(jiti@1.21.7)): dependencies: empathic: 2.0.0 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) jsonc-eslint-parser: 2.4.2 pathe: 2.0.3 - pnpm-workspace-yaml: 1.4.2 + pnpm-workspace-yaml: 1.4.3 tinyglobby: 0.2.15 yaml: 2.8.2 yaml-eslint-parser: 1.3.2 - eslint-plugin-react-debug@1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): + eslint-plugin-react-dom@2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@eslint-react/ast': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/core': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 1.53.1 - '@eslint-react/kit': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/shared': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/var': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) - string-ts: 2.3.1 - ts-pattern: 5.9.0 - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - eslint-plugin-react-dom@1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): - dependencies: - '@eslint-react/ast': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/core': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 1.53.1 - '@eslint-react/kit': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/shared': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/var': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/ast': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/core': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.3.13 + '@eslint-react/shared': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/var': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.49.0 '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) compare-versions: 6.1.1 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) string-ts: 2.3.1 ts-pattern: 5.9.0 - optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - supports-color - eslint-plugin-react-hooks-extra@1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): + eslint-plugin-react-hooks-extra@2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@eslint-react/ast': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/core': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 1.53.1 - '@eslint-react/kit': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/shared': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/var': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/ast': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/core': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.3.13 + '@eslint-react/shared': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/var': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) string-ts: 2.3.1 ts-pattern: 5.9.0 - optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - supports-color - eslint-plugin-react-hooks@5.2.0(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@1.21.7)): dependencies: - eslint: 9.39.1(jiti@1.21.7) + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 + eslint: 9.39.2(jiti@1.21.7) + hermes-parser: 0.25.1 + zod: 3.25.76 + zod-validation-error: 4.0.2(zod@3.25.76) + transitivePeerDependencies: + - supports-color - eslint-plugin-react-naming-convention@1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): + eslint-plugin-react-naming-convention@2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@eslint-react/ast': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/core': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 1.53.1 - '@eslint-react/kit': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/shared': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/var': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/ast': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/core': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.3.13 + '@eslint-react/shared': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/var': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) string-ts: 2.3.1 ts-pattern: 5.9.0 - optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - supports-color - eslint-plugin-react-refresh@0.4.24(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-react-refresh@0.4.26(eslint@9.39.2(jiti@1.21.7)): dependencies: - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) - eslint-plugin-react-web-api@1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): + eslint-plugin-react-web-api@2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@eslint-react/ast': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/core': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 1.53.1 - '@eslint-react/kit': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/shared': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/var': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/ast': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/core': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.3.13 + '@eslint-react/shared': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/var': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.49.0 '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) string-ts: 2.3.1 ts-pattern: 5.9.0 - optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - supports-color - eslint-plugin-react-x@1.53.1(eslint@9.39.1(jiti@1.21.7))(ts-api-utils@2.1.0(typescript@5.9.3))(typescript@5.9.3): + eslint-plugin-react-x@2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@eslint-react/ast': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/core': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 1.53.1 - '@eslint-react/kit': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/shared': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/var': 1.53.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/ast': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/core': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.3.13 + '@eslint-react/shared': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/var': 2.3.13(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.49.0 - '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/types': 8.49.0 - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) compare-versions: 6.1.1 - eslint: 9.39.1(jiti@1.21.7) - is-immutable-type: 5.0.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) + is-immutable-type: 5.0.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) string-ts: 2.3.1 - ts-pattern: 5.9.0 - optionalDependencies: ts-api-utils: 2.1.0(typescript@5.9.3) + ts-pattern: 5.9.0 typescript: 5.9.3 transitivePeerDependencies: - supports-color - eslint-plugin-regexp@2.10.0(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-regexp@2.10.0(eslint@9.39.2(jiti@1.21.7)): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@1.21.7)) '@eslint-community/regexpp': 4.12.2 comment-parser: 1.4.1 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) jsdoc-type-pratt-parser: 4.8.0 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-sonarjs@3.0.5(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-sonarjs@3.0.5(eslint@9.39.2(jiti@1.21.7)): dependencies: '@eslint-community/regexpp': 4.12.1 builtin-modules: 3.3.0 bytes: 3.1.2 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) functional-red-black-tree: 1.0.1 jsx-ast-utils-x: 0.1.0 lodash.merge: 4.6.2 @@ -14140,10 +14103,10 @@ snapshots: semver: 7.7.2 typescript: 5.9.3 - eslint-plugin-storybook@9.1.16(eslint@9.39.1(jiti@1.21.7))(storybook@9.1.17(@testing-library/dom@10.4.1)(vite@7.3.0(@types/node@18.15.0)(jiti@1.21.7)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(typescript@5.9.3): + eslint-plugin-storybook@10.1.10(eslint@9.39.2(jiti@1.21.7))(storybook@9.1.17(@testing-library/dom@10.4.1)(vite@7.3.0(@types/node@18.15.0)(jiti@1.21.7)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)))(typescript@5.9.3): dependencies: - '@typescript-eslint/utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) storybook: 9.1.17(@testing-library/dom@10.4.1)(vite@7.3.0(@types/node@18.15.0)(jiti@1.21.7)(sass@1.95.0)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - supports-color @@ -14155,26 +14118,26 @@ snapshots: postcss: 8.5.6 tailwindcss: 3.4.18(tsx@4.21.0)(yaml@2.8.2) - eslint-plugin-toml@0.12.0(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-toml@0.12.0(eslint@9.39.2(jiti@1.21.7)): dependencies: debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) - eslint-compat-utils: 0.6.5(eslint@9.39.1(jiti@1.21.7)) + eslint: 9.39.2(jiti@1.21.7) + eslint-compat-utils: 0.6.5(eslint@9.39.2(jiti@1.21.7)) lodash: 4.17.21 toml-eslint-parser: 0.10.1 transitivePeerDependencies: - supports-color - eslint-plugin-unicorn@61.0.2(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-unicorn@62.0.0(eslint@9.39.2(jiti@1.21.7)): dependencies: '@babel/helper-validator-identifier': 7.28.5 - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) - '@eslint/plugin-kit': 0.3.4 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@1.21.7)) + '@eslint/plugin-kit': 0.3.5 change-case: 5.4.4 ci-info: 4.3.1 clean-regexp: 1.0.0 core-js-compat: 3.47.0 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) esquery: 1.6.0 find-up-simple: 1.0.1 globals: 16.5.0 @@ -14183,46 +14146,46 @@ snapshots: jsesc: 3.1.0 pluralize: 8.0.0 regexp-tree: 0.1.27 - regjsparser: 0.12.0 + regjsparser: 0.13.0 semver: 7.7.3 strip-indent: 4.1.1 - eslint-plugin-unused-imports@4.3.0(@typescript-eslint/eslint-plugin@8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-unused-imports@4.3.0(@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7)): dependencies: - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - eslint-plugin-vue@10.6.2(@stylistic/eslint-plugin@5.6.1(eslint@9.39.1(jiti@1.21.7)))(@typescript-eslint/parser@8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.1(jiti@1.21.7))(vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@1.21.7))): + eslint-plugin-vue@10.6.2(@stylistic/eslint-plugin@5.6.1(eslint@9.39.2(jiti@1.21.7)))(@typescript-eslint/parser@8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@1.21.7))): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) - eslint: 9.39.1(jiti@1.21.7) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@1.21.7)) + eslint: 9.39.2(jiti@1.21.7) natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 7.1.1 semver: 7.7.3 - vue-eslint-parser: 10.2.0(eslint@9.39.1(jiti@1.21.7)) + vue-eslint-parser: 10.2.0(eslint@9.39.2(jiti@1.21.7)) xml-name-validator: 4.0.0 optionalDependencies: - '@stylistic/eslint-plugin': 5.6.1(eslint@9.39.1(jiti@1.21.7)) - '@typescript-eslint/parser': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) + '@stylistic/eslint-plugin': 5.6.1(eslint@9.39.2(jiti@1.21.7)) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - eslint-plugin-yml@1.19.0(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-yml@1.19.1(eslint@9.39.2(jiti@1.21.7)): dependencies: debug: 4.4.3 diff-sequences: 27.5.1 escape-string-regexp: 4.0.0 - eslint: 9.39.1(jiti@1.21.7) - eslint-compat-utils: 0.6.5(eslint@9.39.1(jiti@1.21.7)) + eslint: 9.39.2(jiti@1.21.7) + eslint-compat-utils: 0.6.5(eslint@9.39.2(jiti@1.21.7)) natural-compare: 1.4.0 yaml-eslint-parser: 1.3.2 transitivePeerDependencies: - supports-color - eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.25)(eslint@9.39.1(jiti@1.21.7)): + eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.25)(eslint@9.39.2(jiti@1.21.7)): dependencies: '@vue/compiler-sfc': 3.5.25 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) eslint-scope@5.1.1: dependencies: @@ -14238,15 +14201,15 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.1(jiti@1.21.7): + eslint@9.39.2(jiti@1.21.7): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@1.21.7)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 '@eslint/eslintrc': 3.3.3 - '@eslint/js': 9.39.1 + '@eslint/js': 9.39.2 '@eslint/plugin-kit': 0.3.5 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 @@ -14802,6 +14765,12 @@ snapshots: he@1.2.0: {} + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + highlight.js@10.7.3: {} highlightjs-vue@1.0.0: {} @@ -15001,10 +14970,10 @@ snapshots: is-hexadecimal@2.0.1: {} - is-immutable-type@5.0.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3): + is-immutable-type@5.0.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.1(jiti@1.21.7) + '@typescript-eslint/type-utils': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) ts-api-utils: 2.1.0(typescript@5.9.3) ts-declaration-location: 1.0.7(typescript@5.9.3) typescript: 5.9.3 @@ -15106,11 +15075,11 @@ snapshots: dependencies: argparse: 2.0.1 - jsdoc-type-pratt-parser@4.1.0: {} - jsdoc-type-pratt-parser@4.8.0: {} - jsdoc-type-pratt-parser@5.4.0: {} + jsdoc-type-pratt-parser@6.10.0: {} + + jsdoc-type-pratt-parser@7.0.0: {} jsdom-testing-mocks@1.16.0: dependencies: @@ -15146,8 +15115,6 @@ snapshots: - supports-color - utf-8-validate - jsesc@3.0.2: {} - jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -15173,8 +15140,6 @@ snapshots: espree: 9.6.1 semver: 7.7.3 - jsonc-parser@3.3.1: {} - jsonfile@6.2.0: dependencies: universalify: 2.0.1 @@ -16118,9 +16083,7 @@ snapshots: object-assign@4.1.1: {} - object-deep-merge@1.0.5: - dependencies: - type-fest: 4.2.0 + object-deep-merge@2.0.0: {} object-hash@3.0.0: {} @@ -16184,17 +16147,6 @@ snapshots: '@oxc-resolver/binding-win32-ia32-msvc': 11.15.0 '@oxc-resolver/binding-win32-x64-msvc': 11.15.0 - oxlint@1.32.0: - optionalDependencies: - '@oxlint/darwin-arm64': 1.32.0 - '@oxlint/darwin-x64': 1.32.0 - '@oxlint/linux-arm64-gnu': 1.32.0 - '@oxlint/linux-arm64-musl': 1.32.0 - '@oxlint/linux-x64-gnu': 1.32.0 - '@oxlint/linux-x64-musl': 1.32.0 - '@oxlint/win32-arm64': 1.32.0 - '@oxlint/win32-x64': 1.32.0 - p-cancelable@2.1.1: {} p-limit@2.3.0: @@ -16388,7 +16340,7 @@ snapshots: pluralize@8.0.0: {} - pnpm-workspace-yaml@1.4.2: + pnpm-workspace-yaml@1.4.3: dependencies: yaml: 2.8.2 @@ -16949,10 +16901,6 @@ snapshots: regjsgen@0.8.0: {} - regjsparser@0.12.0: - dependencies: - jsesc: 3.0.2 - regjsparser@0.13.0: dependencies: jsesc: 3.1.0 @@ -17049,6 +16997,8 @@ snapshots: require-from-string@2.0.2: {} + reserved-identifiers@1.2.0: {} + resize-observer-polyfill@1.5.1: {} resolve-alpn@1.2.1: {} @@ -17661,6 +17611,11 @@ snapshots: dependencies: is-number: 7.0.0 + to-valid-identifier@1.0.0: + dependencies: + '@sindresorhus/base62': 1.0.0 + reserved-identifiers: 1.2.0 + toggle-selection@1.0.6: {} toml-eslint-parser@0.10.1: @@ -17765,7 +17720,8 @@ snapshots: type-fest@2.19.0: {} - type-fest@4.2.0: {} + type-fest@4.2.0: + optional: true typescript@5.9.3: {} @@ -18058,10 +18014,10 @@ snapshots: vscode-uri@3.0.8: {} - vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@1.21.7)): + vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@1.21.7)): dependencies: debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 @@ -18369,6 +18325,10 @@ snapshots: zen-observable@0.8.15: {} + zod-validation-error@4.0.2(zod@3.25.76): + dependencies: + zod: 3.25.76 + zod@3.25.76: {} zod@4.1.13: {} diff --git a/web/scripts/copy-and-start.mjs b/web/scripts/copy-and-start.mjs index b23ce636a4..9525c6b45f 100644 --- a/web/scripts/copy-and-start.mjs +++ b/web/scripts/copy-and-start.mjs @@ -4,8 +4,8 @@ * It is intended to be used as a replacement for `next start`. */ -import { cp, mkdir, stat } from 'node:fs/promises' import { spawn } from 'node:child_process' +import { cp, mkdir, stat } from 'node:fs/promises' import path from 'node:path' // Configuration for directories to copy diff --git a/web/scripts/generate-icons.js b/web/scripts/generate-icons.js index b1b6f24435..979fbf059f 100644 --- a/web/scripts/generate-icons.js +++ b/web/scripts/generate-icons.js @@ -15,40 +15,41 @@ const sizes = [ { size: 128, name: 'icon-128x128.png' }, { size: 144, name: 'icon-144x144.png' }, { size: 152, name: 'icon-152x152.png' }, -]; +] -const inputPath = path.join(__dirname, '../public/icon.svg'); -const outputDir = path.join(__dirname, '../public'); +const inputPath = path.join(__dirname, '../public/icon.svg') +const outputDir = path.join(__dirname, '../public') // Generate icons async function generateIcons() { try { - console.log('Generating PWA icons...'); - + console.log('Generating PWA icons...') + for (const { size, name } of sizes) { - const outputPath = path.join(outputDir, name); - + const outputPath = path.join(outputDir, name) + await sharp(inputPath) .resize(size, size) .png() - .toFile(outputPath); - - console.log(`✓ Generated ${name} (${size}x${size})`); + .toFile(outputPath) + + console.log(`✓ Generated ${name} (${size}x${size})`) } - + // Generate apple-touch-icon await sharp(inputPath) .resize(180, 180) .png() - .toFile(path.join(outputDir, 'apple-touch-icon.png')); - - console.log('✓ Generated apple-touch-icon.png (180x180)'); - - console.log('\n✅ All icons generated successfully!'); - } catch (error) { - console.error('Error generating icons:', error); - process.exit(1); + .toFile(path.join(outputDir, 'apple-touch-icon.png')) + + console.log('✓ Generated apple-touch-icon.png (180x180)') + + console.log('\n✅ All icons generated successfully!') + } + catch (error) { + console.error('Error generating icons:', error) + process.exit(1) } } -generateIcons(); \ No newline at end of file +generateIcons() diff --git a/web/scripts/optimize-standalone.js b/web/scripts/optimize-standalone.js index c2f472bee1..b73667eac6 100644 --- a/web/scripts/optimize-standalone.js +++ b/web/scripts/optimize-standalone.js @@ -10,14 +10,14 @@ import { fileURLToPath } from 'node:url' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) -console.log('🔧 Optimizing standalone output...'); +console.log('🔧 Optimizing standalone output...') -const standaloneDir = path.join(__dirname, '..', '.next', 'standalone'); +const standaloneDir = path.join(__dirname, '..', '.next', 'standalone') // Check if standalone directory exists if (!fs.existsSync(standaloneDir)) { - console.error('❌ Standalone directory not found. Please run "next build" first.'); - process.exit(1); + console.error('❌ Standalone directory not found. Please run "next build" first.') + process.exit(1) } // List of paths to remove (relative to standalone directory) @@ -28,126 +28,136 @@ const pathsToRemove = [ 'node_modules/.pnpm/terser-webpack-plugin@*/node_modules/jest-worker', // Remove actual jest-worker packages (directories only, not symlinks) 'node_modules/.pnpm/jest-worker@*', -]; +] // Function to safely remove a path function removePath(basePath, relativePath) { - const fullPath = path.join(basePath, relativePath); + const fullPath = path.join(basePath, relativePath) // Handle wildcard patterns if (relativePath.includes('*')) { - const parts = relativePath.split('/'); - let currentPath = basePath; + const parts = relativePath.split('/') + let currentPath = basePath for (let i = 0; i < parts.length; i++) { - const part = parts[i]; + const part = parts[i] if (part.includes('*')) { // Find matching directories if (fs.existsSync(currentPath)) { - const entries = fs.readdirSync(currentPath); + const entries = fs.readdirSync(currentPath) // replace '*' with '.*' - const regexPattern = part.replace(/\*/g, '.*'); + const regexPattern = part.replace(/\*/g, '.*') - const regex = new RegExp(`^${regexPattern}$`); + const regex = new RegExp(`^${regexPattern}$`) for (const entry of entries) { if (regex.test(entry)) { - const remainingPath = parts.slice(i + 1).join('/'); - const matchedPath = path.join(currentPath, entry, remainingPath); + const remainingPath = parts.slice(i + 1).join('/') + const matchedPath = path.join(currentPath, entry, remainingPath) try { // Use lstatSync to check if path exists (works for both files and symlinks) - const stats = fs.lstatSync(matchedPath); + const stats = fs.lstatSync(matchedPath) if (stats.isSymbolicLink()) { // Remove symlink - fs.unlinkSync(matchedPath); - console.log(`✅ Removed symlink: ${path.relative(basePath, matchedPath)}`); - } else { - // Remove directory/file - fs.rmSync(matchedPath, { recursive: true, force: true }); - console.log(`✅ Removed: ${path.relative(basePath, matchedPath)}`); + fs.unlinkSync(matchedPath) + console.log(`✅ Removed symlink: ${path.relative(basePath, matchedPath)}`) } - } catch (error) { + else { + // Remove directory/file + fs.rmSync(matchedPath, { recursive: true, force: true }) + console.log(`✅ Removed: ${path.relative(basePath, matchedPath)}`) + } + } + catch (error) { // Silently ignore ENOENT (path not found) errors if (error.code !== 'ENOENT') { - console.error(`❌ Failed to remove ${matchedPath}: ${error.message}`); + console.error(`❌ Failed to remove ${matchedPath}: ${error.message}`) } } } } } - return; - } else { - currentPath = path.join(currentPath, part); + return + } + else { + currentPath = path.join(currentPath, part) } } - } else { + } + else { // Direct path removal if (fs.existsSync(fullPath)) { try { - fs.rmSync(fullPath, { recursive: true, force: true }); - console.log(`✅ Removed: ${relativePath}`); - } catch (error) { - console.error(`❌ Failed to remove ${fullPath}: ${error.message}`); + fs.rmSync(fullPath, { recursive: true, force: true }) + console.log(`✅ Removed: ${relativePath}`) + } + catch (error) { + console.error(`❌ Failed to remove ${fullPath}: ${error.message}`) } } } } // Remove unnecessary paths -console.log('🗑️ Removing unnecessary files...'); +console.log('🗑️ Removing unnecessary files...') for (const pathToRemove of pathsToRemove) { - removePath(standaloneDir, pathToRemove); + removePath(standaloneDir, pathToRemove) } // Calculate size reduction -console.log('\n📊 Optimization complete!'); +console.log('\n📊 Optimization complete!') // Optional: Display the size of remaining jest-related files (if any) const checkForJest = (dir) => { - const jestFiles = []; + const jestFiles = [] function walk(currentPath) { - if (!fs.existsSync(currentPath)) return; + if (!fs.existsSync(currentPath)) + return try { - const entries = fs.readdirSync(currentPath); + const entries = fs.readdirSync(currentPath) for (const entry of entries) { - const fullPath = path.join(currentPath, entry); + const fullPath = path.join(currentPath, entry) try { - const stat = fs.lstatSync(fullPath); // Use lstatSync to handle symlinks + const stat = fs.lstatSync(fullPath) // Use lstatSync to handle symlinks if (stat.isDirectory() && !stat.isSymbolicLink()) { // Skip node_modules subdirectories to avoid deep traversal if (entry === 'node_modules' && currentPath !== standaloneDir) { - continue; + continue } - walk(fullPath); - } else if (stat.isFile() && entry.includes('jest')) { - jestFiles.push(path.relative(standaloneDir, fullPath)); + walk(fullPath) } - } catch (err) { + else if (stat.isFile() && entry.includes('jest')) { + jestFiles.push(path.relative(standaloneDir, fullPath)) + } + } + catch (err) { // Skip files that can't be accessed - continue; + continue } } - } catch (err) { + } + catch (err) { // Skip directories that can't be read - return; + } } - walk(dir); - return jestFiles; -}; - -const remainingJestFiles = checkForJest(standaloneDir); -if (remainingJestFiles.length > 0) { - console.log('\n⚠️ Warning: Some jest-related files still remain:'); - remainingJestFiles.forEach(file => console.log(` - ${file}`)); -} else { - console.log('\n✨ No jest-related files found in standalone output!'); + walk(dir) + return jestFiles +} + +const remainingJestFiles = checkForJest(standaloneDir) +if (remainingJestFiles.length > 0) { + console.log('\n⚠️ Warning: Some jest-related files still remain:') + remainingJestFiles.forEach(file => console.log(` - ${file}`)) +} +else { + console.log('\n✨ No jest-related files found in standalone output!') } diff --git a/web/service/_tools_util.spec.ts b/web/service/_tools_util.spec.ts index 658c276df1..e3bec3efcf 100644 --- a/web/service/_tools_util.spec.ts +++ b/web/service/_tools_util.spec.ts @@ -1,16 +1,16 @@ import { buildProviderQuery } from './_tools_util' describe('makeProviderQuery', () => { - test('collectionName without special chars', () => { + it('collectionName without special chars', () => { expect(buildProviderQuery('ABC')).toBe('provider=ABC') }) - test('should escape &', () => { + it('should escape &', () => { expect(buildProviderQuery('ABC&DEF')).toBe('provider=ABC%26DEF') }) - test('should escape /', () => { + it('should escape /', () => { expect(buildProviderQuery('ABC/DEF')).toBe('provider=ABC%2FDEF') }) - test('should escape ?', () => { + it('should escape ?', () => { expect(buildProviderQuery('ABC?DEF')).toBe('provider=ABC%3FDEF') }) }) diff --git a/web/service/access-control.ts b/web/service/access-control.ts index 18dc9e0001..ad0c14fd0a 100644 --- a/web/service/access-control.ts +++ b/web/service/access-control.ts @@ -1,16 +1,16 @@ -import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import { get, post } from './base' -import { getUserCanAccess } from './share' import type { AccessControlAccount, AccessControlGroup, AccessMode, Subject } from '@/models/access-control' import type { App } from '@/types/app' +import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useGlobalPublicStore } from '@/context/global-public-context' +import { get, post } from './base' +import { getUserCanAccess } from './share' const NAME_SPACE = 'access-control' export const useAppWhiteListSubjects = (appId: string | undefined, enabled: boolean) => { return useQuery({ queryKey: [NAME_SPACE, 'app-whitelist-subjects', appId], - queryFn: () => get<{ groups: AccessControlGroup[]; members: AccessControlAccount[] }>(`/enterprise/webapp/app/subjects?appId=${appId}`), + queryFn: () => get<{ groups: AccessControlGroup[], members: AccessControlAccount[] }>(`/enterprise/webapp/app/subjects?appId=${appId}`), enabled: !!appId && enabled, staleTime: 0, gcTime: 0, @@ -24,7 +24,7 @@ type SearchResults = { hasMore: boolean } -export const useSearchForWhiteListCandidates = (query: { keyword?: string; groupId?: AccessControlGroup['id']; resultsPerPage?: number }, enabled: boolean) => { +export const useSearchForWhiteListCandidates = (query: { keyword?: string, groupId?: AccessControlGroup['id'], resultsPerPage?: number }, enabled: boolean) => { return useInfiniteQuery({ queryKey: [NAME_SPACE, 'app-whitelist-candidates', query], queryFn: ({ pageParam }) => { @@ -70,7 +70,7 @@ export const useUpdateAccessMode = () => { }) } -export const useGetUserCanAccessApp = ({ appId, isInstalledApp = true, enabled }: { appId?: string; isInstalledApp?: boolean; enabled?: boolean }) => { +export const useGetUserCanAccessApp = ({ appId, isInstalledApp = true, enabled }: { appId?: string, isInstalledApp?: boolean, enabled?: boolean }) => { const systemFeatures = useGlobalPublicStore(s => s.systemFeatures) return useQuery({ queryKey: [NAME_SPACE, 'user-can-access-app', appId], diff --git a/web/service/annotation.ts b/web/service/annotation.ts index af708fe174..acdb944386 100644 --- a/web/service/annotation.ts +++ b/web/service/annotation.ts @@ -1,7 +1,7 @@ import type { Fetcher } from 'swr' -import { del, get, post } from './base' import type { AnnotationCreateResponse, AnnotationEnableStatus, AnnotationItemBasic, EmbeddingModelConfig } from '@/app/components/app/annotation/type' import { ANNOTATION_DEFAULT } from '@/config' +import { del, get, post } from './base' export const fetchAnnotationConfig = (appId: string) => { return get(`apps/${appId}/annotation-setting`) @@ -44,12 +44,12 @@ export const addAnnotation = (appId: string, body: AnnotationItemBasic) => { return post<AnnotationCreateResponse>(`apps/${appId}/annotations`, { body }) } -export const annotationBatchImport: Fetcher<{ job_id: string; job_status: string }, { url: string; body: FormData }> = ({ url, body }) => { - return post<{ job_id: string; job_status: string }>(url, { body }, { bodyStringify: false, deleteContentType: true }) +export const annotationBatchImport: Fetcher<{ job_id: string, job_status: string }, { url: string, body: FormData }> = ({ url, body }) => { + return post<{ job_id: string, job_status: string }>(url, { body }, { bodyStringify: false, deleteContentType: true }) } -export const checkAnnotationBatchImportProgress: Fetcher<{ job_id: string; job_status: string }, { jobID: string; appId: string }> = ({ jobID, appId }) => { - return get<{ job_id: string; job_status: string }>(`/apps/${appId}/annotations/batch-import-status/${jobID}`) +export const checkAnnotationBatchImportProgress: Fetcher<{ job_id: string, job_status: string }, { jobID: string, appId: string }> = ({ jobID, appId }) => { + return get<{ job_id: string, job_status: string }>(`/apps/${appId}/annotations/batch-import-status/${jobID}`) } export const editAnnotation = (appId: string, annotationId: string, body: AnnotationItemBasic) => { diff --git a/web/service/apps.ts b/web/service/apps.ts index 89001bffec..1e3c93a33a 100644 --- a/web/service/apps.ts +++ b/web/service/apps.ts @@ -1,19 +1,19 @@ -import { del, get, patch, post, put } from './base' +import type { TracingProvider } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type' import type { ApiKeysListResponse, AppDailyConversationsResponse, AppDailyEndUsersResponse, AppDailyMessagesResponse, AppDetailResponse, AppListResponse, AppStatisticsResponse, AppTemplatesResponse, AppTokenCostsResponse, AppVoicesListResponse, CreateApiKeyResponse, DSLImportMode, DSLImportResponse, GenerationIntroductionResponse, TracingConfig, TracingStatus, UpdateAppModelConfigResponse, UpdateAppSiteCodeResponse, UpdateOpenAIKeyResponse, ValidateOpenAIKeyResponse, WebhookTriggerResponse, WorkflowDailyConversationsResponse } from '@/models/app' import type { CommonResponse } from '@/models/common' import type { AppIconType, AppModeEnum, ModelConfig } from '@/types/app' -import type { TracingProvider } from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type' +import { del, get, patch, post, put } from './base' -export const fetchAppList = ({ url, params }: { url: string; params?: Record<string, any> }): Promise<AppListResponse> => { +export const fetchAppList = ({ url, params }: { url: string, params?: Record<string, any> }): Promise<AppListResponse> => { return get<AppListResponse>(url, { params }) } -export const fetchAppDetail = ({ url, id }: { url: string; id: string }): Promise<AppDetailResponse> => { +export const fetchAppDetail = ({ url, id }: { url: string, id: string }): Promise<AppDetailResponse> => { return get<AppDetailResponse>(`${url}/${id}`) } // Direct API call function for non-SWR usage -export const fetchAppDetailDirect = async ({ url, id }: { url: string; id: string }): Promise<AppDetailResponse> => { +export const fetchAppDetailDirect = async ({ url, id }: { url: string, id: string }): Promise<AppDetailResponse> => { return get<AppDetailResponse>(`${url}/${id}`) } @@ -84,7 +84,7 @@ export const copyApp = ({ return post<AppDetailResponse>(`apps/${appID}/copy`, { body: { name, icon_type, icon, icon_background, mode, description } }) } -export const exportAppConfig = ({ appID, include = false, workflowID }: { appID: string; include?: boolean; workflowID?: string }): Promise<{ data: string }> => { +export const exportAppConfig = ({ appID, include = false, workflowID }: { appID: string, include?: boolean, workflowID?: string }): Promise<{ data: string }> => { const params = new URLSearchParams({ include_secret: include.toString(), }) @@ -93,7 +93,7 @@ export const exportAppConfig = ({ appID, include = false, workflowID }: { appID: return get<{ data: string }>(`apps/${appID}/export?${params.toString()}`) } -export const importDSL = ({ mode, yaml_content, yaml_url, app_id, name, description, icon_type, icon, icon_background }: { mode: DSLImportMode; yaml_content?: string; yaml_url?: string; app_id?: string; name?: string; description?: string; icon_type?: AppIconType; icon?: string; icon_background?: string }): Promise<DSLImportResponse> => { +export const importDSL = ({ mode, yaml_content, yaml_url, app_id, name, description, icon_type, icon, icon_background }: { mode: DSLImportMode, yaml_content?: string, yaml_url?: string, app_id?: string, name?: string, description?: string, icon_type?: AppIconType, icon?: string, icon_background?: string }): Promise<DSLImportResponse> => { return post<DSLImportResponse>('apps/imports', { body: { mode, yaml_content, yaml_url, app_id, name, description, icon, icon_type, icon_background } }) } @@ -101,7 +101,7 @@ export const importDSLConfirm = ({ import_id }: { import_id: string }): Promise< return post<DSLImportResponse>(`apps/imports/${import_id}/confirm`, { body: {} }) } -export const switchApp = ({ appID, name, icon_type, icon, icon_background }: { appID: string; name: string; icon_type: AppIconType; icon: string; icon_background?: string | null }): Promise<{ new_app_id: string }> => { +export const switchApp = ({ appID, name, icon_type, icon, icon_background }: { appID: string, name: string, icon_type: AppIconType, icon: string, icon_background?: string | null }): Promise<{ new_app_id: string }> => { return post<{ new_app_id: string }>(`apps/${appID}/convert-to-workflow`, { body: { name, icon_type, icon, icon_background } }) } @@ -109,16 +109,16 @@ export const deleteApp = (appID: string): Promise<CommonResponse> => { return del<CommonResponse>(`apps/${appID}`) } -export const updateAppSiteStatus = ({ url, body }: { url: string; body: Record<string, any> }): Promise<AppDetailResponse> => { +export const updateAppSiteStatus = ({ url, body }: { url: string, body: Record<string, any> }): Promise<AppDetailResponse> => { return post<AppDetailResponse>(url, { body }) } -export const updateAppApiStatus = ({ url, body }: { url: string; body: Record<string, any> }): Promise<AppDetailResponse> => { +export const updateAppApiStatus = ({ url, body }: { url: string, body: Record<string, any> }): Promise<AppDetailResponse> => { return post<AppDetailResponse>(url, { body }) } // path: /apps/{appId}/rate-limit -export const updateAppRateLimit = ({ url, body }: { url: string; body: Record<string, any> }): Promise<AppDetailResponse> => { +export const updateAppRateLimit = ({ url, body }: { url: string, body: Record<string, any> }): Promise<AppDetailResponse> => { return post<AppDetailResponse>(url, { body }) } @@ -126,68 +126,68 @@ export const updateAppSiteAccessToken = ({ url }: { url: string }): Promise<Upda return post<UpdateAppSiteCodeResponse>(url) } -export const updateAppSiteConfig = ({ url, body }: { url: string; body: Record<string, any> }): Promise<AppDetailResponse> => { +export const updateAppSiteConfig = ({ url, body }: { url: string, body: Record<string, any> }): Promise<AppDetailResponse> => { return post<AppDetailResponse>(url, { body }) } -export const getAppDailyMessages = ({ url, params }: { url: string; params: Record<string, any> }): Promise<AppDailyMessagesResponse> => { +export const getAppDailyMessages = ({ url, params }: { url: string, params: Record<string, any> }): Promise<AppDailyMessagesResponse> => { return get<AppDailyMessagesResponse>(url, { params }) } -export const getAppDailyConversations = ({ url, params }: { url: string; params: Record<string, any> }): Promise<AppDailyConversationsResponse> => { +export const getAppDailyConversations = ({ url, params }: { url: string, params: Record<string, any> }): Promise<AppDailyConversationsResponse> => { return get<AppDailyConversationsResponse>(url, { params }) } -export const getWorkflowDailyConversations = ({ url, params }: { url: string; params: Record<string, any> }): Promise<WorkflowDailyConversationsResponse> => { +export const getWorkflowDailyConversations = ({ url, params }: { url: string, params: Record<string, any> }): Promise<WorkflowDailyConversationsResponse> => { return get<WorkflowDailyConversationsResponse>(url, { params }) } -export const getAppStatistics = ({ url, params }: { url: string; params: Record<string, any> }): Promise<AppStatisticsResponse> => { +export const getAppStatistics = ({ url, params }: { url: string, params: Record<string, any> }): Promise<AppStatisticsResponse> => { return get<AppStatisticsResponse>(url, { params }) } -export const getAppDailyEndUsers = ({ url, params }: { url: string; params: Record<string, any> }): Promise<AppDailyEndUsersResponse> => { +export const getAppDailyEndUsers = ({ url, params }: { url: string, params: Record<string, any> }): Promise<AppDailyEndUsersResponse> => { return get<AppDailyEndUsersResponse>(url, { params }) } -export const getAppTokenCosts = ({ url, params }: { url: string; params: Record<string, any> }): Promise<AppTokenCostsResponse> => { +export const getAppTokenCosts = ({ url, params }: { url: string, params: Record<string, any> }): Promise<AppTokenCostsResponse> => { return get<AppTokenCostsResponse>(url, { params }) } -export const updateAppModelConfig = ({ url, body }: { url: string; body: Record<string, any> }): Promise<UpdateAppModelConfigResponse> => { +export const updateAppModelConfig = ({ url, body }: { url: string, body: Record<string, any> }): Promise<UpdateAppModelConfigResponse> => { return post<UpdateAppModelConfigResponse>(url, { body }) } // For temp testing -export const fetchAppListNoMock = ({ url, params }: { url: string; params: Record<string, any> }): Promise<AppListResponse> => { +export const fetchAppListNoMock = ({ url, params }: { url: string, params: Record<string, any> }): Promise<AppListResponse> => { return get<AppListResponse>(url, params) } -export const fetchApiKeysList = ({ url, params }: { url: string; params: Record<string, any> }): Promise<ApiKeysListResponse> => { +export const fetchApiKeysList = ({ url, params }: { url: string, params: Record<string, any> }): Promise<ApiKeysListResponse> => { return get<ApiKeysListResponse>(url, params) } -export const delApikey = ({ url, params }: { url: string; params: Record<string, any> }): Promise<CommonResponse> => { +export const delApikey = ({ url, params }: { url: string, params: Record<string, any> }): Promise<CommonResponse> => { return del<CommonResponse>(url, params) } -export const createApikey = ({ url, body }: { url: string; body: Record<string, any> }): Promise<CreateApiKeyResponse> => { +export const createApikey = ({ url, body }: { url: string, body: Record<string, any> }): Promise<CreateApiKeyResponse> => { return post<CreateApiKeyResponse>(url, body) } -export const validateOpenAIKey = ({ url, body }: { url: string; body: { token: string } }): Promise<ValidateOpenAIKeyResponse> => { +export const validateOpenAIKey = ({ url, body }: { url: string, body: { token: string } }): Promise<ValidateOpenAIKeyResponse> => { return post<ValidateOpenAIKeyResponse>(url, { body }) } -export const updateOpenAIKey = ({ url, body }: { url: string; body: { token: string } }): Promise<UpdateOpenAIKeyResponse> => { +export const updateOpenAIKey = ({ url, body }: { url: string, body: { token: string } }): Promise<UpdateOpenAIKeyResponse> => { return post<UpdateOpenAIKeyResponse>(url, { body }) } -export const generationIntroduction = ({ url, body }: { url: string; body: { prompt_template: string } }): Promise<GenerationIntroductionResponse> => { +export const generationIntroduction = ({ url, body }: { url: string, body: { prompt_template: string } }): Promise<GenerationIntroductionResponse> => { return post<GenerationIntroductionResponse>(url, { body }) } -export const fetchAppVoices = ({ appId, language }: { appId: string; language?: string }): Promise<AppVoicesListResponse> => { +export const fetchAppVoices = ({ appId, language }: { appId: string, language?: string }): Promise<AppVoicesListResponse> => { language = language || 'en-US' return get<AppVoicesListResponse>(`apps/${appId}/text-to-audio/voices?language=${language}`) } @@ -197,12 +197,12 @@ export const fetchTracingStatus = ({ appId }: { appId: string }): Promise<Tracin return get<TracingStatus>(`/apps/${appId}/trace`) } -export const updateTracingStatus = ({ appId, body }: { appId: string; body: Record<string, any> }): Promise<CommonResponse> => { +export const updateTracingStatus = ({ appId, body }: { appId: string, body: Record<string, any> }): Promise<CommonResponse> => { return post<CommonResponse>(`/apps/${appId}/trace`, { body }) } // Webhook Trigger -export const fetchWebhookUrl = ({ appId, nodeId }: { appId: string; nodeId: string }): Promise<WebhookTriggerResponse> => { +export const fetchWebhookUrl = ({ appId, nodeId }: { appId: string, nodeId: string }): Promise<WebhookTriggerResponse> => { return get<WebhookTriggerResponse>( `apps/${appId}/workflows/triggers/webhook`, { params: { node_id: nodeId } }, @@ -210,7 +210,7 @@ export const fetchWebhookUrl = ({ appId, nodeId }: { appId: string; nodeId: stri ) } -export const fetchTracingConfig = ({ appId, provider }: { appId: string; provider: TracingProvider }): Promise<TracingConfig & { has_not_configured: true }> => { +export const fetchTracingConfig = ({ appId, provider }: { appId: string, provider: TracingProvider }): Promise<TracingConfig & { has_not_configured: true }> => { return get<TracingConfig & { has_not_configured: true }>(`/apps/${appId}/trace-config`, { params: { tracing_provider: provider, @@ -218,14 +218,14 @@ export const fetchTracingConfig = ({ appId, provider }: { appId: string; provide }) } -export const addTracingConfig = ({ appId, body }: { appId: string; body: TracingConfig }): Promise<CommonResponse> => { +export const addTracingConfig = ({ appId, body }: { appId: string, body: TracingConfig }): Promise<CommonResponse> => { return post<CommonResponse>(`/apps/${appId}/trace-config`, { body }) } -export const updateTracingConfig = ({ appId, body }: { appId: string; body: TracingConfig }): Promise<CommonResponse> => { +export const updateTracingConfig = ({ appId, body }: { appId: string, body: TracingConfig }): Promise<CommonResponse> => { return patch<CommonResponse>(`/apps/${appId}/trace-config`, { body }) } -export const removeTracingConfig = ({ appId, provider }: { appId: string; provider: TracingProvider }): Promise<CommonResponse> => { +export const removeTracingConfig = ({ appId, provider }: { appId: string, provider: TracingProvider }): Promise<CommonResponse> => { return del<CommonResponse>(`/apps/${appId}/trace-config?tracing_provider=${provider}`) } diff --git a/web/service/base.ts b/web/service/base.ts index 91051dbf86..d9f3dba53a 100644 --- a/web/service/base.ts +++ b/web/service/base.ts @@ -1,9 +1,11 @@ -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 { refreshAccessTokenOrRelogin } from './refresh-token' -import Toast from '@/app/components/base/toast' -import { basePath } from '@/utils/var' +import type { FetchOptionType, ResponseError } from './fetch' import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/base/chat/chat/type' import type { VisionFile } from '@/types/app' +import type { + DataSourceNodeCompletedResponse, + DataSourceNodeErrorResponse, + DataSourceNodeProcessingResponse, +} from '@/types/pipeline' import type { AgentLogResponse, IterationFinishedResponse, @@ -21,16 +23,15 @@ import type { WorkflowFinishedResponse, WorkflowStartedResponse, } from '@/types/workflow' -import type { FetchOptionType, ResponseError } from './fetch' -import { ContentType, base, getBaseOptions } from './fetch' -import { asyncRunSafe } from '@/utils' -import type { - DataSourceNodeCompletedResponse, - DataSourceNodeErrorResponse, - DataSourceNodeProcessingResponse, -} from '@/types/pipeline' import Cookies from 'js-cookie' +import Toast from '@/app/components/base/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' +import { base, ContentType, getBaseOptions } from './fetch' +import { refreshAccessTokenOrRelogin } from './refresh-token' import { getWebAppPassport } from './webapp-auth' + const TIME_OUT = 100000 export type IOnDataMoreInfo = { @@ -464,7 +465,7 @@ export const ssePost = async ( if (!/^[23]\d{2}$/.test(String(res.status))) { if (res.status === 401) { if (isPublicAPI) { - res.json().then((data: { code?: string; message?: string }) => { + res.json().then((data: { code?: string, message?: string }) => { if (isPublicAPI) { if (data.code === 'web_app_access_denied') requiredWebSSOLogin(data.message, 403) @@ -532,7 +533,8 @@ export const ssePost = async ( onDataSourceNodeCompleted, onDataSourceNodeError, ) - }).catch((e) => { + }) + .catch((e) => { if (e.toString() !== 'AbortError: The user aborted a request.' && !e.toString().includes('TypeError: Cannot assign to read only property')) Toast.notify({ type: 'error', message: e }) onError?.(e) diff --git a/web/service/billing.ts b/web/service/billing.ts index 979a888582..f06c4f06c6 100644 --- a/web/service/billing.ts +++ b/web/service/billing.ts @@ -1,5 +1,5 @@ -import { get, put } from './base' import type { CurrentPlanInfoBackend, SubscriptionUrlsBackend } from '@/app/components/billing/type' +import { get, put } from './base' export const fetchCurrentPlanInfo = () => { return get<CurrentPlanInfoBackend>('/features') diff --git a/web/service/common.ts b/web/service/common.ts index 1793675bc5..5fc4850d5f 100644 --- a/web/service/common.ts +++ b/web/service/common.ts @@ -1,4 +1,16 @@ -import { del, get, patch, post, put } from './base' +import type { + DefaultModelResponse, + Model, + ModelItem, + ModelLoadBalancingConfig, + ModelParameterRule, + ModelProvider, + ModelTypeEnum, +} from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { + UpdateOpenAIKeyResponse, + ValidateOpenAIKeyResponse, +} from '@/models/app' import type { AccountIntegrate, ApiBasedExtension, @@ -7,9 +19,9 @@ import type { DataSourceNotion, FileUploadConfigResponse, ICurrentWorkspace, - IWorkspace, InitValidateStatusResponse, InvitationResponse, + IWorkspace, LangGeniusVersionResponse, Member, ModerateResponse, @@ -21,21 +33,9 @@ import type { SetupStatusResponse, UserProfileOriginResponse, } from '@/models/common' -import type { - UpdateOpenAIKeyResponse, - ValidateOpenAIKeyResponse, -} from '@/models/app' -import type { - DefaultModelResponse, - Model, - ModelItem, - ModelLoadBalancingConfig, - ModelParameterRule, - ModelProvider, - ModelTypeEnum, -} from '@/app/components/header/account-setting/model-provider-page/declarations' import type { RETRIEVE_METHOD } from '@/types/app' import type { SystemFeatures } from '@/types/feature' +import { del, get, patch, post, put } from './base' type LoginSuccess = { result: 'success' @@ -48,10 +48,10 @@ type LoginFail = { message: string } type LoginResponse = LoginSuccess | LoginFail -export const login = ({ url, body }: { url: string; body: Record<string, any> }): Promise<LoginResponse> => { +export const login = ({ url, body }: { url: string, body: Record<string, any> }): Promise<LoginResponse> => { return post<LoginResponse>(url, { body }) } -export const webAppLogin = ({ url, body }: { url: string; body: Record<string, any> }): Promise<LoginResponse> => { +export const webAppLogin = ({ url, body }: { url: string, body: Record<string, any> }): Promise<LoginResponse> => { return post<LoginResponse>(url, { body }, { isPublicAPI: true }) } @@ -71,50 +71,50 @@ export const fetchSetupStatus = (): Promise<SetupStatusResponse> => { return get<SetupStatusResponse>('/setup') } -export const fetchUserProfile = ({ url, params }: { url: string; params: Record<string, any> }): Promise<UserProfileOriginResponse> => { +export const fetchUserProfile = ({ url, params }: { url: string, params: Record<string, any> }): Promise<UserProfileOriginResponse> => { return get<UserProfileOriginResponse>(url, params, { needAllResponseContent: true }) } -export const updateUserProfile = ({ url, body }: { url: string; body: Record<string, any> }): Promise<CommonResponse> => { +export const updateUserProfile = ({ url, body }: { url: string, body: Record<string, any> }): Promise<CommonResponse> => { return post<CommonResponse>(url, { body }) } -export const fetchLangGeniusVersion = ({ url, params }: { url: string; params: Record<string, any> }): Promise<LangGeniusVersionResponse> => { +export const fetchLangGeniusVersion = ({ url, params }: { url: string, params: Record<string, any> }): Promise<LangGeniusVersionResponse> => { return get<LangGeniusVersionResponse>(url, { params }) } -export const oauth = ({ url, params }: { url: string; params: Record<string, any> }): Promise<OauthResponse> => { +export const oauth = ({ url, params }: { url: string, params: Record<string, any> }): Promise<OauthResponse> => { return get<OauthResponse>(url, { params }) } -export const oneMoreStep = ({ url, body }: { url: string; body: Record<string, any> }): Promise<CommonResponse> => { +export const oneMoreStep = ({ url, body }: { url: string, body: Record<string, any> }): Promise<CommonResponse> => { return post<CommonResponse>(url, { body }) } -export const fetchMembers = ({ url, params }: { url: string; params: Record<string, any> }): Promise<{ accounts: Member[] | null }> => { +export const fetchMembers = ({ url, params }: { url: string, params: Record<string, any> }): Promise<{ accounts: Member[] | null }> => { return get<{ accounts: Member[] | null }>(url, { params }) } -export const fetchProviders = ({ url, params }: { url: string; params: Record<string, any> }): Promise<Provider[] | null> => { +export const fetchProviders = ({ url, params }: { url: string, params: Record<string, any> }): Promise<Provider[] | null> => { return get<Provider[] | null>(url, { params }) } -export const validateProviderKey = ({ url, body }: { url: string; body: { token: string } }): Promise<ValidateOpenAIKeyResponse> => { +export const validateProviderKey = ({ url, body }: { url: string, body: { token: string } }): Promise<ValidateOpenAIKeyResponse> => { return post<ValidateOpenAIKeyResponse>(url, { body }) } -export const updateProviderAIKey = ({ url, body }: { url: string; body: { token: string | ProviderAzureToken | ProviderAnthropicToken } }): Promise<UpdateOpenAIKeyResponse> => { +export const updateProviderAIKey = ({ url, body }: { url: string, body: { token: string | ProviderAzureToken | ProviderAnthropicToken } }): Promise<UpdateOpenAIKeyResponse> => { return post<UpdateOpenAIKeyResponse>(url, { body }) } -export const fetchAccountIntegrates = ({ url, params }: { url: string; params: Record<string, any> }): Promise<{ data: AccountIntegrate[] | null }> => { +export const fetchAccountIntegrates = ({ url, params }: { url: string, params: Record<string, any> }): Promise<{ data: AccountIntegrate[] | null }> => { return get<{ data: AccountIntegrate[] | null }>(url, { params }) } -export const inviteMember = ({ url, body }: { url: string; body: Record<string, any> }): Promise<InvitationResponse> => { +export const inviteMember = ({ url, body }: { url: string, body: Record<string, any> }): Promise<InvitationResponse> => { return post<InvitationResponse>(url, { body }) } -export const updateMemberRole = ({ url, body }: { url: string; body: Record<string, any> }): Promise<CommonResponse> => { +export const updateMemberRole = ({ url, body }: { url: string, body: Record<string, any> }): Promise<CommonResponse> => { return put<CommonResponse>(url, { body }) } @@ -125,33 +125,33 @@ export const deleteMemberOrCancelInvitation = ({ url }: { url: string }): Promis export const sendOwnerEmail = (body: { language?: string }): Promise<CommonResponse & { data: string }> => post<CommonResponse & { data: string }>('/workspaces/current/members/send-owner-transfer-confirm-email', { body }) -export const verifyOwnerEmail = (body: { code: string; token: string }): Promise<CommonResponse & { is_valid: boolean; email: string; token: string }> => - post<CommonResponse & { is_valid: boolean; email: string; token: string }>('/workspaces/current/members/owner-transfer-check', { body }) +export const verifyOwnerEmail = (body: { code: string, token: string }): Promise<CommonResponse & { is_valid: boolean, email: string, token: string }> => + post<CommonResponse & { is_valid: boolean, email: string, token: string }>('/workspaces/current/members/owner-transfer-check', { body }) -export const ownershipTransfer = (memberID: string, body: { token: string }): Promise<CommonResponse & { is_valid: boolean; email: string; token: string }> => - post<CommonResponse & { is_valid: boolean; email: string; token: string }>(`/workspaces/current/members/${memberID}/owner-transfer`, { body }) +export const ownershipTransfer = (memberID: string, body: { token: string }): Promise<CommonResponse & { is_valid: boolean, email: string, token: string }> => + post<CommonResponse & { is_valid: boolean, email: string, token: string }>(`/workspaces/current/members/${memberID}/owner-transfer`, { body }) export const fetchFilePreview = ({ fileID }: { fileID: string }): Promise<{ content: string }> => { return get<{ content: string }>(`/files/${fileID}/preview`) } -export const fetchCurrentWorkspace = ({ url, params }: { url: string; params: Record<string, any> }): Promise<ICurrentWorkspace> => { +export const fetchCurrentWorkspace = ({ url, params }: { url: string, params: Record<string, any> }): Promise<ICurrentWorkspace> => { return post<ICurrentWorkspace>(url, { body: params }) } -export const updateCurrentWorkspace = ({ url, body }: { url: string; body: Record<string, any> }): Promise<ICurrentWorkspace> => { +export const updateCurrentWorkspace = ({ url, body }: { url: string, body: Record<string, any> }): Promise<ICurrentWorkspace> => { return post<ICurrentWorkspace>(url, { body }) } -export const fetchWorkspaces = ({ url, params }: { url: string; params: Record<string, any> }): Promise<{ workspaces: IWorkspace[] }> => { +export const fetchWorkspaces = ({ url, params }: { url: string, params: Record<string, any> }): Promise<{ workspaces: IWorkspace[] }> => { return get<{ workspaces: IWorkspace[] }>(url, { params }) } -export const switchWorkspace = ({ url, body }: { url: string; body: Record<string, any> }): Promise<CommonResponse & { new_tenant: IWorkspace }> => { +export const switchWorkspace = ({ url, body }: { url: string, body: Record<string, any> }): Promise<CommonResponse & { new_tenant: IWorkspace }> => { return post<CommonResponse & { new_tenant: IWorkspace }>(url, { body }) } -export const updateWorkspaceInfo = ({ url, body }: { url: string; body: Record<string, any> }): Promise<ICurrentWorkspace> => { +export const updateWorkspaceInfo = ({ url, body }: { url: string, body: Record<string, any> }): Promise<ICurrentWorkspace> => { return post<ICurrentWorkspace>(url, { body }) } @@ -171,18 +171,18 @@ export const fetchPluginProviders = (url: string): Promise<PluginProvider[] | nu return get<PluginProvider[] | null>(url) } -export const validatePluginProviderKey = ({ url, body }: { url: string; body: { credentials: any } }): Promise<ValidateOpenAIKeyResponse> => { +export const validatePluginProviderKey = ({ url, body }: { url: string, body: { credentials: any } }): Promise<ValidateOpenAIKeyResponse> => { return post<ValidateOpenAIKeyResponse>(url, { body }) } -export const updatePluginProviderAIKey = ({ url, body }: { url: string; body: { credentials: any } }): Promise<UpdateOpenAIKeyResponse> => { +export const updatePluginProviderAIKey = ({ url, body }: { url: string, body: { credentials: any } }): Promise<UpdateOpenAIKeyResponse> => { return post<UpdateOpenAIKeyResponse>(url, { body }) } -export const invitationCheck = ({ url, params }: { url: string; params: { workspace_id?: string; email?: string; token: string } }): Promise<CommonResponse & { is_valid: boolean; data: { workspace_name: string; email: string; workspace_id: string } }> => { - return get<CommonResponse & { is_valid: boolean; data: { workspace_name: string; email: string; workspace_id: string } }>(url, { params }) +export const invitationCheck = ({ url, params }: { url: string, params: { workspace_id?: string, email?: string, token: string } }): Promise<CommonResponse & { is_valid: boolean, data: { workspace_name: string, email: string, workspace_id: string } }> => { + return get<CommonResponse & { is_valid: boolean, data: { workspace_name: string, email: string, workspace_id: string } }>(url, { params }) } -export const activateMember = ({ url, body }: { url: string; body: any }): Promise<LoginResponse> => { +export const activateMember = ({ url, body }: { url: string, body: any }): Promise<LoginResponse> => { return post<LoginResponse>(url, { body }) } @@ -216,27 +216,27 @@ export const fetchModelList = (url: string): Promise<{ data: Model[] }> => { return get<{ data: Model[] }>(url) } -export const validateModelProvider = ({ url, body }: { url: string; body: any }): Promise<ValidateOpenAIKeyResponse> => { +export const validateModelProvider = ({ url, body }: { url: string, body: any }): Promise<ValidateOpenAIKeyResponse> => { return post<ValidateOpenAIKeyResponse>(url, { body }) } -export const validateModelLoadBalancingCredentials = ({ url, body }: { url: string; body: any }): Promise<ValidateOpenAIKeyResponse> => { +export const validateModelLoadBalancingCredentials = ({ url, body }: { url: string, body: any }): Promise<ValidateOpenAIKeyResponse> => { return post<ValidateOpenAIKeyResponse>(url, { body }) } -export const setModelProvider = ({ url, body }: { url: string; body: any }): Promise<CommonResponse> => { +export const setModelProvider = ({ url, body }: { url: string, body: any }): Promise<CommonResponse> => { return post<CommonResponse>(url, { body }) } -export const deleteModelProvider = ({ url, body }: { url: string; body?: any }): Promise<CommonResponse> => { +export const deleteModelProvider = ({ url, body }: { url: string, body?: any }): Promise<CommonResponse> => { return del<CommonResponse>(url, { body }) } -export const changeModelProviderPriority = ({ url, body }: { url: string; body: any }): Promise<CommonResponse> => { +export const changeModelProviderPriority = ({ url, body }: { url: string, body: any }): Promise<CommonResponse> => { return post<CommonResponse>(url, { body }) } -export const setModelProviderModel = ({ url, body }: { url: string; body: any }): Promise<CommonResponse> => { +export const setModelProviderModel = ({ url, body }: { url: string, body: any }): Promise<CommonResponse> => { return post<CommonResponse>(url, { body }) } @@ -252,7 +252,7 @@ export const fetchDefaultModal = (url: string): Promise<{ data: DefaultModelResp return get<{ data: DefaultModelResponse }>(url) } -export const updateDefaultModel = ({ url, body }: { url: string; body: any }): Promise<CommonResponse> => { +export const updateDefaultModel = ({ url, body }: { url: string, body: any }): Promise<CommonResponse> => { return post<CommonResponse>(url, { body }) } @@ -280,11 +280,11 @@ export const fetchApiBasedExtensionDetail = (url: string): Promise<ApiBasedExten return get<ApiBasedExtension>(url) } -export const addApiBasedExtension = ({ url, body }: { url: string; body: ApiBasedExtension }): Promise<ApiBasedExtension> => { +export const addApiBasedExtension = ({ url, body }: { url: string, body: ApiBasedExtension }): Promise<ApiBasedExtension> => { return post<ApiBasedExtension>(url, { body }) } -export const updateApiBasedExtension = ({ url, body }: { url: string; body: ApiBasedExtension }): Promise<ApiBasedExtension> => { +export const updateApiBasedExtension = ({ url, body }: { url: string, body: ApiBasedExtension }): Promise<ApiBasedExtension> => { return post<ApiBasedExtension>(url, { body }) } @@ -296,7 +296,7 @@ export const fetchCodeBasedExtensionList = (url: string): Promise<CodeBasedExten return get<CodeBasedExtension>(url) } -export const moderate = (url: string, body: { app_id: string; text: string }): Promise<ModerateResponse> => { +export const moderate = (url: string, body: { app_id: string, text: string }): Promise<ModerateResponse> => { return post<ModerateResponse>(url, { body }) } @@ -311,79 +311,79 @@ export const getSystemFeatures = (): Promise<SystemFeatures> => { return get<SystemFeatures>('/system-features') } -export const enableModel = (url: string, body: { model: string; model_type: ModelTypeEnum }): Promise<CommonResponse> => +export const enableModel = (url: string, body: { model: string, model_type: ModelTypeEnum }): Promise<CommonResponse> => patch<CommonResponse>(url, { body }) -export const disableModel = (url: string, body: { model: string; model_type: ModelTypeEnum }): Promise<CommonResponse> => +export const disableModel = (url: string, body: { model: string, model_type: ModelTypeEnum }): Promise<CommonResponse> => patch<CommonResponse>(url, { body }) -export const sendForgotPasswordEmail = ({ url, body }: { url: string; body: { email: string } }): Promise<CommonResponse & { data: string }> => +export const sendForgotPasswordEmail = ({ url, body }: { url: string, body: { email: string } }): Promise<CommonResponse & { data: string }> => post<CommonResponse & { data: string }>(url, { body }) -export const verifyForgotPasswordToken = ({ url, body }: { url: string; body: { token: string } }): Promise<CommonResponse & { is_valid: boolean; email: string }> => { - return post<CommonResponse & { is_valid: boolean; email: string }>(url, { body }) +export const verifyForgotPasswordToken = ({ url, body }: { url: string, body: { token: string } }): Promise<CommonResponse & { is_valid: boolean, email: string }> => { + return post<CommonResponse & { is_valid: boolean, email: string }>(url, { body }) } -export const changePasswordWithToken = ({ url, body }: { url: string; body: { token: string; new_password: string; password_confirm: string } }): Promise<CommonResponse> => +export const changePasswordWithToken = ({ url, body }: { url: string, body: { token: string, new_password: string, password_confirm: string } }): Promise<CommonResponse> => post<CommonResponse>(url, { body }) -export const sendWebAppForgotPasswordEmail = ({ url, body }: { url: string; body: { email: string } }): Promise<CommonResponse & { data: string }> => +export const sendWebAppForgotPasswordEmail = ({ url, body }: { url: string, body: { email: string } }): Promise<CommonResponse & { data: string }> => post<CommonResponse & { data: string }>(url, { body }, { isPublicAPI: true }) -export const verifyWebAppForgotPasswordToken = ({ url, body }: { url: string; body: { token: string } }): Promise<CommonResponse & { is_valid: boolean; email: string }> => { - return post<CommonResponse & { is_valid: boolean; email: string }>(url, { body }, { isPublicAPI: true }) +export const verifyWebAppForgotPasswordToken = ({ url, body }: { url: string, body: { token: string } }): Promise<CommonResponse & { is_valid: boolean, email: string }> => { + return post<CommonResponse & { is_valid: boolean, email: string }>(url, { body }, { isPublicAPI: true }) } -export const changeWebAppPasswordWithToken = ({ url, body }: { url: string; body: { token: string; new_password: string; password_confirm: string } }): Promise<CommonResponse> => +export const changeWebAppPasswordWithToken = ({ url, body }: { url: string, body: { token: string, new_password: string, password_confirm: string } }): Promise<CommonResponse> => post<CommonResponse>(url, { body }, { isPublicAPI: true }) -export const uploadRemoteFileInfo = (url: string, isPublic?: boolean, silent?: boolean): Promise<{ id: string; name: string; size: number; mime_type: string; url: string }> => { - return post<{ id: string; name: string; size: number; mime_type: string; url: string }>('/remote-files/upload', { body: { url } }, { isPublicAPI: isPublic, silent }) +export const uploadRemoteFileInfo = (url: string, isPublic?: boolean, silent?: boolean): Promise<{ id: string, name: string, size: number, mime_type: string, url: string }> => { + return post<{ id: string, name: string, size: number, mime_type: string, url: string }>('/remote-files/upload', { body: { url } }, { isPublicAPI: isPublic, silent }) } export const sendEMailLoginCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string }> => post<CommonResponse & { data: string }>('/email-code-login', { body: { email, language } }) -export const emailLoginWithCode = (data: { email: string; code: string; token: string; language: string }): Promise<LoginResponse> => +export const emailLoginWithCode = (data: { email: string, code: string, token: string, language: string }): Promise<LoginResponse> => post<LoginResponse>('/email-code-login/validity', { body: data }) -export const sendResetPasswordCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string; message?: string; code?: string }> => - post<CommonResponse & { data: string; message?: string; code?: string }>('/forgot-password', { body: { email, language } }) +export const sendResetPasswordCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string, message?: string, code?: string }> => + post<CommonResponse & { data: string, message?: string, code?: string }>('/forgot-password', { body: { email, language } }) -export const verifyResetPasswordCode = (body: { email: string; code: string; token: string }): Promise<CommonResponse & { is_valid: boolean; token: string }> => - post<CommonResponse & { is_valid: boolean; token: string }>('/forgot-password/validity', { body }) +export const verifyResetPasswordCode = (body: { email: string, code: string, token: string }): Promise<CommonResponse & { is_valid: boolean, token: string }> => + post<CommonResponse & { is_valid: boolean, token: string }>('/forgot-password/validity', { body }) export const sendWebAppEMailLoginCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string }> => post<CommonResponse & { data: string }>('/email-code-login', { body: { email, language } }, { isPublicAPI: true }) -export const webAppEmailLoginWithCode = (data: { email: string; code: string; token: string }): Promise<LoginResponse> => +export const webAppEmailLoginWithCode = (data: { email: string, code: string, token: string }): Promise<LoginResponse> => post<LoginResponse>('/email-code-login/validity', { body: data }, { isPublicAPI: true }) -export const sendWebAppResetPasswordCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string; message?: string; code?: string }> => - post<CommonResponse & { data: string; message?: string; code?: string }>('/forgot-password', { body: { email, language } }, { isPublicAPI: true }) +export const sendWebAppResetPasswordCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string, message?: string, code?: string }> => + post<CommonResponse & { data: string, message?: string, code?: string }>('/forgot-password', { body: { email, language } }, { isPublicAPI: true }) -export const verifyWebAppResetPasswordCode = (body: { email: string; code: string; token: string }): Promise<CommonResponse & { is_valid: boolean; token: string }> => - post<CommonResponse & { is_valid: boolean; token: string }>('/forgot-password/validity', { body }, { isPublicAPI: true }) +export const verifyWebAppResetPasswordCode = (body: { email: string, code: string, token: string }): Promise<CommonResponse & { is_valid: boolean, token: string }> => + post<CommonResponse & { is_valid: boolean, token: string }>('/forgot-password/validity', { body }, { isPublicAPI: true }) export const sendDeleteAccountCode = (): Promise<CommonResponse & { data: string }> => get<CommonResponse & { data: string }>('/account/delete/verify') -export const verifyDeleteAccountCode = (body: { code: string; token: string }): Promise<CommonResponse & { is_valid: boolean }> => +export const verifyDeleteAccountCode = (body: { code: string, token: string }): Promise<CommonResponse & { is_valid: boolean }> => post<CommonResponse & { is_valid: boolean }>('/account/delete', { body }) -export const submitDeleteAccountFeedback = (body: { feedback: string; email: string }): Promise<CommonResponse> => +export const submitDeleteAccountFeedback = (body: { feedback: string, email: string }): Promise<CommonResponse> => post<CommonResponse>('/account/delete/feedback', { body }) export const getDocDownloadUrl = (doc_name: string): Promise<{ url: string }> => get<{ url: string }>('/compliance/download', { params: { doc_name } }, { silent: true }) -export const sendVerifyCode = (body: { email: string; phase: string; token?: string }): Promise<CommonResponse & { data: string }> => +export const sendVerifyCode = (body: { email: string, phase: string, token?: string }): Promise<CommonResponse & { data: string }> => post<CommonResponse & { data: string }>('/account/change-email', { body }) -export const verifyEmail = (body: { email: string; code: string; token: string }): Promise<CommonResponse & { is_valid: boolean; email: string; token: string }> => - post<CommonResponse & { is_valid: boolean; email: string; token: string }>('/account/change-email/validity', { body }) +export const verifyEmail = (body: { email: string, code: string, token: string }): Promise<CommonResponse & { is_valid: boolean, email: string, token: string }> => + post<CommonResponse & { is_valid: boolean, email: string, token: string }>('/account/change-email/validity', { body }) -export const resetEmail = (body: { new_email: string; token: string }): Promise<CommonResponse> => +export const resetEmail = (body: { new_email: string, token: string }): Promise<CommonResponse> => post<CommonResponse>('/account/change-email/reset', { body }) export const checkEmailExisted = (body: { email: string }): Promise<CommonResponse> => diff --git a/web/service/datasets.ts b/web/service/datasets.ts index 8791a61b7c..eb22af8446 100644 --- a/web/service/datasets.ts +++ b/web/service/datasets.ts @@ -1,7 +1,13 @@ -import qs from 'qs' -import { del, get, patch, post, put } from './base' +import type { CreateExternalAPIReq } from '@/app/components/datasets/external-api/declarations' +import type { CreateKnowledgeBaseReq } from '@/app/components/datasets/external-knowledge-base/create/declarations' +import type { + ApiKeysListResponse, + CreateApiKeyResponse, +} from '@/models/app' +import type { CommonResponse, DataSourceNotionWorkspace } from '@/models/common' import type { CreateDocumentReq, + createDocumentResponse, DataSet, DataSetListResponse, ErrorDocsResponse, @@ -21,17 +27,11 @@ import type { IndexingStatusResponse, ProcessRuleResponse, RelatedAppResponse, - createDocumentResponse, } from '@/models/datasets' -import type { CreateKnowledgeBaseReq } from '@/app/components/datasets/external-knowledge-base/create/declarations' -import type { CreateExternalAPIReq } from '@/app/components/datasets/external-api/declarations' -import type { CommonResponse, DataSourceNotionWorkspace } from '@/models/common' -import { DataSourceProvider } from '@/models/common' -import type { - ApiKeysListResponse, - CreateApiKeyResponse, -} from '@/models/app' import type { RetrievalConfig } from '@/types/app' +import qs from 'qs' +import { DataSourceProvider } from '@/models/common' +import { del, get, patch, post, put } from './base' // apis for documents in a dataset @@ -58,9 +58,7 @@ export const updateDatasetSetting = ({ body, }: { datasetId: string - body: Partial<Pick<DataSet, - 'name' | 'description' | 'permission' | 'partial_member_list' | 'indexing_technique' | 'retrieval_model' | 'embedding_model' | 'embedding_model_provider' | 'icon_info' | 'doc_form' - >> + body: Partial<Pick<DataSet, 'name' | 'description' | 'permission' | 'partial_member_list' | 'indexing_technique' | 'retrieval_model' | 'embedding_model' | 'embedding_model_provider' | 'icon_info' | 'doc_form'>> }): Promise<DataSet> => { return patch<DataSet>(`/datasets/${datasetId}`, { body }) } @@ -96,7 +94,7 @@ export const fetchExternalAPI = ({ apiTemplateId }: { apiTemplateId: string }): return get<ExternalAPIItem>(`/datasets/external-knowledge-api/${apiTemplateId}`) } -export const updateExternalAPI = ({ apiTemplateId, body }: { apiTemplateId: string; body: ExternalAPIItem }): Promise<ExternalAPIItem> => { +export const updateExternalAPI = ({ apiTemplateId, body }: { apiTemplateId: string, body: ExternalAPIItem }): Promise<ExternalAPIItem> => { return patch<ExternalAPIItem>(`/datasets/external-knowledge-api/${apiTemplateId}`, { body }) } @@ -127,7 +125,7 @@ export const createFirstDocument = ({ body }: { body: CreateDocumentReq }): Prom return post<createDocumentResponse>('/datasets/init', { body }) } -export const createDocument = ({ datasetId, body }: { datasetId: string; body: CreateDocumentReq }): Promise<createDocumentResponse> => { +export const createDocument = ({ datasetId, body }: { datasetId: string, body: CreateDocumentReq }): Promise<createDocumentResponse> => { return post<createDocumentResponse>(`/datasets/${datasetId}/documents`, { body }) } @@ -160,24 +158,24 @@ export const resumeDocIndexing = ({ datasetId, documentId }: CommonDocReq): Prom return patch<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/processing/resume`) } -export const preImportNotionPages = ({ url, datasetId }: { url: string; datasetId?: string }): Promise<{ notion_info: DataSourceNotionWorkspace[] }> => { +export const preImportNotionPages = ({ url, datasetId }: { url: string, datasetId?: string }): Promise<{ notion_info: DataSourceNotionWorkspace[] }> => { return get<{ notion_info: DataSourceNotionWorkspace[] }>(url, { params: { dataset_id: datasetId } }) } -export const modifyDocMetadata = ({ datasetId, documentId, body }: CommonDocReq & { body: { doc_type: string; doc_metadata: Record<string, any> } }): Promise<CommonResponse> => { +export const modifyDocMetadata = ({ datasetId, documentId, body }: CommonDocReq & { body: { doc_type: string, doc_metadata: Record<string, any> } }): Promise<CommonResponse> => { return put<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/metadata`, { body }) } // hit testing -export const hitTesting = ({ datasetId, queryText, retrieval_model }: { datasetId: string; queryText: string; retrieval_model: RetrievalConfig }): Promise<HitTestingResponse> => { +export const hitTesting = ({ datasetId, queryText, retrieval_model }: { datasetId: string, queryText: string, retrieval_model: RetrievalConfig }): Promise<HitTestingResponse> => { return post<HitTestingResponse>(`/datasets/${datasetId}/hit-testing`, { body: { query: queryText, retrieval_model } }) } -export const externalKnowledgeBaseHitTesting = ({ datasetId, query, external_retrieval_model }: { datasetId: string; query: string; external_retrieval_model: { top_k: number; score_threshold: number; score_threshold_enabled: boolean } }): Promise<ExternalKnowledgeBaseHitTestingResponse> => { +export const externalKnowledgeBaseHitTesting = ({ datasetId, query, external_retrieval_model }: { datasetId: string, query: string, external_retrieval_model: { top_k: number, score_threshold: number, score_threshold_enabled: boolean } }): Promise<ExternalKnowledgeBaseHitTestingResponse> => { return post<ExternalKnowledgeBaseHitTestingResponse>(`/datasets/${datasetId}/external-hit-testing`, { body: { query, external_retrieval_model } }) } -export const fetchTestingRecords = ({ datasetId, params }: { datasetId: string; params: { page: number; limit: number } }): Promise<HitTestingRecordsResponse> => { +export const fetchTestingRecords = ({ datasetId, params }: { datasetId: string, params: { page: number, limit: number } }): Promise<HitTestingRecordsResponse> => { return get<HitTestingRecordsResponse>(`/datasets/${datasetId}/queries`, { params }) } @@ -185,7 +183,7 @@ export const fetchFileIndexingEstimate = (body: IndexingEstimateParams): Promise return post<FileIndexingEstimateResponse>('/datasets/indexing-estimate', { body }) } -export const fetchNotionPagePreview = ({ pageID, pageType, credentialID }: { pageID: string; pageType: string; credentialID: string }): Promise<{ content: string }> => { +export const fetchNotionPagePreview = ({ pageID, pageType, credentialID }: { pageID: string, pageType: string, credentialID: string }): Promise<{ content: string }> => { return get<{ content: string }>(`notion/pages/${pageID}/${pageType}/preview`, { params: { credential_id: credentialID, @@ -193,15 +191,15 @@ export const fetchNotionPagePreview = ({ pageID, pageType, credentialID }: { pag }) } -export const fetchApiKeysList = ({ url, params }: { url: string; params: Record<string, any> }): Promise<ApiKeysListResponse> => { +export const fetchApiKeysList = ({ url, params }: { url: string, params: Record<string, any> }): Promise<ApiKeysListResponse> => { return get<ApiKeysListResponse>(url, params) } -export const delApikey = ({ url, params }: { url: string; params: Record<string, any> }): Promise<CommonResponse> => { +export const delApikey = ({ url, params }: { url: string, params: Record<string, any> }): Promise<CommonResponse> => { return del<CommonResponse>(url, params) } -export const createApikey = ({ url, body }: { url: string; body: Record<string, any> }): Promise<CreateApiKeyResponse> => { +export const createApikey = ({ url, body }: { url: string, body: Record<string, any> }): Promise<CreateApiKeyResponse> => { return post<CreateApiKeyResponse>(url, body) } @@ -286,6 +284,6 @@ export const getErrorDocs = ({ datasetId }: { datasetId: string }): Promise<Erro return get<ErrorDocsResponse>(`/datasets/${datasetId}/error-docs`) } -export const retryErrorDocs = ({ datasetId, document_ids }: { datasetId: string; document_ids: string[] }): Promise<CommonResponse> => { +export const retryErrorDocs = ({ datasetId, document_ids }: { datasetId: string, document_ids: string[] }): Promise<CommonResponse> => { return post<CommonResponse>(`/datasets/${datasetId}/retry`, { body: { document_ids } }) } diff --git a/web/service/debug.ts b/web/service/debug.ts index 3f3abda2d2..850f3dfc24 100644 --- a/web/service/debug.ts +++ b/web/service/debug.ts @@ -1,8 +1,8 @@ -import { get, post, ssePost } from './base' import type { IOnCompleted, IOnData, IOnError, IOnFile, IOnMessageEnd, IOnMessageReplace, IOnThought } from './base' +import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { ChatPromptConfig, CompletionPromptConfig } from '@/models/debug' import type { AppModeEnum, ModelModeType } from '@/types/app' -import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { get, post, ssePost } from './base' export type BasicAppFirstRes = { prompt: string @@ -106,8 +106,8 @@ export const fetchPromptTemplate = ({ mode, modelName, hasSetDataSet, -}: { appMode: AppModeEnum; mode: ModelModeType; modelName: string; hasSetDataSet: boolean }) => { - return get<Promise<{ chat_prompt_config: ChatPromptConfig; completion_prompt_config: CompletionPromptConfig; stop: [] }>>('/app/prompt-templates', { +}: { appMode: AppModeEnum, mode: ModelModeType, modelName: string, hasSetDataSet: boolean }) => { + return get<Promise<{ chat_prompt_config: ChatPromptConfig, completion_prompt_config: CompletionPromptConfig, stop: [] }>>('/app/prompt-templates', { params: { app_mode: appMode, model_mode: mode, @@ -120,6 +120,6 @@ export const fetchPromptTemplate = ({ export const fetchTextGenerationMessage = ({ appId, messageId, -}: { appId: string; messageId: string }) => { +}: { appId: string, messageId: string }) => { return get<Promise<any>>(`/apps/${appId}/messages/${messageId}`) } diff --git a/web/service/demo/index.tsx b/web/service/demo/index.tsx index b0b76bfcff..d538d6fda2 100644 --- a/web/service/demo/index.tsx +++ b/web/service/demo/index.tsx @@ -1,10 +1,10 @@ 'use client' import type { FC } from 'react' -import React from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import { createApp, updateAppApiStatus, updateAppModelConfig, updateAppRateLimit, updateAppSiteAccessToken, updateAppSiteConfig, updateAppSiteStatus } from '../apps' +import React from 'react' import Loading from '@/app/components/base/loading' import { AppModeEnum } from '@/types/app' +import { createApp, updateAppApiStatus, updateAppModelConfig, updateAppRateLimit, updateAppSiteAccessToken, updateAppSiteConfig, updateAppSiteStatus } from '../apps' import { useAppDailyConversations, useAppDailyEndUsers, @@ -84,12 +84,16 @@ const Service: FC = () => { return ( <div> - <div className='flex flex-col gap-3'> + <div className="flex flex-col gap-3"> <div> <div>1.App list</div> <div> {appList.data.map(item => ( - <div key={item.id}>{item.id} {item.name}</div> + <div key={item.id}> + {item.id} + {' '} + {item.name} + </div> ))} </div> </div> diff --git a/web/service/explore.ts b/web/service/explore.ts index 6a440d7f5d..70d5de37f2 100644 --- a/web/service/explore.ts +++ b/web/service/explore.ts @@ -1,6 +1,6 @@ -import { del, get, patch, post } from './base' -import type { App, AppCategory } from '@/models/explore' import type { AccessMode } from '@/models/access-control' +import type { App, AppCategory } from '@/models/explore' +import { del, get, patch, post } from './base' export const fetchAppList = () => { return get<{ diff --git a/web/service/fetch.ts b/web/service/fetch.ts index 030549bdab..d0af932d73 100644 --- a/web/service/fetch.ts +++ b/web/service/fetch.ts @@ -1,9 +1,9 @@ import type { AfterResponseHook, BeforeErrorHook, BeforeRequestHook, Hooks } from 'ky' -import ky from 'ky' import type { IOtherOptions } from './base' +import Cookies from 'js-cookie' +import ky from 'ky' import Toast from '@/app/components/base/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 Cookies from 'js-cookie' import { getWebAppAccessToken, getWebAppPassport } from './webapp-auth' const TIME_OUT = 100000 @@ -24,7 +24,8 @@ export type FetchOptionType = Omit<RequestInit, 'body'> & { } const afterResponse204: AfterResponseHook = async (_request, _options, response) => { - if (response.status === 204) return Response.json({ result: 'success' }) + if (response.status === 204) + return Response.json({ result: 'success' }) } export type ResponseError = { @@ -209,8 +210,9 @@ async function base<T>(url: string, options: FetchOptionType = {}, otherOptions: if ( contentType && [ContentType.download, ContentType.audio, ContentType.downloadZip].includes(contentType) - ) + ) { return await res.blob() as T + } return await res.json() as T } diff --git a/web/service/knowledge/use-create-dataset.ts b/web/service/knowledge/use-create-dataset.ts index 2530188c7e..eb656c2994 100644 --- a/web/service/knowledge/use-create-dataset.ts +++ b/web/service/knowledge/use-create-dataset.ts @@ -1,8 +1,6 @@ -import groupBy from 'lodash-es/groupBy' import type { MutationOptions } from '@tanstack/react-query' -import { useMutation } from '@tanstack/react-query' -import { createDocument, createFirstDocument, fetchDefaultProcessRule, fetchFileIndexingEstimate } from '../datasets' import type { IndexingType } from '@/app/components/datasets/create/step-two' +import type { DataSourceProvider, NotionPage } from '@/models/common' import type { ChunkingMode, CrawlOptions, @@ -10,6 +8,7 @@ import type { CreateDatasetReq, CreateDatasetResponse, CreateDocumentReq, + createDocumentResponse, CustomFile, DataSourceType, FileIndexingEstimateResponse, @@ -17,10 +16,11 @@ import type { NotionInfo, ProcessRule, ProcessRuleResponse, - createDocumentResponse, } from '@/models/datasets' -import type { DataSourceProvider, NotionPage } from '@/models/common' +import { useMutation } from '@tanstack/react-query' +import groupBy from 'lodash-es/groupBy' import { post } from '../base' +import { createDocument, createFirstDocument, fetchDefaultProcessRule, fetchFileIndexingEstimate } from '../datasets' const NAME_SPACE = 'knowledge/create-dataset' diff --git a/web/service/knowledge/use-dataset.ts b/web/service/knowledge/use-dataset.ts index 2b0c78b249..87fc1f2aec 100644 --- a/web/service/knowledge/use-dataset.ts +++ b/web/service/knowledge/use-dataset.ts @@ -1,16 +1,10 @@ import type { MutationOptions } from '@tanstack/react-query' -import { - keepPreviousData, - useInfiniteQuery, - useMutation, - useQuery, - useQueryClient, -} from '@tanstack/react-query' -import qs from 'qs' +import type { ApiKeysListResponse } from '@/models/app' +import type { CommonResponse } from '@/models/common' import type { DataSet, - DataSetListResponse, DatasetListRequest, + DataSetListResponse, ErrorDocsResponse, ExternalAPIListResponse, FetchDatasetsParams, @@ -20,10 +14,16 @@ import type { ProcessRuleResponse, RelatedAppResponse, } from '@/models/datasets' -import type { ApiKeysListResponse } from '@/models/app' +import { + keepPreviousData, + useInfiniteQuery, + useMutation, + useQuery, + useQueryClient, +} from '@tanstack/react-query' +import qs from 'qs' import { get, post } from '../base' import { useInvalid } from '../use-base' -import type { CommonResponse } from '@/models/common' const NAME_SPACE = 'dataset' @@ -199,7 +199,7 @@ export const useInvalidateExternalKnowledgeApiList = () => { export const useDatasetTestingRecords = ( datasetId?: string, - params?: { page: number; limit: number }, + params?: { page: number, limit: number }, ) => { return useQuery<HitTestingRecordsResponse>({ queryKey: [NAME_SPACE, 'testing-records', datasetId, params], diff --git a/web/service/knowledge/use-document.ts b/web/service/knowledge/use-document.ts index c3321b7a76..476f3534bc 100644 --- a/web/service/knowledge/use-document.ts +++ b/web/service/knowledge/use-document.ts @@ -1,15 +1,15 @@ +import type { MetadataType, SortType } from '../datasets' +import type { CommonResponse } from '@/models/common' +import type { DocumentDetailResponse, DocumentListResponse, UpdateDocumentBatchParams } from '@/models/datasets' import { useMutation, useQuery, } from '@tanstack/react-query' -import { del, get, patch } from '../base' -import { useInvalid } from '../use-base' -import type { MetadataType, SortType } from '../datasets' -import { pauseDocIndexing, resumeDocIndexing } from '../datasets' -import type { DocumentDetailResponse, DocumentListResponse, UpdateDocumentBatchParams } from '@/models/datasets' -import { DocumentActionType } from '@/models/datasets' -import type { CommonResponse } from '@/models/common' import { normalizeStatusForQuery } from '@/app/components/datasets/documents/status-filter' +import { DocumentActionType } from '@/models/datasets' +import { del, get, patch } from '../base' +import { pauseDocIndexing, resumeDocIndexing } from '../datasets' +import { useInvalid } from '../use-base' const NAME_SPACE = 'knowledge/document' @@ -22,7 +22,7 @@ export const useDocumentList = (payload: { limit: number sort?: SortType status?: string - }, + } refetchInterval?: number | false }) => { const { query, datasetId, refetchInterval } = payload diff --git a/web/service/knowledge/use-hit-testing.ts b/web/service/knowledge/use-hit-testing.ts index dfa030a01f..75c111483e 100644 --- a/web/service/knowledge/use-hit-testing.ts +++ b/web/service/knowledge/use-hit-testing.ts @@ -1,5 +1,3 @@ -import { useMutation, useQuery } from '@tanstack/react-query' -import { useInvalid } from '../use-base' import type { ExternalKnowledgeBaseHitTestingRequest, ExternalKnowledgeBaseHitTestingResponse, @@ -8,7 +6,9 @@ import type { HitTestingRequest, HitTestingResponse, } from '@/models/datasets' +import { useMutation, useQuery } from '@tanstack/react-query' import { get, post } from '../base' +import { useInvalid } from '../use-base' const NAME_SPACE = 'hit-testing' diff --git a/web/service/knowledge/use-import.ts b/web/service/knowledge/use-import.ts index 07579a065e..0fc7bc9dfa 100644 --- a/web/service/knowledge/use-import.ts +++ b/web/service/knowledge/use-import.ts @@ -1,6 +1,6 @@ +import type { DataSourceNotionWorkspace } from '@/models/common' import { useQuery, useQueryClient } from '@tanstack/react-query' import { get } from '../base' -import type { DataSourceNotionWorkspace } from '@/models/common' type PreImportNotionPagesParams = { datasetId: string diff --git a/web/service/knowledge/use-metadata.spec.tsx b/web/service/knowledge/use-metadata.spec.tsx index 11b68168e1..0ab4825482 100644 --- a/web/service/knowledge/use-metadata.spec.tsx +++ b/web/service/knowledge/use-metadata.spec.tsx @@ -1,6 +1,6 @@ -import { DataType } from '@/app/components/datasets/metadata/types' -import { act, renderHook } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { act, renderHook } from '@testing-library/react' +import { DataType } from '@/app/components/datasets/metadata/types' import { useBatchUpdateDocMetadata } from '@/service/knowledge/use-metadata' import { useDocumentListKey } from './use-document' diff --git a/web/service/knowledge/use-metadata.ts b/web/service/knowledge/use-metadata.ts index eb85142d9f..50b2c47ae3 100644 --- a/web/service/knowledge/use-metadata.ts +++ b/web/service/knowledge/use-metadata.ts @@ -1,9 +1,9 @@ import type { BuiltInMetadataItem, MetadataBatchEditToServer, MetadataItemWithValueLength } from '@/app/components/datasets/metadata/types' -import { del, get, patch, post } from '../base' -import { useDocumentListKey, useInvalidDocumentList } from './use-document' -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import { useInvalid } from '../use-base' import type { DocumentDetailResponse } from '@/models/datasets' +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import { del, get, patch, post } from '../base' +import { useInvalid } from '../use-base' +import { useDocumentListKey, useInvalidDocumentList } from './use-document' const NAME_SPACE = 'dataset-metadata' diff --git a/web/service/knowledge/use-segment.ts b/web/service/knowledge/use-segment.ts index 8b3e939e73..1d0ce4b774 100644 --- a/web/service/knowledge/use-segment.ts +++ b/web/service/knowledge/use-segment.ts @@ -1,5 +1,3 @@ -import { useMutation, useQuery } from '@tanstack/react-query' -import { del, get, patch, post } from '../base' import type { CommonResponse } from '@/models/common' import type { BatchImportResponse, @@ -7,9 +5,11 @@ import type { ChildSegmentsResponse, ChunkingMode, SegmentDetailModel, - SegmentUpdater, SegmentsResponse, + SegmentUpdater, } from '@/models/datasets' +import { useMutation, useQuery } from '@tanstack/react-query' +import { del, get, patch, post } from '../base' const NAME_SPACE = 'segment' @@ -45,9 +45,9 @@ export const useSegmentList = ( export const useUpdateSegment = () => { return useMutation({ mutationKey: [NAME_SPACE, 'update'], - mutationFn: (payload: { datasetId: string; documentId: string; segmentId: string; body: SegmentUpdater }) => { + mutationFn: (payload: { datasetId: string, documentId: string, segmentId: string, body: SegmentUpdater }) => { const { datasetId, documentId, segmentId, body } = payload - return patch<{ data: SegmentDetailModel; doc_form: ChunkingMode }>(`/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}`, { body }) + return patch<{ data: SegmentDetailModel, doc_form: ChunkingMode }>(`/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}`, { body }) }, }) } @@ -55,9 +55,9 @@ export const useUpdateSegment = () => { export const useAddSegment = () => { return useMutation({ mutationKey: [NAME_SPACE, 'add'], - mutationFn: (payload: { datasetId: string; documentId: string; body: SegmentUpdater }) => { + mutationFn: (payload: { datasetId: string, documentId: string, body: SegmentUpdater }) => { const { datasetId, documentId, body } = payload - return post<{ data: SegmentDetailModel; doc_form: ChunkingMode }>(`/datasets/${datasetId}/documents/${documentId}/segment`, { body }) + return post<{ data: SegmentDetailModel, doc_form: ChunkingMode }>(`/datasets/${datasetId}/documents/${documentId}/segment`, { body }) }, }) } @@ -65,7 +65,7 @@ export const useAddSegment = () => { export const useEnableSegment = () => { return useMutation({ mutationKey: [NAME_SPACE, 'enable'], - mutationFn: (payload: { datasetId: string; documentId: string; segmentIds: string[] }) => { + mutationFn: (payload: { datasetId: string, documentId: string, segmentIds: string[] }) => { const { datasetId, documentId, segmentIds } = payload const query = segmentIds.map(id => `segment_id=${id}`).join('&') return patch<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/segment/enable?${query}`) @@ -76,7 +76,7 @@ export const useEnableSegment = () => { export const useDisableSegment = () => { return useMutation({ mutationKey: [NAME_SPACE, 'disable'], - mutationFn: (payload: { datasetId: string; documentId: string; segmentIds: string[] }) => { + mutationFn: (payload: { datasetId: string, documentId: string, segmentIds: string[] }) => { const { datasetId, documentId, segmentIds } = payload const query = segmentIds.map(id => `segment_id=${id}`).join('&') return patch<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/segment/disable?${query}`) @@ -87,7 +87,7 @@ export const useDisableSegment = () => { export const useDeleteSegment = () => { return useMutation({ mutationKey: [NAME_SPACE, 'delete'], - mutationFn: (payload: { datasetId: string; documentId: string; segmentIds: string[] }) => { + mutationFn: (payload: { datasetId: string, documentId: string, segmentIds: string[] }) => { const { datasetId, documentId, segmentIds } = payload const query = segmentIds.map(id => `segment_id=${id}`).join('&') return del<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/segments?${query}`) @@ -124,7 +124,7 @@ export const useChildSegmentList = ( export const useDeleteChildSegment = () => { return useMutation({ mutationKey: [NAME_SPACE, 'childChunk', 'delete'], - mutationFn: (payload: { datasetId: string; documentId: string; segmentId: string; childChunkId: string }) => { + mutationFn: (payload: { datasetId: string, documentId: string, segmentId: string, childChunkId: string }) => { const { datasetId, documentId, segmentId, childChunkId } = payload return del<CommonResponse>(`/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/child_chunks/${childChunkId}`) }, @@ -134,7 +134,7 @@ export const useDeleteChildSegment = () => { export const useAddChildSegment = () => { return useMutation({ mutationKey: [NAME_SPACE, 'childChunk', 'add'], - mutationFn: (payload: { datasetId: string; documentId: string; segmentId: string; body: { content: string } }) => { + mutationFn: (payload: { datasetId: string, documentId: string, segmentId: string, body: { content: string } }) => { const { datasetId, documentId, segmentId, body } = payload return post<{ data: ChildChunkDetail }>(`/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/child_chunks`, { body }) }, @@ -144,7 +144,7 @@ export const useAddChildSegment = () => { export const useUpdateChildSegment = () => { return useMutation({ mutationKey: [NAME_SPACE, 'childChunk', 'update'], - mutationFn: (payload: { datasetId: string; documentId: string; segmentId: string; childChunkId: string; body: { content: string } }) => { + mutationFn: (payload: { datasetId: string, documentId: string, segmentId: string, childChunkId: string, body: { content: string } }) => { const { datasetId, documentId, segmentId, childChunkId, body } = payload return patch<{ data: ChildChunkDetail }>(`/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/child_chunks/${childChunkId}`, { body }) }, @@ -154,7 +154,7 @@ export const useUpdateChildSegment = () => { export const useSegmentBatchImport = () => { return useMutation({ mutationKey: [NAME_SPACE, 'batchImport'], - mutationFn: (payload: { url: string; body: { upload_file_id: string } }) => { + mutationFn: (payload: { url: string, body: { upload_file_id: string } }) => { const { url, body } = payload return post<BatchImportResponse>(url, { body }) }, diff --git a/web/service/log.ts b/web/service/log.ts index f54e93630f..aa0be7ac3b 100644 --- a/web/service/log.ts +++ b/web/service/log.ts @@ -1,5 +1,4 @@ import type { Fetcher } from 'swr' -import { get, post } from './base' import type { AgentLogDetailRequest, AgentLogDetailResponse, @@ -21,13 +20,14 @@ import type { WorkflowRunDetailResponse, } from '@/models/log' import type { NodeTracingListResponse } from '@/types/workflow' +import { get, post } from './base' -export const fetchConversationList: Fetcher<ConversationListResponse, { name: string; appId: string; params?: Record<string, any> }> = ({ appId, params }) => { +export const fetchConversationList: Fetcher<ConversationListResponse, { name: string, appId: string, params?: Record<string, any> }> = ({ appId, params }) => { return get<ConversationListResponse>(`/console/api/apps/${appId}/messages`, params) } // (Text Generation Application) Session List -export const fetchCompletionConversations: Fetcher<CompletionConversationsResponse, { url: string; params?: CompletionConversationsRequest }> = ({ url, params }) => { +export const fetchCompletionConversations: Fetcher<CompletionConversationsResponse, { url: string, params?: CompletionConversationsRequest }> = ({ url, params }) => { return get<CompletionConversationsResponse>(url, { params }) } @@ -37,7 +37,7 @@ export const fetchCompletionConversationDetail: Fetcher<CompletionConversationFu } // (Chat Application) Session List -export const fetchChatConversations: Fetcher<ChatConversationsResponse, { url: string; params?: ChatConversationsRequest }> = ({ url, params }) => { +export const fetchChatConversations: Fetcher<ChatConversationsResponse, { url: string, params?: ChatConversationsRequest }> = ({ url, params }) => { return get<ChatConversationsResponse>(url, { params }) } @@ -47,15 +47,15 @@ export const fetchChatConversationDetail: Fetcher<ChatConversationFullDetailResp } // (Chat Application) Message list in one session -export const fetchChatMessages: Fetcher<ChatMessagesResponse, { url: string; params: ChatMessagesRequest }> = ({ url, params }) => { +export const fetchChatMessages: Fetcher<ChatMessagesResponse, { url: string, params: ChatMessagesRequest }> = ({ url, params }) => { return get<ChatMessagesResponse>(url, { params }) } -export const updateLogMessageFeedbacks: Fetcher<LogMessageFeedbacksResponse, { url: string; body: LogMessageFeedbacksRequest }> = ({ url, body }) => { +export const updateLogMessageFeedbacks: Fetcher<LogMessageFeedbacksResponse, { url: string, body: LogMessageFeedbacksRequest }> = ({ url, body }) => { return post<LogMessageFeedbacksResponse>(url, { body }) } -export const updateLogMessageAnnotations: Fetcher<LogMessageAnnotationsResponse, { url: string; body: LogMessageAnnotationsRequest }> = ({ url, body }) => { +export const updateLogMessageAnnotations: Fetcher<LogMessageAnnotationsResponse, { url: string, body: LogMessageAnnotationsRequest }> = ({ url, body }) => { return post<LogMessageAnnotationsResponse>(url, { body }) } @@ -63,7 +63,7 @@ export const fetchAnnotationsCount: Fetcher<AnnotationsCountResponse, { url: str return get<AnnotationsCountResponse>(url) } -export const fetchWorkflowLogs: Fetcher<WorkflowLogsResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => { +export const fetchWorkflowLogs: Fetcher<WorkflowLogsResponse, { url: string, params: Record<string, any> }> = ({ url, params }) => { return get<WorkflowLogsResponse>(url, { params }) } @@ -75,6 +75,6 @@ export const fetchTracingList: Fetcher<NodeTracingListResponse, { url: string }> return get<NodeTracingListResponse>(url) } -export const fetchAgentLogDetail = ({ appID, params }: { appID: string; params: AgentLogDetailRequest }) => { +export const fetchAgentLogDetail = ({ appID, params }: { appID: string, params: AgentLogDetailRequest }) => { return get<AgentLogDetailResponse>(`/apps/${appID}/agent/logs`, { params }) } diff --git a/web/service/plugins.ts b/web/service/plugins.ts index 0a880b865f..afaebf4fb5 100644 --- a/web/service/plugins.ts +++ b/web/service/plugins.ts @@ -1,5 +1,8 @@ import type { Fetcher } from 'swr' -import { get, getMarketplace, post, upload } from './base' +import type { + MarketplaceCollectionPluginsResponse, + MarketplaceCollectionsResponse, +} from '@/app/components/plugins/marketplace/types' import type { Dependency, InstallPackageResponse, @@ -13,10 +16,7 @@ import type { updatePackageResponse, uploadGitHubResponse, } from '@/app/components/plugins/types' -import type { - MarketplaceCollectionPluginsResponse, - MarketplaceCollectionsResponse, -} from '@/app/components/plugins/marketplace/types' +import { get, getMarketplace, post, upload } from './base' export const uploadFile = async (file: File, isBundle: boolean) => { const formData = new FormData() @@ -33,8 +33,7 @@ export const updateFromMarketPlace = async (body: Record<string, string>) => { }) } -export const updateFromGitHub = async (repoUrl: string, selectedVersion: string, selectedPackage: string, - originalPlugin: string, newPlugin: string) => { +export const updateFromGitHub = async (repoUrl: string, selectedVersion: string, selectedPackage: string, originalPlugin: string, newPlugin: string) => { return post<updatePackageResponse>('/workspaces/current/plugin/upgrade/github', { body: { repo: repoUrl, @@ -83,7 +82,7 @@ export const fetchPluginInfoFromMarketPlace = async ({ return getMarketplace<{ data: { plugin: PluginInfoFromMarketPlace, version: { version: string } } }>(`/plugins/${org}/${name}`) } -export const fetchMarketplaceCollections: Fetcher<MarketplaceCollectionsResponse, { url: string; }> = ({ url }) => { +export const fetchMarketplaceCollections: Fetcher<MarketplaceCollectionsResponse, { url: string }> = ({ url }) => { return get<MarketplaceCollectionsResponse>(url) } diff --git a/web/service/share.ts b/web/service/share.ts index dffd3aecb7..203dc896db 100644 --- a/web/service/share.ts +++ b/web/service/share.ts @@ -13,28 +13,35 @@ import type { IOnMessageReplace, IOnNodeFinished, IOnNodeStarted, - IOnTTSChunk, - IOnTTSEnd, IOnTextChunk, IOnTextReplace, IOnThought, + IOnTTSChunk, + IOnTTSEnd, IOnWorkflowFinished, IOnWorkflowStarted, } from './base' -import { - del as consoleDel, get as consoleGet, patch as consolePatch, post as consolePost, - delPublic as del, getPublic as get, patchPublic as patch, postPublic as post, ssePost, -} from './base' import type { FeedbackType } from '@/app/components/base/chat/chat/type' +import type { ChatConfig } from '@/app/components/base/chat/types' +import type { AccessMode } from '@/models/access-control' import type { AppConversationData, AppData, AppMeta, ConversationItem, } from '@/models/share' -import type { ChatConfig } from '@/app/components/base/chat/types' -import type { AccessMode } from '@/models/access-control' import { WEB_APP_SHARE_CODE_HEADER_NAME } from '@/config' +import { + del as consoleDel, + get as consoleGet, + patch as consolePatch, + post as consolePost, + delPublic as del, + getPublic as get, + patchPublic as patch, + postPublic as post, + ssePost, +} from './base' import { getWebAppAccessToken } from './webapp-auth' function getAction(action: 'get' | 'post' | 'del' | 'patch', isInstalledApp: boolean) { @@ -255,7 +262,7 @@ export const fetchAppMeta = async (isInstalledApp: boolean, installedAppId = '') return (getAction('get', isInstalledApp))(getUrl('meta', isInstalledApp, installedAppId)) as Promise<AppMeta> } -export const updateFeedback = async ({ url, body }: { url: string; body: FeedbackType }, isInstalledApp: boolean, installedAppId = '') => { +export const updateFeedback = async ({ url, body }: { url: string, body: FeedbackType }, isInstalledApp: boolean, installedAppId = '') => { return (getAction('post', isInstalledApp))(getUrl(url, isInstalledApp, installedAppId), { body }) } @@ -291,7 +298,7 @@ export const textToAudio = (url: string, isPublicAPI: boolean, body: FormData) = return (getAction('post', !isPublicAPI))(url, { body }, { bodyStringify: false, deleteContentType: true }) as Promise<{ data: string }> } -export const textToAudioStream = (url: string, isPublicAPI: boolean, header: { content_type: string }, body: { streaming: boolean; voice?: string; message_id?: string; text?: string | null | undefined }) => { +export const textToAudioStream = (url: string, isPublicAPI: boolean, header: { content_type: string }, body: { streaming: boolean, voice?: string, message_id?: string, text?: string | null | undefined }) => { return (getAction('post', !isPublicAPI))(url, { body, header }, { needAllResponseContent: true }) } diff --git a/web/service/sso.ts b/web/service/sso.ts index eb22799ba5..ba331d4bc5 100644 --- a/web/service/sso.ts +++ b/web/service/sso.ts @@ -7,10 +7,10 @@ export const getUserSAMLSSOUrl = (invite_token?: string) => { export const getUserOIDCSSOUrl = (invite_token?: string) => { const url = invite_token ? `/enterprise/sso/oidc/login?invite_token=${invite_token}` : '/enterprise/sso/oidc/login' - return get<{ url: string; state: string }>(url) + return get<{ url: string, state: string }>(url) } export const getUserOAuth2SSOUrl = (invite_token?: string) => { const url = invite_token ? `/enterprise/sso/oauth2/login?invite_token=${invite_token}` : '/enterprise/sso/oauth2/login' - return get<{ url: string; state: string }>(url) + return get<{ url: string, state: string }>(url) } diff --git a/web/service/tag.ts b/web/service/tag.ts index 99cfe07342..cf9d56951e 100644 --- a/web/service/tag.ts +++ b/web/service/tag.ts @@ -1,5 +1,5 @@ -import { del, get, patch, post } from './base' import type { Tag } from '@/app/components/base/tag-management/constant' +import { del, get, patch, post } from './base' export const fetchTagList = (type: string) => { return get<Tag[]>('/tags', { params: { type } }) diff --git a/web/service/tools.ts b/web/service/tools.ts index 2897ccac12..99b84d3981 100644 --- a/web/service/tools.ts +++ b/web/service/tools.ts @@ -1,4 +1,3 @@ -import { get, post } from './base' import type { Collection, CustomCollectionBackend, @@ -9,6 +8,7 @@ import type { WorkflowToolProviderResponse, } from '@/app/components/tools/types' import { buildProviderQuery } from './_tools_util' +import { get, post } from './base' export const fetchCollectionList = () => { return get<Collection[]>('/workspaces/current/tool-providers') @@ -58,7 +58,7 @@ export const removeBuiltInToolCredential = (collectionName: string) => { } export const parseParamsSchema = (schema: string) => { - return post<{ parameters_schema: CustomParamSchema[]; schema_type: string }>('/workspaces/current/tool-provider/api/schema', { + return post<{ parameters_schema: CustomParamSchema[], schema_type: string }>('/workspaces/current/tool-provider/api/schema', { body: { schema, }, diff --git a/web/service/use-apps.ts b/web/service/use-apps.ts index cc408c5d1a..0f6c4a64ac 100644 --- a/web/service/use-apps.ts +++ b/web/service/use-apps.ts @@ -1,4 +1,4 @@ -import { get, post } from './base' +import type { GeneratorType } from '@/app/components/app/configuration/config/automatic/types' import type { ApiKeysListResponse, AppDailyConversationsResponse, @@ -11,13 +11,13 @@ import type { WorkflowDailyConversationsResponse, } from '@/models/app' import type { App, AppModeEnum } from '@/types/app' -import { useInvalid } from './use-base' import { useInfiniteQuery, useQuery, useQueryClient, } from '@tanstack/react-query' -import type { GeneratorType } from '@/app/components/app/configuration/config/automatic/types' +import { get, post } from './base' +import { useInvalid } from './use-base' const NAME_SPACE = 'apps' diff --git a/web/service/use-base.ts b/web/service/use-base.ts index b6445f4baf..0a77501747 100644 --- a/web/service/use-base.ts +++ b/web/service/use-base.ts @@ -1,5 +1,6 @@ +import type { QueryKey } from '@tanstack/react-query' import { - type QueryKey, + useQueryClient, } from '@tanstack/react-query' diff --git a/web/service/use-billing.ts b/web/service/use-billing.ts index 2701861bc0..3dc2b8a994 100644 --- a/web/service/use-billing.ts +++ b/web/service/use-billing.ts @@ -6,7 +6,7 @@ const NAME_SPACE = 'billing' export const useBindPartnerStackInfo = () => { return useMutation({ mutationKey: [NAME_SPACE, 'bind-partner-stack'], - mutationFn: (data: { partnerKey: string; clickId: string }) => bindPartnerStackInfo(data.partnerKey, data.clickId), + mutationFn: (data: { partnerKey: string, clickId: string }) => bindPartnerStackInfo(data.partnerKey, data.clickId), }) } diff --git a/web/service/use-common.ts b/web/service/use-common.ts index 5c71553781..7db65caccb 100644 --- a/web/service/use-common.ts +++ b/web/service/use-common.ts @@ -1,27 +1,29 @@ -import { get, post } from './base' -import type { - AccountIntegrate, - CommonResponse, - DataSourceNotion, - FileUploadConfigResponse, - Member, - StructuredOutputRulesRequestBody, - StructuredOutputRulesResponse, -} from '@/models/common' -import { useMutation, useQuery } from '@tanstack/react-query' import type { FileTypesRes } from './datasets' -import type { ICurrentWorkspace, IWorkspace, UserProfileResponse } from '@/models/common' import type { Model, + ModelParameterRule, ModelProvider, ModelTypeEnum, } from '@/app/components/header/account-setting/model-provider-page/declarations' +import type { + AccountIntegrate, + ApiBasedExtension, + CodeBasedExtension, + CommonResponse, + DataSourceNotion, + FileUploadConfigResponse, + ICurrentWorkspace, + IWorkspace, + LangGeniusVersionResponse, + Member, + PluginProvider, + StructuredOutputRulesRequestBody, + StructuredOutputRulesResponse, + UserProfileResponse, +} from '@/models/common' import type { RETRIEVE_METHOD } from '@/types/app' -import type { LangGeniusVersionResponse } from '@/models/common' -import type { PluginProvider } from '@/models/common' -import type { ApiBasedExtension } from '@/models/common' -import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations' -import type { CodeBasedExtension } from '@/models/common' +import { useMutation, useQuery } from '@tanstack/react-query' +import { get, post } from './base' import { useInvalid } from './use-base' const NAME_SPACE = 'common' @@ -44,7 +46,7 @@ export const commonQueryKeys = { notionConnection: [NAME_SPACE, 'notion-connection'] as const, apiBasedExtensions: [NAME_SPACE, 'api-based-extensions'] as const, codeBasedExtensions: (module?: string) => [NAME_SPACE, 'code-based-extensions', module] as const, - invitationCheck: (params?: { workspace_id?: string; email?: string; token?: string }) => [ + invitationCheck: (params?: { workspace_id?: string, email?: string, token?: string }) => [ NAME_SPACE, 'invitation-check', params?.workspace_id ?? '', @@ -220,7 +222,7 @@ export const useIsLogin = () => { }) } catch (e: any) { - if(e.status === 401) + if (e.status === 401) return { logged_in: false } return { logged_in: true } } @@ -236,7 +238,7 @@ export const useLogout = () => { }) } -type ForgotPasswordValidity = CommonResponse & { is_valid: boolean; email: string } +type ForgotPasswordValidity = CommonResponse & { is_valid: boolean, email: string } export const useVerifyForgotPasswordToken = (token?: string | null) => { return useQuery<ForgotPasswordValidity>({ queryKey: commonQueryKeys.forgotPasswordValidity(token), @@ -345,12 +347,12 @@ export const useApiBasedExtensions = () => { }) } -export const useInvitationCheck = (params?: { workspace_id?: string; email?: string; token?: string }, enabled?: boolean) => { +export const useInvitationCheck = (params?: { workspace_id?: string, email?: string, token?: string }, enabled?: boolean) => { return useQuery({ queryKey: commonQueryKeys.invitationCheck(params), queryFn: () => get<{ is_valid: boolean - data: { workspace_name: string; email: string; workspace_id: string } + data: { workspace_name: string, email: string, workspace_id: string } result: string }>('/activate/check', { params }), enabled: enabled ?? !!params?.token, diff --git a/web/service/use-datasource.ts b/web/service/use-datasource.ts index ede9bbfe27..07fe6cd563 100644 --- a/web/service/use-datasource.ts +++ b/web/service/use-datasource.ts @@ -1,13 +1,13 @@ +import type { + DataSourceAuth, + DataSourceCredential, +} from '@/app/components/header/account-setting/data-source-page-new/types' import { useMutation, useQuery, } from '@tanstack/react-query' import { get } from './base' import { useInvalid } from './use-base' -import type { - DataSourceAuth, - DataSourceCredential, -} from '@/app/components/header/account-setting/data-source-page-new/types' const NAME_SPACE = 'data-source-auth' @@ -49,7 +49,8 @@ export const useGetDataSourceOAuthUrl = ( authorization_url: string state: string context_id: string - }>(`/oauth/plugin/${provider}/datasource/get-authorization-url?credential_id=${credentialId}`) + } + >(`/oauth/plugin/${provider}/datasource/get-authorization-url?credential_id=${credentialId}`) }, }) } diff --git a/web/service/use-education.ts b/web/service/use-education.ts index c71833c061..34b08e59c6 100644 --- a/web/service/use-education.ts +++ b/web/service/use-education.ts @@ -1,10 +1,10 @@ -import { get, post } from './base' +import type { EducationAddParams } from '@/app/education-apply/types' import { useMutation, useQuery, } from '@tanstack/react-query' +import { get, post } from './base' import { useInvalid } from './use-base' -import type { EducationAddParams } from '@/app/education-apply/types' const NAME_SPACE = 'education' @@ -46,7 +46,7 @@ export const useEducationAutocomplete = () => { page = 0, limit = 40, } = searchParams - return get<{ data: string[]; has_next: boolean; curr_page: number }>(`/account/education/autocomplete?keywords=${keywords}&page=${page}&limit=${limit}`) + return get<{ data: string[], has_next: boolean, curr_page: number }>(`/account/education/autocomplete?keywords=${keywords}&page=${page}&limit=${limit}`) }, }) } diff --git a/web/service/use-endpoints.ts b/web/service/use-endpoints.ts index 43a82480b9..79702068ab 100644 --- a/web/service/use-endpoints.ts +++ b/web/service/use-endpoints.ts @@ -1,4 +1,3 @@ -import { get, post } from './base' import type { EndpointsResponse, } from '@/app/components/plugins/types' @@ -7,6 +6,7 @@ import { useQuery, useQueryClient, } from '@tanstack/react-query' +import { get, post } from './base' const NAME_SPACE = 'endpoints' @@ -29,7 +29,8 @@ export const useInvalidateEndpointList = () => { queryClient.invalidateQueries( { queryKey: [NAME_SPACE, 'list', pluginID], - }) + }, + ) } } diff --git a/web/service/use-explore.ts b/web/service/use-explore.ts index b7d078edbc..6e57599b69 100644 --- a/web/service/use-explore.ts +++ b/web/service/use-explore.ts @@ -1,6 +1,6 @@ +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useGlobalPublicStore } from '@/context/global-public-context' import { AccessMode } from '@/models/access-control' -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { fetchInstalledAppList, getAppAccessModeByAppId, uninstallApp, updatePinStatus } from './explore' import { fetchAppMeta, fetchAppParams } from './share' @@ -30,7 +30,7 @@ export const useUpdateAppPinStatus = () => { const client = useQueryClient() return useMutation({ mutationKey: [NAME_SPACE, 'updateAppPinStatus'], - mutationFn: ({ appId, isPinned }: { appId: string; isPinned: boolean }) => updatePinStatus(appId, isPinned), + mutationFn: ({ appId, isPinned }: { appId: string, isPinned: boolean }) => updatePinStatus(appId, isPinned), onSuccess: () => { client.invalidateQueries({ queryKey: [NAME_SPACE, 'installedApps'] }) }, diff --git a/web/service/use-flow.ts b/web/service/use-flow.ts index 54820a8d1d..30bec6dd23 100644 --- a/web/service/use-flow.ts +++ b/web/service/use-flow.ts @@ -1,4 +1,5 @@ import type { FlowType } from '@/types/common' +import { curry } from 'lodash-es' import { useDeleteAllInspectorVars as useDeleteAllInspectorVarsInner, useDeleteInspectVar as useDeleteInspectVarInner, @@ -9,7 +10,6 @@ import { useResetConversationVar as useResetConversationVarInner, useResetToLastRunValue as useResetToLastRunValueInner, } from './use-workflow' -import { curry } from 'lodash-es' type Params = { flowType: FlowType diff --git a/web/service/use-models.ts b/web/service/use-models.ts index d6eb929646..d960bda33f 100644 --- a/web/service/use-models.ts +++ b/web/service/use-models.ts @@ -1,9 +1,3 @@ -import { - del, - get, - post, - put, -} from './base' import type { ModelCredential, ModelItem, @@ -16,6 +10,12 @@ import { useQuery, // useQueryClient, } from '@tanstack/react-query' +import { + del, + get, + post, + put, +} from './base' const NAME_SPACE = 'models' diff --git a/web/service/use-oauth.ts b/web/service/use-oauth.ts index d3860fe8d8..b55704e164 100644 --- a/web/service/use-oauth.ts +++ b/web/service/use-oauth.ts @@ -1,5 +1,5 @@ -import { post } from './base' import { useMutation, useQuery } from '@tanstack/react-query' +import { post } from './base' const NAME_SPACE = 'oauth-provider' diff --git a/web/service/use-pipeline.ts b/web/service/use-pipeline.ts index 92a7542c56..c1abbb1984 100644 --- a/web/service/use-pipeline.ts +++ b/web/service/use-pipeline.ts @@ -1,7 +1,7 @@ import type { MutationOptions } from '@tanstack/react-query' -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import { del, get, patch, post } from './base' -import { DatasourceType } from '@/models/pipeline' +import type { ToolCredential } from '@/app/components/tools/types' +import type { DataSourceItem } from '@/app/components/workflow/block-selector/types' +import type { IconInfo } from '@/models/datasets' import type { ConversionResponse, DatasourceNodeSingleRunRequest, @@ -31,9 +31,9 @@ import type { UpdateTemplateInfoRequest, UpdateTemplateInfoResponse, } from '@/models/pipeline' -import type { DataSourceItem } from '@/app/components/workflow/block-selector/types' -import type { ToolCredential } from '@/app/components/tools/types' -import type { IconInfo } from '@/models/datasets' +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import { DatasourceType } from '@/models/pipeline' +import { del, get, patch, post } from './base' import { useInvalid } from './use-base' const NAME_SPACE = 'pipeline' @@ -248,7 +248,7 @@ export const useUpdateDataSourceCredentials = ( pluginId, credentials, name, - }: { provider: string; pluginId: string; credentials: Record<string, any>; name: string; }) => { + }: { provider: string, pluginId: string, credentials: Record<string, any>, name: string }) => { return post('/auth/plugin/datasource', { body: { provider, @@ -303,7 +303,7 @@ export const useExportPipelineDSL = () => { mutationFn: ({ pipelineId, include = false, - }: { pipelineId: string; include?: boolean }) => { + }: { pipelineId: string, include?: boolean }) => { return get<ExportTemplateDSLResponse>(`/rag/pipelines/${pipelineId}/exports?include_secret=${include}`) }, }) @@ -318,10 +318,10 @@ export const usePublishAsCustomizedPipeline = () => { icon_info, description, }: { - pipelineId: string, - name: string, - icon_info: IconInfo, - description?: string, + pipelineId: string + name: string + icon_info: IconInfo + description?: string }) => { return post(`/rag/pipelines/${pipelineId}/customized/publish`, { body: { diff --git a/web/service/use-plugins-auth.ts b/web/service/use-plugins-auth.ts index f2f3c5b532..3427fc7a6a 100644 --- a/web/service/use-plugins-auth.ts +++ b/web/service/use-plugins-auth.ts @@ -1,14 +1,14 @@ +import type { FormSchema } from '@/app/components/base/form/types' +import type { + Credential, + CredentialTypeEnum, +} from '@/app/components/plugins/plugin-auth/types' import { useMutation, useQuery, } from '@tanstack/react-query' import { del, get, post } from './base' import { useInvalid } from './use-base' -import type { - Credential, - CredentialTypeEnum, -} from '@/app/components/plugins/plugin-auth/types' -import type { FormSchema } from '@/app/components/base/form/types' const NAME_SPACE = 'plugins-auth' @@ -112,7 +112,8 @@ export const useGetPluginOAuthUrl = ( authorization_url: string state: string context_id: string - }>(url) + } + >(url) }, }) } diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts index 639d889fa0..58454125ed 100644 --- a/web/service/use-plugins.ts +++ b/web/service/use-plugins.ts @@ -1,50 +1,48 @@ -import { useCallback, useEffect, useState } from 'react' +import type { MutateOptions, QueryOptions } from '@tanstack/react-query' import type { FormOption, ModelProvider, } from '@/app/components/header/account-setting/model-provider-page/declarations' -import { fetchModelProviderModelList } from '@/service/common' -import { fetchPluginInfoFromMarketPlace } from '@/service/plugins' +import type { + PluginsSearchParams, +} from '@/app/components/plugins/marketplace/types' import type { DebugInfo as DebugInfoTypes, Dependency, GitHubItemAndMarketPlaceDependency, - InstallPackageResponse, - InstallStatusResponse, InstalledLatestVersionResponse, InstalledPluginListWithTotalResponse, + InstallPackageResponse, + InstallStatusResponse, PackageDependency, Plugin, PluginDeclaration, PluginDetail, PluginInfoFromMarketPlace, - PluginTask, PluginsFromMarketplaceByInfoResponse, PluginsFromMarketplaceResponse, + PluginTask, ReferenceSetting, + uploadGitHubResponse, VersionInfo, VersionListResponse, - uploadGitHubResponse, } from '@/app/components/plugins/types' -import { TaskStatus } from '@/app/components/plugins/types' -import { PluginCategoryEnum } from '@/app/components/plugins/types' -import type { - PluginsSearchParams, -} from '@/app/components/plugins/marketplace/types' -import { get, getMarketplace, post, postMarketplace } from './base' -import type { MutateOptions, QueryOptions } from '@tanstack/react-query' import { useInfiniteQuery, useMutation, useQuery, useQueryClient, } from '@tanstack/react-query' -import { useInvalidateAllBuiltInTools } from './use-tools' -import useReferenceSetting from '@/app/components/plugins/plugin-page/use-reference-setting' -import { uninstallPlugin } from '@/service/plugins' -import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list' import { cloneDeep } from 'lodash-es' +import { useCallback, useEffect, useState } from 'react' +import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list' import { getFormattedPlugin } from '@/app/components/plugins/marketplace/utils' +import useReferenceSetting from '@/app/components/plugins/plugin-page/use-reference-setting' +import { PluginCategoryEnum, TaskStatus } from '@/app/components/plugins/types' +import { fetchModelProviderModelList } from '@/service/common' +import { fetchPluginInfoFromMarketPlace, uninstallPlugin } from '@/service/plugins' +import { get, getMarketplace, post, postMarketplace } from './base' +import { useInvalidateAllBuiltInTools } from './use-tools' const NAME_SPACE = 'plugins' @@ -53,7 +51,7 @@ export const useCheckInstalled = ({ pluginIds, enabled, }: { - pluginIds: string[], + pluginIds: string[] enabled: boolean }) => { return useQuery<{ plugins: PluginDetail[] }>({ @@ -165,10 +163,12 @@ export const useInstalledPluginList = (disable?: boolean, pageSize = 100) => { const total = data?.pages[0].total ?? 0 return { - data: disable ? undefined : { - plugins, - total, - }, + data: disable + ? undefined + : { + plugins, + total, + }, isLastPage: !hasNextPage, loadNextPage: () => { fetchNextPage() @@ -200,7 +200,8 @@ export const useInvalidateInstalledPluginList = () => { queryClient.invalidateQueries( { queryKey: useInstalledPluginListKey, - }) + }, + ) invalidateAllBuiltInTools() } } @@ -300,8 +301,8 @@ export const useInstallOrUpdate = ({ return useMutation({ mutationFn: (data: { - payload: Dependency[], - plugin: Plugin[], + payload: Dependency[] + plugin: Plugin[] installedInfo: Record<string, VersionInfo> }) => { const { payload, plugin, installedInfo } = data @@ -456,7 +457,8 @@ export const useInvalidateReferenceSettings = () => { queryClient.invalidateQueries( { queryKey: useReferenceSettingKey, - }) + }, + ) } } @@ -633,7 +635,7 @@ export const usePluginTaskList = (category?: PluginCategoryEnum | string) => { export const useMutationClearTaskPlugin = () => { return useMutation({ - mutationFn: ({ taskId, pluginId }: { taskId: string; pluginId: string }) => { + mutationFn: ({ taskId, pluginId }: { taskId: string, pluginId: string }) => { const encodedPluginId = encodeURIComponent(pluginId) return post<{ success: boolean }>(`/workspaces/current/plugin/tasks/${taskId}/delete/${encodedPluginId}`) }, @@ -657,7 +659,7 @@ export const usePluginManifestInfo = (pluginUID: string) => { }) } -export const useDownloadPlugin = (info: { organization: string; pluginName: string; version: string }, needDownload: boolean) => { +export const useDownloadPlugin = (info: { organization: string, pluginName: string, version: string }, needDownload: boolean) => { return useQuery({ queryKey: [NAME_SPACE, 'downloadPlugin', info], queryFn: () => getMarketplace<Blob>(`/plugins/${info.organization}/${info.pluginName}/${info.version}/download`), @@ -678,7 +680,8 @@ export const useModelInList = (currentProvider?: ModelProvider, modelId?: string return useQuery({ queryKey: ['modelInList', currentProvider?.provider, modelId], queryFn: async () => { - if (!modelId || !currentProvider) return false + if (!modelId || !currentProvider) + return false try { const modelsData = await fetchModelProviderModelList(`/workspaces/current/model-providers/${currentProvider?.provider}/models`) return !!modelId && !!modelsData.data.find(item => item.model === modelId) @@ -695,7 +698,8 @@ export const usePluginInfo = (providerName?: string) => { return useQuery({ queryKey: ['pluginInfo', providerName], queryFn: async () => { - if (!providerName) return null + if (!providerName) + return null const parts = providerName.split('/') const org = parts[0] const name = parts[1] diff --git a/web/service/use-strategy.ts b/web/service/use-strategy.ts index af591ac019..b66a1e8b46 100644 --- a/web/service/use-strategy.ts +++ b/web/service/use-strategy.ts @@ -1,12 +1,12 @@ +import type { QueryOptions } from '@tanstack/react-query' import type { StrategyPluginDetail, } from '@/app/components/plugins/types' -import { useInvalid } from './use-base' -import type { QueryOptions } from '@tanstack/react-query' import { useQuery, } from '@tanstack/react-query' import { fetchStrategyDetail, fetchStrategyList } from './strategy' +import { useInvalid } from './use-base' const NAME_SPACE = 'agent_strategy' diff --git a/web/service/use-tools.ts b/web/service/use-tools.ts index 6ac57e84d3..49a28eba3c 100644 --- a/web/service/use-tools.ts +++ b/web/service/use-tools.ts @@ -1,19 +1,19 @@ -import { del, get, post, put } from './base' +import type { QueryKey } from '@tanstack/react-query' import type { Collection, MCPServerDetail, Tool, } from '@/app/components/tools/types' -import { CollectionType } from '@/app/components/tools/types' import type { RAGRecommendedPlugins, ToolWithProvider } from '@/app/components/workflow/types' import type { AppIconType } from '@/types/app' -import { useInvalid } from './use-base' -import type { QueryKey } from '@tanstack/react-query' import { useMutation, useQuery, useQueryClient, } from '@tanstack/react-query' +import { CollectionType } from '@/app/components/tools/types' +import { del, get, post, put } from './base' +import { useInvalid } from './use-base' const NAME_SPACE = 'tools' @@ -160,8 +160,8 @@ export const useDeleteMCP = ({ export const useAuthorizeMCP = () => { return useMutation({ mutationKey: [NAME_SPACE, 'authorize-mcp'], - mutationFn: (payload: { provider_id: string; }) => { - return post<{ result?: string; authorization_url?: string }>('/workspaces/current/tool-provider/mcp/auth', { + mutationFn: (payload: { provider_id: string }) => { + return post<{ result?: string, authorization_url?: string }>('/workspaces/current/tool-provider/mcp/auth', { body: payload, }) }, @@ -171,7 +171,7 @@ export const useAuthorizeMCP = () => { export const useUpdateMCPAuthorizationToken = () => { return useMutation({ mutationKey: [NAME_SPACE, 'refresh-mcp-server-code'], - mutationFn: (payload: { provider_id: string; authorization_code: string }) => { + mutationFn: (payload: { provider_id: string, authorization_code: string }) => { return get<MCPServerDetail>('/workspaces/current/tool-provider/mcp/token', { params: { ...payload, @@ -194,7 +194,8 @@ export const useInvalidateMCPTools = () => { queryClient.invalidateQueries( { queryKey: [NAME_SPACE, 'get-MCP-provider-tool', providerID], - }) + }, + ) } } @@ -217,7 +218,8 @@ export const useInvalidateMCPServerDetail = () => { queryClient.invalidateQueries( { queryKey: [NAME_SPACE, 'MCPServerDetail', appID], - }) + }, + ) } } @@ -281,7 +283,8 @@ export const useInvalidateBuiltinProviderInfo = () => { queryClient.invalidateQueries( { queryKey: [NAME_SPACE, 'builtin-provider-info', providerName], - }) + }, + ) } } diff --git a/web/service/use-triggers.ts b/web/service/use-triggers.ts index 67522d2e55..c21d1aa979 100644 --- a/web/service/use-triggers.ts +++ b/web/service/use-triggers.ts @@ -1,5 +1,3 @@ -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import { del, get, post } from './base' import type { TriggerLogEntity, TriggerOAuthClientParams, @@ -9,7 +7,9 @@ import type { TriggerSubscriptionBuilder, TriggerWithProvider, } from '@/app/components/workflow/block-selector/types' +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { CollectionType } from '@/app/components/tools/types' +import { del, get, post } from './base' import { useInvalid } from './use-base' const NAME_SPACE = 'triggers' @@ -274,7 +274,7 @@ export const useInitiateTriggerOAuth = () => { return useMutation({ mutationKey: [NAME_SPACE, 'initiate-oauth'], mutationFn: (provider: string) => { - return get<{ authorization_url: string; subscription_builder: TriggerSubscriptionBuilder }>( + return get<{ authorization_url: string, subscription_builder: TriggerSubscriptionBuilder }>( `/workspaces/current/trigger-provider/${provider}/subscriptions/oauth/authorize`, {}, { silent: true }, @@ -292,9 +292,9 @@ export const useTriggerPluginDynamicOptions = (payload: { credential_id: string extra?: Record<string, any> }, enabled = true) => { - return useQuery<{ options: Array<{ value: string; label: any }> }>({ + return useQuery<{ options: Array<{ value: string, label: any }> }>({ queryKey: [NAME_SPACE, 'dynamic-options', payload.plugin_id, payload.provider, payload.action, payload.parameter, payload.credential_id, payload.extra], - queryFn: () => get<{ options: Array<{ value: string; label: any }> }>( + queryFn: () => get<{ options: Array<{ value: string, label: any }> }>( '/workspaces/current/plugin/parameters/dynamic-options', { params: { diff --git a/web/service/use-workflow.ts b/web/service/use-workflow.ts index 5da83be360..f5c3021c92 100644 --- a/web/service/use-workflow.ts +++ b/web/service/use-workflow.ts @@ -1,5 +1,5 @@ -import { del, get, patch, post, put } from './base' -import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import type { CommonResponse } from '@/models/common' +import type { FlowType } from '@/types/common' import type { FetchWorkflowDraftPageParams, FetchWorkflowDraftPageResponse, @@ -10,9 +10,9 @@ import type { VarInInspect, WorkflowConfigResponse, } from '@/types/workflow' -import type { CommonResponse } from '@/models/common' +import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import { del, get, patch, post, put } from './base' import { useInvalid, useReset } from './use-base' -import type { FlowType } from '@/types/common' import { getFlowPrefix } from './utils' const NAME_SPACE = 'workflow' @@ -31,7 +31,8 @@ export const useInvalidateAppWorkflow = () => { queryClient.invalidateQueries( { queryKey: [NAME_SPACE, 'publish', appID], - }) + }, + ) } } diff --git a/web/service/utils.spec.ts b/web/service/utils.spec.ts index fc5385c309..212e3dccc3 100644 --- a/web/service/utils.spec.ts +++ b/web/service/utils.spec.ts @@ -1,3 +1,4 @@ +import { FlowType } from '@/types/common' /** * Test suite for service utility functions * @@ -12,7 +13,6 @@ * with a fallback to 'apps' for undefined or unknown flow types. */ import { flowPrefixMap, getFlowPrefix } from './utils' -import { FlowType } from '@/types/common' describe('Service Utils', () => { describe('flowPrefixMap', () => { diff --git a/web/service/workflow-payload.ts b/web/service/workflow-payload.ts index b80c4a3731..b294141cb7 100644 --- a/web/service/workflow-payload.ts +++ b/web/service/workflow-payload.ts @@ -1,8 +1,8 @@ -import { produce } from 'immer' -import type { Edge, Node } from '@/app/components/workflow/types' -import { BlockEnum } from '@/app/components/workflow/types' import type { PluginTriggerNodeType } from '@/app/components/workflow/nodes/trigger-plugin/types' +import type { Edge, Node } from '@/app/components/workflow/types' import type { FetchWorkflowDraftResponse } from '@/types/workflow' +import { produce } from 'immer' +import { BlockEnum } from '@/app/components/workflow/types' export type TriggerPluginNodePayload = { title: string diff --git a/web/service/workflow.ts b/web/service/workflow.ts index 654fe3d01a..96af869ba5 100644 --- a/web/service/workflow.ts +++ b/web/service/workflow.ts @@ -1,16 +1,16 @@ import type { Fetcher } from 'swr' -import { get, post } from './base' +import type { BlockEnum } from '@/app/components/workflow/types' import type { CommonResponse } from '@/models/common' +import type { FlowType } from '@/types/common' import type { ChatRunHistoryResponse, ConversationVariableResponse, FetchWorkflowDraftResponse, NodesDefaultConfigsResponse, + VarInInspect, WorkflowRunHistoryResponse, } from '@/types/workflow' -import type { BlockEnum } from '@/app/components/workflow/types' -import type { VarInInspect } from '@/types/workflow' -import type { FlowType } from '@/types/common' +import { get, post } from './base' import { getFlowPrefix } from './utils' export const fetchWorkflowDraft = (url: string) => { @@ -21,7 +21,7 @@ export const syncWorkflowDraft = ({ url, params }: { url: string params: Pick<FetchWorkflowDraftResponse, 'graph' | 'features' | 'environment_variables' | 'conversation_variables'> }) => { - return post<CommonResponse & { updated_at: number; hash: string }>(url, { body: params }, { silent: true }) + return post<CommonResponse & { updated_at: number, hash: string }>(url, { body: params }, { silent: true }) } export const fetchNodesDefaultConfigs: Fetcher<NodesDefaultConfigsResponse, string> = (url) => { diff --git a/web/tailwind.config.js b/web/tailwind.config.js index a9959e7b76..3cb71081da 100644 --- a/web/tailwind.config.js +++ b/web/tailwind.config.js @@ -1,5 +1,6 @@ // import type { Config } from 'tailwindcss' import commonConfig from './tailwind-common-config' + const config = { content: [ './app/**/*.{js,ts,jsx,tsx}', diff --git a/web/testing/analyze-component.js b/web/testing/analyze-component.js index f9414d9a74..3f70f3d1ec 100755 --- a/web/testing/analyze-component.js +++ b/web/testing/analyze-component.js @@ -3,9 +3,9 @@ import { spawnSync } from 'node:child_process' import fs from 'node:fs' import path from 'node:path' +import tsParser from '@typescript-eslint/parser' import { Linter } from 'eslint' import sonarPlugin from 'eslint-plugin-sonarjs' -import tsParser from '@typescript-eslint/parser' // ============================================================================ // Simple Analyzer @@ -47,7 +47,7 @@ class ComponentAnalyzer { hasImperativeHandle: code.includes('useImperativeHandle'), hasSWR: code.includes('useSWR'), hasReactQuery: code.includes('useQuery') || code.includes('useMutation'), - hasAhooks: code.includes("from 'ahooks'"), + hasAhooks: code.includes('from \'ahooks\''), complexity, maxComplexity, rawComplexity, @@ -60,17 +60,27 @@ class ComponentAnalyzer { detectType(filePath, code) { const normalizedPath = filePath.replace(/\\/g, '/') - if (normalizedPath.includes('/hooks/')) return 'hook' - if (normalizedPath.includes('/utils/')) return 'util' - if (/\/page\.(t|j)sx?$/.test(normalizedPath)) return 'page' - if (/\/layout\.(t|j)sx?$/.test(normalizedPath)) return 'layout' - if (/\/providers?\//.test(normalizedPath)) return 'provider' + if (normalizedPath.includes('/hooks/')) + return 'hook' + if (normalizedPath.includes('/utils/')) + return 'util' + if (/\/page\.(t|j)sx?$/.test(normalizedPath)) + return 'page' + if (/\/layout\.(t|j)sx?$/.test(normalizedPath)) + return 'layout' + if (/\/providers?\//.test(normalizedPath)) + return 'provider' // Dify-specific types - if (normalizedPath.includes('/components/base/')) return 'base-component' - if (normalizedPath.includes('/context/')) return 'context' - if (normalizedPath.includes('/store/')) return 'store' - if (normalizedPath.includes('/service/')) return 'service' - if (/use[A-Z]\w+/.test(code)) return 'component' + if (normalizedPath.includes('/components/base/')) + return 'base-component' + if (normalizedPath.includes('/context/')) + return 'context' + if (normalizedPath.includes('/store/')) + return 'store' + if (normalizedPath.includes('/service/')) + return 'service' + if (/use[A-Z]\w+/.test(code)) + return 'component' return 'component' } @@ -112,7 +122,7 @@ class ComponentAnalyzer { msg => msg.ruleId === 'sonarjs/cognitive-complexity' && msg.messageId === 'fileComplexity', ) - const total = totalMsg ? parseInt(totalMsg.message, 10) : 0 + const total = totalMsg ? Number.parseInt(totalMsg.message, 10) : 0 // Get max function complexity by analyzing each function const maxConfig = { @@ -127,7 +137,7 @@ class ComponentAnalyzer { if (msg.ruleId === 'sonarjs/cognitive-complexity') { const match = msg.message.match(complexityPattern) if (match && match[1]) - max = Math.max(max, parseInt(match[1], 10)) + max = Math.max(max, Number.parseInt(match[1], 10)) } }) @@ -182,10 +192,12 @@ class ComponentAnalyzer { searchName = path.basename(parentDir) } - if (!searchName) return 0 + if (!searchName) + return 0 const searchRoots = this.collectSearchRoots(resolvedComponentPath) - if (searchRoots.length === 0) return 0 + if (searchRoots.length === 0) + return 0 const escapedName = ComponentAnalyzer.escapeRegExp(searchName) const patterns = [ @@ -201,29 +213,34 @@ class ComponentAnalyzer { const stack = [...searchRoots] while (stack.length > 0) { const currentDir = stack.pop() - if (!currentDir || visited.has(currentDir)) continue + if (!currentDir || visited.has(currentDir)) + continue visited.add(currentDir) const entries = fs.readdirSync(currentDir, { withFileTypes: true }) - entries.forEach(entry => { + entries.forEach((entry) => { const entryPath = path.join(currentDir, entry.name) if (entry.isDirectory()) { - if (this.shouldSkipDir(entry.name)) return + if (this.shouldSkipDir(entry.name)) + return stack.push(entryPath) return } - if (!this.shouldInspectFile(entry.name)) return + if (!this.shouldInspectFile(entry.name)) + return const normalizedEntryPath = path.resolve(entryPath) - if (normalizedEntryPath === path.resolve(resolvedComponentPath)) return + if (normalizedEntryPath === path.resolve(resolvedComponentPath)) + return const source = fs.readFileSync(entryPath, 'utf-8') - if (!source.includes(searchName)) return + if (!source.includes(searchName)) + return - if (patterns.some(pattern => { + if (patterns.some((pattern) => { pattern.lastIndex = 0 return pattern.test(source) })) { @@ -252,7 +269,8 @@ class ComponentAnalyzer { break } - if (currentDir === workspaceRoot) break + if (currentDir === workspaceRoot) + break currentDir = path.dirname(currentDir) } @@ -262,8 +280,9 @@ class ComponentAnalyzer { path.join(workspaceRoot, 'src'), ] - fallbackRoots.forEach(root => { - if (fs.existsSync(root) && fs.statSync(root).isDirectory()) roots.add(root) + fallbackRoots.forEach((root) => { + if (fs.existsSync(root) && fs.statSync(root).isDirectory()) + roots.add(root) }) return Array.from(roots) @@ -286,10 +305,14 @@ class ComponentAnalyzer { shouldInspectFile(fileName) { const normalized = fileName.toLowerCase() - if (!(/\.(ts|tsx)$/i.test(fileName))) return false - if (normalized.endsWith('.d.ts')) return false - if (/\.(spec|test)\.(ts|tsx)$/.test(normalized)) return false - if (normalized.endsWith('.stories.tsx')) return false + if (!(/\.(ts|tsx)$/i.test(fileName))) + return false + if (normalized.endsWith('.d.ts')) + return false + if (/\.(spec|test)\.(ts|tsx)$/.test(normalized)) + return false + if (normalized.endsWith('.stories.tsx')) + return false return true } @@ -341,9 +364,12 @@ class ComponentAnalyzer { * Get priority level based on score (0-100 scale) */ getPriorityLevel(score) { - if (score > 75) return '🔴 CRITICAL' - if (score > 50) return '🟠 HIGH' - if (score > 25) return '🟡 MEDIUM' + if (score > 75) + return '🔴 CRITICAL' + if (score > 50) + return '🟠 HIGH' + if (score > 25) + return '🟡 MEDIUM' return '🟢 LOW' } } @@ -420,27 +446,42 @@ Create the test file at: ${testPath} getComplexityLevel(score) { // Normalized complexity thresholds (0-100 scale) - if (score <= 25) return '🟢 Simple' - if (score <= 50) return '🟡 Medium' - if (score <= 75) return '🟠 Complex' + if (score <= 25) + return '🟢 Simple' + if (score <= 50) + return '🟡 Medium' + if (score <= 75) + return '🟠 Complex' return '🔴 Very Complex' } buildFocusPoints(analysis) { const points = [] - if (analysis.hasState) points.push('- Testing state management and updates') - if (analysis.hasEffects) points.push('- Testing side effects and cleanup') - if (analysis.hasCallbacks) points.push('- Testing callback stability and memoization') - if (analysis.hasMemo) points.push('- Testing memoization logic and dependencies') - if (analysis.hasEvents) points.push('- Testing user interactions and event handlers') - if (analysis.hasRouter) points.push('- Mocking Next.js router hooks') - if (analysis.hasAPI) points.push('- Mocking API calls') - if (analysis.hasForwardRef) points.push('- Testing ref forwarding behavior') - if (analysis.hasComponentMemo) points.push('- Testing component memoization') - if (analysis.hasSuspense) points.push('- Testing Suspense boundaries and lazy loading') - if (analysis.hasPortal) points.push('- Testing Portal rendering') - if (analysis.hasImperativeHandle) points.push('- Testing imperative handle methods') + if (analysis.hasState) + points.push('- Testing state management and updates') + if (analysis.hasEffects) + points.push('- Testing side effects and cleanup') + if (analysis.hasCallbacks) + points.push('- Testing callback stability and memoization') + if (analysis.hasMemo) + points.push('- Testing memoization logic and dependencies') + if (analysis.hasEvents) + points.push('- Testing user interactions and event handlers') + if (analysis.hasRouter) + points.push('- Mocking Next.js router hooks') + if (analysis.hasAPI) + points.push('- Mocking API calls') + if (analysis.hasForwardRef) + points.push('- Testing ref forwarding behavior') + if (analysis.hasComponentMemo) + points.push('- Testing component memoization') + if (analysis.hasSuspense) + points.push('- Testing Suspense boundaries and lazy loading') + if (analysis.hasPortal) + points.push('- Testing Portal rendering') + if (analysis.hasImperativeHandle) + points.push('- Testing imperative handle methods') points.push('- Testing edge cases and error handling') points.push('- Testing all prop variations') @@ -524,9 +565,12 @@ Create the test file at: ${testPath} // ===== Performance Optimization ===== if (analysis.hasCallbacks || analysis.hasMemo || analysis.hasComponentMemo) { const features = [] - if (analysis.hasCallbacks) features.push('useCallback') - if (analysis.hasMemo) features.push('useMemo') - if (analysis.hasComponentMemo) features.push('React.memo') + if (analysis.hasCallbacks) + features.push('useCallback') + if (analysis.hasMemo) + features.push('useMemo') + if (analysis.hasComponentMemo) + features.push('React.memo') guidelines.push(`🚀 Performance optimization (${features.join(', ')}):`) guidelines.push(' - Verify callbacks maintain referential equality') @@ -689,12 +733,14 @@ Output format: function extractCopyContent(prompt) { const marker = '📋 PROMPT FOR AI ASSISTANT' const markerIndex = prompt.indexOf(marker) - if (markerIndex === -1) return '' + if (markerIndex === -1) + return '' const section = prompt.slice(markerIndex) const lines = section.split('\n') const firstDivider = lines.findIndex(line => line.includes('━━━━━━━━')) - if (firstDivider === -1) return '' + if (firstDivider === -1) + return '' const startIdx = firstDivider + 1 let endIdx = lines.length @@ -706,7 +752,8 @@ function extractCopyContent(prompt) { } } - if (startIdx >= endIdx) return '' + if (startIdx >= endIdx) + return '' return lines.slice(startIdx, endIdx).join('\n').trim() } @@ -722,8 +769,13 @@ function extractCopyContent(prompt) { function resolveDirectoryEntry(absolutePath, componentPath) { // Entry files in priority order: index files first, then common entry files const entryFiles = [ - 'index.tsx', 'index.ts', // Priority 1: index files - 'node.tsx', 'panel.tsx', 'component.tsx', 'main.tsx', 'container.tsx', // Priority 2: common entry files + 'index.tsx', + 'index.ts', // Priority 1: index files + 'node.tsx', + 'panel.tsx', + 'component.tsx', + 'main.tsx', + 'container.tsx', // Priority 2: common entry files ] for (const entryFile of entryFiles) { const entryPath = path.join(absolutePath, entryFile) @@ -752,9 +804,12 @@ function listAnalyzableFiles(dirPath) { const priority = ['index.tsx', 'index.ts', 'node.tsx', 'panel.tsx', 'component.tsx', 'main.tsx', 'container.tsx'] const aIdx = priority.indexOf(a) const bIdx = priority.indexOf(b) - if (aIdx !== -1 && bIdx !== -1) return aIdx - bIdx - if (aIdx !== -1) return -1 - if (bIdx !== -1) return 1 + if (aIdx !== -1 && bIdx !== -1) + return aIdx - bIdx + if (aIdx !== -1) + return -1 + if (bIdx !== -1) + return 1 return a.localeCompare(b) }) } @@ -797,7 +852,7 @@ function main() { let isJsonMode = false const args = [] - rawArgs.forEach(arg => { + rawArgs.forEach((arg) => { if (arg === '--review') { isReviewMode = true return @@ -949,9 +1004,11 @@ This component is too complex to test effectively. Please consider: try { const checkPbcopy = spawnSync('which', ['pbcopy'], { stdio: 'pipe' }) - if (checkPbcopy.status !== 0) return + if (checkPbcopy.status !== 0) + return const copyContent = extractCopyContent(prompt) - if (!copyContent) return + if (!copyContent) + return const result = spawnSync('pbcopy', [], { input: copyContent, @@ -973,7 +1030,8 @@ This component is too complex to test effectively. Please consider: function inferTestPath(componentPath) { const ext = path.extname(componentPath) - if (!ext) return `${componentPath}.spec.ts` + if (!ext) + return `${componentPath}.spec.ts` return componentPath.replace(ext, `.spec${ext}`) } diff --git a/web/testing/testing.md b/web/testing/testing.md index 78af5375d9..a2c8399d45 100644 --- a/web/testing/testing.md +++ b/web/testing/testing.md @@ -266,8 +266,8 @@ const mockGithubStar = (status: number, body: Record<string, unknown>, delayMs = ### Example Structure -```typescript -import { render, screen, fireEvent, waitFor } from '@testing-library/react' +```tsx +import { fireEvent, render, screen, waitFor } from '@testing-library/react' import Component from './index' // ✅ Import real project components (DO NOT mock these) @@ -286,18 +286,18 @@ let mockSharedState = false describe('ComponentName', () => { beforeEach(() => { - vi.clearAllMocks() // ✅ Reset mocks before each test - mockSharedState = false // ✅ Reset shared state if used in mocks + vi.clearAllMocks() // ✅ Reset mocks before each test + mockSharedState = false // ✅ Reset shared state if used in mocks }) describe('Rendering', () => { it('should render without crashing', () => { // Arrange const props = { title: 'Test' } - + // Act render(<Component {...props} />) - + // Assert expect(screen.getByText('Test')).toBeInTheDocument() }) @@ -307,9 +307,9 @@ describe('ComponentName', () => { it('should handle click events', () => { const handleClick = vi.fn() render(<Component onClick={handleClick} />) - + fireEvent.click(screen.getByRole('button')) - + expect(handleClick).toHaveBeenCalledTimes(1) }) }) @@ -348,26 +348,27 @@ describe('ComponentName', () => { 1. **Example - Correct mock with conditional rendering**: -```typescript +```tsx // ✅ CORRECT: Matches actual component behavior let mockPortalOpenState = false vi.mock('@/app/components/base/portal-to-follow-elem', () => ({ PortalToFollowElem: ({ children, open, ...props }: any) => { - mockPortalOpenState = open || false // Update shared state + mockPortalOpenState = open || false // Update shared state return <div data-open={open}>{children}</div> }, PortalToFollowElemContent: ({ children }: any) => { // ✅ Matches actual: returns null when open is false - if (!mockPortalOpenState) return null + if (!mockPortalOpenState) + return null return <div>{children}</div> }, })) describe('Component', () => { beforeEach(() => { - vi.clearAllMocks() // ✅ Reset mock call history - mockPortalOpenState = false // ✅ Reset shared state + vi.clearAllMocks() // ✅ Reset mock call history + mockPortalOpenState = false // ✅ Reset shared state }) }) ``` diff --git a/web/tsconfig.json b/web/tsconfig.json index d2ba8e7bc1..78c5930aa2 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -1,29 +1,15 @@ { "compilerOptions": { + "incremental": true, "target": "es2015", + "jsx": "preserve", "lib": [ "dom", "dom.iterable", "esnext" ], - "types": ["vitest/globals", "node"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, "module": "esnext", "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], "paths": { "@/*": [ "./*" @@ -31,7 +17,21 @@ "~@/*": [ "./*" ] - } + }, + "resolveJsonModule": true, + "types": ["vitest/globals", "node"], + "allowJs": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "skipLibCheck": true, + "plugins": [ + { + "name": "next" + } + ] }, "include": [ "next-env.d.ts", diff --git a/web/types/app.ts b/web/types/app.ts index 73e11d396a..eb1b29bb60 100644 --- a/web/types/app.ts +++ b/web/types/app.ts @@ -1,14 +1,14 @@ -import type { AnnotationReplyConfig, ChatPromptConfig, CompletionPromptConfig, DatasetConfigs, PromptMode } from '@/models/debug' -import type { CollectionType } from '@/app/components/tools/types' -import type { LanguagesSupported } from '@/i18n-config/language' import type { Tag } from '@/app/components/base/tag-management/constant' +import type { CollectionType } from '@/app/components/tools/types' +import type { UploadFileSetting } from '@/app/components/workflow/types' +import type { LanguagesSupported } from '@/i18n-config/language' +import type { AccessMode } from '@/models/access-control' +import type { ExternalDataTool } from '@/models/common' import type { RerankingModeEnum, WeightedScoreEnum, } from '@/models/datasets' -import type { UploadFileSetting } from '@/app/components/workflow/types' -import type { AccessMode } from '@/models/access-control' -import type { ExternalDataTool } from '@/models/common' +import type { AnnotationReplyConfig, ChatPromptConfig, CompletionPromptConfig, DatasetConfigs, PromptMode } from '@/models/debug' export enum Theme { light = 'light', @@ -274,9 +274,10 @@ export type SiteConfig = { title: string /** Application Description will be shown in the Client */ description: string - /** Define the color in hex for different elements of the chatbot, such as: + /** + * Define the color in hex for different elements of the chatbot, such as: * The header, the button , etc. - */ + */ chat_color_theme: string /** Invert the color of the theme set in chat_color_theme */ chat_color_theme_inverted: boolean @@ -328,12 +329,12 @@ export type App = { /** Description */ description: string /** Author Name */ - author_name: string; + author_name: string /** * Icon Type * @default 'emoji' - */ + */ icon_type: AppIconType | null /** Icon, stores file ID if icon_type is 'image' */ icon: string @@ -375,7 +376,7 @@ export type App = { updated_at: number updated_by?: string } - deleted_tools?: Array<{ id: string; tool_name: string }> + deleted_tools?: Array<{ id: string, tool_name: string }> /** access control */ access_mode: AccessMode max_active_requests?: number | null diff --git a/web/types/feature.ts b/web/types/feature.ts index 6c3bb29201..4f8d92a774 100644 --- a/web/types/feature.ts +++ b/web/types/feature.ts @@ -27,9 +27,9 @@ type License = { export type SystemFeatures = { plugin_installation_permission: { - plugin_installation_scope: InstallationScope, + plugin_installation_scope: InstallationScope restrict_to_marketplace_only: boolean - }, + } sso_enforced_for_signin: boolean sso_enforced_for_signin_protocol: SSOProtocol | '' sso_enforced_for_web: boolean diff --git a/web/types/i18n.d.ts b/web/types/i18n.d.ts index 826fcc1613..b5e5b39aa7 100644 --- a/web/types/i18n.d.ts +++ b/web/types/i18n.d.ts @@ -38,45 +38,45 @@ type WorkflowMessages = typeof import('../i18n/en-US/workflow').default // Complete type structure that matches i18next-config.ts camelCase conversion export type Messages = { - appAnnotation: AppAnnotationMessages; - appApi: AppApiMessages; - appDebug: AppDebugMessages; - appLog: AppLogMessages; - appOverview: AppOverviewMessages; - app: AppMessages; - billing: BillingMessages; - common: CommonMessages; - custom: CustomMessages; - datasetCreation: DatasetCreationMessages; - datasetDocuments: DatasetDocumentsMessages; - datasetHitTesting: DatasetHitTestingMessages; - datasetPipeline: DatasetPipelineMessages; - datasetSettings: DatasetSettingsMessages; - dataset: DatasetMessages; - education: EducationMessages; - explore: ExploreMessages; - layout: LayoutMessages; - login: LoginMessages; - oauth: OauthMessages; - pipeline: PipelineMessages; - pluginTags: PluginTagsMessages; - pluginTrigger: PluginTriggerMessages; - plugin: PluginMessages; - register: RegisterMessages; - runLog: RunLogMessages; - share: ShareMessages; - time: TimeMessages; - tools: ToolsMessages; - workflow: WorkflowMessages; + appAnnotation: AppAnnotationMessages + appApi: AppApiMessages + appDebug: AppDebugMessages + appLog: AppLogMessages + appOverview: AppOverviewMessages + app: AppMessages + billing: BillingMessages + common: CommonMessages + custom: CustomMessages + datasetCreation: DatasetCreationMessages + datasetDocuments: DatasetDocumentsMessages + datasetHitTesting: DatasetHitTestingMessages + datasetPipeline: DatasetPipelineMessages + datasetSettings: DatasetSettingsMessages + dataset: DatasetMessages + education: EducationMessages + explore: ExploreMessages + layout: LayoutMessages + login: LoginMessages + oauth: OauthMessages + pipeline: PipelineMessages + pluginTags: PluginTagsMessages + pluginTrigger: PluginTriggerMessages + plugin: PluginMessages + register: RegisterMessages + runLog: RunLogMessages + share: ShareMessages + time: TimeMessages + tools: ToolsMessages + workflow: WorkflowMessages } // Utility type to flatten nested object keys into dot notation type FlattenKeys<T> = T extends object ? { - [K in keyof T]: T[K] extends object - ? `${K & string}.${FlattenKeys<T[K]> & string}` - : `${K & string}` - }[keyof T] + [K in keyof T]: T[K] extends object + ? `${K & string}.${FlattenKeys<T[K]> & string}` + : `${K & string}` + }[keyof T] : never export type ValidTranslationKeys = FlattenKeys<Messages> @@ -84,19 +84,19 @@ export type ValidTranslationKeys = FlattenKeys<Messages> // Extend react-i18next with Dify's type structure declare module 'react-i18next' { type CustomTypeOptions = { - defaultNS: 'translation'; + defaultNS: 'translation' resources: { - translation: Messages; - }; + translation: Messages + } } } // Extend i18next for complete type safety declare module 'i18next' { type CustomTypeOptions = { - defaultNS: 'translation'; + defaultNS: 'translation' resources: { - translation: Messages; - }; + translation: Messages + } } } diff --git a/web/types/react-18-input-autosize.d.ts b/web/types/react-18-input-autosize.d.ts index 0864b33e6a..c8eae58efb 100644 --- a/web/types/react-18-input-autosize.d.ts +++ b/web/types/react-18-input-autosize.d.ts @@ -1,5 +1,5 @@ declare module 'react-18-input-autosize' { - import type { CSSProperties, ChangeEvent, FocusEvent, KeyboardEvent } from 'react' + import type { ChangeEvent, CSSProperties, FocusEvent, KeyboardEvent } from 'react' export type AutosizeInputProps = { value?: string | number diff --git a/web/types/workflow.ts b/web/types/workflow.ts index efeb53185a..5f74ef2c12 100644 --- a/web/types/workflow.ts +++ b/web/types/workflow.ts @@ -1,26 +1,26 @@ -import type { Viewport } from 'reactflow' -import type { BlockEnum, CommonNodeType, ConversationVariable, Edge, EnvironmentVariable, InputVar, Node, ValueSelector, VarType, Variable } from '@/app/components/workflow/types' -import type { TransferMethod } from '@/types/app' -import type { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' -import type { RAGPipelineVariables } from '@/models/pipeline' -import type { BeforeRunFormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form' -import type { SpecialResultPanelProps } from '@/app/components/workflow/run/special-result-panel' import type { RefObject } from 'react' +import type { Viewport } from 'reactflow' +import type { BeforeRunFormProps } from '@/app/components/workflow/nodes/_base/components/before-run-form' +import type { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types' +import type { SpecialResultPanelProps } from '@/app/components/workflow/run/special-result-panel' +import type { BlockEnum, CommonNodeType, ConversationVariable, Edge, EnvironmentVariable, InputVar, Node, ValueSelector, Variable, VarType } from '@/app/components/workflow/types' +import type { RAGPipelineVariables } from '@/models/pipeline' +import type { TransferMethod } from '@/types/app' export type AgentLogItem = { - node_execution_id: string, - message_id: string, - node_id: string, - parent_id?: string, - label: string, - data: object, // debug data - error?: string, - status: string, + node_execution_id: string + message_id: string + node_id: string + parent_id?: string + label: string + data: object // debug data + error?: string + status: string metadata?: { elapsed_time?: number provider?: string icon?: string - }, + } } export type AgentLogItemWithChildren = AgentLogItem & { @@ -126,7 +126,7 @@ export type FetchWorkflowDraftResponse = { id: string name: string email: string - }, + } tool_published: boolean environment_variables?: EnvironmentVariable[] conversation_variables?: ConversationVariable[] @@ -337,7 +337,7 @@ export type NodesDefaultConfigsResponse = { }[] export type ConversationVariableResponse = { - data: (ConversationVariable & { updated_at: number; created_at: number })[] + data: (ConversationVariable & { updated_at: number, created_at: number })[] has_more: boolean limit: number total: number diff --git a/web/utils/app-redirection.spec.ts b/web/utils/app-redirection.spec.ts index 00aada9e53..ab790b1acc 100644 --- a/web/utils/app-redirection.spec.ts +++ b/web/utils/app-redirection.spec.ts @@ -12,43 +12,43 @@ describe('app-redirection', () => { * - App mode (workflow, advanced-chat, chat, completion, agent-chat) */ describe('getRedirectionPath', () => { - test('returns overview path when user is not editor', () => { + it('returns overview path when user is not editor', () => { const app = { id: 'app-123', mode: AppModeEnum.CHAT } const result = getRedirectionPath(false, app) expect(result).toBe('/app/app-123/overview') }) - test('returns workflow path for workflow mode when user is editor', () => { + it('returns workflow path for workflow mode when user is editor', () => { const app = { id: 'app-123', mode: AppModeEnum.WORKFLOW } const result = getRedirectionPath(true, app) expect(result).toBe('/app/app-123/workflow') }) - test('returns workflow path for advanced-chat mode when user is editor', () => { + it('returns workflow path for advanced-chat mode when user is editor', () => { const app = { id: 'app-123', mode: AppModeEnum.ADVANCED_CHAT } const result = getRedirectionPath(true, app) expect(result).toBe('/app/app-123/workflow') }) - test('returns configuration path for chat mode when user is editor', () => { + it('returns configuration path for chat mode when user is editor', () => { const app = { id: 'app-123', mode: AppModeEnum.CHAT } const result = getRedirectionPath(true, app) expect(result).toBe('/app/app-123/configuration') }) - test('returns configuration path for completion mode when user is editor', () => { + it('returns configuration path for completion mode when user is editor', () => { const app = { id: 'app-123', mode: AppModeEnum.COMPLETION } const result = getRedirectionPath(true, app) expect(result).toBe('/app/app-123/configuration') }) - test('returns configuration path for agent-chat mode when user is editor', () => { + it('returns configuration path for agent-chat mode when user is editor', () => { const app = { id: 'app-456', mode: AppModeEnum.AGENT_CHAT } const result = getRedirectionPath(true, app) expect(result).toBe('/app/app-456/configuration') }) - test('handles different app IDs', () => { + it('handles different app IDs', () => { const app1 = { id: 'abc-123', mode: AppModeEnum.CHAT } const app2 = { id: 'xyz-789', mode: AppModeEnum.WORKFLOW } @@ -64,7 +64,7 @@ describe('app-redirection', () => { /** * Tests that the redirection function is called with the correct path */ - test('calls redirection function with correct path for non-editor', () => { + it('calls redirection function with correct path for non-editor', () => { const app = { id: 'app-123', mode: AppModeEnum.CHAT } const mockRedirect = vi.fn() @@ -74,7 +74,7 @@ describe('app-redirection', () => { expect(mockRedirect).toHaveBeenCalledTimes(1) }) - test('calls redirection function with workflow path for editor', () => { + it('calls redirection function with workflow path for editor', () => { const app = { id: 'app-123', mode: AppModeEnum.WORKFLOW } const mockRedirect = vi.fn() @@ -84,7 +84,7 @@ describe('app-redirection', () => { expect(mockRedirect).toHaveBeenCalledTimes(1) }) - test('calls redirection function with configuration path for chat mode editor', () => { + it('calls redirection function with configuration path for chat mode editor', () => { const app = { id: 'app-123', mode: AppModeEnum.CHAT } const mockRedirect = vi.fn() @@ -94,7 +94,7 @@ describe('app-redirection', () => { expect(mockRedirect).toHaveBeenCalledTimes(1) }) - test('works with different redirection functions', () => { + it('works with different redirection functions', () => { const app = { id: 'app-123', mode: AppModeEnum.WORKFLOW } const paths: string[] = [] const customRedirect = (path: string) => paths.push(path) diff --git a/web/utils/classnames.spec.ts b/web/utils/classnames.spec.ts index a1481e9e49..1b8f487856 100644 --- a/web/utils/classnames.spec.ts +++ b/web/utils/classnames.spec.ts @@ -13,7 +13,7 @@ describe('classnames', () => { * - Falsy value filtering * - Object-based conditional classes */ - test('classnames libs feature', () => { + it('classnames libs feature', () => { expect(cn('foo')).toBe('foo') expect(cn('foo', 'bar')).toBe('foo bar') expect(cn(['foo', 'bar'])).toBe('foo bar') @@ -37,7 +37,7 @@ describe('classnames', () => { * - Custom color classes * - Arbitrary values */ - test('tailwind-merge', () => { + it('tailwind-merge', () => { /* eslint-disable tailwindcss/classnames-order */ expect(cn('p-0')).toBe('p-0') expect(cn('text-right text-center text-left')).toBe('text-left') @@ -68,7 +68,7 @@ describe('classnames', () => { * Tests the integration of classnames and tailwind-merge: * - Object-based conditional classes with Tailwind conflict resolution */ - test('classnames combined with tailwind-merge', () => { + it('classnames combined with tailwind-merge', () => { expect(cn('text-right', { 'text-center': true, })).toBe('text-center') @@ -83,7 +83,7 @@ describe('classnames', () => { * - Strings, arrays, and objects in a single call * - Tailwind merge working across different argument types */ - test('multiple mixed argument types', () => { + it('multiple mixed argument types', () => { expect(cn('foo', ['bar', 'baz'], { qux: true, quux: false })).toBe('foo bar baz qux') expect(cn('p-4', ['p-2', 'm-4'], { 'text-left': true, 'text-right': true })).toBe('p-2 m-4 text-right') }) @@ -93,7 +93,7 @@ describe('classnames', () => { * - Deep array flattening * - Tailwind merge with nested structures */ - test('nested arrays', () => { + it('nested arrays', () => { expect(cn(['foo', ['bar', 'baz']])).toBe('foo bar baz') expect(cn(['p-4', ['p-2', 'text-center']])).toBe('p-2 text-center') }) @@ -103,7 +103,7 @@ describe('classnames', () => { * - Empty strings, arrays, and objects * - Mixed empty and non-empty values */ - test('empty inputs', () => { + it('empty inputs', () => { expect(cn('')).toBe('') expect(cn([])).toBe('') expect(cn({})).toBe('') @@ -116,7 +116,7 @@ describe('classnames', () => { * - Truthy numbers converted to strings * - Zero treated as falsy */ - test('numbers as inputs', () => { + it('numbers as inputs', () => { expect(cn(1)).toBe('1') expect(cn(0)).toBe('') expect(cn('foo', 1, 'bar')).toBe('foo 1 bar') @@ -127,7 +127,7 @@ describe('classnames', () => { * - Object merging * - Tailwind conflict resolution across objects */ - test('multiple objects', () => { + it('multiple objects', () => { expect(cn({ foo: true }, { bar: true })).toBe('foo bar') expect(cn({ foo: true, bar: false }, { bar: true, baz: true })).toBe('foo bar baz') expect(cn({ 'p-4': true }, { 'p-2': true })).toBe('p-2') @@ -139,7 +139,7 @@ describe('classnames', () => { * - Nested arrays with falsy values * - Multiple conflicting Tailwind classes */ - test('complex edge cases', () => { + it('complex edge cases', () => { expect(cn('foo', null, undefined, false, 'bar', 0, 1, '')).toBe('foo bar 1') expect(cn(['foo', null, ['bar', undefined, 'baz']])).toBe('foo bar baz') expect(cn('text-sm', { 'text-lg': false, 'text-xl': true }, 'text-2xl')).toBe('text-2xl') @@ -150,7 +150,7 @@ describe('classnames', () => { * - Important modifiers in objects * - Conflict resolution with important prefix */ - test('important modifier with objects', () => { + it('important modifier with objects', () => { expect(cn({ '!font-medium': true }, { '!font-bold': true })).toBe('!font-bold') expect(cn('font-normal', { '!font-bold': true })).toBe('font-normal !font-bold') }) diff --git a/web/utils/classnames.ts b/web/utils/classnames.ts index d32b0fe652..abba253f04 100644 --- a/web/utils/classnames.ts +++ b/web/utils/classnames.ts @@ -1,4 +1,5 @@ -import { type ClassValue, clsx } from 'clsx' +import type { ClassValue } from 'clsx' +import { clsx } from 'clsx' import { twMerge } from 'tailwind-merge' export function cn(...inputs: ClassValue[]) { diff --git a/web/utils/completion-params.spec.ts b/web/utils/completion-params.spec.ts index 56aa1c0586..0b691a0baa 100644 --- a/web/utils/completion-params.spec.ts +++ b/web/utils/completion-params.spec.ts @@ -1,9 +1,9 @@ -import { mergeValidCompletionParams } from './completion-params' import type { FormValue, ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations' +import { mergeValidCompletionParams } from './completion-params' describe('completion-params', () => { describe('mergeValidCompletionParams', () => { - test('returns empty params and removedDetails for undefined oldParams', () => { + it('returns empty params and removedDetails for undefined oldParams', () => { const rules: ModelParameterRule[] = [] const result = mergeValidCompletionParams(undefined, rules) @@ -11,7 +11,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({}) }) - test('returns empty params and removedDetails for empty oldParams', () => { + it('returns empty params and removedDetails for empty oldParams', () => { const rules: ModelParameterRule[] = [] const result = mergeValidCompletionParams({}, rules) @@ -19,7 +19,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({}) }) - test('validates int type parameter within range', () => { + it('validates int type parameter within range', () => { const rules: ModelParameterRule[] = [ { name: 'max_tokens', type: 'int', min: 1, max: 4096, label: { en_US: 'Max Tokens', zh_Hans: '最大标记' }, required: false }, ] @@ -30,7 +30,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({}) }) - test('removes int parameter below minimum', () => { + it('removes int parameter below minimum', () => { const rules: ModelParameterRule[] = [ { name: 'max_tokens', type: 'int', min: 1, max: 4096, label: { en_US: 'Max Tokens', zh_Hans: '最大标记' }, required: false }, ] @@ -41,7 +41,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({ max_tokens: 'out of range (1-4096)' }) }) - test('removes int parameter above maximum', () => { + it('removes int parameter above maximum', () => { const rules: ModelParameterRule[] = [ { name: 'max_tokens', type: 'int', min: 1, max: 4096, label: { en_US: 'Max Tokens', zh_Hans: '最大标记' }, required: false }, ] @@ -52,7 +52,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({ max_tokens: 'out of range (1-4096)' }) }) - test('removes int parameter with invalid type', () => { + it('removes int parameter with invalid type', () => { const rules: ModelParameterRule[] = [ { name: 'max_tokens', type: 'int', min: 1, max: 4096, label: { en_US: 'Max Tokens', zh_Hans: '最大标记' }, required: false }, ] @@ -63,7 +63,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({ max_tokens: 'invalid type' }) }) - test('validates float type parameter', () => { + it('validates float type parameter', () => { const rules: ModelParameterRule[] = [ { name: 'temperature', type: 'float', min: 0, max: 2, label: { en_US: 'Temperature', zh_Hans: '温度' }, required: false }, ] @@ -74,7 +74,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({}) }) - test('validates float at boundary values', () => { + it('validates float at boundary values', () => { const rules: ModelParameterRule[] = [ { name: 'temperature', type: 'float', min: 0, max: 2, label: { en_US: 'Temperature', zh_Hans: '温度' }, required: false }, ] @@ -86,7 +86,7 @@ describe('completion-params', () => { expect(result2.params).toEqual({ temperature: 2 }) }) - test('validates boolean type parameter', () => { + it('validates boolean type parameter', () => { const rules: ModelParameterRule[] = [ { name: 'stream', type: 'boolean', label: { en_US: 'Stream', zh_Hans: '流' }, required: false }, ] @@ -97,7 +97,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({}) }) - test('removes boolean parameter with invalid type', () => { + it('removes boolean parameter with invalid type', () => { const rules: ModelParameterRule[] = [ { name: 'stream', type: 'boolean', label: { en_US: 'Stream', zh_Hans: '流' }, required: false }, ] @@ -108,7 +108,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({ stream: 'invalid type' }) }) - test('validates string type parameter', () => { + it('validates string type parameter', () => { const rules: ModelParameterRule[] = [ { name: 'model', type: 'string', label: { en_US: 'Model', zh_Hans: '模型' }, required: false }, ] @@ -119,7 +119,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({}) }) - test('validates string parameter with options', () => { + it('validates string parameter with options', () => { const rules: ModelParameterRule[] = [ { name: 'model', type: 'string', options: ['gpt-3.5-turbo', 'gpt-4'], label: { en_US: 'Model', zh_Hans: '模型' }, required: false }, ] @@ -130,7 +130,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({}) }) - test('removes string parameter with invalid option', () => { + it('removes string parameter with invalid option', () => { const rules: ModelParameterRule[] = [ { name: 'model', type: 'string', options: ['gpt-3.5-turbo', 'gpt-4'], label: { en_US: 'Model', zh_Hans: '模型' }, required: false }, ] @@ -141,7 +141,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({ model: 'unsupported option' }) }) - test('validates text type parameter', () => { + it('validates text type parameter', () => { const rules: ModelParameterRule[] = [ { name: 'prompt', type: 'text', label: { en_US: 'Prompt', zh_Hans: '提示' }, required: false }, ] @@ -152,7 +152,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({}) }) - test('removes unsupported parameters', () => { + it('removes unsupported parameters', () => { const rules: ModelParameterRule[] = [ { name: 'temperature', type: 'float', min: 0, max: 2, label: { en_US: 'Temperature', zh_Hans: '温度' }, required: false }, ] @@ -163,7 +163,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({ unsupported_param: 'unsupported' }) }) - test('keeps stop parameter in advanced mode even without rule', () => { + it('keeps stop parameter in advanced mode even without rule', () => { const rules: ModelParameterRule[] = [] const oldParams: FormValue = { stop: ['END'] } const result = mergeValidCompletionParams(oldParams, rules, true) @@ -172,7 +172,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({}) }) - test('removes stop parameter in normal mode without rule', () => { + it('removes stop parameter in normal mode without rule', () => { const rules: ModelParameterRule[] = [] const oldParams: FormValue = { stop: ['END'] } const result = mergeValidCompletionParams(oldParams, rules, false) @@ -181,7 +181,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({ stop: 'unsupported' }) }) - test('handles multiple parameters with mixed validity', () => { + it('handles multiple parameters with mixed validity', () => { const rules: ModelParameterRule[] = [ { name: 'temperature', type: 'float', min: 0, max: 2, label: { en_US: 'Temperature', zh_Hans: '温度' }, required: false }, { name: 'max_tokens', type: 'int', min: 1, max: 4096, label: { en_US: 'Max Tokens', zh_Hans: '最大标记' }, required: false }, @@ -205,7 +205,7 @@ describe('completion-params', () => { }) }) - test('handles parameters without min/max constraints', () => { + it('handles parameters without min/max constraints', () => { const rules: ModelParameterRule[] = [ { name: 'value', type: 'int', label: { en_US: 'Value', zh_Hans: '值' }, required: false }, ] @@ -216,7 +216,7 @@ describe('completion-params', () => { expect(result.removedDetails).toEqual({}) }) - test('removes parameter with unsupported rule type', () => { + it('removes parameter with unsupported rule type', () => { const rules: ModelParameterRule[] = [ { name: 'custom', type: 'unknown_type', label: { en_US: 'Custom', zh_Hans: '自定义' }, required: false } as any, ] diff --git a/web/utils/completion-params.ts b/web/utils/completion-params.ts index 62eb884825..27c34cc8ea 100644 --- a/web/utils/completion-params.ts +++ b/web/utils/completion-params.ts @@ -4,7 +4,7 @@ export const mergeValidCompletionParams = ( oldParams: FormValue | undefined, rules: ModelParameterRule[], isAdvancedMode: boolean = false, -): { params: FormValue; removedDetails: Record<string, string> } => { +): { params: FormValue, removedDetails: Record<string, string> } => { if (!oldParams || Object.keys(oldParams).length === 0) return { params: {}, removedDetails: {} } @@ -81,7 +81,7 @@ export const fetchAndMergeValidCompletionParams = async ( modelId: string, oldParams: FormValue | undefined, isAdvancedMode: boolean = false, -): Promise<{ params: FormValue; removedDetails: Record<string, string> }> => { +): Promise<{ params: FormValue, removedDetails: Record<string, string> }> => { const { fetchModelParameterRules } = await import('@/service/common') const url = `/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}` const { data: parameterRules } = await fetchModelParameterRules(url) diff --git a/web/utils/context.spec.ts b/web/utils/context.spec.ts index 48a086ac4d..b70a15639a 100644 --- a/web/utils/context.spec.ts +++ b/web/utils/context.spec.ts @@ -1,3 +1,4 @@ +import { renderHook } from '@testing-library/react' /** * Test suite for React context creation utilities * @@ -9,7 +10,6 @@ * - createSelectorCtx: Context with selector support using use-context-selector library */ import React from 'react' -import { renderHook } from '@testing-library/react' import { createCtx, createSelectorCtx } from './context' describe('Context Utilities', () => { @@ -106,8 +106,8 @@ describe('Context Utilities', () => { */ it('should handle complex context values', () => { type ComplexContext = { - user: { id: string; name: string } - settings: { theme: string; locale: string } + user: { id: string, name: string } + settings: { theme: string, locale: string } actions: Array<() => void> } diff --git a/web/utils/context.ts b/web/utils/context.ts index 8829a679ce..6afd6131c7 100644 --- a/web/utils/context.ts +++ b/web/utils/context.ts @@ -1,9 +1,11 @@ -import { type Context, type Provider, createContext, useContext } from 'react' +import type { Context, Provider } from 'react' +import { createContext, useContext } from 'react' import * as selector from 'use-context-selector' const createCreateCtxFunction = ( useContextImpl: typeof useContext, - createContextImpl: typeof createContext) => { + createContextImpl: typeof createContext, +) => { return function<T>({ name, defaultValue }: CreateCtxOptions<T> = {}): CreateCtxReturn<T> { const emptySymbol = Symbol(`empty ${name}`) // @ts-expect-error it's ok here diff --git a/web/utils/emoji.spec.ts b/web/utils/emoji.spec.ts index 7cd026f6b3..9ee12e9430 100644 --- a/web/utils/emoji.spec.ts +++ b/web/utils/emoji.spec.ts @@ -1,6 +1,6 @@ import type { Mock } from 'vitest' -import { searchEmoji } from './emoji' import { SearchIndex } from 'emoji-mart' +import { searchEmoji } from './emoji' vi.mock('emoji-mart', () => ({ SearchIndex: { diff --git a/web/utils/emoji.ts b/web/utils/emoji.ts index 9123f780f2..7d58f12b45 100644 --- a/web/utils/emoji.ts +++ b/web/utils/emoji.ts @@ -1,5 +1,5 @@ -import { SearchIndex } from 'emoji-mart' import type { Emoji } from '@emoji-mart/data' +import { SearchIndex } from 'emoji-mart' export async function searchEmoji(value: string) { const emojis: Emoji[] = await SearchIndex.search(value) || [] diff --git a/web/utils/encryption.ts b/web/utils/encryption.ts index f96d8d02ac..2c9e985f12 100644 --- a/web/utils/encryption.ts +++ b/web/utils/encryption.ts @@ -14,15 +14,15 @@ */ export function encryptField(plaintext: string): string { try { - // Base64 encode the plaintext - // btoa works with ASCII, so we need to handle UTF-8 properly + // Base64 encode the plaintext + // btoa works with ASCII, so we need to handle UTF-8 properly const utf8Bytes = new TextEncoder().encode(plaintext) const base64 = btoa(String.fromCharCode(...utf8Bytes)) return base64 } catch (error) { console.error('Field encoding failed:', error) - // If encoding fails, throw error to prevent sending plaintext + // If encoding fails, throw error to prevent sending plaintext throw new Error('Encoding failed. Please check your input.') } } diff --git a/web/utils/format.spec.ts b/web/utils/format.spec.ts index e02d17d335..0fde0ccbe8 100644 --- a/web/utils/format.spec.ts +++ b/web/utils/format.spec.ts @@ -1,67 +1,67 @@ import { downloadFile, formatFileSize, formatNumber, formatNumberAbbreviated, formatTime } from './format' describe('formatNumber', () => { - test('should correctly format integers', () => { + it('should correctly format integers', () => { expect(formatNumber(1234567)).toBe('1,234,567') }) - test('should correctly format decimals', () => { + it('should correctly format decimals', () => { expect(formatNumber(1234567.89)).toBe('1,234,567.89') }) - test('should correctly handle string input', () => { + it('should correctly handle string input', () => { expect(formatNumber('1234567')).toBe('1,234,567') }) - test('should correctly handle zero', () => { + it('should correctly handle zero', () => { expect(formatNumber(0)).toBe(0) }) - test('should correctly handle negative numbers', () => { + it('should correctly handle negative numbers', () => { expect(formatNumber(-1234567)).toBe('-1,234,567') }) - test('should correctly handle empty input', () => { + it('should correctly handle empty input', () => { expect(formatNumber('')).toBe('') }) }) describe('formatFileSize', () => { - test('should return the input if it is falsy', () => { + it('should return the input if it is falsy', () => { expect(formatFileSize(0)).toBe(0) }) - test('should format bytes correctly', () => { + it('should format bytes correctly', () => { expect(formatFileSize(500)).toBe('500.00 bytes') }) - test('should format kilobytes correctly', () => { + it('should format kilobytes correctly', () => { expect(formatFileSize(1500)).toBe('1.46 KB') }) - test('should format megabytes correctly', () => { + it('should format megabytes correctly', () => { expect(formatFileSize(1500000)).toBe('1.43 MB') }) - test('should format gigabytes correctly', () => { + it('should format gigabytes correctly', () => { expect(formatFileSize(1500000000)).toBe('1.40 GB') }) - test('should format terabytes correctly', () => { + it('should format terabytes correctly', () => { expect(formatFileSize(1500000000000)).toBe('1.36 TB') }) - test('should format petabytes correctly', () => { + it('should format petabytes correctly', () => { expect(formatFileSize(1500000000000000)).toBe('1.33 PB') }) }) describe('formatTime', () => { - test('should return the input if it is falsy', () => { + it('should return the input if it is falsy', () => { expect(formatTime(0)).toBe(0) }) - test('should format seconds correctly', () => { + it('should format seconds correctly', () => { expect(formatTime(30)).toBe('30.00 sec') }) - test('should format minutes correctly', () => { + it('should format minutes correctly', () => { expect(formatTime(90)).toBe('1.50 min') }) - test('should format hours correctly', () => { + it('should format hours correctly', () => { expect(formatTime(3600)).toBe('1.00 h') }) - test('should handle large numbers', () => { + it('should handle large numbers', () => { expect(formatTime(7200)).toBe('2.00 h') }) }) describe('downloadFile', () => { - test('should create a link and trigger a download correctly', () => { + it('should create a link and trigger a download correctly', () => { // Mock data const blob = new Blob(['test content'], { type: 'text/plain' }) const fileName = 'test-file.txt' diff --git a/web/utils/format.ts b/web/utils/format.ts index fe5b1deb7d..a2c3ef9751 100644 --- a/web/utils/format.ts +++ b/web/utils/format.ts @@ -1,5 +1,5 @@ -import type { Locale } from '@/i18n-config' import type { Dayjs } from 'dayjs' +import type { Locale } from '@/i18n-config' import 'dayjs/locale/de' import 'dayjs/locale/es' import 'dayjs/locale/fa' @@ -95,7 +95,7 @@ export const formatTime = (seconds: number) => { return `${seconds.toFixed(2)} ${units[index]}` } -export const downloadFile = ({ data, fileName }: { data: Blob; fileName: string }) => { +export const downloadFile = ({ data, fileName }: { data: Blob, fileName: string }) => { const url = window.URL.createObjectURL(data) const a = document.createElement('a') a.href = url @@ -119,7 +119,8 @@ export const downloadFile = ({ data, fileName }: { data: Blob; fileName: string */ export const formatNumberAbbreviated = (num: number) => { // If less than 1000, return as-is - if (num < 1000) return num.toString() + if (num < 1000) + return num.toString() // Define thresholds and suffixes const units = [ diff --git a/web/utils/get-icon.spec.ts b/web/utils/get-icon.spec.ts index 98eb2288fd..84c2191dd8 100644 --- a/web/utils/get-icon.spec.ts +++ b/web/utils/get-icon.spec.ts @@ -1,16 +1,16 @@ +import { MARKETPLACE_API_PREFIX } from '@/config' /** * Test suite for icon utility functions * Tests the generation of marketplace plugin icon URLs */ import { getIconFromMarketPlace } from './get-icon' -import { MARKETPLACE_API_PREFIX } from '@/config' describe('get-icon', () => { describe('getIconFromMarketPlace', () => { /** * Tests basic URL generation for marketplace plugin icons */ - test('returns correct marketplace icon URL', () => { + it('returns correct marketplace icon URL', () => { const pluginId = 'test-plugin-123' const result = getIconFromMarketPlace(pluginId) expect(result).toBe(`${MARKETPLACE_API_PREFIX}/plugins/${pluginId}/icon`) @@ -20,7 +20,7 @@ describe('get-icon', () => { * Tests URL generation with plugin IDs containing special characters * like dashes and underscores */ - test('handles plugin ID with special characters', () => { + it('handles plugin ID with special characters', () => { const pluginId = 'plugin-with-dashes_and_underscores' const result = getIconFromMarketPlace(pluginId) expect(result).toBe(`${MARKETPLACE_API_PREFIX}/plugins/${pluginId}/icon`) @@ -30,7 +30,7 @@ describe('get-icon', () => { * Tests behavior with empty plugin ID * Note: This creates a malformed URL but doesn't throw an error */ - test('handles empty plugin ID', () => { + it('handles empty plugin ID', () => { const pluginId = '' const result = getIconFromMarketPlace(pluginId) expect(result).toBe(`${MARKETPLACE_API_PREFIX}/plugins//icon`) @@ -40,7 +40,7 @@ describe('get-icon', () => { * Tests URL generation with plugin IDs containing spaces * Spaces will be URL-encoded when actually used */ - test('handles plugin ID with spaces', () => { + it('handles plugin ID with spaces', () => { const pluginId = 'plugin with spaces' const result = getIconFromMarketPlace(pluginId) expect(result).toBe(`${MARKETPLACE_API_PREFIX}/plugins/${pluginId}/icon`) @@ -51,7 +51,7 @@ describe('get-icon', () => { * These tests document current behavior and potential security concerns * Note: Current implementation does not sanitize path traversal sequences */ - test('handles path traversal attempts', () => { + it('handles path traversal attempts', () => { const pluginId = '../../../etc/passwd' const result = getIconFromMarketPlace(pluginId) // Current implementation includes path traversal sequences in URL @@ -60,7 +60,7 @@ describe('get-icon', () => { expect(result).toContain(pluginId) }) - test('handles multiple path traversal attempts', () => { + it('handles multiple path traversal attempts', () => { const pluginId = '../../../../etc/passwd' const result = getIconFromMarketPlace(pluginId) // Current implementation includes path traversal sequences in URL @@ -68,7 +68,7 @@ describe('get-icon', () => { expect(result).toContain(pluginId) }) - test('passes through URL-encoded path traversal sequences', () => { + it('passes through URL-encoded path traversal sequences', () => { const pluginId = '..%2F..%2Fetc%2Fpasswd' const result = getIconFromMarketPlace(pluginId) expect(result).toContain(pluginId) @@ -79,14 +79,14 @@ describe('get-icon', () => { * These tests document current behavior with invalid input types * Note: Current implementation converts null/undefined to strings instead of throwing */ - test('handles null plugin ID', () => { + it('handles null plugin ID', () => { // Current implementation converts null to string "null" const result = getIconFromMarketPlace(null as any) expect(result).toContain('null') // This is a potential issue - should validate input type }) - test('handles undefined plugin ID', () => { + it('handles undefined plugin ID', () => { // Current implementation converts undefined to string "undefined" const result = getIconFromMarketPlace(undefined as any) expect(result).toContain('undefined') @@ -97,7 +97,7 @@ describe('get-icon', () => { * Security tests: URL-sensitive characters * These tests verify that URL-sensitive characters are handled appropriately */ - test('does not encode URL-sensitive characters', () => { + it('does not encode URL-sensitive characters', () => { const pluginId = 'plugin/with?special=chars#hash' const result = getIconFromMarketPlace(pluginId) // Note: Current implementation doesn't encode, but test documents the behavior @@ -107,7 +107,7 @@ describe('get-icon', () => { expect(result).toContain('=') }) - test('handles URL characters like & and %', () => { + it('handles URL characters like & and %', () => { const pluginId = 'plugin&with%encoding' const result = getIconFromMarketPlace(pluginId) expect(result).toContain(pluginId) @@ -117,20 +117,20 @@ describe('get-icon', () => { * Edge case tests: Extreme inputs * These tests verify behavior with unusual but valid inputs */ - test('handles very long plugin ID', () => { + it('handles very long plugin ID', () => { const pluginId = 'a'.repeat(10000) const result = getIconFromMarketPlace(pluginId) expect(result).toContain(pluginId) expect(result.length).toBeGreaterThan(10000) }) - test('handles Unicode characters', () => { + it('handles Unicode characters', () => { const pluginId = '插件-🚀-测试-日本語' const result = getIconFromMarketPlace(pluginId) expect(result).toContain(pluginId) }) - test('handles control characters', () => { + it('handles control characters', () => { const pluginId = 'plugin\nwith\ttabs\r\nand\0null' const result = getIconFromMarketPlace(pluginId) expect(result).toContain(pluginId) @@ -140,20 +140,20 @@ describe('get-icon', () => { * Security tests: XSS attempts * These tests verify that XSS attempts are handled appropriately */ - test('handles XSS attempts with script tags', () => { + it('handles XSS attempts with script tags', () => { const pluginId = '<script>alert("xss")</script>' const result = getIconFromMarketPlace(pluginId) expect(result).toContain(pluginId) // Note: Current implementation doesn't sanitize, but test documents the behavior }) - test('handles XSS attempts with event handlers', () => { + it('handles XSS attempts with event handlers', () => { const pluginId = 'plugin"onerror="alert(1)"' const result = getIconFromMarketPlace(pluginId) expect(result).toContain(pluginId) }) - test('handles XSS attempts with encoded script tags', () => { + it('handles XSS attempts with encoded script tags', () => { const pluginId = '%3Cscript%3Ealert%28%22xss%22%29%3C%2Fscript%3E' const result = getIconFromMarketPlace(pluginId) expect(result).toContain(pluginId) diff --git a/web/utils/index.spec.ts b/web/utils/index.spec.ts index d547c75d67..7eb6c32eca 100644 --- a/web/utils/index.spec.ts +++ b/web/utils/index.spec.ts @@ -408,7 +408,7 @@ describe('randomString extended', () => { }) it('should only contain valid characters', () => { - const validChars = /^[0-9a-zA-Z_-]+$/ + const validChars = /^[\w-]+$/ const str = randomString(100) expect(validChars.test(str)).toBe(true) }) diff --git a/web/utils/mcp.spec.ts b/web/utils/mcp.spec.ts index d3c5ef1eab..39d5881e93 100644 --- a/web/utils/mcp.spec.ts +++ b/web/utils/mcp.spec.ts @@ -13,39 +13,39 @@ describe('mcp', () => { /** * The link emoji (🔗) is used as a special marker for MCP icons */ - test('returns true for emoji object with 🔗 content', () => { + it('returns true for emoji object with 🔗 content', () => { const src = { content: '🔗', background: '#fff' } expect(shouldUseMcpIcon(src)).toBe(true) }) - test('returns false for emoji object with different content', () => { + it('returns false for emoji object with different content', () => { const src = { content: '🎉', background: '#fff' } expect(shouldUseMcpIcon(src)).toBe(false) }) - test('returns false for string URL', () => { + it('returns false for string URL', () => { const src = 'https://example.com/icon.png' expect(shouldUseMcpIcon(src)).toBe(false) }) - test('returns false for null', () => { + it('returns false for null', () => { expect(shouldUseMcpIcon(null)).toBe(false) }) - test('returns false for undefined', () => { + it('returns false for undefined', () => { expect(shouldUseMcpIcon(undefined)).toBe(false) }) - test('returns false for empty object', () => { + it('returns false for empty object', () => { expect(shouldUseMcpIcon({})).toBe(false) }) - test('returns false for object without content property', () => { + it('returns false for object without content property', () => { const src = { background: '#fff' } expect(shouldUseMcpIcon(src)).toBe(false) }) - test('returns false for object with null content', () => { + it('returns false for object with null content', () => { const src = { content: null, background: '#fff' } expect(shouldUseMcpIcon(src)).toBe(false) }) @@ -61,27 +61,27 @@ describe('mcp', () => { * - Icon type is 'emoji' * - Icon content is the link emoji (🔗) */ - test('returns true when iconType is emoji and icon is 🔗', () => { + it('returns true when iconType is emoji and icon is 🔗', () => { expect(shouldUseMcpIconForAppIcon('emoji', '🔗')).toBe(true) }) - test('returns false when iconType is emoji but icon is different', () => { + it('returns false when iconType is emoji but icon is different', () => { expect(shouldUseMcpIconForAppIcon('emoji', '🎉')).toBe(false) }) - test('returns false when iconType is image', () => { + it('returns false when iconType is image', () => { expect(shouldUseMcpIconForAppIcon('image', '🔗')).toBe(false) }) - test('returns false when iconType is image and icon is different', () => { + it('returns false when iconType is image and icon is different', () => { expect(shouldUseMcpIconForAppIcon('image', 'file-id-123')).toBe(false) }) - test('returns false for empty strings', () => { + it('returns false for empty strings', () => { expect(shouldUseMcpIconForAppIcon('', '')).toBe(false) }) - test('returns false when iconType is empty but icon is 🔗', () => { + it('returns false when iconType is empty but icon is 🔗', () => { expect(shouldUseMcpIconForAppIcon('', '🔗')).toBe(false) }) }) diff --git a/web/utils/model-config.spec.ts b/web/utils/model-config.spec.ts index 2cccaabc61..9e39883e61 100644 --- a/web/utils/model-config.spec.ts +++ b/web/utils/model-config.spec.ts @@ -1,3 +1,5 @@ +import type { PromptVariable } from '@/models/debug' +import type { UserInputFormItem } from '@/types/app' /** * Test suite for model configuration transformation utilities * @@ -15,8 +17,6 @@ import { promptVariablesToUserInputsForm, userInputsFormToPromptVariables, } from './model-config' -import type { UserInputFormItem } from '@/types/app' -import type { PromptVariable } from '@/models/debug' describe('Model Config Utilities', () => { describe('userInputsFormToPromptVariables', () => { diff --git a/web/utils/model-config.ts b/web/utils/model-config.ts index 707a3685b9..6ed408b37f 100644 --- a/web/utils/model-config.ts +++ b/web/utils/model-config.ts @@ -1,5 +1,5 @@ -import type { UserInputFormItem } from '@/types/app' import type { PromptVariable } from '@/models/debug' +import type { UserInputFormItem } from '@/types/app' export const userInputsFormToPromptVariables = (useInputs: UserInputFormItem[] | null, dataset_query_variable?: string) => { if (!useInputs) @@ -196,7 +196,7 @@ export const promptVariablesToUserInputsForm = (promptVariables: PromptVariable[ } export const formatBooleanInputs = (useInputs?: PromptVariable[] | null, inputs?: Record<string, string | number | object | boolean> | null) => { - if(!useInputs) + if (!useInputs) return inputs const res = { ...inputs } useInputs.forEach((item) => { diff --git a/web/utils/navigation.spec.ts b/web/utils/navigation.spec.ts index dadb34e714..c6327e6b63 100644 --- a/web/utils/navigation.spec.ts +++ b/web/utils/navigation.spec.ts @@ -33,28 +33,28 @@ describe('navigation', () => { * Tests createNavigationPath which builds URLs with optional query parameter preservation */ describe('createNavigationPath', () => { - test('preserves query parameters by default', () => { + it('preserves query parameters by default', () => { const result = createNavigationPath('/datasets/123/documents') expect(result).toBe('/datasets/123/documents?page=3&limit=10&keyword=test') }) - test('returns clean path when preserveParams is false', () => { + it('returns clean path when preserveParams is false', () => { const result = createNavigationPath('/datasets/123/documents', false) expect(result).toBe('/datasets/123/documents') }) - test('handles empty query string', () => { + it('handles empty query string', () => { globalThis.window.location.search = '' const result = createNavigationPath('/datasets/123/documents') expect(result).toBe('/datasets/123/documents') }) - test('handles path with trailing slash', () => { + it('handles path with trailing slash', () => { const result = createNavigationPath('/datasets/123/documents/') expect(result).toBe('/datasets/123/documents/?page=3&limit=10&keyword=test') }) - test('handles root path', () => { + it('handles root path', () => { const result = createNavigationPath('/') expect(result).toBe('/?page=3&limit=10&keyword=test') }) @@ -67,7 +67,7 @@ describe('navigation', () => { /** * Tests that the returned function properly navigates with preserved params */ - test('returns function that calls router.push with correct path', () => { + it('returns function that calls router.push with correct path', () => { const mockRouter = { push: vi.fn() } const backNav = createBackNavigation(mockRouter, '/datasets/123/documents') @@ -76,7 +76,7 @@ describe('navigation', () => { expect(mockRouter.push).toHaveBeenCalledWith('/datasets/123/documents?page=3&limit=10&keyword=test') }) - test('returns function that navigates without params when preserveParams is false', () => { + it('returns function that navigates without params when preserveParams is false', () => { const mockRouter = { push: vi.fn() } const backNav = createBackNavigation(mockRouter, '/datasets/123/documents', false) @@ -85,7 +85,7 @@ describe('navigation', () => { expect(mockRouter.push).toHaveBeenCalledWith('/datasets/123/documents') }) - test('can be called multiple times', () => { + it('can be called multiple times', () => { const mockRouter = { push: vi.fn() } const backNav = createBackNavigation(mockRouter, '/datasets/123/documents') @@ -103,32 +103,32 @@ describe('navigation', () => { /** * Tests selective parameter extraction */ - test('extracts specified parameters', () => { + it('extracts specified parameters', () => { const result = extractQueryParams(['page', 'limit']) expect(result).toEqual({ page: '3', limit: '10' }) }) - test('extracts all specified parameters including keyword', () => { + it('extracts all specified parameters including keyword', () => { const result = extractQueryParams(['page', 'limit', 'keyword']) expect(result).toEqual({ page: '3', limit: '10', keyword: 'test' }) }) - test('ignores non-existent parameters', () => { + it('ignores non-existent parameters', () => { const result = extractQueryParams(['page', 'nonexistent']) expect(result).toEqual({ page: '3' }) }) - test('returns empty object when no parameters match', () => { + it('returns empty object when no parameters match', () => { const result = extractQueryParams(['foo', 'bar']) expect(result).toEqual({}) }) - test('returns empty object for empty array', () => { + it('returns empty object for empty array', () => { const result = extractQueryParams([]) expect(result).toEqual({}) }) - test('handles empty query string', () => { + it('handles empty query string', () => { globalThis.window.location.search = '' const result = extractQueryParams(['page', 'limit']) expect(result).toEqual({}) @@ -142,7 +142,7 @@ describe('navigation', () => { /** * Tests URL construction with custom parameters */ - test('creates path with specified parameters', () => { + it('creates path with specified parameters', () => { const result = createNavigationPathWithParams('/datasets/123/documents', { page: '1', limit: '25', @@ -150,7 +150,7 @@ describe('navigation', () => { expect(result).toBe('/datasets/123/documents?page=1&limit=25') }) - test('handles string and number values', () => { + it('handles string and number values', () => { const result = createNavigationPathWithParams('/datasets/123/documents', { page: 1, limit: 25, @@ -159,7 +159,7 @@ describe('navigation', () => { expect(result).toBe('/datasets/123/documents?page=1&limit=25&keyword=search') }) - test('filters out empty string values', () => { + it('filters out empty string values', () => { const result = createNavigationPathWithParams('/datasets/123/documents', { page: '1', keyword: '', @@ -167,7 +167,7 @@ describe('navigation', () => { expect(result).toBe('/datasets/123/documents?page=1') }) - test('filters out null and undefined values', () => { + it('filters out null and undefined values', () => { const result = createNavigationPathWithParams('/datasets/123/documents', { page: '1', keyword: null as any, @@ -176,12 +176,12 @@ describe('navigation', () => { expect(result).toBe('/datasets/123/documents?page=1') }) - test('returns base path when params are empty', () => { + it('returns base path when params are empty', () => { const result = createNavigationPathWithParams('/datasets/123/documents', {}) expect(result).toBe('/datasets/123/documents') }) - test('encodes special characters in values', () => { + it('encodes special characters in values', () => { const result = createNavigationPathWithParams('/datasets/123/documents', { keyword: 'search term', }) @@ -196,51 +196,51 @@ describe('navigation', () => { /** * Tests parameter merging and overriding */ - test('merges new params with existing ones', () => { + it('merges new params with existing ones', () => { const result = mergeQueryParams({ keyword: 'new', page: '1' }) expect(result.get('page')).toBe('1') expect(result.get('limit')).toBe('10') expect(result.get('keyword')).toBe('new') }) - test('overrides existing parameters', () => { + it('overrides existing parameters', () => { const result = mergeQueryParams({ page: '5' }) expect(result.get('page')).toBe('5') expect(result.get('limit')).toBe('10') }) - test('adds new parameters', () => { + it('adds new parameters', () => { const result = mergeQueryParams({ filter: 'active' }) expect(result.get('filter')).toBe('active') expect(result.get('page')).toBe('3') }) - test('removes parameters with null value', () => { + it('removes parameters with null value', () => { const result = mergeQueryParams({ page: null }) expect(result.get('page')).toBeNull() expect(result.get('limit')).toBe('10') }) - test('removes parameters with undefined value', () => { + it('removes parameters with undefined value', () => { const result = mergeQueryParams({ page: undefined }) expect(result.get('page')).toBeNull() expect(result.get('limit')).toBe('10') }) - test('does not preserve existing when preserveExisting is false', () => { + it('does not preserve existing when preserveExisting is false', () => { const result = mergeQueryParams({ filter: 'active' }, false) expect(result.get('filter')).toBe('active') expect(result.get('page')).toBeNull() expect(result.get('limit')).toBeNull() }) - test('handles number values', () => { + it('handles number values', () => { const result = mergeQueryParams({ page: 5, limit: 20 }) expect(result.get('page')).toBe('5') expect(result.get('limit')).toBe('20') }) - test('does not add empty string values', () => { + it('does not add empty string values', () => { const result = mergeQueryParams({ newParam: '' }) expect(result.get('newParam')).toBeNull() // Existing params are preserved @@ -256,7 +256,7 @@ describe('navigation', () => { * Tests navigation back to dataset documents list */ describe('backToDocuments', () => { - test('creates navigation function with preserved params', () => { + it('creates navigation function with preserved params', () => { const mockRouter = { push: vi.fn() } const backNav = datasetNavigation.backToDocuments(mockRouter, 'dataset-123') @@ -270,7 +270,7 @@ describe('navigation', () => { * Tests navigation to document detail page */ describe('toDocumentDetail', () => { - test('creates navigation function to document detail', () => { + it('creates navigation function to document detail', () => { const mockRouter = { push: vi.fn() } const navFunc = datasetNavigation.toDocumentDetail(mockRouter, 'dataset-123', 'doc-456') @@ -284,7 +284,7 @@ describe('navigation', () => { * Tests navigation to document settings page */ describe('toDocumentSettings', () => { - test('creates navigation function to document settings', () => { + it('creates navigation function to document settings', () => { const mockRouter = { push: vi.fn() } const navFunc = datasetNavigation.toDocumentSettings(mockRouter, 'dataset-123', 'doc-456') diff --git a/web/utils/permission.spec.ts b/web/utils/permission.spec.ts index 758c38037e..42c6e9636e 100644 --- a/web/utils/permission.spec.ts +++ b/web/utils/permission.spec.ts @@ -1,9 +1,9 @@ +import { DatasetPermission } from '@/models/datasets' /** * Test suite for permission utility functions * Tests dataset edit permission logic based on user roles and dataset settings */ import { hasEditPermissionForDataset } from './permission' -import { DatasetPermission } from '@/models/datasets' describe('permission', () => { /** @@ -18,7 +18,7 @@ describe('permission', () => { const creatorId = 'creator-456' const otherUserId = 'user-789' - test('returns true when permission is onlyMe and user is creator', () => { + it('returns true when permission is onlyMe and user is creator', () => { const config = { createdBy: userId, partialMemberList: [], @@ -27,7 +27,7 @@ describe('permission', () => { expect(hasEditPermissionForDataset(userId, config)).toBe(true) }) - test('returns false when permission is onlyMe and user is not creator', () => { + it('returns false when permission is onlyMe and user is not creator', () => { const config = { createdBy: creatorId, partialMemberList: [], @@ -36,7 +36,7 @@ describe('permission', () => { expect(hasEditPermissionForDataset(userId, config)).toBe(false) }) - test('returns true when permission is allTeamMembers for any user', () => { + it('returns true when permission is allTeamMembers for any user', () => { const config = { createdBy: creatorId, partialMemberList: [], @@ -47,7 +47,7 @@ describe('permission', () => { expect(hasEditPermissionForDataset(creatorId, config)).toBe(true) }) - test('returns true when permission is partialMembers and user is in list', () => { + it('returns true when permission is partialMembers and user is in list', () => { const config = { createdBy: creatorId, partialMemberList: [userId, otherUserId], @@ -56,7 +56,7 @@ describe('permission', () => { expect(hasEditPermissionForDataset(userId, config)).toBe(true) }) - test('returns false when permission is partialMembers and user is not in list', () => { + it('returns false when permission is partialMembers and user is not in list', () => { const config = { createdBy: creatorId, partialMemberList: [otherUserId], @@ -65,7 +65,7 @@ describe('permission', () => { expect(hasEditPermissionForDataset(userId, config)).toBe(false) }) - test('returns false when permission is partialMembers with empty list', () => { + it('returns false when permission is partialMembers with empty list', () => { const config = { createdBy: creatorId, partialMemberList: [], @@ -74,7 +74,7 @@ describe('permission', () => { expect(hasEditPermissionForDataset(userId, config)).toBe(false) }) - test('creator is not automatically granted access with partialMembers permission', () => { + it('creator is not automatically granted access with partialMembers permission', () => { const config = { createdBy: creatorId, partialMemberList: [userId], @@ -83,7 +83,7 @@ describe('permission', () => { expect(hasEditPermissionForDataset(creatorId, config)).toBe(false) }) - test('creator has access when included in partialMemberList', () => { + it('creator has access when included in partialMemberList', () => { const config = { createdBy: creatorId, partialMemberList: [creatorId, userId], diff --git a/web/utils/time.spec.ts b/web/utils/time.spec.ts index fc57390fad..9f194950e7 100644 --- a/web/utils/time.spec.ts +++ b/web/utils/time.spec.ts @@ -10,36 +10,36 @@ describe('time', () => { * Returns true if the first date is after the second */ describe('isAfter', () => { - test('returns true when first date is after second date', () => { + it('returns true when first date is after second date', () => { const date1 = '2024-01-02' const date2 = '2024-01-01' expect(isAfter(date1, date2)).toBe(true) }) - test('returns false when first date is before second date', () => { + it('returns false when first date is before second date', () => { const date1 = '2024-01-01' const date2 = '2024-01-02' expect(isAfter(date1, date2)).toBe(false) }) - test('returns false when dates are equal', () => { + it('returns false when dates are equal', () => { const date = '2024-01-01' expect(isAfter(date, date)).toBe(false) }) - test('works with Date objects', () => { + it('works with Date objects', () => { const date1 = new Date('2024-01-02') const date2 = new Date('2024-01-01') expect(isAfter(date1, date2)).toBe(true) }) - test('works with timestamps', () => { + it('works with timestamps', () => { const date1 = 1704240000000 // 2024-01-03 const date2 = 1704153600000 // 2024-01-02 expect(isAfter(date1, date2)).toBe(true) }) - test('handles time differences within same day', () => { + it('handles time differences within same day', () => { const date1 = '2024-01-01 12:00:00' const date2 = '2024-01-01 11:00:00' expect(isAfter(date1, date2)).toBe(true) @@ -54,44 +54,44 @@ describe('time', () => { /** * Tests basic date formatting with standard format */ - test('formats date with YYYY-MM-DD format', () => { + it('formats date with YYYY-MM-DD format', () => { const date = '2024-01-15' const result = formatTime({ date, dateFormat: 'YYYY-MM-DD' }) expect(result).toBe('2024-01-15') }) - test('formats date with custom format', () => { + it('formats date with custom format', () => { const date = '2024-01-15 14:30:00' const result = formatTime({ date, dateFormat: 'MMM DD, YYYY HH:mm' }) expect(result).toBe('Jan 15, 2024 14:30') }) - test('formats date with full month name', () => { + it('formats date with full month name', () => { const date = '2024-01-15' const result = formatTime({ date, dateFormat: 'MMMM DD, YYYY' }) expect(result).toBe('January 15, 2024') }) - test('formats date with time only', () => { + it('formats date with time only', () => { const date = '2024-01-15 14:30:45' const result = formatTime({ date, dateFormat: 'HH:mm:ss' }) expect(result).toBe('14:30:45') }) - test('works with Date objects', () => { + it('works with Date objects', () => { const date = new Date(2024, 0, 15) // Month is 0-indexed const result = formatTime({ date, dateFormat: 'YYYY-MM-DD' }) expect(result).toBe('2024-01-15') }) - test('works with timestamps', () => { + it('works with timestamps', () => { const date = 1705276800000 // 2024-01-15 00:00:00 UTC const result = formatTime({ date, dateFormat: 'YYYY-MM-DD' }) // Account for timezone differences: UTC-5 to UTC+8 can result in 2024-01-14 or 2024-01-15 expect(result).toMatch(/^2024-01-(14|15)$/) }) - test('handles ISO 8601 format', () => { + it('handles ISO 8601 format', () => { const date = '2024-01-15T14:30:00Z' const result = formatTime({ date, dateFormat: 'YYYY-MM-DD HH:mm' }) expect(result).toContain('2024-01-15') diff --git a/web/utils/time.ts b/web/utils/time.ts index daa54a5bf3..0a26a24652 100644 --- a/web/utils/time.ts +++ b/web/utils/time.ts @@ -1,4 +1,5 @@ -import dayjs, { type ConfigType } from 'dayjs' +import type { ConfigType } from 'dayjs' +import dayjs from 'dayjs' import utc from 'dayjs/plugin/utc' dayjs.extend(utc) @@ -7,7 +8,7 @@ export const isAfter = (date: ConfigType, compare: ConfigType) => { return dayjs(date).isAfter(dayjs(compare)) } -export const formatTime = ({ date, dateFormat }: { date: ConfigType; dateFormat: string }) => { +export const formatTime = ({ date, dateFormat }: { date: ConfigType, dateFormat: string }) => { return dayjs(date).format(dateFormat) } diff --git a/web/utils/timezone.json b/web/utils/timezone.json index 80e07ac416..99104a7801 100644 --- a/web/utils/timezone.json +++ b/web/utils/timezone.json @@ -1,1274 +1,1274 @@ [ - { - "name": "-11:00 Niue Time - Alofi", - "value": "Pacific/Niue" - }, - { - "name": "-11:00 Samoa Time - Midway", - "value": "Pacific/Midway" - }, - { - "name": "-11:00 Samoa Time - Pago Pago", - "value": "Pacific/Pago_Pago" - }, - { - "name": "-10:00 Cook Islands Time - Avarua", - "value": "Pacific/Rarotonga" - }, - { - "name": "-10:00 Hawaii-Aleutian Time - Adak", - "value": "America/Adak" - }, - { - "name": "-10:00 Hawaii-Aleutian Time - Honolulu, East Honolulu, Pearl City, Hilo", - "value": "Pacific/Honolulu" - }, - { - "name": "-10:00 Tahiti Time - Faaa, Papeete, Punaauia", - "value": "Pacific/Tahiti" - }, - { - "name": "-09:30 Marquesas Time - Marquesas", - "value": "Pacific/Marquesas" - }, - { - "name": "-09:00 Alaska Time - Anchorage, Juneau, Fairbanks, Eagle River", - "value": "America/Anchorage" - }, - { - "name": "-09:00 Gambier Time - Gambier", - "value": "Pacific/Gambier" - }, - { - "name": "-08:00 Pacific Time - Los Angeles, San Diego, San Jose, San Francisco", - "value": "America/Los_Angeles" - }, - { - "name": "-08:00 Pacific Time - Tijuana, Mexicali, Ensenada, Rosarito", - "value": "America/Tijuana" - }, - { - "name": "-08:00 Pacific Time - Vancouver, Surrey, Okanagan, Victoria", - "value": "America/Vancouver" - }, - { - "name": "-08:00 Pitcairn Time - Adamstown", - "value": "Pacific/Pitcairn" - }, - { - "name": "-07:00 Mexican Pacific Time - Hermosillo, Culiacán, Ciudad Obregón, Mazatlán", - "value": "America/Hermosillo" - }, - { - "name": "-07:00 Mountain Time - Calgary, Edmonton, Red Deer, Sherwood Park", - "value": "America/Edmonton" - }, - { - "name": "-07:00 Mountain Time - Ciudad Juárez", - "value": "America/Ciudad_Juarez" - }, - { - "name": "-07:00 Mountain Time - Denver, El Paso, Albuquerque, Colorado Springs", - "value": "America/Denver" - }, - { - "name": "-07:00 Mountain Time - Phoenix, Tucson, Mesa, Chandler", - "value": "America/Phoenix" - }, - { - "name": "-07:00 Yukon Time - Whitehorse, Fort St. John, Creston, Dawson", - "value": "America/Whitehorse" - }, - { - "name": "-06:00 Central Time - Belize City, San Ignacio, San Pedro, Orange Walk", - "value": "America/Belize" - }, - { - "name": "-06:00 Central Time - Chicago, Houston, San Antonio, Dallas", - "value": "America/Chicago" - }, - { - "name": "-06:00 Central Time - Guatemala City, Villa Nueva, Mixco, Cobán", - "value": "America/Guatemala" - }, - { - "name": "-06:00 Central Time - Managua, León, Masaya, Chinandega", - "value": "America/Managua" - }, - { - "name": "-06:00 Central Time - Mexico City, Iztapalapa, León de los Aldama, Puebla", - "value": "America/Mexico_City" - }, - { - "name": "-06:00 Central Time - Reynosa, Heroica Matamoros, Nuevo Laredo, Piedras Negras", - "value": "America/Matamoros" - }, - { - "name": "-06:00 Central Time - San José, Limón, San Francisco, Alajuela", - "value": "America/Costa_Rica" - }, - { - "name": "-06:00 Central Time - San Salvador, Soyapango, San Miguel, Santa Ana", - "value": "America/El_Salvador" - }, - { - "name": "-06:00 Central Time - Saskatoon, Regina, Prince Albert, Moose Jaw", - "value": "America/Regina" - }, - { - "name": "-06:00 Central Time - Tegucigalpa, San Pedro Sula, La Ceiba, Choloma", - "value": "America/Tegucigalpa" - }, - { - "name": "-06:00 Central Time - Winnipeg, Brandon, Steinbach, Kenora", - "value": "America/Winnipeg" - }, - { - "name": "-06:00 Easter Island Time - Easter", - "value": "Pacific/Easter" - }, - { - "name": "-06:00 Galapagos Time - Galapagos", - "value": "Pacific/Galapagos" - }, - { - "name": "-05:00 Acre Time - Rio Branco, Cruzeiro do Sul, Senador Guiomard, Sena Madureira", - "value": "America/Rio_Branco" - }, - { - "name": "-05:00 Colombia Time - Bogotá, Cali, Medellín, Barranquilla", - "value": "America/Bogota" - }, - { - "name": "-05:00 Cuba Time - Havana, Santiago de Cuba, Camagüey, Holguín", - "value": "America/Havana" - }, - { - "name": "-05:00 Eastern Time - Atikokan", - "value": "America/Atikokan" - }, - { - "name": "-05:00 Eastern Time - Cancún, Chetumal, Playa del Carmen, Cozumel", - "value": "America/Cancun" - }, - { - "name": "-05:00 Eastern Time - Cockburn Town", - "value": "America/Grand_Turk" - }, - { - "name": "-05:00 Eastern Time - George Town, West Bay", - "value": "America/Cayman" - }, - { - "name": "-05:00 Eastern Time - Kingston, New Kingston, Spanish Town, Portmore", - "value": "America/Jamaica" - }, - { - "name": "-05:00 Eastern Time - Nassau, Lucaya, Freeport", - "value": "America/Nassau" - }, - { - "name": "-05:00 Eastern Time - New York City, Brooklyn, Queens, Philadelphia", - "value": "America/New_York" - }, - { - "name": "-05:00 Eastern Time - Panamá, San Miguelito, Juan Díaz, David", - "value": "America/Panama" - }, - { - "name": "-05:00 Eastern Time - Port-au-Prince, Carrefour, Delmas 73, Port-de-Paix", - "value": "America/Port-au-Prince" - }, - { - "name": "-05:00 Eastern Time - Toronto, Montréal, Ottawa, Mississauga", - "value": "America/Toronto" - }, - { - "name": "-05:00 Ecuador Time - Quito, Guayaquil, Cuenca, Santo Domingo de los Colorados", - "value": "America/Guayaquil" - }, - { - "name": "-05:00 Peru Time - Lima, Callao, Arequipa, Trujillo", - "value": "America/Lima" - }, - { - "name": "-04:00 Amazon Time - Manaus, Campo Grande, Cuiabá, Porto Velho", - "value": "America/Manaus" - }, - { - "name": "-04:00 Atlantic Time - Basseterre", - "value": "America/St_Kitts" - }, - { - "name": "-04:00 Atlantic Time - Blanc-Sablon", - "value": "America/Blanc-Sablon" - }, - { - "name": "-04:00 Atlantic Time - Brades, Plymouth", - "value": "America/Montserrat" - }, - { - "name": "-04:00 Atlantic Time - Bridgetown", - "value": "America/Barbados" - }, - { - "name": "-04:00 Atlantic Time - Castries", - "value": "America/St_Lucia" - }, - { - "name": "-04:00 Atlantic Time - Chaguanas, Mon Repos, San Fernando, Port of Spain", - "value": "America/Port_of_Spain" - }, - { - "name": "-04:00 Atlantic Time - Fort-de-France, Le Lamentin, Le Robert, Sainte-Marie", - "value": "America/Martinique" - }, - { - "name": "-04:00 Atlantic Time - Gustavia", - "value": "America/St_Barthelemy" - }, - { - "name": "-04:00 Atlantic Time - Halifax, Moncton, Sydney, Dartmouth", - "value": "America/Halifax" - }, - { - "name": "-04:00 Atlantic Time - Hamilton", - "value": "Atlantic/Bermuda" - }, - { - "name": "-04:00 Atlantic Time - Kingstown, Kingstown Park", - "value": "America/St_Vincent" - }, - { - "name": "-04:00 Atlantic Time - Kralendijk", - "value": "America/Kralendijk" - }, - { - "name": "-04:00 Atlantic Time - Les Abymes, Baie-Mahault, Le Gosier, Petit-Bourg", - "value": "America/Guadeloupe" - }, - { - "name": "-04:00 Atlantic Time - Marigot", - "value": "America/Marigot" - }, - { - "name": "-04:00 Atlantic Time - Oranjestad, Tanki Leendert, San Nicolas", - "value": "America/Aruba" - }, - { - "name": "-04:00 Atlantic Time - Philipsburg", - "value": "America/Lower_Princes" - }, - { - "name": "-04:00 Atlantic Time - Road Town", - "value": "America/Tortola" - }, - { - "name": "-04:00 Atlantic Time - Roseau", - "value": "America/Dominica" - }, - { - "name": "-04:00 Atlantic Time - Saint Croix, Charlotte Amalie", - "value": "America/St_Thomas" - }, - { - "name": "-04:00 Atlantic Time - Saint George's", - "value": "America/Grenada" - }, - { - "name": "-04:00 Atlantic Time - Saint John’s", - "value": "America/Antigua" - }, - { - "name": "-04:00 Atlantic Time - San Juan, Bayamón, Carolina, Ponce", - "value": "America/Puerto_Rico" - }, - { - "name": "-04:00 Atlantic Time - Santo Domingo, Santiago de los Caballeros, Santo Domingo Oeste, Santo Domingo Este", - "value": "America/Santo_Domingo" - }, - { - "name": "-04:00 Atlantic Time - The Valley", - "value": "America/Anguilla" - }, - { - "name": "-04:00 Atlantic Time - Thule", - "value": "America/Thule" - }, - { - "name": "-04:00 Atlantic Time - Willemstad", - "value": "America/Curacao" - }, - { - "name": "-04:00 Bolivia Time - La Paz, Santa Cruz de la Sierra, Cochabamba, Sucre", - "value": "America/La_Paz" - }, - { - "name": "-04:00 Chile Time - Santiago, Puente Alto, Antofagasta, Viña del Mar", - "value": "America/Santiago" - }, - { - "name": "-04:00 Guyana Time - Georgetown, Linden, New Amsterdam", - "value": "America/Guyana" - }, - { - "name": "-04:00 Paraguay Time - Asunción, Ciudad del Este, San Lorenzo, Capiatá", - "value": "America/Asuncion" - }, - { - "name": "-04:00 Venezuela Time - Caracas, Maracaibo, Maracay, Valencia", - "value": "America/Caracas" - }, - { - "name": "-03:30 Newfoundland Time - St. John's, Mount Pearl, Corner Brook, Conception Bay South", - "value": "America/St_Johns" - }, - { - "name": "-03:00 Argentina Time - Buenos Aires, Córdoba, Rosario, Mar del Plata", - "value": "America/Argentina/Buenos_Aires" - }, - { - "name": "-03:00 Brasilia Time - São Paulo, Rio de Janeiro, Belo Horizonte, Salvador", - "value": "America/Sao_Paulo" - }, - { - "name": "-03:00 Chile Time - Palmer, Rothera", - "value": "Antarctica/Palmer" - }, - { - "name": "-03:00 Chile Time - Punta Arenas, Puerto Natales", - "value": "America/Punta_Arenas" - }, - { - "name": "-03:00 Falkland Islands Time - Stanley", - "value": "Atlantic/Stanley" - }, - { - "name": "-03:00 French Guiana Time - Cayenne, Matoury, Saint-Laurent-du-Maroni, Kourou", - "value": "America/Cayenne" - }, - { - "name": "-03:00 St. Pierre & Miquelon Time - Saint-Pierre", - "value": "America/Miquelon" - }, - { - "name": "-03:00 Suriname Time - Paramaribo, Lelydorp", - "value": "America/Paramaribo" - }, - { - "name": "-03:00 Uruguay Time - Montevideo, Salto, Paysandú, Las Piedras", - "value": "America/Montevideo" - }, - { - "name": "-03:00 West Greenland Time - Nuuk", - "value": "America/Nuuk" - }, - { - "name": "-02:00 Fernando de Noronha Time - Noronha", - "value": "America/Noronha" - }, - { - "name": "-02:00 South Georgia Time - Grytviken", - "value": "Atlantic/South_Georgia" - }, - { - "name": "-01:00 Azores Time - Ponta Delgada", - "value": "Atlantic/Azores" - }, - { - "name": "-01:00 Cape Verde Time - Praia, Mindelo, Santa Maria, Cova Figueira", - "value": "Atlantic/Cape_Verde" - }, - { - "name": "-01:00 East Greenland Time - Scoresbysund", - "value": "America/Scoresbysund" - }, - { - "name": "+00:00 Greenwich Mean Time - Abidjan, Abobo, Bouaké, Korhogo", - "value": "Africa/Abidjan" - }, - { - "name": "+00:00 Greenwich Mean Time - Accra, Kumasi, Tamale, Takoradi", - "value": "Africa/Accra" - }, - { - "name": "+00:00 Greenwich Mean Time - Bamako, Ségou, Sikasso, Mopti", - "value": "Africa/Bamako" - }, - { - "name": "+00:00 Greenwich Mean Time - Bissau, Bafatá", - "value": "Africa/Bissau" - }, - { - "name": "+00:00 Greenwich Mean Time - Camayenne, Conakry, Nzérékoré, Kindia", - "value": "Africa/Conakry" - }, - { - "name": "+00:00 Greenwich Mean Time - Dakar, Pikine, Touba, Thiès", - "value": "Africa/Dakar" - }, - { - "name": "+00:00 Greenwich Mean Time - Danmarkshavn", - "value": "America/Danmarkshavn" - }, - { - "name": "+00:00 Greenwich Mean Time - Douglas", - "value": "Europe/Isle_of_Man" - }, - { - "name": "+00:00 Greenwich Mean Time - Dublin, South Dublin, Cork, Limerick", - "value": "Europe/Dublin" - }, - { - "name": "+00:00 Greenwich Mean Time - Freetown, Bo, Kenema, Koidu", - "value": "Africa/Freetown" - }, - { - "name": "+00:00 Greenwich Mean Time - Jamestown", - "value": "Atlantic/St_Helena" - }, - { - "name": "+00:00 Greenwich Mean Time - Lomé, Sokodé, Kara, Atakpamé", - "value": "Africa/Lome" - }, - { - "name": "+00:00 Greenwich Mean Time - London, Birmingham, Liverpool, Glasgow", - "value": "Europe/London" - }, - { - "name": "+00:00 Greenwich Mean Time - Monrovia, Gbarnga, Kakata, Bensonville", - "value": "Africa/Monrovia" - }, - { - "name": "+00:00 Greenwich Mean Time - Nouakchott, Nouadhibou, Dar Naim, Néma", - "value": "Africa/Nouakchott" - }, - { - "name": "+00:00 Greenwich Mean Time - Ouagadougou, Bobo-Dioulasso, Koudougou, Ouahigouya", - "value": "Africa/Ouagadougou" - }, - { - "name": "+00:00 Greenwich Mean Time - Reykjavík, Kópavogur, Hafnarfjörður, Reykjanesbær", - "value": "Atlantic/Reykjavik" - }, - { - "name": "+00:00 Greenwich Mean Time - Saint Helier", - "value": "Europe/Jersey" - }, - { - "name": "+00:00 Greenwich Mean Time - Saint Peter Port", - "value": "Europe/Guernsey" - }, - { - "name": "+00:00 Greenwich Mean Time - Serekunda, Brikama, Bakau, Banjul", - "value": "Africa/Banjul" - }, - { - "name": "+00:00 Greenwich Mean Time - São Tomé", - "value": "Africa/Sao_Tome" - }, - { - "name": "+00:00 Greenwich Mean Time - Troll", - "value": "Antarctica/Troll" - }, - { - "name": "+00:00 Western European Time - Casablanca, Rabat, Fès, Sale", - "value": "Africa/Casablanca" - }, - { - "name": "+00:00 Western European Time - Laayoune, Dakhla, Boujdour", - "value": "Africa/El_Aaiun" - }, - { - "name": "+00:00 Western European Time - Las Palmas de Gran Canaria, Santa Cruz de Tenerife, La Laguna, Telde", - "value": "Atlantic/Canary" - }, - { - "name": "+00:00 Western European Time - Lisbon, Porto, Amadora, Braga", - "value": "Europe/Lisbon" - }, - { - "name": "+00:00 Western European Time - Tórshavn", - "value": "Atlantic/Faroe" - }, - { - "name": "+01:00 Central Africa Time - Windhoek, Rundu, Walvis Bay, Oshakati", - "value": "Africa/Windhoek" - }, - { - "name": "+01:00 Central European Time - Algiers, Boumerdas, Oran, Tébessa", - "value": "Africa/Algiers" - }, - { - "name": "+01:00 Central European Time - Amsterdam, Rotterdam, The Hague, Utrecht", - "value": "Europe/Amsterdam" - }, - { - "name": "+01:00 Central European Time - Andorra la Vella, les Escaldes", - "value": "Europe/Andorra" - }, - { - "name": "+01:00 Central European Time - Belgrade, Niš, Novi Sad, Zemun", - "value": "Europe/Belgrade" - }, - { - "name": "+01:00 Central European Time - Berlin, Hamburg, Munich, Köln", - "value": "Europe/Berlin" - }, - { - "name": "+01:00 Central European Time - Bratislava, Košice, Nitra, Prešov", - "value": "Europe/Bratislava" - }, - { - "name": "+01:00 Central European Time - Brussels, Antwerpen, Gent, Charleroi", - "value": "Europe/Brussels" - }, - { - "name": "+01:00 Central European Time - Budapest, Debrecen, Szeged, Miskolc", - "value": "Europe/Budapest" - }, - { - "name": "+01:00 Central European Time - Copenhagen, Århus, Odense, Aalborg", - "value": "Europe/Copenhagen" - }, - { - "name": "+01:00 Central European Time - Gibraltar", - "value": "Europe/Gibraltar" - }, - { - "name": "+01:00 Central European Time - Ljubljana, Maribor, Kranj, Celje", - "value": "Europe/Ljubljana" - }, - { - "name": "+01:00 Central European Time - Longyearbyen", - "value": "Arctic/Longyearbyen" - }, - { - "name": "+01:00 Central European Time - Luxembourg, Esch-sur-Alzette, Dudelange", - "value": "Europe/Luxembourg" - }, - { - "name": "+01:00 Central European Time - Madrid, Barcelona, Valencia, Sevilla", - "value": "Europe/Madrid" - }, - { - "name": "+01:00 Central European Time - Monaco, Monte-Carlo", - "value": "Europe/Monaco" - }, - { - "name": "+01:00 Central European Time - Oslo, Bergen, Trondheim, Stavanger", - "value": "Europe/Oslo" - }, - { - "name": "+01:00 Central European Time - Paris, Marseille, Lyon, Toulouse", - "value": "Europe/Paris" - }, - { - "name": "+01:00 Central European Time - Podgorica, Nikšić, Herceg Novi, Pljevlja", - "value": "Europe/Podgorica" - }, - { - "name": "+01:00 Central European Time - Prague, Brno, Ostrava, Pilsen", - "value": "Europe/Prague" - }, - { - "name": "+01:00 Central European Time - Rome, Milan, Naples, Turin", - "value": "Europe/Rome" - }, - { - "name": "+01:00 Central European Time - San Marino", - "value": "Europe/San_Marino" - }, - { - "name": "+01:00 Central European Time - San Pawl il-Baħar, Birkirkara, Mosta, Sliema", - "value": "Europe/Malta" - }, - { - "name": "+01:00 Central European Time - Sarajevo, Banja Luka, Zenica, Tuzla", - "value": "Europe/Sarajevo" - }, - { - "name": "+01:00 Central European Time - Skopje, Kumanovo, Prilep, Bitola", - "value": "Europe/Skopje" - }, - { - "name": "+01:00 Central European Time - Stockholm, Göteborg, Malmö, Uppsala", - "value": "Europe/Stockholm" - }, - { - "name": "+01:00 Central European Time - Tirana, Durrës, Elbasan, Vlorë", - "value": "Europe/Tirane" - }, - { - "name": "+01:00 Central European Time - Tunis, Sfax, Sousse, Kairouan", - "value": "Africa/Tunis" - }, - { - "name": "+01:00 Central European Time - Vaduz", - "value": "Europe/Vaduz" - }, - { - "name": "+01:00 Central European Time - Vatican City", - "value": "Europe/Vatican" - }, - { - "name": "+01:00 Central European Time - Vienna, Graz, Linz, Favoriten", - "value": "Europe/Vienna" - }, - { - "name": "+01:00 Central European Time - Warsaw, Łódź, Kraków, Wrocław", - "value": "Europe/Warsaw" - }, - { - "name": "+01:00 Central European Time - Zagreb, Split, Rijeka, Osijek", - "value": "Europe/Zagreb" - }, - { - "name": "+01:00 Central European Time - Zürich, Genève, Basel, Lausanne", - "value": "Europe/Zurich" - }, - { - "name": "+01:00 West Africa Time - Bangui, Bimbo, Mbaïki, Berbérati", - "value": "Africa/Bangui" - }, - { - "name": "+01:00 West Africa Time - Bata, Malabo, Ebebiyin", - "value": "Africa/Malabo" - }, - { - "name": "+01:00 West Africa Time - Brazzaville, Pointe-Noire, Dolisie, Kayes", - "value": "Africa/Brazzaville" - }, - { - "name": "+01:00 West Africa Time - Cotonou, Abomey-Calavi, Djougou, Porto-Novo", - "value": "Africa/Porto-Novo" - }, - { - "name": "+01:00 West Africa Time - Douala, Yaoundé, Garoua, Kousséri", - "value": "Africa/Douala" - }, - { - "name": "+01:00 West Africa Time - Kinshasa, Masina, Kikwit, Mbandaka", - "value": "Africa/Kinshasa" - }, - { - "name": "+01:00 West Africa Time - Lagos, Kano, Ibadan, Port Harcourt", - "value": "Africa/Lagos" - }, - { - "name": "+01:00 West Africa Time - Libreville, Port-Gentil, Franceville, Oyem", - "value": "Africa/Libreville" - }, - { - "name": "+01:00 West Africa Time - Luanda, N’dalatando, Huambo, Lobito", - "value": "Africa/Luanda" - }, - { - "name": "+01:00 West Africa Time - N'Djamena, Moundou, Sarh, Abéché", - "value": "Africa/Ndjamena" - }, - { - "name": "+01:00 West Africa Time - Niamey, Zinder, Maradi, Agadez", - "value": "Africa/Niamey" - }, - { - "name": "+02:00 Central Africa Time - Bujumbura, Muyinga, Gitega, Ruyigi", - "value": "Africa/Bujumbura" - }, - { - "name": "+02:00 Central Africa Time - Gaborone, Francistown, Molepolole, Maun", - "value": "Africa/Gaborone" - }, - { - "name": "+02:00 Central Africa Time - Harare, Bulawayo, Chitungwiza, Mutare", - "value": "Africa/Harare" - }, - { - "name": "+02:00 Central Africa Time - Juba, Winejok, Yei, Malakal", - "value": "Africa/Juba" - }, - { - "name": "+02:00 Central Africa Time - Khartoum, Omdurman, Nyala, Port Sudan", - "value": "Africa/Khartoum" - }, - { - "name": "+02:00 Central Africa Time - Kigali, Gisenyi, Butare, Gitarama", - "value": "Africa/Kigali" - }, - { - "name": "+02:00 Central Africa Time - Lilongwe, Blantyre, Mzuzu, Zomba", - "value": "Africa/Blantyre" - }, - { - "name": "+02:00 Central Africa Time - Lubumbashi, Mbuji-Mayi, Kisangani, Kananga", - "value": "Africa/Lubumbashi" - }, - { - "name": "+02:00 Central Africa Time - Lusaka, Ndola, Kitwe, Chipata", - "value": "Africa/Lusaka" - }, - { - "name": "+02:00 Central Africa Time - Maputo, Matola, Nampula, Beira", - "value": "Africa/Maputo" - }, - { - "name": "+02:00 Eastern European Time - Athens, Thessaloníki, Pátra, Piraeus", - "value": "Europe/Athens" - }, - { - "name": "+02:00 Eastern European Time - Beirut, Ra’s Bayrūt, Tripoli, Sidon", - "value": "Asia/Beirut" - }, - { - "name": "+02:00 Eastern European Time - Bucharest, Sector 3, Iaşi, Sector 6", - "value": "Europe/Bucharest" - }, - { - "name": "+02:00 Eastern European Time - Cairo, Alexandria, Giza, Shubrā al Khaymah", - "value": "Africa/Cairo" - }, - { - "name": "+02:00 Eastern European Time - Chisinau, Tiraspol, Bălţi, Bender", - "value": "Europe/Chisinau" - }, - { - "name": "+02:00 Eastern European Time - East Jerusalem, Gaza, Khān Yūnis, Jabālyā", - "value": "Asia/Hebron" - }, - { - "name": "+02:00 Eastern European Time - Helsinki, Espoo, Tampere, Oulu", - "value": "Europe/Helsinki" - }, - { - "name": "+02:00 Eastern European Time - Kaliningrad, Chernyakhovsk, Sovetsk, Baltiysk", - "value": "Europe/Kaliningrad" - }, - { - "name": "+02:00 Eastern European Time - Kyiv, Kharkiv, Odesa, Dnipro", - "value": "Europe/Kyiv" - }, - { - "name": "+02:00 Eastern European Time - Mariehamn", - "value": "Europe/Mariehamn" - }, - { - "name": "+02:00 Eastern European Time - Nicosia, Limassol, Larnaca, Stróvolos", - "value": "Asia/Nicosia" - }, - { - "name": "+02:00 Eastern European Time - Riga, Daugavpils, Liepāja, Jelgava", - "value": "Europe/Riga" - }, - { - "name": "+02:00 Eastern European Time - Sofia, Plovdiv, Varna, Burgas", - "value": "Europe/Sofia" - }, - { - "name": "+02:00 Eastern European Time - Tallinn, Tartu, Narva, Pärnu", - "value": "Europe/Tallinn" - }, - { - "name": "+02:00 Eastern European Time - Tripoli, Benghazi, Ajdabiya, Mişrātah", - "value": "Africa/Tripoli" - }, - { - "name": "+02:00 Eastern European Time - Vilnius, Kaunas, Klaipėda, Šiauliai", - "value": "Europe/Vilnius" - }, - { - "name": "+02:00 Israel Time - Jerusalem, Tel Aviv, West Jerusalem, Haifa", - "value": "Asia/Jerusalem" - }, - { - "name": "+02:00 South Africa Time - Johannesburg, Cape Town, Durban, Soweto", - "value": "Africa/Johannesburg" - }, - { - "name": "+02:00 South Africa Time - Manzini, Mbabane, Lobamba", - "value": "Africa/Mbabane" - }, - { - "name": "+02:00 South Africa Time - Maseru, Mohale’s Hoek, Mafeteng, Leribe", - "value": "Africa/Maseru" - }, - { - "name": "+03:00 Arabian Time - Al Aḩmadī, Ḩawallī, As Sālimīyah, Şabāḩ as Sālim", - "value": "Asia/Kuwait" - }, - { - "name": "+03:00 Arabian Time - Ar Rifā‘, Manama, Al Muharraq, Dār Kulayb", - "value": "Asia/Bahrain" - }, - { - "name": "+03:00 Arabian Time - Baghdad, Al Mawşil al Jadīdah, Al Başrah al Qadīmah, Mosul", - "value": "Asia/Baghdad" - }, - { - "name": "+03:00 Arabian Time - Doha, Ar Rayyān, Umm Şalāl Muḩammad, Al Wakrah", - "value": "Asia/Qatar" - }, - { - "name": "+03:00 Arabian Time - Jeddah, Riyadh, Mecca, Medina", - "value": "Asia/Riyadh" - }, - { - "name": "+03:00 Arabian Time - Sanaa, Aden, Al Ḩudaydah, Taiz", - "value": "Asia/Aden" - }, - { - "name": "+03:00 Asia/Amman - Amman, Zarqa, Irbid, Russeifa", - "value": "Asia/Amman" - }, - { - "name": "+03:00 Asia/Damascus - Aleppo, Damascus, Homs, Latakia", - "value": "Asia/Damascus" - }, - { - "name": "+03:00 East Africa Time - Addis Ababa, Jijiga, Gondar, Mek'ele", - "value": "Africa/Addis_Ababa" - }, - { - "name": "+03:00 East Africa Time - Antananarivo, Toamasina, Antsirabe, Mahajanga", - "value": "Indian/Antananarivo" - }, - { - "name": "+03:00 East Africa Time - Asmara, Keren, Massawa, Assab", - "value": "Africa/Asmara" - }, - { - "name": "+03:00 East Africa Time - Dar es Salaam, Mwanza, Zanzibar, Arusha", - "value": "Africa/Dar_es_Salaam" - }, - { - "name": "+03:00 East Africa Time - Djibouti, 'Ali Sabieh, Tadjourah, Obock", - "value": "Africa/Djibouti" - }, - { - "name": "+03:00 East Africa Time - Kampala, Gulu, Lira, Mbarara", - "value": "Africa/Kampala" - }, - { - "name": "+03:00 East Africa Time - Mamoudzou, Koungou, Dzaoudzi", - "value": "Indian/Mayotte" - }, - { - "name": "+03:00 East Africa Time - Mogadishu, Hargeysa, Berbera, Kismayo", - "value": "Africa/Mogadishu" - }, - { - "name": "+03:00 East Africa Time - Moroni, Moutsamoudou", - "value": "Indian/Comoro" - }, - { - "name": "+03:00 East Africa Time - Nairobi, Kakamega, Mombasa, Ruiru", - "value": "Africa/Nairobi" - }, - { - "name": "+03:00 Moscow Time - Minsk, Homyel', Hrodna, Mahilyow", - "value": "Europe/Minsk" - }, - { - "name": "+03:00 Moscow Time - Moscow, Saint Petersburg, Nizhniy Novgorod, Kazan", - "value": "Europe/Moscow" - }, - { - "name": "+03:00 Moscow Time - Sevastopol, Simferopol, Kerch, Yevpatoriya", - "value": "Europe/Simferopol" - }, - { - "name": "+03:00 Syowa Time - Syowa", - "value": "Antarctica/Syowa" - }, - { - "name": "+03:00 Turkey Time - Istanbul, Ankara, Bursa, İzmir", - "value": "Europe/Istanbul" - }, - { - "name": "+03:30 Iran Time - Tehran, Mashhad, Isfahan, Karaj", - "value": "Asia/Tehran" - }, - { - "name": "+04:00 Armenia Time - Yerevan, Gyumri, Vanadzor, Vagharshapat", - "value": "Asia/Yerevan" - }, - { - "name": "+04:00 Azerbaijan Time - Baku, Sumqayıt, Ganja, Lankaran", - "value": "Asia/Baku" - }, - { - "name": "+04:00 Georgia Time - Tbilisi, Batumi, Kutaisi, Rustavi", - "value": "Asia/Tbilisi" - }, - { - "name": "+04:00 Gulf Time - Dubai, Abu Dhabi, Sharjah, Al Ain City", - "value": "Asia/Dubai" - }, - { - "name": "+04:00 Gulf Time - Muscat, Seeb, Bawshar, ‘Ibrī", - "value": "Asia/Muscat" - }, - { - "name": "+04:00 Mauritius Time - Port Louis, Vacoas, Beau Bassin-Rose Hill, Curepipe", - "value": "Indian/Mauritius" - }, - { - "name": "+04:00 Réunion Time - Saint-Denis, Saint-Paul, Le Tampon, Saint-Pierre", - "value": "Indian/Reunion" - }, - { - "name": "+04:00 Samara Time - Samara, Saratov, Tolyatti, Izhevsk", - "value": "Europe/Samara" - }, - { - "name": "+04:00 Seychelles Time - Victoria", - "value": "Indian/Mahe" - }, - { - "name": "+04:30 Afghanistan Time - Kabul, Herāt, Mazār-e Sharīf, Kandahār", - "value": "Asia/Kabul" - }, - { - "name": "+05:00 French Southern & Antarctic Time - Port-aux-Français", - "value": "Indian/Kerguelen" - }, - { - "name": "+05:00 Maldives Time - Male", - "value": "Indian/Maldives" - }, - { - "name": "+05:00 Mawson Time - Mawson", - "value": "Antarctica/Mawson" - }, - { - "name": "+05:00 Pakistan Time - Karachi, Lahore, Faisalabad, Rawalpindi", - "value": "Asia/Karachi" - }, - { - "name": "+05:00 Tajikistan Time - Dushanbe, Isfara, Istaravshan, Kŭlob", - "value": "Asia/Dushanbe" - }, - { - "name": "+05:00 Turkmenistan Time - Ashgabat, Türkmenabat, Daşoguz, Mary", - "value": "Asia/Ashgabat" - }, - { - "name": "+05:00 Uzbekistan Time - Tashkent, Namangan, Samarkand, Andijon", - "value": "Asia/Tashkent" - }, - { - "name": "+05:00 West Kazakhstan Time - Aktobe, Kyzylorda, Oral, Atyrau", - "value": "Asia/Aqtobe" - }, - { - "name": "+05:00 Yekaterinburg Time - Yekaterinburg, Chelyabinsk, Ufa, Perm", - "value": "Asia/Yekaterinburg" - }, - { - "name": "+05:30 India Time - Colombo, Dehiwala-Mount Lavinia, Maharagama, Jaffna", - "value": "Asia/Colombo" - }, - { - "name": "+05:30 India Time - Mumbai, Delhi, Bengaluru, Hyderābād", - "value": "Asia/Kolkata" - }, - { - "name": "+05:45 Nepal Time - Kathmandu, Bharatpur, Pātan, Birgañj", - "value": "Asia/Kathmandu" - }, - { - "name": "+06:00 Bangladesh Time - Dhaka, Chattogram, Khulna, Rangpur", - "value": "Asia/Dhaka" - }, - { - "name": "+06:00 Bhutan Time - Thimphu, Phuntsholing, Tsirang, Punākha", - "value": "Asia/Thimphu" - }, - { - "name": "+06:00 China Time - Ürümqi, Shihezi, Korla, Aksu", - "value": "Asia/Urumqi" - }, - { - "name": "+06:00 East Kazakhstan Time - Almaty, Shymkent, Karagandy, Taraz", - "value": "Asia/Almaty" - }, - { - "name": "+06:00 Indian Ocean Time - Chagos", - "value": "Indian/Chagos" - }, - { - "name": "+06:00 Kyrgyzstan Time - Bishkek, Osh, Jalal-Abad, Karakol", - "value": "Asia/Bishkek" - }, - { - "name": "+06:00 Omsk Time - Omsk, Tara, Kalachinsk", - "value": "Asia/Omsk" - }, - { - "name": "+06:00 Vostok Time - Vostok", - "value": "Antarctica/Vostok" - }, - { - "name": "+06:30 Cocos Islands Time - West Island", - "value": "Indian/Cocos" - }, - { - "name": "+06:30 Myanmar Time - Yangon, Mandalay, Nay Pyi Taw, Mawlamyine", - "value": "Asia/Yangon" - }, - { - "name": "+07:00 Christmas Island Time - Flying Fish Cove", - "value": "Indian/Christmas" - }, - { - "name": "+07:00 Davis Time - Davis", - "value": "Antarctica/Davis" - }, - { - "name": "+07:00 Hovd Time - Ulaangom, Khovd, Ölgii, Altai", - "value": "Asia/Hovd" - }, - { - "name": "+07:00 Indochina Time - Bangkok, Samut Prakan, Mueang Nonthaburi, Chon Buri", - "value": "Asia/Bangkok" - }, - { - "name": "+07:00 Indochina Time - Ho Chi Minh City, Da Nang, Biên Hòa, Cần Thơ", - "value": "Asia/Ho_Chi_Minh" - }, - { - "name": "+07:00 Indochina Time - Phnom Penh, Takeo, Siem Reap, Battambang", - "value": "Asia/Phnom_Penh" - }, - { - "name": "+07:00 Indochina Time - Vientiane, Savannakhet, Pakse, Thakhèk", - "value": "Asia/Vientiane" - }, - { - "name": "+07:00 Novosibirsk Time - Novosibirsk, Krasnoyarsk, Barnaul, Tomsk", - "value": "Asia/Novosibirsk" - }, - { - "name": "+07:00 Western Indonesia Time - Jakarta, Surabaya, Bekasi, Bandung", - "value": "Asia/Jakarta" - }, - { - "name": "+08:00 Australian Western Time - Perth, Mandurah, Bunbury, Baldivis", - "value": "Australia/Perth" - }, - { - "name": "+08:00 Brunei Darussalam Time - Bandar Seri Begawan, Kuala Belait, Seria, Tutong", - "value": "Asia/Brunei" - }, - { - "name": "+08:00 Central Indonesia Time - Makassar, Samarinda, Denpasar, Balikpapan", - "value": "Asia/Makassar" - }, - { - "name": "+08:00 China Time - Macau", - "value": "Asia/Macau" - }, - { - "name": "+08:00 China Time - Shanghai, Beijing, Shenzhen, Guangzhou", - "value": "Asia/Shanghai" - }, - { - "name": "+08:00 Hong Kong Time - Hong Kong, Kowloon, Victoria, Tuen Mun", - "value": "Asia/Hong_Kong" - }, - { - "name": "+08:00 Irkutsk Time - Irkutsk, Ulan-Ude, Bratsk, Angarsk", - "value": "Asia/Irkutsk" - }, - { - "name": "+08:00 Malaysia Time - Kuala Lumpur, Petaling Jaya, Klang, Johor Bahru", - "value": "Asia/Kuala_Lumpur" - }, - { - "name": "+08:00 Philippine Time - Quezon City, Davao, Manila, Caloocan City", - "value": "Asia/Manila" - }, - { - "name": "+08:00 Singapore Time - Singapore, Woodlands, Geylang, Queenstown Estate", - "value": "Asia/Singapore" - }, - { - "name": "+08:00 Taipei Time - Taipei, Kaohsiung, Taichung, Tainan", - "value": "Asia/Taipei" - }, - { - "name": "+08:00 Ulaanbaatar Time - Ulan Bator, Erdenet, Darhan, Mörön", - "value": "Asia/Ulaanbaatar" - }, - { - "name": "+08:45 Australian Central Western Time - Eucla", - "value": "Australia/Eucla" - }, - { - "name": "+09:00 East Timor Time - Dili, Maliana, Suai, Likisá", - "value": "Asia/Dili" - }, - { - "name": "+09:00 Eastern Indonesia Time - Jayapura, Ambon, Sorong, Ternate", - "value": "Asia/Jayapura" - }, - { - "name": "+09:00 Japan Time - Tokyo, Yokohama, Osaka, Nagoya", - "value": "Asia/Tokyo" - }, - { - "name": "+09:00 Korean Time - Pyongyang, Hamhŭng, Namp’o, Sunch’ŏn", - "value": "Asia/Pyongyang" - }, - { - "name": "+09:00 Korean Time - Seoul, Busan, Incheon, Daegu", - "value": "Asia/Seoul" - }, - { - "name": "+09:00 Palau Time - Ngerulmud", - "value": "Pacific/Palau" - }, - { - "name": "+09:00 Yakutsk Time - Chita, Yakutsk, Blagoveshchensk, Belogorsk", - "value": "Asia/Chita" - }, - { - "name": "+09:30 Australian Central Time - Adelaide, Adelaide Hills, Mount Gambier, Morphett Vale", - "value": "Australia/Adelaide" - }, - { - "name": "+09:30 Australian Central Time - Darwin, Alice Springs, Palmerston", - "value": "Australia/Darwin" - }, - { - "name": "+10:00 Australian Eastern Time - Brisbane, Gold Coast, Logan City, Townsville", - "value": "Australia/Brisbane" - }, - { - "name": "+10:00 Australian Eastern Time - Sydney, Melbourne, Canberra, Newcastle", - "value": "Australia/Sydney" - }, - { - "name": "+10:00 Chamorro Time - Dededo Village, Yigo Village, Tamuning-Tumon-Harmon Village, Tamuning", - "value": "Pacific/Guam" - }, - { - "name": "+10:00 Chamorro Time - Saipan", - "value": "Pacific/Saipan" - }, - { - "name": "+10:00 Chuuk Time - Chuuk", - "value": "Pacific/Chuuk" - }, - { - "name": "+10:00 Dumont-d’Urville Time - DumontDUrville", - "value": "Antarctica/DumontDUrville" - }, - { - "name": "+10:00 Papua New Guinea Time - Port Moresby, Lae, Mount Hagen, Popondetta", - "value": "Pacific/Port_Moresby" - }, - { - "name": "+10:00 Vladivostok Time - Khabarovsk, Vladivostok, Khabarovsk Vtoroy, Komsomolsk-on-Amur", - "value": "Asia/Vladivostok" - }, - { - "name": "+10:30 Lord Howe Time - Lord Howe", - "value": "Australia/Lord_Howe" - }, - { - "name": "+11:00 Bougainville Time - Arawa", - "value": "Pacific/Bougainville" - }, - { - "name": "+11:00 Casey Time - Casey", - "value": "Antarctica/Casey" - }, - { - "name": "+11:00 Kosrae Time - Kosrae, Palikir - National Government Center", - "value": "Pacific/Kosrae" - }, - { - "name": "+11:00 New Caledonia Time - Nouméa, Mont-Dore, Dumbéa", - "value": "Pacific/Noumea" - }, - { - "name": "+11:00 Norfolk Island Time - Kingston", - "value": "Pacific/Norfolk" - }, - { - "name": "+11:00 Sakhalin Time - Yuzhno-Sakhalinsk, Magadan, Korsakov, Kholmsk", - "value": "Asia/Sakhalin" - }, - { - "name": "+11:00 Solomon Islands Time - Honiara", - "value": "Pacific/Guadalcanal" - }, - { - "name": "+11:00 Vanuatu Time - Port-Vila", - "value": "Pacific/Efate" - }, - { - "name": "+12:00 Fiji Time - Nasinu, Suva, Lautoka, Nadi", - "value": "Pacific/Fiji" - }, - { - "name": "+12:00 Gilbert Islands Time - Tarawa", - "value": "Pacific/Tarawa" - }, - { - "name": "+12:00 Marshall Islands Time - Majuro, Kwajalein, RMI Capitol", - "value": "Pacific/Majuro" - }, - { - "name": "+12:00 Nauru Time - Yaren", - "value": "Pacific/Nauru" - }, - { - "name": "+12:00 New Zealand Time - Auckland, Wellington, Christchurch, Manukau City", - "value": "Pacific/Auckland" - }, - { - "name": "+12:00 New Zealand Time - McMurdo", - "value": "Antarctica/McMurdo" - }, - { - "name": "+12:00 Petropavlovsk-Kamchatski Time - Petropavlovsk-Kamchatsky, Yelizovo, Vilyuchinsk, Anadyr", - "value": "Asia/Kamchatka" - }, - { - "name": "+12:00 Tuvalu Time - Funafuti", - "value": "Pacific/Funafuti" - }, - { - "name": "+12:00 Wake Island Time - Wake", - "value": "Pacific/Wake" - }, - { - "name": "+12:00 Wallis & Futuna Time - Mata-Utu", - "value": "Pacific/Wallis" - }, - { - "name": "+12:45 Chatham Time - Chatham", - "value": "Pacific/Chatham" - }, - { - "name": "+13:00 Apia Time - Apia", - "value": "Pacific/Apia" - }, - { - "name": "+13:00 Phoenix Islands Time - Kanton", - "value": "Pacific/Kanton" - }, - { - "name": "+13:00 Tokelau Time - Fakaofo", - "value": "Pacific/Fakaofo" - }, - { - "name": "+13:00 Tonga Time - Nuku‘alofa", - "value": "Pacific/Tongatapu" - }, - { - "name": "+14:00 Line Islands Time - Kiritimati", - "value": "Pacific/Kiritimati" - } + { + "name": "-11:00 Niue Time - Alofi", + "value": "Pacific/Niue" + }, + { + "name": "-11:00 Samoa Time - Midway", + "value": "Pacific/Midway" + }, + { + "name": "-11:00 Samoa Time - Pago Pago", + "value": "Pacific/Pago_Pago" + }, + { + "name": "-10:00 Cook Islands Time - Avarua", + "value": "Pacific/Rarotonga" + }, + { + "name": "-10:00 Hawaii-Aleutian Time - Adak", + "value": "America/Adak" + }, + { + "name": "-10:00 Hawaii-Aleutian Time - Honolulu, East Honolulu, Pearl City, Hilo", + "value": "Pacific/Honolulu" + }, + { + "name": "-10:00 Tahiti Time - Faaa, Papeete, Punaauia", + "value": "Pacific/Tahiti" + }, + { + "name": "-09:30 Marquesas Time - Marquesas", + "value": "Pacific/Marquesas" + }, + { + "name": "-09:00 Alaska Time - Anchorage, Juneau, Fairbanks, Eagle River", + "value": "America/Anchorage" + }, + { + "name": "-09:00 Gambier Time - Gambier", + "value": "Pacific/Gambier" + }, + { + "name": "-08:00 Pacific Time - Los Angeles, San Diego, San Jose, San Francisco", + "value": "America/Los_Angeles" + }, + { + "name": "-08:00 Pacific Time - Tijuana, Mexicali, Ensenada, Rosarito", + "value": "America/Tijuana" + }, + { + "name": "-08:00 Pacific Time - Vancouver, Surrey, Okanagan, Victoria", + "value": "America/Vancouver" + }, + { + "name": "-08:00 Pitcairn Time - Adamstown", + "value": "Pacific/Pitcairn" + }, + { + "name": "-07:00 Mexican Pacific Time - Hermosillo, Culiacán, Ciudad Obregón, Mazatlán", + "value": "America/Hermosillo" + }, + { + "name": "-07:00 Mountain Time - Calgary, Edmonton, Red Deer, Sherwood Park", + "value": "America/Edmonton" + }, + { + "name": "-07:00 Mountain Time - Ciudad Juárez", + "value": "America/Ciudad_Juarez" + }, + { + "name": "-07:00 Mountain Time - Denver, El Paso, Albuquerque, Colorado Springs", + "value": "America/Denver" + }, + { + "name": "-07:00 Mountain Time - Phoenix, Tucson, Mesa, Chandler", + "value": "America/Phoenix" + }, + { + "name": "-07:00 Yukon Time - Whitehorse, Fort St. John, Creston, Dawson", + "value": "America/Whitehorse" + }, + { + "name": "-06:00 Central Time - Belize City, San Ignacio, San Pedro, Orange Walk", + "value": "America/Belize" + }, + { + "name": "-06:00 Central Time - Chicago, Houston, San Antonio, Dallas", + "value": "America/Chicago" + }, + { + "name": "-06:00 Central Time - Guatemala City, Villa Nueva, Mixco, Cobán", + "value": "America/Guatemala" + }, + { + "name": "-06:00 Central Time - Managua, León, Masaya, Chinandega", + "value": "America/Managua" + }, + { + "name": "-06:00 Central Time - Mexico City, Iztapalapa, León de los Aldama, Puebla", + "value": "America/Mexico_City" + }, + { + "name": "-06:00 Central Time - Reynosa, Heroica Matamoros, Nuevo Laredo, Piedras Negras", + "value": "America/Matamoros" + }, + { + "name": "-06:00 Central Time - San José, Limón, San Francisco, Alajuela", + "value": "America/Costa_Rica" + }, + { + "name": "-06:00 Central Time - San Salvador, Soyapango, San Miguel, Santa Ana", + "value": "America/El_Salvador" + }, + { + "name": "-06:00 Central Time - Saskatoon, Regina, Prince Albert, Moose Jaw", + "value": "America/Regina" + }, + { + "name": "-06:00 Central Time - Tegucigalpa, San Pedro Sula, La Ceiba, Choloma", + "value": "America/Tegucigalpa" + }, + { + "name": "-06:00 Central Time - Winnipeg, Brandon, Steinbach, Kenora", + "value": "America/Winnipeg" + }, + { + "name": "-06:00 Easter Island Time - Easter", + "value": "Pacific/Easter" + }, + { + "name": "-06:00 Galapagos Time - Galapagos", + "value": "Pacific/Galapagos" + }, + { + "name": "-05:00 Acre Time - Rio Branco, Cruzeiro do Sul, Senador Guiomard, Sena Madureira", + "value": "America/Rio_Branco" + }, + { + "name": "-05:00 Colombia Time - Bogotá, Cali, Medellín, Barranquilla", + "value": "America/Bogota" + }, + { + "name": "-05:00 Cuba Time - Havana, Santiago de Cuba, Camagüey, Holguín", + "value": "America/Havana" + }, + { + "name": "-05:00 Eastern Time - Atikokan", + "value": "America/Atikokan" + }, + { + "name": "-05:00 Eastern Time - Cancún, Chetumal, Playa del Carmen, Cozumel", + "value": "America/Cancun" + }, + { + "name": "-05:00 Eastern Time - Cockburn Town", + "value": "America/Grand_Turk" + }, + { + "name": "-05:00 Eastern Time - George Town, West Bay", + "value": "America/Cayman" + }, + { + "name": "-05:00 Eastern Time - Kingston, New Kingston, Spanish Town, Portmore", + "value": "America/Jamaica" + }, + { + "name": "-05:00 Eastern Time - Nassau, Lucaya, Freeport", + "value": "America/Nassau" + }, + { + "name": "-05:00 Eastern Time - New York City, Brooklyn, Queens, Philadelphia", + "value": "America/New_York" + }, + { + "name": "-05:00 Eastern Time - Panamá, San Miguelito, Juan Díaz, David", + "value": "America/Panama" + }, + { + "name": "-05:00 Eastern Time - Port-au-Prince, Carrefour, Delmas 73, Port-de-Paix", + "value": "America/Port-au-Prince" + }, + { + "name": "-05:00 Eastern Time - Toronto, Montréal, Ottawa, Mississauga", + "value": "America/Toronto" + }, + { + "name": "-05:00 Ecuador Time - Quito, Guayaquil, Cuenca, Santo Domingo de los Colorados", + "value": "America/Guayaquil" + }, + { + "name": "-05:00 Peru Time - Lima, Callao, Arequipa, Trujillo", + "value": "America/Lima" + }, + { + "name": "-04:00 Amazon Time - Manaus, Campo Grande, Cuiabá, Porto Velho", + "value": "America/Manaus" + }, + { + "name": "-04:00 Atlantic Time - Basseterre", + "value": "America/St_Kitts" + }, + { + "name": "-04:00 Atlantic Time - Blanc-Sablon", + "value": "America/Blanc-Sablon" + }, + { + "name": "-04:00 Atlantic Time - Brades, Plymouth", + "value": "America/Montserrat" + }, + { + "name": "-04:00 Atlantic Time - Bridgetown", + "value": "America/Barbados" + }, + { + "name": "-04:00 Atlantic Time - Castries", + "value": "America/St_Lucia" + }, + { + "name": "-04:00 Atlantic Time - Chaguanas, Mon Repos, San Fernando, Port of Spain", + "value": "America/Port_of_Spain" + }, + { + "name": "-04:00 Atlantic Time - Fort-de-France, Le Lamentin, Le Robert, Sainte-Marie", + "value": "America/Martinique" + }, + { + "name": "-04:00 Atlantic Time - Gustavia", + "value": "America/St_Barthelemy" + }, + { + "name": "-04:00 Atlantic Time - Halifax, Moncton, Sydney, Dartmouth", + "value": "America/Halifax" + }, + { + "name": "-04:00 Atlantic Time - Hamilton", + "value": "Atlantic/Bermuda" + }, + { + "name": "-04:00 Atlantic Time - Kingstown, Kingstown Park", + "value": "America/St_Vincent" + }, + { + "name": "-04:00 Atlantic Time - Kralendijk", + "value": "America/Kralendijk" + }, + { + "name": "-04:00 Atlantic Time - Les Abymes, Baie-Mahault, Le Gosier, Petit-Bourg", + "value": "America/Guadeloupe" + }, + { + "name": "-04:00 Atlantic Time - Marigot", + "value": "America/Marigot" + }, + { + "name": "-04:00 Atlantic Time - Oranjestad, Tanki Leendert, San Nicolas", + "value": "America/Aruba" + }, + { + "name": "-04:00 Atlantic Time - Philipsburg", + "value": "America/Lower_Princes" + }, + { + "name": "-04:00 Atlantic Time - Road Town", + "value": "America/Tortola" + }, + { + "name": "-04:00 Atlantic Time - Roseau", + "value": "America/Dominica" + }, + { + "name": "-04:00 Atlantic Time - Saint Croix, Charlotte Amalie", + "value": "America/St_Thomas" + }, + { + "name": "-04:00 Atlantic Time - Saint George's", + "value": "America/Grenada" + }, + { + "name": "-04:00 Atlantic Time - Saint John’s", + "value": "America/Antigua" + }, + { + "name": "-04:00 Atlantic Time - San Juan, Bayamón, Carolina, Ponce", + "value": "America/Puerto_Rico" + }, + { + "name": "-04:00 Atlantic Time - Santo Domingo, Santiago de los Caballeros, Santo Domingo Oeste, Santo Domingo Este", + "value": "America/Santo_Domingo" + }, + { + "name": "-04:00 Atlantic Time - The Valley", + "value": "America/Anguilla" + }, + { + "name": "-04:00 Atlantic Time - Thule", + "value": "America/Thule" + }, + { + "name": "-04:00 Atlantic Time - Willemstad", + "value": "America/Curacao" + }, + { + "name": "-04:00 Bolivia Time - La Paz, Santa Cruz de la Sierra, Cochabamba, Sucre", + "value": "America/La_Paz" + }, + { + "name": "-04:00 Chile Time - Santiago, Puente Alto, Antofagasta, Viña del Mar", + "value": "America/Santiago" + }, + { + "name": "-04:00 Guyana Time - Georgetown, Linden, New Amsterdam", + "value": "America/Guyana" + }, + { + "name": "-04:00 Paraguay Time - Asunción, Ciudad del Este, San Lorenzo, Capiatá", + "value": "America/Asuncion" + }, + { + "name": "-04:00 Venezuela Time - Caracas, Maracaibo, Maracay, Valencia", + "value": "America/Caracas" + }, + { + "name": "-03:30 Newfoundland Time - St. John's, Mount Pearl, Corner Brook, Conception Bay South", + "value": "America/St_Johns" + }, + { + "name": "-03:00 Argentina Time - Buenos Aires, Córdoba, Rosario, Mar del Plata", + "value": "America/Argentina/Buenos_Aires" + }, + { + "name": "-03:00 Brasilia Time - São Paulo, Rio de Janeiro, Belo Horizonte, Salvador", + "value": "America/Sao_Paulo" + }, + { + "name": "-03:00 Chile Time - Palmer, Rothera", + "value": "Antarctica/Palmer" + }, + { + "name": "-03:00 Chile Time - Punta Arenas, Puerto Natales", + "value": "America/Punta_Arenas" + }, + { + "name": "-03:00 Falkland Islands Time - Stanley", + "value": "Atlantic/Stanley" + }, + { + "name": "-03:00 French Guiana Time - Cayenne, Matoury, Saint-Laurent-du-Maroni, Kourou", + "value": "America/Cayenne" + }, + { + "name": "-03:00 St. Pierre & Miquelon Time - Saint-Pierre", + "value": "America/Miquelon" + }, + { + "name": "-03:00 Suriname Time - Paramaribo, Lelydorp", + "value": "America/Paramaribo" + }, + { + "name": "-03:00 Uruguay Time - Montevideo, Salto, Paysandú, Las Piedras", + "value": "America/Montevideo" + }, + { + "name": "-03:00 West Greenland Time - Nuuk", + "value": "America/Nuuk" + }, + { + "name": "-02:00 Fernando de Noronha Time - Noronha", + "value": "America/Noronha" + }, + { + "name": "-02:00 South Georgia Time - Grytviken", + "value": "Atlantic/South_Georgia" + }, + { + "name": "-01:00 Azores Time - Ponta Delgada", + "value": "Atlantic/Azores" + }, + { + "name": "-01:00 Cape Verde Time - Praia, Mindelo, Santa Maria, Cova Figueira", + "value": "Atlantic/Cape_Verde" + }, + { + "name": "-01:00 East Greenland Time - Scoresbysund", + "value": "America/Scoresbysund" + }, + { + "name": "+00:00 Greenwich Mean Time - Abidjan, Abobo, Bouaké, Korhogo", + "value": "Africa/Abidjan" + }, + { + "name": "+00:00 Greenwich Mean Time - Accra, Kumasi, Tamale, Takoradi", + "value": "Africa/Accra" + }, + { + "name": "+00:00 Greenwich Mean Time - Bamako, Ségou, Sikasso, Mopti", + "value": "Africa/Bamako" + }, + { + "name": "+00:00 Greenwich Mean Time - Bissau, Bafatá", + "value": "Africa/Bissau" + }, + { + "name": "+00:00 Greenwich Mean Time - Camayenne, Conakry, Nzérékoré, Kindia", + "value": "Africa/Conakry" + }, + { + "name": "+00:00 Greenwich Mean Time - Dakar, Pikine, Touba, Thiès", + "value": "Africa/Dakar" + }, + { + "name": "+00:00 Greenwich Mean Time - Danmarkshavn", + "value": "America/Danmarkshavn" + }, + { + "name": "+00:00 Greenwich Mean Time - Douglas", + "value": "Europe/Isle_of_Man" + }, + { + "name": "+00:00 Greenwich Mean Time - Dublin, South Dublin, Cork, Limerick", + "value": "Europe/Dublin" + }, + { + "name": "+00:00 Greenwich Mean Time - Freetown, Bo, Kenema, Koidu", + "value": "Africa/Freetown" + }, + { + "name": "+00:00 Greenwich Mean Time - Jamestown", + "value": "Atlantic/St_Helena" + }, + { + "name": "+00:00 Greenwich Mean Time - Lomé, Sokodé, Kara, Atakpamé", + "value": "Africa/Lome" + }, + { + "name": "+00:00 Greenwich Mean Time - London, Birmingham, Liverpool, Glasgow", + "value": "Europe/London" + }, + { + "name": "+00:00 Greenwich Mean Time - Monrovia, Gbarnga, Kakata, Bensonville", + "value": "Africa/Monrovia" + }, + { + "name": "+00:00 Greenwich Mean Time - Nouakchott, Nouadhibou, Dar Naim, Néma", + "value": "Africa/Nouakchott" + }, + { + "name": "+00:00 Greenwich Mean Time - Ouagadougou, Bobo-Dioulasso, Koudougou, Ouahigouya", + "value": "Africa/Ouagadougou" + }, + { + "name": "+00:00 Greenwich Mean Time - Reykjavík, Kópavogur, Hafnarfjörður, Reykjanesbær", + "value": "Atlantic/Reykjavik" + }, + { + "name": "+00:00 Greenwich Mean Time - Saint Helier", + "value": "Europe/Jersey" + }, + { + "name": "+00:00 Greenwich Mean Time - Saint Peter Port", + "value": "Europe/Guernsey" + }, + { + "name": "+00:00 Greenwich Mean Time - Serekunda, Brikama, Bakau, Banjul", + "value": "Africa/Banjul" + }, + { + "name": "+00:00 Greenwich Mean Time - São Tomé", + "value": "Africa/Sao_Tome" + }, + { + "name": "+00:00 Greenwich Mean Time - Troll", + "value": "Antarctica/Troll" + }, + { + "name": "+00:00 Western European Time - Casablanca, Rabat, Fès, Sale", + "value": "Africa/Casablanca" + }, + { + "name": "+00:00 Western European Time - Laayoune, Dakhla, Boujdour", + "value": "Africa/El_Aaiun" + }, + { + "name": "+00:00 Western European Time - Las Palmas de Gran Canaria, Santa Cruz de Tenerife, La Laguna, Telde", + "value": "Atlantic/Canary" + }, + { + "name": "+00:00 Western European Time - Lisbon, Porto, Amadora, Braga", + "value": "Europe/Lisbon" + }, + { + "name": "+00:00 Western European Time - Tórshavn", + "value": "Atlantic/Faroe" + }, + { + "name": "+01:00 Central Africa Time - Windhoek, Rundu, Walvis Bay, Oshakati", + "value": "Africa/Windhoek" + }, + { + "name": "+01:00 Central European Time - Algiers, Boumerdas, Oran, Tébessa", + "value": "Africa/Algiers" + }, + { + "name": "+01:00 Central European Time - Amsterdam, Rotterdam, The Hague, Utrecht", + "value": "Europe/Amsterdam" + }, + { + "name": "+01:00 Central European Time - Andorra la Vella, les Escaldes", + "value": "Europe/Andorra" + }, + { + "name": "+01:00 Central European Time - Belgrade, Niš, Novi Sad, Zemun", + "value": "Europe/Belgrade" + }, + { + "name": "+01:00 Central European Time - Berlin, Hamburg, Munich, Köln", + "value": "Europe/Berlin" + }, + { + "name": "+01:00 Central European Time - Bratislava, Košice, Nitra, Prešov", + "value": "Europe/Bratislava" + }, + { + "name": "+01:00 Central European Time - Brussels, Antwerpen, Gent, Charleroi", + "value": "Europe/Brussels" + }, + { + "name": "+01:00 Central European Time - Budapest, Debrecen, Szeged, Miskolc", + "value": "Europe/Budapest" + }, + { + "name": "+01:00 Central European Time - Copenhagen, Århus, Odense, Aalborg", + "value": "Europe/Copenhagen" + }, + { + "name": "+01:00 Central European Time - Gibraltar", + "value": "Europe/Gibraltar" + }, + { + "name": "+01:00 Central European Time - Ljubljana, Maribor, Kranj, Celje", + "value": "Europe/Ljubljana" + }, + { + "name": "+01:00 Central European Time - Longyearbyen", + "value": "Arctic/Longyearbyen" + }, + { + "name": "+01:00 Central European Time - Luxembourg, Esch-sur-Alzette, Dudelange", + "value": "Europe/Luxembourg" + }, + { + "name": "+01:00 Central European Time - Madrid, Barcelona, Valencia, Sevilla", + "value": "Europe/Madrid" + }, + { + "name": "+01:00 Central European Time - Monaco, Monte-Carlo", + "value": "Europe/Monaco" + }, + { + "name": "+01:00 Central European Time - Oslo, Bergen, Trondheim, Stavanger", + "value": "Europe/Oslo" + }, + { + "name": "+01:00 Central European Time - Paris, Marseille, Lyon, Toulouse", + "value": "Europe/Paris" + }, + { + "name": "+01:00 Central European Time - Podgorica, Nikšić, Herceg Novi, Pljevlja", + "value": "Europe/Podgorica" + }, + { + "name": "+01:00 Central European Time - Prague, Brno, Ostrava, Pilsen", + "value": "Europe/Prague" + }, + { + "name": "+01:00 Central European Time - Rome, Milan, Naples, Turin", + "value": "Europe/Rome" + }, + { + "name": "+01:00 Central European Time - San Marino", + "value": "Europe/San_Marino" + }, + { + "name": "+01:00 Central European Time - San Pawl il-Baħar, Birkirkara, Mosta, Sliema", + "value": "Europe/Malta" + }, + { + "name": "+01:00 Central European Time - Sarajevo, Banja Luka, Zenica, Tuzla", + "value": "Europe/Sarajevo" + }, + { + "name": "+01:00 Central European Time - Skopje, Kumanovo, Prilep, Bitola", + "value": "Europe/Skopje" + }, + { + "name": "+01:00 Central European Time - Stockholm, Göteborg, Malmö, Uppsala", + "value": "Europe/Stockholm" + }, + { + "name": "+01:00 Central European Time - Tirana, Durrës, Elbasan, Vlorë", + "value": "Europe/Tirane" + }, + { + "name": "+01:00 Central European Time - Tunis, Sfax, Sousse, Kairouan", + "value": "Africa/Tunis" + }, + { + "name": "+01:00 Central European Time - Vaduz", + "value": "Europe/Vaduz" + }, + { + "name": "+01:00 Central European Time - Vatican City", + "value": "Europe/Vatican" + }, + { + "name": "+01:00 Central European Time - Vienna, Graz, Linz, Favoriten", + "value": "Europe/Vienna" + }, + { + "name": "+01:00 Central European Time - Warsaw, Łódź, Kraków, Wrocław", + "value": "Europe/Warsaw" + }, + { + "name": "+01:00 Central European Time - Zagreb, Split, Rijeka, Osijek", + "value": "Europe/Zagreb" + }, + { + "name": "+01:00 Central European Time - Zürich, Genève, Basel, Lausanne", + "value": "Europe/Zurich" + }, + { + "name": "+01:00 West Africa Time - Bangui, Bimbo, Mbaïki, Berbérati", + "value": "Africa/Bangui" + }, + { + "name": "+01:00 West Africa Time - Bata, Malabo, Ebebiyin", + "value": "Africa/Malabo" + }, + { + "name": "+01:00 West Africa Time - Brazzaville, Pointe-Noire, Dolisie, Kayes", + "value": "Africa/Brazzaville" + }, + { + "name": "+01:00 West Africa Time - Cotonou, Abomey-Calavi, Djougou, Porto-Novo", + "value": "Africa/Porto-Novo" + }, + { + "name": "+01:00 West Africa Time - Douala, Yaoundé, Garoua, Kousséri", + "value": "Africa/Douala" + }, + { + "name": "+01:00 West Africa Time - Kinshasa, Masina, Kikwit, Mbandaka", + "value": "Africa/Kinshasa" + }, + { + "name": "+01:00 West Africa Time - Lagos, Kano, Ibadan, Port Harcourt", + "value": "Africa/Lagos" + }, + { + "name": "+01:00 West Africa Time - Libreville, Port-Gentil, Franceville, Oyem", + "value": "Africa/Libreville" + }, + { + "name": "+01:00 West Africa Time - Luanda, N’dalatando, Huambo, Lobito", + "value": "Africa/Luanda" + }, + { + "name": "+01:00 West Africa Time - N'Djamena, Moundou, Sarh, Abéché", + "value": "Africa/Ndjamena" + }, + { + "name": "+01:00 West Africa Time - Niamey, Zinder, Maradi, Agadez", + "value": "Africa/Niamey" + }, + { + "name": "+02:00 Central Africa Time - Bujumbura, Muyinga, Gitega, Ruyigi", + "value": "Africa/Bujumbura" + }, + { + "name": "+02:00 Central Africa Time - Gaborone, Francistown, Molepolole, Maun", + "value": "Africa/Gaborone" + }, + { + "name": "+02:00 Central Africa Time - Harare, Bulawayo, Chitungwiza, Mutare", + "value": "Africa/Harare" + }, + { + "name": "+02:00 Central Africa Time - Juba, Winejok, Yei, Malakal", + "value": "Africa/Juba" + }, + { + "name": "+02:00 Central Africa Time - Khartoum, Omdurman, Nyala, Port Sudan", + "value": "Africa/Khartoum" + }, + { + "name": "+02:00 Central Africa Time - Kigali, Gisenyi, Butare, Gitarama", + "value": "Africa/Kigali" + }, + { + "name": "+02:00 Central Africa Time - Lilongwe, Blantyre, Mzuzu, Zomba", + "value": "Africa/Blantyre" + }, + { + "name": "+02:00 Central Africa Time - Lubumbashi, Mbuji-Mayi, Kisangani, Kananga", + "value": "Africa/Lubumbashi" + }, + { + "name": "+02:00 Central Africa Time - Lusaka, Ndola, Kitwe, Chipata", + "value": "Africa/Lusaka" + }, + { + "name": "+02:00 Central Africa Time - Maputo, Matola, Nampula, Beira", + "value": "Africa/Maputo" + }, + { + "name": "+02:00 Eastern European Time - Athens, Thessaloníki, Pátra, Piraeus", + "value": "Europe/Athens" + }, + { + "name": "+02:00 Eastern European Time - Beirut, Ra’s Bayrūt, Tripoli, Sidon", + "value": "Asia/Beirut" + }, + { + "name": "+02:00 Eastern European Time - Bucharest, Sector 3, Iaşi, Sector 6", + "value": "Europe/Bucharest" + }, + { + "name": "+02:00 Eastern European Time - Cairo, Alexandria, Giza, Shubrā al Khaymah", + "value": "Africa/Cairo" + }, + { + "name": "+02:00 Eastern European Time - Chisinau, Tiraspol, Bălţi, Bender", + "value": "Europe/Chisinau" + }, + { + "name": "+02:00 Eastern European Time - East Jerusalem, Gaza, Khān Yūnis, Jabālyā", + "value": "Asia/Hebron" + }, + { + "name": "+02:00 Eastern European Time - Helsinki, Espoo, Tampere, Oulu", + "value": "Europe/Helsinki" + }, + { + "name": "+02:00 Eastern European Time - Kaliningrad, Chernyakhovsk, Sovetsk, Baltiysk", + "value": "Europe/Kaliningrad" + }, + { + "name": "+02:00 Eastern European Time - Kyiv, Kharkiv, Odesa, Dnipro", + "value": "Europe/Kyiv" + }, + { + "name": "+02:00 Eastern European Time - Mariehamn", + "value": "Europe/Mariehamn" + }, + { + "name": "+02:00 Eastern European Time - Nicosia, Limassol, Larnaca, Stróvolos", + "value": "Asia/Nicosia" + }, + { + "name": "+02:00 Eastern European Time - Riga, Daugavpils, Liepāja, Jelgava", + "value": "Europe/Riga" + }, + { + "name": "+02:00 Eastern European Time - Sofia, Plovdiv, Varna, Burgas", + "value": "Europe/Sofia" + }, + { + "name": "+02:00 Eastern European Time - Tallinn, Tartu, Narva, Pärnu", + "value": "Europe/Tallinn" + }, + { + "name": "+02:00 Eastern European Time - Tripoli, Benghazi, Ajdabiya, Mişrātah", + "value": "Africa/Tripoli" + }, + { + "name": "+02:00 Eastern European Time - Vilnius, Kaunas, Klaipėda, Šiauliai", + "value": "Europe/Vilnius" + }, + { + "name": "+02:00 Israel Time - Jerusalem, Tel Aviv, West Jerusalem, Haifa", + "value": "Asia/Jerusalem" + }, + { + "name": "+02:00 South Africa Time - Johannesburg, Cape Town, Durban, Soweto", + "value": "Africa/Johannesburg" + }, + { + "name": "+02:00 South Africa Time - Manzini, Mbabane, Lobamba", + "value": "Africa/Mbabane" + }, + { + "name": "+02:00 South Africa Time - Maseru, Mohale’s Hoek, Mafeteng, Leribe", + "value": "Africa/Maseru" + }, + { + "name": "+03:00 Arabian Time - Al Aḩmadī, Ḩawallī, As Sālimīyah, Şabāḩ as Sālim", + "value": "Asia/Kuwait" + }, + { + "name": "+03:00 Arabian Time - Ar Rifā‘, Manama, Al Muharraq, Dār Kulayb", + "value": "Asia/Bahrain" + }, + { + "name": "+03:00 Arabian Time - Baghdad, Al Mawşil al Jadīdah, Al Başrah al Qadīmah, Mosul", + "value": "Asia/Baghdad" + }, + { + "name": "+03:00 Arabian Time - Doha, Ar Rayyān, Umm Şalāl Muḩammad, Al Wakrah", + "value": "Asia/Qatar" + }, + { + "name": "+03:00 Arabian Time - Jeddah, Riyadh, Mecca, Medina", + "value": "Asia/Riyadh" + }, + { + "name": "+03:00 Arabian Time - Sanaa, Aden, Al Ḩudaydah, Taiz", + "value": "Asia/Aden" + }, + { + "name": "+03:00 Asia/Amman - Amman, Zarqa, Irbid, Russeifa", + "value": "Asia/Amman" + }, + { + "name": "+03:00 Asia/Damascus - Aleppo, Damascus, Homs, Latakia", + "value": "Asia/Damascus" + }, + { + "name": "+03:00 East Africa Time - Addis Ababa, Jijiga, Gondar, Mek'ele", + "value": "Africa/Addis_Ababa" + }, + { + "name": "+03:00 East Africa Time - Antananarivo, Toamasina, Antsirabe, Mahajanga", + "value": "Indian/Antananarivo" + }, + { + "name": "+03:00 East Africa Time - Asmara, Keren, Massawa, Assab", + "value": "Africa/Asmara" + }, + { + "name": "+03:00 East Africa Time - Dar es Salaam, Mwanza, Zanzibar, Arusha", + "value": "Africa/Dar_es_Salaam" + }, + { + "name": "+03:00 East Africa Time - Djibouti, 'Ali Sabieh, Tadjourah, Obock", + "value": "Africa/Djibouti" + }, + { + "name": "+03:00 East Africa Time - Kampala, Gulu, Lira, Mbarara", + "value": "Africa/Kampala" + }, + { + "name": "+03:00 East Africa Time - Mamoudzou, Koungou, Dzaoudzi", + "value": "Indian/Mayotte" + }, + { + "name": "+03:00 East Africa Time - Mogadishu, Hargeysa, Berbera, Kismayo", + "value": "Africa/Mogadishu" + }, + { + "name": "+03:00 East Africa Time - Moroni, Moutsamoudou", + "value": "Indian/Comoro" + }, + { + "name": "+03:00 East Africa Time - Nairobi, Kakamega, Mombasa, Ruiru", + "value": "Africa/Nairobi" + }, + { + "name": "+03:00 Moscow Time - Minsk, Homyel', Hrodna, Mahilyow", + "value": "Europe/Minsk" + }, + { + "name": "+03:00 Moscow Time - Moscow, Saint Petersburg, Nizhniy Novgorod, Kazan", + "value": "Europe/Moscow" + }, + { + "name": "+03:00 Moscow Time - Sevastopol, Simferopol, Kerch, Yevpatoriya", + "value": "Europe/Simferopol" + }, + { + "name": "+03:00 Syowa Time - Syowa", + "value": "Antarctica/Syowa" + }, + { + "name": "+03:00 Turkey Time - Istanbul, Ankara, Bursa, İzmir", + "value": "Europe/Istanbul" + }, + { + "name": "+03:30 Iran Time - Tehran, Mashhad, Isfahan, Karaj", + "value": "Asia/Tehran" + }, + { + "name": "+04:00 Armenia Time - Yerevan, Gyumri, Vanadzor, Vagharshapat", + "value": "Asia/Yerevan" + }, + { + "name": "+04:00 Azerbaijan Time - Baku, Sumqayıt, Ganja, Lankaran", + "value": "Asia/Baku" + }, + { + "name": "+04:00 Georgia Time - Tbilisi, Batumi, Kutaisi, Rustavi", + "value": "Asia/Tbilisi" + }, + { + "name": "+04:00 Gulf Time - Dubai, Abu Dhabi, Sharjah, Al Ain City", + "value": "Asia/Dubai" + }, + { + "name": "+04:00 Gulf Time - Muscat, Seeb, Bawshar, ‘Ibrī", + "value": "Asia/Muscat" + }, + { + "name": "+04:00 Mauritius Time - Port Louis, Vacoas, Beau Bassin-Rose Hill, Curepipe", + "value": "Indian/Mauritius" + }, + { + "name": "+04:00 Réunion Time - Saint-Denis, Saint-Paul, Le Tampon, Saint-Pierre", + "value": "Indian/Reunion" + }, + { + "name": "+04:00 Samara Time - Samara, Saratov, Tolyatti, Izhevsk", + "value": "Europe/Samara" + }, + { + "name": "+04:00 Seychelles Time - Victoria", + "value": "Indian/Mahe" + }, + { + "name": "+04:30 Afghanistan Time - Kabul, Herāt, Mazār-e Sharīf, Kandahār", + "value": "Asia/Kabul" + }, + { + "name": "+05:00 French Southern & Antarctic Time - Port-aux-Français", + "value": "Indian/Kerguelen" + }, + { + "name": "+05:00 Maldives Time - Male", + "value": "Indian/Maldives" + }, + { + "name": "+05:00 Mawson Time - Mawson", + "value": "Antarctica/Mawson" + }, + { + "name": "+05:00 Pakistan Time - Karachi, Lahore, Faisalabad, Rawalpindi", + "value": "Asia/Karachi" + }, + { + "name": "+05:00 Tajikistan Time - Dushanbe, Isfara, Istaravshan, Kŭlob", + "value": "Asia/Dushanbe" + }, + { + "name": "+05:00 Turkmenistan Time - Ashgabat, Türkmenabat, Daşoguz, Mary", + "value": "Asia/Ashgabat" + }, + { + "name": "+05:00 Uzbekistan Time - Tashkent, Namangan, Samarkand, Andijon", + "value": "Asia/Tashkent" + }, + { + "name": "+05:00 West Kazakhstan Time - Aktobe, Kyzylorda, Oral, Atyrau", + "value": "Asia/Aqtobe" + }, + { + "name": "+05:00 Yekaterinburg Time - Yekaterinburg, Chelyabinsk, Ufa, Perm", + "value": "Asia/Yekaterinburg" + }, + { + "name": "+05:30 India Time - Colombo, Dehiwala-Mount Lavinia, Maharagama, Jaffna", + "value": "Asia/Colombo" + }, + { + "name": "+05:30 India Time - Mumbai, Delhi, Bengaluru, Hyderābād", + "value": "Asia/Kolkata" + }, + { + "name": "+05:45 Nepal Time - Kathmandu, Bharatpur, Pātan, Birgañj", + "value": "Asia/Kathmandu" + }, + { + "name": "+06:00 Bangladesh Time - Dhaka, Chattogram, Khulna, Rangpur", + "value": "Asia/Dhaka" + }, + { + "name": "+06:00 Bhutan Time - Thimphu, Phuntsholing, Tsirang, Punākha", + "value": "Asia/Thimphu" + }, + { + "name": "+06:00 China Time - Ürümqi, Shihezi, Korla, Aksu", + "value": "Asia/Urumqi" + }, + { + "name": "+06:00 East Kazakhstan Time - Almaty, Shymkent, Karagandy, Taraz", + "value": "Asia/Almaty" + }, + { + "name": "+06:00 Indian Ocean Time - Chagos", + "value": "Indian/Chagos" + }, + { + "name": "+06:00 Kyrgyzstan Time - Bishkek, Osh, Jalal-Abad, Karakol", + "value": "Asia/Bishkek" + }, + { + "name": "+06:00 Omsk Time - Omsk, Tara, Kalachinsk", + "value": "Asia/Omsk" + }, + { + "name": "+06:00 Vostok Time - Vostok", + "value": "Antarctica/Vostok" + }, + { + "name": "+06:30 Cocos Islands Time - West Island", + "value": "Indian/Cocos" + }, + { + "name": "+06:30 Myanmar Time - Yangon, Mandalay, Nay Pyi Taw, Mawlamyine", + "value": "Asia/Yangon" + }, + { + "name": "+07:00 Christmas Island Time - Flying Fish Cove", + "value": "Indian/Christmas" + }, + { + "name": "+07:00 Davis Time - Davis", + "value": "Antarctica/Davis" + }, + { + "name": "+07:00 Hovd Time - Ulaangom, Khovd, Ölgii, Altai", + "value": "Asia/Hovd" + }, + { + "name": "+07:00 Indochina Time - Bangkok, Samut Prakan, Mueang Nonthaburi, Chon Buri", + "value": "Asia/Bangkok" + }, + { + "name": "+07:00 Indochina Time - Ho Chi Minh City, Da Nang, Biên Hòa, Cần Thơ", + "value": "Asia/Ho_Chi_Minh" + }, + { + "name": "+07:00 Indochina Time - Phnom Penh, Takeo, Siem Reap, Battambang", + "value": "Asia/Phnom_Penh" + }, + { + "name": "+07:00 Indochina Time - Vientiane, Savannakhet, Pakse, Thakhèk", + "value": "Asia/Vientiane" + }, + { + "name": "+07:00 Novosibirsk Time - Novosibirsk, Krasnoyarsk, Barnaul, Tomsk", + "value": "Asia/Novosibirsk" + }, + { + "name": "+07:00 Western Indonesia Time - Jakarta, Surabaya, Bekasi, Bandung", + "value": "Asia/Jakarta" + }, + { + "name": "+08:00 Australian Western Time - Perth, Mandurah, Bunbury, Baldivis", + "value": "Australia/Perth" + }, + { + "name": "+08:00 Brunei Darussalam Time - Bandar Seri Begawan, Kuala Belait, Seria, Tutong", + "value": "Asia/Brunei" + }, + { + "name": "+08:00 Central Indonesia Time - Makassar, Samarinda, Denpasar, Balikpapan", + "value": "Asia/Makassar" + }, + { + "name": "+08:00 China Time - Macau", + "value": "Asia/Macau" + }, + { + "name": "+08:00 China Time - Shanghai, Beijing, Shenzhen, Guangzhou", + "value": "Asia/Shanghai" + }, + { + "name": "+08:00 Hong Kong Time - Hong Kong, Kowloon, Victoria, Tuen Mun", + "value": "Asia/Hong_Kong" + }, + { + "name": "+08:00 Irkutsk Time - Irkutsk, Ulan-Ude, Bratsk, Angarsk", + "value": "Asia/Irkutsk" + }, + { + "name": "+08:00 Malaysia Time - Kuala Lumpur, Petaling Jaya, Klang, Johor Bahru", + "value": "Asia/Kuala_Lumpur" + }, + { + "name": "+08:00 Philippine Time - Quezon City, Davao, Manila, Caloocan City", + "value": "Asia/Manila" + }, + { + "name": "+08:00 Singapore Time - Singapore, Woodlands, Geylang, Queenstown Estate", + "value": "Asia/Singapore" + }, + { + "name": "+08:00 Taipei Time - Taipei, Kaohsiung, Taichung, Tainan", + "value": "Asia/Taipei" + }, + { + "name": "+08:00 Ulaanbaatar Time - Ulan Bator, Erdenet, Darhan, Mörön", + "value": "Asia/Ulaanbaatar" + }, + { + "name": "+08:45 Australian Central Western Time - Eucla", + "value": "Australia/Eucla" + }, + { + "name": "+09:00 East Timor Time - Dili, Maliana, Suai, Likisá", + "value": "Asia/Dili" + }, + { + "name": "+09:00 Eastern Indonesia Time - Jayapura, Ambon, Sorong, Ternate", + "value": "Asia/Jayapura" + }, + { + "name": "+09:00 Japan Time - Tokyo, Yokohama, Osaka, Nagoya", + "value": "Asia/Tokyo" + }, + { + "name": "+09:00 Korean Time - Pyongyang, Hamhŭng, Namp’o, Sunch’ŏn", + "value": "Asia/Pyongyang" + }, + { + "name": "+09:00 Korean Time - Seoul, Busan, Incheon, Daegu", + "value": "Asia/Seoul" + }, + { + "name": "+09:00 Palau Time - Ngerulmud", + "value": "Pacific/Palau" + }, + { + "name": "+09:00 Yakutsk Time - Chita, Yakutsk, Blagoveshchensk, Belogorsk", + "value": "Asia/Chita" + }, + { + "name": "+09:30 Australian Central Time - Adelaide, Adelaide Hills, Mount Gambier, Morphett Vale", + "value": "Australia/Adelaide" + }, + { + "name": "+09:30 Australian Central Time - Darwin, Alice Springs, Palmerston", + "value": "Australia/Darwin" + }, + { + "name": "+10:00 Australian Eastern Time - Brisbane, Gold Coast, Logan City, Townsville", + "value": "Australia/Brisbane" + }, + { + "name": "+10:00 Australian Eastern Time - Sydney, Melbourne, Canberra, Newcastle", + "value": "Australia/Sydney" + }, + { + "name": "+10:00 Chamorro Time - Dededo Village, Yigo Village, Tamuning-Tumon-Harmon Village, Tamuning", + "value": "Pacific/Guam" + }, + { + "name": "+10:00 Chamorro Time - Saipan", + "value": "Pacific/Saipan" + }, + { + "name": "+10:00 Chuuk Time - Chuuk", + "value": "Pacific/Chuuk" + }, + { + "name": "+10:00 Dumont-d’Urville Time - DumontDUrville", + "value": "Antarctica/DumontDUrville" + }, + { + "name": "+10:00 Papua New Guinea Time - Port Moresby, Lae, Mount Hagen, Popondetta", + "value": "Pacific/Port_Moresby" + }, + { + "name": "+10:00 Vladivostok Time - Khabarovsk, Vladivostok, Khabarovsk Vtoroy, Komsomolsk-on-Amur", + "value": "Asia/Vladivostok" + }, + { + "name": "+10:30 Lord Howe Time - Lord Howe", + "value": "Australia/Lord_Howe" + }, + { + "name": "+11:00 Bougainville Time - Arawa", + "value": "Pacific/Bougainville" + }, + { + "name": "+11:00 Casey Time - Casey", + "value": "Antarctica/Casey" + }, + { + "name": "+11:00 Kosrae Time - Kosrae, Palikir - National Government Center", + "value": "Pacific/Kosrae" + }, + { + "name": "+11:00 New Caledonia Time - Nouméa, Mont-Dore, Dumbéa", + "value": "Pacific/Noumea" + }, + { + "name": "+11:00 Norfolk Island Time - Kingston", + "value": "Pacific/Norfolk" + }, + { + "name": "+11:00 Sakhalin Time - Yuzhno-Sakhalinsk, Magadan, Korsakov, Kholmsk", + "value": "Asia/Sakhalin" + }, + { + "name": "+11:00 Solomon Islands Time - Honiara", + "value": "Pacific/Guadalcanal" + }, + { + "name": "+11:00 Vanuatu Time - Port-Vila", + "value": "Pacific/Efate" + }, + { + "name": "+12:00 Fiji Time - Nasinu, Suva, Lautoka, Nadi", + "value": "Pacific/Fiji" + }, + { + "name": "+12:00 Gilbert Islands Time - Tarawa", + "value": "Pacific/Tarawa" + }, + { + "name": "+12:00 Marshall Islands Time - Majuro, Kwajalein, RMI Capitol", + "value": "Pacific/Majuro" + }, + { + "name": "+12:00 Nauru Time - Yaren", + "value": "Pacific/Nauru" + }, + { + "name": "+12:00 New Zealand Time - Auckland, Wellington, Christchurch, Manukau City", + "value": "Pacific/Auckland" + }, + { + "name": "+12:00 New Zealand Time - McMurdo", + "value": "Antarctica/McMurdo" + }, + { + "name": "+12:00 Petropavlovsk-Kamchatski Time - Petropavlovsk-Kamchatsky, Yelizovo, Vilyuchinsk, Anadyr", + "value": "Asia/Kamchatka" + }, + { + "name": "+12:00 Tuvalu Time - Funafuti", + "value": "Pacific/Funafuti" + }, + { + "name": "+12:00 Wake Island Time - Wake", + "value": "Pacific/Wake" + }, + { + "name": "+12:00 Wallis & Futuna Time - Mata-Utu", + "value": "Pacific/Wallis" + }, + { + "name": "+12:45 Chatham Time - Chatham", + "value": "Pacific/Chatham" + }, + { + "name": "+13:00 Apia Time - Apia", + "value": "Pacific/Apia" + }, + { + "name": "+13:00 Phoenix Islands Time - Kanton", + "value": "Pacific/Kanton" + }, + { + "name": "+13:00 Tokelau Time - Fakaofo", + "value": "Pacific/Fakaofo" + }, + { + "name": "+13:00 Tonga Time - Nuku‘alofa", + "value": "Pacific/Tongatapu" + }, + { + "name": "+14:00 Line Islands Time - Kiritimati", + "value": "Pacific/Kiritimati" + } ] diff --git a/web/utils/tool-call.spec.ts b/web/utils/tool-call.spec.ts index ccfb06f0cc..faffc0b9e4 100644 --- a/web/utils/tool-call.spec.ts +++ b/web/utils/tool-call.spec.ts @@ -1,9 +1,9 @@ +import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' /** * Test suite for tool call utility functions * Tests detection of function/tool call support in AI models */ import { supportFunctionCall } from './tool-call' -import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' describe('tool-call', () => { /** @@ -14,7 +14,7 @@ describe('tool-call', () => { /** * Tests detection of basic tool call support */ - test('returns true when features include toolCall', () => { + it('returns true when features include toolCall', () => { const features = [ModelFeatureEnum.toolCall] expect(supportFunctionCall(features)).toBe(true) }) @@ -22,7 +22,7 @@ describe('tool-call', () => { /** * Tests detection of multi-tool call support (calling multiple tools in one request) */ - test('returns true when features include multiToolCall', () => { + it('returns true when features include multiToolCall', () => { const features = [ModelFeatureEnum.multiToolCall] expect(supportFunctionCall(features)).toBe(true) }) @@ -30,12 +30,12 @@ describe('tool-call', () => { /** * Tests detection of streaming tool call support */ - test('returns true when features include streamToolCall', () => { + it('returns true when features include streamToolCall', () => { const features = [ModelFeatureEnum.streamToolCall] expect(supportFunctionCall(features)).toBe(true) }) - test('returns true when features include multiple tool call types', () => { + it('returns true when features include multiple tool call types', () => { const features = [ ModelFeatureEnum.toolCall, ModelFeatureEnum.multiToolCall, @@ -47,7 +47,7 @@ describe('tool-call', () => { /** * Tests that tool call support is detected even when mixed with other features */ - test('returns true when features include tool call among other features', () => { + it('returns true when features include tool call among other features', () => { const features = [ ModelFeatureEnum.agentThought, ModelFeatureEnum.toolCall, @@ -59,20 +59,20 @@ describe('tool-call', () => { /** * Tests that false is returned when no tool call features are present */ - test('returns false when features do not include any tool call type', () => { + it('returns false when features do not include any tool call type', () => { const features = [ModelFeatureEnum.agentThought, ModelFeatureEnum.vision] expect(supportFunctionCall(features)).toBe(false) }) - test('returns false for empty array', () => { + it('returns false for empty array', () => { expect(supportFunctionCall([])).toBe(false) }) - test('returns false for undefined', () => { + it('returns false for undefined', () => { expect(supportFunctionCall(undefined)).toBe(false) }) - test('returns false for null', () => { + it('returns false for null', () => { expect(supportFunctionCall(null as any)).toBe(false) }) }) diff --git a/web/utils/tool-call.ts b/web/utils/tool-call.ts index 8735cc5d81..8d34cbdcb6 100644 --- a/web/utils/tool-call.ts +++ b/web/utils/tool-call.ts @@ -1,6 +1,7 @@ import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' export const supportFunctionCall = (features: ModelFeatureEnum[] = []): boolean => { - if (!features || !features.length) return false + if (!features || !features.length) + return false return features.some(feature => [ModelFeatureEnum.toolCall, ModelFeatureEnum.multiToolCall, ModelFeatureEnum.streamToolCall].includes(feature)) } diff --git a/web/utils/urlValidation.ts b/web/utils/urlValidation.ts index db6de5275a..fcc5c4b5d8 100644 --- a/web/utils/urlValidation.ts +++ b/web/utils/urlValidation.ts @@ -15,8 +15,9 @@ export function validateRedirectUrl(url: string): void { if ( error instanceof Error && error.message === 'Authorization URL must be HTTP or HTTPS' - ) + ) { throw error + } // If URL parsing fails, it's also invalid throw new Error(`Invalid URL: ${url}`) } diff --git a/web/utils/var.spec.ts b/web/utils/var.spec.ts index 6f55df0d34..9120b7a784 100644 --- a/web/utils/var.spec.ts +++ b/web/utils/var.spec.ts @@ -1,3 +1,4 @@ +import { InputVarType } from '@/app/components/workflow/types' import { checkKey, checkKeys, @@ -8,7 +9,6 @@ import { hasDuplicateStr, replaceSpaceWithUnderscoreInVarNameInput, } from './var' -import { InputVarType } from '@/app/components/workflow/types' describe('Variable Utilities', () => { describe('checkKey', () => { diff --git a/web/utils/var.ts b/web/utils/var.ts index 3181d2bbd7..c72310178d 100644 --- a/web/utils/var.ts +++ b/web/utils/var.ts @@ -1,12 +1,12 @@ -import { MARKETPLACE_URL_PREFIX, MAX_VAR_KEY_LENGTH, VAR_ITEM_TEMPLATE, VAR_ITEM_TEMPLATE_IN_WORKFLOW, getMaxVarNameLength } from '@/config' +import type { InputVar } from '@/app/components/workflow/types' import { CONTEXT_PLACEHOLDER_TEXT, HISTORY_PLACEHOLDER_TEXT, PRE_PROMPT_PLACEHOLDER_TEXT, QUERY_PLACEHOLDER_TEXT, } from '@/app/components/base/prompt-editor/constants' -import type { InputVar } from '@/app/components/workflow/types' import { InputVarType } from '@/app/components/workflow/types' +import { getMaxVarNameLength, MARKETPLACE_URL_PREFIX, MAX_VAR_KEY_LENGTH, VAR_ITEM_TEMPLATE, VAR_ITEM_TEMPLATE_IN_WORKFLOW } from '@/config' const otherAllowedRegex = /^\w+$/ @@ -97,7 +97,7 @@ export const hasDuplicateStr = (strArr: string[]) => { return !!Object.keys(strObj).find(key => strObj[key] > 1) } -const varRegex = /\{\{([a-zA-Z_]\w*)\}\}/g +const varRegex = /\{\{([a-z_]\w*)\}\}/gi export const getVars = (value: string) => { if (!value) return [] diff --git a/web/utils/zod.spec.ts b/web/utils/zod.spec.ts index f5d079e23d..e3676aa054 100644 --- a/web/utils/zod.spec.ts +++ b/web/utils/zod.spec.ts @@ -1,4 +1,4 @@ -import { ZodError, z } from 'zod' +import { z, ZodError } from 'zod' describe('Zod Features', () => { it('should support string', () => { diff --git a/web/vitest.config.ts b/web/vitest.config.ts index 77e63b49bc..2befbecba6 100644 --- a/web/vitest.config.ts +++ b/web/vitest.config.ts @@ -1,6 +1,6 @@ -import { defineConfig } from 'vitest/config' import react from '@vitejs/plugin-react' import tsconfigPaths from 'vite-tsconfig-paths' +import { defineConfig } from 'vitest/config' export default defineConfig({ plugins: [tsconfigPaths(), react() as any], diff --git a/web/vitest.setup.ts b/web/vitest.setup.ts index 5d997ac329..e4ea9ed31c 100644 --- a/web/vitest.setup.ts +++ b/web/vitest.setup.ts @@ -1,6 +1,6 @@ -import '@testing-library/jest-dom/vitest' import { cleanup } from '@testing-library/react' import { mockAnimationsApi, mockResizeObserver } from 'jsdom-testing-mocks' +import '@testing-library/jest-dom/vitest' mockResizeObserver() From 72ca3607a34631beb44855ec316a37f697b2e75b Mon Sep 17 00:00:00 2001 From: Coding On Star <447357187@qq.com> Date: Tue, 23 Dec 2025 17:48:20 +0800 Subject: [PATCH 06/15] feat: Add polyfill for Array.prototype.toSpliced method (#30031) Co-authored-by: CodingOnStar <hanxujiang@dify.ai> --- .../components/browser-initializer.spec.ts | 78 +++++++++++++++++++ web/app/components/browser-initializer.tsx | 14 ++++ 2 files changed, 92 insertions(+) create mode 100644 web/app/components/browser-initializer.spec.ts diff --git a/web/app/components/browser-initializer.spec.ts b/web/app/components/browser-initializer.spec.ts new file mode 100644 index 0000000000..f767b80826 --- /dev/null +++ b/web/app/components/browser-initializer.spec.ts @@ -0,0 +1,78 @@ +/** + * Tests for Array.prototype.toSpliced polyfill + */ + +describe('toSpliced polyfill', () => { + let originalToSpliced: typeof Array.prototype.toSpliced + + beforeEach(() => { + // Save original method + originalToSpliced = Array.prototype.toSpliced + }) + + afterEach(() => { + // Restore original method + // eslint-disable-next-line no-extend-native + Array.prototype.toSpliced = originalToSpliced + }) + + const applyPolyfill = () => { + // @ts-expect-error - intentionally deleting for test + delete Array.prototype.toSpliced + + if (!Array.prototype.toSpliced) { + // eslint-disable-next-line no-extend-native + Array.prototype.toSpliced = function <T>(this: T[], start: number, deleteCount?: number, ...items: T[]): T[] { + const copy = this.slice() + copy.splice(start, deleteCount ?? copy.length - start, ...items) + return copy + } + } + } + + it('should add toSpliced method when not available', () => { + applyPolyfill() + expect(typeof Array.prototype.toSpliced).toBe('function') + }) + + it('should return a new array without modifying the original', () => { + applyPolyfill() + const arr = [1, 2, 3, 4, 5] + const result = arr.toSpliced(1, 2) + + expect(result).toEqual([1, 4, 5]) + expect(arr).toEqual([1, 2, 3, 4, 5]) // original unchanged + }) + + it('should insert items at the specified position', () => { + applyPolyfill() + const arr: (number | string)[] = [1, 2, 3] + const result = arr.toSpliced(1, 0, 'a', 'b') + + expect(result).toEqual([1, 'a', 'b', 2, 3]) + }) + + it('should replace items at the specified position', () => { + applyPolyfill() + const arr: (number | string)[] = [1, 2, 3, 4, 5] + const result = arr.toSpliced(1, 2, 'a', 'b') + + expect(result).toEqual([1, 'a', 'b', 4, 5]) + }) + + it('should handle negative start index', () => { + applyPolyfill() + const arr = [1, 2, 3, 4, 5] + const result = arr.toSpliced(-2, 1) + + expect(result).toEqual([1, 2, 3, 5]) + }) + + it('should delete to end when deleteCount is omitted', () => { + applyPolyfill() + const arr = [1, 2, 3, 4, 5] + const result = arr.toSpliced(2) + + expect(result).toEqual([1, 2]) + }) +}) diff --git a/web/app/components/browser-initializer.tsx b/web/app/components/browser-initializer.tsx index fcae22c448..c2194ca8d4 100644 --- a/web/app/components/browser-initializer.tsx +++ b/web/app/components/browser-initializer.tsx @@ -1,5 +1,19 @@ 'use client' +// Polyfill for Array.prototype.toSpliced (ES2023, Chrome 110+) +if (!Array.prototype.toSpliced) { + // eslint-disable-next-line no-extend-native + Array.prototype.toSpliced = function <T>(this: T[], start: number, deleteCount?: number, ...items: T[]): T[] { + const copy = this.slice() + // When deleteCount is undefined (omitted), delete to end; otherwise let splice handle coercion + if (deleteCount === undefined) + copy.splice(start, copy.length - start, ...items) + else + copy.splice(start, deleteCount, ...items) + return copy + } +} + class StorageMock { data: Record<string, string> From 403adefc0725e2a28543603c73d59747d31457cb Mon Sep 17 00:00:00 2001 From: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Date: Tue, 23 Dec 2025 18:02:10 +0800 Subject: [PATCH 07/15] chore: lint require and how to import react (#30041) --- web/__tests__/check-i18n.test.ts | 6 +- web/__tests__/embedded-user-id-auth.test.tsx | 2 +- web/__tests__/embedded-user-id-store.test.tsx | 2 +- .../goto-anything/command-selector.test.tsx | 2 +- .../goto-anything/scope-command-tags.test.tsx | 2 +- .../workflow-parallel-limit.test.tsx | 2 +- web/__tests__/xss-prevention.test.tsx | 2 +- .../[appId]/annotations/page.tsx | 2 +- .../[appId]/configuration/page.tsx | 2 +- .../[appId]/develop/page.tsx | 2 +- .../(appDetailLayout)/[appId]/layout-main.tsx | 3 +- .../(appDetailLayout)/[appId]/logs/page.tsx | 2 +- .../[appId]/overview/card-view.tsx | 3 +- .../[appId]/overview/chart-view.tsx | 3 +- .../overview/long-time-range-picker.tsx | 2 +- .../[appId]/overview/page.tsx | 2 +- .../time-range-picker/date-picker.tsx | 3 +- .../overview/time-range-picker/index.tsx | 3 +- .../time-range-picker/range-selector.tsx | 3 +- .../svg-attribute-error-reproduction.spec.tsx | 2 +- .../overview/tracing/config-button.tsx | 3 +- .../[appId]/overview/tracing/config-popup.tsx | 3 +- .../[appId]/overview/tracing/field.tsx | 2 +- .../[appId]/overview/tracing/panel.tsx | 3 +- .../tracing/provider-config-modal.tsx | 3 +- .../overview/tracing/provider-panel.tsx | 3 +- .../[appId]/overview/tracing/tracing-icon.tsx | 2 +- .../app/(appDetailLayout)/layout.tsx | 3 +- .../[datasetId]/api/page.tsx | 2 +- .../documents/[documentId]/page.tsx | 2 +- .../documents/[documentId]/settings/page.tsx | 2 +- .../documents/create-from-pipeline/page.tsx | 2 +- .../[datasetId]/documents/create/page.tsx | 2 +- .../[datasetId]/documents/page.tsx | 2 +- .../[datasetId]/hitTesting/page.tsx | 2 +- .../[datasetId]/layout-main.tsx | 3 +- .../[datasetId]/settings/page.tsx | 2 +- .../datasets/(datasetDetailLayout)/layout.tsx | 2 +- .../(commonLayout)/datasets/connect/page.tsx | 2 +- .../datasets/create-from-pipeline/page.tsx | 2 +- .../(commonLayout)/datasets/create/page.tsx | 2 +- web/app/(commonLayout)/explore/apps/page.tsx | 2 +- .../explore/installed/[appId]/page.tsx | 2 +- web/app/(commonLayout)/explore/layout.tsx | 2 +- web/app/(commonLayout)/layout.tsx | 2 +- web/app/(commonLayout)/tools/page.tsx | 3 +- web/app/(shareLayout)/chat/[token]/page.tsx | 2 +- .../(shareLayout)/chatbot/[token]/page.tsx | 2 +- .../(shareLayout)/completion/[token]/page.tsx | 2 +- .../components/authenticated-layout.tsx | 3 +- .../components/external-member-sso-auth.tsx | 3 +- .../webapp-signin/normalForm.tsx | 3 +- web/app/(shareLayout)/webapp-signin/page.tsx | 3 +- .../(shareLayout)/workflow/[token]/page.tsx | 2 +- .../account-page/AvatarWithEdit.tsx | 3 +- .../account-page/email-change-modal.tsx | 3 +- web/app/account/(commonLayout)/layout.tsx | 2 +- web/app/account/oauth/authorize/page.tsx | 3 +- web/app/activate/page.tsx | 2 +- web/app/components/app-sidebar/app-info.tsx | 3 +- .../app-sidebar/app-sidebar-dropdown.tsx | 3 +- web/app/components/app-sidebar/basic.tsx | 2 +- .../app-sidebar/dataset-info/dropdown.tsx | 3 +- .../app-sidebar/dataset-info/index.spec.tsx | 2 +- .../app-sidebar/dataset-info/index.tsx | 3 +- .../app-sidebar/dataset-info/menu-item.tsx | 2 +- .../app-sidebar/dataset-info/menu.tsx | 2 +- .../app-sidebar/dataset-sidebar-dropdown.tsx | 3 +- web/app/components/app-sidebar/index.tsx | 3 +- .../components/app-sidebar/navLink.spec.tsx | 2 +- web/app/components/app-sidebar/navLink.tsx | 2 +- .../sidebar-animation-issues.spec.tsx | 2 +- .../text-squeeze-fix-verification.spec.tsx | 2 +- .../components/app-sidebar/toggle-button.tsx | 2 +- .../edit-item/index.spec.tsx | 2 +- .../add-annotation-modal/edit-item/index.tsx | 2 +- .../add-annotation-modal/index.spec.tsx | 2 +- .../annotation/add-annotation-modal/index.tsx | 3 +- .../app/annotation/batch-action.spec.tsx | 2 +- .../app/annotation/batch-action.tsx | 2 +- .../csv-downloader.spec.tsx | 2 +- .../csv-downloader.tsx | 2 +- .../csv-uploader.spec.tsx | 2 +- .../csv-uploader.tsx | 3 +- .../batch-add-annotation-modal/index.spec.tsx | 2 +- .../batch-add-annotation-modal/index.tsx | 3 +- .../index.spec.tsx | 2 +- .../index.tsx | 2 +- .../edit-annotation-modal/edit-item/index.tsx | 3 +- .../edit-annotation-modal/index.tsx | 3 +- .../app/annotation/empty-element.spec.tsx | 2 +- .../app/annotation/empty-element.tsx | 2 +- .../components/app/annotation/filter.spec.tsx | 2 +- web/app/components/app/annotation/filter.tsx | 2 +- .../app/annotation/header-opts/index.tsx | 3 +- .../components/app/annotation/index.spec.tsx | 2 +- web/app/components/app/annotation/index.tsx | 3 +- .../components/app/annotation/list.spec.tsx | 2 +- web/app/components/app/annotation/list.tsx | 3 +- .../index.spec.tsx | 2 +- .../remove-annotation-confirm-modal/index.tsx | 2 +- .../hit-history-no-data.tsx | 2 +- .../view-annotation-modal/index.spec.tsx | 2 +- .../view-annotation-modal/index.tsx | 3 +- .../app/app-publisher/features-wrapper.tsx | 3 +- .../app/app-publisher/version-info-modal.tsx | 3 +- .../base/feature-panel/index.tsx | 2 +- .../configuration/base/group-name/index.tsx | 2 +- .../base/operation-btn/index.tsx | 2 +- .../base/var-highlight/index.tsx | 2 +- .../cannot-query-dataset.spec.tsx | 2 +- .../warning-mask/cannot-query-dataset.tsx | 2 +- .../warning-mask/formatting-changed.spec.tsx | 2 +- .../base/warning-mask/formatting-changed.tsx | 2 +- .../warning-mask/has-not-set-api.spec.tsx | 2 +- .../base/warning-mask/has-not-set-api.tsx | 2 +- .../base/warning-mask/index.spec.tsx | 2 +- .../configuration/base/warning-mask/index.tsx | 2 +- .../config-prompt/advanced-prompt-input.tsx | 2 +- .../confirm-add-var/index.spec.tsx | 2 +- .../config-prompt/confirm-add-var/index.tsx | 3 +- .../conversation-history/edit-modal.spec.tsx | 2 +- .../conversation-history/edit-modal.tsx | 3 +- .../history-panel.spec.tsx | 2 +- .../conversation-history/history-panel.tsx | 2 +- .../config-prompt/index.spec.tsx | 2 +- .../app/configuration/config-prompt/index.tsx | 2 +- .../message-type-selector.spec.tsx | 2 +- .../config-prompt/message-type-selector.tsx | 2 +- .../prompt-editor-height-resize-wrap.spec.tsx | 2 +- .../prompt-editor-height-resize-wrap.tsx | 3 +- .../config-prompt/simple-prompt-input.tsx | 3 +- .../config-var/config-modal/field.tsx | 2 +- .../config-var/config-modal/index.tsx | 3 +- .../config-var/config-modal/type-select.tsx | 3 +- .../config-var/config-select/index.tsx | 3 +- .../config-var/config-string/index.tsx | 3 +- .../app/configuration/config-var/index.tsx | 3 +- .../config-var/input-type-icon.tsx | 2 +- .../configuration/config-var/modal-foot.tsx | 2 +- .../select-type-item/index.spec.tsx | 2 +- .../config-var/select-type-item/index.tsx | 2 +- .../config-var/select-var-type.tsx | 3 +- .../app/configuration/config-var/var-item.tsx | 3 +- .../config-vision/index.spec.tsx | 2 +- .../app/configuration/config-vision/index.tsx | 3 +- .../config-vision/param-config-content.tsx | 3 +- .../config/agent-setting-button.spec.tsx | 2 +- .../config/agent-setting-button.tsx | 3 +- .../config/agent/agent-setting/index.spec.tsx | 2 +- .../config/agent/agent-setting/index.tsx | 3 +- .../agent/agent-setting/item-panel.spec.tsx | 2 +- .../config/agent/agent-setting/item-panel.tsx | 2 +- .../config/agent/agent-tools/index.spec.tsx | 3 +- .../config/agent/agent-tools/index.tsx | 3 +- .../setting-built-in-tool.spec.tsx | 2 +- .../agent-tools/setting-built-in-tool.tsx | 3 +- .../config/agent/prompt-editor.tsx | 2 +- .../assistant-type-picker/index.spec.tsx | 2 +- .../config/assistant-type-picker/index.tsx | 3 +- .../config/automatic/automatic-btn.tsx | 2 +- .../config/automatic/get-automatic-res.tsx | 3 +- .../config/automatic/idea-output.tsx | 2 +- .../instruction-editor-in-workflow.tsx | 3 +- .../config/automatic/instruction-editor.tsx | 2 +- .../automatic/prompt-res-in-workflow.tsx | 2 +- .../config/automatic/prompt-res.tsx | 3 +- .../config/automatic/prompt-toast.tsx | 2 +- .../config/automatic/res-placeholder.tsx | 2 +- .../configuration/config/automatic/result.tsx | 2 +- .../config/automatic/version-selector.tsx | 3 +- .../code-generator/get-code-generator-res.tsx | 3 +- .../config/config-audio.spec.tsx | 2 +- .../app/configuration/config/config-audio.tsx | 3 +- .../config/config-document.spec.tsx | 2 +- .../configuration/config/config-document.tsx | 3 +- .../app/configuration/config/index.spec.tsx | 2 +- .../app/configuration/config/index.tsx | 2 +- .../configuration/ctrl-btn-group/index.tsx | 2 +- .../dataset-config/card-item/index.spec.tsx | 2 +- .../dataset-config/card-item/index.tsx | 3 +- .../dataset-config/context-var/index.tsx | 2 +- .../dataset-config/context-var/var-picker.tsx | 3 +- .../configuration/dataset-config/index.tsx | 3 +- .../dataset-config/select-dataset/index.tsx | 3 +- .../configuration/debug/chat-user-input.tsx | 3 +- .../app/configuration/debug/index.tsx | 3 +- .../components/app/configuration/index.tsx | 3 +- .../prompt-value-panel/index.tsx | 3 +- .../app/create-app-dialog/app-list/index.tsx | 3 +- .../app/create-from-dsl-modal/uploader.tsx | 3 +- .../app/duplicate-modal/index.spec.tsx | 2 +- .../components/app/duplicate-modal/index.tsx | 3 +- .../components/app/log-annotation/index.tsx | 3 +- web/app/components/app/log/empty-element.tsx | 2 +- web/app/components/app/log/filter.tsx | 2 +- web/app/components/app/log/index.tsx | 3 +- web/app/components/app/log/list.tsx | 3 +- web/app/components/app/log/model-info.tsx | 2 +- web/app/components/app/log/var-panel.tsx | 3 +- .../app/overview/apikey-info-panel/index.tsx | 3 +- web/app/components/app/overview/app-card.tsx | 3 +- web/app/components/app/overview/app-chart.tsx | 2 +- .../app/overview/customize/index.tsx | 2 +- .../app/overview/embedded/index.tsx | 3 +- .../app/overview/settings/index.tsx | 3 +- .../components/app/overview/trigger-card.tsx | 2 +- .../app/switch-app-modal/index.spec.tsx | 2 +- .../app/text-generate/item/index.tsx | 3 +- .../app/text-generate/saved-items/index.tsx | 2 +- .../saved-items/no-data/index.tsx | 2 +- .../app/type-selector/index.spec.tsx | 2 +- .../components/app/type-selector/index.tsx | 3 +- .../components/app/workflow-log/filter.tsx | 2 +- web/app/components/app/workflow-log/index.tsx | 3 +- web/app/components/app/workflow-log/list.tsx | 3 +- .../app/workflow-log/trigger-by-display.tsx | 2 +- web/app/components/apps/app-card.spec.tsx | 141 +++++++++--------- web/app/components/apps/app-card.tsx | 3 +- web/app/components/apps/empty.spec.tsx | 2 +- web/app/components/apps/empty.tsx | 2 +- web/app/components/apps/footer.spec.tsx | 2 +- web/app/components/apps/footer.tsx | 2 +- web/app/components/apps/index.spec.tsx | 3 +- web/app/components/apps/list.spec.tsx | 55 +++---- web/app/components/apps/new-app-card.spec.tsx | 53 ++++--- web/app/components/apps/new-app-card.tsx | 3 +- .../components/base/action-button/index.tsx | 2 +- .../base/agent-log-modal/detail.tsx | 3 +- .../base/amplitude/AmplitudeProvider.tsx | 3 +- web/app/components/base/app-icon/index.tsx | 3 +- web/app/components/base/app-unavailable.tsx | 2 +- .../base/audio-gallery/AudioPlayer.tsx | 3 +- .../components/base/audio-gallery/index.tsx | 2 +- web/app/components/base/badge/index.tsx | 2 +- web/app/components/base/block-input/index.tsx | 3 +- web/app/components/base/button/add-button.tsx | 2 +- web/app/components/base/button/index.spec.tsx | 2 +- web/app/components/base/button/index.tsx | 2 +- .../components/base/button/sync-button.tsx | 2 +- .../chat-with-history/header/operation.tsx | 3 +- .../chat-with-history/inputs-form/content.tsx | 3 +- .../chat-with-history/inputs-form/index.tsx | 2 +- .../chat-with-history/sidebar/operation.tsx | 3 +- .../sidebar/rename-modal.tsx | 3 +- .../base/chat/chat/citation/tooltip.tsx | 3 +- .../base/chat/chat/loading-anim/index.tsx | 2 +- .../base/chat/chat/thought/index.tsx | 2 +- .../chat/embedded-chatbot/header/index.tsx | 3 +- .../embedded-chatbot/inputs-form/content.tsx | 3 +- .../embedded-chatbot/inputs-form/index.tsx | 2 +- web/app/components/base/confirm/index.tsx | 3 +- .../components/base/copy-feedback/index.tsx | 3 +- web/app/components/base/copy-icon/index.tsx | 3 +- .../calendar/days-of-week.tsx | 2 +- .../date-and-time-picker/calendar/item.tsx | 2 +- .../common/option-list-item.tsx | 3 +- .../date-picker/footer.tsx | 2 +- .../date-picker/header.tsx | 2 +- .../date-picker/index.tsx | 3 +- .../time-picker/footer.tsx | 2 +- .../time-picker/header.tsx | 2 +- .../time-picker/index.spec.tsx | 2 +- .../time-picker/index.tsx | 3 +- .../time-picker/options.tsx | 2 +- .../year-and-month-picker/footer.tsx | 2 +- .../year-and-month-picker/header.tsx | 2 +- .../year-and-month-picker/options.tsx | 2 +- web/app/components/base/divider/index.tsx | 2 +- web/app/components/base/drawer-plus/index.tsx | 3 +- web/app/components/base/drawer/index.spec.tsx | 2 +- web/app/components/base/effect/index.tsx | 2 +- .../components/base/emoji-picker/Inner.tsx | 3 +- .../components/base/emoji-picker/index.tsx | 3 +- .../components/base/error-boundary/index.tsx | 3 +- .../annotation-ctrl-button.tsx | 2 +- .../annotation-reply/config-param-modal.tsx | 3 +- .../annotation-reply/config-param.tsx | 2 +- .../annotation-reply/index.tsx | 3 +- .../annotation-reply/score-slider/index.tsx | 2 +- .../annotation-reply/use-annotation-config.ts | 3 +- .../features/new-feature-panel/citation.tsx | 3 +- .../conversation-opener/index.tsx | 3 +- .../conversation-opener/modal.tsx | 3 +- .../new-feature-panel/feature-bar.tsx | 3 +- .../new-feature-panel/feature-card.tsx | 2 +- .../new-feature-panel/file-upload/index.tsx | 3 +- .../file-upload/setting-content.tsx | 3 +- .../features/new-feature-panel/follow-up.tsx | 3 +- .../new-feature-panel/image-upload/index.tsx | 3 +- .../base/features/new-feature-panel/index.tsx | 2 +- .../new-feature-panel/moderation/index.tsx | 3 +- .../new-feature-panel/more-like-this.tsx | 3 +- .../new-feature-panel/speech-to-text.tsx | 3 +- .../text-to-speech/index.tsx | 3 +- .../text-to-speech/param-config-content.tsx | 3 +- .../base/file-thumb/image-render.tsx | 2 +- web/app/components/base/file-thumb/index.tsx | 3 +- .../base/file-uploader/audio-preview.tsx | 2 +- .../base/file-uploader/file-list-in-log.tsx | 3 +- .../base/file-uploader/pdf-preview.tsx | 3 +- .../base/file-uploader/video-preview.tsx | 2 +- .../form/components/field/file-uploader.tsx | 2 +- .../field/input-type-select/option.tsx | 2 +- .../field/input-type-select/trigger.tsx | 2 +- .../form/components/field/number-input.tsx | 2 +- .../base/form/components/field/text-area.tsx | 2 +- .../base/form/components/field/text.tsx | 2 +- .../base/form/form-scenarios/base/field.tsx | 2 +- .../base/form/form-scenarios/base/index.tsx | 3 +- .../form/form-scenarios/input-field/field.tsx | 2 +- .../form/form-scenarios/node-panel/field.tsx | 2 +- web/app/components/base/ga/index.tsx | 2 +- .../components/base/icons/IconBase.spec.tsx | 2 +- .../base/icons/icon-gallery.stories.tsx | 2 +- web/app/components/base/icons/utils.ts | 2 +- .../components/base/image-gallery/index.tsx | 3 +- .../base/image-uploader/image-preview.tsx | 3 +- .../base/inline-delete-confirm/index.spec.tsx | 2 +- .../base/input-with-copy/index.spec.tsx | 2 +- .../components/base/input-with-copy/index.tsx | 3 +- web/app/components/base/input/index.spec.tsx | 2 +- web/app/components/base/input/index.tsx | 2 +- .../base/linked-apps-panel/index.tsx | 2 +- web/app/components/base/list-empty/index.tsx | 2 +- .../components/base/loading/index.spec.tsx | 2 +- web/app/components/base/loading/index.tsx | 2 +- .../base/markdown-blocks/audio-block.tsx | 3 +- .../components/base/markdown-blocks/form.tsx | 3 +- .../components/base/markdown-blocks/img.tsx | 2 +- .../components/base/markdown-blocks/link.tsx | 2 +- .../base/markdown-blocks/paragraph.tsx | 2 +- .../base/markdown-blocks/plugin-img.tsx | 3 +- .../base/markdown-blocks/plugin-paragraph.tsx | 3 +- .../base/markdown-blocks/pre-code.tsx | 3 +- .../base/markdown-blocks/think-block.tsx | 3 +- .../base/markdown-blocks/video-block.tsx | 3 +- .../base/markdown/error-boundary.tsx | 3 +- web/app/components/base/mermaid/index.tsx | 3 +- .../components/base/modal-like-wrap/index.tsx | 2 +- web/app/components/base/node-status/index.tsx | 2 +- .../base/notion-connector/index.tsx | 2 +- .../credential-selector/index.tsx | 3 +- web/app/components/base/pagination/hook.ts | 3 +- web/app/components/base/pagination/index.tsx | 2 +- .../components/base/pagination/pagination.tsx | 2 +- .../base/param-item/score-threshold-item.tsx | 2 +- .../components/base/param-item/top-k-item.tsx | 2 +- .../base/portal-to-follow-elem/index.spec.tsx | 2 +- .../base/portal-to-follow-elem/index.tsx | 3 +- .../components/base/premium-badge/index.tsx | 2 +- .../components/base/prompt-editor/index.tsx | 3 +- web/app/components/base/qrcode/index.tsx | 3 +- web/app/components/base/radio-card/index.tsx | 2 +- .../base/radio-card/simple/index.tsx | 2 +- web/app/components/base/radio/index.tsx | 2 +- web/app/components/base/radio/ui.tsx | 2 +- .../base/segmented-control/index.tsx | 2 +- web/app/components/base/select/index.tsx | 3 +- .../components/base/spinner/index.spec.tsx | 2 +- web/app/components/base/spinner/index.tsx | 2 +- web/app/components/base/svg/index.tsx | 2 +- web/app/components/base/switch/index.tsx | 3 +- web/app/components/base/tab-header/index.tsx | 2 +- .../base/tab-slider-plain/index.tsx | 2 +- .../components/base/tag-management/panel.tsx | 3 +- .../base/tag-management/trigger.tsx | 2 +- web/app/components/base/tag/index.tsx | 2 +- web/app/components/base/textarea/index.tsx | 2 +- .../timezone-label/__tests__/index.test.tsx | 2 +- .../components/base/timezone-label/index.tsx | 3 +- web/app/components/base/toast/index.spec.tsx | 2 +- web/app/components/base/toast/index.tsx | 3 +- .../components/base/tooltip/index.spec.tsx | 2 +- web/app/components/base/tooltip/index.tsx | 3 +- .../base/video-gallery/VideoPlayer.tsx | 3 +- .../components/base/video-gallery/index.tsx | 2 +- .../base/with-input-validation/index.tsx | 2 +- .../billing/annotation-full/index.tsx | 2 +- .../billing/annotation-full/modal.tsx | 2 +- .../billing/annotation-full/usage.tsx | 2 +- .../billing/apps-full-in-dialog/index.tsx | 2 +- .../components/billing/billing-page/index.tsx | 2 +- .../billing/header-billing-btn/index.tsx | 2 +- .../billing/partner-stack/index.tsx | 3 +- .../billing/plan-upgrade-modal/index.spec.tsx | 2 +- .../billing/plan-upgrade-modal/index.tsx | 3 +- .../billing/plan/assets/sandbox.spec.tsx | 2 +- .../billing/plan/assets/sandbox.tsx | 2 +- web/app/components/billing/plan/index.tsx | 3 +- web/app/components/billing/pricing/footer.tsx | 2 +- web/app/components/billing/pricing/header.tsx | 2 +- web/app/components/billing/pricing/index.tsx | 3 +- .../billing/pricing/plan-switcher/index.tsx | 2 +- .../plan-switcher/plan-range-switcher.tsx | 2 +- .../billing/pricing/plan-switcher/tab.tsx | 3 +- .../plans/cloud-plan-item/button.spec.tsx | 2 +- .../pricing/plans/cloud-plan-item/button.tsx | 2 +- .../plans/cloud-plan-item/index.spec.tsx | 2 +- .../pricing/plans/cloud-plan-item/index.tsx | 3 +- .../plans/cloud-plan-item/list/index.spec.tsx | 2 +- .../plans/cloud-plan-item/list/index.tsx | 2 +- .../plans/cloud-plan-item/list/item/index.tsx | 2 +- .../cloud-plan-item/list/item/tooltip.tsx | 2 +- .../billing/pricing/plans/index.spec.tsx | 2 +- .../self-hosted-plan-item/button.spec.tsx | 2 +- .../plans/self-hosted-plan-item/button.tsx | 3 +- .../self-hosted-plan-item/index.spec.tsx | 2 +- .../plans/self-hosted-plan-item/index.tsx | 3 +- .../self-hosted-plan-item/list/index.spec.tsx | 2 +- .../self-hosted-plan-item/list/index.tsx | 2 +- .../self-hosted-plan-item/list/item.spec.tsx | 2 +- .../plans/self-hosted-plan-item/list/item.tsx | 2 +- .../trigger-events-limit-modal/index.tsx | 2 +- .../components/billing/upgrade-btn/index.tsx | 2 +- .../billing/usage-info/apps-info.tsx | 2 +- .../components/billing/usage-info/index.tsx | 2 +- .../billing/usage-info/vector-space-info.tsx | 2 +- .../billing/vector-space-full/index.tsx | 2 +- .../custom/custom-page/index.spec.tsx | 2 +- web/app/components/datasets/api/index.tsx | 2 +- .../datasets/common/chunking-mode-label.tsx | 2 +- .../datasets/common/credential-icon.tsx | 3 +- .../datasets/common/document-file-icon.tsx | 2 +- .../common/document-picker/document-list.tsx | 3 +- .../common/document-picker/index.spec.tsx | 2 +- .../datasets/common/document-picker/index.tsx | 3 +- .../preview-document-picker.spec.tsx | 2 +- .../preview-document-picker.tsx | 3 +- .../auto-disabled-document.tsx | 3 +- .../index-failed.tsx | 3 +- .../status-with-action.tsx | 2 +- .../index.tsx | 2 +- .../datasets/common/image-list/more.tsx | 3 +- .../image-uploader-in-chunk/image-input.tsx | 2 +- .../image-input.tsx | 2 +- .../retrieval-method-config/index.spec.tsx | 2 +- .../common/retrieval-method-config/index.tsx | 3 +- .../common/retrieval-method-info/index.tsx | 2 +- .../common/retrieval-param-config/index.tsx | 3 +- .../create-from-dsl-modal/header.tsx | 2 +- .../create-from-dsl-modal/tab/index.tsx | 2 +- .../create-from-dsl-modal/tab/item.tsx | 2 +- .../create-from-dsl-modal/uploader.tsx | 3 +- .../datasets/create-from-pipeline/footer.tsx | 3 +- .../datasets/create-from-pipeline/header.tsx | 2 +- .../create-from-pipeline/list/create-card.tsx | 3 +- .../list/template-card/actions.tsx | 2 +- .../list/template-card/content.tsx | 2 +- .../details/chunk-structure-card.tsx | 2 +- .../list/template-card/details/index.tsx | 3 +- .../list/template-card/edit-pipeline-info.tsx | 3 +- .../list/template-card/index.tsx | 3 +- .../list/template-card/operations.tsx | 2 +- .../create/embedding-process/index.tsx | 3 +- .../index.spec.tsx | 2 +- .../empty-dataset-creation-modal/index.tsx | 3 +- .../datasets/create/file-preview/index.tsx | 3 +- .../datasets/create/file-uploader/index.tsx | 3 +- .../components/datasets/create/index.spec.tsx | 2 +- web/app/components/datasets/create/index.tsx | 3 +- .../create/notion-page-preview/index.tsx | 3 +- .../datasets/create/step-one/index.tsx | 3 +- .../datasets/create/step-one/upgrade-card.tsx | 3 +- .../datasets/create/step-three/index.tsx | 2 +- .../datasets/create/step-two/index.tsx | 3 +- .../step-two/language-select/index.spec.tsx | 2 +- .../create/step-two/language-select/index.tsx | 2 +- .../step-two/preview-item/index.spec.tsx | 2 +- .../create/step-two/preview-item/index.tsx | 2 +- .../create/stop-embedding-modal/index.tsx | 2 +- .../website/base/checkbox-with-label.tsx | 2 +- .../website/base/crawled-result-item.tsx | 3 +- .../create/website/base/crawled-result.tsx | 3 +- .../datasets/create/website/base/crawling.tsx | 2 +- .../create/website/base/error-message.tsx | 2 +- .../datasets/create/website/base/field.tsx | 2 +- .../datasets/create/website/base/header.tsx | 2 +- .../datasets/create/website/base/input.tsx | 3 +- .../create/website/base/options-wrap.tsx | 3 +- .../create/website/base/url-input.tsx | 3 +- .../create/website/firecrawl/index.tsx | 3 +- .../create/website/firecrawl/options.tsx | 3 +- .../datasets/create/website/index.tsx | 3 +- .../website/jina-reader/base/url-input.tsx | 3 +- .../create/website/jina-reader/index.tsx | 3 +- .../create/website/jina-reader/options.tsx | 3 +- .../datasets/create/website/no-data.tsx | 2 +- .../datasets/create/website/preview.tsx | 2 +- .../create/website/watercrawl/index.tsx | 3 +- .../create/website/watercrawl/options.tsx | 3 +- .../actions/index.spec.tsx | 2 +- .../create-from-pipeline/actions/index.tsx | 3 +- .../data-source-options/index.spec.tsx | 2 +- .../data-source-options/option-card.tsx | 2 +- .../base/credential-selector/index.spec.tsx | 2 +- .../base/credential-selector/index.tsx | 3 +- .../base/credential-selector/item.tsx | 3 +- .../base/credential-selector/list.tsx | 2 +- .../base/credential-selector/trigger.tsx | 2 +- .../data-source/base/header.spec.tsx | 2 +- .../data-source/base/header.tsx | 2 +- .../data-source/local-file/index.tsx | 3 +- .../online-documents/index.spec.tsx | 2 +- .../page-selector/index.spec.tsx | 2 +- .../online-documents/page-selector/item.tsx | 2 +- .../data-source/online-documents/title.tsx | 2 +- .../file-list/header/breadcrumbs/bucket.tsx | 3 +- .../file-list/header/breadcrumbs/drive.tsx | 2 +- .../breadcrumbs/dropdown/index.spec.tsx | 2 +- .../header/breadcrumbs/dropdown/index.tsx | 3 +- .../header/breadcrumbs/dropdown/item.tsx | 3 +- .../header/breadcrumbs/dropdown/menu.tsx | 2 +- .../header/breadcrumbs/index.spec.tsx | 2 +- .../file-list/header/breadcrumbs/index.tsx | 3 +- .../file-list/header/breadcrumbs/item.tsx | 3 +- .../file-list/header/index.spec.tsx | 2 +- .../online-drive/file-list/header/index.tsx | 2 +- .../online-drive/file-list/index.spec.tsx | 2 +- .../file-list/list/empty-folder.tsx | 2 +- .../file-list/list/empty-search-result.tsx | 2 +- .../online-drive/file-list/list/file-icon.tsx | 3 +- .../file-list/list/index.spec.tsx | 2 +- .../online-drive/file-list/list/index.tsx | 3 +- .../online-drive/file-list/list/item.tsx | 3 +- .../data-source/online-drive/header.tsx | 2 +- .../data-source/online-drive/index.spec.tsx | 2 +- .../base/checkbox-with-label.tsx | 2 +- .../base/crawled-result-item.tsx | 3 +- .../website-crawl/base/crawled-result.tsx | 3 +- .../website-crawl/base/crawling.tsx | 2 +- .../website-crawl/base/error-message.tsx | 2 +- .../website-crawl/base/index.spec.tsx | 2 +- .../website-crawl/base/options/index.spec.tsx | 2 +- .../data-source/website-crawl/index.spec.tsx | 2 +- .../data-source/website-crawl/index.tsx | 3 +- .../create-from-pipeline/left-header.tsx | 2 +- .../preview/chunk-preview.spec.tsx | 2 +- .../preview/chunk-preview.tsx | 3 +- .../preview/file-preview.spec.tsx | 2 +- .../preview/file-preview.tsx | 3 +- .../create-from-pipeline/preview/loading.tsx | 2 +- .../preview/online-document-preview.spec.tsx | 2 +- .../preview/online-document-preview.tsx | 3 +- .../preview/web-preview.spec.tsx | 2 +- .../preview/web-preview.tsx | 2 +- .../process-documents/actions.tsx | 2 +- .../process-documents/components.spec.tsx | 2 +- .../process-documents/header.tsx | 2 +- .../process-documents/index.spec.tsx | 2 +- .../process-documents/index.tsx | 2 +- .../embedding-process/index.spec.tsx | 2 +- .../processing/embedding-process/index.tsx | 3 +- .../embedding-process/rule-detail.spec.tsx | 2 +- .../embedding-process/rule-detail.tsx | 3 +- .../processing/index.spec.tsx | 2 +- .../create-from-pipeline/processing/index.tsx | 2 +- .../create-from-pipeline/step-indicator.tsx | 2 +- .../detail/batch-modal/csv-downloader.tsx | 2 +- .../detail/batch-modal/csv-uploader.tsx | 3 +- .../documents/detail/batch-modal/index.tsx | 3 +- .../detail/completed/child-segment-detail.tsx | 3 +- .../completed/common/action-buttons.tsx | 3 +- .../detail/completed/common/add-another.tsx | 2 +- .../detail/completed/common/batch-action.tsx | 2 +- .../detail/completed/common/chunk-content.tsx | 3 +- .../documents/detail/completed/common/dot.tsx | 2 +- .../detail/completed/common/drawer.tsx | 3 +- .../detail/completed/common/empty.tsx | 2 +- .../completed/common/full-screen-drawer.tsx | 2 +- .../detail/completed/common/keywords.tsx | 2 +- .../completed/common/regeneration-modal.tsx | 3 +- .../completed/common/segment-index-tag.tsx | 3 +- .../documents/detail/completed/common/tag.tsx | 2 +- .../detail/completed/display-toggle.tsx | 2 +- .../documents/detail/completed/index.tsx | 3 +- .../completed/segment-card/chunk-content.tsx | 2 +- .../completed/segment-card/index.spec.tsx | 2 +- .../detail/completed/segment-card/index.tsx | 3 +- .../detail/completed/segment-detail.tsx | 3 +- .../detail/completed/segment-list.tsx | 3 +- .../skeleton/full-doc-list-skeleton.tsx | 2 +- .../skeleton/general-list-skeleton.tsx | 2 +- .../skeleton/paragraph-list-skeleton.tsx | 2 +- .../skeleton/parent-chunk-card-skeleton.tsx | 2 +- .../detail/completed/status-item.tsx | 2 +- .../documents/detail/embedding/index.tsx | 3 +- .../detail/embedding/skeleton/index.tsx | 2 +- .../datasets/documents/detail/index.tsx | 3 +- .../documents/detail/metadata/index.tsx | 3 +- .../documents/detail/segment-add/index.tsx | 3 +- .../detail/settings/document-settings.tsx | 3 +- .../documents/detail/settings/index.tsx | 2 +- .../pipeline-settings/left-header.tsx | 3 +- .../process-documents/actions.tsx | 2 +- .../components/datasets/documents/index.tsx | 3 +- .../components/datasets/documents/list.tsx | 3 +- .../datasets/documents/operations.tsx | 3 +- .../datasets/documents/rename-modal.tsx | 3 +- .../datasets/documents/status-item/index.tsx | 3 +- .../external-api/external-api-modal/Form.tsx | 2 +- .../external-api/external-api-panel/index.tsx | 2 +- .../external-knowledge-api-card/index.tsx | 3 +- .../connector/index.tsx | 3 +- .../create/ExternalApiSelect.tsx | 3 +- .../create/ExternalApiSelection.tsx | 3 +- .../create/KnowledgeBaseInfo.tsx | 2 +- .../create/RetrievalSettings.tsx | 2 +- .../create/index.spec.tsx | 2 +- .../components/datasets/extra-info/index.tsx | 2 +- .../datasets/extra-info/service-api/card.tsx | 3 +- .../datasets/extra-info/service-api/index.tsx | 3 +- .../datasets/extra-info/statistics.tsx | 2 +- .../components/child-chunks-item.tsx | 2 +- .../components/chunk-detail-modal.tsx | 3 +- .../hit-testing/components/empty-records.tsx | 2 +- .../datasets/hit-testing/components/mask.tsx | 2 +- .../components/query-input/index.tsx | 3 +- .../components/query-input/textarea.tsx | 2 +- .../hit-testing/components/records.tsx | 3 +- .../components/result-item-external.tsx | 2 +- .../components/result-item-footer.tsx | 2 +- .../components/result-item-meta.tsx | 2 +- .../hit-testing/components/result-item.tsx | 3 +- .../datasets/hit-testing/components/score.tsx | 2 +- .../components/datasets/hit-testing/index.tsx | 3 +- .../hit-testing/modify-retrieval-modal.tsx | 3 +- .../datasets/list/dataset-card/index.tsx | 3 +- .../list/dataset-card/operation-item.tsx | 2 +- .../datasets/list/dataset-card/operations.tsx | 2 +- .../datasets/list/dataset-footer/index.tsx | 2 +- .../datasets/list/new-dataset-card/index.tsx | 2 +- .../datasets/list/new-dataset-card/option.tsx | 2 +- .../datasets/metadata/add-metadata-button.tsx | 2 +- .../metadata/edit-metadata-batch/add-row.tsx | 2 +- .../metadata/edit-metadata-batch/edit-row.tsx | 2 +- .../edit-metadata-batch/edited-beacon.tsx | 3 +- .../edit-metadata-batch/input-combined.tsx | 2 +- .../input-has-set-multiple-value.tsx | 2 +- .../metadata/edit-metadata-batch/label.tsx | 2 +- .../metadata/edit-metadata-batch/modal.tsx | 3 +- .../metadata-dataset/create-content.tsx | 3 +- .../create-metadata-modal.tsx | 2 +- .../dataset-metadata-drawer.tsx | 3 +- .../metadata/metadata-dataset/field.tsx | 2 +- .../select-metadata-modal.tsx | 3 +- .../metadata-dataset/select-metadata.tsx | 3 +- .../metadata/metadata-document/field.tsx | 2 +- .../metadata/metadata-document/index.tsx | 2 +- .../metadata/metadata-document/info-group.tsx | 2 +- .../metadata/metadata-document/no-data.tsx | 2 +- .../datasets/no-linked-apps-panel.tsx | 2 +- .../settings/chunk-structure/index.tsx | 2 +- .../settings/index-method/keyword-number.tsx | 3 +- .../datasets/settings/option-card.tsx | 2 +- .../settings/permission-selector/index.tsx | 3 +- .../permission-selector/member-item.tsx | 2 +- .../permission-selector/permission-item.tsx | 2 +- .../develop/secret-key/input-copy.tsx | 3 +- .../explore/app-card/index.spec.tsx | 2 +- web/app/components/explore/app-list/index.tsx | 3 +- web/app/components/explore/category.tsx | 2 +- .../explore/create-app-modal/index.spec.tsx | 2 +- .../explore/create-app-modal/index.tsx | 3 +- web/app/components/explore/index.tsx | 3 +- .../explore/installed-app/index.tsx | 3 +- .../explore/item-operation/index.tsx | 3 +- .../explore/sidebar/app-nav-item/index.tsx | 3 +- web/app/components/explore/sidebar/index.tsx | 3 +- .../actions/commands/account.tsx | 2 +- .../actions/commands/community.tsx | 2 +- .../goto-anything/actions/commands/docs.tsx | 2 +- .../goto-anything/actions/commands/forum.tsx | 2 +- .../goto-anything/actions/commands/theme.tsx | 2 +- .../goto-anything/actions/commands/zen.tsx | 2 +- .../goto-anything/command-selector.spec.tsx | 2 +- .../components/goto-anything/context.spec.tsx | 2 +- web/app/components/goto-anything/context.tsx | 3 +- .../components/goto-anything/index.spec.tsx | 2 +- .../data-source-notion/index.tsx | 3 +- .../config-firecrawl-modal.tsx | 3 +- .../config-jina-reader-modal.tsx | 3 +- .../config-watercrawl-modal.tsx | 3 +- .../data-source-website/index.tsx | 3 +- .../data-source-page/panel/config-item.tsx | 2 +- .../data-source-page/panel/index.tsx | 2 +- .../invite-modal/role-selector.tsx | 3 +- .../invited-modal/invitation-link.tsx | 3 +- .../transfer-ownership-modal/index.tsx | 3 +- .../member-selector.tsx | 3 +- web/app/components/header/app-back/index.tsx | 3 +- .../header/github-star/index.spec.tsx | 2 +- web/app/components/header/header-wrapper.tsx | 3 +- web/app/components/header/nav/index.tsx | 3 +- web/app/components/i18n-server.tsx | 2 +- web/app/components/i18n.tsx | 3 +- .../plugins/base/badges/icon-with-tooltip.tsx | 2 +- .../plugins/base/deprecation-notice.tsx | 3 +- .../plugins/base/key-value-item.tsx | 3 +- .../plugins/card/base/description.tsx | 3 +- .../plugins/card/base/download-count.tsx | 2 +- .../plugins/card/card-more-info.tsx | 2 +- web/app/components/plugins/card/index.tsx | 2 +- .../plugins/install-plugin/base/installed.tsx | 2 +- .../install-plugin/base/loading-error.tsx | 2 +- .../plugins/install-plugin/base/loading.tsx | 2 +- .../plugins/install-plugin/base/version.tsx | 2 +- .../install-plugin/install-bundle/index.tsx | 3 +- .../install-bundle/item/github-item.tsx | 3 +- .../install-bundle/item/loaded-item.tsx | 2 +- .../install-bundle/item/marketplace-item.tsx | 2 +- .../install-bundle/item/package-item.tsx | 2 +- .../install-bundle/ready-to-install.tsx | 3 +- .../install-bundle/steps/install-multi.tsx | 3 +- .../install-bundle/steps/install.tsx | 3 +- .../install-bundle/steps/installed.tsx | 2 +- .../install-from-github/index.tsx | 3 +- .../install-from-github/steps/loaded.tsx | 3 +- .../steps/selectPackage.tsx | 2 +- .../install-from-github/steps/setURL.tsx | 2 +- .../install-from-local-package/index.tsx | 3 +- .../ready-to-install.tsx | 3 +- .../steps/install.tsx | 3 +- .../steps/uploading.tsx | 2 +- .../install-from-marketplace/index.tsx | 3 +- .../steps/install.tsx | 3 +- .../plugins/marketplace/list/card-wrapper.tsx | 3 +- .../search-box/trigger/marketplace.tsx | 2 +- .../search-box/trigger/tool-selector.tsx | 2 +- .../plugin-detail-panel/action-list.tsx | 3 +- .../agent-strategy-list.tsx | 3 +- .../app-selector/app-inputs-panel.tsx | 3 +- .../app-selector/app-picker.tsx | 3 +- .../app-selector/app-trigger.tsx | 2 +- .../app-selector/index.tsx | 3 +- .../datasource-action-list.tsx | 3 +- .../plugin-detail-panel/detail-header.tsx | 3 +- .../plugin-detail-panel/endpoint-card.tsx | 3 +- .../plugin-detail-panel/endpoint-list.tsx | 3 +- .../plugin-detail-panel/endpoint-modal.tsx | 2 +- .../plugin-detail-panel/model-list.tsx | 2 +- .../model-selector/llm-params-panel.tsx | 3 +- .../model-selector/tts-params-panel.tsx | 3 +- .../multiple-tool-selector/index.tsx | 2 +- .../operation-dropdown.tsx | 3 +- .../plugin-detail-panel/strategy-detail.tsx | 3 +- .../plugin-detail-panel/strategy-item.tsx | 3 +- .../subscription-list/create/common-modal.tsx | 3 +- .../subscription-list/create/oauth-client.tsx | 3 +- .../subscription-list/list-view.tsx | 2 +- .../subscription-list/log-viewer.tsx | 3 +- .../subscription-list/selector-view.tsx | 3 +- .../tool-selector/index.tsx | 3 +- .../tool-selector/schema-modal.tsx | 2 +- .../tool-selector/tool-credentials-form.tsx | 3 +- .../tool-selector/tool-item.tsx | 3 +- .../tool-selector/tool-trigger.tsx | 2 +- .../components/plugins/plugin-item/action.tsx | 3 +- .../components/plugins/plugin-item/index.tsx | 3 +- .../plugins/plugin-mutation-model/index.tsx | 3 +- .../plugins/plugin-page/debug-info.tsx | 2 +- .../plugins/plugin-page/empty/index.tsx | 3 +- .../plugin-page/filter-management/index.tsx | 3 +- .../plugins/plugin-page/plugin-info.tsx | 2 +- web/app/components/plugins/provider-card.tsx | 3 +- .../plugins/readme-panel/entrance.tsx | 2 +- .../auto-update-setting/index.tsx | 3 +- .../no-data-placeholder.tsx | 2 +- .../no-plugin-selected.tsx | 2 +- .../auto-update-setting/plugins-picker.tsx | 2 +- .../auto-update-setting/plugins-selected.tsx | 2 +- .../auto-update-setting/tool-item.tsx | 2 +- .../auto-update-setting/tool-picker.tsx | 3 +- .../plugins/reference-setting-modal/label.tsx | 2 +- .../plugins/reference-setting-modal/modal.tsx | 3 +- .../plugins/update-plugin/from-github.tsx | 2 +- .../update-plugin/from-market-place.tsx | 3 +- .../plugins/update-plugin/index.tsx | 2 +- .../update-plugin/plugin-version-picker.tsx | 3 +- .../components/chunk-card-list/chunk-card.tsx | 3 +- .../components/chunk-card-list/q-a-item.tsx | 2 +- .../rag-pipeline/components/conversion.tsx | 3 +- .../input-field/editor/form/hidden-fields.tsx | 2 +- .../editor/form/initial-fields.tsx | 3 +- .../editor/form/show-all-settings.tsx | 2 +- .../input-field/field-list/field-item.tsx | 3 +- .../panel/input-field/field-list/index.tsx | 3 +- .../panel/input-field/footer-tip.tsx | 2 +- .../label-right-content/datasource.tsx | 2 +- .../label-right-content/global-inputs.tsx | 2 +- .../panel/input-field/preview/data-source.tsx | 2 +- .../input-field/preview/process-documents.tsx | 2 +- .../components/panel/test-run/header.tsx | 3 +- .../test-run/preparation/actions/index.tsx | 2 +- .../data-source-options/option-card.tsx | 3 +- .../document-processing/actions.tsx | 2 +- .../preparation/document-processing/index.tsx | 3 +- .../test-run/preparation/footer-tips.tsx | 2 +- .../panel/test-run/preparation/index.tsx | 3 +- .../test-run/preparation/step-indicator.tsx | 2 +- .../test-run/result/result-preview/index.tsx | 3 +- .../panel/test-run/result/tabs/index.tsx | 2 +- .../panel/test-run/result/tabs/tab.tsx | 3 +- .../rag-pipeline-header/run-mode.tsx | 3 +- .../rag-pipeline/components/screenshot.tsx | 2 +- .../share/text-generation/index.tsx | 3 +- .../share/text-generation/info-modal.tsx | 2 +- .../share/text-generation/menu-dropdown.tsx | 3 +- .../text-generation/no-data/index.spec.tsx | 2 +- .../share/text-generation/no-data/index.tsx | 2 +- .../share/text-generation/result/content.tsx | 2 +- .../share/text-generation/result/header.tsx | 2 +- .../share/text-generation/result/index.tsx | 3 +- .../run-batch/csv-download/index.spec.tsx | 2 +- .../run-batch/csv-download/index.tsx | 2 +- .../run-batch/csv-reader/index.spec.tsx | 2 +- .../run-batch/csv-reader/index.tsx | 3 +- .../text-generation/run-batch/index.spec.tsx | 2 +- .../share/text-generation/run-batch/index.tsx | 2 +- .../run-batch/res-download/index.spec.tsx | 2 +- .../run-batch/res-download/index.tsx | 2 +- .../text-generation/run-once/index.spec.tsx | 3 +- .../share/text-generation/run-once/index.tsx | 3 +- web/app/components/splash.tsx | 2 +- .../config-credentials.tsx | 2 +- .../get-schema.tsx | 3 +- .../edit-custom-collection-modal/index.tsx | 3 +- .../edit-custom-collection-modal/test-api.tsx | 3 +- .../tools/marketplace/index.spec.tsx | 2 +- .../components/tools/mcp/detail/content.tsx | 3 +- .../tools/mcp/detail/list-loading.tsx | 2 +- .../tools/mcp/detail/operation-dropdown.tsx | 3 +- .../tools/mcp/detail/provider-detail.tsx | 2 +- .../components/tools/mcp/detail/tool-item.tsx | 2 +- .../components/tools/mcp/headers-input.tsx | 2 +- .../components/tools/mcp/mcp-server-modal.tsx | 2 +- .../tools/mcp/mcp-server-param-item.tsx | 2 +- .../components/tools/mcp/mcp-service-card.tsx | 3 +- web/app/components/tools/mcp/modal.tsx | 3 +- web/app/components/tools/provider/detail.tsx | 3 +- .../components/tools/provider/tool-item.tsx | 3 +- .../setting/build-in/config-credentials.tsx | 3 +- .../tools/workflow-tool/configure-button.tsx | 3 +- .../confirm-modal/index.spec.tsx | 2 +- .../components/tools/workflow-tool/index.tsx | 3 +- .../workflow-onboarding-modal/index.spec.tsx | 2 +- .../start-node-option.spec.tsx | 2 +- .../start-node-selection-panel.spec.tsx | 2 +- .../__tests__/trigger-status-sync.test.tsx | 3 +- .../market-place-plugin/action.tsx | 3 +- .../market-place-plugin/item.tsx | 2 +- .../rag-tool-recommendations/index.tsx | 3 +- .../uninstalled-item.tsx | 2 +- .../workflow/block-selector/tool-picker.tsx | 3 +- .../block-selector/tool/action-item.tsx | 3 +- .../tool/tool-list-flat-view/list.tsx | 3 +- .../tool/tool-list-tree-view/item.tsx | 2 +- .../tool/tool-list-tree-view/list.tsx | 3 +- .../workflow/block-selector/tool/tool.tsx | 3 +- .../trigger-plugin/action-item.tsx | 2 +- .../block-selector/trigger-plugin/item.tsx | 3 +- .../block-selector/use-sticky-scroll.ts | 2 +- .../block-selector/view-type-select.tsx | 3 +- .../workflow/dsl-export-confirm-modal.tsx | 3 +- .../components/workflow/header/run-mode.tsx | 3 +- .../header/version-history-button.tsx | 3 +- .../nodes/_base/components/add-button.tsx | 2 +- .../components/before-run-form/bool-input.tsx | 3 +- .../components/before-run-form/form-item.tsx | 3 +- .../_base/components/before-run-form/form.tsx | 3 +- .../components/before-run-form/index.tsx | 3 +- .../components/before-run-form/panel-wrap.tsx | 2 +- .../components/code-generator-button.tsx | 3 +- .../nodes/_base/components/config-vision.tsx | 3 +- .../nodes/_base/components/editor/base.tsx | 3 +- .../code-editor/editor-support-vars.tsx | 3 +- .../components/editor/code-editor/index.tsx | 3 +- .../_base/components/editor/text-editor.tsx | 3 +- .../nodes/_base/components/editor/wrap.tsx | 2 +- .../workflow/nodes/_base/components/field.tsx | 2 +- .../nodes/_base/components/file-type-item.tsx | 3 +- .../_base/components/file-upload-setting.tsx | 3 +- .../nodes/_base/components/info-panel.tsx | 2 +- .../components/input-number-with-slider.tsx | 3 +- .../components/input-support-select-var.tsx | 3 +- .../_base/components/input-var-type-icon.tsx | 2 +- .../components/list-no-data-placeholder.tsx | 2 +- .../mcp-tool-not-support-tooltip.tsx | 2 +- .../nodes/_base/components/memory-config.tsx | 3 +- .../nodes/_base/components/option-card.tsx | 3 +- .../nodes/_base/components/output-vars.tsx | 2 +- .../nodes/_base/components/prompt/editor.tsx | 3 +- .../readonly-input-with-select-var.tsx | 2 +- .../nodes/_base/components/remove-button.tsx | 2 +- .../components/remove-effect-var-confirm.tsx | 2 +- .../nodes/_base/components/selector.tsx | 2 +- .../workflow/nodes/_base/components/split.tsx | 2 +- .../components/support-var-input/index.tsx | 2 +- .../_base/components/toggle-expand-btn.tsx | 3 +- .../variable/assigned-var-reference-popup.tsx | 2 +- .../components/variable/constant-field.tsx | 3 +- .../object-child-tree-panel/picker/field.tsx | 2 +- .../object-child-tree-panel/picker/index.tsx | 3 +- .../object-child-tree-panel/show/field.tsx | 2 +- .../object-child-tree-panel/show/index.tsx | 2 +- .../tree-indent-line.tsx | 2 +- .../components/variable/output-var-list.tsx | 3 +- .../variable/var-full-path-panel.tsx | 2 +- .../_base/components/variable/var-list.tsx | 3 +- .../variable/var-reference-picker.tsx | 3 +- .../variable/var-reference-popup.tsx | 3 +- .../variable/var-reference-vars.tsx | 3 +- .../components/variable/var-type-picker.tsx | 3 +- .../_base/components/workflow-panel/index.tsx | 3 +- .../workflow-panel/last-run/index.tsx | 3 +- .../workflow-panel/last-run/no-data.tsx | 2 +- .../_base/components/workflow-panel/tab.tsx | 2 +- .../components/workflow/nodes/answer/node.tsx | 2 +- .../workflow/nodes/answer/panel.tsx | 2 +- .../assigner/components/var-list/index.tsx | 3 +- .../workflow/nodes/assigner/node.tsx | 2 +- .../workflow/nodes/assigner/panel.tsx | 2 +- .../workflow/nodes/code/dependency-picker.tsx | 3 +- .../components/workflow/nodes/code/node.tsx | 2 +- .../components/workflow/nodes/code/panel.tsx | 2 +- .../nodes/data-source/before-run-form.tsx | 3 +- .../nodes/document-extractor/node.tsx | 2 +- .../nodes/document-extractor/panel.tsx | 2 +- .../components/workflow/nodes/end/node.tsx | 2 +- .../components/workflow/nodes/end/panel.tsx | 2 +- .../nodes/http/components/api-input.tsx | 3 +- .../http/components/authorization/index.tsx | 3 +- .../components/authorization/radio-group.tsx | 3 +- .../nodes/http/components/curl-panel.tsx | 3 +- .../nodes/http/components/edit-body/index.tsx | 3 +- .../components/key-value/bulk-edit/index.tsx | 3 +- .../nodes/http/components/key-value/index.tsx | 2 +- .../key-value/key-value-edit/index.tsx | 3 +- .../key-value/key-value-edit/input-item.tsx | 3 +- .../key-value/key-value-edit/item.tsx | 3 +- .../nodes/http/components/timeout/index.tsx | 2 +- .../components/workflow/nodes/http/node.tsx | 2 +- .../if-else/components/condition-wrap.tsx | 3 +- .../workflow/nodes/if-else/node.tsx | 3 +- .../workflow/nodes/iteration/panel.tsx | 2 +- .../chunk-structure/instruction/index.tsx | 2 +- .../chunk-structure/instruction/line.tsx | 2 +- .../components/add-dataset.tsx | 3 +- .../components/dataset-item.tsx | 3 +- .../components/dataset-list.tsx | 3 +- .../components/retrieval-config.tsx | 3 +- .../nodes/knowledge-retrieval/node.tsx | 3 +- .../components/extract-input.tsx | 3 +- .../components/filter-condition.tsx | 3 +- .../list-operator/components/limit-config.tsx | 3 +- .../components/sub-variable-picker.tsx | 3 +- .../workflow/nodes/list-operator/node.tsx | 2 +- .../workflow/nodes/list-operator/panel.tsx | 2 +- .../llm/components/config-prompt-item.tsx | 3 +- .../nodes/llm/components/config-prompt.tsx | 3 +- .../json-schema-config-modal/code-editor.tsx | 3 +- .../error-message.tsx | 2 +- .../json-schema-config-modal/index.tsx | 2 +- .../json-importer.tsx | 3 +- .../json-schema-config.tsx | 3 +- .../generated-result.tsx | 3 +- .../json-schema-generator/index.tsx | 3 +- .../json-schema-generator/prompt-editor.tsx | 3 +- .../schema-editor.tsx | 2 +- .../visual-editor/add-field.tsx | 3 +- .../visual-editor/card.tsx | 2 +- .../visual-editor/edit-card/actions.tsx | 2 +- .../edit-card/advanced-actions.tsx | 2 +- .../edit-card/advanced-options.tsx | 3 +- .../edit-card/auto-width-input.tsx | 3 +- .../visual-editor/edit-card/index.tsx | 3 +- .../edit-card/required-switch.tsx | 2 +- .../visual-editor/schema-node.tsx | 3 +- .../llm/components/prompt-generator-btn.tsx | 3 +- .../components/reasoning-format-config.tsx | 2 +- .../llm/components/resolution-picker.tsx | 3 +- .../nodes/llm/components/structure-output.tsx | 3 +- .../components/workflow/nodes/llm/node.tsx | 2 +- .../components/workflow/nodes/llm/panel.tsx | 3 +- .../nodes/loop/components/condition-wrap.tsx | 3 +- .../components/workflow/nodes/loop/panel.tsx | 2 +- .../components/extract-parameter/item.tsx | 2 +- .../components/extract-parameter/list.tsx | 3 +- .../components/extract-parameter/update.tsx | 3 +- .../components/reasoning-mode-picker.tsx | 3 +- .../nodes/parameter-extractor/node.tsx | 2 +- .../nodes/parameter-extractor/panel.tsx | 2 +- .../components/advanced-setting.tsx | 2 +- .../components/class-item.tsx | 3 +- .../components/class-list.tsx | 3 +- .../nodes/question-classifier/node.tsx | 2 +- .../nodes/question-classifier/panel.tsx | 2 +- .../nodes/start/components/var-item.tsx | 3 +- .../nodes/start/components/var-list.tsx | 3 +- .../components/workflow/nodes/start/node.tsx | 2 +- .../components/workflow/nodes/start/panel.tsx | 2 +- .../nodes/template-transform/node.tsx | 2 +- .../nodes/template-transform/panel.tsx | 2 +- .../nodes/tool/components/copy-id.tsx | 3 +- .../nodes/tool/components/input-var-list.tsx | 3 +- .../components/workflow/nodes/tool/node.tsx | 3 +- .../components/workflow/nodes/tool/panel.tsx | 2 +- .../workflow/nodes/trigger-plugin/node.tsx | 3 +- .../workflow/nodes/trigger-plugin/panel.tsx | 2 +- .../components/frequency-selector.tsx | 3 +- .../components/mode-switcher.tsx | 2 +- .../components/mode-toggle.tsx | 2 +- .../components/monthly-days-selector.tsx | 2 +- .../components/next-execution-times.tsx | 2 +- .../components/on-minute-selector.tsx | 2 +- .../components/weekday-selector.tsx | 2 +- .../workflow/nodes/trigger-schedule/node.tsx | 2 +- .../workflow/nodes/trigger-schedule/panel.tsx | 2 +- .../components/generic-table.tsx | 3 +- .../components/header-table.tsx | 2 +- .../components/paragraph-input.tsx | 3 +- .../components/parameter-table.tsx | 3 +- .../workflow/nodes/trigger-webhook/node.tsx | 2 +- .../workflow/nodes/trigger-webhook/panel.tsx | 3 +- .../utils/render-output-vars.tsx | 2 +- .../components/var-group-item.tsx | 3 +- .../components/var-list/index.tsx | 3 +- .../nodes/variable-assigner/panel.tsx | 2 +- .../components/array-bool-list.tsx | 3 +- .../components/array-value-list.tsx | 3 +- .../components/bool-value.tsx | 3 +- .../components/object-value-item.tsx | 3 +- .../components/object-value-list.tsx | 2 +- .../components/variable-modal-trigger.tsx | 2 +- .../components/variable-modal.tsx | 3 +- .../components/variable-type-select.tsx | 3 +- .../conversation-variable-modal.tsx | 3 +- .../panel/env-panel/variable-modal.tsx | 3 +- .../panel/env-panel/variable-trigger.tsx | 2 +- .../context-menu/index.tsx | 3 +- .../context-menu/menu-item.tsx | 2 +- .../delete-confirm-modal.tsx | 2 +- .../panel/version-history-panel/empty.tsx | 2 +- .../filter/filter-item.tsx | 2 +- .../filter/filter-switch.tsx | 2 +- .../version-history-panel/filter/index.tsx | 3 +- .../panel/version-history-panel/index.tsx | 3 +- .../version-history-panel/loading/item.tsx | 2 +- .../restore-confirm-modal.tsx | 2 +- .../version-history-item.tsx | 3 +- web/app/components/workflow/run/index.tsx | 3 +- .../iteration-log/iteration-result-panel.tsx | 3 +- .../run/loop-log/loop-result-panel.tsx | 3 +- .../workflow/run/loop-result-panel.tsx | 3 +- .../components/workflow/run/tracing-panel.tsx | 5 +- .../variable-inspect/display-content.tsx | 3 +- .../variable-inspect/large-data-alert.tsx | 2 +- .../variable-inspect/value-content.tsx | 3 +- .../components/nodes/if-else/node.tsx | 3 +- .../nodes/question-classifier/node.tsx | 2 +- .../education-apply/expire-notice-modal.tsx | 2 +- .../education-apply/verify-state-modal.tsx | 3 +- .../forgot-password/ForgotPasswordForm.tsx | 3 +- web/app/forgot-password/page.tsx | 2 +- web/app/init/page.tsx | 2 +- web/app/install/installForm.tsx | 3 +- web/app/install/page.tsx | 2 +- web/app/signin/_header.tsx | 2 +- web/app/signin/normal-form.tsx | 3 +- web/app/signin/one-more-step.tsx | 3 +- web/app/signin/split.tsx | 2 +- web/context/external-api-panel-context.tsx | 3 +- web/context/modal-context.test.tsx | 2 +- web/context/provider-context-mock.tsx | 2 +- web/eslint.config.mjs | 2 +- web/hooks/use-breakpoints.ts | 2 +- web/service/demo/index.tsx | 2 +- web/utils/context.spec.ts | 2 +- 1078 files changed, 1680 insertions(+), 1216 deletions(-) diff --git a/web/__tests__/check-i18n.test.ts b/web/__tests__/check-i18n.test.ts index bd315a3fa9..a6f86d8107 100644 --- a/web/__tests__/check-i18n.test.ts +++ b/web/__tests__/check-i18n.test.ts @@ -1,9 +1,7 @@ import fs from 'node:fs' import path from 'node:path' - -// Mock functions to simulate the check-i18n functionality -const vm = require('node:vm') -const transpile = require('typescript').transpile +import vm from 'node:vm' +import { transpile } from 'typescript' describe('check-i18n script functionality', () => { const testDir = path.join(__dirname, '../i18n-test') diff --git a/web/__tests__/embedded-user-id-auth.test.tsx b/web/__tests__/embedded-user-id-auth.test.tsx index 8817aa2e1e..9231ac6199 100644 --- a/web/__tests__/embedded-user-id-auth.test.tsx +++ b/web/__tests__/embedded-user-id-auth.test.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import CheckCode from '@/app/(shareLayout)/webapp-signin/check-code/page' import MailAndPasswordAuth from '@/app/(shareLayout)/webapp-signin/components/mail-and-password-auth' diff --git a/web/__tests__/embedded-user-id-store.test.tsx b/web/__tests__/embedded-user-id-store.test.tsx index d1f99b8833..276b22bcd7 100644 --- a/web/__tests__/embedded-user-id-store.test.tsx +++ b/web/__tests__/embedded-user-id-store.test.tsx @@ -1,5 +1,5 @@ import { render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import WebAppStoreProvider, { useWebAppStore } from '@/context/web-app-context' import { AccessMode } from '@/models/access-control' diff --git a/web/__tests__/goto-anything/command-selector.test.tsx b/web/__tests__/goto-anything/command-selector.test.tsx index 1a9ef33b00..f0168ab3be 100644 --- a/web/__tests__/goto-anything/command-selector.test.tsx +++ b/web/__tests__/goto-anything/command-selector.test.tsx @@ -1,6 +1,6 @@ import type { ActionItem } from '../../app/components/goto-anything/actions/types' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import CommandSelector from '../../app/components/goto-anything/command-selector' vi.mock('cmdk', () => ({ diff --git a/web/__tests__/goto-anything/scope-command-tags.test.tsx b/web/__tests__/goto-anything/scope-command-tags.test.tsx index de34875d02..c25f4fc74e 100644 --- a/web/__tests__/goto-anything/scope-command-tags.test.tsx +++ b/web/__tests__/goto-anything/scope-command-tags.test.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' // Type alias for search mode type SearchMode = 'scopes' | 'commands' | null diff --git a/web/__tests__/workflow-parallel-limit.test.tsx b/web/__tests__/workflow-parallel-limit.test.tsx index d2afb30882..18657f4bd2 100644 --- a/web/__tests__/workflow-parallel-limit.test.tsx +++ b/web/__tests__/workflow-parallel-limit.test.tsx @@ -6,7 +6,7 @@ */ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' // Mock environment variables before importing constants const originalEnv = process.env.NEXT_PUBLIC_MAX_PARALLEL_LIMIT diff --git a/web/__tests__/xss-prevention.test.tsx b/web/__tests__/xss-prevention.test.tsx index b236f9ed3e..10a8b21a30 100644 --- a/web/__tests__/xss-prevention.test.tsx +++ b/web/__tests__/xss-prevention.test.tsx @@ -6,7 +6,7 @@ */ import { cleanup, render } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import BlockInput from '../app/components/base/block-input' import SupportVarInput from '../app/components/workflow/nodes/_base/components/support-var-input' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/annotations/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/annotations/page.tsx index 5fb3ff4b4d..a17a4a3d03 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/annotations/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/annotations/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Main from '@/app/components/app/log-annotation' import { PageType } from '@/app/components/base/features/new-feature-panel/annotation-reply/type' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/configuration/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/configuration/page.tsx index 41143b979a..850bd47aad 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/configuration/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/configuration/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Configuration from '@/app/components/app/configuration' const IConfiguration = async () => { diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx index ba04eae64a..14864aba8b 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/develop/page.tsx @@ -1,5 +1,5 @@ import type { Locale } from '@/i18n-config' -import React from 'react' +import * as React from 'react' import DevelopMain from '@/app/components/develop' export type IDevelopProps = { diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx index 41bf4c3acf..c1feb3ea5d 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx @@ -15,7 +15,8 @@ import { import { useUnmount } from 'ahooks' import dynamic from 'next/dynamic' import { usePathname, useRouter } from 'next/navigation' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useShallow } from 'zustand/react/shallow' import AppSideBar from '@/app/components/app-sidebar' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/logs/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/logs/page.tsx index 244a357616..eb8ff8f795 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/logs/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/logs/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Main from '@/app/components/app/log-annotation' import { PageType } from '@/app/components/base/features/new-feature-panel/annotation-reply/type' 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 f9110c9c1b..e9877f1715 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx @@ -4,7 +4,8 @@ import type { IAppCardProps } from '@/app/components/app/overview/app-card' import type { BlockEnum } from '@/app/components/workflow/types' import type { UpdateAppSiteCodeResponse } from '@/models/app' import type { App } from '@/types/app' -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import AppCard from '@/app/components/app/overview/app-card' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chart-view.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chart-view.tsx index 9304b45284..e1fca90c5d 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chart-view.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/chart-view.tsx @@ -2,7 +2,8 @@ import type { PeriodParams } from '@/app/components/app/overview/app-chart' import dayjs from 'dayjs' import quarterOfYear from 'dayjs/plugin/quarterOfYear' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { TIME_PERIOD_MAPPING as LONG_TIME_PERIOD_MAPPING } from '@/app/components/app/log/filter' import { AvgResponseTime, AvgSessionInteractions, AvgUserInteractions, ConversationsChart, CostChart, EndUsersChart, MessagesChart, TokenPerSecond, UserSatisfactionRate, WorkflowCostChart, WorkflowDailyTerminalsChart, WorkflowMessagesChart } from '@/app/components/app/overview/app-chart' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/long-time-range-picker.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/long-time-range-picker.tsx index 4d59f4a67f..557b723259 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/long-time-range-picker.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/long-time-range-picker.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import type { PeriodParams } from '@/app/components/app/overview/app-chart' import type { Item } from '@/app/components/base/select' import dayjs from 'dayjs' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { SimpleSelect } from '@/app/components/base/select' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx index e7ee4eb203..39c42d067d 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import ApikeyInfoPanel from '@/app/components/app/overview/apikey-info-panel' import ChartView from './chart-view' import TracingPanel from './tracing/panel' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/date-picker.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/date-picker.tsx index bc8bf58354..ab39846a36 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/date-picker.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/date-picker.tsx @@ -5,7 +5,8 @@ import type { TriggerProps } from '@/app/components/base/date-and-time-picker/ty import { RiCalendarLine } from '@remixicon/react' import dayjs from 'dayjs' import { noop } from 'lodash-es' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import Picker from '@/app/components/base/date-and-time-picker/date-picker' import { useI18N } from '@/context/i18n' import { cn } from '@/utils/classnames' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/index.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/index.tsx index 2a6dca33d7..469bc97737 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/index.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/index.tsx @@ -3,7 +3,8 @@ import type { Dayjs } from 'dayjs' import type { FC } from 'react' import type { PeriodParams, PeriodParamsWithTimeRange } from '@/app/components/app/overview/app-chart' import dayjs from 'dayjs' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { HourglassShape } from '@/app/components/base/icons/src/vender/other' import { useI18N } from '@/context/i18n' import { formatToLocalTime } from '@/utils/format' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/range-selector.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/range-selector.tsx index e820ae46e1..be7181c759 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/range-selector.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/range-selector.tsx @@ -4,7 +4,8 @@ import type { PeriodParamsWithTimeRange, TimeRange } from '@/app/components/app/ import type { Item } from '@/app/components/base/select' import { RiArrowDownSLine, RiCheckLine } from '@remixicon/react' import dayjs from 'dayjs' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { SimpleSelect } from '@/app/components/base/select' import { cn } from '@/utils/classnames' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/__tests__/svg-attribute-error-reproduction.spec.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/__tests__/svg-attribute-error-reproduction.spec.tsx index a08a1476d7..fc27f84c60 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/__tests__/svg-attribute-error-reproduction.spec.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/__tests__/svg-attribute-error-reproduction.spec.tsx @@ -1,5 +1,5 @@ import { render } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { OpikIconBig } from '@/app/components/base/icons/src/public/tracing' import iconData from '@/app/components/base/icons/src/public/tracing/OpikIconBig.json' import { normalizeAttrs } from '@/app/components/base/icons/utils' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx index 8dbb410f8c..8429f8a3a9 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { PopupProps } from './config-popup' -import React, { useCallback, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useRef, useState } from 'react' import { PortalToFollowElem, PortalToFollowElemContent, 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 3cbfcb4315..35ab8a61ec 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,7 +2,8 @@ import type { FC, JSX } from 'react' import type { AliyunConfig, ArizeConfig, DatabricksConfig, LangFuseConfig, LangSmithConfig, MLflowConfig, OpikConfig, PhoenixConfig, TencentConfig, WeaveConfig } from './type' import { useBoolean } from 'ahooks' -import React, { useCallback, useState } from 'react' +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' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx index a827a2b80c..7c47249830 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/field.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import Input from '@/app/components/base/input' import { cn } from '@/utils/classnames' 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 1ea89012e3..438dbddfb8 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/panel.tsx @@ -8,7 +8,8 @@ import { } from '@remixicon/react' import { useBoolean } from 'ahooks' import { usePathname } from 'next/navigation' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' 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' 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 254ca4e01a..0ef97e6970 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 @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { AliyunConfig, ArizeConfig, DatabricksConfig, LangFuseConfig, LangSmithConfig, MLflowConfig, OpikConfig, PhoenixConfig, TencentConfig, WeaveConfig } from './type' import { useBoolean } from 'ahooks' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx index aebdf70b55..6c66b19ad3 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import { RiEqualizer2Line, } from '@remixicon/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { AliyunIconBig, ArizeIconBig, DatabricksIconBig, LangfuseIconBig, LangsmithIconBig, MlflowIconBig, OpikIconBig, PhoenixIconBig, TencentIconBig, WeaveIconBig } from '@/app/components/base/icons/src/public/tracing' import { Eye as View } from '@/app/components/base/icons/src/vender/solid/general' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx index aa8567548d..137fff05df 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/tracing-icon.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { TracingIcon as Icon } from '@/app/components/base/icons/src/public/tracing' import { cn } from '@/utils/classnames' diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/layout.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/layout.tsx index f41babeb4e..4135482dd9 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/layout.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/layout.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { useRouter } from 'next/navigation' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import { useAppContext } from '@/context/app-context' import useDocumentTitle from '@/hooks/use-document-title' diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/api/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/api/page.tsx index 167520ca7b..200fc994ea 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/api/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/api/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' const page = () => { return ( diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx index ed6365c890..dd51c84bcf 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import MainDetail from '@/app/components/datasets/documents/detail' export type IDocumentDetailProps = { diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx index e8576d4a4b..cd9a37b426 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/[documentId]/settings/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Settings from '@/app/components/datasets/documents/detail/settings' export type IProps = { diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/create-from-pipeline/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/create-from-pipeline/page.tsx index 9ce86bbef4..046f69dab2 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/create-from-pipeline/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/create-from-pipeline/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import CreateFromPipeline from '@/app/components/datasets/documents/create-from-pipeline' const CreateFromPipelinePage = async () => { diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/create/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/create/page.tsx index 8fd2caa246..987bd1ea70 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/create/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/create/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import DatasetUpdateForm from '@/app/components/datasets/create' export type IProps = { diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/page.tsx index 2ff4631dea..7a049e0b1b 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/documents/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Main from '@/app/components/datasets/documents' export type IProps = { diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/hitTesting/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/hitTesting/page.tsx index 9a701c68f5..9a339030b9 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/hitTesting/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/hitTesting/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Main from '@/app/components/datasets/hit-testing' type Props = { diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx index 6caecc4826..10a12d75e1 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx @@ -10,7 +10,8 @@ import { RiFocus2Line, } from '@remixicon/react' import { usePathname } from 'next/navigation' -import React, { useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import AppSideBar from '@/app/components/app-sidebar' import { useStore } from '@/app/components/app/store' diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx index 59540a1854..9dfeaef528 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/settings/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Form from '@/app/components/datasets/settings/form' import { getLocaleOnServer, useTranslation as translate } from '@/i18n-config/server' diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/layout.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/layout.tsx index ccbc58f5e5..09555ae0f0 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/layout.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/layout.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import * as React from 'react' export type IDatasetDetail = { children: React.ReactNode diff --git a/web/app/(commonLayout)/datasets/connect/page.tsx b/web/app/(commonLayout)/datasets/connect/page.tsx index 724c506a7f..1ac4dc2f7c 100644 --- a/web/app/(commonLayout)/datasets/connect/page.tsx +++ b/web/app/(commonLayout)/datasets/connect/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import ExternalKnowledgeBaseConnector from '@/app/components/datasets/external-knowledge-base/connector' const ExternalKnowledgeBaseCreation = () => { diff --git a/web/app/(commonLayout)/datasets/create-from-pipeline/page.tsx b/web/app/(commonLayout)/datasets/create-from-pipeline/page.tsx index 72f5ecdfd9..63f09655b3 100644 --- a/web/app/(commonLayout)/datasets/create-from-pipeline/page.tsx +++ b/web/app/(commonLayout)/datasets/create-from-pipeline/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import CreateFromPipeline from '@/app/components/datasets/create-from-pipeline' const DatasetCreation = async () => { diff --git a/web/app/(commonLayout)/datasets/create/page.tsx b/web/app/(commonLayout)/datasets/create/page.tsx index 50fd1f5a19..fe5765437f 100644 --- a/web/app/(commonLayout)/datasets/create/page.tsx +++ b/web/app/(commonLayout)/datasets/create/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import DatasetUpdateForm from '@/app/components/datasets/create' const DatasetCreation = async () => { diff --git a/web/app/(commonLayout)/explore/apps/page.tsx b/web/app/(commonLayout)/explore/apps/page.tsx index b2430605e7..e0da5d64d2 100644 --- a/web/app/(commonLayout)/explore/apps/page.tsx +++ b/web/app/(commonLayout)/explore/apps/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import AppList from '@/app/components/explore/app-list' const Apps = () => { diff --git a/web/app/(commonLayout)/explore/installed/[appId]/page.tsx b/web/app/(commonLayout)/explore/installed/[appId]/page.tsx index 983fdb9d23..0b71d1a26c 100644 --- a/web/app/(commonLayout)/explore/installed/[appId]/page.tsx +++ b/web/app/(commonLayout)/explore/installed/[appId]/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Main from '@/app/components/explore/installed-app' export type IInstalledAppProps = { diff --git a/web/app/(commonLayout)/explore/layout.tsx b/web/app/(commonLayout)/explore/layout.tsx index 46bd41cb0c..5928308cdc 100644 --- a/web/app/(commonLayout)/explore/layout.tsx +++ b/web/app/(commonLayout)/explore/layout.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC, PropsWithChildren } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ExploreClient from '@/app/components/explore' import useDocumentTitle from '@/hooks/use-document-title' diff --git a/web/app/(commonLayout)/layout.tsx b/web/app/(commonLayout)/layout.tsx index a759506bf0..91bdb3f99a 100644 --- a/web/app/(commonLayout)/layout.tsx +++ b/web/app/(commonLayout)/layout.tsx @@ -1,5 +1,5 @@ import type { ReactNode } from 'react' -import React from 'react' +import * as React from 'react' import AmplitudeProvider from '@/app/components/base/amplitude' import GA, { GaType } from '@/app/components/base/ga' import Zendesk from '@/app/components/base/zendesk' diff --git a/web/app/(commonLayout)/tools/page.tsx b/web/app/(commonLayout)/tools/page.tsx index 3ea50e70ef..2d5c1a8e44 100644 --- a/web/app/(commonLayout)/tools/page.tsx +++ b/web/app/(commonLayout)/tools/page.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { useRouter } from 'next/navigation' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import ToolProviderList from '@/app/components/tools/provider-list' import { useAppContext } from '@/context/app-context' diff --git a/web/app/(shareLayout)/chat/[token]/page.tsx b/web/app/(shareLayout)/chat/[token]/page.tsx index 8ce67585f0..b4248aedbc 100644 --- a/web/app/(shareLayout)/chat/[token]/page.tsx +++ b/web/app/(shareLayout)/chat/[token]/page.tsx @@ -1,5 +1,5 @@ 'use client' -import React from 'react' +import * as React from 'react' import ChatWithHistoryWrap from '@/app/components/base/chat/chat-with-history' import AuthenticatedLayout from '../../components/authenticated-layout' diff --git a/web/app/(shareLayout)/chatbot/[token]/page.tsx b/web/app/(shareLayout)/chatbot/[token]/page.tsx index 5323d0dacc..187d736c54 100644 --- a/web/app/(shareLayout)/chatbot/[token]/page.tsx +++ b/web/app/(shareLayout)/chatbot/[token]/page.tsx @@ -1,5 +1,5 @@ 'use client' -import React from 'react' +import * as React from 'react' import EmbeddedChatbot from '@/app/components/base/chat/embedded-chatbot' import AuthenticatedLayout from '../../components/authenticated-layout' diff --git a/web/app/(shareLayout)/completion/[token]/page.tsx b/web/app/(shareLayout)/completion/[token]/page.tsx index ae91338b9a..ba96ac05cb 100644 --- a/web/app/(shareLayout)/completion/[token]/page.tsx +++ b/web/app/(shareLayout)/completion/[token]/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Main from '@/app/components/share/text-generation' import AuthenticatedLayout from '../../components/authenticated-layout' diff --git a/web/app/(shareLayout)/components/authenticated-layout.tsx b/web/app/(shareLayout)/components/authenticated-layout.tsx index 5f436429d3..00288b7a61 100644 --- a/web/app/(shareLayout)/components/authenticated-layout.tsx +++ b/web/app/(shareLayout)/components/authenticated-layout.tsx @@ -1,7 +1,8 @@ 'use client' import { usePathname, useRouter, useSearchParams } from 'next/navigation' -import React, { useCallback, useEffect } from 'react' +import * as React from 'react' +import { useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' import AppUnavailable from '@/app/components/base/app-unavailable' import Loading from '@/app/components/base/loading' 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 091493812f..0776df036d 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,6 +1,7 @@ 'use client' import { useRouter, useSearchParams } from 'next/navigation' -import React, { useCallback, useEffect } from 'react' +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/toast' diff --git a/web/app/(shareLayout)/webapp-signin/normalForm.tsx b/web/app/(shareLayout)/webapp-signin/normalForm.tsx index 2aaa267962..40d34dcaf5 100644 --- a/web/app/(shareLayout)/webapp-signin/normalForm.tsx +++ b/web/app/(shareLayout)/webapp-signin/normalForm.tsx @@ -1,7 +1,8 @@ 'use client' import { RiContractLine, RiDoorLockLine, RiErrorWarningFill } from '@remixicon/react' import Link from 'next/link' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' import { IS_CE_EDITION } from '@/config' diff --git a/web/app/(shareLayout)/webapp-signin/page.tsx b/web/app/(shareLayout)/webapp-signin/page.tsx index ca380c9398..cfa0295b28 100644 --- a/web/app/(shareLayout)/webapp-signin/page.tsx +++ b/web/app/(shareLayout)/webapp-signin/page.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { useRouter, useSearchParams } from 'next/navigation' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import AppUnavailable from '@/app/components/base/app-unavailable' import { useGlobalPublicStore } from '@/context/global-public-context' diff --git a/web/app/(shareLayout)/workflow/[token]/page.tsx b/web/app/(shareLayout)/workflow/[token]/page.tsx index 4f5923e91f..b2828ee5db 100644 --- a/web/app/(shareLayout)/workflow/[token]/page.tsx +++ b/web/app/(shareLayout)/workflow/[token]/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Main from '@/app/components/share/text-generation' import AuthenticatedLayout from '../../components/authenticated-layout' diff --git a/web/app/account/(commonLayout)/account-page/AvatarWithEdit.tsx b/web/app/account/(commonLayout)/account-page/AvatarWithEdit.tsx index dd254435bb..9b65db4eaa 100644 --- a/web/app/account/(commonLayout)/account-page/AvatarWithEdit.tsx +++ b/web/app/account/(commonLayout)/account-page/AvatarWithEdit.tsx @@ -5,7 +5,8 @@ import type { OnImageInput } from '@/app/components/base/app-icon-picker/ImageIn import type { AvatarProps } from '@/app/components/base/avatar' import type { ImageFile } from '@/types/app' import { RiDeleteBin5Line, RiPencilLine } from '@remixicon/react' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import ImageInput from '@/app/components/base/app-icon-picker/ImageInput' 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 83e667a1f3..99b4f5c686 100644 --- a/web/app/account/(commonLayout)/account-page/email-change-modal.tsx +++ b/web/app/account/(commonLayout)/account-page/email-change-modal.tsx @@ -2,7 +2,8 @@ import type { ResponseError } from '@/service/fetch' import { RiCloseLine } from '@remixicon/react' import { noop } from 'lodash-es' import { useRouter } from 'next/navigation' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Button from '@/app/components/base/button' diff --git a/web/app/account/(commonLayout)/layout.tsx b/web/app/account/(commonLayout)/layout.tsx index af1ec0afd6..f264441b86 100644 --- a/web/app/account/(commonLayout)/layout.tsx +++ b/web/app/account/(commonLayout)/layout.tsx @@ -1,5 +1,5 @@ import type { ReactNode } from 'react' -import React from 'react' +import * as React from 'react' import AmplitudeProvider from '@/app/components/base/amplitude' import GA, { GaType } from '@/app/components/base/ga' import HeaderWrapper from '@/app/components/header/header-wrapper' diff --git a/web/app/account/oauth/authorize/page.tsx b/web/app/account/oauth/authorize/page.tsx index 9993c27e4b..bbb96abf4e 100644 --- a/web/app/account/oauth/authorize/page.tsx +++ b/web/app/account/oauth/authorize/page.tsx @@ -9,7 +9,8 @@ import { } from '@remixicon/react' import dayjs from 'dayjs' import { useRouter, useSearchParams } from 'next/navigation' -import React, { useEffect, useRef } from 'react' +import * as React from 'react' +import { useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import Avatar from '@/app/components/base/avatar' import Button from '@/app/components/base/button' diff --git a/web/app/activate/page.tsx b/web/app/activate/page.tsx index 01e4e1a35f..5852ef54e4 100644 --- a/web/app/activate/page.tsx +++ b/web/app/activate/page.tsx @@ -1,5 +1,5 @@ 'use client' -import React from 'react' +import * as React from 'react' import { useGlobalPublicStore } from '@/context/global-public-context' import { cn } from '@/utils/classnames' import Header from '../signin/_header' diff --git a/web/app/components/app-sidebar/app-info.tsx b/web/app/components/app-sidebar/app-info.tsx index e175de5c6e..497124c702 100644 --- a/web/app/components/app-sidebar/app-info.tsx +++ b/web/app/components/app-sidebar/app-info.tsx @@ -13,7 +13,8 @@ import { } from '@remixicon/react' import dynamic from 'next/dynamic' import { useRouter } from 'next/navigation' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import CardView from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view' diff --git a/web/app/components/app-sidebar/app-sidebar-dropdown.tsx b/web/app/components/app-sidebar/app-sidebar-dropdown.tsx index 30ec906c93..4d80af8bcb 100644 --- a/web/app/components/app-sidebar/app-sidebar-dropdown.tsx +++ b/web/app/components/app-sidebar/app-sidebar-dropdown.tsx @@ -3,7 +3,8 @@ import { RiEqualizer2Line, RiMenuLine, } from '@remixicon/react' -import React, { useCallback, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useStore as useAppStore } from '@/app/components/app/store' import { diff --git a/web/app/components/app-sidebar/basic.tsx b/web/app/components/app-sidebar/basic.tsx index 40e95206c8..1503afcdad 100644 --- a/web/app/components/app-sidebar/basic.tsx +++ b/web/app/components/app-sidebar/basic.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { ApiAggregate, diff --git a/web/app/components/app-sidebar/dataset-info/dropdown.tsx b/web/app/components/app-sidebar/dataset-info/dropdown.tsx index f20b35de80..c072bd7547 100644 --- a/web/app/components/app-sidebar/dataset-info/dropdown.tsx +++ b/web/app/components/app-sidebar/dataset-info/dropdown.tsx @@ -1,7 +1,8 @@ import type { DataSet } from '@/models/datasets' import { RiMoreFill } from '@remixicon/react' import { useRouter } from 'next/navigation' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { useSelector as useAppContextWithSelector } from '@/context/app-context' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' diff --git a/web/app/components/app-sidebar/dataset-info/index.spec.tsx b/web/app/components/app-sidebar/dataset-info/index.spec.tsx index a03b5da488..da7eb6d7ff 100644 --- a/web/app/components/app-sidebar/dataset-info/index.spec.tsx +++ b/web/app/components/app-sidebar/dataset-info/index.spec.tsx @@ -2,7 +2,7 @@ import type { DataSet } from '@/models/datasets' import { RiEditLine } from '@remixicon/react' import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import { ChunkingMode, DatasetPermission, diff --git a/web/app/components/app-sidebar/dataset-info/index.tsx b/web/app/components/app-sidebar/dataset-info/index.tsx index 43fded35a9..ce409ff13a 100644 --- a/web/app/components/app-sidebar/dataset-info/index.tsx +++ b/web/app/components/app-sidebar/dataset-info/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { DataSet } from '@/models/datasets' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import { useKnowledge } from '@/hooks/use-knowledge' diff --git a/web/app/components/app-sidebar/dataset-info/menu-item.tsx b/web/app/components/app-sidebar/dataset-info/menu-item.tsx index 5cc082a19e..441482283b 100644 --- a/web/app/components/app-sidebar/dataset-info/menu-item.tsx +++ b/web/app/components/app-sidebar/dataset-info/menu-item.tsx @@ -1,5 +1,5 @@ import type { RemixiconComponentType } from '@remixicon/react' -import React from 'react' +import * as React from 'react' type MenuItemProps = { name: string diff --git a/web/app/components/app-sidebar/dataset-info/menu.tsx b/web/app/components/app-sidebar/dataset-info/menu.tsx index f4a49b3eea..a17e0ed96d 100644 --- a/web/app/components/app-sidebar/dataset-info/menu.tsx +++ b/web/app/components/app-sidebar/dataset-info/menu.tsx @@ -1,5 +1,5 @@ import { RiDeleteBinLine, RiEditLine, RiFileDownloadLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import Divider from '../../base/divider' diff --git a/web/app/components/app-sidebar/dataset-sidebar-dropdown.tsx b/web/app/components/app-sidebar/dataset-sidebar-dropdown.tsx index e8fa050a6d..d8e26826ca 100644 --- a/web/app/components/app-sidebar/dataset-sidebar-dropdown.tsx +++ b/web/app/components/app-sidebar/dataset-sidebar-dropdown.tsx @@ -3,7 +3,8 @@ import type { DataSet } from '@/models/datasets' import { RiMenuLine, } from '@remixicon/react' -import React, { useCallback, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { PortalToFollowElem, diff --git a/web/app/components/app-sidebar/index.tsx b/web/app/components/app-sidebar/index.tsx index 790d5340bc..afc6bd0f13 100644 --- a/web/app/components/app-sidebar/index.tsx +++ b/web/app/components/app-sidebar/index.tsx @@ -1,7 +1,8 @@ import type { NavIcon } from './navLink' import { useHover, useKeyPress } from 'ahooks' import { usePathname } from 'next/navigation' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useShallow } from 'zustand/react/shallow' import { useStore as useAppStore } from '@/app/components/app/store' import { useEventEmitterContextContext } from '@/context/event-emitter' diff --git a/web/app/components/app-sidebar/navLink.spec.tsx b/web/app/components/app-sidebar/navLink.spec.tsx index 410dae6b2a..62ef553386 100644 --- a/web/app/components/app-sidebar/navLink.spec.tsx +++ b/web/app/components/app-sidebar/navLink.spec.tsx @@ -1,6 +1,6 @@ import type { NavLinkProps } from './navLink' import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import NavLink from './navLink' // Mock Next.js navigation diff --git a/web/app/components/app-sidebar/navLink.tsx b/web/app/components/app-sidebar/navLink.tsx index 999c892199..9d5e319046 100644 --- a/web/app/components/app-sidebar/navLink.tsx +++ b/web/app/components/app-sidebar/navLink.tsx @@ -2,7 +2,7 @@ import type { RemixiconComponentType } from '@remixicon/react' import Link from 'next/link' import { useSelectedLayoutSegment } from 'next/navigation' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' export type NavIcon = React.ComponentType< diff --git a/web/app/components/app-sidebar/sidebar-animation-issues.spec.tsx b/web/app/components/app-sidebar/sidebar-animation-issues.spec.tsx index 61f278b577..5d85b99d9a 100644 --- a/web/app/components/app-sidebar/sidebar-animation-issues.spec.tsx +++ b/web/app/components/app-sidebar/sidebar-animation-issues.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' // Simple Mock Components that reproduce the exact UI issues const MockNavLink = ({ name, mode }: { name: string, mode: string }) => { diff --git a/web/app/components/app-sidebar/text-squeeze-fix-verification.spec.tsx b/web/app/components/app-sidebar/text-squeeze-fix-verification.spec.tsx index 7752e29f46..7c0c8b3aca 100644 --- a/web/app/components/app-sidebar/text-squeeze-fix-verification.spec.tsx +++ b/web/app/components/app-sidebar/text-squeeze-fix-verification.spec.tsx @@ -4,7 +4,7 @@ */ import { render } from '@testing-library/react' -import React from 'react' +import * as React from 'react' // Mock Next.js navigation vi.mock('next/navigation', () => ({ diff --git a/web/app/components/app-sidebar/toggle-button.tsx b/web/app/components/app-sidebar/toggle-button.tsx index e144d29e92..b4dc2e9199 100644 --- a/web/app/components/app-sidebar/toggle-button.tsx +++ b/web/app/components/app-sidebar/toggle-button.tsx @@ -1,5 +1,5 @@ import { RiArrowLeftSLine, RiArrowRightSLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import Button from '../base/button' diff --git a/web/app/components/app/annotation/add-annotation-modal/edit-item/index.spec.tsx b/web/app/components/app/annotation/add-annotation-modal/edit-item/index.spec.tsx index 34fbe93be6..ce660f7880 100644 --- a/web/app/components/app/annotation/add-annotation-modal/edit-item/index.spec.tsx +++ b/web/app/components/app/annotation/add-annotation-modal/edit-item/index.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import EditItem, { EditItemType } from './index' describe('AddAnnotationModal/EditItem', () => { diff --git a/web/app/components/app/annotation/add-annotation-modal/edit-item/index.tsx b/web/app/components/app/annotation/add-annotation-modal/edit-item/index.tsx index 62785d0032..ec53243077 100644 --- a/web/app/components/app/annotation/add-annotation-modal/edit-item/index.tsx +++ b/web/app/components/app/annotation/add-annotation-modal/edit-item/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React 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' diff --git a/web/app/components/app/annotation/add-annotation-modal/index.spec.tsx b/web/app/components/app/annotation/add-annotation-modal/index.spec.tsx index a0ddcd13d3..6837516b3c 100644 --- a/web/app/components/app/annotation/add-annotation-modal/index.spec.tsx +++ b/web/app/components/app/annotation/add-annotation-modal/index.spec.tsx @@ -1,6 +1,6 @@ import type { Mock } from 'vitest' import { act, fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { useProviderContext } from '@/context/provider-context' import AddAnnotationModal from './index' 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 24ef88681a..b31fb20822 100644 --- a/web/app/components/app/annotation/add-annotation-modal/index.tsx +++ b/web/app/components/app/annotation/add-annotation-modal/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { AnnotationItemBasic } from '../type' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Checkbox from '@/app/components/base/checkbox' diff --git a/web/app/components/app/annotation/batch-action.spec.tsx b/web/app/components/app/annotation/batch-action.spec.tsx index 8598088702..8d56dde14a 100644 --- a/web/app/components/app/annotation/batch-action.spec.tsx +++ b/web/app/components/app/annotation/batch-action.spec.tsx @@ -1,5 +1,5 @@ import { act, fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import BatchAction from './batch-action' describe('BatchAction', () => { diff --git a/web/app/components/app/annotation/batch-action.tsx b/web/app/components/app/annotation/batch-action.tsx index 5d170d7054..491c68a656 100644 --- a/web/app/components/app/annotation/batch-action.tsx +++ b/web/app/components/app/annotation/batch-action.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import { RiDeleteBinLine } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Confirm from '@/app/components/base/confirm' import Divider from '@/app/components/base/divider' diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/csv-downloader.spec.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/csv-downloader.spec.tsx index a4ca710b88..a3ab73b339 100644 --- a/web/app/components/app/annotation/batch-add-annotation-modal/csv-downloader.spec.tsx +++ b/web/app/components/app/annotation/batch-add-annotation-modal/csv-downloader.spec.tsx @@ -1,6 +1,6 @@ import type { Locale } from '@/i18n-config' import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import I18nContext from '@/context/i18n' import { LanguagesSupported } from '@/i18n-config/language' import CSVDownload from './csv-downloader' diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/csv-downloader.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/csv-downloader.tsx index d966a3e28a..4735afb5cb 100644 --- a/web/app/components/app/annotation/batch-add-annotation-modal/csv-downloader.tsx +++ b/web/app/components/app/annotation/batch-add-annotation-modal/csv-downloader.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useCSVDownloader, diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.spec.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.spec.tsx index 342d2baeca..6a67ba3207 100644 --- a/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.spec.tsx +++ b/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.spec.tsx @@ -1,6 +1,6 @@ import type { Props } from './csv-uploader' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { ToastContext } from '@/app/components/base/toast' import CSVUploader from './csv-uploader' 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 ac10e636cf..79e2faf283 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,7 +1,8 @@ 'use client' import type { FC } from 'react' import { RiDeleteBinLine } from '@remixicon/react' -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Button from '@/app/components/base/button' diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/index.spec.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/index.spec.tsx index feaa5e27e7..d7458d6b90 100644 --- a/web/app/components/app/annotation/batch-add-annotation-modal/index.spec.tsx +++ b/web/app/components/app/annotation/batch-add-annotation-modal/index.spec.tsx @@ -1,7 +1,7 @@ import type { Mock } from 'vitest' import type { IBatchModalProps } from './index' import { act, fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Toast from '@/app/components/base/toast' import { useProviderContext } from '@/context/provider-context' import { annotationBatchImport, checkAnnotationBatchImportProgress } from '@/service/annotation' 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 fcba222656..7fbb745c48 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 @@ -2,7 +2,8 @@ import type { FC } from 'react' import { RiCloseLine } from '@remixicon/react' import { noop } from 'lodash-es' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' diff --git a/web/app/components/app/annotation/clear-all-annotations-confirm-modal/index.spec.tsx b/web/app/components/app/annotation/clear-all-annotations-confirm-modal/index.spec.tsx index 5c2bbcf62f..5dbfd664f8 100644 --- a/web/app/components/app/annotation/clear-all-annotations-confirm-modal/index.spec.tsx +++ b/web/app/components/app/annotation/clear-all-annotations-confirm-modal/index.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import ClearAllAnnotationsConfirmModal from './index' vi.mock('react-i18next', () => ({ 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 ab8c15eab6..df85f4956b 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,7 +1,7 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Confirm from '@/app/components/base/confirm' 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 9ba7d9d5fb..cd03406a67 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,7 +1,8 @@ 'use client' import type { FC } from 'react' import { RiDeleteBinLine, RiEditFill, RiEditLine } from '@remixicon/react' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { Robot, User } from '@/app/components/base/icons/src/public/avatar' 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 288ca009bc..4c4380bc20 100644 --- a/web/app/components/app/annotation/edit-annotation-modal/index.tsx +++ b/web/app/components/app/annotation/edit-annotation-modal/index.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Confirm from '@/app/components/base/confirm' import Drawer from '@/app/components/base/drawer-plus' diff --git a/web/app/components/app/annotation/empty-element.spec.tsx b/web/app/components/app/annotation/empty-element.spec.tsx index 3f96e917fd..89ba7e9ff8 100644 --- a/web/app/components/app/annotation/empty-element.spec.tsx +++ b/web/app/components/app/annotation/empty-element.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import EmptyElement from './empty-element' describe('EmptyElement', () => { diff --git a/web/app/components/app/annotation/empty-element.tsx b/web/app/components/app/annotation/empty-element.tsx index 523f83a7cb..4f41a59f4f 100644 --- a/web/app/components/app/annotation/empty-element.tsx +++ b/web/app/components/app/annotation/empty-element.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC, SVGProps } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' const ThreeDotsIcon = ({ className }: SVGProps<SVGElement>) => { diff --git a/web/app/components/app/annotation/filter.spec.tsx b/web/app/components/app/annotation/filter.spec.tsx index 3dfc60d73f..9b733a8c10 100644 --- a/web/app/components/app/annotation/filter.spec.tsx +++ b/web/app/components/app/annotation/filter.spec.tsx @@ -1,7 +1,7 @@ import type { Mock } from 'vitest' import type { QueryParam } from './filter' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import useSWR from 'swr' import Filter from './filter' diff --git a/web/app/components/app/annotation/filter.tsx b/web/app/components/app/annotation/filter.tsx index 02c2a859bf..76f33d2f1b 100644 --- a/web/app/components/app/annotation/filter.tsx +++ b/web/app/components/app/annotation/filter.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import useSWR from 'swr' import Input from '@/app/components/base/input' diff --git a/web/app/components/app/annotation/header-opts/index.tsx b/web/app/components/app/annotation/header-opts/index.tsx index a6c9d71391..6d2d365808 100644 --- a/web/app/components/app/annotation/header-opts/index.tsx +++ b/web/app/components/app/annotation/header-opts/index.tsx @@ -7,7 +7,8 @@ import { RiDeleteBinLine, RiMoreFill, } from '@remixicon/react' -import React, { Fragment, useEffect, useState } from 'react' +import * as React from 'react' +import { Fragment, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useCSVDownloader, diff --git a/web/app/components/app/annotation/index.spec.tsx b/web/app/components/app/annotation/index.spec.tsx index 202b631f65..2d989a9a59 100644 --- a/web/app/components/app/annotation/index.spec.tsx +++ b/web/app/components/app/annotation/index.spec.tsx @@ -2,7 +2,7 @@ import type { Mock } from 'vitest' import type { AnnotationItem } from './type' import type { App } from '@/types/app' import { act, fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Toast from '@/app/components/base/toast' import { useProviderContext } from '@/context/provider-context' import { diff --git a/web/app/components/app/annotation/index.tsx b/web/app/components/app/annotation/index.tsx index c673db2c28..18175193db 100644 --- a/web/app/components/app/annotation/index.tsx +++ b/web/app/components/app/annotation/index.tsx @@ -6,7 +6,8 @@ import type { AnnotationReplyConfig } from '@/models/debug' import type { App } from '@/types/app' import { RiEqualizer2Line } from '@remixicon/react' import { useDebounce } from 'ahooks' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import ConfigParamModal from '@/app/components/base/features/new-feature-panel/annotation-reply/config-param-modal' diff --git a/web/app/components/app/annotation/list.spec.tsx b/web/app/components/app/annotation/list.spec.tsx index f0a8aa9d93..37e4832740 100644 --- a/web/app/components/app/annotation/list.spec.tsx +++ b/web/app/components/app/annotation/list.spec.tsx @@ -1,6 +1,6 @@ import type { AnnotationItem } from './type' import { fireEvent, render, screen, within } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import List from './list' const mockFormatTime = vi.fn(() => 'formatted-time') diff --git a/web/app/components/app/annotation/list.tsx b/web/app/components/app/annotation/list.tsx index 45e33b1a1a..4d821aa994 100644 --- a/web/app/components/app/annotation/list.tsx +++ b/web/app/components/app/annotation/list.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { AnnotationItem } from './type' import { RiDeleteBinLine, RiEditLine } from '@remixicon/react' -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Checkbox from '@/app/components/base/checkbox' diff --git a/web/app/components/app/annotation/remove-annotation-confirm-modal/index.spec.tsx b/web/app/components/app/annotation/remove-annotation-confirm-modal/index.spec.tsx index e26ea691c6..db3bb63c43 100644 --- a/web/app/components/app/annotation/remove-annotation-confirm-modal/index.spec.tsx +++ b/web/app/components/app/annotation/remove-annotation-confirm-modal/index.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import RemoveAnnotationConfirmModal from './index' vi.mock('react-i18next', () => ({ 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 a6ade49a79..bf21c95d8a 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,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/app/annotation/view-annotation-modal/hit-history-no-data.tsx b/web/app/components/app/annotation/view-annotation-modal/hit-history-no-data.tsx index cebb5630eb..52f360fecc 100644 --- a/web/app/components/app/annotation/view-annotation-modal/hit-history-no-data.tsx +++ b/web/app/components/app/annotation/view-annotation-modal/hit-history-no-data.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { ClockFastForward } from '@/app/components/base/icons/src/vender/line/time' diff --git a/web/app/components/app/annotation/view-annotation-modal/index.spec.tsx b/web/app/components/app/annotation/view-annotation-modal/index.spec.tsx index 9fe8e585f4..3eb278b874 100644 --- a/web/app/components/app/annotation/view-annotation-modal/index.spec.tsx +++ b/web/app/components/app/annotation/view-annotation-modal/index.spec.tsx @@ -1,7 +1,7 @@ import type { Mock } from 'vitest' import type { AnnotationItem, HitHistoryItem } from '../type' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { fetchHitHistoryList } from '@/service/annotation' import ViewAnnotationModal from './index' 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 f2db6de7c0..8f24a830bb 100644 --- a/web/app/components/app/annotation/view-annotation-modal/index.tsx +++ b/web/app/components/app/annotation/view-annotation-modal/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { AnnotationItem, HitHistoryItem } from '../type' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Badge from '@/app/components/base/badge' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/app/app-publisher/features-wrapper.tsx b/web/app/components/app/app-publisher/features-wrapper.tsx index 35d9728728..8257b69fca 100644 --- a/web/app/components/app/app-publisher/features-wrapper.tsx +++ b/web/app/components/app/app-publisher/features-wrapper.tsx @@ -2,7 +2,8 @@ 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 { produce } from 'immer' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import AppPublisher from '@/app/components/app/app-publisher' import Confirm from '@/app/components/base/confirm' 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 647dc57f36..ba8b7a3074 100644 --- a/web/app/components/app/app-publisher/version-info-modal.tsx +++ b/web/app/components/app/app-publisher/version-info-modal.tsx @@ -1,7 +1,8 @@ import type { FC } from 'react' import type { VersionHistory } from '@/types/workflow' import { RiCloseLine } from '@remixicon/react' -import React, { useCallback, useState } from 'react' +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/toast' diff --git a/web/app/components/app/configuration/base/feature-panel/index.tsx b/web/app/components/app/configuration/base/feature-panel/index.tsx index b69de31ac4..20c4a8dc17 100644 --- a/web/app/components/app/configuration/base/feature-panel/index.tsx +++ b/web/app/components/app/configuration/base/feature-panel/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC, ReactNode } from 'react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' export type IFeaturePanelProps = { diff --git a/web/app/components/app/configuration/base/group-name/index.tsx b/web/app/components/app/configuration/base/group-name/index.tsx index 1ae3107876..b21b0c5825 100644 --- a/web/app/components/app/configuration/base/group-name/index.tsx +++ b/web/app/components/app/configuration/base/group-name/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' export type IGroupNameProps = { name: string diff --git a/web/app/components/app/configuration/base/operation-btn/index.tsx b/web/app/components/app/configuration/base/operation-btn/index.tsx index 2deaba3743..b9f55de26b 100644 --- a/web/app/components/app/configuration/base/operation-btn/index.tsx +++ b/web/app/components/app/configuration/base/operation-btn/index.tsx @@ -5,7 +5,7 @@ import { RiEditLine, } from '@remixicon/react' import { noop } from 'lodash-es' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' diff --git a/web/app/components/app/configuration/base/var-highlight/index.tsx b/web/app/components/app/configuration/base/var-highlight/index.tsx index b2360751c2..697007d0b0 100644 --- a/web/app/components/app/configuration/base/var-highlight/index.tsx +++ b/web/app/components/app/configuration/base/var-highlight/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import s from './style.module.css' diff --git a/web/app/components/app/configuration/base/warning-mask/cannot-query-dataset.spec.tsx b/web/app/components/app/configuration/base/warning-mask/cannot-query-dataset.spec.tsx index 4ca20f7117..730b251e67 100644 --- a/web/app/components/app/configuration/base/warning-mask/cannot-query-dataset.spec.tsx +++ b/web/app/components/app/configuration/base/warning-mask/cannot-query-dataset.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import CannotQueryDataset from './cannot-query-dataset' describe('CannotQueryDataset WarningMask', () => { 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 2c916eaf3b..baa7782b12 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,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import WarningMask from '.' diff --git a/web/app/components/app/configuration/base/warning-mask/formatting-changed.spec.tsx b/web/app/components/app/configuration/base/warning-mask/formatting-changed.spec.tsx index 3171e2c183..9b5a5d93e1 100644 --- a/web/app/components/app/configuration/base/warning-mask/formatting-changed.spec.tsx +++ b/web/app/components/app/configuration/base/warning-mask/formatting-changed.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import FormattingChanged from './formatting-changed' describe('FormattingChanged WarningMask', () => { 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 24bb245b25..df7d8569f8 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,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import WarningMask from '.' diff --git a/web/app/components/app/configuration/base/warning-mask/has-not-set-api.spec.tsx b/web/app/components/app/configuration/base/warning-mask/has-not-set-api.spec.tsx index 24ba0aeb3b..be4377bfd9 100644 --- a/web/app/components/app/configuration/base/warning-mask/has-not-set-api.spec.tsx +++ b/web/app/components/app/configuration/base/warning-mask/has-not-set-api.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import HasNotSetAPI from './has-not-set-api' describe('HasNotSetAPI WarningMask', () => { diff --git a/web/app/components/app/configuration/base/warning-mask/has-not-set-api.tsx b/web/app/components/app/configuration/base/warning-mask/has-not-set-api.tsx index 001a7ff79e..7be3f2001d 100644 --- a/web/app/components/app/configuration/base/warning-mask/has-not-set-api.tsx +++ b/web/app/components/app/configuration/base/warning-mask/has-not-set-api.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import WarningMask from '.' diff --git a/web/app/components/app/configuration/base/warning-mask/index.spec.tsx b/web/app/components/app/configuration/base/warning-mask/index.spec.tsx index 9546a3e8d9..cb8ef0b678 100644 --- a/web/app/components/app/configuration/base/warning-mask/index.spec.tsx +++ b/web/app/components/app/configuration/base/warning-mask/index.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import WarningMask from './index' describe('WarningMask', () => { diff --git a/web/app/components/app/configuration/base/warning-mask/index.tsx b/web/app/components/app/configuration/base/warning-mask/index.tsx index 3d6fef72a4..6d6aeceb97 100644 --- a/web/app/components/app/configuration/base/warning-mask/index.tsx +++ b/web/app/components/app/configuration/base/warning-mask/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import s from './style.module.css' 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 a5d97c47f1..15ba089d77 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 @@ -9,7 +9,7 @@ import { import { useBoolean } from 'ahooks' import copy from 'copy-to-clipboard' import { produce } from 'immer' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { ADD_EXTERNAL_DATA_TOOL } from '@/app/components/app/configuration/config-var' diff --git a/web/app/components/app/configuration/config-prompt/confirm-add-var/index.spec.tsx b/web/app/components/app/configuration/config-prompt/confirm-add-var/index.spec.tsx index f732da3f95..360676f829 100644 --- a/web/app/components/app/configuration/config-prompt/confirm-add-var/index.spec.tsx +++ b/web/app/components/app/configuration/config-prompt/confirm-add-var/index.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import ConfirmAddVar from './index' vi.mock('../../base/var-highlight', () => ({ 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 e8106a790a..6c149688f4 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,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useRef } from 'react' +import * as React from 'react' +import { useRef } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import VarHighlight from '../../base/var-highlight' diff --git a/web/app/components/app/configuration/config-prompt/conversation-history/edit-modal.spec.tsx b/web/app/components/app/configuration/config-prompt/conversation-history/edit-modal.spec.tsx index a6033fcb60..e6532d26fc 100644 --- a/web/app/components/app/configuration/config-prompt/conversation-history/edit-modal.spec.tsx +++ b/web/app/components/app/configuration/config-prompt/conversation-history/edit-modal.spec.tsx @@ -1,6 +1,6 @@ import type { ConversationHistoriesRole } from '@/models/debug' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import EditModal from './edit-modal' vi.mock('@/app/components/base/modal', () => ({ 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 c21ddeb30e..741461d53a 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,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { ConversationHistoriesRole } from '@/models/debug' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' diff --git a/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.spec.tsx b/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.spec.tsx index 4d3da2afa4..c6f5b3ed19 100644 --- a/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.spec.tsx +++ b/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import HistoryPanel from './history-panel' const mockDocLink = vi.fn(() => 'doc-link') diff --git a/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx b/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx index b4a133db94..d6df316201 100644 --- a/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx +++ b/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Panel from '@/app/components/app/configuration/base/feature-panel' import OperationBtn from '@/app/components/app/configuration/base/operation-btn' diff --git a/web/app/components/app/configuration/config-prompt/index.spec.tsx b/web/app/components/app/configuration/config-prompt/index.spec.tsx index f3992d2a05..ceb9cf3f42 100644 --- a/web/app/components/app/configuration/config-prompt/index.spec.tsx +++ b/web/app/components/app/configuration/config-prompt/index.spec.tsx @@ -1,7 +1,7 @@ import type { IPromptProps } from './index' import type { PromptItem, PromptVariable } from '@/models/debug' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' 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/index.tsx b/web/app/components/app/configuration/config-prompt/index.tsx index ab13c6fc70..0d30e0d863 100644 --- a/web/app/components/app/configuration/config-prompt/index.tsx +++ b/web/app/components/app/configuration/config-prompt/index.tsx @@ -6,7 +6,7 @@ import { RiAddLine, } from '@remixicon/react' import { produce } from 'immer' -import React from 'react' +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' diff --git a/web/app/components/app/configuration/config-prompt/message-type-selector.spec.tsx b/web/app/components/app/configuration/config-prompt/message-type-selector.spec.tsx index 5354beda8a..5eb1896dc6 100644 --- a/web/app/components/app/configuration/config-prompt/message-type-selector.spec.tsx +++ b/web/app/components/app/configuration/config-prompt/message-type-selector.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { PromptRole } from '@/models/debug' import MessageTypeSelector from './message-type-selector' diff --git a/web/app/components/app/configuration/config-prompt/message-type-selector.tsx b/web/app/components/app/configuration/config-prompt/message-type-selector.tsx index 5bc29da9e4..0b404eea90 100644 --- a/web/app/components/app/configuration/config-prompt/message-type-selector.tsx +++ b/web/app/components/app/configuration/config-prompt/message-type-selector.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { useBoolean, useClickAway } from 'ahooks' -import React from 'react' +import * as React from 'react' import { ChevronSelectorVertical } from '@/app/components/base/icons/src/vender/line/arrows' import { PromptRole } from '@/models/debug' import { cn } from '@/utils/classnames' diff --git a/web/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.spec.tsx b/web/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.spec.tsx index cfdff40559..abd95e7660 100644 --- a/web/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.spec.tsx +++ b/web/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import PromptEditorHeightResizeWrap from './prompt-editor-height-resize-wrap' describe('PromptEditorHeightResizeWrap', () => { diff --git a/web/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.tsx b/web/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.tsx index 985aa527b0..24c77c1dae 100644 --- a/web/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.tsx +++ b/web/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { useDebounceFn } from 'ahooks' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { cn } from '@/utils/classnames' type Props = { 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 5d9d4abe1e..9b558b58c1 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 @@ -6,7 +6,8 @@ import type { GenRes } from '@/service/debug' import { useBoolean } from 'ahooks' import { produce } from 'immer' import { noop } from 'lodash-es' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { ADD_EXTERNAL_DATA_TOOL } from '@/app/components/app/configuration/config-var' diff --git a/web/app/components/app/configuration/config-var/config-modal/field.tsx b/web/app/components/app/configuration/config-var/config-modal/field.tsx index 8fe612a82b..c7a9bbfa03 100644 --- a/web/app/components/app/configuration/config-var/config-modal/field.tsx +++ b/web/app/components/app/configuration/config-var/config-modal/field.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' 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 155266469a..41f37b5895 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 @@ -4,7 +4,8 @@ import type { Item as SelectItem } from './type-select' import type { FileEntity } from '@/app/components/base/file-uploader/types' import type { InputVar, MoreInfo, UploadFileSetting } from '@/app/components/workflow/types' import { produce } from 'immer' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +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' diff --git a/web/app/components/app/configuration/config-var/config-modal/type-select.tsx b/web/app/components/app/configuration/config-var/config-modal/type-select.tsx index f476887409..66ec5a2a69 100644 --- a/web/app/components/app/configuration/config-var/config-modal/type-select.tsx +++ b/web/app/components/app/configuration/config-var/config-modal/type-select.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { InputVarType } from '@/app/components/workflow/types' import { ChevronDownIcon } from '@heroicons/react/20/solid' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import Badge from '@/app/components/base/badge' import { PortalToFollowElem, diff --git a/web/app/components/app/configuration/config-var/config-select/index.tsx b/web/app/components/app/configuration/config-var/config-select/index.tsx index 565cf77207..61bc8b7023 100644 --- a/web/app/components/app/configuration/config-var/config-select/index.tsx +++ b/web/app/components/app/configuration/config-var/config-select/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { RiAddLine, RiDeleteBinLine, RiDraggable } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { ReactSortable } from 'react-sortablejs' import { cn } from '@/utils/classnames' diff --git a/web/app/components/app/configuration/config-var/config-string/index.tsx b/web/app/components/app/configuration/config-var/config-string/index.tsx index 78f185bd85..fce687ac3e 100644 --- a/web/app/components/app/configuration/config-var/config-string/index.tsx +++ b/web/app/components/app/configuration/config-var/config-string/index.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import Input from '@/app/components/base/input' export type IConfigStringProps = { diff --git a/web/app/components/app/configuration/config-var/index.tsx b/web/app/components/app/configuration/config-var/index.tsx index 092a7aee6c..7a2a86393a 100644 --- a/web/app/components/app/configuration/config-var/index.tsx +++ b/web/app/components/app/configuration/config-var/index.tsx @@ -5,7 +5,8 @@ import type { ExternalDataTool } from '@/models/common' import type { PromptVariable } from '@/models/debug' import { useBoolean } from 'ahooks' import { produce } from 'immer' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { ReactSortable } from 'react-sortablejs' import { useContext } from 'use-context-selector' diff --git a/web/app/components/app/configuration/config-var/input-type-icon.tsx b/web/app/components/app/configuration/config-var/input-type-icon.tsx index 3d6db27616..d79964ab70 100644 --- a/web/app/components/app/configuration/config-var/input-type-icon.tsx +++ b/web/app/components/app/configuration/config-var/input-type-icon.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { ApiConnection } from '@/app/components/base/icons/src/vender/solid/development' import InputVarTypeIcon from '@/app/components/workflow/nodes/_base/components/input-var-type-icon' import { InputVarType } from '@/app/components/workflow/types' 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 d1eed20a03..cad5bcb3e9 100644 --- a/web/app/components/app/configuration/config-var/modal-foot.tsx +++ b/web/app/components/app/configuration/config-var/modal-foot.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' diff --git a/web/app/components/app/configuration/config-var/select-type-item/index.spec.tsx b/web/app/components/app/configuration/config-var/select-type-item/index.spec.tsx index b21d69bc8e..f34dd52f49 100644 --- a/web/app/components/app/configuration/config-var/select-type-item/index.spec.tsx +++ b/web/app/components/app/configuration/config-var/select-type-item/index.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { InputVarType } from '@/app/components/workflow/types' import SelectTypeItem from './index' diff --git a/web/app/components/app/configuration/config-var/select-type-item/index.tsx b/web/app/components/app/configuration/config-var/select-type-item/index.tsx index 4f0c3ace9e..ccb958977c 100644 --- a/web/app/components/app/configuration/config-var/select-type-item/index.tsx +++ b/web/app/components/app/configuration/config-var/select-type-item/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { InputVarType } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import InputVarTypeIcon from '@/app/components/workflow/nodes/_base/components/input-var-type-icon' import { cn } from '@/utils/classnames' diff --git a/web/app/components/app/configuration/config-var/select-var-type.tsx b/web/app/components/app/configuration/config-var/select-var-type.tsx index a74aeff45c..0c19aeb137 100644 --- a/web/app/components/app/configuration/config-var/select-var-type.tsx +++ b/web/app/components/app/configuration/config-var/select-var-type.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import OperationBtn from '@/app/components/app/configuration/base/operation-btn' import { ApiConnection } from '@/app/components/base/icons/src/vender/solid/development' diff --git a/web/app/components/app/configuration/config-var/var-item.tsx b/web/app/components/app/configuration/config-var/var-item.tsx index 633af6dc28..a4888db628 100644 --- a/web/app/components/app/configuration/config-var/var-item.tsx +++ b/web/app/components/app/configuration/config-var/var-item.tsx @@ -6,7 +6,8 @@ import { RiDraggable, RiEditLine, } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import Badge from '@/app/components/base/badge' import { BracketsX as VarIcon } from '@/app/components/base/icons/src/vender/line/development' import { cn } from '@/utils/classnames' diff --git a/web/app/components/app/configuration/config-vision/index.spec.tsx b/web/app/components/app/configuration/config-vision/index.spec.tsx index 7fd1d448e3..5fc7648bea 100644 --- a/web/app/components/app/configuration/config-vision/index.spec.tsx +++ b/web/app/components/app/configuration/config-vision/index.spec.tsx @@ -3,7 +3,7 @@ import type { FeatureStoreState } from '@/app/components/base/features/store' import type { FileUpload } from '@/app/components/base/features/types' import { fireEvent, render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import { SupportUploadFileTypes } from '@/app/components/workflow/types' import { Resolution, TransferMethod } from '@/types/app' import ConfigVision from './index' diff --git a/web/app/components/app/configuration/config-vision/index.tsx b/web/app/components/app/configuration/config-vision/index.tsx index 6a73b9e545..e53cdd4dfd 100644 --- a/web/app/components/app/configuration/config-vision/index.tsx +++ b/web/app/components/app/configuration/config-vision/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' // import { Resolution } from '@/types/app' diff --git a/web/app/components/app/configuration/config-vision/param-config-content.tsx b/web/app/components/app/configuration/config-vision/param-config-content.tsx index ebb8befbb3..2de14b9b6d 100644 --- a/web/app/components/app/configuration/config-vision/param-config-content.tsx +++ b/web/app/components/app/configuration/config-vision/param-config-content.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { FileUpload } from '@/app/components/base/features/types' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' import ParamItem from '@/app/components/base/param-item' diff --git a/web/app/components/app/configuration/config/agent-setting-button.spec.tsx b/web/app/components/app/configuration/config/agent-setting-button.spec.tsx index 1858a67a3b..963a671a23 100644 --- a/web/app/components/app/configuration/config/agent-setting-button.spec.tsx +++ b/web/app/components/app/configuration/config/agent-setting-button.spec.tsx @@ -1,7 +1,7 @@ import type { AgentConfig } from '@/models/debug' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import { AgentStrategy } from '@/types/app' import AgentSettingButton from './agent-setting-button' 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 332b25eb1e..c7c6ea417a 100644 --- a/web/app/components/app/configuration/config/agent-setting-button.tsx +++ b/web/app/components/app/configuration/config/agent-setting-button.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { AgentConfig } from '@/models/debug' import { RiSettings2Line } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import AgentSetting from './agent/agent-setting' diff --git a/web/app/components/app/configuration/config/agent/agent-setting/index.spec.tsx b/web/app/components/app/configuration/config/agent/agent-setting/index.spec.tsx index 5bb955842b..b3a9bd7abc 100644 --- a/web/app/components/app/configuration/config/agent/agent-setting/index.spec.tsx +++ b/web/app/components/app/configuration/config/agent/agent-setting/index.spec.tsx @@ -1,6 +1,6 @@ import type { AgentConfig } from '@/models/debug' import { act, fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { MAX_ITERATIONS_NUM } from '@/config' import AgentSetting from './index' diff --git a/web/app/components/app/configuration/config/agent/agent-setting/index.tsx b/web/app/components/app/configuration/config/agent/agent-setting/index.tsx index 1adab75ed1..dae2461876 100644 --- a/web/app/components/app/configuration/config/agent/agent-setting/index.tsx +++ b/web/app/components/app/configuration/config/agent/agent-setting/index.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { AgentConfig } from '@/models/debug' import { RiCloseLine } from '@remixicon/react' import { useClickAway } from 'ahooks' -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { CuteRobot } from '@/app/components/base/icons/src/vender/solid/communication' diff --git a/web/app/components/app/configuration/config/agent/agent-setting/item-panel.spec.tsx b/web/app/components/app/configuration/config/agent/agent-setting/item-panel.spec.tsx index dad576c983..a4dcb7a6f3 100644 --- a/web/app/components/app/configuration/config/agent/agent-setting/item-panel.spec.tsx +++ b/web/app/components/app/configuration/config/agent/agent-setting/item-panel.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import ItemPanel from './item-panel' describe('AgentSetting/ItemPanel', () => { diff --git a/web/app/components/app/configuration/config/agent/agent-setting/item-panel.tsx b/web/app/components/app/configuration/config/agent/agent-setting/item-panel.tsx index 2a6002632f..92d3239608 100644 --- a/web/app/components/app/configuration/config/agent/agent-setting/item-panel.tsx +++ b/web/app/components/app/configuration/config/agent/agent-setting/item-panel.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import Tooltip from '@/app/components/base/tooltip' import { cn } from '@/utils/classnames' diff --git a/web/app/components/app/configuration/config/agent/agent-tools/index.spec.tsx b/web/app/components/app/configuration/config/agent/agent-tools/index.spec.tsx index cf9dd79031..1625db97b8 100644 --- a/web/app/components/app/configuration/config/agent/agent-tools/index.spec.tsx +++ b/web/app/components/app/configuration/config/agent/agent-tools/index.spec.tsx @@ -12,7 +12,8 @@ import type { AgentTool } from '@/types/app' import { act, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import copy from 'copy-to-clipboard' -import React, { +import * as React from 'react' +import { useEffect, useMemo, useState, diff --git a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx index f1e70e304f..dfbab1f6f2 100644 --- a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx +++ b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx @@ -11,7 +11,8 @@ import { } from '@remixicon/react' import copy from 'copy-to-clipboard' import { produce } from 'immer' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Panel from '@/app/components/app/configuration/base/feature-panel' diff --git a/web/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool.spec.tsx b/web/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool.spec.tsx index 654cd627a2..e056baaa2f 100644 --- a/web/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool.spec.tsx +++ b/web/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool.spec.tsx @@ -1,7 +1,7 @@ import type { Tool, ToolParameter } from '@/app/components/tools/types' import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import { CollectionType } from '@/app/components/tools/types' import I18n from '@/context/i18n' import SettingBuiltInTool from './setting-built-in-tool' diff --git a/web/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool.tsx b/web/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool.tsx index 31cccbf39c..c59d7a3b6e 100644 --- a/web/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool.tsx +++ b/web/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool.tsx @@ -6,7 +6,8 @@ import { RiArrowLeftLine, RiCloseLine, } from '@remixicon/react' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import ActionButton from '@/app/components/base/action-button' diff --git a/web/app/components/app/configuration/config/agent/prompt-editor.tsx b/web/app/components/app/configuration/config/agent/prompt-editor.tsx index 6319cc1bc5..0a09609cca 100644 --- a/web/app/components/app/configuration/config/agent/prompt-editor.tsx +++ b/web/app/components/app/configuration/config/agent/prompt-editor.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import type { ExternalDataTool } from '@/models/common' import copy from 'copy-to-clipboard' import { noop } from 'lodash-es' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import s from '@/app/components/app/configuration/config-prompt/style.module.css' diff --git a/web/app/components/app/configuration/config/assistant-type-picker/index.spec.tsx b/web/app/components/app/configuration/config/assistant-type-picker/index.spec.tsx index 8436a132d6..86201e996d 100644 --- a/web/app/components/app/configuration/config/assistant-type-picker/index.spec.tsx +++ b/web/app/components/app/configuration/config/assistant-type-picker/index.spec.tsx @@ -1,7 +1,7 @@ import type { AgentConfig } from '@/models/debug' import { act, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import { AgentStrategy } from '@/types/app' import AssistantTypePicker from './index' diff --git a/web/app/components/app/configuration/config/assistant-type-picker/index.tsx b/web/app/components/app/configuration/config/assistant-type-picker/index.tsx index 0a283835fc..8c08e7c921 100644 --- a/web/app/components/app/configuration/config/assistant-type-picker/index.tsx +++ b/web/app/components/app/configuration/config/assistant-type-picker/index.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { AgentConfig } from '@/models/debug' import { RiArrowDownSLine } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { ArrowUpRight } from '@/app/components/base/icons/src/vender/line/arrows' import { Settings04 } from '@/app/components/base/icons/src/vender/line/general' 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 86a16ba995..636c2577b1 100644 --- a/web/app/components/app/configuration/config/automatic/automatic-btn.tsx +++ b/web/app/components/app/configuration/config/automatic/automatic-btn.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import { RiSparklingFill, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' 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 bc5d0fc7de..46fcaee52b 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 @@ -16,7 +16,8 @@ import { RiUser2Line, } from '@remixicon/react' import { useBoolean, useSessionStorageState } from 'ahooks' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/app/configuration/config/automatic/idea-output.tsx b/web/app/components/app/configuration/config/automatic/idea-output.tsx index 560d9be15b..2d91683ac8 100644 --- a/web/app/components/app/configuration/config/automatic/idea-output.tsx +++ b/web/app/components/app/configuration/config/automatic/idea-output.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { useBoolean } from 'ahooks' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/general' import Textarea from '@/app/components/base/textarea' diff --git a/web/app/components/app/configuration/config/automatic/instruction-editor-in-workflow.tsx b/web/app/components/app/configuration/config/automatic/instruction-editor-in-workflow.tsx index 2c6e09302c..31a96c5818 100644 --- a/web/app/components/app/configuration/config/automatic/instruction-editor-in-workflow.tsx +++ b/web/app/components/app/configuration/config/automatic/instruction-editor-in-workflow.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { GeneratorType } from './types' import type { ValueSelector, Var } from '@/app/components/workflow/types' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useWorkflowVariableType } from '@/app/components/workflow/hooks' import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' import { useWorkflowStore } from '@/app/components/workflow/store' diff --git a/web/app/components/app/configuration/config/automatic/instruction-editor.tsx b/web/app/components/app/configuration/config/automatic/instruction-editor.tsx index e42d027061..77d6e9b56c 100644 --- a/web/app/components/app/configuration/config/automatic/instruction-editor.tsx +++ b/web/app/components/app/configuration/config/automatic/instruction-editor.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { GeneratorType } from './types' import type { Node, NodeOutPutVar, ValueSelector } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import PromptEditor from '@/app/components/base/prompt-editor' import { PROMPT_EDITOR_INSERT_QUICKLY } from '@/app/components/base/prompt-editor/plugins/update-block' diff --git a/web/app/components/app/configuration/config/automatic/prompt-res-in-workflow.tsx b/web/app/components/app/configuration/config/automatic/prompt-res-in-workflow.tsx index 80cd357eb9..71d0c95d1d 100644 --- a/web/app/components/app/configuration/config/automatic/prompt-res-in-workflow.tsx +++ b/web/app/components/app/configuration/config/automatic/prompt-res-in-workflow.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' import { Type } from '@/app/components/workflow/nodes/llm/types' diff --git a/web/app/components/app/configuration/config/automatic/prompt-res.tsx b/web/app/components/app/configuration/config/automatic/prompt-res.tsx index 8a0e85aab7..ced438a18a 100644 --- a/web/app/components/app/configuration/config/automatic/prompt-res.tsx +++ b/web/app/components/app/configuration/config/automatic/prompt-res.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { WorkflowVariableBlockType } from '@/app/components/base/prompt-editor/types' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import PromptEditor from '@/app/components/base/prompt-editor' type Props = { diff --git a/web/app/components/app/configuration/config/automatic/prompt-toast.tsx b/web/app/components/app/configuration/config/automatic/prompt-toast.tsx index 2e53eb563c..65a78d1e67 100644 --- a/web/app/components/app/configuration/config/automatic/prompt-toast.tsx +++ b/web/app/components/app/configuration/config/automatic/prompt-toast.tsx @@ -1,6 +1,6 @@ import { RiArrowDownSLine, RiSparklingFill } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { Markdown } from '@/app/components/base/markdown' import { cn } from '@/utils/classnames' diff --git a/web/app/components/app/configuration/config/automatic/res-placeholder.tsx b/web/app/components/app/configuration/config/automatic/res-placeholder.tsx index 01d9043c82..80ab8e1f3f 100644 --- a/web/app/components/app/configuration/config/automatic/res-placeholder.tsx +++ b/web/app/components/app/configuration/config/automatic/res-placeholder.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { Generator } from '@/app/components/base/icons/src/vender/other' diff --git a/web/app/components/app/configuration/config/automatic/result.tsx b/web/app/components/app/configuration/config/automatic/result.tsx index c2a4f55f80..b97975a6be 100644 --- a/web/app/components/app/configuration/config/automatic/result.tsx +++ b/web/app/components/app/configuration/config/automatic/result.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import type { GenRes } from '@/service/debug' import { RiClipboardLine } from '@remixicon/react' import copy from 'copy-to-clipboard' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Toast from '@/app/components/base/toast' diff --git a/web/app/components/app/configuration/config/automatic/version-selector.tsx b/web/app/components/app/configuration/config/automatic/version-selector.tsx index fe8c26f2d5..5449f518a5 100644 --- a/web/app/components/app/configuration/config/automatic/version-selector.tsx +++ b/web/app/components/app/configuration/config/automatic/version-selector.tsx @@ -1,6 +1,7 @@ import { RiArrowDownSLine, RiCheckLine } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' import { cn } from '@/utils/classnames' 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 9eb1acf77d..23ab46973f 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 @@ -5,7 +5,8 @@ import type { GenRes } from '@/service/debug' import type { AppModeEnum, CompletionParams, Model, ModelModeType } from '@/types/app' import { useSessionStorageState } from 'ahooks' import useBoolean from 'ahooks/lib/useBoolean' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/app/configuration/config/config-audio.spec.tsx b/web/app/components/app/configuration/config/config-audio.spec.tsx index 2ca34f9742..c29e2ac2b4 100644 --- a/web/app/components/app/configuration/config/config-audio.spec.tsx +++ b/web/app/components/app/configuration/config/config-audio.spec.tsx @@ -2,7 +2,7 @@ import type { Mock } from 'vitest' import type { FeatureStoreState } from '@/app/components/base/features/store' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import { SupportUploadFileTypes } from '@/app/components/workflow/types' import ConfigAudio from './config-audio' diff --git a/web/app/components/app/configuration/config/config-audio.tsx b/web/app/components/app/configuration/config/config-audio.tsx index 66a094804e..93cc0d5bae 100644 --- a/web/app/components/app/configuration/config/config-audio.tsx +++ b/web/app/components/app/configuration/config/config-audio.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' diff --git a/web/app/components/app/configuration/config/config-document.spec.tsx b/web/app/components/app/configuration/config/config-document.spec.tsx index 4d874dc37e..2aa87717fc 100644 --- a/web/app/components/app/configuration/config/config-document.spec.tsx +++ b/web/app/components/app/configuration/config/config-document.spec.tsx @@ -2,7 +2,7 @@ import type { Mock } from 'vitest' import type { FeatureStoreState } from '@/app/components/base/features/store' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import { SupportUploadFileTypes } from '@/app/components/workflow/types' import ConfigDocument from './config-document' diff --git a/web/app/components/app/configuration/config/config-document.tsx b/web/app/components/app/configuration/config/config-document.tsx index 5616c201c6..b2caf73397 100644 --- a/web/app/components/app/configuration/config/config-document.tsx +++ b/web/app/components/app/configuration/config/config-document.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' diff --git a/web/app/components/app/configuration/config/index.spec.tsx b/web/app/components/app/configuration/config/index.spec.tsx index 94361ba28c..25a112ec09 100644 --- a/web/app/components/app/configuration/config/index.spec.tsx +++ b/web/app/components/app/configuration/config/index.spec.tsx @@ -2,7 +2,7 @@ import type { Mock } from 'vitest' import type { ModelConfig, PromptVariable } from '@/models/debug' import type { ToolItem } from '@/types/app' import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import * as useContextSelector from 'use-context-selector' import { AgentStrategy, AppModeEnum, ModelModeType } from '@/types/app' import Config from './index' diff --git a/web/app/components/app/configuration/config/index.tsx b/web/app/components/app/configuration/config/index.tsx index 2b9d2ce44e..f208b99e59 100644 --- a/web/app/components/app/configuration/config/index.tsx +++ b/web/app/components/app/configuration/config/index.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { ModelConfig, PromptVariable } from '@/models/debug' import { produce } from 'immer' -import React from 'react' +import * as React from 'react' import { useContext } from 'use-context-selector' import ConfigPrompt from '@/app/components/app/configuration/config-prompt' import ConfigVar from '@/app/components/app/configuration/config-var' 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 c955c497fb..efd3809201 100644 --- a/web/app/components/app/configuration/ctrl-btn-group/index.tsx +++ b/web/app/components/app/configuration/ctrl-btn-group/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import s from './style.module.css' diff --git a/web/app/components/app/configuration/dataset-config/card-item/index.spec.tsx b/web/app/components/app/configuration/dataset-config/card-item/index.spec.tsx index a69b0882d3..2e3cb47c98 100644 --- a/web/app/components/app/configuration/dataset-config/card-item/index.spec.tsx +++ b/web/app/components/app/configuration/dataset-config/card-item/index.spec.tsx @@ -1,4 +1,4 @@ -import type React from 'react' +import type * as React from 'react' import type { MockedFunction } from 'vitest' import type { IndexingType } from '@/app/components/datasets/create/step-two' import type { DataSet } from '@/models/datasets' diff --git a/web/app/components/app/configuration/dataset-config/card-item/index.tsx b/web/app/components/app/configuration/dataset-config/card-item/index.tsx index 223c594870..e0b50d1be3 100644 --- a/web/app/components/app/configuration/dataset-config/card-item/index.tsx +++ b/web/app/components/app/configuration/dataset-config/card-item/index.tsx @@ -5,7 +5,8 @@ import { RiDeleteBinLine, RiEditLine, } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' import AppIcon from '@/app/components/base/app-icon' diff --git a/web/app/components/app/configuration/dataset-config/context-var/index.tsx b/web/app/components/app/configuration/dataset-config/context-var/index.tsx index 4baa731f28..b2983c313d 100644 --- a/web/app/components/app/configuration/dataset-config/context-var/index.tsx +++ b/web/app/components/app/configuration/dataset-config/context-var/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { Props } from './var-picker' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { BracketsX } from '@/app/components/base/icons/src/vender/line/development' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/app/configuration/dataset-config/context-var/var-picker.tsx b/web/app/components/app/configuration/dataset-config/context-var/var-picker.tsx index 485e7aeb89..6f467afa84 100644 --- a/web/app/components/app/configuration/dataset-config/context-var/var-picker.tsx +++ b/web/app/components/app/configuration/dataset-config/context-var/var-picker.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { IInputTypeIconProps } from '@/app/components/app/configuration/config-var/input-type-icon' import { ChevronDownIcon } from '@heroicons/react/24/outline' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import IconTypeIcon from '@/app/components/app/configuration/config-var/input-type-icon' import { diff --git a/web/app/components/app/configuration/dataset-config/index.tsx b/web/app/components/app/configuration/dataset-config/index.tsx index b954944a6e..9ac1729590 100644 --- a/web/app/components/app/configuration/dataset-config/index.tsx +++ b/web/app/components/app/configuration/dataset-config/index.tsx @@ -10,7 +10,8 @@ import type { import type { DataSet } from '@/models/datasets' import { produce } from 'immer' import { intersectionBy } from 'lodash-es' -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { v4 as uuid4 } from 'uuid' 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 4e6bbe4a69..c1df954bde 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 @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { DataSet } from '@/models/datasets' import { useInfiniteScroll } from 'ahooks' import Link from 'next/link' -import React, { useEffect, useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import Badge from '@/app/components/base/badge' diff --git a/web/app/components/app/configuration/debug/chat-user-input.tsx b/web/app/components/app/configuration/debug/chat-user-input.tsx index 2847f55307..3c65394301 100644 --- a/web/app/components/app/configuration/debug/chat-user-input.tsx +++ b/web/app/components/app/configuration/debug/chat-user-input.tsx @@ -1,5 +1,6 @@ import type { Inputs } from '@/models/debug' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Input from '@/app/components/base/input' diff --git a/web/app/components/app/configuration/debug/index.tsx b/web/app/components/app/configuration/debug/index.tsx index 875e3bc35f..fe1c6550f5 100644 --- a/web/app/components/app/configuration/debug/index.tsx +++ b/web/app/components/app/configuration/debug/index.tsx @@ -14,7 +14,8 @@ import { useBoolean } from 'ahooks' import { produce, setAutoFreeze } from 'immer' import { noop } from 'lodash-es' import cloneDeep from 'lodash-es/cloneDeep' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { useShallow } from 'zustand/react/shallow' diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx index 9f9baea042..eb7a9f5a32 100644 --- a/web/app/components/app/configuration/index.tsx +++ b/web/app/components/app/configuration/index.tsx @@ -23,7 +23,8 @@ import { useBoolean, useGetState } from 'ahooks' import { produce } from 'immer' import { clone, isEqual } from 'lodash-es' import { usePathname } from 'next/navigation' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +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 { useShallow } from 'zustand/react/shallow' 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 8d006e8b2b..0b9388c664 100644 --- a/web/app/components/app/configuration/prompt-value-panel/index.tsx +++ b/web/app/components/app/configuration/prompt-value-panel/index.tsx @@ -7,7 +7,8 @@ import { RiArrowRightSLine, RiPlayLargeFill, } from '@remixicon/react' -import React, { useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { useStore as useAppStore } from '@/app/components/app/store' 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 0af1b347af..df54de2ff1 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 @@ -5,7 +5,8 @@ import type { App } from '@/models/explore' import { RiRobot2Line } from '@remixicon/react' import { useDebounceFn } from 'ahooks' import { useRouter } from 'next/navigation' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import useSWR from 'swr' import { useContext } from 'use-context-selector' 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 73043643c7..cef288acfc 100644 --- a/web/app/components/app/create-from-dsl-modal/uploader.tsx +++ b/web/app/components/app/create-from-dsl-modal/uploader.tsx @@ -4,7 +4,8 @@ import { RiDeleteBinLine, RiUploadCloud2Line, } from '@remixicon/react' -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import ActionButton from '@/app/components/base/action-button' diff --git a/web/app/components/app/duplicate-modal/index.spec.tsx b/web/app/components/app/duplicate-modal/index.spec.tsx index 7a8c33d0d2..f214f8e343 100644 --- a/web/app/components/app/duplicate-modal/index.spec.tsx +++ b/web/app/components/app/duplicate-modal/index.spec.tsx @@ -1,7 +1,7 @@ import type { ProviderContextState } from '@/context/provider-context' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import Toast from '@/app/components/base/toast' import { Plan } from '@/app/components/billing/type' import { baseProviderContextValue } from '@/context/provider-context' diff --git a/web/app/components/app/duplicate-modal/index.tsx b/web/app/components/app/duplicate-modal/index.tsx index 39b7d87304..420a6b159a 100644 --- a/web/app/components/app/duplicate-modal/index.tsx +++ b/web/app/components/app/duplicate-modal/index.tsx @@ -2,7 +2,8 @@ import type { AppIconType } from '@/types/app' import { RiCloseLine } from '@remixicon/react' import { noop } from 'lodash-es' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import Button from '@/app/components/base/button' diff --git a/web/app/components/app/log-annotation/index.tsx b/web/app/components/app/log-annotation/index.tsx index fd2a730ca1..27a70c29e7 100644 --- a/web/app/components/app/log-annotation/index.tsx +++ b/web/app/components/app/log-annotation/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { useRouter } from 'next/navigation' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import Annotation from '@/app/components/app/annotation' import Log from '@/app/components/app/log' diff --git a/web/app/components/app/log/empty-element.tsx b/web/app/components/app/log/empty-element.tsx index e19bc6e90e..792684587c 100644 --- a/web/app/components/app/log/empty-element.tsx +++ b/web/app/components/app/log/empty-element.tsx @@ -2,7 +2,7 @@ import type { FC, SVGProps } from 'react' import type { App } from '@/types/app' import Link from 'next/link' -import React from 'react' +import * as React from 'react' import { Trans, useTranslation } from 'react-i18next' import { AppModeEnum } from '@/types/app' import { getRedirectionPath } from '@/utils/app-redirection' diff --git a/web/app/components/app/log/filter.tsx b/web/app/components/app/log/filter.tsx index 34c39d822a..8984ff3494 100644 --- a/web/app/components/app/log/filter.tsx +++ b/web/app/components/app/log/filter.tsx @@ -4,7 +4,7 @@ import type { QueryParam } from './index' import { RiCalendarLine } from '@remixicon/react' import dayjs from 'dayjs' import quarterOfYear from 'dayjs/plugin/quarterOfYear' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import useSWR from 'swr' import Chip from '@/app/components/base/chip' diff --git a/web/app/components/app/log/index.tsx b/web/app/components/app/log/index.tsx index 4ac9a577a9..183826464f 100644 --- a/web/app/components/app/log/index.tsx +++ b/web/app/components/app/log/index.tsx @@ -5,7 +5,8 @@ import { useDebounce } from 'ahooks' import dayjs from 'dayjs' import { omit } from 'lodash-es' import { usePathname, useRouter, useSearchParams } from 'next/navigation' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import useSWR from 'swr' import Loading from '@/app/components/base/loading' diff --git a/web/app/components/app/log/list.tsx b/web/app/components/app/log/list.tsx index c29a47123c..06cd20b323 100644 --- a/web/app/components/app/log/list.tsx +++ b/web/app/components/app/log/list.tsx @@ -14,7 +14,8 @@ import timezone from 'dayjs/plugin/timezone' import utc from 'dayjs/plugin/utc' import { get, noop } from 'lodash-es' import { usePathname, useRouter, useSearchParams } from 'next/navigation' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import useSWR from 'swr' import { createContext, useContext } from 'use-context-selector' diff --git a/web/app/components/app/log/model-info.tsx b/web/app/components/app/log/model-info.tsx index 8094b74c28..c89ea61e18 100644 --- a/web/app/components/app/log/model-info.tsx +++ b/web/app/components/app/log/model-info.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import { RiInformation2Line, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { PortalToFollowElem, diff --git a/web/app/components/app/log/var-panel.tsx b/web/app/components/app/log/var-panel.tsx index f95f2de571..f41737dec3 100644 --- a/web/app/components/app/log/var-panel.tsx +++ b/web/app/components/app/log/var-panel.tsx @@ -5,7 +5,8 @@ import { RiArrowRightSLine, } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import ImagePreview from '@/app/components/base/image-uploader/image-preview' diff --git a/web/app/components/app/overview/apikey-info-panel/index.tsx b/web/app/components/app/overview/apikey-info-panel/index.tsx index e1a4ea0891..77e0eb99c2 100644 --- a/web/app/components/app/overview/apikey-info-panel/index.tsx +++ b/web/app/components/app/overview/apikey-info-panel/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { RiCloseLine } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general' diff --git a/web/app/components/app/overview/app-card.tsx b/web/app/components/app/overview/app-card.tsx index 7b33a05b5e..ac43828c36 100644 --- a/web/app/components/app/overview/app-card.tsx +++ b/web/app/components/app/overview/app-card.tsx @@ -15,7 +15,8 @@ import { RiWindowLine, } from '@remixicon/react' import { usePathname, useRouter } from 'next/navigation' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import AppBasic from '@/app/components/app-sidebar/basic' import { useStore as useAppStore } from '@/app/components/app/store' diff --git a/web/app/components/app/overview/app-chart.tsx b/web/app/components/app/overview/app-chart.tsx index 3c2a3734d0..d876dbda27 100644 --- a/web/app/components/app/overview/app-chart.tsx +++ b/web/app/components/app/overview/app-chart.tsx @@ -7,7 +7,7 @@ import dayjs from 'dayjs' import Decimal from 'decimal.js' import ReactECharts from 'echarts-for-react' import { get } from 'lodash-es' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Basic from '@/app/components/app-sidebar/basic' import Loading from '@/app/components/base/loading' diff --git a/web/app/components/app/overview/customize/index.tsx b/web/app/components/app/overview/customize/index.tsx index 9dd3e3816a..453c91f51b 100644 --- a/web/app/components/app/overview/customize/index.tsx +++ b/web/app/components/app/overview/customize/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' diff --git a/web/app/components/app/overview/embedded/index.tsx b/web/app/components/app/overview/embedded/index.tsx index 92ea8bc49a..5cad0b79fa 100644 --- a/web/app/components/app/overview/embedded/index.tsx +++ b/web/app/components/app/overview/embedded/index.tsx @@ -4,7 +4,8 @@ import { RiClipboardLine, } from '@remixicon/react' import copy from 'copy-to-clipboard' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import { useThemeContext } from '@/app/components/base/chat/embedded-chatbot/theme/theme-context' diff --git a/web/app/components/app/overview/settings/index.tsx b/web/app/components/app/overview/settings/index.tsx index 6464f40309..c75c6fe53e 100644 --- a/web/app/components/app/overview/settings/index.tsx +++ b/web/app/components/app/overview/settings/index.tsx @@ -5,7 +5,8 @@ import type { AppDetailResponse } from '@/models/app' import type { AppIconType, AppSSO, Language } from '@/types/app' import { RiArrowRightSLine, RiCloseLine } from '@remixicon/react' import Link from 'next/link' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import AppIcon from '@/app/components/base/app-icon' diff --git a/web/app/components/app/overview/trigger-card.tsx b/web/app/components/app/overview/trigger-card.tsx index 74b8e4100c..bcff6cb844 100644 --- a/web/app/components/app/overview/trigger-card.tsx +++ b/web/app/components/app/overview/trigger-card.tsx @@ -3,7 +3,7 @@ import type { AppDetailResponse } from '@/models/app' import type { AppTrigger } from '@/service/use-tools' import type { AppSSO } from '@/types/app' import Link from 'next/link' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { TriggerAll } from '@/app/components/base/icons/src/vender/workflow' import Switch from '@/app/components/base/switch' diff --git a/web/app/components/app/switch-app-modal/index.spec.tsx b/web/app/components/app/switch-app-modal/index.spec.tsx index 1f3c787dfa..abb8dcca2a 100644 --- a/web/app/components/app/switch-app-modal/index.spec.tsx +++ b/web/app/components/app/switch-app-modal/index.spec.tsx @@ -1,7 +1,7 @@ import type { App } from '@/types/app' import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import { ToastContext } from '@/app/components/base/toast' import { Plan } from '@/app/components/billing/type' import { NEED_REFRESH_APP_LIST_KEY } from '@/config' diff --git a/web/app/components/app/text-generate/item/index.tsx b/web/app/components/app/text-generate/item/index.tsx index 320bb10301..faa8c73999 100644 --- a/web/app/components/app/text-generate/item/index.tsx +++ b/web/app/components/app/text-generate/item/index.tsx @@ -17,7 +17,8 @@ import { import { useBoolean } from 'ahooks' import copy from 'copy-to-clipboard' import { useParams } from 'next/navigation' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useStore as useAppStore } from '@/app/components/app/store' import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' diff --git a/web/app/components/app/text-generate/saved-items/index.tsx b/web/app/components/app/text-generate/saved-items/index.tsx index 8e065d095e..59a4db8fcc 100644 --- a/web/app/components/app/text-generate/saved-items/index.tsx +++ b/web/app/components/app/text-generate/saved-items/index.tsx @@ -6,7 +6,7 @@ import { RiDeleteBinLine, } from '@remixicon/react' import copy from 'copy-to-clipboard' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import { Markdown } from '@/app/components/base/markdown' diff --git a/web/app/components/app/text-generate/saved-items/no-data/index.tsx b/web/app/components/app/text-generate/saved-items/no-data/index.tsx index ed60372bab..e93d2f5275 100644 --- a/web/app/components/app/text-generate/saved-items/no-data/index.tsx +++ b/web/app/components/app/text-generate/saved-items/no-data/index.tsx @@ -4,7 +4,7 @@ import { RiAddLine, RiBookmark3Line, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' diff --git a/web/app/components/app/type-selector/index.spec.tsx b/web/app/components/app/type-selector/index.spec.tsx index 0fb51e40a9..e24d963305 100644 --- a/web/app/components/app/type-selector/index.spec.tsx +++ b/web/app/components/app/type-selector/index.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen, within } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { AppModeEnum } from '@/types/app' import AppTypeSelector, { AppTypeIcon, AppTypeLabel } from './index' diff --git a/web/app/components/app/type-selector/index.tsx b/web/app/components/app/type-selector/index.tsx index 07e4b3f343..2e5a8286ab 100644 --- a/web/app/components/app/type-selector/index.tsx +++ b/web/app/components/app/type-selector/index.tsx @@ -1,5 +1,6 @@ import { RiArrowDownSLine, RiCloseCircleFill, RiExchange2Fill, RiFilter3Line } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { BubbleTextMod, ChatBot, ListSparkle, Logic } from '@/app/components/base/icons/src/vender/solid/communication' import { diff --git a/web/app/components/app/workflow-log/filter.tsx b/web/app/components/app/workflow-log/filter.tsx index 21e956ed50..9e3b213deb 100644 --- a/web/app/components/app/workflow-log/filter.tsx +++ b/web/app/components/app/workflow-log/filter.tsx @@ -4,7 +4,7 @@ import type { QueryParam } from './index' import { RiCalendarLine } from '@remixicon/react' import dayjs from 'dayjs' import quarterOfYear from 'dayjs/plugin/quarterOfYear' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { trackEvent } from '@/app/components/base/amplitude/utils' import Chip from '@/app/components/base/chip' diff --git a/web/app/components/app/workflow-log/index.tsx b/web/app/components/app/workflow-log/index.tsx index 14751ac809..1390f2d435 100644 --- a/web/app/components/app/workflow-log/index.tsx +++ b/web/app/components/app/workflow-log/index.tsx @@ -6,7 +6,8 @@ import dayjs from 'dayjs' import timezone from 'dayjs/plugin/timezone' import utc from 'dayjs/plugin/utc' import { omit } from 'lodash-es' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import useSWR from 'swr' import EmptyElement from '@/app/components/app/log/empty-element' diff --git a/web/app/components/app/workflow-log/list.tsx b/web/app/components/app/workflow-log/list.tsx index bafede0890..d3896e5227 100644 --- a/web/app/components/app/workflow-log/list.tsx +++ b/web/app/components/app/workflow-log/list.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { WorkflowAppLogDetail, WorkflowLogsResponse, WorkflowRunTriggeredFrom } from '@/models/log' import type { App } from '@/types/app' import { ArrowDownIcon } from '@heroicons/react/24/outline' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Drawer from '@/app/components/base/drawer' import Loading from '@/app/components/base/loading' diff --git a/web/app/components/app/workflow-log/trigger-by-display.tsx b/web/app/components/app/workflow-log/trigger-by-display.tsx index 736a75841e..f243bcd2f1 100644 --- a/web/app/components/app/workflow-log/trigger-by-display.tsx +++ b/web/app/components/app/workflow-log/trigger-by-display.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { TriggerMetadata } from '@/models/log' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { Code, diff --git a/web/app/components/apps/app-card.spec.tsx b/web/app/components/apps/app-card.spec.tsx index 60ee222837..b2afbabcb0 100644 --- a/web/app/components/apps/app-card.spec.tsx +++ b/web/app/components/apps/app-card.spec.tsx @@ -1,6 +1,6 @@ import type { Mock } from 'vitest' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { AccessMode } from '@/models/access-control' // Mock API services - import for direct manipulation import * as appsService from '@/service/apps' @@ -23,18 +23,15 @@ vi.mock('next/navigation', () => ({ // Mock use-context-selector with stable mockNotify reference for tracking calls // Include createContext for components that use it (like Toast) const mockNotify = vi.fn() -vi.mock('use-context-selector', () => { - const React = require('react') - return { - createContext: (defaultValue: any) => React.createContext(defaultValue), - useContext: () => ({ - notify: mockNotify, - }), - useContextSelector: (_context: any, selector: any) => selector({ - notify: mockNotify, - }), - } -}) +vi.mock('use-context-selector', () => ({ + createContext: (defaultValue: any) => React.createContext(defaultValue), + useContext: () => ({ + notify: mockNotify, + }), + useContextSelector: (_context: any, selector: any) => selector({ + notify: mockNotify, + }), +})) // Mock app context vi.mock('@/context/app-context', () => ({ @@ -108,73 +105,70 @@ vi.mock('@/utils/time', () => ({ })) // Mock dynamic imports -vi.mock('next/dynamic', () => { - const React = require('react') - return { - default: (importFn: () => Promise<any>) => { - const fnString = importFn.toString() +vi.mock('next/dynamic', () => ({ + default: (importFn: () => Promise<any>) => { + const fnString = importFn.toString() - if (fnString.includes('create-app-modal') || fnString.includes('explore/create-app-modal')) { - return function MockEditAppModal({ show, onHide, onConfirm }: any) { - if (!show) - return null - return React.createElement('div', { 'data-testid': 'edit-app-modal' }, React.createElement('button', { 'onClick': onHide, 'data-testid': 'close-edit-modal' }, 'Close'), React.createElement('button', { - 'onClick': () => onConfirm?.({ - name: 'Updated App', - icon_type: 'emoji', - icon: '🎯', - icon_background: '#FFEAD5', - description: 'Updated description', - use_icon_as_answer_icon: false, - max_active_requests: null, - }), - 'data-testid': 'confirm-edit-modal', - }, 'Confirm')) - } + if (fnString.includes('create-app-modal') || fnString.includes('explore/create-app-modal')) { + return function MockEditAppModal({ show, onHide, onConfirm }: any) { + if (!show) + return null + return React.createElement('div', { 'data-testid': 'edit-app-modal' }, React.createElement('button', { 'onClick': onHide, 'data-testid': 'close-edit-modal' }, 'Close'), React.createElement('button', { + 'onClick': () => onConfirm?.({ + name: 'Updated App', + icon_type: 'emoji', + icon: '🎯', + icon_background: '#FFEAD5', + description: 'Updated description', + use_icon_as_answer_icon: false, + max_active_requests: null, + }), + 'data-testid': 'confirm-edit-modal', + }, 'Confirm')) } - if (fnString.includes('duplicate-modal')) { - return function MockDuplicateAppModal({ show, onHide, onConfirm }: any) { - if (!show) - return null - return React.createElement('div', { 'data-testid': 'duplicate-modal' }, React.createElement('button', { 'onClick': onHide, 'data-testid': 'close-duplicate-modal' }, 'Close'), React.createElement('button', { - 'onClick': () => onConfirm?.({ - name: 'Copied App', - icon_type: 'emoji', - icon: '📋', - icon_background: '#E4FBCC', - }), - 'data-testid': 'confirm-duplicate-modal', - }, 'Confirm')) - } + } + if (fnString.includes('duplicate-modal')) { + return function MockDuplicateAppModal({ show, onHide, onConfirm }: any) { + if (!show) + return null + return React.createElement('div', { 'data-testid': 'duplicate-modal' }, React.createElement('button', { 'onClick': onHide, 'data-testid': 'close-duplicate-modal' }, 'Close'), React.createElement('button', { + 'onClick': () => onConfirm?.({ + name: 'Copied App', + icon_type: 'emoji', + icon: '📋', + icon_background: '#E4FBCC', + }), + 'data-testid': 'confirm-duplicate-modal', + }, 'Confirm')) } - if (fnString.includes('switch-app-modal')) { - return function MockSwitchAppModal({ show, onClose, onSuccess }: any) { - if (!show) - return null - return React.createElement('div', { 'data-testid': 'switch-modal' }, React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-switch-modal' }, 'Close'), React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'confirm-switch-modal' }, 'Switch')) - } + } + if (fnString.includes('switch-app-modal')) { + return function MockSwitchAppModal({ show, onClose, onSuccess }: any) { + if (!show) + return null + return React.createElement('div', { 'data-testid': 'switch-modal' }, React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-switch-modal' }, 'Close'), React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'confirm-switch-modal' }, 'Switch')) } - if (fnString.includes('base/confirm')) { - return function MockConfirm({ isShow, onCancel, onConfirm }: any) { - if (!isShow) - return null - return React.createElement('div', { 'data-testid': 'confirm-dialog' }, React.createElement('button', { 'onClick': onCancel, 'data-testid': 'cancel-confirm' }, 'Cancel'), React.createElement('button', { 'onClick': onConfirm, 'data-testid': 'confirm-confirm' }, 'Confirm')) - } + } + if (fnString.includes('base/confirm')) { + return function MockConfirm({ isShow, onCancel, onConfirm }: any) { + if (!isShow) + return null + return React.createElement('div', { 'data-testid': 'confirm-dialog' }, React.createElement('button', { 'onClick': onCancel, 'data-testid': 'cancel-confirm' }, 'Cancel'), React.createElement('button', { 'onClick': onConfirm, 'data-testid': 'confirm-confirm' }, 'Confirm')) } - if (fnString.includes('dsl-export-confirm-modal')) { - return function MockDSLExportModal({ onClose, onConfirm }: any) { - return React.createElement('div', { 'data-testid': 'dsl-export-modal' }, React.createElement('button', { 'onClick': () => onClose?.(), 'data-testid': 'close-dsl-export' }, 'Close'), React.createElement('button', { 'onClick': () => onConfirm?.(true), 'data-testid': 'confirm-dsl-export' }, 'Export with secrets'), React.createElement('button', { 'onClick': () => onConfirm?.(false), 'data-testid': 'confirm-dsl-export-no-secrets' }, 'Export without secrets')) - } + } + if (fnString.includes('dsl-export-confirm-modal')) { + return function MockDSLExportModal({ onClose, onConfirm }: any) { + return React.createElement('div', { 'data-testid': 'dsl-export-modal' }, React.createElement('button', { 'onClick': () => onClose?.(), 'data-testid': 'close-dsl-export' }, 'Close'), React.createElement('button', { 'onClick': () => onConfirm?.(true), 'data-testid': 'confirm-dsl-export' }, 'Export with secrets'), React.createElement('button', { 'onClick': () => onConfirm?.(false), 'data-testid': 'confirm-dsl-export-no-secrets' }, 'Export without secrets')) } - if (fnString.includes('app-access-control')) { - return function MockAccessControl({ onClose, onConfirm }: any) { - return React.createElement('div', { 'data-testid': 'access-control-modal' }, React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-access-control' }, 'Close'), React.createElement('button', { 'onClick': onConfirm, 'data-testid': 'confirm-access-control' }, 'Confirm')) - } + } + if (fnString.includes('app-access-control')) { + return function MockAccessControl({ onClose, onConfirm }: any) { + return React.createElement('div', { 'data-testid': 'access-control-modal' }, React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-access-control' }, 'Close'), React.createElement('button', { 'onClick': onConfirm, 'data-testid': 'confirm-access-control' }, 'Confirm')) } - return () => null - }, - } -}) + } + return () => null + }, +})) // Popover uses @headlessui/react portals - mock for controlled interaction testing vi.mock('@/app/components/base/popover', () => { @@ -202,7 +196,6 @@ vi.mock('@/app/components/base/tooltip', () => ({ vi.mock('@/app/components/base/tag-management/selector', () => ({ __esModule: true, default: ({ tags }: any) => { - const React = require('react') return React.createElement('div', { 'aria-label': 'tag-selector' }, tags?.map((tag: any) => React.createElement('span', { key: tag.id }, tag.name))) }, })) diff --git a/web/app/components/apps/app-card.tsx b/web/app/components/apps/app-card.tsx index 0490c771b0..3a3b9d6153 100644 --- a/web/app/components/apps/app-card.tsx +++ b/web/app/components/apps/app-card.tsx @@ -9,7 +9,8 @@ import type { App } from '@/types/app' import { RiBuildingLine, RiGlobalLine, RiLockLine, RiMoreFill, RiVerifiedBadgeLine } from '@remixicon/react' import dynamic from 'next/dynamic' import { useRouter } from 'next/navigation' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { AppTypeIcon } from '@/app/components/app/type-selector' diff --git a/web/app/components/apps/empty.spec.tsx b/web/app/components/apps/empty.spec.tsx index b7bd7f2d65..58a96f313a 100644 --- a/web/app/components/apps/empty.spec.tsx +++ b/web/app/components/apps/empty.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Empty from './empty' describe('Empty', () => { diff --git a/web/app/components/apps/empty.tsx b/web/app/components/apps/empty.tsx index 86e03a5fb4..b59efa9b35 100644 --- a/web/app/components/apps/empty.tsx +++ b/web/app/components/apps/empty.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' const DefaultCards = React.memo(() => { diff --git a/web/app/components/apps/footer.spec.tsx b/web/app/components/apps/footer.spec.tsx index 89fe6fa471..d93869b480 100644 --- a/web/app/components/apps/footer.spec.tsx +++ b/web/app/components/apps/footer.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Footer from './footer' describe('Footer', () => { diff --git a/web/app/components/apps/footer.tsx b/web/app/components/apps/footer.tsx index 49ac7a0eb4..09ec7ee7c4 100644 --- a/web/app/components/apps/footer.tsx +++ b/web/app/components/apps/footer.tsx @@ -1,6 +1,6 @@ import { RiDiscordFill, RiDiscussLine, RiGithubFill } from '@remixicon/react' import Link from 'next/link' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' type CustomLinkProps = { diff --git a/web/app/components/apps/index.spec.tsx b/web/app/components/apps/index.spec.tsx index 6bb53386a4..f518c5e039 100644 --- a/web/app/components/apps/index.spec.tsx +++ b/web/app/components/apps/index.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' // Import after mocks import Apps from './index' @@ -27,7 +27,6 @@ vi.mock('@/app/education-apply/hooks', () => ({ vi.mock('./list', () => ({ __esModule: true, default: () => { - const React = require('react') return React.createElement('div', { 'data-testid': 'apps-list' }, 'Apps List') }, })) diff --git a/web/app/components/apps/list.spec.tsx b/web/app/components/apps/list.spec.tsx index d39f2621c5..244a8d997d 100644 --- a/web/app/components/apps/list.spec.tsx +++ b/web/app/components/apps/list.spec.tsx @@ -1,5 +1,5 @@ import { act, fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { AppModeEnum } from '@/types/app' // Import after mocks @@ -141,7 +141,6 @@ let mockTagFilterOnChange: ((value: string[]) => void) | null = null vi.mock('@/app/components/base/tag-management/filter', () => ({ __esModule: true, default: ({ onChange }: { onChange: (value: string[]) => void }) => { - const React = require('react') mockTagFilterOnChange = onChange return React.createElement('div', { 'data-testid': 'tag-filter' }, 'common.tag.placeholder') }, @@ -161,7 +160,6 @@ vi.mock('@/hooks/use-pay', () => ({ vi.mock('ahooks', () => ({ useDebounceFn: (fn: () => void) => ({ run: fn }), useMount: (fn: () => void) => { - const React = require('react') const fnRef = React.useRef(fn) fnRef.current = fn React.useEffect(() => { @@ -171,28 +169,25 @@ vi.mock('ahooks', () => ({ })) // Mock dynamic imports -vi.mock('next/dynamic', () => { - const React = require('react') - return { - default: (importFn: () => Promise<any>) => { - const fnString = importFn.toString() +vi.mock('next/dynamic', () => ({ + default: (importFn: () => Promise<any>) => { + const fnString = importFn.toString() - if (fnString.includes('tag-management')) { - return function MockTagManagement() { - return React.createElement('div', { 'data-testid': 'tag-management-modal' }) - } + if (fnString.includes('tag-management')) { + return function MockTagManagement() { + return React.createElement('div', { 'data-testid': 'tag-management-modal' }) } - if (fnString.includes('create-from-dsl-modal')) { - return function MockCreateFromDSLModal({ show, onClose, onSuccess }: any) { - if (!show) - return null - return React.createElement('div', { 'data-testid': 'create-dsl-modal' }, React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-dsl-modal' }, 'Close'), React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-dsl-modal' }, 'Success')) - } + } + if (fnString.includes('create-from-dsl-modal')) { + return function MockCreateFromDSLModal({ show, onClose, onSuccess }: any) { + if (!show) + return null + return React.createElement('div', { 'data-testid': 'create-dsl-modal' }, React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-dsl-modal' }, 'Close'), React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-dsl-modal' }, 'Success')) } - return () => null - }, - } -}) + } + return () => null + }, +})) /** * Mock child components for focused List component testing. @@ -202,24 +197,19 @@ vi.mock('next/dynamic', () => { vi.mock('./app-card', () => ({ __esModule: true, default: ({ app }: any) => { - const React = require('react') return React.createElement('div', { 'data-testid': `app-card-${app.id}`, 'role': 'article' }, app.name) }, })) -vi.mock('./new-app-card', () => { - const React = require('react') - return { - default: React.forwardRef((_props: any, _ref: any) => { - return React.createElement('div', { 'data-testid': 'new-app-card', 'role': 'button' }, 'New App Card') - }), - } -}) +vi.mock('./new-app-card', () => ({ + default: React.forwardRef((_props: any, _ref: any) => { + return React.createElement('div', { 'data-testid': 'new-app-card', 'role': 'button' }, 'New App Card') + }), +})) vi.mock('./empty', () => ({ __esModule: true, default: () => { - const React = require('react') return React.createElement('div', { 'data-testid': 'empty-state', 'role': 'status' }, 'No apps found') }, })) @@ -227,7 +217,6 @@ vi.mock('./empty', () => ({ vi.mock('./footer', () => ({ __esModule: true, default: () => { - const React = require('react') return React.createElement('footer', { 'data-testid': 'footer', 'role': 'contentinfo' }, 'Footer') }, })) diff --git a/web/app/components/apps/new-app-card.spec.tsx b/web/app/components/apps/new-app-card.spec.tsx index bc8b10a2b6..92e769adc7 100644 --- a/web/app/components/apps/new-app-card.spec.tsx +++ b/web/app/components/apps/new-app-card.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' // Import after mocks import CreateAppCard from './new-app-card' @@ -22,37 +22,34 @@ vi.mock('@/context/provider-context', () => ({ })) // Mock next/dynamic to immediately resolve components -vi.mock('next/dynamic', () => { - const React = require('react') - return { - default: (importFn: () => Promise<any>) => { - const fnString = importFn.toString() +vi.mock('next/dynamic', () => ({ + default: (importFn: () => Promise<any>) => { + const fnString = importFn.toString() - if (fnString.includes('create-app-modal') && !fnString.includes('create-from-dsl-modal')) { - return function MockCreateAppModal({ show, onClose, onSuccess, onCreateFromTemplate }: any) { - if (!show) - return null - return React.createElement('div', { 'data-testid': 'create-app-modal' }, React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-create-modal' }, 'Close'), React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-create-modal' }, 'Success'), React.createElement('button', { 'onClick': onCreateFromTemplate, 'data-testid': 'to-template-modal' }, 'To Template')) - } + if (fnString.includes('create-app-modal') && !fnString.includes('create-from-dsl-modal')) { + return function MockCreateAppModal({ show, onClose, onSuccess, onCreateFromTemplate }: any) { + if (!show) + return null + return React.createElement('div', { 'data-testid': 'create-app-modal' }, React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-create-modal' }, 'Close'), React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-create-modal' }, 'Success'), React.createElement('button', { 'onClick': onCreateFromTemplate, 'data-testid': 'to-template-modal' }, 'To Template')) } - if (fnString.includes('create-app-dialog')) { - return function MockCreateAppTemplateDialog({ show, onClose, onSuccess, onCreateFromBlank }: any) { - if (!show) - return null - return React.createElement('div', { 'data-testid': 'create-template-dialog' }, React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-template-dialog' }, 'Close'), React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-template-dialog' }, 'Success'), React.createElement('button', { 'onClick': onCreateFromBlank, 'data-testid': 'to-blank-modal' }, 'To Blank')) - } + } + if (fnString.includes('create-app-dialog')) { + return function MockCreateAppTemplateDialog({ show, onClose, onSuccess, onCreateFromBlank }: any) { + if (!show) + return null + return React.createElement('div', { 'data-testid': 'create-template-dialog' }, React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-template-dialog' }, 'Close'), React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-template-dialog' }, 'Success'), React.createElement('button', { 'onClick': onCreateFromBlank, 'data-testid': 'to-blank-modal' }, 'To Blank')) } - if (fnString.includes('create-from-dsl-modal')) { - return function MockCreateFromDSLModal({ show, onClose, onSuccess }: any) { - if (!show) - return null - return React.createElement('div', { 'data-testid': 'create-dsl-modal' }, React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-dsl-modal' }, 'Close'), React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-dsl-modal' }, 'Success')) - } + } + if (fnString.includes('create-from-dsl-modal')) { + return function MockCreateFromDSLModal({ show, onClose, onSuccess }: any) { + if (!show) + return null + return React.createElement('div', { 'data-testid': 'create-dsl-modal' }, React.createElement('button', { 'onClick': onClose, 'data-testid': 'close-dsl-modal' }, 'Close'), React.createElement('button', { 'onClick': onSuccess, 'data-testid': 'success-dsl-modal' }, 'Success')) } - return () => null - }, - } -}) + } + return () => null + }, +})) // Mock CreateFromDSLModalTab enum vi.mock('@/app/components/app/create-from-dsl-modal', () => ({ diff --git a/web/app/components/apps/new-app-card.tsx b/web/app/components/apps/new-app-card.tsx index dd0918c746..2117b3d2d0 100644 --- a/web/app/components/apps/new-app-card.tsx +++ b/web/app/components/apps/new-app-card.tsx @@ -5,7 +5,8 @@ import { useRouter, useSearchParams, } from 'next/navigation' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { CreateFromDSLModalTab } from '@/app/components/app/create-from-dsl-modal' import { FileArrow01, FilePlus01, FilePlus02 } from '@/app/components/base/icons/src/vender/line/files' diff --git a/web/app/components/base/action-button/index.tsx b/web/app/components/base/action-button/index.tsx index 503847ba6b..0c86adb6db 100644 --- a/web/app/components/base/action-button/index.tsx +++ b/web/app/components/base/action-button/index.tsx @@ -1,7 +1,7 @@ import type { VariantProps } from 'class-variance-authority' import type { CSSProperties } from 'react' import { cva } from 'class-variance-authority' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' enum ActionButtonState { diff --git a/web/app/components/base/agent-log-modal/detail.tsx b/web/app/components/base/agent-log-modal/detail.tsx index c436e9e23a..5451126c9e 100644 --- a/web/app/components/base/agent-log-modal/detail.tsx +++ b/web/app/components/base/agent-log-modal/detail.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { IChatItem } from '@/app/components/base/chat/chat/type' import type { AgentIteration, AgentLogDetailResponse } from '@/models/log' import { flatten, uniq } from 'lodash-es' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { useStore as useAppStore } from '@/app/components/app/store' diff --git a/web/app/components/base/amplitude/AmplitudeProvider.tsx b/web/app/components/base/amplitude/AmplitudeProvider.tsx index ec4d586d48..91c3713a07 100644 --- a/web/app/components/base/amplitude/AmplitudeProvider.tsx +++ b/web/app/components/base/amplitude/AmplitudeProvider.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import * as amplitude from '@amplitude/analytics-browser' import { sessionReplayPlugin } from '@amplitude/plugin-session-replay-browser' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { AMPLITUDE_API_KEY, IS_CLOUD_EDITION } from '@/config' export type IAmplitudeProps = { diff --git a/web/app/components/base/app-icon/index.tsx b/web/app/components/base/app-icon/index.tsx index 01d4530b4d..78624bab07 100644 --- a/web/app/components/base/app-icon/index.tsx +++ b/web/app/components/base/app-icon/index.tsx @@ -6,7 +6,8 @@ import { RiEditLine } from '@remixicon/react' import { useHover } from 'ahooks' import { cva } from 'class-variance-authority' import { init } from 'emoji-mart' -import React, { useRef } from 'react' +import * as React from 'react' +import { useRef } from 'react' import { cn } from '@/utils/classnames' init({ data }) diff --git a/web/app/components/base/app-unavailable.tsx b/web/app/components/base/app-unavailable.tsx index 898f9dbd00..e25b8c9ca6 100644 --- a/web/app/components/base/app-unavailable.tsx +++ b/web/app/components/base/app-unavailable.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' diff --git a/web/app/components/base/audio-gallery/AudioPlayer.tsx b/web/app/components/base/audio-gallery/AudioPlayer.tsx index d1a245d193..859e27b08e 100644 --- a/web/app/components/base/audio-gallery/AudioPlayer.tsx +++ b/web/app/components/base/audio-gallery/AudioPlayer.tsx @@ -3,7 +3,8 @@ import { RiPlayLargeFill, } from '@remixicon/react' import { t } from 'i18next' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import Toast from '@/app/components/base/toast' import useTheme from '@/hooks/use-theme' import { Theme } from '@/types/app' diff --git a/web/app/components/base/audio-gallery/index.tsx b/web/app/components/base/audio-gallery/index.tsx index 2fdacab582..e5f0b7f46b 100644 --- a/web/app/components/base/audio-gallery/index.tsx +++ b/web/app/components/base/audio-gallery/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import AudioPlayer from './AudioPlayer' type Props = { diff --git a/web/app/components/base/badge/index.tsx b/web/app/components/base/badge/index.tsx index 663e40d2e5..9120b28ea6 100644 --- a/web/app/components/base/badge/index.tsx +++ b/web/app/components/base/badge/index.tsx @@ -1,7 +1,7 @@ import type { VariantProps } from 'class-variance-authority' import type { CSSProperties, ReactNode } from 'react' import { cva } from 'class-variance-authority' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import './index.css' diff --git a/web/app/components/base/block-input/index.tsx b/web/app/components/base/block-input/index.tsx index 51f5097e7d..1b80b21059 100644 --- a/web/app/components/base/block-input/index.tsx +++ b/web/app/components/base/block-input/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { ChangeEvent, FC } from 'react' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import { checkKeys } from '@/utils/var' diff --git a/web/app/components/base/button/add-button.tsx b/web/app/components/base/button/add-button.tsx index 762766239a..332b52daca 100644 --- a/web/app/components/base/button/add-button.tsx +++ b/web/app/components/base/button/add-button.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiAddLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type Props = { diff --git a/web/app/components/base/button/index.spec.tsx b/web/app/components/base/button/index.spec.tsx index aa83070964..0377fa334f 100644 --- a/web/app/components/base/button/index.spec.tsx +++ b/web/app/components/base/button/index.spec.tsx @@ -1,5 +1,5 @@ import { cleanup, fireEvent, render } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Button from './index' afterEach(cleanup) diff --git a/web/app/components/base/button/index.tsx b/web/app/components/base/button/index.tsx index 8d87174a3e..0de57617af 100644 --- a/web/app/components/base/button/index.tsx +++ b/web/app/components/base/button/index.tsx @@ -1,7 +1,7 @@ import type { VariantProps } from 'class-variance-authority' import type { CSSProperties } from 'react' import { cva } from 'class-variance-authority' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import Spinner from '../spinner' diff --git a/web/app/components/base/button/sync-button.tsx b/web/app/components/base/button/sync-button.tsx index 1e0a4a42f7..12c34026cb 100644 --- a/web/app/components/base/button/sync-button.tsx +++ b/web/app/components/base/button/sync-button.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiRefreshLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import TooltipPlus from '@/app/components/base/tooltip' import { cn } from '@/utils/classnames' diff --git a/web/app/components/base/chat/chat-with-history/header/operation.tsx b/web/app/components/base/chat/chat-with-history/header/operation.tsx index c279c7b785..fa239e198e 100644 --- a/web/app/components/base/chat/chat-with-history/header/operation.tsx +++ b/web/app/components/base/chat/chat-with-history/header/operation.tsx @@ -4,7 +4,8 @@ import type { FC } from 'react' import { RiArrowDownSLine, } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' import { cn } from '@/utils/classnames' diff --git a/web/app/components/base/chat/chat-with-history/inputs-form/content.tsx b/web/app/components/base/chat/chat-with-history/inputs-form/content.tsx index 105bbe52ea..0fd9a4cd9e 100644 --- a/web/app/components/base/chat/chat-with-history/inputs-form/content.tsx +++ b/web/app/components/base/chat/chat-with-history/inputs-form/content.tsx @@ -1,4 +1,5 @@ -import React, { memo, useCallback } from 'react' +import * as React from 'react' +import { memo, useCallback } from 'react' import { useTranslation } from 'react-i18next' import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader' import Input from '@/app/components/base/input' diff --git a/web/app/components/base/chat/chat-with-history/inputs-form/index.tsx b/web/app/components/base/chat/chat-with-history/inputs-form/index.tsx index d6e0a4003d..d3e52eb549 100644 --- a/web/app/components/base/chat/chat-with-history/inputs-form/index.tsx +++ b/web/app/components/base/chat/chat-with-history/inputs-form/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import InputsFormContent from '@/app/components/base/chat/chat-with-history/inputs-form/content' diff --git a/web/app/components/base/chat/chat-with-history/sidebar/operation.tsx b/web/app/components/base/chat/chat-with-history/sidebar/operation.tsx index c3084c5536..feff8d6a13 100644 --- a/web/app/components/base/chat/chat-with-history/sidebar/operation.tsx +++ b/web/app/components/base/chat/chat-with-history/sidebar/operation.tsx @@ -8,7 +8,8 @@ import { RiUnpinLine, } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' diff --git a/web/app/components/base/chat/chat-with-history/sidebar/rename-modal.tsx b/web/app/components/base/chat/chat-with-history/sidebar/rename-modal.tsx index 2d6168e21f..a4f5d48316 100644 --- a/web/app/components/base/chat/chat-with-history/sidebar/rename-modal.tsx +++ b/web/app/components/base/chat/chat-with-history/sidebar/rename-modal.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' diff --git a/web/app/components/base/chat/chat/citation/tooltip.tsx b/web/app/components/base/chat/chat/citation/tooltip.tsx index 24490a96c0..a4ac64c82f 100644 --- a/web/app/components/base/chat/chat/citation/tooltip.tsx +++ b/web/app/components/base/chat/chat/citation/tooltip.tsx @@ -1,5 +1,6 @@ import type { FC } from 'react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { PortalToFollowElem, PortalToFollowElemContent, diff --git a/web/app/components/base/chat/chat/loading-anim/index.tsx b/web/app/components/base/chat/chat/loading-anim/index.tsx index 496937332d..74cc3444de 100644 --- a/web/app/components/base/chat/chat/loading-anim/index.tsx +++ b/web/app/components/base/chat/chat/loading-anim/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import s from './style.module.css' diff --git a/web/app/components/base/chat/chat/thought/index.tsx b/web/app/components/base/chat/chat/thought/index.tsx index f77208541b..00b8676cd8 100644 --- a/web/app/components/base/chat/chat/thought/index.tsx +++ b/web/app/components/base/chat/chat/thought/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { ThoughtItem, ToolInfoInThought } from '../type' -import React from 'react' +import * as React from 'react' import ToolDetail from '@/app/components/base/chat/chat/answer/tool-detail' export type IThoughtProps = { diff --git a/web/app/components/base/chat/embedded-chatbot/header/index.tsx b/web/app/components/base/chat/embedded-chatbot/header/index.tsx index 8d0b1dfd07..84f8691aff 100644 --- a/web/app/components/base/chat/embedded-chatbot/header/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/header/index.tsx @@ -1,7 +1,8 @@ import type { FC } from 'react' import type { Theme } from '../theme/theme-context' import { RiCollapseDiagonal2Line, RiExpandDiagonal2Line, RiResetLeftLine } from '@remixicon/react' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import ViewFormDropdown from '@/app/components/base/chat/embedded-chatbot/inputs-form/view-form-dropdown' diff --git a/web/app/components/base/chat/embedded-chatbot/inputs-form/content.tsx b/web/app/components/base/chat/embedded-chatbot/inputs-form/content.tsx index 9c17013196..0136453149 100644 --- a/web/app/components/base/chat/embedded-chatbot/inputs-form/content.tsx +++ b/web/app/components/base/chat/embedded-chatbot/inputs-form/content.tsx @@ -1,4 +1,5 @@ -import React, { memo, useCallback } from 'react' +import * as React from 'react' +import { memo, useCallback } from 'react' import { useTranslation } from 'react-i18next' import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader' import Input from '@/app/components/base/input' diff --git a/web/app/components/base/chat/embedded-chatbot/inputs-form/index.tsx b/web/app/components/base/chat/embedded-chatbot/inputs-form/index.tsx index 20585067c1..b54f261f3d 100644 --- a/web/app/components/base/chat/embedded-chatbot/inputs-form/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/inputs-form/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import InputsFormContent from '@/app/components/base/chat/embedded-chatbot/inputs-form/content' diff --git a/web/app/components/base/confirm/index.tsx b/web/app/components/base/confirm/index.tsx index f9f4d3b1d7..590d28682b 100644 --- a/web/app/components/base/confirm/index.tsx +++ b/web/app/components/base/confirm/index.tsx @@ -1,4 +1,5 @@ -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { createPortal } from 'react-dom' import { useTranslation } from 'react-i18next' import Button from '../button' diff --git a/web/app/components/base/copy-feedback/index.tsx b/web/app/components/base/copy-feedback/index.tsx index 788dcee89c..bf809a2d18 100644 --- a/web/app/components/base/copy-feedback/index.tsx +++ b/web/app/components/base/copy-feedback/index.tsx @@ -5,7 +5,8 @@ import { } from '@remixicon/react' import copy from 'copy-to-clipboard' import { debounce } from 'lodash-es' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/base/copy-icon/index.tsx b/web/app/components/base/copy-icon/index.tsx index a7cb12c12b..935444a3c1 100644 --- a/web/app/components/base/copy-icon/index.tsx +++ b/web/app/components/base/copy-icon/index.tsx @@ -1,7 +1,8 @@ 'use client' import copy from 'copy-to-clipboard' import { debounce } from 'lodash-es' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { Copy, diff --git a/web/app/components/base/date-and-time-picker/calendar/days-of-week.tsx b/web/app/components/base/date-and-time-picker/calendar/days-of-week.tsx index 30aab7a30f..ac14d49ead 100644 --- a/web/app/components/base/date-and-time-picker/calendar/days-of-week.tsx +++ b/web/app/components/base/date-and-time-picker/calendar/days-of-week.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useDaysOfWeek } from '../hooks' export const DaysOfWeek = () => { diff --git a/web/app/components/base/date-and-time-picker/calendar/item.tsx b/web/app/components/base/date-and-time-picker/calendar/item.tsx index 8b6b33f96e..f79fed0879 100644 --- a/web/app/components/base/date-and-time-picker/calendar/item.tsx +++ b/web/app/components/base/date-and-time-picker/calendar/item.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import type { CalendarItemProps } from '../types' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import dayjs from '../utils/dayjs' diff --git a/web/app/components/base/date-and-time-picker/common/option-list-item.tsx b/web/app/components/base/date-and-time-picker/common/option-list-item.tsx index 916e4e3d88..040f30f686 100644 --- a/web/app/components/base/date-and-time-picker/common/option-list-item.tsx +++ b/web/app/components/base/date-and-time-picker/common/option-list-item.tsx @@ -1,5 +1,6 @@ import type { FC } from 'react' -import React, { useEffect, useRef } from 'react' +import * as React from 'react' +import { useEffect, useRef } from 'react' import { cn } from '@/utils/classnames' type OptionListItemProps = { diff --git a/web/app/components/base/date-and-time-picker/date-picker/footer.tsx b/web/app/components/base/date-and-time-picker/date-picker/footer.tsx index 44288b5109..d229564596 100644 --- a/web/app/components/base/date-and-time-picker/date-picker/footer.tsx +++ b/web/app/components/base/date-and-time-picker/date-picker/footer.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { DatePickerFooterProps } from '../types' import { RiTimeLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import Button from '../../button' diff --git a/web/app/components/base/date-and-time-picker/date-picker/header.tsx b/web/app/components/base/date-and-time-picker/date-picker/header.tsx index 811a8387e2..4177edfe7a 100644 --- a/web/app/components/base/date-and-time-picker/date-picker/header.tsx +++ b/web/app/components/base/date-and-time-picker/date-picker/header.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { DatePickerHeaderProps } from '../types' import { RiArrowDownSLine, RiArrowUpSLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useMonths } from '../hooks' const Header: FC<DatePickerHeaderProps> = ({ diff --git a/web/app/components/base/date-and-time-picker/date-picker/index.tsx b/web/app/components/base/date-and-time-picker/date-picker/index.tsx index 877b221dfc..75a3ec6144 100644 --- a/web/app/components/base/date-and-time-picker/date-picker/index.tsx +++ b/web/app/components/base/date-and-time-picker/date-picker/index.tsx @@ -1,7 +1,8 @@ import type { Dayjs } from 'dayjs' import type { DatePickerProps, Period } from '../types' import { RiCalendarLine, RiCloseCircleFill } from '@remixicon/react' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { PortalToFollowElem, diff --git a/web/app/components/base/date-and-time-picker/time-picker/footer.tsx b/web/app/components/base/date-and-time-picker/time-picker/footer.tsx index 41ab93e25e..74103fbf0b 100644 --- a/web/app/components/base/date-and-time-picker/time-picker/footer.tsx +++ b/web/app/components/base/date-and-time-picker/time-picker/footer.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import type { TimePickerFooterProps } from '../types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '../../button' diff --git a/web/app/components/base/date-and-time-picker/time-picker/header.tsx b/web/app/components/base/date-and-time-picker/time-picker/header.tsx index 3f74b8e20d..b5c7fb0bcb 100644 --- a/web/app/components/base/date-and-time-picker/time-picker/header.tsx +++ b/web/app/components/base/date-and-time-picker/time-picker/header.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' type Props = { diff --git a/web/app/components/base/date-and-time-picker/time-picker/index.spec.tsx b/web/app/components/base/date-and-time-picker/time-picker/index.spec.tsx index 5489002210..596b1a69b6 100644 --- a/web/app/components/base/date-and-time-picker/time-picker/index.spec.tsx +++ b/web/app/components/base/date-and-time-picker/time-picker/index.spec.tsx @@ -1,6 +1,6 @@ import type { TimePickerProps } from '../types' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import dayjs, { isDayjsObject } from '../utils/dayjs' import TimePicker from './index' diff --git a/web/app/components/base/date-and-time-picker/time-picker/index.tsx b/web/app/components/base/date-and-time-picker/time-picker/index.tsx index 874146b457..3e0ff2d353 100644 --- a/web/app/components/base/date-and-time-picker/time-picker/index.tsx +++ b/web/app/components/base/date-and-time-picker/time-picker/index.tsx @@ -1,7 +1,8 @@ import type { Dayjs } from 'dayjs' import type { TimePickerProps } from '../types' import { RiCloseCircleFill, RiTimeLine } from '@remixicon/react' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { PortalToFollowElem, diff --git a/web/app/components/base/date-and-time-picker/time-picker/options.tsx b/web/app/components/base/date-and-time-picker/time-picker/options.tsx index abd7e33b56..a4117f03fb 100644 --- a/web/app/components/base/date-and-time-picker/time-picker/options.tsx +++ b/web/app/components/base/date-and-time-picker/time-picker/options.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import type { TimeOptionsProps } from '../types' -import React from 'react' +import * as React from 'react' import OptionListItem from '../common/option-list-item' import { useTimeOptions } from '../hooks' diff --git a/web/app/components/base/date-and-time-picker/year-and-month-picker/footer.tsx b/web/app/components/base/date-and-time-picker/year-and-month-picker/footer.tsx index 883540a36b..b77ab0f8a7 100644 --- a/web/app/components/base/date-and-time-picker/year-and-month-picker/footer.tsx +++ b/web/app/components/base/date-and-time-picker/year-and-month-picker/footer.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import type { YearAndMonthPickerFooterProps } from '../types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '../../button' diff --git a/web/app/components/base/date-and-time-picker/year-and-month-picker/header.tsx b/web/app/components/base/date-and-time-picker/year-and-month-picker/header.tsx index 8e1af95550..043598884a 100644 --- a/web/app/components/base/date-and-time-picker/year-and-month-picker/header.tsx +++ b/web/app/components/base/date-and-time-picker/year-and-month-picker/header.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { YearAndMonthPickerHeaderProps } from '../types' import { RiArrowUpSLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useMonths } from '../hooks' const Header: FC<YearAndMonthPickerHeaderProps> = ({ diff --git a/web/app/components/base/date-and-time-picker/year-and-month-picker/options.tsx b/web/app/components/base/date-and-time-picker/year-and-month-picker/options.tsx index e0f5f5387f..9b0d602a3e 100644 --- a/web/app/components/base/date-and-time-picker/year-and-month-picker/options.tsx +++ b/web/app/components/base/date-and-time-picker/year-and-month-picker/options.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import type { YearAndMonthPickerOptionsProps } from '../types' -import React from 'react' +import * as React from 'react' import OptionListItem from '../common/option-list-item' import { useMonths, useYearOptions } from '../hooks' diff --git a/web/app/components/base/divider/index.tsx b/web/app/components/base/divider/index.tsx index 1a34be7397..cde3189ed0 100644 --- a/web/app/components/base/divider/index.tsx +++ b/web/app/components/base/divider/index.tsx @@ -1,7 +1,7 @@ import type { VariantProps } from 'class-variance-authority' import type { CSSProperties, FC } from 'react' import { cva } from 'class-variance-authority' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' const dividerVariants = cva('', { diff --git a/web/app/components/base/drawer-plus/index.tsx b/web/app/components/base/drawer-plus/index.tsx index 71f4372d35..0b59997885 100644 --- a/web/app/components/base/drawer-plus/index.tsx +++ b/web/app/components/base/drawer-plus/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { RiCloseLine } from '@remixicon/react' -import React, { useRef } from 'react' +import * as React from 'react' +import { useRef } from 'react' import Drawer from '@/app/components/base/drawer' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import { cn } from '@/utils/classnames' diff --git a/web/app/components/base/drawer/index.spec.tsx b/web/app/components/base/drawer/index.spec.tsx index dae574332c..51cf0fa55c 100644 --- a/web/app/components/base/drawer/index.spec.tsx +++ b/web/app/components/base/drawer/index.spec.tsx @@ -1,6 +1,6 @@ import type { IDrawerProps } from './index' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Drawer from './index' // Capture dialog onClose for testing diff --git a/web/app/components/base/effect/index.tsx b/web/app/components/base/effect/index.tsx index 85fc5a7cd8..dd3415dc07 100644 --- a/web/app/components/base/effect/index.tsx +++ b/web/app/components/base/effect/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type EffectProps = { diff --git a/web/app/components/base/emoji-picker/Inner.tsx b/web/app/components/base/emoji-picker/Inner.tsx index 4add3de9ba..f125cfa63b 100644 --- a/web/app/components/base/emoji-picker/Inner.tsx +++ b/web/app/components/base/emoji-picker/Inner.tsx @@ -8,7 +8,8 @@ import { MagnifyingGlassIcon, } from '@heroicons/react/24/outline' import { init } from 'emoji-mart' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import Divider from '@/app/components/base/divider' import Input from '@/app/components/base/input' import { cn } from '@/utils/classnames' diff --git a/web/app/components/base/emoji-picker/index.tsx b/web/app/components/base/emoji-picker/index.tsx index 0c04f25fb5..53bef278f6 100644 --- a/web/app/components/base/emoji-picker/index.tsx +++ b/web/app/components/base/emoji-picker/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { noop } from 'lodash-es' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' diff --git a/web/app/components/base/error-boundary/index.tsx b/web/app/components/base/error-boundary/index.tsx index d72621a754..b041bba4d6 100644 --- a/web/app/components/base/error-boundary/index.tsx +++ b/web/app/components/base/error-boundary/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { ErrorInfo, ReactNode } from 'react' import { RiAlertLine, RiBugLine } from '@remixicon/react' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import Button from '@/app/components/base/button' import { cn } from '@/utils/classnames' diff --git a/web/app/components/base/features/new-feature-panel/annotation-reply/annotation-ctrl-button.tsx b/web/app/components/base/features/new-feature-panel/annotation-reply/annotation-ctrl-button.tsx index 5d562315b1..fbeffa2422 100644 --- a/web/app/components/base/features/new-feature-panel/annotation-reply/annotation-ctrl-button.tsx +++ b/web/app/components/base/features/new-feature-panel/annotation-reply/annotation-ctrl-button.tsx @@ -4,7 +4,7 @@ import { RiEditLine, RiFileEditLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Toast from '@/app/components/base/toast' diff --git a/web/app/components/base/features/new-feature-panel/annotation-reply/config-param-modal.tsx b/web/app/components/base/features/new-feature-panel/annotation-reply/config-param-modal.tsx index 5e46e4d478..9050da6194 100644 --- a/web/app/components/base/features/new-feature-panel/annotation-reply/config-param-modal.tsx +++ b/web/app/components/base/features/new-feature-panel/annotation-reply/config-param-modal.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { AnnotationReplyConfig } from '@/models/debug' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' diff --git a/web/app/components/base/features/new-feature-panel/annotation-reply/config-param.tsx b/web/app/components/base/features/new-feature-panel/annotation-reply/config-param.tsx index f1a92f5628..9642aed0a8 100644 --- a/web/app/components/base/features/new-feature-panel/annotation-reply/config-param.tsx +++ b/web/app/components/base/features/new-feature-panel/annotation-reply/config-param.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import Tooltip from '@/app/components/base/tooltip' export const Item: FC<{ title: string, tooltip: string, children: React.JSX.Element }> = ({ diff --git a/web/app/components/base/features/new-feature-panel/annotation-reply/index.tsx b/web/app/components/base/features/new-feature-panel/annotation-reply/index.tsx index b6d0235bfd..6acd6cad70 100644 --- a/web/app/components/base/features/new-feature-panel/annotation-reply/index.tsx +++ b/web/app/components/base/features/new-feature-panel/annotation-reply/index.tsx @@ -3,7 +3,8 @@ import type { AnnotationReplyConfig } from '@/models/debug' import { RiEqualizer2Line, RiExternalLinkLine } from '@remixicon/react' import { produce } from 'immer' import { usePathname, useRouter } from 'next/navigation' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' diff --git a/web/app/components/base/features/new-feature-panel/annotation-reply/score-slider/index.tsx b/web/app/components/base/features/new-feature-panel/annotation-reply/score-slider/index.tsx index d04b56b228..a5fd2b75bf 100644 --- a/web/app/components/base/features/new-feature-panel/annotation-reply/score-slider/index.tsx +++ b/web/app/components/base/features/new-feature-panel/annotation-reply/score-slider/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Slider from '@/app/components/base/features/new-feature-panel/annotation-reply/score-slider/base-slider' diff --git a/web/app/components/base/features/new-feature-panel/annotation-reply/use-annotation-config.ts b/web/app/components/base/features/new-feature-panel/annotation-reply/use-annotation-config.ts index bd4de98daa..c74175846d 100644 --- a/web/app/components/base/features/new-feature-panel/annotation-reply/use-annotation-config.ts +++ b/web/app/components/base/features/new-feature-panel/annotation-reply/use-annotation-config.ts @@ -1,7 +1,8 @@ import type { EmbeddingModelConfig } from '@/app/components/app/annotation/type' import type { AnnotationReplyConfig } from '@/models/debug' import { produce } from 'immer' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { AnnotationEnableStatus, JobStatus } from '@/app/components/app/annotation/type' import { ANNOTATION_DEFAULT } from '@/config' import { useProviderContext } from '@/context/provider-context' diff --git a/web/app/components/base/features/new-feature-panel/citation.tsx b/web/app/components/base/features/new-feature-panel/citation.tsx index 321958bea4..e8854176f9 100644 --- a/web/app/components/base/features/new-feature-panel/citation.tsx +++ b/web/app/components/base/features/new-feature-panel/citation.tsx @@ -1,6 +1,7 @@ import type { OnFeaturesChange } from '@/app/components/base/features/types' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' import FeatureCard from '@/app/components/base/features/new-feature-panel/feature-card' diff --git a/web/app/components/base/features/new-feature-panel/conversation-opener/index.tsx b/web/app/components/base/features/new-feature-panel/conversation-opener/index.tsx index 0fef89fc33..3568ce64f5 100644 --- a/web/app/components/base/features/new-feature-panel/conversation-opener/index.tsx +++ b/web/app/components/base/features/new-feature-panel/conversation-opener/index.tsx @@ -3,7 +3,8 @@ import type { InputVar } from '@/app/components/workflow/types' import type { PromptVariable } from '@/models/debug' import { RiEditLine } from '@remixicon/react' import { produce } from 'immer' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' diff --git a/web/app/components/base/features/new-feature-panel/conversation-opener/modal.tsx b/web/app/components/base/features/new-feature-panel/conversation-opener/modal.tsx index cda5a25ae0..a1b66ae0fc 100644 --- a/web/app/components/base/features/new-feature-panel/conversation-opener/modal.tsx +++ b/web/app/components/base/features/new-feature-panel/conversation-opener/modal.tsx @@ -5,7 +5,8 @@ import { RiAddLine, RiAsterisk, RiCloseLine, RiDeleteBinLine, RiDraggable } from import { useBoolean } from 'ahooks' import { produce } from 'immer' import { noop } from 'lodash-es' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { ReactSortable } from 'react-sortablejs' import ConfirmAddVar from '@/app/components/app/configuration/config-prompt/confirm-add-var' diff --git a/web/app/components/base/features/new-feature-panel/feature-bar.tsx b/web/app/components/base/features/new-feature-panel/feature-bar.tsx index 585c2d923b..94b278c5de 100644 --- a/web/app/components/base/features/new-feature-panel/feature-bar.tsx +++ b/web/app/components/base/features/new-feature-panel/feature-bar.tsx @@ -1,5 +1,6 @@ import { RiApps2AddLine, RiArrowRightLine, RiSparklingFill } from '@remixicon/react' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { useFeatures } from '@/app/components/base/features/hooks' diff --git a/web/app/components/base/features/new-feature-panel/feature-card.tsx b/web/app/components/base/features/new-feature-panel/feature-card.tsx index b2b530e8bd..7b7327517b 100644 --- a/web/app/components/base/features/new-feature-panel/feature-card.tsx +++ b/web/app/components/base/features/new-feature-panel/feature-card.tsx @@ -1,7 +1,7 @@ import { RiQuestionLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import Switch from '@/app/components/base/switch' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/base/features/new-feature-panel/file-upload/index.tsx b/web/app/components/base/features/new-feature-panel/file-upload/index.tsx index 66f138037c..1af70ddb19 100644 --- a/web/app/components/base/features/new-feature-panel/file-upload/index.tsx +++ b/web/app/components/base/features/new-feature-panel/file-upload/index.tsx @@ -1,7 +1,8 @@ import type { OnFeaturesChange } from '@/app/components/base/features/types' import { RiEqualizer2Line } from '@remixicon/react' import { produce } from 'immer' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' diff --git a/web/app/components/base/features/new-feature-panel/file-upload/setting-content.tsx b/web/app/components/base/features/new-feature-panel/file-upload/setting-content.tsx index caa9a99e22..da306a6ac9 100644 --- a/web/app/components/base/features/new-feature-panel/file-upload/setting-content.tsx +++ b/web/app/components/base/features/new-feature-panel/file-upload/setting-content.tsx @@ -2,7 +2,8 @@ import type { OnFeaturesChange } from '@/app/components/base/features/types' import type { UploadFileSetting } from '@/app/components/workflow/types' import { RiCloseLine } from '@remixicon/react' import { produce } from 'immer' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' diff --git a/web/app/components/base/features/new-feature-panel/follow-up.tsx b/web/app/components/base/features/new-feature-panel/follow-up.tsx index ad651ab1e0..d45476c680 100644 --- a/web/app/components/base/features/new-feature-panel/follow-up.tsx +++ b/web/app/components/base/features/new-feature-panel/follow-up.tsx @@ -1,6 +1,7 @@ import type { OnFeaturesChange } from '@/app/components/base/features/types' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' import FeatureCard from '@/app/components/base/features/new-feature-panel/feature-card' diff --git a/web/app/components/base/features/new-feature-panel/image-upload/index.tsx b/web/app/components/base/features/new-feature-panel/image-upload/index.tsx index 043d061512..9c2990fb72 100644 --- a/web/app/components/base/features/new-feature-panel/image-upload/index.tsx +++ b/web/app/components/base/features/new-feature-panel/image-upload/index.tsx @@ -1,7 +1,8 @@ import type { OnFeaturesChange } from '@/app/components/base/features/types' import { RiEqualizer2Line, RiImage2Fill } from '@remixicon/react' import { produce } from 'immer' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Badge from '@/app/components/base/badge' import Button from '@/app/components/base/button' diff --git a/web/app/components/base/features/new-feature-panel/index.tsx b/web/app/components/base/features/new-feature-panel/index.tsx index 8ecbb2e42f..b6d260f03d 100644 --- a/web/app/components/base/features/new-feature-panel/index.tsx +++ b/web/app/components/base/features/new-feature-panel/index.tsx @@ -2,7 +2,7 @@ import type { OnFeaturesChange } from '@/app/components/base/features/types' import type { InputVar } from '@/app/components/workflow/types' import type { PromptVariable } from '@/models/debug' import { RiCloseLine, RiInformation2Fill } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import AnnotationReply from '@/app/components/base/features/new-feature-panel/annotation-reply' diff --git a/web/app/components/base/features/new-feature-panel/moderation/index.tsx b/web/app/components/base/features/new-feature-panel/moderation/index.tsx index 9fdb684f1b..d1b11aa8c8 100644 --- a/web/app/components/base/features/new-feature-panel/moderation/index.tsx +++ b/web/app/components/base/features/new-feature-panel/moderation/index.tsx @@ -1,7 +1,8 @@ import type { OnFeaturesChange } from '@/app/components/base/features/types' import { RiEqualizer2Line } from '@remixicon/react' import { produce } from 'immer' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Button from '@/app/components/base/button' diff --git a/web/app/components/base/features/new-feature-panel/more-like-this.tsx b/web/app/components/base/features/new-feature-panel/more-like-this.tsx index 4e2fbe07ac..98eb82c885 100644 --- a/web/app/components/base/features/new-feature-panel/more-like-this.tsx +++ b/web/app/components/base/features/new-feature-panel/more-like-this.tsx @@ -1,7 +1,8 @@ import type { OnFeaturesChange } from '@/app/components/base/features/types' import { RiSparklingFill } from '@remixicon/react' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' import FeatureCard from '@/app/components/base/features/new-feature-panel/feature-card' diff --git a/web/app/components/base/features/new-feature-panel/speech-to-text.tsx b/web/app/components/base/features/new-feature-panel/speech-to-text.tsx index 136f7f32fa..6e9a6b06e7 100644 --- a/web/app/components/base/features/new-feature-panel/speech-to-text.tsx +++ b/web/app/components/base/features/new-feature-panel/speech-to-text.tsx @@ -1,6 +1,7 @@ import type { OnFeaturesChange } from '@/app/components/base/features/types' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' import FeatureCard from '@/app/components/base/features/new-feature-panel/feature-card' diff --git a/web/app/components/base/features/new-feature-panel/text-to-speech/index.tsx b/web/app/components/base/features/new-feature-panel/text-to-speech/index.tsx index 482a3634cc..96a0eaa5ff 100644 --- a/web/app/components/base/features/new-feature-panel/text-to-speech/index.tsx +++ b/web/app/components/base/features/new-feature-panel/text-to-speech/index.tsx @@ -1,7 +1,8 @@ import type { OnFeaturesChange } from '@/app/components/base/features/types' import { RiEqualizer2Line } from '@remixicon/react' import { produce } from 'immer' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' diff --git a/web/app/components/base/features/new-feature-panel/text-to-speech/param-config-content.tsx b/web/app/components/base/features/new-feature-panel/text-to-speech/param-config-content.tsx index 53117d7999..fc1052e172 100644 --- a/web/app/components/base/features/new-feature-panel/text-to-speech/param-config-content.tsx +++ b/web/app/components/base/features/new-feature-panel/text-to-speech/param-config-content.tsx @@ -6,7 +6,8 @@ import { CheckIcon, ChevronDownIcon } from '@heroicons/react/20/solid' import { RiCloseLine } from '@remixicon/react' import { produce } from 'immer' import { usePathname } from 'next/navigation' -import React, { Fragment } from 'react' +import * as React from 'react' +import { Fragment } from 'react' import { useTranslation } from 'react-i18next' import AudioBtn from '@/app/components/base/audio-btn' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' diff --git a/web/app/components/base/file-thumb/image-render.tsx b/web/app/components/base/file-thumb/image-render.tsx index f21f0a84e5..bda092ffc3 100644 --- a/web/app/components/base/file-thumb/image-render.tsx +++ b/web/app/components/base/file-thumb/image-render.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' type ImageRenderProps = { sourceUrl: string diff --git a/web/app/components/base/file-thumb/index.tsx b/web/app/components/base/file-thumb/index.tsx index e3601c445a..09f88d5d3b 100644 --- a/web/app/components/base/file-thumb/index.tsx +++ b/web/app/components/base/file-thumb/index.tsx @@ -1,6 +1,7 @@ import type { VariantProps } from 'class-variance-authority' import { cva } from 'class-variance-authority' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { cn } from '@/utils/classnames' import { FileTypeIcon } from '../file-uploader' import { getFileAppearanceType } from '../file-uploader/utils' diff --git a/web/app/components/base/file-uploader/audio-preview.tsx b/web/app/components/base/file-uploader/audio-preview.tsx index 80ccc6225c..e8be22fc9f 100644 --- a/web/app/components/base/file-uploader/audio-preview.tsx +++ b/web/app/components/base/file-uploader/audio-preview.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import { RiCloseLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { createPortal } from 'react-dom' import { useHotkeys } from 'react-hotkeys-hook' diff --git a/web/app/components/base/file-uploader/file-list-in-log.tsx b/web/app/components/base/file-uploader/file-list-in-log.tsx index d35e6e096a..279341c2bb 100644 --- a/web/app/components/base/file-uploader/file-list-in-log.tsx +++ b/web/app/components/base/file-uploader/file-list-in-log.tsx @@ -1,6 +1,7 @@ import type { FileEntity } from './types' import { RiArrowRightSLine } from '@remixicon/react' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import { SupportUploadFileTypes } from '@/app/components/workflow/types' diff --git a/web/app/components/base/file-uploader/pdf-preview.tsx b/web/app/components/base/file-uploader/pdf-preview.tsx index 30aeccfbeb..04a90a414c 100644 --- a/web/app/components/base/file-uploader/pdf-preview.tsx +++ b/web/app/components/base/file-uploader/pdf-preview.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import { RiCloseLine, RiZoomInLine, RiZoomOutLine } from '@remixicon/react' import { t } from 'i18next' import { noop } from 'lodash-es' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { createPortal } from 'react-dom' import { useHotkeys } from 'react-hotkeys-hook' import { PdfHighlighter, PdfLoader } from 'react-pdf-highlighter' diff --git a/web/app/components/base/file-uploader/video-preview.tsx b/web/app/components/base/file-uploader/video-preview.tsx index ef0c5c17b1..94d9a94c58 100644 --- a/web/app/components/base/file-uploader/video-preview.tsx +++ b/web/app/components/base/file-uploader/video-preview.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import { RiCloseLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { createPortal } from 'react-dom' import { useHotkeys } from 'react-hotkeys-hook' diff --git a/web/app/components/base/form/components/field/file-uploader.tsx b/web/app/components/base/form/components/field/file-uploader.tsx index 278e8e0fa5..b498cfb951 100644 --- a/web/app/components/base/form/components/field/file-uploader.tsx +++ b/web/app/components/base/form/components/field/file-uploader.tsx @@ -1,7 +1,7 @@ import type { FileUploaderInAttachmentWrapperProps } from '../../../file-uploader/file-uploader-in-attachment' import type { FileEntity } from '../../../file-uploader/types' import type { LabelProps } from '../label' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import { useFieldContext } from '../..' import FileUploaderInAttachmentWrapper from '../../../file-uploader/file-uploader-in-attachment' diff --git a/web/app/components/base/form/components/field/input-type-select/option.tsx b/web/app/components/base/form/components/field/input-type-select/option.tsx index f0b9ee3068..d678a3e93d 100644 --- a/web/app/components/base/form/components/field/input-type-select/option.tsx +++ b/web/app/components/base/form/components/field/input-type-select/option.tsx @@ -1,5 +1,5 @@ import type { FileTypeSelectOption } from './types' -import React from 'react' +import * as React from 'react' import Badge from '@/app/components/base/badge' type OptionProps = { diff --git a/web/app/components/base/form/components/field/input-type-select/trigger.tsx b/web/app/components/base/form/components/field/input-type-select/trigger.tsx index bdd6796f70..eb26a49be9 100644 --- a/web/app/components/base/form/components/field/input-type-select/trigger.tsx +++ b/web/app/components/base/form/components/field/input-type-select/trigger.tsx @@ -1,6 +1,6 @@ import type { FileTypeSelectOption } from './types' import { RiArrowDownSLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Badge from '@/app/components/base/badge' import { cn } from '@/utils/classnames' diff --git a/web/app/components/base/form/components/field/number-input.tsx b/web/app/components/base/form/components/field/number-input.tsx index dcc4fd6663..a7844983ae 100644 --- a/web/app/components/base/form/components/field/number-input.tsx +++ b/web/app/components/base/form/components/field/number-input.tsx @@ -1,6 +1,6 @@ import type { InputNumberProps } from '../../../input-number' import type { LabelProps } from '../label' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import { useFieldContext } from '../..' import { InputNumber } from '../../../input-number' diff --git a/web/app/components/base/form/components/field/text-area.tsx b/web/app/components/base/form/components/field/text-area.tsx index 16979d3748..295482e56a 100644 --- a/web/app/components/base/form/components/field/text-area.tsx +++ b/web/app/components/base/form/components/field/text-area.tsx @@ -1,6 +1,6 @@ import type { TextareaProps } from '../../../textarea' import type { LabelProps } from '../label' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import { useFieldContext } from '../..' import Textarea from '../../../textarea' diff --git a/web/app/components/base/form/components/field/text.tsx b/web/app/components/base/form/components/field/text.tsx index 470033b58a..c9b46919a4 100644 --- a/web/app/components/base/form/components/field/text.tsx +++ b/web/app/components/base/form/components/field/text.tsx @@ -1,6 +1,6 @@ import type { InputProps } from '../../../input' import type { LabelProps } from '../label' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import { useFieldContext } from '../..' import Input from '../../../input' diff --git a/web/app/components/base/form/form-scenarios/base/field.tsx b/web/app/components/base/form/form-scenarios/base/field.tsx index d41499bbb2..6fc59d9062 100644 --- a/web/app/components/base/form/form-scenarios/base/field.tsx +++ b/web/app/components/base/form/form-scenarios/base/field.tsx @@ -1,6 +1,6 @@ import type { BaseConfiguration } from './types' import { useStore } from '@tanstack/react-form' -import React from 'react' +import * as React from 'react' import { withForm } from '../..' import { BaseFieldType } from './types' diff --git a/web/app/components/base/form/form-scenarios/base/index.tsx b/web/app/components/base/form/form-scenarios/base/index.tsx index 4f154cc2e5..af552e4a81 100644 --- a/web/app/components/base/form/form-scenarios/base/index.tsx +++ b/web/app/components/base/form/form-scenarios/base/index.tsx @@ -1,5 +1,6 @@ import type { BaseFormProps } from './types' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useAppForm } from '../..' import BaseField from './field' import { generateZodSchema } from './utils' diff --git a/web/app/components/base/form/form-scenarios/input-field/field.tsx b/web/app/components/base/form/form-scenarios/input-field/field.tsx index 0892cc2f45..e82ae8f914 100644 --- a/web/app/components/base/form/form-scenarios/input-field/field.tsx +++ b/web/app/components/base/form/form-scenarios/input-field/field.tsx @@ -1,6 +1,6 @@ import type { InputFieldConfiguration } from './types' import { useStore } from '@tanstack/react-form' -import React from 'react' +import * as React from 'react' import { withForm } from '../..' import { InputFieldType } from './types' diff --git a/web/app/components/base/form/form-scenarios/node-panel/field.tsx b/web/app/components/base/form/form-scenarios/node-panel/field.tsx index 5e2755a14c..6597d8fa32 100644 --- a/web/app/components/base/form/form-scenarios/node-panel/field.tsx +++ b/web/app/components/base/form/form-scenarios/node-panel/field.tsx @@ -1,6 +1,6 @@ import type { InputFieldConfiguration } from './types' import { useStore } from '@tanstack/react-form' -import React from 'react' +import * as React from 'react' import { withForm } from '../..' import { InputFieldType } from './types' diff --git a/web/app/components/base/ga/index.tsx b/web/app/components/base/ga/index.tsx index 91614c34e0..2d5fe101d0 100644 --- a/web/app/components/base/ga/index.tsx +++ b/web/app/components/base/ga/index.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import { headers } from 'next/headers' import Script from 'next/script' -import React from 'react' +import * as React from 'react' import { IS_CE_EDITION } from '@/config' export enum GaType { diff --git a/web/app/components/base/icons/IconBase.spec.tsx b/web/app/components/base/icons/IconBase.spec.tsx index badbbc5212..ba5efd8429 100644 --- a/web/app/components/base/icons/IconBase.spec.tsx +++ b/web/app/components/base/icons/IconBase.spec.tsx @@ -1,6 +1,6 @@ import type { IconData } from './IconBase' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import IconBase from './IconBase' import * as utils from './utils' diff --git a/web/app/components/base/icons/icon-gallery.stories.tsx b/web/app/components/base/icons/icon-gallery.stories.tsx index de7d251a1f..55322a7ea3 100644 --- a/web/app/components/base/icons/icon-gallery.stories.tsx +++ b/web/app/components/base/icons/icon-gallery.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/nextjs' -import React from 'react' +import * as React from 'react' declare const require: any diff --git a/web/app/components/base/icons/utils.ts b/web/app/components/base/icons/utils.ts index 76be8ecc46..9a15a0816d 100644 --- a/web/app/components/base/icons/utils.ts +++ b/web/app/components/base/icons/utils.ts @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' export type AbstractNode = { name: string diff --git a/web/app/components/base/image-gallery/index.tsx b/web/app/components/base/image-gallery/index.tsx index 438fcc5b11..8ca8be0fc7 100644 --- a/web/app/components/base/image-gallery/index.tsx +++ b/web/app/components/base/image-gallery/index.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import ImagePreview from '@/app/components/base/image-uploader/image-preview' import { cn } from '@/utils/classnames' import s from './style.module.css' diff --git a/web/app/components/base/image-uploader/image-preview.tsx b/web/app/components/base/image-uploader/image-preview.tsx index 37170fe1d9..bfabe5e247 100644 --- a/web/app/components/base/image-uploader/image-preview.tsx +++ b/web/app/components/base/image-uploader/image-preview.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import { RiAddBoxLine, RiCloseLine, RiDownloadCloud2Line, RiFileCopyLine, RiZoomInLine, RiZoomOutLine } from '@remixicon/react' import { t } from 'i18next' import { noop } from 'lodash-es' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { createPortal } from 'react-dom' import { useHotkeys } from 'react-hotkeys-hook' import Toast from '@/app/components/base/toast' diff --git a/web/app/components/base/inline-delete-confirm/index.spec.tsx b/web/app/components/base/inline-delete-confirm/index.spec.tsx index d42e482775..8b360929b5 100644 --- a/web/app/components/base/inline-delete-confirm/index.spec.tsx +++ b/web/app/components/base/inline-delete-confirm/index.spec.tsx @@ -1,5 +1,5 @@ import { cleanup, fireEvent, render } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import InlineDeleteConfirm from './index' // Mock react-i18next diff --git a/web/app/components/base/input-with-copy/index.spec.tsx b/web/app/components/base/input-with-copy/index.spec.tsx index b90e52adb9..782b2bab25 100644 --- a/web/app/components/base/input-with-copy/index.spec.tsx +++ b/web/app/components/base/input-with-copy/index.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import InputWithCopy from './index' // Create a mock function that we can track using vi.hoisted diff --git a/web/app/components/base/input-with-copy/index.tsx b/web/app/components/base/input-with-copy/index.tsx index 57848cb6bd..151fa435e7 100644 --- a/web/app/components/base/input-with-copy/index.tsx +++ b/web/app/components/base/input-with-copy/index.tsx @@ -3,7 +3,8 @@ import type { InputProps } from '../input' import { RiClipboardFill, RiClipboardLine } from '@remixicon/react' import copy from 'copy-to-clipboard' import { debounce } from 'lodash-es' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import ActionButton from '../action-button' diff --git a/web/app/components/base/input/index.spec.tsx b/web/app/components/base/input/index.spec.tsx index 7409559df1..226217a146 100644 --- a/web/app/components/base/input/index.spec.tsx +++ b/web/app/components/base/input/index.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Input, { inputVariants } from './index' // Mock the i18n hook diff --git a/web/app/components/base/input/index.tsx b/web/app/components/base/input/index.tsx index 948c15456d..98529a26bc 100644 --- a/web/app/components/base/input/index.tsx +++ b/web/app/components/base/input/index.tsx @@ -3,7 +3,7 @@ import type { ChangeEventHandler, CSSProperties, FocusEventHandler } from 'react import { RiCloseCircleFill, RiErrorWarningLine, RiSearchLine } from '@remixicon/react' import { cva } from 'class-variance-authority' import { noop } from 'lodash-es' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import { CopyFeedbackNew } from '../copy-feedback' diff --git a/web/app/components/base/linked-apps-panel/index.tsx b/web/app/components/base/linked-apps-panel/index.tsx index 00a562046a..adc8ccf729 100644 --- a/web/app/components/base/linked-apps-panel/index.tsx +++ b/web/app/components/base/linked-apps-panel/index.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import type { RelatedApp } from '@/models/datasets' import { RiArrowRightUpLine } from '@remixicon/react' import Link from 'next/link' -import React from 'react' +import * as React from 'react' import AppIcon from '@/app/components/base/app-icon' import { AppModeEnum } from '@/types/app' import { cn } from '@/utils/classnames' diff --git a/web/app/components/base/list-empty/index.tsx b/web/app/components/base/list-empty/index.tsx index ab1c968682..6d728e3aec 100644 --- a/web/app/components/base/list-empty/index.tsx +++ b/web/app/components/base/list-empty/index.tsx @@ -1,5 +1,5 @@ import type { ReactNode } from 'react' -import React from 'react' +import * as React from 'react' import { Variable02 } from '../icons/src/vender/solid/development' import HorizontalLine from './horizontal-line' import VerticalLine from './vertical-line' diff --git a/web/app/components/base/loading/index.spec.tsx b/web/app/components/base/loading/index.spec.tsx index 0003247586..5140f1216b 100644 --- a/web/app/components/base/loading/index.spec.tsx +++ b/web/app/components/base/loading/index.spec.tsx @@ -1,5 +1,5 @@ import { render } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Loading from './index' describe('Loading Component', () => { diff --git a/web/app/components/base/loading/index.tsx b/web/app/components/base/loading/index.tsx index 385d59b599..072b761833 100644 --- a/web/app/components/base/loading/index.tsx +++ b/web/app/components/base/loading/index.tsx @@ -1,6 +1,6 @@ 'use client' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import './style.css' diff --git a/web/app/components/base/markdown-blocks/audio-block.tsx b/web/app/components/base/markdown-blocks/audio-block.tsx index 09001f105b..8633c908d5 100644 --- a/web/app/components/base/markdown-blocks/audio-block.tsx +++ b/web/app/components/base/markdown-blocks/audio-block.tsx @@ -3,7 +3,8 @@ * Extracted from the main markdown renderer for modularity. * Uses the AudioGallery component to display audio players. */ -import React, { memo } from 'react' +import * as React from 'react' +import { memo } from 'react' import AudioGallery from '@/app/components/base/audio-gallery' const AudioBlock: any = memo(({ node }: any) => { diff --git a/web/app/components/base/markdown-blocks/form.tsx b/web/app/components/base/markdown-blocks/form.tsx index 19872738c7..2ccf216538 100644 --- a/web/app/components/base/markdown-blocks/form.tsx +++ b/web/app/components/base/markdown-blocks/form.tsx @@ -1,4 +1,5 @@ -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import Button from '@/app/components/base/button' import { useChatContext } from '@/app/components/base/chat/chat/context' import Checkbox from '@/app/components/base/checkbox' diff --git a/web/app/components/base/markdown-blocks/img.tsx b/web/app/components/base/markdown-blocks/img.tsx index 33fce13f0b..57182828a2 100644 --- a/web/app/components/base/markdown-blocks/img.tsx +++ b/web/app/components/base/markdown-blocks/img.tsx @@ -3,7 +3,7 @@ * Extracted from the main markdown renderer for modularity. * Uses the ImageGallery component to display images. */ -import React from 'react' +import * as React from 'react' import ImageGallery from '@/app/components/base/image-gallery' const Img = ({ src }: any) => { diff --git a/web/app/components/base/markdown-blocks/link.tsx b/web/app/components/base/markdown-blocks/link.tsx index ec2518990e..717352609f 100644 --- a/web/app/components/base/markdown-blocks/link.tsx +++ b/web/app/components/base/markdown-blocks/link.tsx @@ -3,7 +3,7 @@ * Extracted from the main markdown renderer for modularity. * Handles special rendering for "abbr:" type links for interactive chat actions. */ -import React from 'react' +import * as React from 'react' import { useChatContext } from '@/app/components/base/chat/chat/context' import { isValidUrl } from './utils' diff --git a/web/app/components/base/markdown-blocks/paragraph.tsx b/web/app/components/base/markdown-blocks/paragraph.tsx index fb1612477a..adef509a31 100644 --- a/web/app/components/base/markdown-blocks/paragraph.tsx +++ b/web/app/components/base/markdown-blocks/paragraph.tsx @@ -3,7 +3,7 @@ * Extracted from the main markdown renderer for modularity. * Handles special rendering for paragraphs that directly contain an image. */ -import React from 'react' +import * as React from 'react' import ImageGallery from '@/app/components/base/image-gallery' const Paragraph = (paragraph: any) => { diff --git a/web/app/components/base/markdown-blocks/plugin-img.tsx b/web/app/components/base/markdown-blocks/plugin-img.tsx index 486f5900d2..259f49ca9b 100644 --- a/web/app/components/base/markdown-blocks/plugin-img.tsx +++ b/web/app/components/base/markdown-blocks/plugin-img.tsx @@ -4,7 +4,8 @@ import type { SimplePluginInfo } from '../markdown/react-markdown-wrapper' * Extracted from the main markdown renderer for modularity. * Uses the ImageGallery component to display images. */ -import React, { useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useState } from 'react' import ImageGallery from '@/app/components/base/image-gallery' import { usePluginReadmeAsset } from '@/service/use-plugins' import { getMarkdownImageURL } from './utils' diff --git a/web/app/components/base/markdown-blocks/plugin-paragraph.tsx b/web/app/components/base/markdown-blocks/plugin-paragraph.tsx index dca4ee52a2..73189289f3 100644 --- a/web/app/components/base/markdown-blocks/plugin-paragraph.tsx +++ b/web/app/components/base/markdown-blocks/plugin-paragraph.tsx @@ -1,5 +1,6 @@ import type { SimplePluginInfo } from '../markdown/react-markdown-wrapper' -import React, { useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useState } from 'react' /** * @fileoverview Paragraph component for rendering <p> tags in Markdown. * Extracted from the main markdown renderer for modularity. diff --git a/web/app/components/base/markdown-blocks/pre-code.tsx b/web/app/components/base/markdown-blocks/pre-code.tsx index 9d10c42d3c..efce56a158 100644 --- a/web/app/components/base/markdown-blocks/pre-code.tsx +++ b/web/app/components/base/markdown-blocks/pre-code.tsx @@ -3,7 +3,8 @@ * Extracted from the main markdown renderer for modularity. * This is a simple wrapper around the HTML <pre> element. */ -import React, { useRef } from 'react' +import * as React from 'react' +import { useRef } from 'react' function PreCode(props: { children: any }) { const ref = useRef<HTMLPreElement>(null) diff --git a/web/app/components/base/markdown-blocks/think-block.tsx b/web/app/components/base/markdown-blocks/think-block.tsx index 20d2c5cc05..a2b0b3c293 100644 --- a/web/app/components/base/markdown-blocks/think-block.tsx +++ b/web/app/components/base/markdown-blocks/think-block.tsx @@ -1,4 +1,5 @@ -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import { useChatContext } from '../chat/chat/context' diff --git a/web/app/components/base/markdown-blocks/video-block.tsx b/web/app/components/base/markdown-blocks/video-block.tsx index 9f1a36f678..13ea490499 100644 --- a/web/app/components/base/markdown-blocks/video-block.tsx +++ b/web/app/components/base/markdown-blocks/video-block.tsx @@ -3,7 +3,8 @@ * Extracted from the main markdown renderer for modularity. * Uses the VideoGallery component to display videos. */ -import React, { memo } from 'react' +import * as React from 'react' +import { memo } from 'react' import VideoGallery from '@/app/components/base/video-gallery' const VideoBlock: any = memo(({ node }: any) => { diff --git a/web/app/components/base/markdown/error-boundary.tsx b/web/app/components/base/markdown/error-boundary.tsx index 9347491aea..fd11af5b1c 100644 --- a/web/app/components/base/markdown/error-boundary.tsx +++ b/web/app/components/base/markdown/error-boundary.tsx @@ -5,7 +5,8 @@ * logs those errors, and displays a fallback UI instead of the crashed component tree. * Primarily used around complex rendering logic like ECharts or SVG within Markdown. */ -import React, { Component } from 'react' +import * as React from 'react' +import { Component } from 'react' // **Add an ECharts runtime error handler // Avoid error #7832 (Crash when ECharts accesses undefined objects) // This can happen when a component attempts to access an undefined object that references an unregistered map, causing the program to crash. diff --git a/web/app/components/base/mermaid/index.tsx b/web/app/components/base/mermaid/index.tsx index 540df44a56..7721b8316e 100644 --- a/web/app/components/base/mermaid/index.tsx +++ b/web/app/components/base/mermaid/index.tsx @@ -2,7 +2,8 @@ import type { MermaidConfig } from 'mermaid' import { ExclamationTriangleIcon } from '@heroicons/react/24/outline' import { MoonIcon, SunIcon } from '@heroicons/react/24/solid' import mermaid from 'mermaid' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import LoadingAnim from '@/app/components/base/chat/chat/loading-anim' import ImagePreview from '@/app/components/base/image-uploader/image-preview' diff --git a/web/app/components/base/modal-like-wrap/index.tsx b/web/app/components/base/modal-like-wrap/index.tsx index 65c2873582..4c45ff2f7c 100644 --- a/web/app/components/base/modal-like-wrap/index.tsx +++ b/web/app/components/base/modal-like-wrap/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiCloseLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import Button from '../button' diff --git a/web/app/components/base/node-status/index.tsx b/web/app/components/base/node-status/index.tsx index 118ab7181a..3c39fa1fb3 100644 --- a/web/app/components/base/node-status/index.tsx +++ b/web/app/components/base/node-status/index.tsx @@ -3,7 +3,7 @@ import type { VariantProps } from 'class-variance-authority' import type { CSSProperties } from 'react' import { RiErrorWarningFill } from '@remixicon/react' import { cva } from 'class-variance-authority' -import React from 'react' +import * as React from 'react' import AlertTriangle from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback/AlertTriangle' import { cn } from '@/utils/classnames' diff --git a/web/app/components/base/notion-connector/index.tsx b/web/app/components/base/notion-connector/index.tsx index 48c4b3a584..cec3a9076d 100644 --- a/web/app/components/base/notion-connector/index.tsx +++ b/web/app/components/base/notion-connector/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '../button' import { Notion } from '../icons/src/public/common' diff --git a/web/app/components/base/notion-page-selector/credential-selector/index.tsx b/web/app/components/base/notion-page-selector/credential-selector/index.tsx index 810ec1f4e7..a7bfa1053d 100644 --- a/web/app/components/base/notion-page-selector/credential-selector/index.tsx +++ b/web/app/components/base/notion-page-selector/credential-selector/index.tsx @@ -1,7 +1,8 @@ 'use client' import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react' import { RiArrowDownSLine } from '@remixicon/react' -import React, { Fragment, useMemo } from 'react' +import * as React from 'react' +import { Fragment, useMemo } from 'react' import { CredentialIcon } from '@/app/components/datasets/common/credential-icon' export type NotionCredential = { diff --git a/web/app/components/base/pagination/hook.ts b/web/app/components/base/pagination/hook.ts index 366e1db822..25fa24cb26 100644 --- a/web/app/components/base/pagination/hook.ts +++ b/web/app/components/base/pagination/hook.ts @@ -1,5 +1,6 @@ import type { IPaginationProps, IUsePagination } from './type' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' const usePagination = ({ currentPage, diff --git a/web/app/components/base/pagination/index.tsx b/web/app/components/base/pagination/index.tsx index 45aa97c986..d85b8082f9 100644 --- a/web/app/components/base/pagination/index.tsx +++ b/web/app/components/base/pagination/index.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import { RiArrowLeftLine, RiArrowRightLine } from '@remixicon/react' import { useDebounceFn } from 'ahooks' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' diff --git a/web/app/components/base/pagination/pagination.tsx b/web/app/components/base/pagination/pagination.tsx index 599df58dc4..25bdf5a31b 100644 --- a/web/app/components/base/pagination/pagination.tsx +++ b/web/app/components/base/pagination/pagination.tsx @@ -5,7 +5,7 @@ import type { PageButtonProps, } from './type' import { noop } from 'lodash-es' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import usePagination from './hook' diff --git a/web/app/components/base/param-item/score-threshold-item.tsx b/web/app/components/base/param-item/score-threshold-item.tsx index 17feea2678..d7fe82e392 100644 --- a/web/app/components/base/param-item/score-threshold-item.tsx +++ b/web/app/components/base/param-item/score-threshold-item.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ParamItem from '.' diff --git a/web/app/components/base/param-item/top-k-item.tsx b/web/app/components/base/param-item/top-k-item.tsx index cfae5cf6a9..fcfd5a9f6d 100644 --- a/web/app/components/base/param-item/top-k-item.tsx +++ b/web/app/components/base/param-item/top-k-item.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ParamItem from '.' diff --git a/web/app/components/base/portal-to-follow-elem/index.spec.tsx b/web/app/components/base/portal-to-follow-elem/index.spec.tsx index 2b7cb83a53..f320cd2a74 100644 --- a/web/app/components/base/portal-to-follow-elem/index.spec.tsx +++ b/web/app/components/base/portal-to-follow-elem/index.spec.tsx @@ -1,5 +1,5 @@ import { cleanup, fireEvent, render } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '.' const useFloatingMock = vi.fn() diff --git a/web/app/components/base/portal-to-follow-elem/index.tsx b/web/app/components/base/portal-to-follow-elem/index.tsx index d2964e4c8a..a656ab5308 100644 --- a/web/app/components/base/portal-to-follow-elem/index.tsx +++ b/web/app/components/base/portal-to-follow-elem/index.tsx @@ -16,7 +16,8 @@ import { useRole, } from '@floating-ui/react' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { cn } from '@/utils/classnames' export type PortalToFollowElemOptions = { diff --git a/web/app/components/base/premium-badge/index.tsx b/web/app/components/base/premium-badge/index.tsx index 130729e135..50a5832a28 100644 --- a/web/app/components/base/premium-badge/index.tsx +++ b/web/app/components/base/premium-badge/index.tsx @@ -1,7 +1,7 @@ import type { VariantProps } from 'class-variance-authority' import type { CSSProperties, ReactNode } from 'react' import { cva } from 'class-variance-authority' -import React from 'react' +import * as React from 'react' import { Highlight } from '@/app/components/base/icons/src/public/common' import { cn } from '@/utils/classnames' import './index.css' diff --git a/web/app/components/base/prompt-editor/index.tsx b/web/app/components/base/prompt-editor/index.tsx index be1b6b79a5..99e3a3325c 100644 --- a/web/app/components/base/prompt-editor/index.tsx +++ b/web/app/components/base/prompt-editor/index.tsx @@ -26,7 +26,8 @@ import { $getRoot, TextNode, } from 'lexical' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { useEventEmitterContextContext } from '@/context/event-emitter' import { cn } from '@/utils/classnames' import { diff --git a/web/app/components/base/qrcode/index.tsx b/web/app/components/base/qrcode/index.tsx index 6f6752d2ae..664a4f104c 100644 --- a/web/app/components/base/qrcode/index.tsx +++ b/web/app/components/base/qrcode/index.tsx @@ -3,7 +3,8 @@ import { RiQrCodeLine, } from '@remixicon/react' import { QRCodeCanvas as QRCode } from 'qrcode.react' -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/base/radio-card/index.tsx b/web/app/components/base/radio-card/index.tsx index 7eb84ff2e4..ae8bb00099 100644 --- a/web/app/components/base/radio-card/index.tsx +++ b/web/app/components/base/radio-card/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { noop } from 'lodash-es' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type Props = { diff --git a/web/app/components/base/radio-card/simple/index.tsx b/web/app/components/base/radio-card/simple/index.tsx index 3a542599e1..d5f2f0ab9c 100644 --- a/web/app/components/base/radio-card/simple/index.tsx +++ b/web/app/components/base/radio-card/simple/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import s from './style.module.css' diff --git a/web/app/components/base/radio/index.tsx b/web/app/components/base/radio/index.tsx index 0a07600746..f5f18bac01 100644 --- a/web/app/components/base/radio/index.tsx +++ b/web/app/components/base/radio/index.tsx @@ -1,4 +1,4 @@ -import type React from 'react' +import type * as React from 'react' import type { IRadioProps } from './component/radio' import Group from './component/group' import RadioComps from './component/radio' diff --git a/web/app/components/base/radio/ui.tsx b/web/app/components/base/radio/ui.tsx index 2b2770c104..e836dddc96 100644 --- a/web/app/components/base/radio/ui.tsx +++ b/web/app/components/base/radio/ui.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type Props = { diff --git a/web/app/components/base/segmented-control/index.tsx b/web/app/components/base/segmented-control/index.tsx index b9c749befb..89a15a1f65 100644 --- a/web/app/components/base/segmented-control/index.tsx +++ b/web/app/components/base/segmented-control/index.tsx @@ -1,7 +1,7 @@ import type { RemixiconComponentType } from '@remixicon/react' import type { VariantProps } from 'class-variance-authority' import { cva } from 'class-variance-authority' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import Divider from '../divider' import './index.css' diff --git a/web/app/components/base/select/index.tsx b/web/app/components/base/select/index.tsx index 8ab8925366..2d134bf6df 100644 --- a/web/app/components/base/select/index.tsx +++ b/web/app/components/base/select/index.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import { Combobox, ComboboxButton, ComboboxInput, ComboboxOption, ComboboxOptions, Listbox, ListboxButton, ListboxOption, ListboxOptions } from '@headlessui/react' import { ChevronDownIcon, ChevronUpIcon, XMarkIcon } from '@heroicons/react/20/solid' import { RiCheckLine, RiLoader4Line } from '@remixicon/react' -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { PortalToFollowElem, diff --git a/web/app/components/base/spinner/index.spec.tsx b/web/app/components/base/spinner/index.spec.tsx index d6e8fde823..652d061206 100644 --- a/web/app/components/base/spinner/index.spec.tsx +++ b/web/app/components/base/spinner/index.spec.tsx @@ -1,5 +1,5 @@ import { render } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Spinner from './index' describe('Spinner component', () => { diff --git a/web/app/components/base/spinner/index.tsx b/web/app/components/base/spinner/index.tsx index f3810f4cff..1c66a127f6 100644 --- a/web/app/components/base/spinner/index.tsx +++ b/web/app/components/base/spinner/index.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import * as React from 'react' type Props = { loading?: boolean diff --git a/web/app/components/base/svg/index.tsx b/web/app/components/base/svg/index.tsx index e8e0fad9bd..4ebfa5f3d0 100644 --- a/web/app/components/base/svg/index.tsx +++ b/web/app/components/base/svg/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import ActionButton from '../action-button' import s from './style.module.css' diff --git a/web/app/components/base/switch/index.tsx b/web/app/components/base/switch/index.tsx index 4e32b22440..6296a33141 100644 --- a/web/app/components/base/switch/index.tsx +++ b/web/app/components/base/switch/index.tsx @@ -1,6 +1,7 @@ 'use client' import { Switch as OriginalSwitch } from '@headlessui/react' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { cn } from '@/utils/classnames' type SwitchProps = { diff --git a/web/app/components/base/tab-header/index.tsx b/web/app/components/base/tab-header/index.tsx index b4dfd1f526..e762e23232 100644 --- a/web/app/components/base/tab-header/index.tsx +++ b/web/app/components/base/tab-header/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type Item = { diff --git a/web/app/components/base/tab-slider-plain/index.tsx b/web/app/components/base/tab-slider-plain/index.tsx index 4ef4d916c9..5b8eb270ee 100644 --- a/web/app/components/base/tab-slider-plain/index.tsx +++ b/web/app/components/base/tab-slider-plain/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type Option = { diff --git a/web/app/components/base/tag-management/panel.tsx b/web/app/components/base/tag-management/panel.tsx index 42414e4d3d..854de012a5 100644 --- a/web/app/components/base/tag-management/panel.tsx +++ b/web/app/components/base/tag-management/panel.tsx @@ -4,7 +4,8 @@ import type { Tag } from '@/app/components/base/tag-management/constant' import { RiAddLine, RiPriceTag3Line } from '@remixicon/react' import { useUnmount } from 'ahooks' import { noop } from 'lodash-es' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Checkbox from '@/app/components/base/checkbox' diff --git a/web/app/components/base/tag-management/trigger.tsx b/web/app/components/base/tag-management/trigger.tsx index 84d3af55d3..471e8cfa3d 100644 --- a/web/app/components/base/tag-management/trigger.tsx +++ b/web/app/components/base/tag-management/trigger.tsx @@ -1,5 +1,5 @@ import { RiPriceTag3Line } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' type TriggerProps = { diff --git a/web/app/components/base/tag/index.tsx b/web/app/components/base/tag/index.tsx index 38a686fa8e..fa225e0f84 100644 --- a/web/app/components/base/tag/index.tsx +++ b/web/app/components/base/tag/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' export type ITagProps = { diff --git a/web/app/components/base/textarea/index.tsx b/web/app/components/base/textarea/index.tsx index 039703d828..bea9a7bd41 100644 --- a/web/app/components/base/textarea/index.tsx +++ b/web/app/components/base/textarea/index.tsx @@ -1,7 +1,7 @@ import type { VariantProps } from 'class-variance-authority' import type { CSSProperties } from 'react' import { cva } from 'class-variance-authority' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' const textareaVariants = cva( diff --git a/web/app/components/base/timezone-label/__tests__/index.test.tsx b/web/app/components/base/timezone-label/__tests__/index.test.tsx index 9fb1c6127b..15aa4ee685 100644 --- a/web/app/components/base/timezone-label/__tests__/index.test.tsx +++ b/web/app/components/base/timezone-label/__tests__/index.test.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import TimezoneLabel from '../index' // Mock the convertTimezoneToOffsetStr function diff --git a/web/app/components/base/timezone-label/index.tsx b/web/app/components/base/timezone-label/index.tsx index 2904555e5b..f614280b3e 100644 --- a/web/app/components/base/timezone-label/index.tsx +++ b/web/app/components/base/timezone-label/index.tsx @@ -1,4 +1,5 @@ -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { convertTimezoneToOffsetStr } from '@/app/components/base/date-and-time-picker/utils/dayjs' import { cn } from '@/utils/classnames' diff --git a/web/app/components/base/toast/index.spec.tsx b/web/app/components/base/toast/index.spec.tsx index 94b3a81d54..d32619f59a 100644 --- a/web/app/components/base/toast/index.spec.tsx +++ b/web/app/components/base/toast/index.spec.tsx @@ -1,7 +1,7 @@ import type { ReactNode } from 'react' import { act, render, screen, waitFor } from '@testing-library/react' import { noop } from 'lodash-es' -import React from 'react' +import * as React from 'react' import Toast, { ToastProvider, useToastContext } from '.' const TestComponent = () => { diff --git a/web/app/components/base/toast/index.tsx b/web/app/components/base/toast/index.tsx index f120bdeb7c..a016778996 100644 --- a/web/app/components/base/toast/index.tsx +++ b/web/app/components/base/toast/index.tsx @@ -8,7 +8,8 @@ import { RiInformation2Fill, } from '@remixicon/react' import { noop } from 'lodash-es' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { createRoot } from 'react-dom/client' import { createContext, useContext } from 'use-context-selector' import ActionButton from '@/app/components/base/action-button' diff --git a/web/app/components/base/tooltip/index.spec.tsx b/web/app/components/base/tooltip/index.spec.tsx index 8f77db71f6..66d3157ddc 100644 --- a/web/app/components/base/tooltip/index.spec.tsx +++ b/web/app/components/base/tooltip/index.spec.tsx @@ -1,5 +1,5 @@ import { act, cleanup, fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Tooltip from './index' afterEach(cleanup) diff --git a/web/app/components/base/tooltip/index.tsx b/web/app/components/base/tooltip/index.tsx index db570ac6ca..3bd379b318 100644 --- a/web/app/components/base/tooltip/index.tsx +++ b/web/app/components/base/tooltip/index.tsx @@ -3,7 +3,8 @@ import type { OffsetOptions, Placement } from '@floating-ui/react' import type { FC } from 'react' import { RiQuestionLine } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' import { cn } from '@/utils/classnames' import { tooltipManager } from './TooltipManager' diff --git a/web/app/components/base/video-gallery/VideoPlayer.tsx b/web/app/components/base/video-gallery/VideoPlayer.tsx index f94b97ed90..8adaf71f58 100644 --- a/web/app/components/base/video-gallery/VideoPlayer.tsx +++ b/web/app/components/base/video-gallery/VideoPlayer.tsx @@ -1,4 +1,5 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import styles from './VideoPlayer.module.css' type VideoPlayerProps = { diff --git a/web/app/components/base/video-gallery/index.tsx b/web/app/components/base/video-gallery/index.tsx index 87133b94b9..b058b0b08a 100644 --- a/web/app/components/base/video-gallery/index.tsx +++ b/web/app/components/base/video-gallery/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import VideoPlayer from './VideoPlayer' type Props = { diff --git a/web/app/components/base/with-input-validation/index.tsx b/web/app/components/base/with-input-validation/index.tsx index f8ea354208..9a78dbb298 100644 --- a/web/app/components/base/with-input-validation/index.tsx +++ b/web/app/components/base/with-input-validation/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { ZodSchema } from 'zod' -import React from 'react' +import * as React from 'react' function withValidation<T extends Record<string, unknown>, K extends keyof T>( WrappedComponent: React.ComponentType<T>, diff --git a/web/app/components/billing/annotation-full/index.tsx b/web/app/components/billing/annotation-full/index.tsx index 090e383e22..fc770a9357 100644 --- a/web/app/components/billing/annotation-full/index.tsx +++ b/web/app/components/billing/annotation-full/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import GridMask from '@/app/components/base/grid-mask' import { cn } from '@/utils/classnames' diff --git a/web/app/components/billing/annotation-full/modal.tsx b/web/app/components/billing/annotation-full/modal.tsx index 0474fc0722..0e4fbfee32 100644 --- a/web/app/components/billing/annotation-full/modal.tsx +++ b/web/app/components/billing/annotation-full/modal.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import GridMask from '@/app/components/base/grid-mask' import { cn } from '@/utils/classnames' diff --git a/web/app/components/billing/annotation-full/usage.tsx b/web/app/components/billing/annotation-full/usage.tsx index 7ab984d04f..eb2b79cc89 100644 --- a/web/app/components/billing/annotation-full/usage.tsx +++ b/web/app/components/billing/annotation-full/usage.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useProviderContext } from '@/context/provider-context' import { MessageFastPlus } from '../../base/icons/src/vender/line/communication' diff --git a/web/app/components/billing/apps-full-in-dialog/index.tsx b/web/app/components/billing/apps-full-in-dialog/index.tsx index dbcbe49081..c69378c2f1 100644 --- a/web/app/components/billing/apps-full-in-dialog/index.tsx +++ b/web/app/components/billing/apps-full-in-dialog/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import ProgressBar from '@/app/components/billing/progress-bar' diff --git a/web/app/components/billing/billing-page/index.tsx b/web/app/components/billing/billing-page/index.tsx index c754386a3f..ee68cafe2e 100644 --- a/web/app/components/billing/billing-page/index.tsx +++ b/web/app/components/billing/billing-page/index.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import { RiArrowRightUpLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useAppContext } from '@/context/app-context' import { useProviderContext } from '@/context/provider-context' diff --git a/web/app/components/billing/header-billing-btn/index.tsx b/web/app/components/billing/header-billing-btn/index.tsx index 049ee214c0..862093c210 100644 --- a/web/app/components/billing/header-billing-btn/index.tsx +++ b/web/app/components/billing/header-billing-btn/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useProviderContext } from '@/context/provider-context' import { cn } from '@/utils/classnames' import { Plan } from '../type' diff --git a/web/app/components/billing/partner-stack/index.tsx b/web/app/components/billing/partner-stack/index.tsx index 06a7905475..e7b954a576 100644 --- a/web/app/components/billing/partner-stack/index.tsx +++ b/web/app/components/billing/partner-stack/index.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { IS_CLOUD_EDITION } from '@/config' import usePSInfo from './use-ps-info' diff --git a/web/app/components/billing/plan-upgrade-modal/index.spec.tsx b/web/app/components/billing/plan-upgrade-modal/index.spec.tsx index 215218d42a..9dbe115a89 100644 --- a/web/app/components/billing/plan-upgrade-modal/index.spec.tsx +++ b/web/app/components/billing/plan-upgrade-modal/index.spec.tsx @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import PlanUpgradeModal from './index' const mockSetShowPricingModal = vi.fn() diff --git a/web/app/components/billing/plan-upgrade-modal/index.tsx b/web/app/components/billing/plan-upgrade-modal/index.tsx index 7bf8c1bb0d..0d5dccdd73 100644 --- a/web/app/components/billing/plan-upgrade-modal/index.tsx +++ b/web/app/components/billing/plan-upgrade-modal/index.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' diff --git a/web/app/components/billing/plan/assets/sandbox.spec.tsx b/web/app/components/billing/plan/assets/sandbox.spec.tsx index 0c70f979df..024213cf5a 100644 --- a/web/app/components/billing/plan/assets/sandbox.spec.tsx +++ b/web/app/components/billing/plan/assets/sandbox.spec.tsx @@ -1,5 +1,5 @@ import { render } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Sandbox from './sandbox' describe('Sandbox Icon Component', () => { diff --git a/web/app/components/billing/plan/assets/sandbox.tsx b/web/app/components/billing/plan/assets/sandbox.tsx index 22230f1c3d..0839e8d979 100644 --- a/web/app/components/billing/plan/assets/sandbox.tsx +++ b/web/app/components/billing/plan/assets/sandbox.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' const Sandbox = () => { return ( diff --git a/web/app/components/billing/plan/index.tsx b/web/app/components/billing/plan/index.tsx index b08a9037ca..9f826f5255 100644 --- a/web/app/components/billing/plan/index.tsx +++ b/web/app/components/billing/plan/index.tsx @@ -8,7 +8,8 @@ import { } from '@remixicon/react' import { useUnmountedRef } from 'ahooks' import { usePathname, useRouter } from 'next/navigation' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { ApiAggregate, TriggerAll } from '@/app/components/base/icons/src/vender/workflow' diff --git a/web/app/components/billing/pricing/footer.tsx b/web/app/components/billing/pricing/footer.tsx index f7842327a2..48b4a9f4bd 100644 --- a/web/app/components/billing/pricing/footer.tsx +++ b/web/app/components/billing/pricing/footer.tsx @@ -1,7 +1,7 @@ import type { Category } from '.' import { RiArrowRightUpLine } from '@remixicon/react' import Link from 'next/link' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import { CategoryEnum } from '.' diff --git a/web/app/components/billing/pricing/header.tsx b/web/app/components/billing/pricing/header.tsx index 55f9960eee..c282664e9c 100644 --- a/web/app/components/billing/pricing/header.tsx +++ b/web/app/components/billing/pricing/header.tsx @@ -1,5 +1,5 @@ import { RiCloseLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '../../base/button' import DifyLogo from '../../base/logo/dify-logo' diff --git a/web/app/components/billing/pricing/index.tsx b/web/app/components/billing/pricing/index.tsx index d726fa7256..2b58158146 100644 --- a/web/app/components/billing/pricing/index.tsx +++ b/web/app/components/billing/pricing/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { useKeyPress } from 'ahooks' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { createPortal } from 'react-dom' import { useAppContext } from '@/context/app-context' import { useGetPricingPageLanguage } from '@/context/i18n' diff --git a/web/app/components/billing/pricing/plan-switcher/index.tsx b/web/app/components/billing/pricing/plan-switcher/index.tsx index 3a25c2a569..416a645438 100644 --- a/web/app/components/billing/pricing/plan-switcher/index.tsx +++ b/web/app/components/billing/pricing/plan-switcher/index.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { Category } from '../index' import type { PlanRange } from './plan-range-switcher' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import { Cloud, SelfHosted } from '../assets' diff --git a/web/app/components/billing/pricing/plan-switcher/plan-range-switcher.tsx b/web/app/components/billing/pricing/plan-switcher/plan-range-switcher.tsx index 770a19505b..080ae42fbe 100644 --- a/web/app/components/billing/pricing/plan-switcher/plan-range-switcher.tsx +++ b/web/app/components/billing/pricing/plan-switcher/plan-range-switcher.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Switch from '../../../base/switch' diff --git a/web/app/components/billing/pricing/plan-switcher/tab.tsx b/web/app/components/billing/pricing/plan-switcher/tab.tsx index fab24addf8..db1632e8df 100644 --- a/web/app/components/billing/pricing/plan-switcher/tab.tsx +++ b/web/app/components/billing/pricing/plan-switcher/tab.tsx @@ -1,4 +1,5 @@ -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { cn } from '@/utils/classnames' type TabProps<T> = { diff --git a/web/app/components/billing/pricing/plans/cloud-plan-item/button.spec.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/button.spec.tsx index ed6c7e4efb..d57f1c022d 100644 --- a/web/app/components/billing/pricing/plans/cloud-plan-item/button.spec.tsx +++ b/web/app/components/billing/pricing/plans/cloud-plan-item/button.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { Plan } from '../../../type' import Button from './button' diff --git a/web/app/components/billing/pricing/plans/cloud-plan-item/button.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/button.tsx index 8cf7351790..9cdef8e333 100644 --- a/web/app/components/billing/pricing/plans/cloud-plan-item/button.tsx +++ b/web/app/components/billing/pricing/plans/cloud-plan-item/button.tsx @@ -1,6 +1,6 @@ import type { BasicPlan } from '../../../type' import { RiArrowRightLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import { Plan } from '../../../type' diff --git a/web/app/components/billing/pricing/plans/cloud-plan-item/index.spec.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/index.spec.tsx index c2a17b0d04..f6df314917 100644 --- a/web/app/components/billing/pricing/plans/cloud-plan-item/index.spec.tsx +++ b/web/app/components/billing/pricing/plans/cloud-plan-item/index.spec.tsx @@ -1,6 +1,6 @@ import type { Mock } from 'vitest' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { useAppContext } from '@/context/app-context' import { useAsyncWindowOpen } from '@/hooks/use-async-window-open' import { fetchBillingUrl, fetchSubscriptionUrls } from '@/service/billing' diff --git a/web/app/components/billing/pricing/plans/cloud-plan-item/index.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/index.tsx index e104e9cdd3..57c85cf297 100644 --- a/web/app/components/billing/pricing/plans/cloud-plan-item/index.tsx +++ b/web/app/components/billing/pricing/plans/cloud-plan-item/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { BasicPlan } from '../../../type' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useAppContext } from '@/context/app-context' import { useAsyncWindowOpen } from '@/hooks/use-async-window-open' diff --git a/web/app/components/billing/pricing/plans/cloud-plan-item/list/index.spec.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/list/index.spec.tsx index 5e2ba2a994..bc33798482 100644 --- a/web/app/components/billing/pricing/plans/cloud-plan-item/list/index.spec.tsx +++ b/web/app/components/billing/pricing/plans/cloud-plan-item/list/index.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { Plan } from '../../../../type' import List from './index' diff --git a/web/app/components/billing/pricing/plans/cloud-plan-item/list/index.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/list/index.tsx index 600a3c0653..1ba8b2c9bc 100644 --- a/web/app/components/billing/pricing/plans/cloud-plan-item/list/index.tsx +++ b/web/app/components/billing/pricing/plans/cloud-plan-item/list/index.tsx @@ -1,5 +1,5 @@ import type { BasicPlan } from '../../../../type' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import { ALL_PLANS, NUM_INFINITE } from '../../../../config' diff --git a/web/app/components/billing/pricing/plans/cloud-plan-item/list/item/index.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/list/item/index.tsx index 95e232fefe..df93a5fb0e 100644 --- a/web/app/components/billing/pricing/plans/cloud-plan-item/list/item/index.tsx +++ b/web/app/components/billing/pricing/plans/cloud-plan-item/list/item/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Tooltip from './tooltip' type ItemProps = { diff --git a/web/app/components/billing/pricing/plans/cloud-plan-item/list/item/tooltip.tsx b/web/app/components/billing/pricing/plans/cloud-plan-item/list/item/tooltip.tsx index 34341ed97a..bfac36e529 100644 --- a/web/app/components/billing/pricing/plans/cloud-plan-item/list/item/tooltip.tsx +++ b/web/app/components/billing/pricing/plans/cloud-plan-item/list/item/tooltip.tsx @@ -1,5 +1,5 @@ import { RiInfoI } from '@remixicon/react' -import React from 'react' +import * as React from 'react' type TooltipProps = { content: string diff --git a/web/app/components/billing/pricing/plans/index.spec.tsx b/web/app/components/billing/pricing/plans/index.spec.tsx index 166f75e941..3accaee345 100644 --- a/web/app/components/billing/pricing/plans/index.spec.tsx +++ b/web/app/components/billing/pricing/plans/index.spec.tsx @@ -1,7 +1,7 @@ import type { Mock } from 'vitest' import type { UsagePlanInfo } from '../../type' import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { Plan } from '../../type' import { PlanRange } from '../plan-switcher/plan-range-switcher' import cloudPlanItem from './cloud-plan-item' diff --git a/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.spec.tsx b/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.spec.tsx index 193e16be45..35a484e7c3 100644 --- a/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.spec.tsx +++ b/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.spec.tsx @@ -1,6 +1,6 @@ import type { MockedFunction } from 'vitest' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import useTheme from '@/hooks/use-theme' import { Theme } from '@/types/app' import { SelfHostedPlan } from '../../../type' diff --git a/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.tsx b/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.tsx index 148420a85a..544141a6a5 100644 --- a/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.tsx +++ b/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.tsx @@ -1,5 +1,6 @@ import { RiArrowRightLine } from '@remixicon/react' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { AwsMarketplaceDark, AwsMarketplaceLight } from '@/app/components/base/icons/src/public/billing' import useTheme from '@/hooks/use-theme' diff --git a/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.spec.tsx b/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.spec.tsx index aee3b7e8d1..3a687de5c1 100644 --- a/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.spec.tsx +++ b/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.spec.tsx @@ -1,6 +1,6 @@ import type { Mock } from 'vitest' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { useAppContext } from '@/context/app-context' import Toast from '../../../../base/toast' import { contactSalesUrl, getStartedWithCommunityUrl, getWithPremiumUrl } from '../../../config' diff --git a/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.tsx b/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.tsx index db1f44b23a..b89d0c6941 100644 --- a/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.tsx +++ b/web/app/components/billing/pricing/plans/self-hosted-plan-item/index.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { Azure, GoogleCloud } from '@/app/components/base/icons/src/public/billing' import { useAppContext } from '@/context/app-context' diff --git a/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/index.spec.tsx b/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/index.spec.tsx index 6188ac3e0a..97329e47e5 100644 --- a/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/index.spec.tsx +++ b/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/index.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { SelfHostedPlan } from '@/app/components/billing/type' import List from './index' diff --git a/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/index.tsx b/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/index.tsx index 137b14db6b..4ed307d36e 100644 --- a/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/index.tsx +++ b/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/index.tsx @@ -1,5 +1,5 @@ import type { SelfHostedPlan } from '@/app/components/billing/type' -import React from 'react' +import * as React from 'react' import { Trans, useTranslation } from 'react-i18next' import Item from './item' diff --git a/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/item.spec.tsx b/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/item.spec.tsx index eda8527445..2f2957fb9d 100644 --- a/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/item.spec.tsx +++ b/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/item.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Item from './item' describe('SelfHostedPlanItem/List/Item', () => { diff --git a/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/item.tsx b/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/item.tsx index 8d219f30c2..ee14117d24 100644 --- a/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/item.tsx +++ b/web/app/components/billing/pricing/plans/self-hosted-plan-item/list/item.tsx @@ -1,5 +1,5 @@ import { RiCheckLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' type ItemProps = { label: string diff --git a/web/app/components/billing/trigger-events-limit-modal/index.tsx b/web/app/components/billing/trigger-events-limit-modal/index.tsx index 35d5e8bf65..421ec752dd 100644 --- a/web/app/components/billing/trigger-events-limit-modal/index.tsx +++ b/web/app/components/billing/trigger-events-limit-modal/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { TriggerAll } from '@/app/components/base/icons/src/vender/workflow' import PlanUpgradeModal from '@/app/components/billing/plan-upgrade-modal' diff --git a/web/app/components/billing/upgrade-btn/index.tsx b/web/app/components/billing/upgrade-btn/index.tsx index 10ec04e4de..0f23022b35 100644 --- a/web/app/components/billing/upgrade-btn/index.tsx +++ b/web/app/components/billing/upgrade-btn/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { CSSProperties, FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { SparklesSoft } from '@/app/components/base/icons/src/public/common' diff --git a/web/app/components/billing/usage-info/apps-info.tsx b/web/app/components/billing/usage-info/apps-info.tsx index 79ebd31a5f..024f3c47b1 100644 --- a/web/app/components/billing/usage-info/apps-info.tsx +++ b/web/app/components/billing/usage-info/apps-info.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import { RiApps2Line, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useProviderContext } from '@/context/provider-context' import UsageInfo from '../usage-info' diff --git a/web/app/components/billing/usage-info/index.tsx b/web/app/components/billing/usage-info/index.tsx index 7fa9efab2a..3681be9a20 100644 --- a/web/app/components/billing/usage-info/index.tsx +++ b/web/app/components/billing/usage-info/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import { cn } from '@/utils/classnames' diff --git a/web/app/components/billing/usage-info/vector-space-info.tsx b/web/app/components/billing/usage-info/vector-space-info.tsx index b946a2580f..1da573c708 100644 --- a/web/app/components/billing/usage-info/vector-space-info.tsx +++ b/web/app/components/billing/usage-info/vector-space-info.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import { RiHardDrive3Line, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useProviderContext } from '@/context/provider-context' import UsageInfo from '../usage-info' diff --git a/web/app/components/billing/vector-space-full/index.tsx b/web/app/components/billing/vector-space-full/index.tsx index 67d4cd8c34..21a856b660 100644 --- a/web/app/components/billing/vector-space-full/index.tsx +++ b/web/app/components/billing/vector-space-full/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import GridMask from '@/app/components/base/grid-mask' import { cn } from '@/utils/classnames' diff --git a/web/app/components/custom/custom-page/index.spec.tsx b/web/app/components/custom/custom-page/index.spec.tsx index 3fa4479450..0eea48fb6e 100644 --- a/web/app/components/custom/custom-page/index.spec.tsx +++ b/web/app/components/custom/custom-page/index.spec.tsx @@ -1,7 +1,7 @@ import type { Mock } from 'vitest' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import { createMockProviderContextValue } from '@/__mocks__/provider-context' import { contactSalesUrl } from '@/app/components/billing/config' import { Plan } from '@/app/components/billing/type' diff --git a/web/app/components/datasets/api/index.tsx b/web/app/components/datasets/api/index.tsx index 3ca84c3e11..801070030c 100644 --- a/web/app/components/datasets/api/index.tsx +++ b/web/app/components/datasets/api/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' const index = () => { return ( diff --git a/web/app/components/datasets/common/chunking-mode-label.tsx b/web/app/components/datasets/common/chunking-mode-label.tsx index 5147d44ad8..1a265855a4 100644 --- a/web/app/components/datasets/common/chunking-mode-label.tsx +++ b/web/app/components/datasets/common/chunking-mode-label.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Badge from '@/app/components/base/badge' import { GeneralChunk, ParentChildChunk } from '@/app/components/base/icons/src/vender/knowledge' diff --git a/web/app/components/datasets/common/credential-icon.tsx b/web/app/components/datasets/common/credential-icon.tsx index 27dea86993..a97cf7d410 100644 --- a/web/app/components/datasets/common/credential-icon.tsx +++ b/web/app/components/datasets/common/credential-icon.tsx @@ -1,4 +1,5 @@ -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { cn } from '@/utils/classnames' type CredentialIconProps = { diff --git a/web/app/components/datasets/common/document-file-icon.tsx b/web/app/components/datasets/common/document-file-icon.tsx index d4ce8878ba..d831cd78aa 100644 --- a/web/app/components/datasets/common/document-file-icon.tsx +++ b/web/app/components/datasets/common/document-file-icon.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { FileAppearanceType } from '@/app/components/base/file-uploader/types' -import React from 'react' +import * as React from 'react' import { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types' import FileTypeIcon from '../../base/file-uploader/file-type-icon' diff --git a/web/app/components/datasets/common/document-picker/document-list.tsx b/web/app/components/datasets/common/document-picker/document-list.tsx index 966bc0e4c8..574792ee14 100644 --- a/web/app/components/datasets/common/document-picker/document-list.tsx +++ b/web/app/components/datasets/common/document-picker/document-list.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { DocumentItem } from '@/models/datasets' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { cn } from '@/utils/classnames' import FileIcon from '../document-file-icon' diff --git a/web/app/components/datasets/common/document-picker/index.spec.tsx b/web/app/components/datasets/common/document-picker/index.spec.tsx index d236cf4e62..2ed29a0e23 100644 --- a/web/app/components/datasets/common/document-picker/index.spec.tsx +++ b/web/app/components/datasets/common/document-picker/index.spec.tsx @@ -1,7 +1,7 @@ import type { ParentMode, SimpleDocumentDetail } from '@/models/datasets' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { ChunkingMode, DataSourceType } from '@/models/datasets' import DocumentPicker from './index' diff --git a/web/app/components/datasets/common/document-picker/index.tsx b/web/app/components/datasets/common/document-picker/index.tsx index fe5b96a50b..14acd1db6c 100644 --- a/web/app/components/datasets/common/document-picker/index.tsx +++ b/web/app/components/datasets/common/document-picker/index.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { DocumentItem, ParentMode, SimpleDocumentDetail } from '@/models/datasets' import { RiArrowDownSLine } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { GeneralChunk, ParentChildChunk } from '@/app/components/base/icons/src/vender/knowledge' import Loading from '@/app/components/base/loading' diff --git a/web/app/components/datasets/common/document-picker/preview-document-picker.spec.tsx b/web/app/components/datasets/common/document-picker/preview-document-picker.spec.tsx index 5a70cada88..65272b77be 100644 --- a/web/app/components/datasets/common/document-picker/preview-document-picker.spec.tsx +++ b/web/app/components/datasets/common/document-picker/preview-document-picker.spec.tsx @@ -1,6 +1,6 @@ import type { DocumentItem } from '@/models/datasets' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import PreviewDocumentPicker from './preview-document-picker' // Override shared i18n mock for custom translations diff --git a/web/app/components/datasets/common/document-picker/preview-document-picker.tsx b/web/app/components/datasets/common/document-picker/preview-document-picker.tsx index 8b7cf8340b..4403cf29f2 100644 --- a/web/app/components/datasets/common/document-picker/preview-document-picker.tsx +++ b/web/app/components/datasets/common/document-picker/preview-document-picker.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { DocumentItem } from '@/models/datasets' import { RiArrowDownSLine } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' import { diff --git a/web/app/components/datasets/common/document-status-with-action/auto-disabled-document.tsx b/web/app/components/datasets/common/document-status-with-action/auto-disabled-document.tsx index 3fceccb481..8cb4d4e22b 100644 --- a/web/app/components/datasets/common/document-status-with-action/auto-disabled-document.tsx +++ b/web/app/components/datasets/common/document-status-with-action/auto-disabled-document.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Toast from '@/app/components/base/toast' import { useAutoDisabledDocuments, useDocumentEnable, useInvalidDisabledDocument } from '@/service/knowledge/use-document' diff --git a/web/app/components/datasets/common/document-status-with-action/index-failed.tsx b/web/app/components/datasets/common/document-status-with-action/index-failed.tsx index 4fafbd8997..1635720037 100644 --- a/web/app/components/datasets/common/document-status-with-action/index-failed.tsx +++ b/web/app/components/datasets/common/document-status-with-action/index-failed.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { IndexingStatusResponse } from '@/models/datasets' import { noop } from 'lodash-es' -import React, { useEffect, useReducer } from 'react' +import * as React from 'react' +import { useEffect, useReducer } from 'react' import { useTranslation } from 'react-i18next' import { retryErrorDocs } from '@/service/datasets' import { useDatasetErrorDocs } from '@/service/knowledge/use-dataset' diff --git a/web/app/components/datasets/common/document-status-with-action/status-with-action.tsx b/web/app/components/datasets/common/document-status-with-action/status-with-action.tsx index b4eedd0cfe..9d42d40b1a 100644 --- a/web/app/components/datasets/common/document-status-with-action/status-with-action.tsx +++ b/web/app/components/datasets/common/document-status-with-action/status-with-action.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiAlertFill, RiCheckboxCircleFill, RiErrorWarningFill, RiInformation2Fill } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import Divider from '@/app/components/base/divider' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/common/economical-retrieval-method-config/index.tsx b/web/app/components/datasets/common/economical-retrieval-method-config/index.tsx index ec6878cd64..83f2911093 100644 --- a/web/app/components/datasets/common/economical-retrieval-method-config/index.tsx +++ b/web/app/components/datasets/common/economical-retrieval-method-config/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { RetrievalConfig } from '@/types/app' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { VectorSearch } from '@/app/components/base/icons/src/vender/knowledge' import { RETRIEVE_METHOD } from '@/types/app' diff --git a/web/app/components/datasets/common/image-list/more.tsx b/web/app/components/datasets/common/image-list/more.tsx index 6b809ee5c5..be6b53a5a5 100644 --- a/web/app/components/datasets/common/image-list/more.tsx +++ b/web/app/components/datasets/common/image-list/more.tsx @@ -1,4 +1,5 @@ -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' type MoreProps = { count: number diff --git a/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/image-input.tsx b/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/image-input.tsx index e7e198fa86..63d3656dda 100644 --- a/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/image-input.tsx +++ b/web/app/components/datasets/common/image-uploader/image-uploader-in-chunk/image-input.tsx @@ -1,5 +1,5 @@ import { RiUploadCloud2Line } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import { ACCEPT_TYPES } from '../constants' diff --git a/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/image-input.tsx b/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/image-input.tsx index 5525913d3c..390e1f1ed4 100644 --- a/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/image-input.tsx +++ b/web/app/components/datasets/common/image-uploader/image-uploader-in-retrieval-testing/image-input.tsx @@ -1,5 +1,5 @@ import { RiImageAddLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import { ACCEPT_TYPES } from '../constants' diff --git a/web/app/components/datasets/common/retrieval-method-config/index.spec.tsx b/web/app/components/datasets/common/retrieval-method-config/index.spec.tsx index da07401d0c..ec6da2b160 100644 --- a/web/app/components/datasets/common/retrieval-method-config/index.spec.tsx +++ b/web/app/components/datasets/common/retrieval-method-config/index.spec.tsx @@ -1,6 +1,6 @@ import type { RetrievalConfig } from '@/types/app' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { DEFAULT_WEIGHTED_SCORE, RerankingModeEnum, diff --git a/web/app/components/datasets/common/retrieval-method-config/index.tsx b/web/app/components/datasets/common/retrieval-method-config/index.tsx index cd700f55f1..255f51f23e 100644 --- a/web/app/components/datasets/common/retrieval-method-config/index.tsx +++ b/web/app/components/datasets/common/retrieval-method-config/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { RetrievalConfig } from '@/types/app' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { FullTextSearch, HybridSearch, VectorSearch } from '@/app/components/base/icons/src/vender/knowledge' import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' diff --git a/web/app/components/datasets/common/retrieval-method-info/index.tsx b/web/app/components/datasets/common/retrieval-method-info/index.tsx index ee388a9580..df8a93f666 100644 --- a/web/app/components/datasets/common/retrieval-method-info/index.tsx +++ b/web/app/components/datasets/common/retrieval-method-info/index.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { RetrievalConfig } from '@/types/app' import Image from 'next/image' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import RadioCard from '@/app/components/base/radio-card' import { RETRIEVE_METHOD } from '@/types/app' diff --git a/web/app/components/datasets/common/retrieval-param-config/index.tsx b/web/app/components/datasets/common/retrieval-param-config/index.tsx index 19653ebad7..2517bb842b 100644 --- a/web/app/components/datasets/common/retrieval-param-config/index.tsx +++ b/web/app/components/datasets/common/retrieval-param-config/index.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { RetrievalConfig } from '@/types/app' import Image from 'next/image' -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import WeightedScore from '@/app/components/app/configuration/dataset-config/params-config/weighted-score' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/header.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/header.tsx index 7e36043318..5b2fc33e3e 100644 --- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/header.tsx +++ b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/header.tsx @@ -1,5 +1,5 @@ import { RiCloseLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' type HeaderProps = { diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/index.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/index.tsx index c6ec52c0f8..7024233dd6 100644 --- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/index.tsx +++ b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { CreateFromDSLModalTab } from '@/app/components/app/create-from-dsl-modal' import Item from './item' diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/item.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/item.tsx index f734e1ab90..87af4714bd 100644 --- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/item.tsx +++ b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/tab/item.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type ItemProps = { diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/uploader.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/uploader.tsx index 669068ab6e..4d5c06d523 100644 --- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/uploader.tsx +++ b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/uploader.tsx @@ -5,7 +5,8 @@ import { RiNodeTree, RiUploadCloud2Line, } from '@remixicon/react' -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import ActionButton from '@/app/components/base/action-button' diff --git a/web/app/components/datasets/create-from-pipeline/footer.tsx b/web/app/components/datasets/create-from-pipeline/footer.tsx index df29d06ce5..a744a8a5ca 100644 --- a/web/app/components/datasets/create-from-pipeline/footer.tsx +++ b/web/app/components/datasets/create-from-pipeline/footer.tsx @@ -1,6 +1,7 @@ import { RiFileUploadLine } from '@remixicon/react' import { useRouter, useSearchParams } from 'next/navigation' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useInvalidDatasetList } from '@/service/knowledge/use-dataset' import Divider from '../../base/divider' diff --git a/web/app/components/datasets/create-from-pipeline/header.tsx b/web/app/components/datasets/create-from-pipeline/header.tsx index dd43998453..690f78958c 100644 --- a/web/app/components/datasets/create-from-pipeline/header.tsx +++ b/web/app/components/datasets/create-from-pipeline/header.tsx @@ -1,6 +1,6 @@ import { RiArrowLeftLine } from '@remixicon/react' import Link from 'next/link' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '../../base/button' diff --git a/web/app/components/datasets/create-from-pipeline/list/create-card.tsx b/web/app/components/datasets/create-from-pipeline/list/create-card.tsx index 144926d97d..57ea9202bb 100644 --- a/web/app/components/datasets/create-from-pipeline/list/create-card.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/create-card.tsx @@ -1,6 +1,7 @@ import { RiAddCircleLine } from '@remixicon/react' import { useRouter } from 'next/navigation' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { trackEvent } from '@/app/components/base/amplitude' import Toast from '@/app/components/base/toast' diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/actions.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/actions.tsx index c7f1b742a8..a621862a08 100644 --- a/web/app/components/datasets/create-from-pipeline/list/template-card/actions.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/template-card/actions.tsx @@ -1,5 +1,5 @@ import { RiAddLine, RiArrowRightUpLine, RiMoreFill } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import CustomPopover from '@/app/components/base/popover' diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/content.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/content.tsx index 85873d97ca..8452d20d2d 100644 --- a/web/app/components/datasets/create-from-pipeline/list/template-card/content.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/template-card/content.tsx @@ -1,5 +1,5 @@ import type { ChunkingMode, IconInfo } from '@/models/datasets' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import { General } from '@/app/components/base/icons/src/public/knowledge/dataset-card' diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/details/chunk-structure-card.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/details/chunk-structure-card.tsx index 39d03ae28d..4a6efa29a2 100644 --- a/web/app/components/datasets/create-from-pipeline/list/template-card/details/chunk-structure-card.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/template-card/details/chunk-structure-card.tsx @@ -1,5 +1,5 @@ import type { Option } from './types' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import { EffectColor } from './types' diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/details/index.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/details/index.tsx index 9f0aebf6ff..0d48576f10 100644 --- a/web/app/components/datasets/create-from-pipeline/list/template-card/details/index.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/template-card/details/index.tsx @@ -1,6 +1,7 @@ import type { AppIconType } from '@/types/app' import { RiAddLine, RiCloseLine } from '@remixicon/react' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import Button from '@/app/components/base/button' diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/edit-pipeline-info.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/edit-pipeline-info.tsx index 46d09b8477..1c1d4f0a8c 100644 --- a/web/app/components/datasets/create-from-pipeline/list/template-card/edit-pipeline-info.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/template-card/edit-pipeline-info.tsx @@ -1,7 +1,8 @@ import type { AppIconSelection } from '@/app/components/base/app-icon-picker' import type { PipelineTemplate } from '@/models/pipeline' import { RiCloseLine } from '@remixicon/react' -import React, { useCallback, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import AppIconPicker from '@/app/components/base/app-icon-picker' diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/index.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/index.tsx index 877e96bd1d..61192c6430 100644 --- a/web/app/components/datasets/create-from-pipeline/list/template-card/index.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/template-card/index.tsx @@ -1,6 +1,7 @@ import type { PipelineTemplate } from '@/models/pipeline' import { useRouter } from 'next/navigation' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { trackEvent } from '@/app/components/base/amplitude' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/operations.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/operations.tsx index a6ab47faec..8fc33f2178 100644 --- a/web/app/components/datasets/create-from-pipeline/list/template-card/operations.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/template-card/operations.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' diff --git a/web/app/components/datasets/create/embedding-process/index.tsx b/web/app/components/datasets/create/embedding-process/index.tsx index 5cc376207f..541eb62b60 100644 --- a/web/app/components/datasets/create/embedding-process/index.tsx +++ b/web/app/components/datasets/create/embedding-process/index.tsx @@ -16,7 +16,8 @@ import { import Image from 'next/image' import Link from 'next/link' import { useRouter } from 'next/navigation' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' diff --git a/web/app/components/datasets/create/empty-dataset-creation-modal/index.spec.tsx b/web/app/components/datasets/create/empty-dataset-creation-modal/index.spec.tsx index 708ccc4ba3..cef945c968 100644 --- a/web/app/components/datasets/create/empty-dataset-creation-modal/index.spec.tsx +++ b/web/app/components/datasets/create/empty-dataset-creation-modal/index.spec.tsx @@ -1,6 +1,6 @@ import type { MockedFunction } from 'vitest' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { createEmptyDataset } from '@/service/datasets' import { useInvalidDatasetList } from '@/service/knowledge/use-dataset' import EmptyDatasetCreationModal from './index' diff --git a/web/app/components/datasets/create/empty-dataset-creation-modal/index.tsx b/web/app/components/datasets/create/empty-dataset-creation-modal/index.tsx index 4427009b06..c88eb0b47d 100644 --- a/web/app/components/datasets/create/empty-dataset-creation-modal/index.tsx +++ b/web/app/components/datasets/create/empty-dataset-creation-modal/index.tsx @@ -1,6 +1,7 @@ 'use client' import { useRouter } from 'next/navigation' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { trackEvent } from '@/app/components/base/amplitude' diff --git a/web/app/components/datasets/create/file-preview/index.tsx b/web/app/components/datasets/create/file-preview/index.tsx index dd174837c4..3212f61d0c 100644 --- a/web/app/components/datasets/create/file-preview/index.tsx +++ b/web/app/components/datasets/create/file-preview/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { CustomFile as File } from '@/models/datasets' import { XMarkIcon } from '@heroicons/react/20/solid' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' import { fetchFilePreview } from '@/service/common' diff --git a/web/app/components/datasets/create/file-uploader/index.tsx b/web/app/components/datasets/create/file-uploader/index.tsx index b0680905d1..97d1625c10 100644 --- a/web/app/components/datasets/create/file-uploader/index.tsx +++ b/web/app/components/datasets/create/file-uploader/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { CustomFile as File, FileItem } from '@/models/datasets' import { RiDeleteBinLine, RiUploadCloud2Line } from '@remixicon/react' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +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 { getFileUploadErrorMessage } from '@/app/components/base/file-uploader/utils' diff --git a/web/app/components/datasets/create/index.spec.tsx b/web/app/components/datasets/create/index.spec.tsx index a0248d59a1..66a642db3a 100644 --- a/web/app/components/datasets/create/index.spec.tsx +++ b/web/app/components/datasets/create/index.spec.tsx @@ -1,7 +1,7 @@ import type { DataSourceAuth } from '@/app/components/header/account-setting/data-source-page-new/types' import type { DataSet } from '@/models/datasets' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { DataSourceProvider } from '@/models/common' import { ChunkingMode, DatasetPermission, DataSourceType } from '@/models/datasets' import { RETRIEVE_METHOD } from '@/types/app' diff --git a/web/app/components/datasets/create/index.tsx b/web/app/components/datasets/create/index.tsx index 357b2256fc..3e4e870628 100644 --- a/web/app/components/datasets/create/index.tsx +++ b/web/app/components/datasets/create/index.tsx @@ -2,7 +2,8 @@ import type { NotionPage } from '@/models/common' import type { CrawlOptions, CrawlResultItem, createDocumentResponse, FileItem } from '@/models/datasets' import { produce } from 'immer' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' diff --git a/web/app/components/datasets/create/notion-page-preview/index.tsx b/web/app/components/datasets/create/notion-page-preview/index.tsx index ac590cc0c3..e245cd3490 100644 --- a/web/app/components/datasets/create/notion-page-preview/index.tsx +++ b/web/app/components/datasets/create/notion-page-preview/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { NotionPage } from '@/models/common' import { XMarkIcon } from '@heroicons/react/20/solid' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' import NotionIcon from '@/app/components/base/notion-icon' diff --git a/web/app/components/datasets/create/step-one/index.tsx b/web/app/components/datasets/create/step-one/index.tsx index ddbfb05e7d..ff99c218b2 100644 --- a/web/app/components/datasets/create/step-one/index.tsx +++ b/web/app/components/datasets/create/step-one/index.tsx @@ -4,7 +4,8 @@ import type { DataSourceProvider, NotionPage } from '@/models/common' import type { CrawlOptions, CrawlResultItem, FileItem } from '@/models/datasets' import { RiArrowRightLine, RiFolder6Line } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import NotionConnector from '@/app/components/base/notion-connector' diff --git a/web/app/components/datasets/create/step-one/upgrade-card.tsx b/web/app/components/datasets/create/step-one/upgrade-card.tsx index a4fcca73c0..a354e91e56 100644 --- a/web/app/components/datasets/create/step-one/upgrade-card.tsx +++ b/web/app/components/datasets/create/step-one/upgrade-card.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import UpgradeBtn from '@/app/components/billing/upgrade-btn' import { useModalContext } from '@/context/modal-context' diff --git a/web/app/components/datasets/create/step-three/index.tsx b/web/app/components/datasets/create/step-three/index.tsx index 56ff55de84..b4c122990f 100644 --- a/web/app/components/datasets/create/step-three/index.tsx +++ b/web/app/components/datasets/create/step-three/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { createDocumentResponse, FullDocumentDetail } from '@/models/datasets' import { RiBookOpenLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' diff --git a/web/app/components/datasets/create/step-two/index.tsx b/web/app/components/datasets/create/step-two/index.tsx index b2f1a221f6..981b6c5a8f 100644 --- a/web/app/components/datasets/create/step-two/index.tsx +++ b/web/app/components/datasets/create/step-two/index.tsx @@ -12,7 +12,8 @@ import { import { noop } from 'lodash-es' import Image from 'next/image' import Link from 'next/link' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { trackEvent } from '@/app/components/base/amplitude' diff --git a/web/app/components/datasets/create/step-two/language-select/index.spec.tsx b/web/app/components/datasets/create/step-two/language-select/index.spec.tsx index ba1097ecb4..a2f0d96d80 100644 --- a/web/app/components/datasets/create/step-two/language-select/index.spec.tsx +++ b/web/app/components/datasets/create/step-two/language-select/index.spec.tsx @@ -1,6 +1,6 @@ import type { ILanguageSelectProps } from './index' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { languages } from '@/i18n-config/language' import LanguageSelect from './index' diff --git a/web/app/components/datasets/create/step-two/language-select/index.tsx b/web/app/components/datasets/create/step-two/language-select/index.tsx index 9de71c12b8..b54db21970 100644 --- a/web/app/components/datasets/create/step-two/language-select/index.tsx +++ b/web/app/components/datasets/create/step-two/language-select/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiArrowDownSLine, RiCheckLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import Popover from '@/app/components/base/popover' import { languages } from '@/i18n-config/language' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/create/step-two/preview-item/index.spec.tsx b/web/app/components/datasets/create/step-two/preview-item/index.spec.tsx index 911982b7b3..c4cdf75480 100644 --- a/web/app/components/datasets/create/step-two/preview-item/index.spec.tsx +++ b/web/app/components/datasets/create/step-two/preview-item/index.spec.tsx @@ -1,6 +1,6 @@ import type { IPreviewItemProps } from './index' import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import PreviewItem, { PreviewType } from './index' // Test data builder for props diff --git a/web/app/components/datasets/create/step-two/preview-item/index.tsx b/web/app/components/datasets/create/step-two/preview-item/index.tsx index 67706fae4c..c1c95f4e62 100644 --- a/web/app/components/datasets/create/step-two/preview-item/index.tsx +++ b/web/app/components/datasets/create/step-two/preview-item/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' export type IPreviewItemProps = { diff --git a/web/app/components/datasets/create/stop-embedding-modal/index.tsx b/web/app/components/datasets/create/stop-embedding-modal/index.tsx index cb3a572b32..5a65692a27 100644 --- a/web/app/components/datasets/create/stop-embedding-modal/index.tsx +++ b/web/app/components/datasets/create/stop-embedding-modal/index.tsx @@ -1,5 +1,5 @@ 'use client' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' diff --git a/web/app/components/datasets/create/website/base/checkbox-with-label.tsx b/web/app/components/datasets/create/website/base/checkbox-with-label.tsx index 214cc9dc04..182645d5bc 100644 --- a/web/app/components/datasets/create/website/base/checkbox-with-label.tsx +++ b/web/app/components/datasets/create/website/base/checkbox-with-label.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import Checkbox from '@/app/components/base/checkbox' import Tooltip from '@/app/components/base/tooltip' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/create/website/base/crawled-result-item.tsx b/web/app/components/datasets/create/website/base/crawled-result-item.tsx index 47fdda193e..4cc1e16a5f 100644 --- a/web/app/components/datasets/create/website/base/crawled-result-item.tsx +++ b/web/app/components/datasets/create/website/base/crawled-result-item.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { CrawlResultItem as CrawlResultItemType } from '@/models/datasets' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Checkbox from '@/app/components/base/checkbox' diff --git a/web/app/components/datasets/create/website/base/crawled-result.tsx b/web/app/components/datasets/create/website/base/crawled-result.tsx index 987958c5c5..c922a77169 100644 --- a/web/app/components/datasets/create/website/base/crawled-result.tsx +++ b/web/app/components/datasets/create/website/base/crawled-result.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { CrawlResultItem } from '@/models/datasets' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import CheckboxWithLabel from './checkbox-with-label' diff --git a/web/app/components/datasets/create/website/base/crawling.tsx b/web/app/components/datasets/create/website/base/crawling.tsx index 80642ad2f4..a9e28985f1 100644 --- a/web/app/components/datasets/create/website/base/crawling.tsx +++ b/web/app/components/datasets/create/website/base/crawling.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { RowStruct } from '@/app/components/base/icons/src/public/other' diff --git a/web/app/components/datasets/create/website/base/error-message.tsx b/web/app/components/datasets/create/website/base/error-message.tsx index d021d1431c..97b18d00c1 100644 --- a/web/app/components/datasets/create/website/base/error-message.tsx +++ b/web/app/components/datasets/create/website/base/error-message.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/create/website/base/field.tsx b/web/app/components/datasets/create/website/base/field.tsx index 76671b65f7..43f9e4bb37 100644 --- a/web/app/components/datasets/create/website/base/field.tsx +++ b/web/app/components/datasets/create/website/base/field.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import Tooltip from '@/app/components/base/tooltip' import { cn } from '@/utils/classnames' import Input from './input' diff --git a/web/app/components/datasets/create/website/base/header.tsx b/web/app/components/datasets/create/website/base/header.tsx index 92f50a0989..cf4d537e3f 100644 --- a/web/app/components/datasets/create/website/base/header.tsx +++ b/web/app/components/datasets/create/website/base/header.tsx @@ -1,5 +1,5 @@ import { RiBookOpenLine, RiEqualizer2Line } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/create/website/base/input.tsx b/web/app/components/datasets/create/website/base/input.tsx index 64288f2872..aff683c0e4 100644 --- a/web/app/components/datasets/create/website/base/input.tsx +++ b/web/app/components/datasets/create/website/base/input.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' type Props = { value: string | number diff --git a/web/app/components/datasets/create/website/base/options-wrap.tsx b/web/app/components/datasets/create/website/base/options-wrap.tsx index 50701251e1..4b6d9a5522 100644 --- a/web/app/components/datasets/create/website/base/options-wrap.tsx +++ b/web/app/components/datasets/create/website/base/options-wrap.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import { RiEqualizer2Line } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/create/website/base/url-input.tsx b/web/app/components/datasets/create/website/base/url-input.tsx index 1137c8d1c4..c23655dcfa 100644 --- a/web/app/components/datasets/create/website/base/url-input.tsx +++ b/web/app/components/datasets/create/website/base/url-input.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { useDocLink } from '@/context/i18n' diff --git a/web/app/components/datasets/create/website/firecrawl/index.tsx b/web/app/components/datasets/create/website/firecrawl/index.tsx index c1146e8add..0a79b8f660 100644 --- a/web/app/components/datasets/create/website/firecrawl/index.tsx +++ b/web/app/components/datasets/create/website/firecrawl/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { CrawlOptions, CrawlResultItem } from '@/models/datasets' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Toast from '@/app/components/base/toast' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' diff --git a/web/app/components/datasets/create/website/firecrawl/options.tsx b/web/app/components/datasets/create/website/firecrawl/options.tsx index caf1895e64..59b23d34b0 100644 --- a/web/app/components/datasets/create/website/firecrawl/options.tsx +++ b/web/app/components/datasets/create/website/firecrawl/options.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { CrawlOptions } from '@/models/datasets' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import CheckboxWithLabel from '../base/checkbox-with-label' diff --git a/web/app/components/datasets/create/website/index.tsx b/web/app/components/datasets/create/website/index.tsx index 3d0d79dc77..2014631155 100644 --- a/web/app/components/datasets/create/website/index.tsx +++ b/web/app/components/datasets/create/website/index.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { DataSourceAuth } from '@/app/components/header/account-setting/data-source-page-new/types' import type { CrawlOptions, CrawlResultItem } from '@/models/datasets' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' import { ENABLE_WEBSITE_FIRECRAWL, ENABLE_WEBSITE_JINAREADER, ENABLE_WEBSITE_WATERCRAWL } from '@/config' diff --git a/web/app/components/datasets/create/website/jina-reader/base/url-input.tsx b/web/app/components/datasets/create/website/jina-reader/base/url-input.tsx index 0e20a76d1a..71a5037e71 100644 --- a/web/app/components/datasets/create/website/jina-reader/base/url-input.tsx +++ b/web/app/components/datasets/create/website/jina-reader/base/url-input.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { useDocLink } from '@/context/i18n' diff --git a/web/app/components/datasets/create/website/jina-reader/index.tsx b/web/app/components/datasets/create/website/jina-reader/index.tsx index 44556a4bcb..953f869c44 100644 --- a/web/app/components/datasets/create/website/jina-reader/index.tsx +++ b/web/app/components/datasets/create/website/jina-reader/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { CrawlOptions, CrawlResultItem } from '@/models/datasets' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Toast from '@/app/components/base/toast' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' diff --git a/web/app/components/datasets/create/website/jina-reader/options.tsx b/web/app/components/datasets/create/website/jina-reader/options.tsx index 67991055df..c9aeae9ee5 100644 --- a/web/app/components/datasets/create/website/jina-reader/options.tsx +++ b/web/app/components/datasets/create/website/jina-reader/options.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { CrawlOptions } from '@/models/datasets' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import CheckboxWithLabel from '../base/checkbox-with-label' diff --git a/web/app/components/datasets/create/website/no-data.tsx b/web/app/components/datasets/create/website/no-data.tsx index f01ec18f1a..ad3e1d4010 100644 --- a/web/app/components/datasets/create/website/no-data.tsx +++ b/web/app/components/datasets/create/website/no-data.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { Icon3Dots } from '@/app/components/base/icons/src/vender/line/others' diff --git a/web/app/components/datasets/create/website/preview.tsx b/web/app/components/datasets/create/website/preview.tsx index f9213e3c89..cb1c5822ba 100644 --- a/web/app/components/datasets/create/website/preview.tsx +++ b/web/app/components/datasets/create/website/preview.tsx @@ -1,7 +1,7 @@ 'use client' import type { CrawlResultItem } from '@/models/datasets' import { XMarkIcon } from '@heroicons/react/20/solid' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import s from '../file-preview/index.module.css' diff --git a/web/app/components/datasets/create/website/watercrawl/index.tsx b/web/app/components/datasets/create/website/watercrawl/index.tsx index 938d1dd813..9f3a130419 100644 --- a/web/app/components/datasets/create/website/watercrawl/index.tsx +++ b/web/app/components/datasets/create/website/watercrawl/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { CrawlOptions, CrawlResultItem } from '@/models/datasets' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Toast from '@/app/components/base/toast' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' diff --git a/web/app/components/datasets/create/website/watercrawl/options.tsx b/web/app/components/datasets/create/website/watercrawl/options.tsx index 0858647c60..5af2a5e7bb 100644 --- a/web/app/components/datasets/create/website/watercrawl/options.tsx +++ b/web/app/components/datasets/create/website/watercrawl/options.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { CrawlOptions } from '@/models/datasets' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import CheckboxWithLabel from '../base/checkbox-with-label' diff --git a/web/app/components/datasets/documents/create-from-pipeline/actions/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/actions/index.spec.tsx index 077b4fee3a..cbb74bb796 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/actions/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/actions/index.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Actions from './index' // ========================================== diff --git a/web/app/components/datasets/documents/create-from-pipeline/actions/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/actions/index.tsx index ad860e0f59..fc2759cbda 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/actions/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/actions/index.tsx @@ -1,7 +1,8 @@ import { RiArrowRightLine } from '@remixicon/react' import Link from 'next/link' import { useParams } from 'next/navigation' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Checkbox from '@/app/components/base/checkbox' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source-options/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source-options/index.spec.tsx index f5c56995b8..57b73e9222 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source-options/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source-options/index.spec.tsx @@ -3,7 +3,7 @@ import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-so import type { Node } from '@/app/components/workflow/types' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { act, fireEvent, render, renderHook, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { BlockEnum } from '@/app/components/workflow/types' import DatasourceIcon from './datasource-icon' import { useDatasourceIcon } from './hooks' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source-options/option-card.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source-options/option-card.tsx index 6e8d1d4105..b938afa950 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source-options/option-card.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source-options/option-card.tsx @@ -1,5 +1,5 @@ import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import DatasourceIcon from './datasource-icon' import { useDatasourceIcon } from './hooks' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.spec.tsx index 1477fe71e9..da5075ec8a 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.spec.tsx @@ -1,7 +1,7 @@ import type { CredentialSelectorProps } from './index' import type { DataSourceCredential } from '@/types/pipeline' import { fireEvent, render, screen, waitFor, within } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import CredentialSelector from './index' // Mock CredentialTypeEnum to avoid deep import chain issues diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.tsx index abeff83ebf..2f14b0f3b8 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.tsx @@ -1,6 +1,7 @@ import type { DataSourceCredential } from '@/types/pipeline' import { useBoolean } from 'ahooks' -import React, { useCallback, useEffect, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo } from 'react' import { PortalToFollowElem, PortalToFollowElemContent, diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/item.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/item.tsx index 65ea951798..4d54a04d1f 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/item.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/item.tsx @@ -1,6 +1,7 @@ import type { DataSourceCredential } from '@/types/pipeline' import { RiCheckLine } from '@remixicon/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { CredentialIcon } from '@/app/components/datasets/common/credential-icon' type ItemProps = { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/list.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/list.tsx index d90feaf2c0..09988a42d5 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/list.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/list.tsx @@ -1,5 +1,5 @@ import type { DataSourceCredential } from '@/types/pipeline' -import React from 'react' +import * as React from 'react' import Item from './item' type ListProps = { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/trigger.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/trigger.tsx index 7bac6afd35..ed68eaef5d 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/trigger.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/trigger.tsx @@ -1,6 +1,6 @@ import type { DataSourceCredential } from '@/types/pipeline' import { RiArrowDownSLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { CredentialIcon } from '@/app/components/datasets/common/credential-icon' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/header.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/header.spec.tsx index cadfbdae0f..31be2cdba6 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/base/header.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/base/header.spec.tsx @@ -1,6 +1,6 @@ import type { DataSourceCredential } from '@/types/pipeline' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Header from './header' // Mock CredentialTypeEnum to avoid deep import chain issues 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 cbdd24e5b9..c08e39937f 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,6 +1,6 @@ import type { CredentialSelectorProps } from './credential-selector' import { RiBookOpenLine, RiEqualizer2Line } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx index ffa5ed6bb8..31570ef4cf 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx @@ -3,7 +3,8 @@ import type { CustomFile as File, FileItem } from '@/models/datasets' import { RiDeleteBinLine, RiErrorWarningFill, RiUploadCloud2Line } from '@remixicon/react' import { produce } from 'immer' import dynamic from 'next/dynamic' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +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 { getFileUploadErrorMessage } from '@/app/components/base/file-uploader/utils' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.spec.tsx index 80109c738a..543d53ac39 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.spec.tsx @@ -1,7 +1,7 @@ import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' import type { DataSourceNotionWorkspace, NotionPage } from '@/models/common' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { VarKindType } from '@/app/components/workflow/nodes/_base/types' import OnlineDocuments from './index' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/index.spec.tsx index c1fe9a8cda..60da0e7c9f 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/index.spec.tsx @@ -1,7 +1,7 @@ import type { NotionPageTreeItem, NotionPageTreeMap } from './index' import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import PageSelector from './index' import { recursivePushInParentDescendants } from './utils' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/item.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/item.tsx index bc494d93aa..99ecb84ddd 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/item.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/page-selector/item.tsx @@ -1,7 +1,7 @@ import type { ListChildComponentProps } from 'react-window' import type { DataSourceNotionPage, DataSourceNotionPageMap } from '@/models/common' import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { areEqual } from 'react-window' import Checkbox from '@/app/components/base/checkbox' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/title.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/title.tsx index c9f48d0539..376274ba44 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/title.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/title.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' type TitleProps = { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/bucket.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/bucket.tsx index 06e4dc8386..ae84b21027 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/bucket.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/bucket.tsx @@ -1,4 +1,5 @@ -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { BucketsGray } from '@/app/components/base/icons/src/public/knowledge/online-drive' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/drive.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/drive.tsx index 91884ac2c8..208658ab5b 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/drive.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/drive.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/index.spec.tsx index 174e5f6287..13abce1c81 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/index.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Dropdown from './index' // ========================================== 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 5b4948241f..f6eda7f7af 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,5 +1,6 @@ import { RiMoreFill } from '@remixicon/react' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { PortalToFollowElem, PortalToFollowElemContent, diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/item.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/item.tsx index 59ad8a6e10..864cade85c 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/item.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/item.tsx @@ -1,4 +1,5 @@ -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' type ItemProps = { name: string diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/menu.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/menu.tsx index 9c5b15cb47..44af10cd95 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/menu.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/menu.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Item from './item' type MenuProps = { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.spec.tsx index 24500822c6..b7e53ed1be 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Breadcrumbs from './index' // ========================================== diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.tsx index 4657b79c19..a85137927a 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/index.tsx @@ -1,4 +1,5 @@ -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useDataSourceStore, useDataSourceStoreWithSelector } from '../../../../store' import Bucket from './bucket' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/item.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/item.tsx index fa019642f3..1bf32ab769 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/item.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/item.tsx @@ -1,4 +1,5 @@ -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { cn } from '@/utils/classnames' type BreadcrumbItemProps = { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/index.spec.tsx index ff2bdb2769..3c836465b8 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/index.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Header from './index' // ========================================== diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/index.tsx index cda916a4e8..8f0d169f1b 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import Breadcrumbs from './breadcrumbs' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/index.spec.tsx index 0e69a18574..2ad62aae8e 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/index.spec.tsx @@ -1,6 +1,6 @@ import type { OnlineDriveFile } from '@/models/pipeline' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { OnlineDriveFileType } from '@/models/pipeline' import FileList from './index' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/empty-folder.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/empty-folder.tsx index 595e976ba3..304210ca8f 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/empty-folder.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/empty-folder.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' const EmptyFolder = () => { 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 d435f38e64..b2266b7bb3 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,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { SearchMenu } from '@/app/components/base/icons/src/vender/knowledge' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/file-icon.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/file-icon.tsx index 1c25532884..ee87390892 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/file-icon.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/file-icon.tsx @@ -1,4 +1,5 @@ -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import FileTypeIcon from '@/app/components/base/file-uploader/file-type-icon' import { BucketsBlue, Folder } from '@/app/components/base/icons/src/public/knowledge/online-drive' import { OnlineDriveFileType } from '@/models/pipeline' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/index.spec.tsx index 29683bcfa9..0a8066bdc7 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/index.spec.tsx @@ -1,7 +1,7 @@ import type { Mock } from 'vitest' import type { OnlineDriveFile } from '@/models/pipeline' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { OnlineDriveFileType } from '@/models/pipeline' import List from './index' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/index.tsx index ecf28026d3..977001dbdd 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/index.tsx @@ -1,6 +1,7 @@ import type { OnlineDriveFile } from '@/models/pipeline' import { RiLoader2Line } from '@remixicon/react' -import React, { useEffect, useRef } from 'react' +import * as React from 'react' +import { useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' import { useDataSourceStore } from '../../../store' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/item.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/item.tsx index 8672a1841a..07ee21486a 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/item.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/item.tsx @@ -1,6 +1,7 @@ import type { Placement } from '@floating-ui/react' import type { OnlineDriveFile } from '@/models/pipeline' -import React, { useCallback } from 'react' +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' 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 4092a5b80c..3a6f294e09 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,5 +1,5 @@ import { RiBookOpenLine, RiEqualizer2Line } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.spec.tsx index ff65ad1385..7bf1d123f6 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.spec.tsx @@ -2,7 +2,7 @@ import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-so import type { OnlineDriveFile } from '@/models/pipeline' import type { OnlineDriveData } from '@/types/pipeline' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' import { DatasourceType, OnlineDriveFileType } from '@/models/pipeline' import Header from './header' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/checkbox-with-label.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/checkbox-with-label.tsx index f109737a41..17dfa37569 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/checkbox-with-label.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/checkbox-with-label.tsx @@ -1,5 +1,5 @@ 'use client' -import React from 'react' +import * as React from 'react' import Checkbox from '@/app/components/base/checkbox' import Tooltip from '@/app/components/base/tooltip' import { cn } from '@/utils/classnames' 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 871be218b3..7ca7c03d97 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,6 +1,7 @@ 'use client' import type { CrawlResultItem as CrawlResultItemType } from '@/models/datasets' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Checkbox from '@/app/components/base/checkbox' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx index ecd5c709b3..b10b1e8457 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx @@ -1,6 +1,7 @@ 'use client' import type { CrawlResultItem } from '@/models/datasets' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import CheckboxWithLabel from './checkbox-with-label' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawling.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawling.tsx index 65eb2b2c76..3b98ec76a0 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawling.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawling.tsx @@ -1,5 +1,5 @@ 'use client' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/error-message.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/error-message.tsx index 1423ab03a6..f0a1fb64a9 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/error-message.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/error-message.tsx @@ -1,5 +1,5 @@ import { RiErrorWarningFill } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type ErrorMessageProps = { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/index.spec.tsx index a544d90c39..94de64d791 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/index.spec.tsx @@ -1,6 +1,6 @@ import type { CrawlResultItem as CrawlResultItemType } from '@/models/datasets' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import CheckboxWithLabel from './checkbox-with-label' import CrawledResult from './crawled-result' import CrawledResultItem from './crawled-result-item' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/index.spec.tsx index 4f92d85ec7..b89114c84b 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/index.spec.tsx @@ -1,7 +1,7 @@ import type { MockInstance } from 'vitest' import type { RAGPipelineVariables } from '@/models/pipeline' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { BaseFieldType } from '@/app/components/base/form/form-scenarios/base/types' import Toast from '@/app/components/base/toast' import { CrawlStep } from '@/models/datasets' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.spec.tsx index 0c38208db9..493dd25730 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.spec.tsx @@ -1,7 +1,7 @@ import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' import type { CrawlResultItem } from '@/models/datasets' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' import { CrawlStep } from '@/models/datasets' import WebsiteCrawl from './index' diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.tsx index 30fa81b608..2a1141cf9e 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.tsx @@ -6,7 +6,8 @@ import type { DataSourceNodeErrorResponse, DataSourceNodeProcessingResponse, } from '@/types/pipeline' -import React, { useCallback, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useShallow } from 'zustand/react/shallow' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' 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 1760286b04..2b30c79022 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 @@ -2,7 +2,7 @@ import type { Step } from './step-indicator' import { RiArrowLeftLine } from '@remixicon/react' import Link from 'next/link' import { useParams } from 'next/navigation' -import React from 'react' +import * as React from 'react' import Button from '@/app/components/base/button' import Effect from '@/app/components/base/effect' import StepIndicator from './step-indicator' diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.spec.tsx index 29584b5da5..f055c90df8 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.spec.tsx @@ -2,7 +2,7 @@ import type { NotionPage } from '@/models/common' import type { CrawlResultItem, CustomFile, FileIndexingEstimateResponse } from '@/models/datasets' import type { OnlineDriveFile } from '@/models/pipeline' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { ChunkingMode } from '@/models/datasets' import { DatasourceType, OnlineDriveFileType } from '@/models/pipeline' import ChunkPreview from './chunk-preview' 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 d2a28feef9..6a137fa98c 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 @@ -2,7 +2,8 @@ import type { NotionPage } from '@/models/common' import type { CrawlResultItem, CustomFile, DocumentItem, FileIndexingEstimateResponse } from '@/models/datasets' import type { OnlineDriveFile } from '@/models/pipeline' import { RiSearchEyeLine } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Badge from '@/app/components/base/badge' import Button from '@/app/components/base/button' diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/file-preview.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/file-preview.spec.tsx index e5aaa27895..6f040ffb00 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/file-preview.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/file-preview.spec.tsx @@ -1,6 +1,6 @@ import type { CustomFile as File } from '@/models/datasets' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import FilePreview from './file-preview' // Uses global react-i18next mock from web/vitest.setup.ts diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/file-preview.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/file-preview.tsx index 6962d63567..53427a60a6 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/file-preview.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/file-preview.tsx @@ -1,7 +1,8 @@ 'use client' import type { CustomFile as File } from '@/models/datasets' import { RiCloseLine } from '@remixicon/react' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useFilePreview } from '@/service/use-common' import { formatFileSize, formatNumberAbbreviated } from '@/utils/format' diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/loading.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/loading.tsx index a367f3675c..dedc9d6a99 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/loading.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/loading.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { SkeletonContainer, SkeletonRectangle } from '@/app/components/base/skeleton' const Loading = () => { diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/online-document-preview.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/online-document-preview.spec.tsx index cd16ed3bbc..5375a0197c 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/online-document-preview.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/online-document-preview.spec.tsx @@ -1,6 +1,6 @@ import type { NotionPage } from '@/models/common' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import Toast from '@/app/components/base/toast' import OnlineDocumentPreview from './online-document-preview' 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 3582eed5df..6c25218421 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,7 +1,8 @@ 'use client' import type { NotionPage } from '@/models/common' import { RiCloseLine } from '@remixicon/react' -import React, { useEffect, useState } from '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' diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/web-preview.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/web-preview.spec.tsx index cfe58de56b..2cfb14f42a 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/web-preview.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/web-preview.spec.tsx @@ -1,6 +1,6 @@ import type { CrawlResultItem } from '@/models/datasets' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import WebsitePreview from './web-preview' // Uses global react-i18next mock from web/vitest.setup.ts diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/web-preview.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/web-preview.tsx index c68ede7734..22179bad05 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/web-preview.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/web-preview.tsx @@ -1,7 +1,7 @@ 'use client' import type { CrawlResultItem } from '@/models/datasets' import { RiCloseLine, RiGlobalLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { formatNumberAbbreviated } from '@/utils/format' diff --git a/web/app/components/datasets/documents/create-from-pipeline/process-documents/actions.tsx b/web/app/components/datasets/documents/create-from-pipeline/process-documents/actions.tsx index a49a8e9964..f4f4898b1f 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/process-documents/actions.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/process-documents/actions.tsx @@ -1,5 +1,5 @@ import { RiArrowLeftLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' diff --git a/web/app/components/datasets/documents/create-from-pipeline/process-documents/components.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/process-documents/components.spec.tsx index 2bd80ea60c..322e6edd49 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/process-documents/components.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/process-documents/components.spec.tsx @@ -1,6 +1,6 @@ import type { BaseConfiguration } from '@/app/components/base/form/form-scenarios/base/types' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { z } from 'zod' import { BaseFieldType } from '@/app/components/base/form/form-scenarios/base/types' import Toast from '@/app/components/base/toast' diff --git a/web/app/components/datasets/documents/create-from-pipeline/process-documents/header.tsx b/web/app/components/datasets/documents/create-from-pipeline/process-documents/header.tsx index 0b6300310a..ac1f3b0fa0 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/process-documents/header.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/process-documents/header.tsx @@ -1,5 +1,5 @@ import { RiSearchEyeLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' diff --git a/web/app/components/datasets/documents/create-from-pipeline/process-documents/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/process-documents/index.spec.tsx index 2b0fd7f0d0..318a6c2cba 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/process-documents/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/process-documents/index.spec.tsx @@ -1,6 +1,6 @@ import type { BaseConfiguration } from '@/app/components/base/form/form-scenarios/base/types' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { BaseFieldType } from '@/app/components/base/form/form-scenarios/base/types' import { useConfigurations, useInitialData } from '@/app/components/rag-pipeline/hooks/use-input-fields' import { useInputVariables } from './hooks' diff --git a/web/app/components/datasets/documents/create-from-pipeline/process-documents/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/process-documents/index.tsx index 4556d7ab85..770c6c820b 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/process-documents/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/process-documents/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { generateZodSchema } from '@/app/components/base/form/form-scenarios/base/utils' import { useConfigurations, useInitialData } from '@/app/components/rag-pipeline/hooks/use-input-fields' import Actions from './actions' diff --git a/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/index.spec.tsx index 1626f4f707..81e97a79a1 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/index.spec.tsx @@ -2,7 +2,7 @@ import type { Mock } from 'vitest' import type { DocumentIndexingStatus, IndexingStatusResponse } from '@/models/datasets' import type { InitialDocumentDetail } from '@/models/pipeline' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { Plan } from '@/app/components/billing/type' import { IndexingType } from '@/app/components/datasets/create/step-two' import { DatasourceType } from '@/models/pipeline' diff --git a/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/index.tsx index 98f83f7458..3c4dbb6e0a 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/index.tsx @@ -12,7 +12,8 @@ import { } from '@remixicon/react' import Link from 'next/link' import { useRouter } from 'next/navigation' -import React, { useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' diff --git a/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/rule-detail.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/rule-detail.spec.tsx index 17d7d8305b..9831896b90 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/rule-detail.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/rule-detail.spec.tsx @@ -1,6 +1,6 @@ import type { ProcessRuleResponse } from '@/models/datasets' import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { IndexingType } from '@/app/components/datasets/create/step-two' import { ProcessMode } from '@/models/datasets' import { RETRIEVE_METHOD } from '@/types/app' diff --git a/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/rule-detail.tsx b/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/rule-detail.tsx index a96a47a569..a16e284bcf 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/rule-detail.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/rule-detail.tsx @@ -1,6 +1,7 @@ import type { ProcessRuleResponse } from '@/models/datasets' import Image from 'next/image' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { indexMethodIcon, retrievalIcon } from '@/app/components/datasets/create/icons' import { IndexingType } from '@/app/components/datasets/create/step-two' diff --git a/web/app/components/datasets/documents/create-from-pipeline/processing/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/processing/index.spec.tsx index 948e3ba118..9d7a3e7b08 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/processing/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/processing/index.spec.tsx @@ -1,7 +1,7 @@ import type { DocumentIndexingStatus } from '@/models/datasets' import type { InitialDocumentDetail } from '@/models/pipeline' import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { DatasourceType } from '@/models/pipeline' import Processing from './index' diff --git a/web/app/components/datasets/documents/create-from-pipeline/processing/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/processing/index.tsx index c57221f8a4..09458dde89 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/processing/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/processing/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { InitialDocumentDetail } from '@/models/pipeline' import { RiBookOpenLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import { useDocLink } from '@/context/i18n' diff --git a/web/app/components/datasets/documents/create-from-pipeline/step-indicator.tsx b/web/app/components/datasets/documents/create-from-pipeline/step-indicator.tsx index 82bc9e9b31..755526df79 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/step-indicator.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/step-indicator.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' export type Step = { diff --git a/web/app/components/datasets/documents/detail/batch-modal/csv-downloader.tsx b/web/app/components/datasets/documents/detail/batch-modal/csv-downloader.tsx index 7cf0890c43..b6f0cbff10 100644 --- a/web/app/components/datasets/documents/detail/batch-modal/csv-downloader.tsx +++ b/web/app/components/datasets/documents/detail/batch-modal/csv-downloader.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useCSVDownloader, diff --git a/web/app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx b/web/app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx index d127471e28..3e55da0a90 100644 --- a/web/app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx +++ b/web/app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx @@ -4,7 +4,8 @@ import type { FileItem } from '@/models/datasets' import { RiDeleteBinLine, } from '@remixicon/react' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +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 Button from '@/app/components/base/button' diff --git a/web/app/components/datasets/documents/detail/batch-modal/index.tsx b/web/app/components/datasets/documents/detail/batch-modal/index.tsx index cc3e9455d8..091d5c493e 100644 --- a/web/app/components/datasets/documents/detail/batch-modal/index.tsx +++ b/web/app/components/datasets/documents/detail/batch-modal/index.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { ChunkingMode, FileItem } from '@/models/datasets' import { RiCloseLine } from '@remixicon/react' import { noop } from 'lodash-es' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' diff --git a/web/app/components/datasets/documents/detail/completed/child-segment-detail.tsx b/web/app/components/datasets/documents/detail/completed/child-segment-detail.tsx index 135d791fb3..f7166ca4dc 100644 --- a/web/app/components/datasets/documents/detail/completed/child-segment-detail.tsx +++ b/web/app/components/datasets/documents/detail/completed/child-segment-detail.tsx @@ -5,7 +5,8 @@ import { RiCollapseDiagonalLine, RiExpandDiagonalLine, } from '@remixicon/react' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import { useEventEmitterContextContext } from '@/context/event-emitter' diff --git a/web/app/components/datasets/documents/detail/completed/common/action-buttons.tsx b/web/app/components/datasets/documents/detail/completed/common/action-buttons.tsx index 69f7ee7889..49a7524a8e 100644 --- a/web/app/components/datasets/documents/detail/completed/common/action-buttons.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/action-buttons.tsx @@ -1,6 +1,7 @@ import type { FC } from 'react' import { useKeyPress } from 'ahooks' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { getKeyboardKeyCodeBySystem, getKeyboardKeyNameBySystem } from '@/app/components/workflow/utils' diff --git a/web/app/components/datasets/documents/detail/completed/common/add-another.tsx b/web/app/components/datasets/documents/detail/completed/common/add-another.tsx index 3c7eb83533..e6103b8ffe 100644 --- a/web/app/components/datasets/documents/detail/completed/common/add-another.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/add-another.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Checkbox from '@/app/components/base/checkbox' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/documents/detail/completed/common/batch-action.tsx b/web/app/components/datasets/documents/detail/completed/common/batch-action.tsx index ed8b6ac562..6cc60453dd 100644 --- a/web/app/components/datasets/documents/detail/completed/common/batch-action.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/batch-action.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import { RiArchive2Line, RiCheckboxCircleLine, RiCloseCircleLine, RiDeleteBinLine, RiDraftLine } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/datasets/documents/detail/completed/common/chunk-content.tsx b/web/app/components/datasets/documents/detail/completed/common/chunk-content.tsx index cb00903016..03ef530b11 100644 --- a/web/app/components/datasets/documents/detail/completed/common/chunk-content.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/chunk-content.tsx @@ -1,5 +1,6 @@ import type { ComponentProps, FC } from 'react' -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { Markdown } from '@/app/components/base/markdown' import { ChunkingMode } from '@/models/datasets' diff --git a/web/app/components/datasets/documents/detail/completed/common/dot.tsx b/web/app/components/datasets/documents/detail/completed/common/dot.tsx index 3ec98cb64f..d0a3543851 100644 --- a/web/app/components/datasets/documents/detail/completed/common/dot.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/dot.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' const Dot = () => { return ( diff --git a/web/app/components/datasets/documents/detail/completed/common/drawer.tsx b/web/app/components/datasets/documents/detail/completed/common/drawer.tsx index a3f30b0ebd..dc1b7192c3 100644 --- a/web/app/components/datasets/documents/detail/completed/common/drawer.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/drawer.tsx @@ -1,5 +1,6 @@ import { useKeyPress } from 'ahooks' -import React, { useCallback, useEffect, useRef } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef } from 'react' import { createPortal } from 'react-dom' import { cn } from '@/utils/classnames' import { useSegmentListContext } from '..' diff --git a/web/app/components/datasets/documents/detail/completed/common/empty.tsx b/web/app/components/datasets/documents/detail/completed/common/empty.tsx index 04ce92a693..48a730076e 100644 --- a/web/app/components/datasets/documents/detail/completed/common/empty.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/empty.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import { RiFileList2Line } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' type IEmptyProps = { diff --git a/web/app/components/datasets/documents/detail/completed/common/full-screen-drawer.tsx b/web/app/components/datasets/documents/detail/completed/common/full-screen-drawer.tsx index 9293dc862d..5f62bf0185 100644 --- a/web/app/components/datasets/documents/detail/completed/common/full-screen-drawer.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/full-screen-drawer.tsx @@ -1,5 +1,5 @@ import { noop } from 'lodash-es' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import Drawer from './drawer' diff --git a/web/app/components/datasets/documents/detail/completed/common/keywords.tsx b/web/app/components/datasets/documents/detail/completed/common/keywords.tsx index be7da98cd9..e62f2fd09d 100644 --- a/web/app/components/datasets/documents/detail/completed/common/keywords.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/keywords.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import type { SegmentDetailModel } from '@/models/datasets' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import TagInput from '@/app/components/base/tag-input' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/documents/detail/completed/common/regeneration-modal.tsx b/web/app/components/datasets/documents/detail/completed/common/regeneration-modal.tsx index 77518b2fe4..4957104e25 100644 --- a/web/app/components/datasets/documents/detail/completed/common/regeneration-modal.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/regeneration-modal.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import { RiLoader2Line } from '@remixicon/react' import { useCountDown } from 'ahooks' import { noop } from 'lodash-es' -import React, { useRef, useState } from 'react' +import * as React from 'react' +import { useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' diff --git a/web/app/components/datasets/documents/detail/completed/common/segment-index-tag.tsx b/web/app/components/datasets/documents/detail/completed/common/segment-index-tag.tsx index 2a837f66a7..a263ca55c8 100644 --- a/web/app/components/datasets/documents/detail/completed/common/segment-index-tag.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/segment-index-tag.tsx @@ -1,5 +1,6 @@ import type { FC } from 'react' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { Chunk } from '@/app/components/base/icons/src/vender/knowledge' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/documents/detail/completed/common/tag.tsx b/web/app/components/datasets/documents/detail/completed/common/tag.tsx index 66bc0bbeaf..f78cbf1c3f 100644 --- a/web/app/components/datasets/documents/detail/completed/common/tag.tsx +++ b/web/app/components/datasets/documents/detail/completed/common/tag.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' const Tag = ({ text, className }: { text: string, className?: string }) => { diff --git a/web/app/components/datasets/documents/detail/completed/display-toggle.tsx b/web/app/components/datasets/documents/detail/completed/display-toggle.tsx index 2f1212acdf..444907311a 100644 --- a/web/app/components/datasets/documents/detail/completed/display-toggle.tsx +++ b/web/app/components/datasets/documents/detail/completed/display-toggle.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import { RiLineHeight } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { Collapse } from '@/app/components/base/icons/src/vender/knowledge' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/datasets/documents/detail/completed/index.tsx b/web/app/components/datasets/documents/detail/completed/index.tsx index 8c2587969d..1b4aadfa50 100644 --- a/web/app/components/datasets/documents/detail/completed/index.tsx +++ b/web/app/components/datasets/documents/detail/completed/index.tsx @@ -6,7 +6,8 @@ import type { ChildChunkDetail, SegmentDetailModel, SegmentUpdater } from '@/mod import { useDebounceFn } from 'ahooks' import { noop } from 'lodash-es' import { usePathname } from 'next/navigation' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { createContext, useContext, useContextSelector } from 'use-context-selector' import Checkbox from '@/app/components/base/checkbox' diff --git a/web/app/components/datasets/documents/detail/completed/segment-card/chunk-content.tsx b/web/app/components/datasets/documents/detail/completed/segment-card/chunk-content.tsx index 11c73349de..dda2d9bf80 100644 --- a/web/app/components/datasets/documents/detail/completed/segment-card/chunk-content.tsx +++ b/web/app/components/datasets/documents/detail/completed/segment-card/chunk-content.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { Markdown } from '@/app/components/base/markdown' import { cn } from '@/utils/classnames' import { useSegmentListContext } from '..' diff --git a/web/app/components/datasets/documents/detail/completed/segment-card/index.spec.tsx b/web/app/components/datasets/documents/detail/completed/segment-card/index.spec.tsx index 31f2e45ab4..536b7af338 100644 --- a/web/app/components/datasets/documents/detail/completed/segment-card/index.spec.tsx +++ b/web/app/components/datasets/documents/detail/completed/segment-card/index.spec.tsx @@ -2,7 +2,7 @@ import type { SegmentListContextValue } from '@/app/components/datasets/document import type { DocumentContextValue } from '@/app/components/datasets/documents/detail/context' import type { Attachment, ChildChunkDetail, ParentMode, SegmentDetailModel } from '@/models/datasets' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { ChunkingMode } from '@/models/datasets' import SegmentCard from './index' diff --git a/web/app/components/datasets/documents/detail/completed/segment-card/index.tsx b/web/app/components/datasets/documents/detail/completed/segment-card/index.tsx index f0f24ec372..2393324d55 100644 --- a/web/app/components/datasets/documents/detail/completed/segment-card/index.tsx +++ b/web/app/components/datasets/documents/detail/completed/segment-card/index.tsx @@ -1,7 +1,8 @@ import type { FC } from 'react' import type { ChildChunkDetail, SegmentDetailModel } from '@/models/datasets' import { RiDeleteBinLine, RiEditLine } from '@remixicon/react' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Badge from '@/app/components/base/badge' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/datasets/documents/detail/completed/segment-detail.tsx b/web/app/components/datasets/documents/detail/completed/segment-detail.tsx index 175c08133d..1ba64176ad 100644 --- a/web/app/components/datasets/documents/detail/completed/segment-detail.tsx +++ b/web/app/components/datasets/documents/detail/completed/segment-detail.tsx @@ -6,7 +6,8 @@ import { RiCollapseDiagonalLine, RiExpandDiagonalLine, } from '@remixicon/react' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { v4 as uuid4 } from 'uuid' import Divider from '@/app/components/base/divider' diff --git a/web/app/components/datasets/documents/detail/completed/segment-list.tsx b/web/app/components/datasets/documents/detail/completed/segment-list.tsx index a0153ae76c..9e5f0ab2fe 100644 --- a/web/app/components/datasets/documents/detail/completed/segment-list.tsx +++ b/web/app/components/datasets/documents/detail/completed/segment-list.tsx @@ -1,5 +1,6 @@ import type { ChildChunkDetail, SegmentDetailModel } from '@/models/datasets' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import Checkbox from '@/app/components/base/checkbox' import Divider from '@/app/components/base/divider' import { ChunkingMode } from '@/models/datasets' diff --git a/web/app/components/datasets/documents/detail/completed/skeleton/full-doc-list-skeleton.tsx b/web/app/components/datasets/documents/detail/completed/skeleton/full-doc-list-skeleton.tsx index e1d7231214..9f9f51b55b 100644 --- a/web/app/components/datasets/documents/detail/completed/skeleton/full-doc-list-skeleton.tsx +++ b/web/app/components/datasets/documents/detail/completed/skeleton/full-doc-list-skeleton.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' const Slice = React.memo(() => { return ( diff --git a/web/app/components/datasets/documents/detail/completed/skeleton/general-list-skeleton.tsx b/web/app/components/datasets/documents/detail/completed/skeleton/general-list-skeleton.tsx index 416a5e15d5..118a39ef56 100644 --- a/web/app/components/datasets/documents/detail/completed/skeleton/general-list-skeleton.tsx +++ b/web/app/components/datasets/documents/detail/completed/skeleton/general-list-skeleton.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Checkbox from '@/app/components/base/checkbox' import Divider from '@/app/components/base/divider' import { diff --git a/web/app/components/datasets/documents/detail/completed/skeleton/paragraph-list-skeleton.tsx b/web/app/components/datasets/documents/detail/completed/skeleton/paragraph-list-skeleton.tsx index aa33bfbf17..9af0543fb6 100644 --- a/web/app/components/datasets/documents/detail/completed/skeleton/paragraph-list-skeleton.tsx +++ b/web/app/components/datasets/documents/detail/completed/skeleton/paragraph-list-skeleton.tsx @@ -1,5 +1,5 @@ import { RiArrowRightSLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import Checkbox from '@/app/components/base/checkbox' import Divider from '@/app/components/base/divider' import { diff --git a/web/app/components/datasets/documents/detail/completed/skeleton/parent-chunk-card-skeleton.tsx b/web/app/components/datasets/documents/detail/completed/skeleton/parent-chunk-card-skeleton.tsx index 4495547edb..be1a1696b2 100644 --- a/web/app/components/datasets/documents/detail/completed/skeleton/parent-chunk-card-skeleton.tsx +++ b/web/app/components/datasets/documents/detail/completed/skeleton/parent-chunk-card-skeleton.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { SkeletonContainer, diff --git a/web/app/components/datasets/documents/detail/completed/status-item.tsx b/web/app/components/datasets/documents/detail/completed/status-item.tsx index ce038742ff..34fc8bf0cb 100644 --- a/web/app/components/datasets/documents/detail/completed/status-item.tsx +++ b/web/app/components/datasets/documents/detail/completed/status-item.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { Item } from '@/app/components/base/select' import { RiCheckLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' type IStatusItemProps = { item: Item diff --git a/web/app/components/datasets/documents/detail/embedding/index.tsx b/web/app/components/datasets/documents/detail/embedding/index.tsx index 8eb5d197cb..db83d89c40 100644 --- a/web/app/components/datasets/documents/detail/embedding/index.tsx +++ b/web/app/components/datasets/documents/detail/embedding/index.tsx @@ -3,7 +3,8 @@ import type { CommonResponse } from '@/models/common' import type { IndexingStatusResponse, ProcessRuleResponse } from '@/models/datasets' import { RiLoader2Line, RiPauseCircleLine, RiPlayCircleLine } from '@remixicon/react' import Image from 'next/image' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +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 Divider from '@/app/components/base/divider' diff --git a/web/app/components/datasets/documents/detail/embedding/skeleton/index.tsx b/web/app/components/datasets/documents/detail/embedding/skeleton/index.tsx index eda512f38e..469d928eaa 100644 --- a/web/app/components/datasets/documents/detail/embedding/skeleton/index.tsx +++ b/web/app/components/datasets/documents/detail/embedding/skeleton/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Divider from '@/app/components/base/divider' import { SkeletonContainer, diff --git a/web/app/components/datasets/documents/detail/index.tsx b/web/app/components/datasets/documents/detail/index.tsx index 88b9fb5153..afb2d47c5b 100644 --- a/web/app/components/datasets/documents/detail/index.tsx +++ b/web/app/components/datasets/documents/detail/index.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { DataSourceInfo, FileItem, LegacyDataSourceInfo } from '@/models/datasets' import { RiArrowLeftLine, RiLayoutLeft2Line, RiLayoutRight2Line } from '@remixicon/react' import { useRouter } from 'next/navigation' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import FloatRightContainer from '@/app/components/base/float-right-container' diff --git a/web/app/components/datasets/documents/detail/metadata/index.tsx b/web/app/components/datasets/documents/detail/metadata/index.tsx index 73e8357e10..87d136c3fe 100644 --- a/web/app/components/datasets/documents/detail/metadata/index.tsx +++ b/web/app/components/datasets/documents/detail/metadata/index.tsx @@ -5,7 +5,8 @@ import type { CommonResponse } from '@/models/common' import type { DocType, FullDocumentDetail } from '@/models/datasets' import { PencilIcon } from '@heroicons/react/24/outline' import { get } from 'lodash-es' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import AutoHeightTextarea from '@/app/components/base/auto-height-textarea' diff --git a/web/app/components/datasets/documents/detail/segment-add/index.tsx b/web/app/components/datasets/documents/detail/segment-add/index.tsx index eed48eac6a..d8c4ab5c69 100644 --- a/web/app/components/datasets/documents/detail/segment-add/index.tsx +++ b/web/app/components/datasets/documents/detail/segment-add/index.tsx @@ -7,7 +7,8 @@ import { RiLoader2Line, } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { CheckCircle } from '@/app/components/base/icons/src/vender/solid/general' import Popover from '@/app/components/base/popover' diff --git a/web/app/components/datasets/documents/detail/settings/document-settings.tsx b/web/app/components/datasets/documents/detail/settings/document-settings.tsx index 96ac687d7a..6046829514 100644 --- a/web/app/components/datasets/documents/detail/settings/document-settings.tsx +++ b/web/app/components/datasets/documents/detail/settings/document-settings.tsx @@ -11,7 +11,8 @@ import type { } from '@/models/datasets' import { useBoolean } from 'ahooks' import { useRouter } from 'next/navigation' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import AppUnavailable from '@/app/components/base/app-unavailable' diff --git a/web/app/components/datasets/documents/detail/settings/index.tsx b/web/app/components/datasets/documents/detail/settings/index.tsx index ba1c1eb197..45b885fb06 100644 --- a/web/app/components/datasets/documents/detail/settings/index.tsx +++ b/web/app/components/datasets/documents/detail/settings/index.tsx @@ -1,5 +1,5 @@ 'use client' -import React from 'react' +import * as React from 'react' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import DocumentSettings from './document-settings' import PipelineSettings from './pipeline-settings' diff --git a/web/app/components/datasets/documents/detail/settings/pipeline-settings/left-header.tsx b/web/app/components/datasets/documents/detail/settings/pipeline-settings/left-header.tsx index fb7d1356c1..547dc8f53b 100644 --- a/web/app/components/datasets/documents/detail/settings/pipeline-settings/left-header.tsx +++ b/web/app/components/datasets/documents/detail/settings/pipeline-settings/left-header.tsx @@ -1,6 +1,7 @@ import { RiArrowLeftLine } from '@remixicon/react' import { useRouter } from 'next/navigation' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Effect from '@/app/components/base/effect' diff --git a/web/app/components/datasets/documents/detail/settings/pipeline-settings/process-documents/actions.tsx b/web/app/components/datasets/documents/detail/settings/pipeline-settings/process-documents/actions.tsx index c14a722ade..2cd379fa5f 100644 --- a/web/app/components/datasets/documents/detail/settings/pipeline-settings/process-documents/actions.tsx +++ b/web/app/components/datasets/documents/detail/settings/pipeline-settings/process-documents/actions.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' diff --git a/web/app/components/datasets/documents/index.tsx b/web/app/components/datasets/documents/index.tsx index 6a8a8ca563..5592c56224 100644 --- a/web/app/components/datasets/documents/index.tsx +++ b/web/app/components/datasets/documents/index.tsx @@ -6,7 +6,8 @@ import { PlusIcon } from '@heroicons/react/24/solid' import { RiDraftLine, RiExternalLinkLine } from '@remixicon/react' import { useDebounce, useDebounceFn } from 'ahooks' import { useRouter } from 'next/navigation' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' diff --git a/web/app/components/datasets/documents/list.tsx b/web/app/components/datasets/documents/list.tsx index 3c95874c46..0b06d5fe15 100644 --- a/web/app/components/datasets/documents/list.tsx +++ b/web/app/components/datasets/documents/list.tsx @@ -11,7 +11,8 @@ import { import { useBoolean } from 'ahooks' import { pick, uniq } from 'lodash-es' import { useRouter } from 'next/navigation' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Checkbox from '@/app/components/base/checkbox' import NotionIcon from '@/app/components/base/notion-icon' diff --git a/web/app/components/datasets/documents/operations.tsx b/web/app/components/datasets/documents/operations.tsx index 561771dc89..825a315178 100644 --- a/web/app/components/datasets/documents/operations.tsx +++ b/web/app/components/datasets/documents/operations.tsx @@ -13,7 +13,8 @@ import { import { useBoolean, useDebounceFn } from 'ahooks' import { noop } from 'lodash-es' import { useRouter } from 'next/navigation' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { DataSourceType, DocumentActionType } from '@/models/datasets' diff --git a/web/app/components/datasets/documents/rename-modal.tsx b/web/app/components/datasets/documents/rename-modal.tsx index ee1b7a5a82..cd4acf8eab 100644 --- a/web/app/components/datasets/documents/rename-modal.tsx +++ b/web/app/components/datasets/documents/rename-modal.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { useBoolean } from 'ahooks' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' diff --git a/web/app/components/datasets/documents/status-item/index.tsx b/web/app/components/datasets/documents/status-item/index.tsx index f152e498ad..415b413a26 100644 --- a/web/app/components/datasets/documents/status-item/index.tsx +++ b/web/app/components/datasets/documents/status-item/index.tsx @@ -3,7 +3,8 @@ import type { ColorMap, IndicatorProps } from '@/app/components/header/indicator import type { CommonResponse } from '@/models/common' import type { DocumentDisplayStatus } from '@/models/datasets' import { useDebounceFn } from 'ahooks' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Switch from '@/app/components/base/switch' diff --git a/web/app/components/datasets/external-api/external-api-modal/Form.tsx b/web/app/components/datasets/external-api/external-api-modal/Form.tsx index 875475f3e4..558ea1414e 100644 --- a/web/app/components/datasets/external-api/external-api-modal/Form.tsx +++ b/web/app/components/datasets/external-api/external-api-modal/Form.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { CreateExternalAPIReq, FormSchema } from '../declarations' import { RiBookOpenLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import { useDocLink } from '@/context/i18n' diff --git a/web/app/components/datasets/external-api/external-api-panel/index.tsx b/web/app/components/datasets/external-api/external-api-panel/index.tsx index def26fe00b..0cfe7657b1 100644 --- a/web/app/components/datasets/external-api/external-api-panel/index.tsx +++ b/web/app/components/datasets/external-api/external-api-panel/index.tsx @@ -3,7 +3,7 @@ import { RiBookOpenLine, RiCloseLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Button from '@/app/components/base/button' diff --git a/web/app/components/datasets/external-api/external-knowledge-api-card/index.tsx b/web/app/components/datasets/external-api/external-knowledge-api-card/index.tsx index af95e6771a..f4158fc462 100644 --- a/web/app/components/datasets/external-api/external-knowledge-api-card/index.tsx +++ b/web/app/components/datasets/external-api/external-knowledge-api-card/index.tsx @@ -4,7 +4,8 @@ import { RiDeleteBinLine, RiEditLine, } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/datasets/external-knowledge-base/connector/index.tsx b/web/app/components/datasets/external-knowledge-base/connector/index.tsx index 5184bdd888..1545c0d232 100644 --- a/web/app/components/datasets/external-knowledge-base/connector/index.tsx +++ b/web/app/components/datasets/external-knowledge-base/connector/index.tsx @@ -2,7 +2,8 @@ import type { CreateKnowledgeBaseReq } from '@/app/components/datasets/external-knowledge-base/create/declarations' import { useRouter } from 'next/navigation' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { trackEvent } from '@/app/components/base/amplitude' import { useToastContext } from '@/app/components/base/toast' import ExternalKnowledgeBaseCreate from '@/app/components/datasets/external-knowledge-base/create' diff --git a/web/app/components/datasets/external-knowledge-base/create/ExternalApiSelect.tsx b/web/app/components/datasets/external-knowledge-base/create/ExternalApiSelect.tsx index 2035f6709a..b07d1091e2 100644 --- a/web/app/components/datasets/external-knowledge-base/create/ExternalApiSelect.tsx +++ b/web/app/components/datasets/external-knowledge-base/create/ExternalApiSelect.tsx @@ -3,7 +3,8 @@ import { RiArrowDownSLine, } from '@remixicon/react' import { useRouter } from 'next/navigation' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/development' import { useExternalKnowledgeApi } from '@/context/external-knowledge-api-context' diff --git a/web/app/components/datasets/external-knowledge-base/create/ExternalApiSelection.tsx b/web/app/components/datasets/external-knowledge-base/create/ExternalApiSelection.tsx index 68231b46d4..6f4bfed1ba 100644 --- a/web/app/components/datasets/external-knowledge-base/create/ExternalApiSelection.tsx +++ b/web/app/components/datasets/external-knowledge-base/create/ExternalApiSelection.tsx @@ -2,7 +2,8 @@ import { RiAddLine } from '@remixicon/react' import { useRouter } from 'next/navigation' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' diff --git a/web/app/components/datasets/external-knowledge-base/create/KnowledgeBaseInfo.tsx b/web/app/components/datasets/external-knowledge-base/create/KnowledgeBaseInfo.tsx index 280e8ac864..e3cddc2c69 100644 --- a/web/app/components/datasets/external-knowledge-base/create/KnowledgeBaseInfo.tsx +++ b/web/app/components/datasets/external-knowledge-base/create/KnowledgeBaseInfo.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' diff --git a/web/app/components/datasets/external-knowledge-base/create/RetrievalSettings.tsx b/web/app/components/datasets/external-knowledge-base/create/RetrievalSettings.tsx index a7de114a2d..36085c5f33 100644 --- a/web/app/components/datasets/external-knowledge-base/create/RetrievalSettings.tsx +++ b/web/app/components/datasets/external-knowledge-base/create/RetrievalSettings.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold-item' import TopKItem from '@/app/components/base/param-item/top-k-item' diff --git a/web/app/components/datasets/external-knowledge-base/create/index.spec.tsx b/web/app/components/datasets/external-knowledge-base/create/index.spec.tsx index 0284a924c0..2fce096cd5 100644 --- a/web/app/components/datasets/external-knowledge-base/create/index.spec.tsx +++ b/web/app/components/datasets/external-knowledge-base/create/index.spec.tsx @@ -1,7 +1,7 @@ import type { ExternalAPIItem } from '@/models/datasets' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import ExternalKnowledgeBaseCreate from './index' import RetrievalSettings from './RetrievalSettings' diff --git a/web/app/components/datasets/extra-info/index.tsx b/web/app/components/datasets/extra-info/index.tsx index 5b46c92798..d0f74fd288 100644 --- a/web/app/components/datasets/extra-info/index.tsx +++ b/web/app/components/datasets/extra-info/index.tsx @@ -1,5 +1,5 @@ import type { RelatedAppResponse } from '@/models/datasets' -import React from 'react' +import * as React from 'react' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import { useDatasetApiBaseUrl } from '@/service/knowledge/use-dataset' import ServiceApi from './service-api' diff --git a/web/app/components/datasets/extra-info/service-api/card.tsx b/web/app/components/datasets/extra-info/service-api/card.tsx index 0452ee4da1..e5de8f66a5 100644 --- a/web/app/components/datasets/extra-info/service-api/card.tsx +++ b/web/app/components/datasets/extra-info/service-api/card.tsx @@ -1,6 +1,7 @@ import { RiBookOpenLine, RiKey2Line } from '@remixicon/react' import Link from 'next/link' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import CopyFeedback from '@/app/components/base/copy-feedback' diff --git a/web/app/components/datasets/extra-info/service-api/index.tsx b/web/app/components/datasets/extra-info/service-api/index.tsx index c653f2cc70..e8a0fbcb5a 100644 --- a/web/app/components/datasets/extra-info/service-api/index.tsx +++ b/web/app/components/datasets/extra-info/service-api/index.tsx @@ -1,4 +1,5 @@ -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { ApiAggregate } from '@/app/components/base/icons/src/vender/knowledge' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' diff --git a/web/app/components/datasets/extra-info/statistics.tsx b/web/app/components/datasets/extra-info/statistics.tsx index c867bade83..4982fffcb0 100644 --- a/web/app/components/datasets/extra-info/statistics.tsx +++ b/web/app/components/datasets/extra-info/statistics.tsx @@ -1,6 +1,6 @@ import type { RelatedAppResponse } from '@/models/datasets' import { RiInformation2Line } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import LinkedAppsPanel from '@/app/components/base/linked-apps-panel' diff --git a/web/app/components/datasets/hit-testing/components/child-chunks-item.tsx b/web/app/components/datasets/hit-testing/components/child-chunks-item.tsx index 680e848185..bca73994bd 100644 --- a/web/app/components/datasets/hit-testing/components/child-chunks-item.tsx +++ b/web/app/components/datasets/hit-testing/components/child-chunks-item.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { HitTestingChildChunk } from '@/models/datasets' -import React from 'react' +import * as React from 'react' import { SliceContent } from '../../formatted-text/flavours/shared' import Score from './score' diff --git a/web/app/components/datasets/hit-testing/components/chunk-detail-modal.tsx b/web/app/components/datasets/hit-testing/components/chunk-detail-modal.tsx index b7468ee08c..942fe46ffb 100644 --- a/web/app/components/datasets/hit-testing/components/chunk-detail-modal.tsx +++ b/web/app/components/datasets/hit-testing/components/chunk-detail-modal.tsx @@ -1,7 +1,8 @@ 'use client' import type { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types' import type { HitTesting } from '@/models/datasets' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import FileIcon from '@/app/components/base/file-uploader/file-type-icon' import { Markdown } from '@/app/components/base/markdown' diff --git a/web/app/components/datasets/hit-testing/components/empty-records.tsx b/web/app/components/datasets/hit-testing/components/empty-records.tsx index 1a93439e73..dccc1f3f19 100644 --- a/web/app/components/datasets/hit-testing/components/empty-records.tsx +++ b/web/app/components/datasets/hit-testing/components/empty-records.tsx @@ -1,5 +1,5 @@ import { RiHistoryLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' const EmptyRecords = () => { diff --git a/web/app/components/datasets/hit-testing/components/mask.tsx b/web/app/components/datasets/hit-testing/components/mask.tsx index 0bf329a3ff..703644bf9d 100644 --- a/web/app/components/datasets/hit-testing/components/mask.tsx +++ b/web/app/components/datasets/hit-testing/components/mask.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type MaskProps = { diff --git a/web/app/components/datasets/hit-testing/components/query-input/index.tsx b/web/app/components/datasets/hit-testing/components/query-input/index.tsx index abaed302c8..959e7f3425 100644 --- a/web/app/components/datasets/hit-testing/components/query-input/index.tsx +++ b/web/app/components/datasets/hit-testing/components/query-input/index.tsx @@ -15,7 +15,8 @@ import { RiPlayCircleLine, } from '@remixicon/react' import Image from 'next/image' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { v4 as uuid4 } from 'uuid' import Button from '@/app/components/base/button' diff --git a/web/app/components/datasets/hit-testing/components/query-input/textarea.tsx b/web/app/components/datasets/hit-testing/components/query-input/textarea.tsx index a3478d5deb..c74bdd4492 100644 --- a/web/app/components/datasets/hit-testing/components/query-input/textarea.tsx +++ b/web/app/components/datasets/hit-testing/components/query-input/textarea.tsx @@ -1,5 +1,5 @@ import type { ChangeEvent } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { Corner } from '@/app/components/base/icons/src/vender/solid/shapes' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/datasets/hit-testing/components/records.tsx b/web/app/components/datasets/hit-testing/components/records.tsx index 37eea71625..5de5391cc0 100644 --- a/web/app/components/datasets/hit-testing/components/records.tsx +++ b/web/app/components/datasets/hit-testing/components/records.tsx @@ -1,6 +1,7 @@ import type { Attachment, HitTestingRecord, Query } from '@/models/datasets' import { RiApps2Line, RiArrowDownLine, RiFocus2Line } from '@remixicon/react' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import useTimestamp from '@/hooks/use-timestamp' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/hit-testing/components/result-item-external.tsx b/web/app/components/datasets/hit-testing/components/result-item-external.tsx index 43d0709994..d4a6f2b002 100644 --- a/web/app/components/datasets/hit-testing/components/result-item-external.tsx +++ b/web/app/components/datasets/hit-testing/components/result-item-external.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { ExternalKnowledgeBaseHitTesting } from '@/models/datasets' import { useBoolean } from 'ahooks' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types' import Modal from '@/app/components/base/modal' diff --git a/web/app/components/datasets/hit-testing/components/result-item-footer.tsx b/web/app/components/datasets/hit-testing/components/result-item-footer.tsx index ad2d07d98e..1c62828cf3 100644 --- a/web/app/components/datasets/hit-testing/components/result-item-footer.tsx +++ b/web/app/components/datasets/hit-testing/components/result-item-footer.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types' import { RiArrowRightUpLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import FileIcon from '@/app/components/base/file-uploader/file-type-icon' diff --git a/web/app/components/datasets/hit-testing/components/result-item-meta.tsx b/web/app/components/datasets/hit-testing/components/result-item-meta.tsx index 558333a103..6277d9af8e 100644 --- a/web/app/components/datasets/hit-testing/components/result-item-meta.tsx +++ b/web/app/components/datasets/hit-testing/components/result-item-meta.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import Dot from '../../documents/detail/completed/common/dot' diff --git a/web/app/components/datasets/hit-testing/components/result-item.tsx b/web/app/components/datasets/hit-testing/components/result-item.tsx index 65ea47f348..0df8c6d560 100644 --- a/web/app/components/datasets/hit-testing/components/result-item.tsx +++ b/web/app/components/datasets/hit-testing/components/result-item.tsx @@ -3,7 +3,8 @@ import type { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader import type { HitTesting } from '@/models/datasets' import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { Markdown } from '@/app/components/base/markdown' import Tag from '@/app/components/datasets/documents/detail/completed/common/tag' diff --git a/web/app/components/datasets/hit-testing/components/score.tsx b/web/app/components/datasets/hit-testing/components/score.tsx index 20113a403e..ed5496740d 100644 --- a/web/app/components/datasets/hit-testing/components/score.tsx +++ b/web/app/components/datasets/hit-testing/components/score.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type Props = { diff --git a/web/app/components/datasets/hit-testing/index.tsx b/web/app/components/datasets/hit-testing/index.tsx index 2ae5e303e2..e75ef48abf 100644 --- a/web/app/components/datasets/hit-testing/index.tsx +++ b/web/app/components/datasets/hit-testing/index.tsx @@ -10,7 +10,8 @@ import type { } from '@/models/datasets' import type { RetrievalConfig } from '@/types/app' import { useBoolean } from 'ahooks' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Drawer from '@/app/components/base/drawer' diff --git a/web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx b/web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx index 801b62340c..7c7d4c4da2 100644 --- a/web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx +++ b/web/app/components/datasets/hit-testing/modify-retrieval-modal.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { IndexingType } from '../create/step-two' import type { RetrievalConfig } from '@/types/app' import { RiCloseLine } from '@remixicon/react' -import React, { useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model' diff --git a/web/app/components/datasets/list/dataset-card/index.tsx b/web/app/components/datasets/list/dataset-card/index.tsx index 4da265b43c..8087b80fda 100644 --- a/web/app/components/datasets/list/dataset-card/index.tsx +++ b/web/app/components/datasets/list/dataset-card/index.tsx @@ -4,7 +4,8 @@ import type { DataSet } from '@/models/datasets' import { RiFileTextFill, RiMoreFill, RiRobot2Fill } from '@remixicon/react' import { useHover } from 'ahooks' import { useRouter } from 'next/navigation' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/datasets/list/dataset-card/operation-item.tsx b/web/app/components/datasets/list/dataset-card/operation-item.tsx index c5c11afe45..afa0f174e8 100644 --- a/web/app/components/datasets/list/dataset-card/operation-item.tsx +++ b/web/app/components/datasets/list/dataset-card/operation-item.tsx @@ -1,5 +1,5 @@ import type { RemixiconComponentType } from '@remixicon/react' -import React from 'react' +import * as React from 'react' type OperationItemProps = { Icon: RemixiconComponentType diff --git a/web/app/components/datasets/list/dataset-card/operations.tsx b/web/app/components/datasets/list/dataset-card/operations.tsx index e6ecbf76b9..d83ed1d396 100644 --- a/web/app/components/datasets/list/dataset-card/operations.tsx +++ b/web/app/components/datasets/list/dataset-card/operations.tsx @@ -1,5 +1,5 @@ import { RiDeleteBinLine, RiEditLine, RiFileDownloadLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import OperationItem from './operation-item' diff --git a/web/app/components/datasets/list/dataset-footer/index.tsx b/web/app/components/datasets/list/dataset-footer/index.tsx index 425ca0df26..233fd26456 100644 --- a/web/app/components/datasets/list/dataset-footer/index.tsx +++ b/web/app/components/datasets/list/dataset-footer/index.tsx @@ -1,6 +1,6 @@ 'use client' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' const DatasetFooter = () => { diff --git a/web/app/components/datasets/list/new-dataset-card/index.tsx b/web/app/components/datasets/list/new-dataset-card/index.tsx index edc05e9919..0f8aa52586 100644 --- a/web/app/components/datasets/list/new-dataset-card/index.tsx +++ b/web/app/components/datasets/list/new-dataset-card/index.tsx @@ -3,7 +3,7 @@ import { RiAddLine, RiFunctionAddLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/development' import Option from './option' diff --git a/web/app/components/datasets/list/new-dataset-card/option.tsx b/web/app/components/datasets/list/new-dataset-card/option.tsx index 97a9e88b16..e862b5c11e 100644 --- a/web/app/components/datasets/list/new-dataset-card/option.tsx +++ b/web/app/components/datasets/list/new-dataset-card/option.tsx @@ -1,5 +1,5 @@ import Link from 'next/link' -import React from 'react' +import * as React from 'react' type OptionProps = { Icon: React.ComponentType<{ className?: string }> diff --git a/web/app/components/datasets/metadata/add-metadata-button.tsx b/web/app/components/datasets/metadata/add-metadata-button.tsx index 3908c8935e..4bb28e0b32 100644 --- a/web/app/components/datasets/metadata/add-metadata-button.tsx +++ b/web/app/components/datasets/metadata/add-metadata-button.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiAddLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import Button from '../../base/button' diff --git a/web/app/components/datasets/metadata/edit-metadata-batch/add-row.tsx b/web/app/components/datasets/metadata/edit-metadata-batch/add-row.tsx index f3a05fb22d..20fad9769a 100644 --- a/web/app/components/datasets/metadata/edit-metadata-batch/add-row.tsx +++ b/web/app/components/datasets/metadata/edit-metadata-batch/add-row.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { MetadataItemWithEdit } from '../types' import { RiIndeterminateCircleLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import InputCombined from './input-combined' import Label from './label' diff --git a/web/app/components/datasets/metadata/edit-metadata-batch/edit-row.tsx b/web/app/components/datasets/metadata/edit-metadata-batch/edit-row.tsx index 907ff127fb..1fdf8c2ef7 100644 --- a/web/app/components/datasets/metadata/edit-metadata-batch/edit-row.tsx +++ b/web/app/components/datasets/metadata/edit-metadata-batch/edit-row.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { MetadataItemWithEdit } from '../types' import { RiDeleteBinLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import { UpdateType } from '../types' import EditedBeacon from './edited-beacon' diff --git a/web/app/components/datasets/metadata/edit-metadata-batch/edited-beacon.tsx b/web/app/components/datasets/metadata/edit-metadata-batch/edited-beacon.tsx index 34f4e43a33..b6b8c2a7d0 100644 --- a/web/app/components/datasets/metadata/edit-metadata-batch/edited-beacon.tsx +++ b/web/app/components/datasets/metadata/edit-metadata-batch/edited-beacon.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import { RiResetLeftLine } from '@remixicon/react' import { useHover } from 'ahooks' -import React, { useRef } from 'react' +import * as React from 'react' +import { useRef } from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/datasets/metadata/edit-metadata-batch/input-combined.tsx b/web/app/components/datasets/metadata/edit-metadata-batch/input-combined.tsx index 51dd81b1fa..aec74bcfef 100644 --- a/web/app/components/datasets/metadata/edit-metadata-batch/input-combined.tsx +++ b/web/app/components/datasets/metadata/edit-metadata-batch/input-combined.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import Input from '@/app/components/base/input' import { InputNumber } from '@/app/components/base/input-number' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/metadata/edit-metadata-batch/input-has-set-multiple-value.tsx b/web/app/components/datasets/metadata/edit-metadata-batch/input-has-set-multiple-value.tsx index 412ef419e9..cf475898d3 100644 --- a/web/app/components/datasets/metadata/edit-metadata-batch/input-has-set-multiple-value.tsx +++ b/web/app/components/datasets/metadata/edit-metadata-batch/input-has-set-multiple-value.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiCloseLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/metadata/edit-metadata-batch/label.tsx b/web/app/components/datasets/metadata/edit-metadata-batch/label.tsx index 009b61f0b8..08f886be18 100644 --- a/web/app/components/datasets/metadata/edit-metadata-batch/label.tsx +++ b/web/app/components/datasets/metadata/edit-metadata-batch/label.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type Props = { diff --git a/web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx b/web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx index 8cec39b1d2..253d271a96 100644 --- a/web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx +++ b/web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { BuiltInMetadataItem, MetadataItemInBatchEdit, MetadataItemWithEdit } from '../types' import { RiQuestionLine } from '@remixicon/react' import { produce } from 'immer' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import Toast from '@/app/components/base/toast' diff --git a/web/app/components/datasets/metadata/metadata-dataset/create-content.tsx b/web/app/components/datasets/metadata/metadata-dataset/create-content.tsx index f2eba083fb..d31e9d7957 100644 --- a/web/app/components/datasets/metadata/metadata-dataset/create-content.tsx +++ b/web/app/components/datasets/metadata/metadata-dataset/create-content.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import { RiArrowLeftLine } from '@remixicon/react' import { noop } from 'lodash-es' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import ModalLikeWrap from '../../../base/modal-like-wrap' diff --git a/web/app/components/datasets/metadata/metadata-dataset/create-metadata-modal.tsx b/web/app/components/datasets/metadata/metadata-dataset/create-metadata-modal.tsx index 9ee326fd53..713804a541 100644 --- a/web/app/components/datasets/metadata/metadata-dataset/create-metadata-modal.tsx +++ b/web/app/components/datasets/metadata/metadata-dataset/create-metadata-modal.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { Props as CreateContentProps } from './create-content' -import React from 'react' +import * as React from 'react' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '../../../base/portal-to-follow-elem' import CreateContent from './create-content' diff --git a/web/app/components/datasets/metadata/metadata-dataset/dataset-metadata-drawer.tsx b/web/app/components/datasets/metadata/metadata-dataset/dataset-metadata-drawer.tsx index bfdda3dd65..f94e6e136f 100644 --- a/web/app/components/datasets/metadata/metadata-dataset/dataset-metadata-drawer.tsx +++ b/web/app/components/datasets/metadata/metadata-dataset/dataset-metadata-drawer.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { BuiltInMetadataItem, MetadataItemWithValueLength } from '../types' import { RiAddLine, RiDeleteBinLine, RiEditLine } from '@remixicon/react' import { useBoolean, useHover } from 'ahooks' -import React, { useCallback, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/datasets/metadata/metadata-dataset/field.tsx b/web/app/components/datasets/metadata/metadata-dataset/field.tsx index 8fb57bac34..4e8e3e02d8 100644 --- a/web/app/components/datasets/metadata/metadata-dataset/field.tsx +++ b/web/app/components/datasets/metadata/metadata-dataset/field.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' type Props = { className?: string diff --git a/web/app/components/datasets/metadata/metadata-dataset/select-metadata-modal.tsx b/web/app/components/datasets/metadata/metadata-dataset/select-metadata-modal.tsx index be0fbe9fab..eb7189a4cd 100644 --- a/web/app/components/datasets/metadata/metadata-dataset/select-metadata-modal.tsx +++ b/web/app/components/datasets/metadata/metadata-dataset/select-metadata-modal.tsx @@ -3,7 +3,8 @@ import type { Placement } from '@floating-ui/react' import type { FC } from 'react' import type { MetadataItem } from '../types' import type { Props as CreateContentProps } from './create-content' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useDatasetMetaData } from '@/service/knowledge/use-metadata' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '../../../base/portal-to-follow-elem' import CreateContent from './create-content' diff --git a/web/app/components/datasets/metadata/metadata-dataset/select-metadata.tsx b/web/app/components/datasets/metadata/metadata-dataset/select-metadata.tsx index 3540939205..0422c7a51a 100644 --- a/web/app/components/datasets/metadata/metadata-dataset/select-metadata.tsx +++ b/web/app/components/datasets/metadata/metadata-dataset/select-metadata.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { MetadataItem } from '../types' import { RiAddLine, RiArrowRightUpLine } from '@remixicon/react' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import SearchInput from '@/app/components/base/search-input' import { getIcon } from '../utils/get-icon' diff --git a/web/app/components/datasets/metadata/metadata-document/field.tsx b/web/app/components/datasets/metadata/metadata-document/field.tsx index 46c7598d5d..f6f5fdf8f1 100644 --- a/web/app/components/datasets/metadata/metadata-document/field.tsx +++ b/web/app/components/datasets/metadata/metadata-document/field.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' type Props = { label: string diff --git a/web/app/components/datasets/metadata/metadata-document/index.tsx b/web/app/components/datasets/metadata/metadata-document/index.tsx index beb88a8b82..4e8c931417 100644 --- a/web/app/components/datasets/metadata/metadata-document/index.tsx +++ b/web/app/components/datasets/metadata/metadata-document/index.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { FullDocumentDetail } from '@/models/datasets' import { RiEditLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' diff --git a/web/app/components/datasets/metadata/metadata-document/info-group.tsx b/web/app/components/datasets/metadata/metadata-document/info-group.tsx index afa490c344..5137859bed 100644 --- a/web/app/components/datasets/metadata/metadata-document/info-group.tsx +++ b/web/app/components/datasets/metadata/metadata-document/info-group.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import type { MetadataItemWithValue } from '../types' import { RiDeleteBinLine, RiQuestionLine } from '@remixicon/react' import { useRouter } from 'next/navigation' -import React from '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' diff --git a/web/app/components/datasets/metadata/metadata-document/no-data.tsx b/web/app/components/datasets/metadata/metadata-document/no-data.tsx index e52f69b590..81021ea51b 100644 --- a/web/app/components/datasets/metadata/metadata-document/no-data.tsx +++ b/web/app/components/datasets/metadata/metadata-document/no-data.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiArrowRightLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' diff --git a/web/app/components/datasets/no-linked-apps-panel.tsx b/web/app/components/datasets/no-linked-apps-panel.tsx index 5ab9689a12..b0a49abcdb 100644 --- a/web/app/components/datasets/no-linked-apps-panel.tsx +++ b/web/app/components/datasets/no-linked-apps-panel.tsx @@ -1,5 +1,5 @@ import { RiApps2AddLine, RiBookOpenLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useDocLink } from '@/context/i18n' diff --git a/web/app/components/datasets/settings/chunk-structure/index.tsx b/web/app/components/datasets/settings/chunk-structure/index.tsx index 2b9073672e..977620c4c1 100644 --- a/web/app/components/datasets/settings/chunk-structure/index.tsx +++ b/web/app/components/datasets/settings/chunk-structure/index.tsx @@ -1,5 +1,5 @@ import type { ChunkingMode } from '@/models/datasets' -import React from 'react' +import * as React from 'react' import OptionCard from '../option-card' import { useChunkStructure } from './hooks' diff --git a/web/app/components/datasets/settings/index-method/keyword-number.tsx b/web/app/components/datasets/settings/index-method/keyword-number.tsx index c754c2a48c..994d98f14a 100644 --- a/web/app/components/datasets/settings/index-method/keyword-number.tsx +++ b/web/app/components/datasets/settings/index-method/keyword-number.tsx @@ -1,5 +1,6 @@ import { RiQuestionLine } from '@remixicon/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { InputNumber } from '@/app/components/base/input-number' import Slider from '@/app/components/base/slider' diff --git a/web/app/components/datasets/settings/option-card.tsx b/web/app/components/datasets/settings/option-card.tsx index 8aa255746f..d17542935b 100644 --- a/web/app/components/datasets/settings/option-card.tsx +++ b/web/app/components/datasets/settings/option-card.tsx @@ -1,5 +1,5 @@ import type { ReactNode } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Badge from '@/app/components/base/badge' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/settings/permission-selector/index.tsx b/web/app/components/datasets/settings/permission-selector/index.tsx index 85c69a46a6..ffbc3e4a1c 100644 --- a/web/app/components/datasets/settings/permission-selector/index.tsx +++ b/web/app/components/datasets/settings/permission-selector/index.tsx @@ -1,7 +1,8 @@ import type { Member } from '@/models/common' import { RiArrowDownSLine, RiGroup2Line, RiLock2Line } from '@remixicon/react' import { useDebounceFn } from 'ahooks' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Avatar from '@/app/components/base/avatar' import Input from '@/app/components/base/input' diff --git a/web/app/components/datasets/settings/permission-selector/member-item.tsx b/web/app/components/datasets/settings/permission-selector/member-item.tsx index f70faa3553..9c1c3da70c 100644 --- a/web/app/components/datasets/settings/permission-selector/member-item.tsx +++ b/web/app/components/datasets/settings/permission-selector/member-item.tsx @@ -1,5 +1,5 @@ import { RiCheckLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' diff --git a/web/app/components/datasets/settings/permission-selector/permission-item.tsx b/web/app/components/datasets/settings/permission-selector/permission-item.tsx index f926e8287c..c5847896f7 100644 --- a/web/app/components/datasets/settings/permission-selector/permission-item.tsx +++ b/web/app/components/datasets/settings/permission-selector/permission-item.tsx @@ -1,5 +1,5 @@ import { RiCheckLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' type PermissionItemProps = { leftIcon: React.ReactNode diff --git a/web/app/components/develop/secret-key/input-copy.tsx b/web/app/components/develop/secret-key/input-copy.tsx index af7edea3c5..8f12d579bc 100644 --- a/web/app/components/develop/secret-key/input-copy.tsx +++ b/web/app/components/develop/secret-key/input-copy.tsx @@ -1,7 +1,8 @@ 'use client' import copy from 'copy-to-clipboard' import { t } from 'i18next' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import CopyFeedback from '@/app/components/base/copy-feedback' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/explore/app-card/index.spec.tsx b/web/app/components/explore/app-card/index.spec.tsx index 6247e8adc7..769b317929 100644 --- a/web/app/components/explore/app-card/index.spec.tsx +++ b/web/app/components/explore/app-card/index.spec.tsx @@ -1,7 +1,7 @@ import type { AppCardProps } from './index' import type { App } from '@/models/explore' import { fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { AppModeEnum } from '@/types/app' import AppCard from './index' diff --git a/web/app/components/explore/app-list/index.tsx b/web/app/components/explore/app-list/index.tsx index 859a4c081d..585c4e60c1 100644 --- a/web/app/components/explore/app-list/index.tsx +++ b/web/app/components/explore/app-list/index.tsx @@ -3,7 +3,8 @@ import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal' import type { App } from '@/models/explore' import { useDebounceFn } from 'ahooks' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import useSWR from 'swr' import { useContext } from 'use-context-selector' diff --git a/web/app/components/explore/category.tsx b/web/app/components/explore/category.tsx index 6d44c9f46b..eba883d849 100644 --- a/web/app/components/explore/category.tsx +++ b/web/app/components/explore/category.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { AppCategory } from '@/models/explore' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { ThumbsUp } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback' import exploreI18n from '@/i18n/en-US/explore' diff --git a/web/app/components/explore/create-app-modal/index.spec.tsx b/web/app/components/explore/create-app-modal/index.spec.tsx index 55ee7f9064..6bc1e1e9a0 100644 --- a/web/app/components/explore/create-app-modal/index.spec.tsx +++ b/web/app/components/explore/create-app-modal/index.spec.tsx @@ -1,7 +1,7 @@ import type { CreateAppModalProps } from './index' import type { UsagePlanInfo } from '@/app/components/billing/type' import { act, fireEvent, render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { createMockPlan, createMockPlanTotal, createMockPlanUsage } from '@/__mocks__/provider-context' import { Plan } from '@/app/components/billing/type' import { AppModeEnum } from '@/types/app' diff --git a/web/app/components/explore/create-app-modal/index.tsx b/web/app/components/explore/create-app-modal/index.tsx index ccd1280201..dac89bc776 100644 --- a/web/app/components/explore/create-app-modal/index.tsx +++ b/web/app/components/explore/create-app-modal/index.tsx @@ -3,7 +3,8 @@ import type { AppIconType } from '@/types/app' import { RiCloseLine, RiCommandLine, RiCornerDownLeftLine } from '@remixicon/react' import { useDebounceFn, useKeyPress } from 'ahooks' import { noop } from 'lodash-es' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import Button from '@/app/components/base/button' diff --git a/web/app/components/explore/index.tsx b/web/app/components/explore/index.tsx index d9919e90d9..a405fe0d28 100644 --- a/web/app/components/explore/index.tsx +++ b/web/app/components/explore/index.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { InstalledApp } from '@/models/explore' import { useRouter } from 'next/navigation' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Sidebar from '@/app/components/explore/sidebar' import { useAppContext } from '@/context/app-context' diff --git a/web/app/components/explore/installed-app/index.tsx b/web/app/components/explore/installed-app/index.tsx index cd8bc468fb..def66c0260 100644 --- a/web/app/components/explore/installed-app/index.tsx +++ b/web/app/components/explore/installed-app/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { AppData } from '@/models/share' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { useContext } from 'use-context-selector' import ChatWithHistory from '@/app/components/base/chat/chat-with-history' import Loading from '@/app/components/base/loading' diff --git a/web/app/components/explore/item-operation/index.tsx b/web/app/components/explore/item-operation/index.tsx index bc145a633a..3703c0d4c0 100644 --- a/web/app/components/explore/item-operation/index.tsx +++ b/web/app/components/explore/item-operation/index.tsx @@ -5,7 +5,8 @@ import { RiEditLine, } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' diff --git a/web/app/components/explore/sidebar/app-nav-item/index.tsx b/web/app/components/explore/sidebar/app-nav-item/index.tsx index 37163de42a..3347efeb3f 100644 --- a/web/app/components/explore/sidebar/app-nav-item/index.tsx +++ b/web/app/components/explore/sidebar/app-nav-item/index.tsx @@ -3,7 +3,8 @@ import type { AppIconType } from '@/types/app' import { useHover } from 'ahooks' import { useRouter } from 'next/navigation' -import React, { useRef } from 'react' +import * as React from 'react' +import { useRef } from 'react' import AppIcon from '@/app/components/base/app-icon' import ItemOperation from '@/app/components/explore/item-operation' import { cn } from '@/utils/classnames' diff --git a/web/app/components/explore/sidebar/index.tsx b/web/app/components/explore/sidebar/index.tsx index d178e65cbe..2d370ef153 100644 --- a/web/app/components/explore/sidebar/index.tsx +++ b/web/app/components/explore/sidebar/index.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import Link from 'next/link' import { useSelectedLayoutSegments } from 'next/navigation' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/goto-anything/actions/commands/account.tsx b/web/app/components/goto-anything/actions/commands/account.tsx index 82fc24ccb9..82025191b1 100644 --- a/web/app/components/goto-anything/actions/commands/account.tsx +++ b/web/app/components/goto-anything/actions/commands/account.tsx @@ -1,6 +1,6 @@ import type { SlashCommandHandler } from './types' import { RiUser3Line } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import i18n from '@/i18n-config/i18next-config' import { registerCommands, unregisterCommands } from './command-bus' diff --git a/web/app/components/goto-anything/actions/commands/community.tsx b/web/app/components/goto-anything/actions/commands/community.tsx index 3327c44c20..95ca9f89f3 100644 --- a/web/app/components/goto-anything/actions/commands/community.tsx +++ b/web/app/components/goto-anything/actions/commands/community.tsx @@ -1,6 +1,6 @@ import type { SlashCommandHandler } from './types' import { RiDiscordLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import i18n from '@/i18n-config/i18next-config' import { registerCommands, unregisterCommands } from './command-bus' diff --git a/web/app/components/goto-anything/actions/commands/docs.tsx b/web/app/components/goto-anything/actions/commands/docs.tsx index d802379570..8a95b5a836 100644 --- a/web/app/components/goto-anything/actions/commands/docs.tsx +++ b/web/app/components/goto-anything/actions/commands/docs.tsx @@ -1,6 +1,6 @@ import type { SlashCommandHandler } from './types' import { RiBookOpenLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { defaultDocBaseUrl } from '@/context/i18n' import i18n from '@/i18n-config/i18next-config' import { getDocLanguage } from '@/i18n-config/language' diff --git a/web/app/components/goto-anything/actions/commands/forum.tsx b/web/app/components/goto-anything/actions/commands/forum.tsx index 50fc55211c..2156642bcf 100644 --- a/web/app/components/goto-anything/actions/commands/forum.tsx +++ b/web/app/components/goto-anything/actions/commands/forum.tsx @@ -1,6 +1,6 @@ import type { SlashCommandHandler } from './types' import { RiFeedbackLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import i18n from '@/i18n-config/i18next-config' import { registerCommands, unregisterCommands } from './command-bus' diff --git a/web/app/components/goto-anything/actions/commands/theme.tsx b/web/app/components/goto-anything/actions/commands/theme.tsx index c70e4378d6..dc8ca46bc0 100644 --- a/web/app/components/goto-anything/actions/commands/theme.tsx +++ b/web/app/components/goto-anything/actions/commands/theme.tsx @@ -2,7 +2,7 @@ import type { ReactNode } from 'react' import type { CommandSearchResult } from '../types' import type { SlashCommandHandler } from './types' import { RiComputerLine, RiMoonLine, RiSunLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import i18n from '@/i18n-config/i18next-config' import { registerCommands, unregisterCommands } from './command-bus' diff --git a/web/app/components/goto-anything/actions/commands/zen.tsx b/web/app/components/goto-anything/actions/commands/zen.tsx index e8ee4c8087..9fa055a8cc 100644 --- a/web/app/components/goto-anything/actions/commands/zen.tsx +++ b/web/app/components/goto-anything/actions/commands/zen.tsx @@ -1,6 +1,6 @@ import type { SlashCommandHandler } from './types' import { RiFullscreenLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { isInWorkflowPage } from '@/app/components/workflow/constants' import i18n from '@/i18n-config/i18next-config' import { registerCommands, unregisterCommands } from './command-bus' diff --git a/web/app/components/goto-anything/command-selector.spec.tsx b/web/app/components/goto-anything/command-selector.spec.tsx index 40e67789cb..0ee2086058 100644 --- a/web/app/components/goto-anything/command-selector.spec.tsx +++ b/web/app/components/goto-anything/command-selector.spec.tsx @@ -2,7 +2,7 @@ import type { ActionItem } from './actions/types' import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { Command } from 'cmdk' -import React from 'react' +import * as React from 'react' import CommandSelector from './command-selector' vi.mock('next/navigation', () => ({ diff --git a/web/app/components/goto-anything/context.spec.tsx b/web/app/components/goto-anything/context.spec.tsx index 6922e83af1..ec979e1f88 100644 --- a/web/app/components/goto-anything/context.spec.tsx +++ b/web/app/components/goto-anything/context.spec.tsx @@ -1,5 +1,5 @@ import { render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { GotoAnythingProvider, useGotoAnythingContext } from './context' let pathnameMock = '/' diff --git a/web/app/components/goto-anything/context.tsx b/web/app/components/goto-anything/context.tsx index e0802d07c5..5c2bf3cb6b 100644 --- a/web/app/components/goto-anything/context.tsx +++ b/web/app/components/goto-anything/context.tsx @@ -2,7 +2,8 @@ import type { ReactNode } from 'react' import { usePathname } from 'next/navigation' -import React, { createContext, useContext, useEffect, useState } from 'react' +import * as React from 'react' +import { createContext, useContext, useEffect, useState } from 'react' import { isInWorkflowPage } from '../workflow/constants' /** diff --git a/web/app/components/goto-anything/index.spec.tsx b/web/app/components/goto-anything/index.spec.tsx index 29f6a6be99..7a8c1ead11 100644 --- a/web/app/components/goto-anything/index.spec.tsx +++ b/web/app/components/goto-anything/index.spec.tsx @@ -1,7 +1,7 @@ import type { ActionItem, SearchResult } from './actions/types' import { act, render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import GotoAnything from './index' const routerPush = vi.fn() diff --git a/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx b/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx index 7897e6da94..d139ab39df 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { DataSourceNotion as TDataSourceNotion } from '@/models/common' import { noop } from 'lodash-es' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import NotionIcon from '@/app/components/base/notion-icon' import Toast from '@/app/components/base/toast' diff --git a/web/app/components/header/account-setting/data-source-page/data-source-website/config-firecrawl-modal.tsx b/web/app/components/header/account-setting/data-source-page/data-source-website/config-firecrawl-modal.tsx index 0d30ed1d85..54a4f4e9cf 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-website/config-firecrawl-modal.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-website/config-firecrawl-modal.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { FirecrawlConfig } from '@/models/common' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general' diff --git a/web/app/components/header/account-setting/data-source-page/data-source-website/config-jina-reader-modal.tsx b/web/app/components/header/account-setting/data-source-page/data-source-website/config-jina-reader-modal.tsx index e02a49e5e1..74392d30da 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-website/config-jina-reader-modal.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-website/config-jina-reader-modal.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general' diff --git a/web/app/components/header/account-setting/data-source-page/data-source-website/config-watercrawl-modal.tsx b/web/app/components/header/account-setting/data-source-page/data-source-website/config-watercrawl-modal.tsx index 7405991f83..92a2f7b806 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-website/config-watercrawl-modal.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-website/config-watercrawl-modal.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { WatercrawlConfig } from '@/models/common' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general' diff --git a/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx b/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx index 4fccd064f9..5ad75a9466 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { DataSourceItem } from '@/models/common' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Toast from '@/app/components/base/toast' import s from '@/app/components/datasets/create/website/index.module.css' diff --git a/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx b/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx index a6bcb3adbb..b98dd7933d 100644 --- a/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx +++ b/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx @@ -4,7 +4,7 @@ import { RiDeleteBinLine, } from '@remixicon/react' import { noop } from 'lodash-es' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import Indicator from '../../../indicator' diff --git a/web/app/components/header/account-setting/data-source-page/panel/index.tsx b/web/app/components/header/account-setting/data-source-page/panel/index.tsx index 0cdaadbd30..49ef183136 100644 --- a/web/app/components/header/account-setting/data-source-page/panel/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/panel/index.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { ConfigItemType } from './config-item' import { RiAddLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' diff --git a/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx b/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx index cee4e2b466..7d8169e4c4 100644 --- a/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx +++ b/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx @@ -1,5 +1,6 @@ import { RiArrowDownSLine } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { Check } from '@/app/components/base/icons/src/vender/line/general' import { diff --git a/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx b/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx index 48776cdfef..825c225a51 100644 --- a/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx +++ b/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx @@ -2,7 +2,8 @@ import type { SuccessInvitationResult } from '.' import copy from 'copy-to-clipboard' import { t } from 'i18next' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import Tooltip from '@/app/components/base/tooltip' import s from './index.module.css' diff --git a/web/app/components/header/account-setting/members-page/transfer-ownership-modal/index.tsx b/web/app/components/header/account-setting/members-page/transfer-ownership-modal/index.tsx index 6ad18d3830..2c6a33dc1f 100644 --- a/web/app/components/header/account-setting/members-page/transfer-ownership-modal/index.tsx +++ b/web/app/components/header/account-setting/members-page/transfer-ownership-modal/index.tsx @@ -1,6 +1,7 @@ import { RiCloseLine } from '@remixicon/react' import { noop } from 'lodash-es' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Button from '@/app/components/base/button' diff --git a/web/app/components/header/account-setting/members-page/transfer-ownership-modal/member-selector.tsx b/web/app/components/header/account-setting/members-page/transfer-ownership-modal/member-selector.tsx index dae7731799..043fa13aa0 100644 --- a/web/app/components/header/account-setting/members-page/transfer-ownership-modal/member-selector.tsx +++ b/web/app/components/header/account-setting/members-page/transfer-ownership-modal/member-selector.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import { RiArrowDownSLine, } from '@remixicon/react' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Avatar from '@/app/components/base/avatar' import Input from '@/app/components/base/input' diff --git a/web/app/components/header/app-back/index.tsx b/web/app/components/header/app-back/index.tsx index 716a37b94e..5f76880fd0 100644 --- a/web/app/components/header/app-back/index.tsx +++ b/web/app/components/header/app-back/index.tsx @@ -2,7 +2,8 @@ import type { AppDetailResponse } from '@/models/app' import { ArrowLeftIcon, Squares2X2Icon } from '@heroicons/react/24/solid' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' diff --git a/web/app/components/header/github-star/index.spec.tsx b/web/app/components/header/github-star/index.spec.tsx index 78a0017b03..f60ced4147 100644 --- a/web/app/components/header/github-star/index.spec.tsx +++ b/web/app/components/header/github-star/index.spec.tsx @@ -1,7 +1,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { render, screen, waitFor } from '@testing-library/react' import nock from 'nock' -import React from 'react' +import * as React from 'react' import GithubStar from './index' const GITHUB_HOST = 'https://api.github.com' diff --git a/web/app/components/header/header-wrapper.tsx b/web/app/components/header/header-wrapper.tsx index 69d9e1d421..1b81c1152c 100644 --- a/web/app/components/header/header-wrapper.tsx +++ b/web/app/components/header/header-wrapper.tsx @@ -1,6 +1,7 @@ 'use client' import { usePathname } from 'next/navigation' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useEventEmitterContextContext } from '@/context/event-emitter' import { cn } from '@/utils/classnames' import s from './index.module.css' diff --git a/web/app/components/header/nav/index.tsx b/web/app/components/header/nav/index.tsx index a3820fcc49..83e75b8513 100644 --- a/web/app/components/header/nav/index.tsx +++ b/web/app/components/header/nav/index.tsx @@ -3,7 +3,8 @@ import type { INavSelectorProps } from './nav-selector' import Link from 'next/link' import { usePathname, useSearchParams, useSelectedLayoutSegment } from 'next/navigation' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useStore as useAppStore } from '@/app/components/app/store' import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows' import { cn } from '@/utils/classnames' diff --git a/web/app/components/i18n-server.tsx b/web/app/components/i18n-server.tsx index a81d137c59..01dc5f0f13 100644 --- a/web/app/components/i18n-server.tsx +++ b/web/app/components/i18n-server.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { getLocaleOnServer } from '@/i18n-config/server' import { ToastProvider } from './base/toast' import I18N from './i18n' diff --git a/web/app/components/i18n.tsx b/web/app/components/i18n.tsx index 5bd9de617e..8a95363c15 100644 --- a/web/app/components/i18n.tsx +++ b/web/app/components/i18n.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { Locale } from '@/i18n-config' import { usePrefetchQuery } from '@tanstack/react-query' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import I18NContext from '@/context/i18n' import { setLocaleOnClient } from '@/i18n-config' import { getSystemFeatures } from '@/service/common' diff --git a/web/app/components/plugins/base/badges/icon-with-tooltip.tsx b/web/app/components/plugins/base/badges/icon-with-tooltip.tsx index 0a75334b5f..fc2aaaa572 100644 --- a/web/app/components/plugins/base/badges/icon-with-tooltip.tsx +++ b/web/app/components/plugins/base/badges/icon-with-tooltip.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import Tooltip from '@/app/components/base/tooltip' import { Theme } from '@/types/app' import { cn } from '@/utils/classnames' diff --git a/web/app/components/plugins/base/deprecation-notice.tsx b/web/app/components/plugins/base/deprecation-notice.tsx index 3359474669..8832c77961 100644 --- a/web/app/components/plugins/base/deprecation-notice.tsx +++ b/web/app/components/plugins/base/deprecation-notice.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import { RiAlertFill } from '@remixicon/react' import { camelCase } from 'lodash-es' import Link from 'next/link' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { Trans } from 'react-i18next' import { cn } from '@/utils/classnames' import { useMixedTranslation } from '../marketplace/hooks' diff --git a/web/app/components/plugins/base/key-value-item.tsx b/web/app/components/plugins/base/key-value-item.tsx index 07f5193773..5f1732cc0b 100644 --- a/web/app/components/plugins/base/key-value-item.tsx +++ b/web/app/components/plugins/base/key-value-item.tsx @@ -4,7 +4,8 @@ import { RiClipboardLine, } from '@remixicon/react' import copy from 'copy-to-clipboard' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import { cn } from '@/utils/classnames' diff --git a/web/app/components/plugins/card/base/description.tsx b/web/app/components/plugins/card/base/description.tsx index 9b9d7e3471..79e77c7e6f 100644 --- a/web/app/components/plugins/card/base/description.tsx +++ b/web/app/components/plugins/card/base/description.tsx @@ -1,5 +1,6 @@ import type { FC } from 'react' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { cn } from '@/utils/classnames' type Props = { diff --git a/web/app/components/plugins/card/base/download-count.tsx b/web/app/components/plugins/card/base/download-count.tsx index 653b595dde..91541cb931 100644 --- a/web/app/components/plugins/card/base/download-count.tsx +++ b/web/app/components/plugins/card/base/download-count.tsx @@ -1,5 +1,5 @@ import { RiInstallLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { formatNumber } from '@/utils/format' type Props = { diff --git a/web/app/components/plugins/card/card-more-info.tsx b/web/app/components/plugins/card/card-more-info.tsx index d81c941e96..33f819f31a 100644 --- a/web/app/components/plugins/card/card-more-info.tsx +++ b/web/app/components/plugins/card/card-more-info.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import DownloadCount from './base/download-count' type Props = { diff --git a/web/app/components/plugins/card/index.tsx b/web/app/components/plugins/card/index.tsx index af3468629e..1cb15bf70b 100644 --- a/web/app/components/plugins/card/index.tsx +++ b/web/app/components/plugins/card/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { Plugin } from '../types' import { RiAlertFill } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useMixedTranslation } from '@/app/components/plugins/marketplace/hooks' import { useGetLanguage } from '@/context/i18n' import useTheme from '@/hooks/use-theme' diff --git a/web/app/components/plugins/install-plugin/base/installed.tsx b/web/app/components/plugins/install-plugin/base/installed.tsx index aa4ca7c982..2c5a5cd088 100644 --- a/web/app/components/plugins/install-plugin/base/installed.tsx +++ b/web/app/components/plugins/install-plugin/base/installed.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { Plugin, PluginDeclaration, PluginManifestInMarket } from '../../types' -import React from 'react' +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/button' diff --git a/web/app/components/plugins/install-plugin/base/loading-error.tsx b/web/app/components/plugins/install-plugin/base/loading-error.tsx index f0067ed4fd..dd156fe7ef 100644 --- a/web/app/components/plugins/install-plugin/base/loading-error.tsx +++ b/web/app/components/plugins/install-plugin/base/loading-error.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiCloseLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Checkbox from '@/app/components/base/checkbox' import { LoadingPlaceholder } from '@/app/components/plugins/card/base/placeholder' diff --git a/web/app/components/plugins/install-plugin/base/loading.tsx b/web/app/components/plugins/install-plugin/base/loading.tsx index 973b574c8f..7416311dc8 100644 --- a/web/app/components/plugins/install-plugin/base/loading.tsx +++ b/web/app/components/plugins/install-plugin/base/loading.tsx @@ -1,5 +1,5 @@ 'use client' -import React from 'react' +import * as React from 'react' import Checkbox from '@/app/components/base/checkbox' import Placeholder from '../../card/base/placeholder' diff --git a/web/app/components/plugins/install-plugin/base/version.tsx b/web/app/components/plugins/install-plugin/base/version.tsx index ad91c91af7..d7bf042ab5 100644 --- a/web/app/components/plugins/install-plugin/base/version.tsx +++ b/web/app/components/plugins/install-plugin/base/version.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { VersionProps } from '../../types' -import React from 'react' +import * as React from 'react' import Badge, { BadgeState } from '@/app/components/base/badge/index' const Version: FC<VersionProps> = ({ diff --git a/web/app/components/plugins/install-plugin/install-bundle/index.tsx b/web/app/components/plugins/install-plugin/install-bundle/index.tsx index 0e32ea7164..6c8a060116 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/index.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { Dependency } from '../../types' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' import { cn } from '@/utils/classnames' diff --git a/web/app/components/plugins/install-plugin/install-bundle/item/github-item.tsx b/web/app/components/plugins/install-plugin/install-bundle/item/github-item.tsx index 382f5473ca..a25c2f5978 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/item/github-item.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/item/github-item.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { GitHubItemAndMarketPlaceDependency, Plugin } from '../../../types' import type { VersionProps } from '@/app/components/plugins/types' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { useUploadGitHub } from '@/service/use-plugins' import Loading from '../../base/loading' import { pluginManifestToCardPluginProps } from '../../utils' diff --git a/web/app/components/plugins/install-plugin/install-bundle/item/loaded-item.tsx b/web/app/components/plugins/install-plugin/install-bundle/item/loaded-item.tsx index 22c48cf55c..29f4a44ddb 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/item/loaded-item.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/item/loaded-item.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { Plugin, VersionProps } from '../../../types' -import React from 'react' +import * as React from 'react' import Checkbox from '@/app/components/base/checkbox' import { MARKETPLACE_API_PREFIX } from '@/config' import Card from '../../../card' diff --git a/web/app/components/plugins/install-plugin/install-bundle/item/marketplace-item.tsx b/web/app/components/plugins/install-plugin/install-bundle/item/marketplace-item.tsx index 1502fd0d65..d02cb0f74f 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/item/marketplace-item.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/item/marketplace-item.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { Plugin } from '../../../types' import type { VersionProps } from '@/app/components/plugins/types' -import React from 'react' +import * as React from 'react' import Loading from '../../base/loading' import LoadedItem from './loaded-item' diff --git a/web/app/components/plugins/install-plugin/install-bundle/item/package-item.tsx b/web/app/components/plugins/install-plugin/install-bundle/item/package-item.tsx index 84cee52ccc..63880bde5f 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/item/package-item.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/item/package-item.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { PackageDependency, Plugin } from '../../../types' import type { VersionProps } from '@/app/components/plugins/types' -import React from 'react' +import * as React from 'react' import LoadingError from '../../base/loading-error' import { pluginManifestToCardPluginProps } from '../../utils' import LoadedItem from './loaded-item' diff --git a/web/app/components/plugins/install-plugin/install-bundle/ready-to-install.tsx b/web/app/components/plugins/install-plugin/install-bundle/ready-to-install.tsx index f10556924c..2310063367 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/ready-to-install.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/ready-to-install.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { Dependency, InstallStatus, Plugin } from '../../types' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { InstallStep } from '../../types' import Install from './steps/install' import Installed from './steps/installed' diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx index f908b4f1c1..1b08ca5a04 100644 --- a/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx +++ b/web/app/components/plugins/install-plugin/install-bundle/steps/install-multi.tsx @@ -1,7 +1,8 @@ 'use client' import type { Dependency, GitHubItemAndMarketPlaceDependency, PackageDependency, Plugin, VersionInfo } from '../../../types' import { produce } from 'immer' -import React, { useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react' import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed' import { useGlobalPublicStore } from '@/context/global-public-context' import { useFetchPluginsInMarketPlaceByInfo } from '@/service/use-plugins' 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 67c3bde8bc..0373e255a0 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 @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { Dependency, InstallStatus, InstallStatusResponse, Plugin, VersionInfo } from '../../../types' import type { ExposeRefs } from './install-multi' import { RiLoader2Line } from '@remixicon/react' -import React, { useCallback, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Checkbox from '@/app/components/base/checkbox' 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 2fb7aab9d2..48096a13d3 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,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { InstallStatus, Plugin } from '../../../types' -import React from 'react' +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/button' 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 291e5d4eca..4a15b263b7 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 @@ -3,7 +3,8 @@ import type { PluginDeclaration, UpdateFromGitHubPayload } from '../../types' import type { Item } from '@/app/components/base/select' import type { InstallState } from '@/app/components/plugins/types' -import React, { useCallback, useState } from 'react' +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/toast' 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 27cc8b7498..3bff22816b 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 @@ -2,7 +2,8 @@ import type { Plugin, PluginDeclaration, UpdateFromGitHubPayload } from '../../../types' import { RiLoader2Line } from '@remixicon/react' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed' 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 8fdd0c8b8a..a5fc79c50b 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,7 +2,7 @@ import type { PluginDeclaration, UpdateFromGitHubPayload } from '../../../types' import type { Item } from '@/app/components/base/select' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { PortalSelect } from '@/app/components/base/select' 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 f07005a253..7d39675162 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,6 +1,6 @@ 'use client' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' diff --git a/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx b/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx index 4c0f26a2b9..b2390e38f3 100644 --- a/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx +++ b/web/app/components/plugins/install-plugin/install-from-local-package/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { Dependency, PluginDeclaration } from '../../types' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' diff --git a/web/app/components/plugins/install-plugin/install-from-local-package/ready-to-install.tsx b/web/app/components/plugins/install-plugin/install-from-local-package/ready-to-install.tsx index 5afe9ccf90..b6f4e9d3ce 100644 --- a/web/app/components/plugins/install-plugin/install-from-local-package/ready-to-install.tsx +++ b/web/app/components/plugins/install-plugin/install-from-local-package/ready-to-install.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { PluginDeclaration } from '../../types' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { InstallStep } from '../../types' import Installed from '../base/installed' import useRefreshPluginList from '../hooks/use-refresh-plugin-list' 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 b8a6891d64..86dda07639 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 @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { PluginDeclaration } from '../../../types' import { RiLoader2Line } from '@remixicon/react' -import React, { useEffect, useMemo } from 'react' +import * as React from 'react' +import { useEffect, useMemo } from 'react' import { Trans, useTranslation } from 'react-i18next' import { gte } from 'semver' import Button from '@/app/components/base/button' 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 fb591064ed..67b2505394 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 @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { Dependency, PluginDeclaration } from '../../../types' import { RiLoader2Line } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { uploadFile } from '@/service/plugins' diff --git a/web/app/components/plugins/install-plugin/install-from-marketplace/index.tsx b/web/app/components/plugins/install-plugin/install-from-marketplace/index.tsx index 72ec874d6f..22ef6cb53a 100644 --- a/web/app/components/plugins/install-plugin/install-from-marketplace/index.tsx +++ b/web/app/components/plugins/install-plugin/install-from-marketplace/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { Dependency, Plugin, PluginManifestInMarket } from '../../types' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' import { cn } from '@/utils/classnames' 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 8a8af380c3..99ee173490 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 @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { Plugin, PluginManifestInMarket } from '../../../types' import { RiLoader2Line } from '@remixicon/react' -import React, { useEffect, useMemo } from 'react' +import * as React from 'react' +import { useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { gte } from 'semver' import Button from '@/app/components/base/button' diff --git a/web/app/components/plugins/marketplace/list/card-wrapper.tsx b/web/app/components/plugins/marketplace/list/card-wrapper.tsx index 9ca005fe52..159107eb97 100644 --- a/web/app/components/plugins/marketplace/list/card-wrapper.tsx +++ b/web/app/components/plugins/marketplace/list/card-wrapper.tsx @@ -3,7 +3,8 @@ import type { Plugin } from '@/app/components/plugins/types' import { RiArrowRightUpLine } from '@remixicon/react' import { useBoolean } from 'ahooks' import { useTheme } from 'next-themes' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import Button from '@/app/components/base/button' import Card from '@/app/components/plugins/card' import CardMoreInfo from '@/app/components/plugins/card/card-more-info' diff --git a/web/app/components/plugins/marketplace/search-box/trigger/marketplace.tsx b/web/app/components/plugins/marketplace/search-box/trigger/marketplace.tsx index 2cc12614ee..e38c9199c4 100644 --- a/web/app/components/plugins/marketplace/search-box/trigger/marketplace.tsx +++ b/web/app/components/plugins/marketplace/search-box/trigger/marketplace.tsx @@ -1,6 +1,6 @@ import type { Tag } from '../../../hooks' import { RiArrowDownSLine, RiCloseCircleFill, RiFilter3Line } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import { useMixedTranslation } from '../../hooks' diff --git a/web/app/components/plugins/marketplace/search-box/trigger/tool-selector.tsx b/web/app/components/plugins/marketplace/search-box/trigger/tool-selector.tsx index 762a31a4fd..4e1fbebd46 100644 --- a/web/app/components/plugins/marketplace/search-box/trigger/tool-selector.tsx +++ b/web/app/components/plugins/marketplace/search-box/trigger/tool-selector.tsx @@ -1,6 +1,6 @@ import type { Tag } from '../../../hooks' import { RiCloseCircleFill, RiPriceTag3Line } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type ToolSelectorTriggerProps = { diff --git a/web/app/components/plugins/plugin-detail-panel/action-list.tsx b/web/app/components/plugins/plugin-detail-panel/action-list.tsx index 96e25bbc54..a1cb1198a1 100644 --- a/web/app/components/plugins/plugin-detail-panel/action-list.tsx +++ b/web/app/components/plugins/plugin-detail-panel/action-list.tsx @@ -1,5 +1,6 @@ import type { PluginDetail } from '@/app/components/plugins/types' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import ToolItem from '@/app/components/tools/provider/tool-item' import { diff --git a/web/app/components/plugins/plugin-detail-panel/agent-strategy-list.tsx b/web/app/components/plugins/plugin-detail-panel/agent-strategy-list.tsx index afc75226ea..e1114853e5 100644 --- a/web/app/components/plugins/plugin-detail-panel/agent-strategy-list.tsx +++ b/web/app/components/plugins/plugin-detail-panel/agent-strategy-list.tsx @@ -1,5 +1,6 @@ import type { PluginDetail } from '@/app/components/plugins/types' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import StrategyItem from '@/app/components/plugins/plugin-detail-panel/strategy-item' import { diff --git a/web/app/components/plugins/plugin-detail-panel/app-selector/app-inputs-panel.tsx b/web/app/components/plugins/plugin-detail-panel/app-selector/app-inputs-panel.tsx index 2fc94bd00d..897bc91707 100644 --- a/web/app/components/plugins/plugin-detail-panel/app-selector/app-inputs-panel.tsx +++ b/web/app/components/plugins/plugin-detail-panel/app-selector/app-inputs-panel.tsx @@ -1,7 +1,8 @@ 'use client' import type { FileUpload } from '@/app/components/base/features/types' import type { App } from '@/types/app' -import React, { useMemo, useRef } from 'react' +import * as React from 'react' +import { useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' diff --git a/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx b/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx index ca916cf662..f46588b5e3 100644 --- a/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx +++ b/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx @@ -5,7 +5,8 @@ import type { } from '@floating-ui/react' import type { FC } from 'react' import type { App } from '@/types/app' -import React, { useCallback, useEffect, useRef } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import Input from '@/app/components/base/input' diff --git a/web/app/components/plugins/plugin-detail-panel/app-selector/app-trigger.tsx b/web/app/components/plugins/plugin-detail-panel/app-selector/app-trigger.tsx index 54dc1562fd..2841864aa1 100644 --- a/web/app/components/plugins/plugin-detail-panel/app-selector/app-trigger.tsx +++ b/web/app/components/plugins/plugin-detail-panel/app-selector/app-trigger.tsx @@ -3,7 +3,7 @@ import type { App } from '@/types/app' import { RiArrowDownSLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import { cn } from '@/utils/classnames' diff --git a/web/app/components/plugins/plugin-detail-panel/app-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/app-selector/index.tsx index 965ae4e47a..ad7281e88e 100644 --- a/web/app/components/plugins/plugin-detail-panel/app-selector/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/app-selector/index.tsx @@ -5,7 +5,8 @@ import type { } from '@floating-ui/react' import type { FC } from 'react' import type { App } from '@/types/app' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { PortalToFollowElem, 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 003829b9ff..e9f5d88a29 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 @@ -5,7 +5,8 @@ // import ToolItem from '@/app/components/tools/provider/tool-item' // import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials' import type { PluginDetail } from '@/app/components/plugins/types' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { transformDataSourceToTool } from '@/app/components/workflow/block-selector/utils' import { useDataSourceList } from '@/service/use-pipeline' diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx index 6fe439b631..0dcd69c01b 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx @@ -6,7 +6,8 @@ import { RiHardDrive3Line, } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import { trackEvent } from '@/app/components/base/amplitude' 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 ba4d26d3dd..9ddaef1d81 100644 --- a/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx +++ b/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx @@ -2,7 +2,8 @@ import type { EndpointListItem, PluginDetail } from '../types' import { RiClipboardLine, RiDeleteBinLine, RiEditLine, RiLoginCircleLine } from '@remixicon/react' import { useBoolean } from 'ahooks' import copy from 'copy-to-clipboard' -import React, { useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Confirm from '@/app/components/base/confirm' 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 a1ccbcd446..7acc065371 100644 --- a/web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx +++ b/web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx @@ -5,7 +5,8 @@ import { RiBookOpenLine, } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Toast from '@/app/components/base/toast' 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 e2d7da6257..5e84dc3abc 100644 --- a/web/app/components/plugins/plugin-detail-panel/endpoint-modal.tsx +++ b/web/app/components/plugins/plugin-detail-panel/endpoint-modal.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import type { FormSchema } from '../../base/form/types' import type { PluginDetail } from '../types' import { RiArrowRightUpLine, RiCloseLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Button from '@/app/components/base/button' diff --git a/web/app/components/plugins/plugin-detail-panel/model-list.tsx b/web/app/components/plugins/plugin-detail-panel/model-list.tsx index 385c128b86..49d5780451 100644 --- a/web/app/components/plugins/plugin-detail-panel/model-list.tsx +++ b/web/app/components/plugins/plugin-detail-panel/model-list.tsx @@ -1,5 +1,5 @@ import type { PluginDetail } from '@/app/components/plugins/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' 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/plugins/plugin-detail-panel/model-selector/llm-params-panel.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/llm-params-panel.tsx index 3b26b40fb6..2d3d07cfef 100644 --- a/web/app/components/plugins/plugin-detail-panel/model-selector/llm-params-panel.tsx +++ b/web/app/components/plugins/plugin-detail-panel/model-selector/llm-params-panel.tsx @@ -3,7 +3,8 @@ import type { ModelParameterRule, } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { ParameterValue } from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' import ParameterItem from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item' 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 db208d349e..cc4f4d4881 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,4 +1,5 @@ -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { PortalSelect } from '@/app/components/base/select' import { languages } from '@/i18n-config/language' diff --git a/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx index e0013089b0..b3d841e86a 100644 --- a/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx @@ -5,7 +5,7 @@ import { RiAddLine, RiQuestionLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Divider from '@/app/components/base/divider' 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 e9d794964d..15e4d6df67 100644 --- a/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx +++ b/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { RiArrowRightUpLine, RiMoreFill } from '@remixicon/react' -import React, { useCallback, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' // import Button from '@/app/components/base/button' diff --git a/web/app/components/plugins/plugin-detail-panel/strategy-detail.tsx b/web/app/components/plugins/plugin-detail-panel/strategy-detail.tsx index 061e44d208..50c803b81b 100644 --- a/web/app/components/plugins/plugin-detail-panel/strategy-detail.tsx +++ b/web/app/components/plugins/plugin-detail-panel/strategy-detail.tsx @@ -8,7 +8,8 @@ import { RiArrowLeftLine, RiCloseLine, } from '@remixicon/react' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Divider from '@/app/components/base/divider' diff --git a/web/app/components/plugins/plugin-detail-panel/strategy-item.tsx b/web/app/components/plugins/plugin-detail-panel/strategy-item.tsx index 19adbd707d..280f1bce49 100644 --- a/web/app/components/plugins/plugin-detail-panel/strategy-item.tsx +++ b/web/app/components/plugins/plugin-detail-panel/strategy-item.tsx @@ -3,7 +3,8 @@ import type { StrategyDetail, } from '@/app/components/plugins/types' import type { Locale } from '@/i18n-config' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useRenderI18nObject } from '@/hooks/use-i18n' import { cn } from '@/utils/classnames' import StrategyDetailPanel from './strategy-detail' diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx index 51b661d2ba..16a789e67b 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/common-modal.tsx @@ -4,7 +4,8 @@ import type { TriggerSubscriptionBuilder } from '@/app/components/workflow/block import type { BuildTriggerSubscriptionPayload } from '@/service/use-triggers' import { RiLoader2Line } from '@remixicon/react' import { debounce } from 'lodash-es' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' // import { CopyFeedbackNew } from '@/app/components/base/copy-feedback' import { EncryptedBottom } from '@/app/components/base/encrypted-bottom' 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 d4c1b79c16..6c1094559e 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 @@ -6,7 +6,8 @@ import { RiClipboardLine, RiInformation2Fill, } from '@remixicon/react' -import React, { useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { BaseForm } from '@/app/components/base/form/components/base' diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/list-view.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/list-view.tsx index fd007409a4..628f561ca2 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/list-view.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/list-view.tsx @@ -1,5 +1,5 @@ 'use client' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import { cn } from '@/utils/classnames' 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 d042653a1b..f7ba8d8c35 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 @@ -8,7 +8,8 @@ import { RiFileCopyLine, } from '@remixicon/react' import dayjs from 'dayjs' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Toast from '@/app/components/base/toast' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/selector-view.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/selector-view.tsx index b98b8cc202..4fd1cdd7d2 100644 --- a/web/app/components/plugins/plugin-detail-panel/subscription-list/selector-view.tsx +++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/selector-view.tsx @@ -1,7 +1,8 @@ 'use client' import type { TriggerSubscription } from '@/app/components/workflow/block-selector/types' import { RiCheckLine, RiDeleteBinLine, RiWebhookLine } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx index bbfcc1fa09..2402a2af64 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/index.tsx @@ -8,7 +8,8 @@ import type { Node } from 'reactflow' import type { ToolDefaultValue, ToolValue } from '@/app/components/workflow/block-selector/types' import type { NodeOutPutVar } from '@/app/components/workflow/types' import Link from 'next/link' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import { diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/schema-modal.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/schema-modal.tsx index 6801a6df88..7b19707bb0 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/schema-modal.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/schema-modal.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { SchemaRoot } from '@/app/components/workflow/nodes/llm/types' import { RiCloseLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Modal from '@/app/components/base/modal' import VisualEditor from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor' diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-credentials-form.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-credentials-form.tsx index 342d91a084..eadbb4104c 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-credentials-form.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-credentials-form.tsx @@ -4,7 +4,8 @@ import type { Collection } from '@/app/components/tools/types' import { RiArrowRightUpLine, } from '@remixicon/react' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Loading from '@/app/components/base/loading' diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-item.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-item.tsx index 09d0eb898f..4cea66e505 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-item.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-item.tsx @@ -4,7 +4,8 @@ import { RiEqualizer2Line, RiErrorWarningFill, } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import AppIcon from '@/app/components/base/app-icon' diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-trigger.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-trigger.tsx index bfad698fff..e7c8d1e3e0 100644 --- a/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-trigger.tsx +++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/tool-trigger.tsx @@ -4,7 +4,7 @@ import { RiArrowDownSLine, RiEqualizer2Line, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import BlockIcon from '@/app/components/workflow/block-icon' import { BlockEnum } from '@/app/components/workflow/types' diff --git a/web/app/components/plugins/plugin-item/action.tsx b/web/app/components/plugins/plugin-item/action.tsx index 87830a745b..3644dee76f 100644 --- a/web/app/components/plugins/plugin-item/action.tsx +++ b/web/app/components/plugins/plugin-item/action.tsx @@ -4,7 +4,8 @@ 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 React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Toast from '@/app/components/base/toast' import { useModalContext } from '@/context/modal-context' diff --git a/web/app/components/plugins/plugin-item/index.tsx b/web/app/components/plugins/plugin-item/index.tsx index fe884f69b2..b2ee45bf68 100644 --- a/web/app/components/plugins/plugin-item/index.tsx +++ b/web/app/components/plugins/plugin-item/index.tsx @@ -8,7 +8,8 @@ import { RiHardDrive3Line, RiLoginCircleLine, } from '@remixicon/react' -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { gte } from 'semver' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/plugins/plugin-mutation-model/index.tsx b/web/app/components/plugins/plugin-mutation-model/index.tsx index 09eb72a656..2ac5368346 100644 --- a/web/app/components/plugins/plugin-mutation-model/index.tsx +++ b/web/app/components/plugins/plugin-mutation-model/index.tsx @@ -1,7 +1,8 @@ import type { UseMutationResult } from '@tanstack/react-query' import type { FC, ReactNode } from 'react' import type { Plugin } from '../types' -import React, { memo } from 'react' +import * as React from 'react' +import { memo } from 'react' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' import Card from '@/app/components/plugins/card' diff --git a/web/app/components/plugins/plugin-page/debug-info.tsx b/web/app/components/plugins/plugin-page/debug-info.tsx index 69b22001c9..ea6d0afccf 100644 --- a/web/app/components/plugins/plugin-page/debug-info.tsx +++ b/web/app/components/plugins/plugin-page/debug-info.tsx @@ -4,7 +4,7 @@ import { RiArrowRightUpLine, RiBugLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Button from '@/app/components/base/button' diff --git a/web/app/components/plugins/plugin-page/empty/index.tsx b/web/app/components/plugins/plugin-page/empty/index.tsx index 83b67c2320..4d8904d293 100644 --- a/web/app/components/plugins/plugin-page/empty/index.tsx +++ b/web/app/components/plugins/plugin-page/empty/index.tsx @@ -1,6 +1,7 @@ 'use client' import { noop } from 'lodash-es' -import React, { useEffect, useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { Group } from '@/app/components/base/icons/src/vender/other' diff --git a/web/app/components/plugins/plugin-page/filter-management/index.tsx b/web/app/components/plugins/plugin-page/filter-management/index.tsx index cc4d64759e..ad1b3329c6 100644 --- a/web/app/components/plugins/plugin-page/filter-management/index.tsx +++ b/web/app/components/plugins/plugin-page/filter-management/index.tsx @@ -1,4 +1,5 @@ -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { usePluginPageContext } from '../context' import CategoriesFilter from './category-filter' import SearchBox from './search-box' diff --git a/web/app/components/plugins/plugin-page/plugin-info.tsx b/web/app/components/plugins/plugin-page/plugin-info.tsx index cc41fb88b8..fdc09b91c2 100644 --- a/web/app/components/plugins/plugin-page/plugin-info.tsx +++ b/web/app/components/plugins/plugin-page/plugin-info.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Modal from '../../base/modal' import KeyValueItem from '../base/key-value-item' diff --git a/web/app/components/plugins/provider-card.tsx b/web/app/components/plugins/provider-card.tsx index ad3f0f005d..3470d28495 100644 --- a/web/app/components/plugins/provider-card.tsx +++ b/web/app/components/plugins/provider-card.tsx @@ -4,7 +4,8 @@ import type { Plugin } from './types' import { RiArrowRightUpLine } from '@remixicon/react' import { useBoolean } from 'ahooks' import { useTheme } from 'next-themes' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' diff --git a/web/app/components/plugins/readme-panel/entrance.tsx b/web/app/components/plugins/readme-panel/entrance.tsx index 611796d70d..9060041476 100644 --- a/web/app/components/plugins/readme-panel/entrance.tsx +++ b/web/app/components/plugins/readme-panel/entrance.tsx @@ -1,6 +1,6 @@ import type { PluginDetail } from '../types' import { RiBookReadLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import { BUILTIN_TOOLS_ARRAY } from './constants' diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/index.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/index.tsx index 05b826f22d..92ff0d8786 100644 --- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/index.tsx +++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/index.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { AutoUpdateConfig } from './types' import type { TriggerParams } from '@/app/components/base/date-and-time-picker/types' import { RiTimeLine } from '@remixicon/react' -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import { Trans, useTranslation } from 'react-i18next' import TimePicker from '@/app/components/base/date-and-time-picker/time-picker' import { convertTimezoneToOffsetStr } from '@/app/components/base/date-and-time-picker/utils/dayjs' diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/no-data-placeholder.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/no-data-placeholder.tsx index 8e2b00dab7..84f78c45cd 100644 --- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/no-data-placeholder.tsx +++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/no-data-placeholder.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { SearchMenu } from '@/app/components/base/icons/src/vender/line/general' import { Group } from '@/app/components/base/icons/src/vender/other' diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/no-plugin-selected.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/no-plugin-selected.tsx index 4e1301e92f..0f3f6e8b8c 100644 --- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/no-plugin-selected.tsx +++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/no-plugin-selected.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { AUTO_UPDATE_MODE } from './types' 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 7a3f864ac4..ead9472606 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 @@ -2,7 +2,7 @@ import type { FC } from 'react' import { RiAddLine } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import NoPluginSelected from './no-plugin-selected' diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/plugins-selected.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/plugins-selected.tsx index 72b5b7398a..0284ec8a7f 100644 --- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/plugins-selected.tsx +++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/plugins-selected.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import Icon from '@/app/components/plugins/card/base/card-icon' import { MARKETPLACE_API_PREFIX } from '@/config' import { cn } from '@/utils/classnames' diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/tool-item.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/tool-item.tsx index 48f7ff37ab..5a6dc08181 100644 --- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/tool-item.tsx +++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/tool-item.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { PluginDetail } from '@/app/components/plugins/types' -import React from 'react' +import * as React from 'react' import Checkbox from '@/app/components/base/checkbox' import Icon from '@/app/components/plugins/card/base/card-icon' import { MARKETPLACE_API_PREFIX } from '@/config' diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/tool-picker.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/tool-picker.tsx index 3020bd96f1..b063277400 100644 --- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/tool-picker.tsx +++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/tool-picker.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' import { diff --git a/web/app/components/plugins/reference-setting-modal/label.tsx b/web/app/components/plugins/reference-setting-modal/label.tsx index dc39c12b28..720361d79a 100644 --- a/web/app/components/plugins/reference-setting-modal/label.tsx +++ b/web/app/components/plugins/reference-setting-modal/label.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type Props = { diff --git a/web/app/components/plugins/reference-setting-modal/modal.tsx b/web/app/components/plugins/reference-setting-modal/modal.tsx index 7c6ff5df8d..bc28960157 100644 --- a/web/app/components/plugins/reference-setting-modal/modal.tsx +++ b/web/app/components/plugins/reference-setting-modal/modal.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { AutoUpdateConfig } from './auto-update-setting/types' import type { Permissions, ReferenceSetting } from '@/app/components/plugins/types' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' diff --git a/web/app/components/plugins/update-plugin/from-github.tsx b/web/app/components/plugins/update-plugin/from-github.tsx index 437837d8d9..c4bf1a8a0a 100644 --- a/web/app/components/plugins/update-plugin/from-github.tsx +++ b/web/app/components/plugins/update-plugin/from-github.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { UpdateFromGitHubPayload } from '../types' -import React from 'react' +import * as React from 'react' import InstallFromGitHub from '../install-plugin/install-from-github' type Props = { 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 d2802db54d..b45f8d24d8 100644 --- a/web/app/components/plugins/update-plugin/from-market-place.tsx +++ b/web/app/components/plugins/update-plugin/from-market-place.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { UpdateFromMarketPlacePayload } from '../types' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +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/button' diff --git a/web/app/components/plugins/update-plugin/index.tsx b/web/app/components/plugins/update-plugin/index.tsx index 2ed3cef9e4..f8f77e845a 100644 --- a/web/app/components/plugins/update-plugin/index.tsx +++ b/web/app/components/plugins/update-plugin/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { UpdatePluginModalType } from '../types' -import React from 'react' +import * as React from 'react' import { PluginSource } from '../types' import UpdateFromGitHub from './from-github' import UpdateFromMarketplace from './from-market-place' 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 3c18d7fc0b..6562acdb6b 100644 --- a/web/app/components/plugins/update-plugin/plugin-version-picker.tsx +++ b/web/app/components/plugins/update-plugin/plugin-version-picker.tsx @@ -4,7 +4,8 @@ import type { Placement, } from '@floating-ui/react' import type { FC } from 'react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { lt } from 'semver' import Badge from '@/app/components/base/badge' diff --git a/web/app/components/rag-pipeline/components/chunk-card-list/chunk-card.tsx b/web/app/components/rag-pipeline/components/chunk-card-list/chunk-card.tsx index 62e90dbf1c..7efe1ff76a 100644 --- a/web/app/components/rag-pipeline/components/chunk-card-list/chunk-card.tsx +++ b/web/app/components/rag-pipeline/components/chunk-card-list/chunk-card.tsx @@ -1,6 +1,7 @@ import type { QAChunk } from './types' import type { ParentMode } from '@/models/datasets' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import Dot from '@/app/components/datasets/documents/detail/completed/common/dot' import SegmentIndexTag from '@/app/components/datasets/documents/detail/completed/common/segment-index-tag' diff --git a/web/app/components/rag-pipeline/components/chunk-card-list/q-a-item.tsx b/web/app/components/rag-pipeline/components/chunk-card-list/q-a-item.tsx index 4a34222e1d..bd317a2ce4 100644 --- a/web/app/components/rag-pipeline/components/chunk-card-list/q-a-item.tsx +++ b/web/app/components/rag-pipeline/components/chunk-card-list/q-a-item.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { QAItemType } from './types' type QAItemProps = { diff --git a/web/app/components/rag-pipeline/components/conversion.tsx b/web/app/components/rag-pipeline/components/conversion.tsx index 3e55350d4c..8e20df54b4 100644 --- a/web/app/components/rag-pipeline/components/conversion.tsx +++ b/web/app/components/rag-pipeline/components/conversion.tsx @@ -1,5 +1,6 @@ import { useParams } from 'next/navigation' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/rag-pipeline/components/panel/input-field/editor/form/hidden-fields.tsx b/web/app/components/rag-pipeline/components/panel/input-field/editor/form/hidden-fields.tsx index 4d052f39cd..f7b35e7916 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/editor/form/hidden-fields.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/editor/form/hidden-fields.tsx @@ -1,5 +1,5 @@ import { useStore } from '@tanstack/react-form' -import React from 'react' +import * as React from 'react' import { withForm } from '@/app/components/base/form' import InputField from '@/app/components/base/form/form-scenarios/input-field/field' import { useHiddenConfigurations } from './hooks' diff --git a/web/app/components/rag-pipeline/components/panel/input-field/editor/form/initial-fields.tsx b/web/app/components/rag-pipeline/components/panel/input-field/editor/form/initial-fields.tsx index 9c6a053509..1cd6e24789 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/editor/form/initial-fields.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/editor/form/initial-fields.tsx @@ -1,4 +1,5 @@ -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { withForm } from '@/app/components/base/form' import InputField from '@/app/components/base/form/form-scenarios/input-field/field' import { useConfigurations } from './hooks' diff --git a/web/app/components/rag-pipeline/components/panel/input-field/editor/form/show-all-settings.tsx b/web/app/components/rag-pipeline/components/panel/input-field/editor/form/show-all-settings.tsx index 2a27710cd0..ac8fa0f715 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/editor/form/show-all-settings.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/editor/form/show-all-settings.tsx @@ -1,6 +1,6 @@ import { RiArrowRightSLine } from '@remixicon/react' import { useStore } from '@tanstack/react-form' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { withForm } from '@/app/components/base/form' import { useHiddenFieldNames } from './hooks' diff --git a/web/app/components/rag-pipeline/components/panel/input-field/field-list/field-item.tsx b/web/app/components/rag-pipeline/components/panel/input-field/field-list/field-item.tsx index a76a4db7ba..9b566b916c 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/field-list/field-item.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/field-list/field-item.tsx @@ -7,7 +7,8 @@ import { RiEditLine, } from '@remixicon/react' import { useHover } from 'ahooks' -import React, { useCallback, useRef } from 'react' +import * as React from 'react' +import { useCallback, useRef } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Badge from '@/app/components/base/badge' diff --git a/web/app/components/rag-pipeline/components/panel/input-field/field-list/index.tsx b/web/app/components/rag-pipeline/components/panel/input-field/field-list/index.tsx index 30d05bef40..0449f7c9a4 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/field-list/index.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/field-list/index.tsx @@ -1,6 +1,7 @@ import type { InputVar } from '@/models/pipeline' import { RiAddLine } from '@remixicon/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import ActionButton from '@/app/components/base/action-button' import RemoveEffectVarConfirm from '@/app/components/workflow/nodes/_base/components/remove-effect-var-confirm' import { cn } from '@/utils/classnames' diff --git a/web/app/components/rag-pipeline/components/panel/input-field/footer-tip.tsx b/web/app/components/rag-pipeline/components/panel/input-field/footer-tip.tsx index a173f36230..4ee55861ec 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/footer-tip.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/footer-tip.tsx @@ -1,5 +1,5 @@ import { RiDragDropLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' const FooterTip = () => { return ( diff --git a/web/app/components/rag-pipeline/components/panel/input-field/label-right-content/datasource.tsx b/web/app/components/rag-pipeline/components/panel/input-field/label-right-content/datasource.tsx index 3f3481b511..aecb250809 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/label-right-content/datasource.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/label-right-content/datasource.tsx @@ -1,5 +1,5 @@ import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' -import React from 'react' +import * as React from 'react' 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/rag-pipeline/components/panel/input-field/label-right-content/global-inputs.tsx b/web/app/components/rag-pipeline/components/panel/input-field/label-right-content/global-inputs.tsx index ead0f3c851..601efe3221 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/label-right-content/global-inputs.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/label-right-content/global-inputs.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/rag-pipeline/components/panel/input-field/preview/data-source.tsx b/web/app/components/rag-pipeline/components/panel/input-field/preview/data-source.tsx index cc216a6aaa..e13336e773 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/preview/data-source.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/preview/data-source.tsx @@ -1,5 +1,5 @@ import type { Datasource } from '../../test-run/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useStore } from '@/app/components/workflow/store' import { useDraftPipelinePreProcessingParams } from '@/service/use-pipeline' diff --git a/web/app/components/rag-pipeline/components/panel/input-field/preview/process-documents.tsx b/web/app/components/rag-pipeline/components/panel/input-field/preview/process-documents.tsx index c473e13add..9beb2c364e 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/preview/process-documents.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/preview/process-documents.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useStore } from '@/app/components/workflow/store' import { useDraftPipelineProcessingParams } from '@/service/use-pipeline' diff --git a/web/app/components/rag-pipeline/components/panel/test-run/header.tsx b/web/app/components/rag-pipeline/components/panel/test-run/header.tsx index a7949dd68f..bc139201bb 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/header.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/header.tsx @@ -1,5 +1,6 @@ import { RiCloseLine } from '@remixicon/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { useWorkflowInteractions } from '@/app/components/workflow/hooks' import { useWorkflowStore } from '@/app/components/workflow/store' diff --git a/web/app/components/rag-pipeline/components/panel/test-run/preparation/actions/index.tsx b/web/app/components/rag-pipeline/components/panel/test-run/preparation/actions/index.tsx index 2e4e97c665..4556031f31 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/preparation/actions/index.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/preparation/actions/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' diff --git a/web/app/components/rag-pipeline/components/panel/test-run/preparation/data-source-options/option-card.tsx b/web/app/components/rag-pipeline/components/panel/test-run/preparation/data-source-options/option-card.tsx index 1074276552..7198d4d988 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/preparation/data-source-options/option-card.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/preparation/data-source-options/option-card.tsx @@ -1,5 +1,6 @@ import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' 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/rag-pipeline/components/panel/test-run/preparation/document-processing/actions.tsx b/web/app/components/rag-pipeline/components/panel/test-run/preparation/document-processing/actions.tsx index 47f06557d4..a9f0c6bff6 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/preparation/document-processing/actions.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/preparation/document-processing/actions.tsx @@ -1,5 +1,5 @@ import type { CustomActionsProps } from '@/app/components/base/form/components/form/actions' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { useStore } from '@/app/components/workflow/store' diff --git a/web/app/components/rag-pipeline/components/panel/test-run/preparation/document-processing/index.tsx b/web/app/components/rag-pipeline/components/panel/test-run/preparation/document-processing/index.tsx index c4ad280dce..3186e5bb7d 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/preparation/document-processing/index.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/preparation/document-processing/index.tsx @@ -1,5 +1,6 @@ import type { CustomActionsProps } from '@/app/components/base/form/components/form/actions' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { generateZodSchema } from '@/app/components/base/form/form-scenarios/base/utils' import { useConfigurations, useInitialData } from '@/app/components/rag-pipeline/hooks/use-input-fields' import Actions from './actions' diff --git a/web/app/components/rag-pipeline/components/panel/test-run/preparation/footer-tips.tsx b/web/app/components/rag-pipeline/components/panel/test-run/preparation/footer-tips.tsx index 58ea748994..a2502838ea 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/preparation/footer-tips.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/preparation/footer-tips.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' const FooterTips = () => { diff --git a/web/app/components/rag-pipeline/components/panel/test-run/preparation/index.tsx b/web/app/components/rag-pipeline/components/panel/test-run/preparation/index.tsx index b5ec39d985..bf4f74493b 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/preparation/index.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/preparation/index.tsx @@ -1,5 +1,6 @@ import type { Datasource } from '../types' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useShallow } from 'zustand/react/shallow' import { trackEvent } from '@/app/components/base/amplitude' import LocalFile from '@/app/components/datasets/documents/create-from-pipeline/data-source/local-file' diff --git a/web/app/components/rag-pipeline/components/panel/test-run/preparation/step-indicator.tsx b/web/app/components/rag-pipeline/components/panel/test-run/preparation/step-indicator.tsx index 9ab7015e07..93c4af8047 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/preparation/step-indicator.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/preparation/step-indicator.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import Divider from '@/app/components/base/divider' import { cn } from '@/utils/classnames' diff --git a/web/app/components/rag-pipeline/components/panel/test-run/result/result-preview/index.tsx b/web/app/components/rag-pipeline/components/panel/test-run/result/result-preview/index.tsx index 395fdc09e9..92932dbc5f 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/result/result-preview/index.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/result/result-preview/index.tsx @@ -1,5 +1,6 @@ import { RiLoader2Line } from '@remixicon/react' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { RAG_PIPELINE_PREVIEW_CHUNK_NUM } from '@/config' diff --git a/web/app/components/rag-pipeline/components/panel/test-run/result/tabs/index.tsx b/web/app/components/rag-pipeline/components/panel/test-run/result/tabs/index.tsx index 4e4d51eb02..7a64810710 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/result/tabs/index.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/result/tabs/index.tsx @@ -1,5 +1,5 @@ import type { WorkflowRunningData } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Tab from './tab' diff --git a/web/app/components/rag-pipeline/components/panel/test-run/result/tabs/tab.tsx b/web/app/components/rag-pipeline/components/panel/test-run/result/tabs/tab.tsx index 640a3feb17..ee9e3f5564 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/result/tabs/tab.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/result/tabs/tab.tsx @@ -1,5 +1,6 @@ import type { WorkflowRunningData } from '@/app/components/workflow/types' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { cn } from '@/utils/classnames' type TabProps = { diff --git a/web/app/components/rag-pipeline/components/rag-pipeline-header/run-mode.tsx b/web/app/components/rag-pipeline/components/rag-pipeline-header/run-mode.tsx index 4d5066a285..be5b4c11bd 100644 --- a/web/app/components/rag-pipeline/components/rag-pipeline-header/run-mode.tsx +++ b/web/app/components/rag-pipeline/components/rag-pipeline-header/run-mode.tsx @@ -1,5 +1,6 @@ import { RiCloseLine, RiDatabase2Line, RiLoader2Line, RiPlayLargeLine } from '@remixicon/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices' import { useWorkflowRun, useWorkflowStartRun } from '@/app/components/workflow/hooks' diff --git a/web/app/components/rag-pipeline/components/screenshot.tsx b/web/app/components/rag-pipeline/components/screenshot.tsx index dae6f04161..3138b846d9 100644 --- a/web/app/components/rag-pipeline/components/screenshot.tsx +++ b/web/app/components/rag-pipeline/components/screenshot.tsx @@ -1,5 +1,5 @@ import Image from 'next/image' -import React from 'react' +import * as React from 'react' import useTheme from '@/hooks/use-theme' import { basePath } from '@/utils/var' diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx index 5c58635e0f..157ed123d1 100644 --- a/web/app/components/share/text-generation/index.tsx +++ b/web/app/components/share/text-generation/index.tsx @@ -15,7 +15,8 @@ import { } from '@remixicon/react' import { useBoolean } from 'ahooks' import { useSearchParams } from 'next/navigation' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import SavedItems from '@/app/components/app/text-generate/saved-items' import AppIcon from '@/app/components/base/app-icon' diff --git a/web/app/components/share/text-generation/info-modal.tsx b/web/app/components/share/text-generation/info-modal.tsx index e206e6d6c9..9ee6557cef 100644 --- a/web/app/components/share/text-generation/info-modal.tsx +++ b/web/app/components/share/text-generation/info-modal.tsx @@ -1,5 +1,5 @@ import type { SiteInfo } from '@/models/share' -import React from 'react' +import * as React from 'react' import AppIcon from '@/app/components/base/app-icon' import Modal from '@/app/components/base/modal' import { appDefaultIconBackground } from '@/config' diff --git a/web/app/components/share/text-generation/menu-dropdown.tsx b/web/app/components/share/text-generation/menu-dropdown.tsx index 270789f2c6..c96c9ffa32 100644 --- a/web/app/components/share/text-generation/menu-dropdown.tsx +++ b/web/app/components/share/text-generation/menu-dropdown.tsx @@ -6,7 +6,8 @@ import { RiEqualizer2Line, } from '@remixicon/react' import { usePathname, useRouter } from 'next/navigation' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import { diff --git a/web/app/components/share/text-generation/no-data/index.spec.tsx b/web/app/components/share/text-generation/no-data/index.spec.tsx index 14a86a3c1b..41de9907fd 100644 --- a/web/app/components/share/text-generation/no-data/index.spec.tsx +++ b/web/app/components/share/text-generation/no-data/index.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import NoData from './index' describe('NoData', () => { diff --git a/web/app/components/share/text-generation/no-data/index.tsx b/web/app/components/share/text-generation/no-data/index.tsx index 5e10c821f8..f99c0af57d 100644 --- a/web/app/components/share/text-generation/no-data/index.tsx +++ b/web/app/components/share/text-generation/no-data/index.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import { RiSparklingFill, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' export type INoDataProps = {} diff --git a/web/app/components/share/text-generation/result/content.tsx b/web/app/components/share/text-generation/result/content.tsx index ec7c439ea9..01161d6dcd 100644 --- a/web/app/components/share/text-generation/result/content.tsx +++ b/web/app/components/share/text-generation/result/content.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import type { FeedbackType } from '@/app/components/base/chat/chat/type' -import React from 'react' +import * as React from 'react' import { format } from '@/service/base' import Header from './header' diff --git a/web/app/components/share/text-generation/result/header.tsx b/web/app/components/share/text-generation/result/header.tsx index b2cacfc3c5..409aad5cef 100644 --- a/web/app/components/share/text-generation/result/header.tsx +++ b/web/app/components/share/text-generation/result/header.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import type { FeedbackType } from '@/app/components/base/chat/chat/type' import { ClipboardDocumentIcon, HandThumbDownIcon, HandThumbUpIcon } from '@heroicons/react/24/outline' import copy from 'copy-to-clipboard' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Toast from '@/app/components/base/toast' diff --git a/web/app/components/share/text-generation/result/index.tsx b/web/app/components/share/text-generation/result/index.tsx index 1b40b48d45..902da881ae 100644 --- a/web/app/components/share/text-generation/result/index.tsx +++ b/web/app/components/share/text-generation/result/index.tsx @@ -11,7 +11,8 @@ import { RiLoader2Line } from '@remixicon/react' import { useBoolean } from 'ahooks' import { t } from 'i18next' import { produce } from 'immer' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import TextGenerationRes from '@/app/components/app/text-generate/item' import Button from '@/app/components/base/button' import { diff --git a/web/app/components/share/text-generation/run-batch/csv-download/index.spec.tsx b/web/app/components/share/text-generation/run-batch/csv-download/index.spec.tsx index 385ccb91df..120e3ed0c2 100644 --- a/web/app/components/share/text-generation/run-batch/csv-download/index.spec.tsx +++ b/web/app/components/share/text-generation/run-batch/csv-download/index.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import CSVDownload from './index' const mockType = { Link: 'mock-link' } diff --git a/web/app/components/share/text-generation/run-batch/csv-download/index.tsx b/web/app/components/share/text-generation/run-batch/csv-download/index.tsx index a7e6d5bf2c..eb1f9aa86a 100644 --- a/web/app/components/share/text-generation/run-batch/csv-download/index.tsx +++ b/web/app/components/share/text-generation/run-batch/csv-download/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useCSVDownloader, diff --git a/web/app/components/share/text-generation/run-batch/csv-reader/index.spec.tsx b/web/app/components/share/text-generation/run-batch/csv-reader/index.spec.tsx index 1b93e7b9f9..83e89a0a04 100644 --- a/web/app/components/share/text-generation/run-batch/csv-reader/index.spec.tsx +++ b/web/app/components/share/text-generation/run-batch/csv-reader/index.spec.tsx @@ -1,5 +1,5 @@ import { act, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import CSVReader from './index' let mockAcceptedFile: { name: string } | null = null diff --git a/web/app/components/share/text-generation/run-batch/csv-reader/index.tsx b/web/app/components/share/text-generation/run-batch/csv-reader/index.tsx index 895d472297..95488c1e85 100644 --- a/web/app/components/share/text-generation/run-batch/csv-reader/index.tsx +++ b/web/app/components/share/text-generation/run-batch/csv-reader/index.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { useCSVReader, diff --git a/web/app/components/share/text-generation/run-batch/index.spec.tsx b/web/app/components/share/text-generation/run-batch/index.spec.tsx index a3c3bbfd40..4359a66a58 100644 --- a/web/app/components/share/text-generation/run-batch/index.spec.tsx +++ b/web/app/components/share/text-generation/run-batch/index.spec.tsx @@ -1,6 +1,6 @@ import type { Mock } from 'vitest' import { act, fireEvent, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' import RunBatch from './index' diff --git a/web/app/components/share/text-generation/run-batch/index.tsx b/web/app/components/share/text-generation/run-batch/index.tsx index 74e77e6165..793817f191 100644 --- a/web/app/components/share/text-generation/run-batch/index.tsx +++ b/web/app/components/share/text-generation/run-batch/index.tsx @@ -4,7 +4,7 @@ import { RiLoader2Line, RiPlayLargeLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' diff --git a/web/app/components/share/text-generation/run-batch/res-download/index.spec.tsx b/web/app/components/share/text-generation/run-batch/res-download/index.spec.tsx index 2a4ec0b3c1..b71b252345 100644 --- a/web/app/components/share/text-generation/run-batch/res-download/index.spec.tsx +++ b/web/app/components/share/text-generation/run-batch/res-download/index.spec.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import ResDownload from './index' const mockType = { Link: 'mock-link' } diff --git a/web/app/components/share/text-generation/run-batch/res-download/index.tsx b/web/app/components/share/text-generation/run-batch/res-download/index.tsx index cdc2b8f41b..d7c42362fd 100644 --- a/web/app/components/share/text-generation/run-batch/res-download/index.tsx +++ b/web/app/components/share/text-generation/run-batch/res-download/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiDownloadLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useCSVDownloader, diff --git a/web/app/components/share/text-generation/run-once/index.spec.tsx b/web/app/components/share/text-generation/run-once/index.spec.tsx index 4283409c1b..abead21c07 100644 --- a/web/app/components/share/text-generation/run-once/index.spec.tsx +++ b/web/app/components/share/text-generation/run-once/index.spec.tsx @@ -2,7 +2,8 @@ import type { PromptConfig, PromptVariable } from '@/models/debug' import type { SiteInfo } from '@/models/share' import type { VisionSettings } from '@/types/app' import { fireEvent, render, screen, waitFor } from '@testing-library/react' -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { Resolution, TransferMethod } from '@/types/app' import RunOnce from './index' diff --git a/web/app/components/share/text-generation/run-once/index.tsx b/web/app/components/share/text-generation/run-once/index.tsx index 09bd4872cb..eea2b1592c 100644 --- a/web/app/components/share/text-generation/run-once/index.tsx +++ b/web/app/components/share/text-generation/run-once/index.tsx @@ -6,7 +6,8 @@ import { RiLoader2Line, RiPlayLargeLine, } from '@remixicon/react' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader' diff --git a/web/app/components/splash.tsx b/web/app/components/splash.tsx index 031b9b3230..e4103e8c93 100644 --- a/web/app/components/splash.tsx +++ b/web/app/components/splash.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC, PropsWithChildren } from 'react' -import React from 'react' +import * as React from 'react' import { useIsLogin } from '@/service/use-common' import Loading from './base/loading' diff --git a/web/app/components/tools/edit-custom-collection-modal/config-credentials.tsx b/web/app/components/tools/edit-custom-collection-modal/config-credentials.tsx index 19fa90fc25..1fa5e23347 100644 --- a/web/app/components/tools/edit-custom-collection-modal/config-credentials.tsx +++ b/web/app/components/tools/edit-custom-collection-modal/config-credentials.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { Credential } from '@/app/components/tools/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Drawer from '@/app/components/base/drawer-plus' diff --git a/web/app/components/tools/edit-custom-collection-modal/get-schema.tsx b/web/app/components/tools/edit-custom-collection-modal/get-schema.tsx index fa4f45e75f..4ecee282f9 100644 --- a/web/app/components/tools/edit-custom-collection-modal/get-schema.tsx +++ b/web/app/components/tools/edit-custom-collection-modal/get-schema.tsx @@ -5,7 +5,8 @@ import { RiArrowDownSLine, } from '@remixicon/react' import { useClickAway } from 'ahooks' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' diff --git a/web/app/components/tools/edit-custom-collection-modal/index.tsx b/web/app/components/tools/edit-custom-collection-modal/index.tsx index 276230aa1b..93ef9142d9 100644 --- a/web/app/components/tools/edit-custom-collection-modal/index.tsx +++ b/web/app/components/tools/edit-custom-collection-modal/index.tsx @@ -4,7 +4,8 @@ import type { Credential, CustomCollectionBackend, CustomParamSchema, Emoji } fr import { RiSettings2Line } from '@remixicon/react' import { useDebounce, useGetState } from 'ahooks' import { produce } from 'immer' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import Button from '@/app/components/base/button' diff --git a/web/app/components/tools/edit-custom-collection-modal/test-api.tsx b/web/app/components/tools/edit-custom-collection-modal/test-api.tsx index 29cd543e1e..8aacb7ad07 100644 --- a/web/app/components/tools/edit-custom-collection-modal/test-api.tsx +++ b/web/app/components/tools/edit-custom-collection-modal/test-api.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { Credential, CustomCollectionBackend, CustomParamSchema } from '@/app/components/tools/types' import { RiSettings2Line } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Button from '@/app/components/base/button' diff --git a/web/app/components/tools/marketplace/index.spec.tsx b/web/app/components/tools/marketplace/index.spec.tsx index 1056fe9d84..dcdda15588 100644 --- a/web/app/components/tools/marketplace/index.spec.tsx +++ b/web/app/components/tools/marketplace/index.spec.tsx @@ -2,7 +2,7 @@ import type { Plugin } from '@/app/components/plugins/types' import type { Collection } from '@/app/components/tools/types' import { act, render, renderHook, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import { SCROLL_BOTTOM_THRESHOLD } from '@/app/components/plugins/marketplace/constants' import { getMarketplaceListCondition } from '@/app/components/plugins/marketplace/utils' import { PluginCategoryEnum } from '@/app/components/plugins/types' diff --git a/web/app/components/tools/mcp/detail/content.tsx b/web/app/components/tools/mcp/detail/content.tsx index bf57515f7d..417d8857f0 100644 --- a/web/app/components/tools/mcp/detail/content.tsx +++ b/web/app/components/tools/mcp/detail/content.tsx @@ -8,7 +8,8 @@ import { } from '@remixicon/react' import { useBoolean } from 'ahooks' import copy from 'copy-to-clipboard' -import React, { useCallback, useEffect } from 'react' +import * as React from 'react' +import { useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Button from '@/app/components/base/button' diff --git a/web/app/components/tools/mcp/detail/list-loading.tsx b/web/app/components/tools/mcp/detail/list-loading.tsx index f770728715..636865fcce 100644 --- a/web/app/components/tools/mcp/detail/list-loading.tsx +++ b/web/app/components/tools/mcp/detail/list-loading.tsx @@ -1,5 +1,5 @@ 'use client' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' const ListLoading = () => { diff --git a/web/app/components/tools/mcp/detail/operation-dropdown.tsx b/web/app/components/tools/mcp/detail/operation-dropdown.tsx index 4ec80c5998..8955a5b50d 100644 --- a/web/app/components/tools/mcp/detail/operation-dropdown.tsx +++ b/web/app/components/tools/mcp/detail/operation-dropdown.tsx @@ -5,7 +5,8 @@ import { RiEditLine, RiMoreFill, } from '@remixicon/react' -import React, { useCallback, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import { diff --git a/web/app/components/tools/mcp/detail/provider-detail.tsx b/web/app/components/tools/mcp/detail/provider-detail.tsx index d2f23b1887..f70006cd05 100644 --- a/web/app/components/tools/mcp/detail/provider-detail.tsx +++ b/web/app/components/tools/mcp/detail/provider-detail.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { ToolWithProvider } from '../../../workflow/types' -import React from 'react' +import * as React from 'react' import Drawer from '@/app/components/base/drawer' import { cn } from '@/utils/classnames' import MCPDetailContent from './content' diff --git a/web/app/components/tools/mcp/detail/tool-item.tsx b/web/app/components/tools/mcp/detail/tool-item.tsx index 83e58817f4..6e7673b3cd 100644 --- a/web/app/components/tools/mcp/detail/tool-item.tsx +++ b/web/app/components/tools/mcp/detail/tool-item.tsx @@ -1,6 +1,6 @@ 'use client' import type { Tool } from '@/app/components/tools/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/tools/mcp/headers-input.tsx b/web/app/components/tools/mcp/headers-input.tsx index 18ee9b1cc6..d6bb00bdb9 100644 --- a/web/app/components/tools/mcp/headers-input.tsx +++ b/web/app/components/tools/mcp/headers-input.tsx @@ -1,6 +1,6 @@ 'use client' import { RiAddLine, RiDeleteBinLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { v4 as uuid } from 'uuid' import ActionButton from '@/app/components/base/action-button' diff --git a/web/app/components/tools/mcp/mcp-server-modal.tsx b/web/app/components/tools/mcp/mcp-server-modal.tsx index 15fd2a573b..e0036a3dc4 100644 --- a/web/app/components/tools/mcp/mcp-server-modal.tsx +++ b/web/app/components/tools/mcp/mcp-server-modal.tsx @@ -3,7 +3,7 @@ import type { MCPServerDetail, } from '@/app/components/tools/types' import { RiCloseLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' diff --git a/web/app/components/tools/mcp/mcp-server-param-item.tsx b/web/app/components/tools/mcp/mcp-server-param-item.tsx index 3d99cc5bad..d951f19caa 100644 --- a/web/app/components/tools/mcp/mcp-server-param-item.tsx +++ b/web/app/components/tools/mcp/mcp-server-param-item.tsx @@ -1,5 +1,5 @@ 'use client' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Textarea from '@/app/components/base/textarea' diff --git a/web/app/components/tools/mcp/mcp-service-card.tsx b/web/app/components/tools/mcp/mcp-service-card.tsx index 521e93222b..07aa5e0168 100644 --- a/web/app/components/tools/mcp/mcp-service-card.tsx +++ b/web/app/components/tools/mcp/mcp-service-card.tsx @@ -2,7 +2,8 @@ import type { AppDetailResponse } from '@/models/app' import type { AppSSO } from '@/types/app' import { RiEditLine, RiLoopLeftLine } from '@remixicon/react' -import React, { useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/tools/mcp/modal.tsx b/web/app/components/tools/mcp/modal.tsx index a739964ee3..eb8f280484 100644 --- a/web/app/components/tools/mcp/modal.tsx +++ b/web/app/components/tools/mcp/modal.tsx @@ -6,7 +6,8 @@ import type { AppIconType } from '@/types/app' import { RiCloseLine, RiEditLine } from '@remixicon/react' import { useHover } from 'ahooks' import { noop } from 'lodash-es' -import React, { useCallback, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { getDomain } from 'tldts' import { v4 as uuid } from 'uuid' diff --git a/web/app/components/tools/provider/detail.tsx b/web/app/components/tools/provider/detail.tsx index e881dd3997..c4b65f353d 100644 --- a/web/app/components/tools/provider/detail.tsx +++ b/web/app/components/tools/provider/detail.tsx @@ -3,7 +3,8 @@ import type { Collection, CustomCollectionBackend, Tool, WorkflowToolProviderReq import { RiCloseLine, } from '@remixicon/react' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import ActionButton from '@/app/components/base/action-button' diff --git a/web/app/components/tools/provider/tool-item.tsx b/web/app/components/tools/provider/tool-item.tsx index 3248e3c024..b240bf6a41 100644 --- a/web/app/components/tools/provider/tool-item.tsx +++ b/web/app/components/tools/provider/tool-item.tsx @@ -1,6 +1,7 @@ 'use client' import type { Collection, Tool } from '../types' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useContext } from 'use-context-selector' import SettingBuiltInTool from '@/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool' import I18n from '@/context/i18n' diff --git a/web/app/components/tools/setting/build-in/config-credentials.tsx b/web/app/components/tools/setting/build-in/config-credentials.tsx index 783d4a6476..43383cdb51 100644 --- a/web/app/components/tools/setting/build-in/config-credentials.tsx +++ b/web/app/components/tools/setting/build-in/config-credentials.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { Collection } from '../../types' import { noop } from 'lodash-es' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Drawer from '@/app/components/base/drawer-plus' diff --git a/web/app/components/tools/workflow-tool/configure-button.tsx b/web/app/components/tools/workflow-tool/configure-button.tsx index 0d28576de5..f142989ff6 100644 --- a/web/app/components/tools/workflow-tool/configure-button.tsx +++ b/web/app/components/tools/workflow-tool/configure-button.tsx @@ -4,7 +4,8 @@ import type { InputVar, Variable } from '@/app/components/workflow/types' import type { PublishWorkflowParams } from '@/types/workflow' import { RiArrowRightUpLine, RiHammerLine } from '@remixicon/react' import { useRouter } from 'next/navigation' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Loading from '@/app/components/base/loading' diff --git a/web/app/components/tools/workflow-tool/confirm-modal/index.spec.tsx b/web/app/components/tools/workflow-tool/confirm-modal/index.spec.tsx index 972ac8c882..a03860d952 100644 --- a/web/app/components/tools/workflow-tool/confirm-modal/index.spec.tsx +++ b/web/app/components/tools/workflow-tool/confirm-modal/index.spec.tsx @@ -1,6 +1,6 @@ import { act, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import ConfirmModal from './index' // Test utilities diff --git a/web/app/components/tools/workflow-tool/index.tsx b/web/app/components/tools/workflow-tool/index.tsx index af226e7071..8804a4128d 100644 --- a/web/app/components/tools/workflow-tool/index.tsx +++ b/web/app/components/tools/workflow-tool/index.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { Emoji, WorkflowToolProviderOutputParameter, WorkflowToolProviderParameter, WorkflowToolProviderRequest } from '../types' import { RiErrorWarningLine } from '@remixicon/react' import { produce } from 'immer' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import Button from '@/app/components/base/button' diff --git a/web/app/components/workflow-app/components/workflow-onboarding-modal/index.spec.tsx b/web/app/components/workflow-app/components/workflow-onboarding-modal/index.spec.tsx index b7a2cefd1c..6dac82a642 100644 --- a/web/app/components/workflow-app/components/workflow-onboarding-modal/index.spec.tsx +++ b/web/app/components/workflow-app/components/workflow-onboarding-modal/index.spec.tsx @@ -1,6 +1,6 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import { BlockEnum } from '@/app/components/workflow/types' import WorkflowOnboardingModal from './index' diff --git a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-option.spec.tsx b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-option.spec.tsx index 0fa5c9f13d..9c77ebfdfe 100644 --- a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-option.spec.tsx +++ b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-option.spec.tsx @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import StartNodeOption from './start-node-option' describe('StartNodeOption', () => { diff --git a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.spec.tsx b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.spec.tsx index 8c59ec3b82..43d8c1a8e1 100644 --- a/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.spec.tsx +++ b/web/app/components/workflow-app/components/workflow-onboarding-modal/start-node-selection-panel.spec.tsx @@ -1,6 +1,6 @@ import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import React from 'react' +import * as React from 'react' import { BlockEnum } from '@/app/components/workflow/types' import StartNodeSelectionPanel from './start-node-selection-panel' diff --git a/web/app/components/workflow/__tests__/trigger-status-sync.test.tsx b/web/app/components/workflow/__tests__/trigger-status-sync.test.tsx index 6bf7e8f542..d3c3d235fe 100644 --- a/web/app/components/workflow/__tests__/trigger-status-sync.test.tsx +++ b/web/app/components/workflow/__tests__/trigger-status-sync.test.tsx @@ -2,7 +2,8 @@ import type { MockedFunction } from 'vitest' import type { EntryNodeStatus } from '../store/trigger-status' import type { BlockEnum } from '../types' import { act, render } from '@testing-library/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTriggerStatusStore } from '../store/trigger-status' import { isTriggerNode } from '../types' diff --git a/web/app/components/workflow/block-selector/market-place-plugin/action.tsx b/web/app/components/workflow/block-selector/market-place-plugin/action.tsx index a23ca32b50..bb98f1043f 100644 --- a/web/app/components/workflow/block-selector/market-place-plugin/action.tsx +++ b/web/app/components/workflow/block-selector/market-place-plugin/action.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import { RiMoreFill } from '@remixicon/react' import { useQueryClient } from '@tanstack/react-query' import { useTheme } from 'next-themes' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' // import Button from '@/app/components/base/button' diff --git a/web/app/components/workflow/block-selector/market-place-plugin/item.tsx b/web/app/components/workflow/block-selector/market-place-plugin/item.tsx index 18d8629260..545eaca41f 100644 --- a/web/app/components/workflow/block-selector/market-place-plugin/item.tsx +++ b/web/app/components/workflow/block-selector/market-place-plugin/item.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { Plugin } from '@/app/components/plugins/types.ts' import { useBoolean } from 'ahooks' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' diff --git a/web/app/components/workflow/block-selector/rag-tool-recommendations/index.tsx b/web/app/components/workflow/block-selector/rag-tool-recommendations/index.tsx index e9255917aa..f090fc8e7c 100644 --- a/web/app/components/workflow/block-selector/rag-tool-recommendations/index.tsx +++ b/web/app/components/workflow/block-selector/rag-tool-recommendations/index.tsx @@ -4,7 +4,8 @@ import type { ViewType } from '@/app/components/workflow/block-selector/view-typ import type { OnSelectBlock } from '@/app/components/workflow/types' import { RiMoreLine } from '@remixicon/react' import Link from 'next/link' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/arrows' import Loading from '@/app/components/base/loading' diff --git a/web/app/components/workflow/block-selector/rag-tool-recommendations/uninstalled-item.tsx b/web/app/components/workflow/block-selector/rag-tool-recommendations/uninstalled-item.tsx index 9a351c4eff..125b307ae0 100644 --- a/web/app/components/workflow/block-selector/rag-tool-recommendations/uninstalled-item.tsx +++ b/web/app/components/workflow/block-selector/rag-tool-recommendations/uninstalled-item.tsx @@ -1,7 +1,7 @@ 'use client' import type { Plugin } from '@/app/components/plugins/types' import { useBoolean } from 'ahooks' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace' diff --git a/web/app/components/workflow/block-selector/tool-picker.tsx b/web/app/components/workflow/block-selector/tool-picker.tsx index cbb8d5a01e..235144ae78 100644 --- a/web/app/components/workflow/block-selector/tool-picker.tsx +++ b/web/app/components/workflow/block-selector/tool-picker.tsx @@ -8,7 +8,8 @@ import type { ToolDefaultValue, ToolValue } from './types' import type { CustomCollectionBackend } from '@/app/components/tools/types' import type { BlockEnum, OnSelectBlock } from '@/app/components/workflow/types' import { useBoolean } from 'ahooks' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { PortalToFollowElem, diff --git a/web/app/components/workflow/block-selector/tool/action-item.tsx b/web/app/components/workflow/block-selector/tool/action-item.tsx index 60fac5e701..6c1c2465ed 100644 --- a/web/app/components/workflow/block-selector/tool/action-item.tsx +++ b/web/app/components/workflow/block-selector/tool/action-item.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { ToolWithProvider } from '../../types' import type { ToolDefaultValue } from '../types' import type { Tool } from '@/app/components/tools/types' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { trackEvent } from '@/app/components/base/amplitude' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx b/web/app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx index 54eb050e06..a911ea23c3 100644 --- a/web/app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx +++ b/web/app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { BlockEnum, ToolWithProvider } from '../../../types' import type { ToolDefaultValue, ToolValue } from '../../types' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { ViewType } from '../../view-type-select' import Tool from '../tool' diff --git a/web/app/components/workflow/block-selector/tool/tool-list-tree-view/item.tsx b/web/app/components/workflow/block-selector/tool/tool-list-tree-view/item.tsx index 64a376e394..308baa45e7 100644 --- a/web/app/components/workflow/block-selector/tool/tool-list-tree-view/item.tsx +++ b/web/app/components/workflow/block-selector/tool/tool-list-tree-view/item.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { BlockEnum, ToolWithProvider } from '../../../types' import type { ToolDefaultValue, ToolValue } from '../../types' -import React from 'react' +import * as React from 'react' import { ViewType } from '../../view-type-select' import Tool from '../tool' diff --git a/web/app/components/workflow/block-selector/tool/tool-list-tree-view/list.tsx b/web/app/components/workflow/block-selector/tool/tool-list-tree-view/list.tsx index 0f790ab036..2b85121e4d 100644 --- a/web/app/components/workflow/block-selector/tool/tool-list-tree-view/list.tsx +++ b/web/app/components/workflow/block-selector/tool/tool-list-tree-view/list.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { BlockEnum, ToolWithProvider } from '../../../types' import type { ToolDefaultValue, ToolValue } from '../../types' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { AGENT_GROUP_NAME, CUSTOM_GROUP_NAME, WORKFLOW_GROUP_NAME } from '../../index-bar' import Item from './item' diff --git a/web/app/components/workflow/block-selector/tool/tool.tsx b/web/app/components/workflow/block-selector/tool/tool.tsx index 366059b311..337742365e 100644 --- a/web/app/components/workflow/block-selector/tool/tool.tsx +++ b/web/app/components/workflow/block-selector/tool/tool.tsx @@ -5,7 +5,8 @@ import type { ToolWithProvider } from '../../types' import type { ToolDefaultValue, ToolValue } from '../types' import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react' import { useHover } from 'ahooks' -import React, { useCallback, useEffect, useMemo, useRef } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import { Mcp } from '@/app/components/base/icons/src/vender/other' import { useGetLanguage } from '@/context/i18n' diff --git a/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx b/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx index c0edec474a..92ee677362 100644 --- a/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx +++ b/web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { TriggerDefaultValue, TriggerWithProvider } from '../types' import type { Event } from '@/app/components/tools/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import { useGetLanguage } from '@/context/i18n' diff --git a/web/app/components/workflow/block-selector/trigger-plugin/item.tsx b/web/app/components/workflow/block-selector/trigger-plugin/item.tsx index a4de7b30dd..9e3ec77790 100644 --- a/web/app/components/workflow/block-selector/trigger-plugin/item.tsx +++ b/web/app/components/workflow/block-selector/trigger-plugin/item.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { TriggerDefaultValue, TriggerWithProvider } from '@/app/components/workflow/block-selector/types' import { RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react' -import React, { useEffect, useMemo, useRef } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import { CollectionType } from '@/app/components/tools/types' import BlockIcon from '@/app/components/workflow/block-icon' diff --git a/web/app/components/workflow/block-selector/use-sticky-scroll.ts b/web/app/components/workflow/block-selector/use-sticky-scroll.ts index 67ea70d94e..4960eea74f 100644 --- a/web/app/components/workflow/block-selector/use-sticky-scroll.ts +++ b/web/app/components/workflow/block-selector/use-sticky-scroll.ts @@ -1,5 +1,5 @@ import { useThrottleFn } from 'ahooks' -import React from 'react' +import * as React from 'react' export enum ScrollPosition { belowTheWrap = 'belowTheWrap', diff --git a/web/app/components/workflow/block-selector/view-type-select.tsx b/web/app/components/workflow/block-selector/view-type-select.tsx index a4830d8e81..c81d09c6dd 100644 --- a/web/app/components/workflow/block-selector/view-type-select.tsx +++ b/web/app/components/workflow/block-selector/view-type-select.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { RiNodeTree, RiSortAlphabetAsc } from '@remixicon/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { cn } from '@/utils/classnames' export enum ViewType { diff --git a/web/app/components/workflow/dsl-export-confirm-modal.tsx b/web/app/components/workflow/dsl-export-confirm-modal.tsx index 63100876c6..b616ec5fb5 100644 --- a/web/app/components/workflow/dsl-export-confirm-modal.tsx +++ b/web/app/components/workflow/dsl-export-confirm-modal.tsx @@ -2,7 +2,8 @@ import type { EnvironmentVariable } from '@/app/components/workflow/types' import { RiCloseLine, RiLock2Line } from '@remixicon/react' import { noop } from 'lodash-es' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Checkbox from '@/app/components/base/checkbox' diff --git a/web/app/components/workflow/header/run-mode.tsx b/web/app/components/workflow/header/run-mode.tsx index 82e33b5c30..21195d489a 100644 --- a/web/app/components/workflow/header/run-mode.tsx +++ b/web/app/components/workflow/header/run-mode.tsx @@ -1,6 +1,7 @@ import type { TestRunMenuRef, TriggerOption } from './test-run-menu' import { RiLoader2Line, RiPlayLargeLine } from '@remixicon/react' -import React, { useCallback, useEffect, useRef } from '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' diff --git a/web/app/components/workflow/header/version-history-button.tsx b/web/app/components/workflow/header/version-history-button.tsx index b29608a022..3be0cfbe32 100644 --- a/web/app/components/workflow/header/version-history-button.tsx +++ b/web/app/components/workflow/header/version-history-button.tsx @@ -1,7 +1,8 @@ import type { FC } from 'react' import { RiHistoryLine } from '@remixicon/react' import { useKeyPress } from 'ahooks' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import useTheme from '@/hooks/use-theme' import { cn } from '@/utils/classnames' 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 99ccc61fe5..95a0a963fe 100644 --- a/web/app/components/workflow/nodes/_base/components/add-button.tsx +++ b/web/app/components/workflow/nodes/_base/components/add-button.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import { RiAddLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import Button from '@/app/components/base/button' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/bool-input.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/bool-input.tsx index db32627dc2..6e71e1a356 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/bool-input.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/bool-input.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Checkbox from '@/app/components/base/checkbox' diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx index c33deae438..5cc68c6048 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx @@ -6,7 +6,8 @@ import { RiDeleteBinLine, } from '@remixicon/react' import { produce } from 'immer' -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader' import { Line3 } from '@/app/components/base/icons/src/public/common' diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx index e45f001924..0f695bb884 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { InputVar } from '../../../../types' import { produce } from 'immer' -import React, { useCallback, useEffect, useMemo, useRef } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useRef } from 'react' import AddButton from '@/app/components/base/button/add-button' import { RETRIEVAL_OUTPUT_STRUCT } from '@/app/components/workflow/constants' import { InputVarType } from '@/app/components/workflow/types' 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 9957d79cf6..f5870cede1 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 @@ -4,7 +4,8 @@ import type { Props as FormProps } from './form' import type { Emoji } from '@/app/components/tools/types' import type { SpecialResultPanelProps } from '@/app/components/workflow/run/special-result-panel' import type { BlockEnum, NodeRunningStatus } from '@/app/components/workflow/types' -import React, { useEffect, useRef } from 'react' +import * as React from 'react' +import { useEffect, useRef } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { getProcessedFiles } from '@/app/components/base/file-uploader/utils' diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/panel-wrap.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/panel-wrap.tsx index 61e614bb9e..4d05194314 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/panel-wrap.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/panel-wrap.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import { RiCloseLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' const i18nPrefix = 'workflow.singleRun' diff --git a/web/app/components/workflow/nodes/_base/components/code-generator-button.tsx b/web/app/components/workflow/nodes/_base/components/code-generator-button.tsx index 6888bff96c..96b9fe7a84 100644 --- a/web/app/components/workflow/nodes/_base/components/code-generator-button.tsx +++ b/web/app/components/workflow/nodes/_base/components/code-generator-button.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { CodeLanguage } from '../../code/types' import type { GenRes } from '@/service/debug' import { useBoolean } from 'ahooks' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { GetCodeGeneratorResModal } from '@/app/components/app/configuration/config/code-generator/get-code-generator-res' import { ActionButton } from '@/app/components/base/action-button' import { Generator } from '@/app/components/base/icons/src/vender/other' 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 3c2cc217a7..f5f47c4012 100644 --- a/web/app/components/workflow/nodes/_base/components/config-vision.tsx +++ b/web/app/components/workflow/nodes/_base/components/config-vision.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { ValueSelector, Var, VisionSetting } from '@/app/components/workflow/types' import { produce } from 'immer' -import React, { useCallback } from 'react' +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' diff --git a/web/app/components/workflow/nodes/_base/components/editor/base.tsx b/web/app/components/workflow/nodes/_base/components/editor/base.tsx index 95aabc0ec0..3d5ca5f78a 100644 --- a/web/app/components/workflow/nodes/_base/components/editor/base.tsx +++ b/web/app/components/workflow/nodes/_base/components/editor/base.tsx @@ -4,7 +4,8 @@ import type { CodeLanguage } from '../../../code/types' import type { FileEntity } from '@/app/components/base/file-uploader/types' import type { Node, NodeOutPutVar } from '@/app/components/workflow/types' import copy from 'copy-to-clipboard' -import React, { useCallback, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useRef, useState } from 'react' import PromptEditorHeightResizeWrap from '@/app/components/app/configuration/config-prompt/prompt-editor-height-resize-wrap' import ActionButton from '@/app/components/base/action-button' import FileListInLog from '@/app/components/base/file-uploader/file-list-in-log' diff --git a/web/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars.tsx b/web/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars.tsx index d4d43ae796..e5cb8f258a 100644 --- a/web/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars.tsx +++ b/web/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { Props as EditorProps } from '.' import type { NodeOutPutVar, Variable } from '@/app/components/workflow/types' import { useBoolean } from 'ahooks' -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx b/web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx index b98e9085de..fa7aef90a5 100644 --- a/web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import Editor, { loader } from '@monaco-editor/react' import { noop } from 'lodash-es' -import React, { useEffect, useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { getFilesInLogs, } from '@/app/components/base/file-uploader/utils' diff --git a/web/app/components/workflow/nodes/_base/components/editor/text-editor.tsx b/web/app/components/workflow/nodes/_base/components/editor/text-editor.tsx index 6fa3c2adfd..888c1c7017 100644 --- a/web/app/components/workflow/nodes/_base/components/editor/text-editor.tsx +++ b/web/app/components/workflow/nodes/_base/components/editor/text-editor.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { useBoolean } from 'ahooks' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import Base from './base' type Props = { diff --git a/web/app/components/workflow/nodes/_base/components/editor/wrap.tsx b/web/app/components/workflow/nodes/_base/components/editor/wrap.tsx index 700f5a4317..4ebedfe596 100644 --- a/web/app/components/workflow/nodes/_base/components/editor/wrap.tsx +++ b/web/app/components/workflow/nodes/_base/components/editor/wrap.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useStore } from '@/app/components/workflow/store' type Props = { diff --git a/web/app/components/workflow/nodes/_base/components/field.tsx b/web/app/components/workflow/nodes/_base/components/field.tsx index 9f46546700..93fd331f94 100644 --- a/web/app/components/workflow/nodes/_base/components/field.tsx +++ b/web/app/components/workflow/nodes/_base/components/field.tsx @@ -4,7 +4,7 @@ import { RiArrowDownSLine, } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React from 'react' +import * as React from 'react' import Tooltip from '@/app/components/base/tooltip' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/nodes/_base/components/file-type-item.tsx b/web/app/components/workflow/nodes/_base/components/file-type-item.tsx index 3dc1e7b132..b354d35276 100644 --- a/web/app/components/workflow/nodes/_base/components/file-type-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/file-type-item.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { noop } from 'lodash-es' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Checkbox from '@/app/components/base/checkbox' import { FileTypeIcon } from '@/app/components/base/file-uploader' diff --git a/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx index c6330daf4d..bcbb9b6373 100644 --- a/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx +++ b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { UploadFileSetting } from '../../../types' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Field from '@/app/components/app/configuration/config-var/config-modal/field' import { useFileSizeLimit } from '@/app/components/base/file-uploader/hooks' diff --git a/web/app/components/workflow/nodes/_base/components/info-panel.tsx b/web/app/components/workflow/nodes/_base/components/info-panel.tsx index cc2426c24f..885adab0fd 100644 --- a/web/app/components/workflow/nodes/_base/components/info-panel.tsx +++ b/web/app/components/workflow/nodes/_base/components/info-panel.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC, ReactNode } from 'react' -import React from 'react' +import * as React from 'react' type Props = { title: string 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 0a021402ba..cb0dc9064c 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,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import Slider from '@/app/components/base/slider' export type InputNumberWithSliderProps = { diff --git a/web/app/components/workflow/nodes/_base/components/input-support-select-var.tsx b/web/app/components/workflow/nodes/_base/components/input-support-select-var.tsx index c06fe55375..348bb23302 100644 --- a/web/app/components/workflow/nodes/_base/components/input-support-select-var.tsx +++ b/web/app/components/workflow/nodes/_base/components/input-support-select-var.tsx @@ -6,7 +6,8 @@ import type { } from '@/app/components/workflow/types' import { useBoolean } from 'ahooks' import { noop } from 'lodash-es' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import PromptEditor from '@/app/components/base/prompt-editor' diff --git a/web/app/components/workflow/nodes/_base/components/input-var-type-icon.tsx b/web/app/components/workflow/nodes/_base/components/input-var-type-icon.tsx index 7e529013cb..586149d727 100644 --- a/web/app/components/workflow/nodes/_base/components/input-var-type-icon.tsx +++ b/web/app/components/workflow/nodes/_base/components/input-var-type-icon.tsx @@ -10,7 +10,7 @@ import { RiHashtag, RiTextSnippet, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { InputVarType } from '../../../types' type Props = { diff --git a/web/app/components/workflow/nodes/_base/components/list-no-data-placeholder.tsx b/web/app/components/workflow/nodes/_base/components/list-no-data-placeholder.tsx index 98e2dc4f29..c3a30a3ac3 100644 --- a/web/app/components/workflow/nodes/_base/components/list-no-data-placeholder.tsx +++ b/web/app/components/workflow/nodes/_base/components/list-no-data-placeholder.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' type Props = { children: React.ReactNode diff --git a/web/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip.tsx b/web/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip.tsx index 33da12239b..49a8f1e406 100644 --- a/web/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip.tsx +++ b/web/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiAlertFill } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' 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 1272f132a6..70dfdde71f 100644 --- a/web/app/components/workflow/nodes/_base/components/memory-config.tsx +++ b/web/app/components/workflow/nodes/_base/components/memory-config.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { Memory } from '../../../types' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import Slider from '@/app/components/base/slider' diff --git a/web/app/components/workflow/nodes/_base/components/option-card.tsx b/web/app/components/workflow/nodes/_base/components/option-card.tsx index 7c3a06daeb..d9deb2ab7b 100644 --- a/web/app/components/workflow/nodes/_base/components/option-card.tsx +++ b/web/app/components/workflow/nodes/_base/components/option-card.tsx @@ -2,7 +2,8 @@ import type { VariantProps } from 'class-variance-authority' import type { FC } from 'react' import { cva } from 'class-variance-authority' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import Tooltip from '@/app/components/base/tooltip' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/nodes/_base/components/output-vars.tsx b/web/app/components/workflow/nodes/_base/components/output-vars.tsx index 2c646148b3..7ec18032ae 100644 --- a/web/app/components/workflow/nodes/_base/components/output-vars.tsx +++ b/web/app/components/workflow/nodes/_base/components/output-vars.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC, ReactNode } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx b/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx index c2bc6481ff..c0dbb181e2 100644 --- a/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx +++ b/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx @@ -11,7 +11,8 @@ import { } from '@remixicon/react' import { useBoolean } from 'ahooks' import copy from 'copy-to-clipboard' -import React, { useCallback, useRef } from 'react' +import * as React from 'react' +import { useCallback, useRef } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' diff --git a/web/app/components/workflow/nodes/_base/components/readonly-input-with-select-var.tsx b/web/app/components/workflow/nodes/_base/components/readonly-input-with-select-var.tsx index fbec61d516..b9a980ae01 100644 --- a/web/app/components/workflow/nodes/_base/components/readonly-input-with-select-var.tsx +++ b/web/app/components/workflow/nodes/_base/components/readonly-input-with-select-var.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { VariableLabelInText, } from '@/app/components/workflow/nodes/_base/components/variable/variable-label' diff --git a/web/app/components/workflow/nodes/_base/components/remove-button.tsx b/web/app/components/workflow/nodes/_base/components/remove-button.tsx index 7b77f956d3..962a3c1828 100644 --- a/web/app/components/workflow/nodes/_base/components/remove-button.tsx +++ b/web/app/components/workflow/nodes/_base/components/remove-button.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiDeleteBinLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import ActionButton from '@/app/components/base/action-button' type Props = { diff --git a/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx b/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx index bf3fd86865..02f61ad178 100644 --- a/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx +++ b/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Confirm from '@/app/components/base/confirm' diff --git a/web/app/components/workflow/nodes/_base/components/selector.tsx b/web/app/components/workflow/nodes/_base/components/selector.tsx index 58b1ecfa31..60498a4a6a 100644 --- a/web/app/components/workflow/nodes/_base/components/selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/selector.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { useBoolean, useClickAway } from 'ahooks' -import React from 'react' +import * as React from 'react' import { ChevronSelectorVertical } from '@/app/components/base/icons/src/vender/line/arrows' import { Check } from '@/app/components/base/icons/src/vender/line/general' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/nodes/_base/components/split.tsx b/web/app/components/workflow/nodes/_base/components/split.tsx index fa5ea3adc1..5cb5153c95 100644 --- a/web/app/components/workflow/nodes/_base/components/split.tsx +++ b/web/app/components/workflow/nodes/_base/components/split.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type Props = { diff --git a/web/app/components/workflow/nodes/_base/components/support-var-input/index.tsx b/web/app/components/workflow/nodes/_base/components/support-var-input/index.tsx index d3753bb5ff..377e1d8bc5 100644 --- a/web/app/components/workflow/nodes/_base/components/support-var-input/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/support-var-input/index.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import VarHighlight from '@/app/components/app/configuration/base/var-highlight' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/nodes/_base/components/toggle-expand-btn.tsx b/web/app/components/workflow/nodes/_base/components/toggle-expand-btn.tsx index 116825ae95..73b221a912 100644 --- a/web/app/components/workflow/nodes/_base/components/toggle-expand-btn.tsx +++ b/web/app/components/workflow/nodes/_base/components/toggle-expand-btn.tsx @@ -4,7 +4,8 @@ import { RiCollapseDiagonalLine, RiExpandDiagonalLine, } from '@remixicon/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import ActionButton from '@/app/components/base/action-button' type Props = { diff --git a/web/app/components/workflow/nodes/_base/components/variable/assigned-var-reference-popup.tsx b/web/app/components/workflow/nodes/_base/components/variable/assigned-var-reference-popup.tsx index 7907c83fc3..9e3aba9bf0 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/assigned-var-reference-popup.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/assigned-var-reference-popup.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ListEmpty from '@/app/components/base/list-empty' import VarReferenceVars from './var-reference-vars' diff --git a/web/app/components/workflow/nodes/_base/components/variable/constant-field.tsx b/web/app/components/workflow/nodes/_base/components/variable/constant-field.tsx index 310e82ff12..f0d7a04294 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/constant-field.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/constant-field.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { CredentialFormSchema, CredentialFormSchemaNumberInput, CredentialFormSchemaSelect } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { Var } from '@/app/components/workflow/types' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { SimpleSelect } from '@/app/components/base/select' 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/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/field.tsx b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/field.tsx index f533c33108..18d84db571 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/field.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/field.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import type { Field as FieldType } from '../../../../../llm/types' import type { ValueSelector } from '@/app/components/workflow/types' import { RiMoreFill } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/index.tsx b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/index.tsx index baf3cfcbd2..b29b1a6992 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker/index.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { StructuredOutput } from '../../../../../llm/types' import type { ValueSelector } from '@/app/components/workflow/types' import { useHover } from 'ahooks' -import React, { useRef } from 'react' +import * as React from 'react' +import { useRef } from 'react' import { cn } from '@/utils/classnames' import Field from './field' diff --git a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/field.tsx b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/field.tsx index fc23cda205..d028bd2c16 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/field.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/field.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import type { Field as FieldType } from '../../../../../llm/types' import { RiArrowDropDownLine } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import { Type } from '../../../../../llm/types' diff --git a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/index.tsx b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/index.tsx index deaab09e6c..c0049a54e8 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { StructuredOutput } from '../../../../../llm/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Field from './field' diff --git a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/tree-indent-line.tsx b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/tree-indent-line.tsx index 9e45a4cccc..786875ddd1 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/tree-indent-line.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/tree-indent-line.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type Props = { diff --git a/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx b/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx index 7eccbe23de..44df18ddf2 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx @@ -5,7 +5,8 @@ import type { ToastHandle } from '@/app/components/base/toast' import type { VarType } from '@/app/components/workflow/types' import { useDebounceFn } from 'ahooks' import { produce } from 'immer' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import Toast from '@/app/components/base/toast' diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-full-path-panel.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-full-path-panel.tsx index 744567daae..b2c63be8b6 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-full-path-panel.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-full-path-panel.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { Field, StructuredOutput, TypeWithArray } from '../../../llm/types' -import React from 'react' +import * as React from 'react' import BlockIcon from '@/app/components/workflow/block-icon' import { PickerPanelMain as Panel } from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/picker' import { BlockEnum } from '@/app/components/workflow/types' diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx index c9cad91236..2d96baaf28 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx @@ -5,7 +5,8 @@ import type { ValueSelector, Var, Variable } from '@/app/components/workflow/typ import { RiDraggable } from '@remixicon/react' import { useDebounceFn } from 'ahooks' import { produce } from 'immer' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { ReactSortable } from 'react-sortablejs' import { v4 as uuid4 } from 'uuid' diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx index 3692cb6413..05e2c913ce 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx @@ -13,7 +13,8 @@ import { } from '@remixicon/react' import { produce } from 'immer' import { noop } from 'lodash-es' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useNodes, diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx index 45ad5d9f8c..22ea741174 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-popup.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import ListEmpty from '@/app/components/base/list-empty' import { useStore } from '@/app/components/workflow/store' diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx index 5482eea996..dd0dfa8682 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx @@ -5,7 +5,8 @@ import type { Field } from '@/app/components/workflow/nodes/llm/types' import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types' import { useHover } from 'ahooks' import { noop } from 'lodash-es' -import React, { useEffect, useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows' import { CodeAssistant, MagicEdit } from '@/app/components/base/icons/src/vender/line/general' diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-type-picker.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-type-picker.tsx index b6b08bc799..3af95587cb 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-type-picker.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-type-picker.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { RiArrowDownSLine } from '@remixicon/react' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { Check } from '@/app/components/base/icons/src/vender/line/general' import { PortalToFollowElem, diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx index 3624e10bb1..8e684afa87 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx @@ -7,7 +7,8 @@ import { RiPlayLargeLine, } from '@remixicon/react' import { debounce } from 'lodash-es' -import React, { +import * as React from 'react' +import { cloneElement, memo, useCallback, diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx index 93d5debb51..31a7e3b9fd 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { ResultPanelProps } from '@/app/components/workflow/run/result-panel' import type { NodeTracing } from '@/types/workflow' import { RiLoader2Line } from '@remixicon/react' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useHooksStore } from '@/app/components/workflow/hooks-store' import ResultPanel from '@/app/components/workflow/run/result-panel' import { NodeRunningStatus } from '@/app/components/workflow/types' diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/no-data.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/no-data.tsx index 4ae6ccd31f..11422bf858 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/no-data.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/no-data.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiPlayLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time' diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/tab.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/tab.tsx index 53ae913a8e..7878f0d476 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/tab.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/tab.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import TabHeader from '@/app/components/base/tab-header' diff --git a/web/app/components/workflow/nodes/answer/node.tsx b/web/app/components/workflow/nodes/answer/node.tsx index 12c7b10a1a..80fd07a900 100644 --- a/web/app/components/workflow/nodes/answer/node.tsx +++ b/web/app/components/workflow/nodes/answer/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { AnswerNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import InfoPanel from '../_base/components/info-panel' import ReadonlyInputWithSelectVar from '../_base/components/readonly-input-with-select-var' diff --git a/web/app/components/workflow/nodes/answer/panel.tsx b/web/app/components/workflow/nodes/answer/panel.tsx index 170cd17bf8..8d539b9216 100644 --- a/web/app/components/workflow/nodes/answer/panel.tsx +++ b/web/app/components/workflow/nodes/answer/panel.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { AnswerNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' diff --git a/web/app/components/workflow/nodes/assigner/components/var-list/index.tsx b/web/app/components/workflow/nodes/assigner/components/var-list/index.tsx index 3f99121835..422cd5a486 100644 --- a/web/app/components/workflow/nodes/assigner/components/var-list/index.tsx +++ b/web/app/components/workflow/nodes/assigner/components/var-list/index.tsx @@ -5,7 +5,8 @@ import type { ValueSelector, Var } from '@/app/components/workflow/types' import { RiDeleteBinLine } from '@remixicon/react' import { produce } from 'immer' import { noop } from 'lodash-es' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Input from '@/app/components/base/input' diff --git a/web/app/components/workflow/nodes/assigner/node.tsx b/web/app/components/workflow/nodes/assigner/node.tsx index c7777c4541..be30104242 100644 --- a/web/app/components/workflow/nodes/assigner/node.tsx +++ b/web/app/components/workflow/nodes/assigner/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { AssignerNodeType } from './types' import type { Node, NodeProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useNodes } from 'reactflow' import Badge from '@/app/components/base/badge' diff --git a/web/app/components/workflow/nodes/assigner/panel.tsx b/web/app/components/workflow/nodes/assigner/panel.tsx index 04da330fd4..b680ba2631 100644 --- a/web/app/components/workflow/nodes/assigner/panel.tsx +++ b/web/app/components/workflow/nodes/assigner/panel.tsx @@ -4,7 +4,7 @@ import type { NodePanelProps } from '@/app/components/workflow/types' import { RiAddLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import VarList from './components/var-list' diff --git a/web/app/components/workflow/nodes/code/dependency-picker.tsx b/web/app/components/workflow/nodes/code/dependency-picker.tsx index 2e0e0c0d59..1c30ce0818 100644 --- a/web/app/components/workflow/nodes/code/dependency-picker.tsx +++ b/web/app/components/workflow/nodes/code/dependency-picker.tsx @@ -4,7 +4,8 @@ import { RiArrowDownSLine, } from '@remixicon/react' import { t } from 'i18next' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { Check } from '@/app/components/base/icons/src/vender/line/general' import Input from '@/app/components/base/input' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' diff --git a/web/app/components/workflow/nodes/code/node.tsx b/web/app/components/workflow/nodes/code/node.tsx index 5fa002913d..66e83dbb71 100644 --- a/web/app/components/workflow/nodes/code/node.tsx +++ b/web/app/components/workflow/nodes/code/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { CodeNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' const Node: FC<NodeProps<CodeNodeType>> = () => { return ( diff --git a/web/app/components/workflow/nodes/code/panel.tsx b/web/app/components/workflow/nodes/code/panel.tsx index 261195c4c5..62c2f55834 100644 --- a/web/app/components/workflow/nodes/code/panel.tsx +++ b/web/app/components/workflow/nodes/code/panel.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { CodeNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import AddButton from '@/app/components/base/button/add-button' import SyncButton from '@/app/components/base/button/sync-button' diff --git a/web/app/components/workflow/nodes/data-source/before-run-form.tsx b/web/app/components/workflow/nodes/data-source/before-run-form.tsx index a091211fa5..172570c802 100644 --- a/web/app/components/workflow/nodes/data-source/before-run-form.tsx +++ b/web/app/components/workflow/nodes/data-source/before-run-form.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { CustomRunFormProps } from './types' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import LocalFile from '@/app/components/datasets/documents/create-from-pipeline/data-source/local-file' diff --git a/web/app/components/workflow/nodes/document-extractor/node.tsx b/web/app/components/workflow/nodes/document-extractor/node.tsx index c092cd353a..f1e61b8353 100644 --- a/web/app/components/workflow/nodes/document-extractor/node.tsx +++ b/web/app/components/workflow/nodes/document-extractor/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { DocExtractorNodeType } from './types' import type { Node, NodeProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useNodes } from 'reactflow' import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' diff --git a/web/app/components/workflow/nodes/document-extractor/panel.tsx b/web/app/components/workflow/nodes/document-extractor/panel.tsx index b7cfddea4b..87a6ab7a37 100644 --- a/web/app/components/workflow/nodes/document-extractor/panel.tsx +++ b/web/app/components/workflow/nodes/document-extractor/panel.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { DocExtractorNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import Field from '@/app/components/workflow/nodes/_base/components/field' diff --git a/web/app/components/workflow/nodes/end/node.tsx b/web/app/components/workflow/nodes/end/node.tsx index 26e7b7d022..f6bace7058 100644 --- a/web/app/components/workflow/nodes/end/node.tsx +++ b/web/app/components/workflow/nodes/end/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { EndNodeType } from './types' import type { NodeProps, Variable } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useIsChatMode, useWorkflow, diff --git a/web/app/components/workflow/nodes/end/panel.tsx b/web/app/components/workflow/nodes/end/panel.tsx index 3970dc8efe..b07df2ef92 100644 --- a/web/app/components/workflow/nodes/end/panel.tsx +++ b/web/app/components/workflow/nodes/end/panel.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { EndNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import AddButton from '@/app/components/base/button/add-button' import Field from '@/app/components/workflow/nodes/_base/components/field' diff --git a/web/app/components/workflow/nodes/http/components/api-input.tsx b/web/app/components/workflow/nodes/http/components/api-input.tsx index a72fc9fde0..eeb9128827 100644 --- a/web/app/components/workflow/nodes/http/components/api-input.tsx +++ b/web/app/components/workflow/nodes/http/components/api-input.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { Var } from '../../../types' import { RiArrowDownSLine } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/nodes/http/components/authorization/index.tsx b/web/app/components/workflow/nodes/http/components/authorization/index.tsx index 50505fd4c8..5293df5597 100644 --- a/web/app/components/workflow/nodes/http/components/authorization/index.tsx +++ b/web/app/components/workflow/nodes/http/components/authorization/index.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { Authorization as AuthorizationPayloadType } from '../../types' import type { Var } from '@/app/components/workflow/types' import { produce } from 'immer' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import BaseInput from '@/app/components/base/input' diff --git a/web/app/components/workflow/nodes/http/components/authorization/radio-group.tsx b/web/app/components/workflow/nodes/http/components/authorization/radio-group.tsx index 6edd325b18..48ad4066b9 100644 --- a/web/app/components/workflow/nodes/http/components/authorization/radio-group.tsx +++ b/web/app/components/workflow/nodes/http/components/authorization/radio-group.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { cn } from '@/utils/classnames' type Option = { diff --git a/web/app/components/workflow/nodes/http/components/curl-panel.tsx b/web/app/components/workflow/nodes/http/components/curl-panel.tsx index 2710fd3c5d..ef4ce45f38 100644 --- a/web/app/components/workflow/nodes/http/components/curl-panel.tsx +++ b/web/app/components/workflow/nodes/http/components/curl-panel.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { HttpNodeType } from '../types' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' diff --git a/web/app/components/workflow/nodes/http/components/edit-body/index.tsx b/web/app/components/workflow/nodes/http/components/edit-body/index.tsx index 1770d01ef5..c475f1234a 100644 --- a/web/app/components/workflow/nodes/http/components/edit-body/index.tsx +++ b/web/app/components/workflow/nodes/http/components/edit-body/index.tsx @@ -4,7 +4,8 @@ import type { Body, BodyPayload, KeyValue as KeyValueType } from '../../types' import type { ValueSelector, Var } from '@/app/components/workflow/types' import { produce } from 'immer' import { uniqueId } from 'lodash-es' -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import InputWithVar from '@/app/components/workflow/nodes/_base/components/prompt/editor' import { VarType } from '@/app/components/workflow/types' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/nodes/http/components/key-value/bulk-edit/index.tsx b/web/app/components/workflow/nodes/http/components/key-value/bulk-edit/index.tsx index ea43c726e2..2a5b9484f7 100644 --- a/web/app/components/workflow/nodes/http/components/key-value/bulk-edit/index.tsx +++ b/web/app/components/workflow/nodes/http/components/key-value/bulk-edit/index.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { LayoutGrid02 } from '@/app/components/base/icons/src/vender/line/layout' import TextEditor from '@/app/components/workflow/nodes/_base/components/editor/text-editor' diff --git a/web/app/components/workflow/nodes/http/components/key-value/index.tsx b/web/app/components/workflow/nodes/http/components/key-value/index.tsx index 0191cb0c7a..02ba7c641d 100644 --- a/web/app/components/workflow/nodes/http/components/key-value/index.tsx +++ b/web/app/components/workflow/nodes/http/components/key-value/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { KeyValue } from '../../types' -import React from 'react' +import * as React from 'react' import KeyValueEdit from './key-value-edit' type Props = { diff --git a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/index.tsx b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/index.tsx index 61d6292e06..cba9e82b37 100644 --- a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/index.tsx +++ b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/index.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { KeyValue } from '../../../types' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import KeyValueItem from './item' diff --git a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/input-item.tsx b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/input-item.tsx index 2f1857f7af..f463388dad 100644 --- a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/input-item.tsx +++ b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/input-item.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { Var } from '@/app/components/workflow/types' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button' diff --git a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx index 365367fd97..a03f09d18a 100644 --- a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx +++ b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { KeyValue } from '../../../types' import type { ValueSelector, Var } from '@/app/components/workflow/types' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { PortalSelect } from '@/app/components/base/select' import { VarType } from '@/app/components/workflow/types' diff --git a/web/app/components/workflow/nodes/http/components/timeout/index.tsx b/web/app/components/workflow/nodes/http/components/timeout/index.tsx index 11edfa8c93..0bac909f69 100644 --- a/web/app/components/workflow/nodes/http/components/timeout/index.tsx +++ b/web/app/components/workflow/nodes/http/components/timeout/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { Timeout as TimeoutPayloadType } from '../../types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse' diff --git a/web/app/components/workflow/nodes/http/node.tsx b/web/app/components/workflow/nodes/http/node.tsx index 332a28c44c..7ade3a691b 100644 --- a/web/app/components/workflow/nodes/http/node.tsx +++ b/web/app/components/workflow/nodes/http/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { HttpNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import ReadonlyInputWithSelectVar from '../_base/components/readonly-input-with-select-var' const Node: FC<NodeProps<HttpNodeType>> = ({ 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 b9f80cb9f0..cdcd7561db 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 @@ -8,7 +8,8 @@ import { RiDraggable, } from '@remixicon/react' import { noop } from 'lodash-es' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { ReactSortable } from 'react-sortablejs' import Button from '@/app/components/base/button' diff --git a/web/app/components/workflow/nodes/if-else/node.tsx b/web/app/components/workflow/nodes/if-else/node.tsx index 41a7ec5d46..83f61f1f8e 100644 --- a/web/app/components/workflow/nodes/if-else/node.tsx +++ b/web/app/components/workflow/nodes/if-else/node.tsx @@ -1,7 +1,8 @@ import type { FC } from 'react' import type { NodeProps } from 'reactflow' import type { Condition, IfElseNodeType } from './types' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { VarType } from '../../types' import { NodeSourceHandle } from '../_base/components/node-handle' diff --git a/web/app/components/workflow/nodes/iteration/panel.tsx b/web/app/components/workflow/nodes/iteration/panel.tsx index 5cffb356a2..404fb20b82 100644 --- a/web/app/components/workflow/nodes/iteration/panel.tsx +++ b/web/app/components/workflow/nodes/iteration/panel.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { IterationNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import Select from '@/app/components/base/select' diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/index.tsx b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/index.tsx index 82af5f8d2f..8f288364c8 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/index.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { AddChunks } from '@/app/components/base/icons/src/vender/knowledge' import { useDocLink } from '@/context/i18n' diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/line.tsx b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/line.tsx index a2a3835be6..c4eab5d370 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/line.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/instruction/line.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' type LineProps = { type?: 'vertical' | 'horizontal' diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/add-dataset.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/add-dataset.tsx index b51d085113..a513280dec 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/add-dataset.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/add-dataset.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { DataSet } from '@/models/datasets' import { useBoolean } from 'ahooks' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import SelectDataset from '@/app/components/app/configuration/dataset-config/select-dataset' import AddButton from '@/app/components/base/button/add-button' diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx index 440aa9189a..b3f2701524 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx @@ -6,7 +6,8 @@ import { RiEditLine, } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import SettingsModal from '@/app/components/app/configuration/dataset-config/settings-modal' import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx index a804ae8953..8554fdf3e3 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { DataSet } from '@/models/datasets' import { produce } from 'immer' -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useSelector as useAppContextSelector } from '@/context/app-context' import { hasEditPermissionForDataset } from '@/utils/permission' 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 ced1bfcdae..02ae01ba16 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,7 +5,8 @@ import type { MultipleRetrievalConfig, SingleRetrievalConfig } from '../types' import type { DataSet } from '@/models/datasets' import type { DatasetConfigs } from '@/models/debug' import { RiEqualizer2Line } from '@remixicon/react' -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import ConfigRetrievalContent from '@/app/components/app/configuration/dataset-config/params-config/config-content' import Button from '@/app/components/base/button' diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/node.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/node.tsx index 55715f2fb0..9f5fe1f31c 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/node.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/node.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { KnowledgeRetrievalNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' import type { DataSet } from '@/models/datasets' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import AppIcon from '@/app/components/base/app-icon' import { useDatasetsDetailStore } from '../../datasets-detail-store/store' diff --git a/web/app/components/workflow/nodes/list-operator/components/extract-input.tsx b/web/app/components/workflow/nodes/list-operator/components/extract-input.tsx index 7d4b472fd3..c9a6151d72 100644 --- a/web/app/components/workflow/nodes/list-operator/components/extract-input.tsx +++ b/web/app/components/workflow/nodes/list-operator/components/extract-input.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { Var } from '../../../types' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' diff --git a/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx b/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx index d77a0c3eb3..8dd817a5ad 100644 --- a/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx +++ b/web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { Condition } from '../types' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { SimpleSelect as Select } from '@/app/components/base/select' import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var' diff --git a/web/app/components/workflow/nodes/list-operator/components/limit-config.tsx b/web/app/components/workflow/nodes/list-operator/components/limit-config.tsx index f7356b58aa..1793deb293 100644 --- a/web/app/components/workflow/nodes/list-operator/components/limit-config.tsx +++ b/web/app/components/workflow/nodes/list-operator/components/limit-config.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { Limit } from '../types' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Switch from '@/app/components/base/switch' import Field from '@/app/components/workflow/nodes/_base/components/field' diff --git a/web/app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx b/web/app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx index ee8703ca6c..52669526c6 100644 --- a/web/app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx +++ b/web/app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { Item } from '@/app/components/base/select' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import { SimpleSelect as Select } from '@/app/components/base/select' diff --git a/web/app/components/workflow/nodes/list-operator/node.tsx b/web/app/components/workflow/nodes/list-operator/node.tsx index 4d02595fcf..29b79636dd 100644 --- a/web/app/components/workflow/nodes/list-operator/node.tsx +++ b/web/app/components/workflow/nodes/list-operator/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { ListFilterNodeType } from './types' import type { Node, NodeProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { useNodes } from 'reactflow' import { isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' diff --git a/web/app/components/workflow/nodes/list-operator/panel.tsx b/web/app/components/workflow/nodes/list-operator/panel.tsx index be1d79dcad..f9d279f966 100644 --- a/web/app/components/workflow/nodes/list-operator/panel.tsx +++ b/web/app/components/workflow/nodes/list-operator/panel.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { ListFilterNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Switch from '@/app/components/base/switch' import Field from '@/app/components/workflow/nodes/_base/components/field' diff --git a/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx b/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx index a712d6e408..776ad6804c 100644 --- a/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx +++ b/web/app/components/workflow/nodes/llm/components/config-prompt-item.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import type { ModelConfig, PromptItem, Variable } from '../../../types' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' diff --git a/web/app/components/workflow/nodes/llm/components/config-prompt.tsx b/web/app/components/workflow/nodes/llm/components/config-prompt.tsx index 856b88ac00..228156f009 100644 --- a/web/app/components/workflow/nodes/llm/components/config-prompt.tsx +++ b/web/app/components/workflow/nodes/llm/components/config-prompt.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { ModelConfig, PromptItem, ValueSelector, Var, Variable } from '../../../types' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { ReactSortable } from 'react-sortablejs' import { v4 as uuid4 } from 'uuid' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor.tsx index 72620ee233..0e1aac8a32 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/code-editor.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import { Editor } from '@monaco-editor/react' import { RiClipboardLine, RiIndentIncrease } from '@remixicon/react' import copy from 'copy-to-clipboard' -import React, { useCallback, useEffect, useMemo, useRef } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import useTheme from '@/hooks/use-theme' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/error-message.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/error-message.tsx index 5cb2a421d5..041894fee6 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/error-message.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/error-message.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import { RiErrorWarningFill } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type ErrorMessageProps = { diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx index b7a0a40f32..66ea3bfc59 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import type { SchemaRoot } from '../../types' -import React from 'react' +import * as React from 'react' import Modal from '../../../../../base/modal' import JsonSchemaConfig from './json-schema-config' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx index 0110756b47..b69ce186f9 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx @@ -1,6 +1,7 @@ import type { FC } from 'react' import { RiCloseLine } from '@remixicon/react' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx index c5eaea6efd..38c6539d89 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx @@ -1,7 +1,8 @@ import type { FC } from 'react' import type { SchemaRoot } from '../../types' import { RiBracesLine, RiCloseLine, RiExternalLinkLine, RiTimelineView } from '@remixicon/react' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/generated-result.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/generated-result.tsx index 0e7d8c8d0c..5921976c41 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/generated-result.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/generated-result.tsx @@ -1,7 +1,8 @@ import type { FC } from 'react' import type { SchemaRoot } from '../../../types' import { RiArrowLeftLine, RiCloseLine, RiSparklingLine } from '@remixicon/react' -import React, { useCallback, useMemo, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Loading from '@/app/components/base/loading' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx index a4da5b69e3..6a34925275 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { SchemaRoot } from '../../../types' import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { CompletionParams, Model } from '@/types/app' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { PortalToFollowElem, PortalToFollowElemContent, diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/prompt-editor.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/prompt-editor.tsx index 17641fdf37..b9e0938e9b 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/prompt-editor.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/prompt-editor.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { Model } from '@/types/app' import { RiCloseLine, RiSparklingFill } from '@remixicon/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Textarea from '@/app/components/base/textarea' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor.tsx index 54753f08b4..fc95b4a998 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/schema-editor.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import LargeDataAlert from '@/app/components/workflow/variable-inspect/large-data-alert' import { cn } from '@/utils/classnames' import CodeEditor from './code-editor' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/add-field.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/add-field.tsx index 54a3b6bb85..967481ac94 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/add-field.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/add-field.tsx @@ -1,5 +1,6 @@ import { RiAddCircleFill } from '@remixicon/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { useMittContext } from './context' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/card.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/card.tsx index 3510498835..56d7c7dc31 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/card.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/card.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' type CardProps = { diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/actions.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/actions.tsx index a612701adc..fae1d2ab5a 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/actions.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/actions.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import { RiAddCircleLine, RiDeleteBinLine, RiEditLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-actions.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-actions.tsx index ee60195fdb..1555f20a83 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-actions.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-actions.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import { useKeyPress } from 'ahooks' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { getKeyboardKeyCodeBySystem, getKeyboardKeyNameBySystem } from '@/app/components/workflow/utils' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-options.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-options.tsx index 28ea12d9a3..10e6e721a8 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-options.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-options.tsx @@ -1,5 +1,6 @@ import type { FC } from 'react' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import Textarea from '@/app/components/base/textarea' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/auto-width-input.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/auto-width-input.tsx index 2dfaa88260..4f471ec38c 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/auto-width-input.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/auto-width-input.tsx @@ -1,5 +1,6 @@ import type { FC } from 'react' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { cn } from '@/utils/classnames' type AutoWidthInputProps = { diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx index e3ae0d16ab..81899f6b69 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx @@ -3,7 +3,8 @@ import type { SchemaEnumType } from '../../../../types' import type { AdvancedOptionsType } from './advanced-options' import type { TypeItem } from './type-selector' import { useUnmount } from 'ahooks' -import React, { useCallback, useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import Divider from '@/app/components/base/divider' import { JSON_SCHEMA_MAX_DEPTH } from '@/config' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/required-switch.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/required-switch.tsx index b84bdd0775..7ee68d1bcb 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/required-switch.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/required-switch.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Switch from '@/app/components/base/switch' diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/schema-node.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/schema-node.tsx index 23cd1ee477..0fdf5b4349 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/schema-node.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/schema-node.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { Field } from '../../../types' import { RiArrowDropDownLine, RiArrowDropRightLine } from '@remixicon/react' import { useDebounceFn } from 'ahooks' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import Divider from '@/app/components/base/divider' import { JSON_SCHEMA_MAX_DEPTH } from '@/config' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx b/web/app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx index eb285ee389..da272b4b14 100644 --- a/web/app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx +++ b/web/app/components/workflow/nodes/llm/components/prompt-generator-btn.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { ModelConfig } from '@/app/components/workflow/types' import type { GenRes } from '@/service/debug' import { useBoolean } from 'ahooks' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import GetAutomaticResModal from '@/app/components/app/configuration/config/automatic/get-automatic-res' import { ActionButton } from '@/app/components/base/action-button' import { Generator } from '@/app/components/base/icons/src/vender/other' diff --git a/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx b/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx index 147981e398..6a20c89315 100644 --- a/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx +++ b/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Switch from '@/app/components/base/switch' import Field from '@/app/components/workflow/nodes/_base/components/field' diff --git a/web/app/components/workflow/nodes/llm/components/resolution-picker.tsx b/web/app/components/workflow/nodes/llm/components/resolution-picker.tsx index fe078ba6fa..e59c2764a0 100644 --- a/web/app/components/workflow/nodes/llm/components/resolution-picker.tsx +++ b/web/app/components/workflow/nodes/llm/components/resolution-picker.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card' import { Resolution } from '@/types/app' diff --git a/web/app/components/workflow/nodes/llm/components/structure-output.tsx b/web/app/components/workflow/nodes/llm/components/structure-output.tsx index b97d5e20b7..c4db2d6637 100644 --- a/web/app/components/workflow/nodes/llm/components/structure-output.tsx +++ b/web/app/components/workflow/nodes/llm/components/structure-output.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { SchemaRoot, StructuredOutput } from '../types' import { RiEditLine } from '@remixicon/react' import { useBoolean } from 'ahooks' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import ShowPanel from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show' diff --git a/web/app/components/workflow/nodes/llm/node.tsx b/web/app/components/workflow/nodes/llm/node.tsx index 9d44d49475..6a9574a10b 100644 --- a/web/app/components/workflow/nodes/llm/node.tsx +++ b/web/app/components/workflow/nodes/llm/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { LLMNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTextGenerationCurrentProviderAndModelAndModelList, } from '@/app/components/header/account-setting/model-provider-page/hooks' diff --git a/web/app/components/workflow/nodes/llm/panel.tsx b/web/app/components/workflow/nodes/llm/panel.tsx index 4044989a07..fd20b1a2bb 100644 --- a/web/app/components/workflow/nodes/llm/panel.tsx +++ b/web/app/components/workflow/nodes/llm/panel.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { LLMNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' import { RiAlertFill, RiQuestionLine } from '@remixicon/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import AddButton2 from '@/app/components/base/button/add-button' import Switch from '@/app/components/base/switch' diff --git a/web/app/components/workflow/nodes/loop/components/condition-wrap.tsx b/web/app/components/workflow/nodes/loop/components/condition-wrap.tsx index 00f44ab244..72dfd92d51 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-wrap.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-wrap.tsx @@ -5,7 +5,8 @@ import type { Condition, HandleAddCondition, HandleAddSubVariableCondition, Hand import { RiAddLine, } from '@remixicon/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { PortalSelect as Select } from '@/app/components/base/select' diff --git a/web/app/components/workflow/nodes/loop/panel.tsx b/web/app/components/workflow/nodes/loop/panel.tsx index 45fec4030f..036abda6de 100644 --- a/web/app/components/workflow/nodes/loop/panel.tsx +++ b/web/app/components/workflow/nodes/loop/panel.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { LoopNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' import { RiAddLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Field from '@/app/components/workflow/nodes/_base/components/field' import { LOOP_NODE_MAX_COUNT } from '@/config' diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/item.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/item.tsx index 6382b33154..317d2583e2 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/item.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/item.tsx @@ -5,7 +5,7 @@ import { RiDeleteBinLine, RiEditLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/list.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/list.tsx index 1cd1232983..94343dd5d7 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/list.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/list.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { Param } from '../../types' import type { MoreInfo } from '@/app/components/workflow/types' import { useBoolean } from 'ahooks' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import ListNoDataPlaceholder from '../../../_base/components/list-no-data-placeholder' import Item from './item' diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx index 3048a78118..288e486ea7 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { Param } from '../../types' import type { MoreInfo } from '@/app/components/workflow/types' import { useBoolean } from 'ahooks' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Field from '@/app/components/app/configuration/config-var/config-modal/field' import ConfigSelect from '@/app/components/app/configuration/config-var/config-select' diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/reasoning-mode-picker.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/reasoning-mode-picker.tsx index dc5354a21a..7990bcc361 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/components/reasoning-mode-picker.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/components/reasoning-mode-picker.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Field from '../../_base/components/field' import OptionCard from '../../_base/components/option-card' diff --git a/web/app/components/workflow/nodes/parameter-extractor/node.tsx b/web/app/components/workflow/nodes/parameter-extractor/node.tsx index 014706810f..9e02d657ff 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/node.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { ParameterExtractorNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTextGenerationCurrentProviderAndModelAndModelList, } from '@/app/components/header/account-setting/model-provider-page/hooks' diff --git a/web/app/components/workflow/nodes/parameter-extractor/panel.tsx b/web/app/components/workflow/nodes/parameter-extractor/panel.tsx index 563c124102..9603da5869 100644 --- a/web/app/components/workflow/nodes/parameter-extractor/panel.tsx +++ b/web/app/components/workflow/nodes/parameter-extractor/panel.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { ParameterExtractorNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' diff --git a/web/app/components/workflow/nodes/question-classifier/components/advanced-setting.tsx b/web/app/components/workflow/nodes/question-classifier/components/advanced-setting.tsx index 336bd3463a..0a6b3dbbfb 100644 --- a/web/app/components/workflow/nodes/question-classifier/components/advanced-setting.tsx +++ b/web/app/components/workflow/nodes/question-classifier/components/advanced-setting.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import type { Memory, Node, NodeOutPutVar } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' diff --git a/web/app/components/workflow/nodes/question-classifier/components/class-item.tsx b/web/app/components/workflow/nodes/question-classifier/components/class-item.tsx index eb629a857c..2af2f8036a 100644 --- a/web/app/components/workflow/nodes/question-classifier/components/class-item.tsx +++ b/web/app/components/workflow/nodes/question-classifier/components/class-item.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { Topic } from '../types' import type { ValueSelector, Var } from '@/app/components/workflow/types' import { uniqueId } from 'lodash-es' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor' import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' diff --git a/web/app/components/workflow/nodes/question-classifier/components/class-list.tsx b/web/app/components/workflow/nodes/question-classifier/components/class-list.tsx index 387d60d671..8e61f918a5 100644 --- a/web/app/components/workflow/nodes/question-classifier/components/class-list.tsx +++ b/web/app/components/workflow/nodes/question-classifier/components/class-list.tsx @@ -5,7 +5,8 @@ import type { ValueSelector, Var } from '@/app/components/workflow/types' import { RiDraggable } from '@remixicon/react' import { produce } from 'immer' import { noop } from 'lodash-es' -import React, { useCallback, useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { ReactSortable } from 'react-sortablejs' import { ArrowDownRoundFill } from '@/app/components/base/icons/src/vender/solid/general' diff --git a/web/app/components/workflow/nodes/question-classifier/node.tsx b/web/app/components/workflow/nodes/question-classifier/node.tsx index e0a330e110..e00eee9d41 100644 --- a/web/app/components/workflow/nodes/question-classifier/node.tsx +++ b/web/app/components/workflow/nodes/question-classifier/node.tsx @@ -2,7 +2,7 @@ import type { TFunction } from 'i18next' import type { FC } from 'react' import type { NodeProps } from 'reactflow' import type { QuestionClassifierNodeType } from './types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' import { diff --git a/web/app/components/workflow/nodes/question-classifier/panel.tsx b/web/app/components/workflow/nodes/question-classifier/panel.tsx index 9496f90915..05b93c98b9 100644 --- a/web/app/components/workflow/nodes/question-classifier/panel.tsx +++ b/web/app/components/workflow/nodes/question-classifier/panel.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { QuestionClassifierNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse' diff --git a/web/app/components/workflow/nodes/start/components/var-item.tsx b/web/app/components/workflow/nodes/start/components/var-item.tsx index 317a733d9b..a506c51e31 100644 --- a/web/app/components/workflow/nodes/start/components/var-item.tsx +++ b/web/app/components/workflow/nodes/start/components/var-item.tsx @@ -6,7 +6,8 @@ import { } from '@remixicon/react' import { useBoolean, useHover } from 'ahooks' import { noop } from 'lodash-es' -import React, { useCallback, useRef } from 'react' +import * as React from 'react' +import { useCallback, useRef } from 'react' import { useTranslation } from 'react-i18next' import ConfigVarModal from '@/app/components/app/configuration/config-var/config-modal' import Badge from '@/app/components/base/badge' diff --git a/web/app/components/workflow/nodes/start/components/var-list.tsx b/web/app/components/workflow/nodes/start/components/var-list.tsx index 5ae45c7192..bda45ca5dd 100644 --- a/web/app/components/workflow/nodes/start/components/var-list.tsx +++ b/web/app/components/workflow/nodes/start/components/var-list.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { InputVar, MoreInfo } from '@/app/components/workflow/types' import { RiDraggable } from '@remixicon/react' import { produce } from 'immer' -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { ReactSortable } from 'react-sortablejs' import Toast from '@/app/components/base/toast' diff --git a/web/app/components/workflow/nodes/start/node.tsx b/web/app/components/workflow/nodes/start/node.tsx index e8642ff616..cc772dfc6a 100644 --- a/web/app/components/workflow/nodes/start/node.tsx +++ b/web/app/components/workflow/nodes/start/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { StartNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development' import InputVarTypeIcon from '../_base/components/input-var-type-icon' diff --git a/web/app/components/workflow/nodes/start/panel.tsx b/web/app/components/workflow/nodes/start/panel.tsx index 5871ab1852..b29ece7266 100644 --- a/web/app/components/workflow/nodes/start/panel.tsx +++ b/web/app/components/workflow/nodes/start/panel.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { StartNodeType } from './types' import type { InputVar, NodePanelProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ConfigVarModal from '@/app/components/app/configuration/config-var/config-modal' import AddButton from '@/app/components/base/button/add-button' diff --git a/web/app/components/workflow/nodes/template-transform/node.tsx b/web/app/components/workflow/nodes/template-transform/node.tsx index 3a4c5c3319..4485d66258 100644 --- a/web/app/components/workflow/nodes/template-transform/node.tsx +++ b/web/app/components/workflow/nodes/template-transform/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { TemplateTransformNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' const Node: FC<NodeProps<TemplateTransformNodeType>> = () => { return ( diff --git a/web/app/components/workflow/nodes/template-transform/panel.tsx b/web/app/components/workflow/nodes/template-transform/panel.tsx index c8fc293329..7e2c39247c 100644 --- a/web/app/components/workflow/nodes/template-transform/panel.tsx +++ b/web/app/components/workflow/nodes/template-transform/panel.tsx @@ -4,7 +4,7 @@ import type { NodePanelProps } from '@/app/components/workflow/types' import { RiQuestionLine, } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import AddButton from '@/app/components/base/button/add-button' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars' diff --git a/web/app/components/workflow/nodes/tool/components/copy-id.tsx b/web/app/components/workflow/nodes/tool/components/copy-id.tsx index 39ffd7edd1..8e53970749 100644 --- a/web/app/components/workflow/nodes/tool/components/copy-id.tsx +++ b/web/app/components/workflow/nodes/tool/components/copy-id.tsx @@ -2,7 +2,8 @@ import { RiFileCopyLine } from '@remixicon/react' import copy from 'copy-to-clipboard' import { debounce } from 'lodash-es' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/workflow/nodes/tool/components/input-var-list.tsx b/web/app/components/workflow/nodes/tool/components/input-var-list.tsx index 4f5d5f24bc..8b1bd46eeb 100644 --- a/web/app/components/workflow/nodes/tool/components/input-var-list.tsx +++ b/web/app/components/workflow/nodes/tool/components/input-var-list.tsx @@ -6,7 +6,8 @@ import type { Tool } from '@/app/components/tools/types' import type { ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types' import { produce } from 'immer' import { noop } from 'lodash-es' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' 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/workflow/nodes/tool/node.tsx b/web/app/components/workflow/nodes/tool/node.tsx index e2bcd26bd2..0cf4f0ff58 100644 --- a/web/app/components/workflow/nodes/tool/node.tsx +++ b/web/app/components/workflow/nodes/tool/node.tsx @@ -1,7 +1,8 @@ import type { FC } from 'react' import type { ToolNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useNodeDataUpdate } from '@/app/components/workflow/hooks/use-node-data-update' import { useNodePluginInstallation } from '@/app/components/workflow/hooks/use-node-plugin-installation' diff --git a/web/app/components/workflow/nodes/tool/panel.tsx b/web/app/components/workflow/nodes/tool/panel.tsx index 3e1b778a7a..559d42fd9f 100644 --- a/web/app/components/workflow/nodes/tool/panel.tsx +++ b/web/app/components/workflow/nodes/tool/panel.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { ToolNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' import Field from '@/app/components/workflow/nodes/_base/components/field' diff --git a/web/app/components/workflow/nodes/trigger-plugin/node.tsx b/web/app/components/workflow/nodes/trigger-plugin/node.tsx index da4dc83d34..94f7d0a314 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/node.tsx +++ b/web/app/components/workflow/nodes/trigger-plugin/node.tsx @@ -1,7 +1,8 @@ import type { FC } from 'react' import type { PluginTriggerNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' -import React, { useEffect, useMemo } from 'react' +import * as React from 'react' +import { useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' import NodeStatus, { NodeStatusEnum } from '@/app/components/base/node-status' import { useNodeDataUpdate } from '@/app/components/workflow/hooks/use-node-data-update' diff --git a/web/app/components/workflow/nodes/trigger-plugin/panel.tsx b/web/app/components/workflow/nodes/trigger-plugin/panel.tsx index ffa3a5503c..a74639faf5 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/panel.tsx +++ b/web/app/components/workflow/nodes/trigger-plugin/panel.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { PluginTriggerNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' import Split from '@/app/components/workflow/nodes/_base/components/split' import StructureOutputItem from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show' diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/frequency-selector.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/frequency-selector.tsx index b4f62de436..c257949ca2 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/frequency-selector.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/frequency-selector.tsx @@ -1,5 +1,6 @@ import type { ScheduleFrequency } from '../types' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { SimpleSelect } from '@/app/components/base/select' diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx index 724fedc7b2..7c1f4e8f9d 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/mode-switcher.tsx @@ -1,6 +1,6 @@ import type { ScheduleMode } from '../types' import { RiCalendarLine, RiCodeLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { SegmentedControl } from '@/app/components/base/segmented-control' diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/mode-toggle.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/mode-toggle.tsx index 35ffaff939..583c92ccaf 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/mode-toggle.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/mode-toggle.tsx @@ -1,5 +1,5 @@ import type { ScheduleMode } from '../types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { Asterisk, CalendarCheckLine } from '@/app/components/base/icons/src/vender/workflow' diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/monthly-days-selector.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/monthly-days-selector.tsx index e5a50522e1..16399bea27 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/monthly-days-selector.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/monthly-days-selector.tsx @@ -1,5 +1,5 @@ import { RiQuestionLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Tooltip from '@/app/components/base/tooltip' diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/next-execution-times.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/next-execution-times.tsx index c84bca483c..fe246f1c67 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/next-execution-times.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/next-execution-times.tsx @@ -1,5 +1,5 @@ import type { ScheduleTriggerNodeType } from '../types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { getFormattedExecutionTimes } from '../utils/execution-time-calculator' diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/on-minute-selector.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/on-minute-selector.tsx index 992a111d19..f9eef3691d 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/on-minute-selector.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/on-minute-selector.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Slider from '@/app/components/base/slider' diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/weekday-selector.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/weekday-selector.tsx index 348fd53454..9255a6b485 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/components/weekday-selector.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/components/weekday-selector.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' type WeekdaySelectorProps = { diff --git a/web/app/components/workflow/nodes/trigger-schedule/node.tsx b/web/app/components/workflow/nodes/trigger-schedule/node.tsx index 45e9b2afdb..6e9226b0be 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/node.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { ScheduleTriggerNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { getNextExecutionTime } from './utils/execution-time-calculator' diff --git a/web/app/components/workflow/nodes/trigger-schedule/panel.tsx b/web/app/components/workflow/nodes/trigger-schedule/panel.tsx index 8daedc50a9..b4ca1860b5 100644 --- a/web/app/components/workflow/nodes/trigger-schedule/panel.tsx +++ b/web/app/components/workflow/nodes/trigger-schedule/panel.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { ScheduleTriggerNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import TimePicker from '@/app/components/base/date-and-time-picker/time-picker' import Input from '@/app/components/base/input' diff --git a/web/app/components/workflow/nodes/trigger-webhook/components/generic-table.tsx b/web/app/components/workflow/nodes/trigger-webhook/components/generic-table.tsx index a6644d7312..d85b622e10 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/components/generic-table.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/components/generic-table.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC, ReactNode } from 'react' import { RiDeleteBinLine } from '@remixicon/react' -import React, { useCallback, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useMemo } from 'react' import Checkbox from '@/app/components/base/checkbox' import Input from '@/app/components/base/input' import { SimpleSelect } from '@/app/components/base/select' diff --git a/web/app/components/workflow/nodes/trigger-webhook/components/header-table.tsx b/web/app/components/workflow/nodes/trigger-webhook/components/header-table.tsx index da54cac16a..b681eec9b1 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/components/header-table.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/components/header-table.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import type { WebhookHeader } from '../types' import type { ColumnConfig, GenericTableRow } from './generic-table' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import GenericTable from './generic-table' diff --git a/web/app/components/workflow/nodes/trigger-webhook/components/paragraph-input.tsx b/web/app/components/workflow/nodes/trigger-webhook/components/paragraph-input.tsx index b26238fdd1..c49ce9cd5d 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/components/paragraph-input.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/components/paragraph-input.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useRef } from 'react' +import * as React from 'react' +import { useRef } from 'react' import { cn } from '@/utils/classnames' type ParagraphInputProps = { diff --git a/web/app/components/workflow/nodes/trigger-webhook/components/parameter-table.tsx b/web/app/components/workflow/nodes/trigger-webhook/components/parameter-table.tsx index 1fa038ff73..d4dc05f741 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/components/parameter-table.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/components/parameter-table.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { WebhookParameter } from '../types' import type { ColumnConfig, GenericTableRow } from './generic-table' -import React, { useMemo } from 'react' +import * as React from 'react' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { VarType } from '@/app/components/workflow/types' import { createParameterTypeOptions, normalizeParameterType } from '../utils/parameter-type-utils' diff --git a/web/app/components/workflow/nodes/trigger-webhook/node.tsx b/web/app/components/workflow/nodes/trigger-webhook/node.tsx index 77f42b6db2..2de1b30aee 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/node.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { WebhookTriggerNodeType } from './types' import type { NodeProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' const Node: FC<NodeProps<WebhookTriggerNodeType>> = ({ data, diff --git a/web/app/components/workflow/nodes/trigger-webhook/panel.tsx b/web/app/components/workflow/nodes/trigger-webhook/panel.tsx index efc541bbb3..e5773e1afd 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/panel.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/panel.tsx @@ -3,7 +3,8 @@ import type { HttpMethod, WebhookTriggerNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' import copy from 'copy-to-clipboard' -import React, { useEffect, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { InputNumber } from '@/app/components/base/input-number' import InputWithCopy from '@/app/components/base/input-with-copy' diff --git a/web/app/components/workflow/nodes/trigger-webhook/utils/render-output-vars.tsx b/web/app/components/workflow/nodes/trigger-webhook/utils/render-output-vars.tsx index 984ffc03dd..d58561b603 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/utils/render-output-vars.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/utils/render-output-vars.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import type { Variable } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' type OutputVariablesContentProps = { variables?: Variable[] diff --git a/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx b/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx index 277c44744b..8fb1cfba61 100644 --- a/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx @@ -7,7 +7,8 @@ import { } from '@remixicon/react' import { useBoolean } from 'ahooks' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { Folder } from '@/app/components/base/icons/src/vender/line/files' import Toast from '@/app/components/base/toast' diff --git a/web/app/components/workflow/nodes/variable-assigner/components/var-list/index.tsx b/web/app/components/workflow/nodes/variable-assigner/components/var-list/index.tsx index a767e704fe..19ead7ead1 100644 --- a/web/app/components/workflow/nodes/variable-assigner/components/var-list/index.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/components/var-list/index.tsx @@ -3,7 +3,8 @@ import type { FC } from 'react' import type { ValueSelector, Var } from '@/app/components/workflow/types' import { produce } from 'immer' import { noop } from 'lodash-es' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker' import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types' diff --git a/web/app/components/workflow/nodes/variable-assigner/panel.tsx b/web/app/components/workflow/nodes/variable-assigner/panel.tsx index 0a6c1c3c84..c7edffc933 100644 --- a/web/app/components/workflow/nodes/variable-assigner/panel.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/panel.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { VariableAssignerNodeType } from './types' import type { NodePanelProps } from '@/app/components/workflow/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Switch from '@/app/components/base/switch' import AddButton from '@/app/components/workflow/nodes/_base/components/add-button' diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/array-bool-list.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/array-bool-list.tsx index 3d22437830..e1ec55de12 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/array-bool-list.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/array-bool-list.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import { RiAddLine } from '@remixicon/react' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button' diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx index e1025eda6a..42a1d3f247 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import { RiAddLine } from '@remixicon/react' import { produce } from 'immer' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Input from '@/app/components/base/input' diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/bool-value.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/bool-value.tsx index 89b309db58..859a8bc728 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/bool-value.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/bool-value.tsx @@ -1,6 +1,7 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import OptionCard from '../../../nodes/_base/components/option-card' type Props = { diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx index 1132434122..33235e2423 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' import { produce } from 'immer' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { ToastContext } from '@/app/components/base/toast' diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/object-value-list.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/object-value-list.tsx index 0e39bfcfcc..0c2f8fc4f1 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/object-value-list.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/object-value-list.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import ObjectValueItem from '@/app/components/workflow/panel/chat-variable-panel/components/object-value-item' diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx index 1fe4e5fe5a..14cd1b3cf1 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx @@ -1,7 +1,7 @@ 'use client' import type { ConversationVariable } from '@/app/components/workflow/types' import { RiAddLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx index e30da0fff3..33e2e07376 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx @@ -1,6 +1,7 @@ import type { ConversationVariable } from '@/app/components/workflow/types' import { RiCloseLine, RiDraftLine, RiInputField } from '@remixicon/react' -import React, { useCallback, useEffect, useMemo } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { v4 as uuid4 } from 'uuid' diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx index 1922374941..69ef1366b9 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx @@ -1,6 +1,7 @@ 'use client' import { RiArrowDownSLine, RiCheckLine } from '@remixicon/react' -import React, { useState } from 'react' +import * as React from 'react' +import { useState } from 'react' import { PortalToFollowElem, PortalToFollowElemContent, diff --git a/web/app/components/workflow/panel/debug-and-preview/conversation-variable-modal.tsx b/web/app/components/workflow/panel/debug-and-preview/conversation-variable-modal.tsx index 117247901e..6e130180d0 100644 --- a/web/app/components/workflow/panel/debug-and-preview/conversation-variable-modal.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/conversation-variable-modal.tsx @@ -6,7 +6,8 @@ import { RiCloseLine } from '@remixicon/react' import { useMount } from 'ahooks' import copy from 'copy-to-clipboard' import { capitalize, noop } from 'lodash-es' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import { Copy, 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 6cf193fe96..e253d6c27c 100644 --- a/web/app/components/workflow/panel/env-panel/variable-modal.tsx +++ b/web/app/components/workflow/panel/env-panel/variable-modal.tsx @@ -1,6 +1,7 @@ import type { EnvironmentVariable } from '@/app/components/workflow/types' import { RiCloseLine } from '@remixicon/react' -import React, { useEffect } from 'react' +import * as React from 'react' +import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { v4 as uuid4 } from 'uuid' 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 30551562a7..448d6f1aa9 100644 --- a/web/app/components/workflow/panel/env-panel/variable-trigger.tsx +++ b/web/app/components/workflow/panel/env-panel/variable-trigger.tsx @@ -1,7 +1,7 @@ 'use client' import type { EnvironmentVariable } from '@/app/components/workflow/types' import { RiAddLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { 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 47dc68687d..225f4b08f8 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,6 +1,7 @@ import type { FC } from 'react' import { RiMoreFill } from '@remixicon/react' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import Button from '@/app/components/base/button' import Divider from '@/app/components/base/divider' import { 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 a307056148..938b8089ca 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,6 +1,6 @@ import type { FC } from 'react' import type { VersionHistoryContextMenuOptions } from '../../../types' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type MenuItemProps = { 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 3ad7d0dc8a..a879593698 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,6 +1,6 @@ import type { FC } from 'react' import type { VersionHistory } from '@/types/workflow' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' 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 c020c076ad..bc81fc7503 100644 --- a/web/app/components/workflow/panel/version-history-panel/empty.tsx +++ b/web/app/components/workflow/panel/version-history-panel/empty.tsx @@ -1,6 +1,6 @@ import type { FC } from 'react' import { RiHistoryLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' diff --git a/web/app/components/workflow/panel/version-history-panel/filter/filter-item.tsx b/web/app/components/workflow/panel/version-history-panel/filter/filter-item.tsx index c9a7c28112..d7a37caa5e 100644 --- a/web/app/components/workflow/panel/version-history-panel/filter/filter-item.tsx +++ b/web/app/components/workflow/panel/version-history-panel/filter/filter-item.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { WorkflowVersionFilterOptions } from '../../../types' import { RiCheckLine } from '@remixicon/react' -import React from 'react' +import * as React from 'react' type FilterItemProps = { item: { 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 6db331338b..d6d79f9a6a 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,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Switch from '@/app/components/base/switch' diff --git a/web/app/components/workflow/panel/version-history-panel/filter/index.tsx b/web/app/components/workflow/panel/version-history-panel/filter/index.tsx index 8def221926..83156be732 100644 --- a/web/app/components/workflow/panel/version-history-panel/filter/index.tsx +++ b/web/app/components/workflow/panel/version-history-panel/filter/index.tsx @@ -1,6 +1,7 @@ import type { FC } from 'react' import { RiFilter3Line } from '@remixicon/react' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import Divider from '@/app/components/base/divider' import { PortalToFollowElem, 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 0bdb608d94..06a27eb7c7 100644 --- a/web/app/components/workflow/panel/version-history-panel/index.tsx +++ b/web/app/components/workflow/panel/version-history-panel/index.tsx @@ -2,7 +2,8 @@ import type { VersionHistory } from '@/types/workflow' import { RiArrowDownDoubleLine, RiCloseLine, RiLoader2Line } from '@remixicon/react' import copy from 'copy-to-clipboard' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +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' diff --git a/web/app/components/workflow/panel/version-history-panel/loading/item.tsx b/web/app/components/workflow/panel/version-history-panel/loading/item.tsx index c17d725fb3..58d5cc3bd4 100644 --- a/web/app/components/workflow/panel/version-history-panel/loading/item.tsx +++ b/web/app/components/workflow/panel/version-history-panel/loading/item.tsx @@ -1,5 +1,5 @@ import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type ItemProps = { 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 09bc5d79b4..a8dfb8b21c 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,6 +1,6 @@ import type { FC } from 'react' import type { VersionHistory } from '@/types/workflow' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' diff --git a/web/app/components/workflow/panel/version-history-panel/version-history-item.tsx b/web/app/components/workflow/panel/version-history-panel/version-history-item.tsx index 7739d10af2..4818c371a7 100644 --- a/web/app/components/workflow/panel/version-history-panel/version-history-item.tsx +++ b/web/app/components/workflow/panel/version-history-panel/version-history-item.tsx @@ -1,7 +1,8 @@ import type { VersionHistoryContextMenuOptions } from '../../types' import type { VersionHistory } from '@/types/workflow' import dayjs from 'dayjs' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' import { WorkflowVersion } from '../../types' diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx index 1bff24e2cc..378daa7b19 100644 --- a/web/app/components/workflow/run/index.tsx +++ b/web/app/components/workflow/run/index.tsx @@ -2,7 +2,8 @@ import type { FC } from 'react' import type { WorkflowRunDetailResponse } from '@/models/log' import type { NodeTracing } from '@/types/workflow' -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +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 Loading from '@/app/components/base/loading' diff --git a/web/app/components/workflow/run/iteration-log/iteration-result-panel.tsx b/web/app/components/workflow/run/iteration-log/iteration-result-panel.tsx index 5933e897bd..12812aeef7 100644 --- a/web/app/components/workflow/run/iteration-log/iteration-result-panel.tsx +++ b/web/app/components/workflow/run/iteration-log/iteration-result-panel.tsx @@ -7,7 +7,8 @@ import { RiErrorWarningLine, RiLoader2Line, } from '@remixicon/react' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { Iteration } from '@/app/components/base/icons/src/vender/workflow' import TracingPanel from '@/app/components/workflow/run/tracing-panel' diff --git a/web/app/components/workflow/run/loop-log/loop-result-panel.tsx b/web/app/components/workflow/run/loop-log/loop-result-panel.tsx index 758148d8c1..219888a56f 100644 --- a/web/app/components/workflow/run/loop-log/loop-result-panel.tsx +++ b/web/app/components/workflow/run/loop-log/loop-result-panel.tsx @@ -7,7 +7,8 @@ import { RiErrorWarningLine, RiLoader2Line, } from '@remixicon/react' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { Loop } from '@/app/components/base/icons/src/vender/workflow' import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor' diff --git a/web/app/components/workflow/run/loop-result-panel.tsx b/web/app/components/workflow/run/loop-result-panel.tsx index 61ba6db115..8238be82f3 100644 --- a/web/app/components/workflow/run/loop-result-panel.tsx +++ b/web/app/components/workflow/run/loop-result-panel.tsx @@ -5,7 +5,8 @@ import { RiArrowRightSLine, RiCloseLine, } from '@remixicon/react' -import React, { useCallback, useState } from 'react' +import * as React from 'react' +import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { Loop } from '@/app/components/base/icons/src/vender/workflow' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/run/tracing-panel.tsx b/web/app/components/workflow/run/tracing-panel.tsx index 0e1d6578ab..8931c8f7fe 100644 --- a/web/app/components/workflow/run/tracing-panel.tsx +++ b/web/app/components/workflow/run/tracing-panel.tsx @@ -5,9 +5,8 @@ import { RiArrowDownSLine, RiMenu4Line, } from '@remixicon/react' -import -React, -{ +import * as React from 'react' +import { useCallback, useState, } from 'react' diff --git a/web/app/components/workflow/variable-inspect/display-content.tsx b/web/app/components/workflow/variable-inspect/display-content.tsx index 901c0fa6dc..ebeaa17c42 100644 --- a/web/app/components/workflow/variable-inspect/display-content.tsx +++ b/web/app/components/workflow/variable-inspect/display-content.tsx @@ -2,7 +2,8 @@ import type { VarType } from '../types' import type { ChunkInfo } from '@/app/components/rag-pipeline/components/chunk-card-list/types' import type { ParentMode } from '@/models/datasets' import { RiBracesLine, RiEyeLine } from '@remixicon/react' -import React, { useMemo, useState } from 'react' +import * as React from 'react' +import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { Markdown } from '@/app/components/base/markdown' import { SegmentedControl } from '@/app/components/base/segmented-control' diff --git a/web/app/components/workflow/variable-inspect/large-data-alert.tsx b/web/app/components/workflow/variable-inspect/large-data-alert.tsx index a2750c82e7..6ab3e65f41 100644 --- a/web/app/components/workflow/variable-inspect/large-data-alert.tsx +++ b/web/app/components/workflow/variable-inspect/large-data-alert.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { RiInformation2Fill } from '@remixicon/react' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import { cn } from '@/utils/classnames' diff --git a/web/app/components/workflow/variable-inspect/value-content.tsx b/web/app/components/workflow/variable-inspect/value-content.tsx index 6d6a04434a..1d4f1cfd13 100644 --- a/web/app/components/workflow/variable-inspect/value-content.tsx +++ b/web/app/components/workflow/variable-inspect/value-content.tsx @@ -1,6 +1,7 @@ import type { VarInInspect } from '@/types/workflow' import { useDebounceFn } from 'ahooks' -import React, { useEffect, useMemo, useRef, useState } from 'react' +import * as React from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader' import { getProcessedFiles, getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils' import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' diff --git a/web/app/components/workflow/workflow-preview/components/nodes/if-else/node.tsx b/web/app/components/workflow/workflow-preview/components/nodes/if-else/node.tsx index b0a9e6903f..bac19c579f 100644 --- a/web/app/components/workflow/workflow-preview/components/nodes/if-else/node.tsx +++ b/web/app/components/workflow/workflow-preview/components/nodes/if-else/node.tsx @@ -1,7 +1,8 @@ import type { FC } from 'react' import type { NodeProps } from 'reactflow' import type { Condition, IfElseNodeType } from '@/app/components/workflow/nodes/if-else/types' -import React, { useCallback } from 'react' +import * as React from 'react' +import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import ConditionFilesListValue from '@/app/components/workflow/nodes/if-else/components/condition-files-list-value' import ConditionValue from '@/app/components/workflow/nodes/if-else/components/condition-value' diff --git a/web/app/components/workflow/workflow-preview/components/nodes/question-classifier/node.tsx b/web/app/components/workflow/workflow-preview/components/nodes/question-classifier/node.tsx index c164d624e4..2511483b36 100644 --- a/web/app/components/workflow/workflow-preview/components/nodes/question-classifier/node.tsx +++ b/web/app/components/workflow/workflow-preview/components/nodes/question-classifier/node.tsx @@ -1,7 +1,7 @@ import type { FC } from 'react' import type { NodeProps } from 'reactflow' import type { QuestionClassifierNodeType } from '@/app/components/workflow/nodes/question-classifier/types' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import InfoPanel from '@/app/components/workflow/nodes/_base/components/info-panel' import { NodeSourceHandle } from '../../node-handle' diff --git a/web/app/education-apply/expire-notice-modal.tsx b/web/app/education-apply/expire-notice-modal.tsx index 51a3ba66b1..6755de1241 100644 --- a/web/app/education-apply/expire-notice-modal.tsx +++ b/web/app/education-apply/expire-notice-modal.tsx @@ -2,7 +2,7 @@ import { RiExternalLinkLine } from '@remixicon/react' import Link from 'next/link' import { useRouter } from 'next/navigation' -import React from 'react' +import * as React from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' diff --git a/web/app/education-apply/verify-state-modal.tsx b/web/app/education-apply/verify-state-modal.tsx index e4a5cd9bbe..5d4e89c92e 100644 --- a/web/app/education-apply/verify-state-modal.tsx +++ b/web/app/education-apply/verify-state-modal.tsx @@ -1,7 +1,8 @@ import { RiExternalLinkLine, } from '@remixicon/react' -import React, { useEffect, useRef, useState } from 'react' +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/button' diff --git a/web/app/forgot-password/ForgotPasswordForm.tsx b/web/app/forgot-password/ForgotPasswordForm.tsx index 43aa1006d6..2b50c1c452 100644 --- a/web/app/forgot-password/ForgotPasswordForm.tsx +++ b/web/app/forgot-password/ForgotPasswordForm.tsx @@ -4,7 +4,8 @@ import { zodResolver } from '@hookform/resolvers/zod' import { useRouter } from 'next/navigation' -import React, { useEffect, useState } from 'react' +import * as React from 'react' +import { useEffect, useState } from 'react' import { useForm } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { z } from 'zod' diff --git a/web/app/forgot-password/page.tsx b/web/app/forgot-password/page.tsx index 4c37e096ca..338f4eaf13 100644 --- a/web/app/forgot-password/page.tsx +++ b/web/app/forgot-password/page.tsx @@ -1,6 +1,6 @@ 'use client' import { useSearchParams } from 'next/navigation' -import React from 'react' +import * as React from 'react' import ChangePasswordForm from '@/app/forgot-password/ChangePasswordForm' import { useGlobalPublicStore } from '@/context/global-public-context' import useDocumentTitle from '@/hooks/use-document-title' diff --git a/web/app/init/page.tsx b/web/app/init/page.tsx index c61457f984..7c1d849bdd 100644 --- a/web/app/init/page.tsx +++ b/web/app/init/page.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' import InitPasswordPopup from './InitPasswordPopup' diff --git a/web/app/install/installForm.tsx b/web/app/install/installForm.tsx index c3d9c1dfa6..60de8e0501 100644 --- a/web/app/install/installForm.tsx +++ b/web/app/install/installForm.tsx @@ -7,7 +7,8 @@ import { useDebounceFn } from 'ahooks' import Link from 'next/link' import { useRouter } from 'next/navigation' -import React, { useCallback, useEffect } from 'react' +import * as React from 'react' +import { useCallback, useEffect } from 'react' import { useForm } from 'react-hook-form' import { useTranslation } from 'react-i18next' import { z } from 'zod' diff --git a/web/app/install/page.tsx b/web/app/install/page.tsx index b9a770405f..db30d5bc5a 100644 --- a/web/app/install/page.tsx +++ b/web/app/install/page.tsx @@ -1,5 +1,5 @@ 'use client' -import React from 'react' +import * as React from 'react' import { useGlobalPublicStore } from '@/context/global-public-context' import { cn } from '@/utils/classnames' import Header from '../signin/_header' diff --git a/web/app/signin/_header.tsx b/web/app/signin/_header.tsx index 5ef24cd03e..01135c2bf6 100644 --- a/web/app/signin/_header.tsx +++ b/web/app/signin/_header.tsx @@ -1,7 +1,7 @@ 'use client' import type { Locale } from '@/i18n-config' import dynamic from 'next/dynamic' -import React from 'react' +import * as React from 'react' import { useContext } from 'use-context-selector' import Divider from '@/app/components/base/divider' import LocaleSigninSelect from '@/app/components/base/select/locale-signin' diff --git a/web/app/signin/normal-form.tsx b/web/app/signin/normal-form.tsx index 6bc37e6dd3..a4e6e4607e 100644 --- a/web/app/signin/normal-form.tsx +++ b/web/app/signin/normal-form.tsx @@ -1,7 +1,8 @@ import { RiContractLine, RiDoorLockLine, RiErrorWarningFill } from '@remixicon/react' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' -import React, { useCallback, useEffect, useState } from 'react' +import * as React from 'react' +import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Toast from '@/app/components/base/toast' import { IS_CE_EDITION } from '@/config' diff --git a/web/app/signin/one-more-step.tsx b/web/app/signin/one-more-step.tsx index 80013e622f..76f707493b 100644 --- a/web/app/signin/one-more-step.tsx +++ b/web/app/signin/one-more-step.tsx @@ -2,7 +2,8 @@ import type { Reducer } from 'react' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' -import React, { useReducer } from 'react' +import * as React from 'react' +import { useReducer } from 'react' import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import { SimpleSelect } from '@/app/components/base/select' diff --git a/web/app/signin/split.tsx b/web/app/signin/split.tsx index b6e848357c..370f108421 100644 --- a/web/app/signin/split.tsx +++ b/web/app/signin/split.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { cn } from '@/utils/classnames' type Props = { diff --git a/web/context/external-api-panel-context.tsx b/web/context/external-api-panel-context.tsx index 05ae5c45c1..e50420c169 100644 --- a/web/context/external-api-panel-context.tsx +++ b/web/context/external-api-panel-context.tsx @@ -1,6 +1,7 @@ 'use client' -import React, { createContext, useContext, useState } from 'react' +import * as React from 'react' +import { createContext, useContext, useState } from 'react' type ExternalApiPanelContextType = { showExternalApiPanel: boolean diff --git a/web/context/modal-context.test.tsx b/web/context/modal-context.test.tsx index 07a82939a0..4f41c19df6 100644 --- a/web/context/modal-context.test.tsx +++ b/web/context/modal-context.test.tsx @@ -1,5 +1,5 @@ import { act, render, screen, waitFor } from '@testing-library/react' -import React from 'react' +import * as React from 'react' import { defaultPlan } from '@/app/components/billing/config' import { Plan } from '@/app/components/billing/type' import { ModalContextProvider } from '@/context/modal-context' diff --git a/web/context/provider-context-mock.tsx b/web/context/provider-context-mock.tsx index b42847a9ec..174affca0d 100644 --- a/web/context/provider-context-mock.tsx +++ b/web/context/provider-context-mock.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import * as React from 'react' import { useProviderContext } from '@/context/provider-context' const ProviderContextMock: FC = () => { diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index da425efb62..9cd4d7831e 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -11,6 +11,7 @@ export default antfu( 'react/no-context-provider': 'off', 'react/no-forward-ref': 'off', 'react/no-use-context': 'off', + 'react/prefer-namespace-import': 'error', }, }, nextjs: true, @@ -54,7 +55,6 @@ export default antfu( 'test/no-identical-title': 'warn', 'test/prefer-hooks-in-order': 'warn', 'ts/no-empty-object-type': 'warn', - 'ts/no-require-imports': 'warn', 'unicorn/prefer-number-properties': 'warn', 'unused-imports/no-unused-vars': 'warn', }, diff --git a/web/hooks/use-breakpoints.ts b/web/hooks/use-breakpoints.ts index 99c2b75d67..e0bd45c01c 100644 --- a/web/hooks/use-breakpoints.ts +++ b/web/hooks/use-breakpoints.ts @@ -1,5 +1,5 @@ 'use client' -import React from 'react' +import * as React from 'react' export enum MediaType { mobile = 'mobile', diff --git a/web/service/demo/index.tsx b/web/service/demo/index.tsx index d538d6fda2..afce18d468 100644 --- a/web/service/demo/index.tsx +++ b/web/service/demo/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { FC } from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import React from 'react' +import * as React from 'react' import Loading from '@/app/components/base/loading' import { AppModeEnum } from '@/types/app' import { createApp, updateAppApiStatus, updateAppModelConfig, updateAppRateLimit, updateAppSiteAccessToken, updateAppSiteConfig, updateAppSiteStatus } from '../apps' diff --git a/web/utils/context.spec.ts b/web/utils/context.spec.ts index b70a15639a..40f39dda6a 100644 --- a/web/utils/context.spec.ts +++ b/web/utils/context.spec.ts @@ -9,7 +9,7 @@ import { renderHook } from '@testing-library/react' * - createCtx: Standard React context using useContext/createContext * - createSelectorCtx: Context with selector support using use-context-selector library */ -import React from 'react' +import * as React from 'react' import { createCtx, createSelectorCtx } from './context' describe('Context Utilities', () => { From efac8766a124011f121e0ea8c92116871c5be918 Mon Sep 17 00:00:00 2001 From: -LAN- <laipz8200@outlook.com> Date: Tue, 23 Dec 2025 18:14:39 +0800 Subject: [PATCH 08/15] fix: YAML URL import rewrite for GitHub attachments (#30003) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- api/services/app_dsl_service.py | 1 + .../test_app_dsl_service_import_yaml_url.py | 71 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 api/tests/unit_tests/services/test_app_dsl_service_import_yaml_url.py diff --git a/api/services/app_dsl_service.py b/api/services/app_dsl_service.py index 1dd6faea5d..deba0b79e8 100644 --- a/api/services/app_dsl_service.py +++ b/api/services/app_dsl_service.py @@ -155,6 +155,7 @@ class AppDslService: parsed_url.scheme == "https" and parsed_url.netloc == "github.com" and parsed_url.path.endswith((".yml", ".yaml")) + and "/blob/" in parsed_url.path ): yaml_url = yaml_url.replace("https://github.com", "https://raw.githubusercontent.com") yaml_url = yaml_url.replace("/blob/", "/") diff --git a/api/tests/unit_tests/services/test_app_dsl_service_import_yaml_url.py b/api/tests/unit_tests/services/test_app_dsl_service_import_yaml_url.py new file mode 100644 index 0000000000..41c1d0ea2a --- /dev/null +++ b/api/tests/unit_tests/services/test_app_dsl_service_import_yaml_url.py @@ -0,0 +1,71 @@ +from unittest.mock import MagicMock + +import httpx + +from models import Account +from services import app_dsl_service +from services.app_dsl_service import AppDslService, ImportMode, ImportStatus + + +def _build_response(url: str, status_code: int, content: bytes = b"") -> httpx.Response: + request = httpx.Request("GET", url) + return httpx.Response(status_code=status_code, request=request, content=content) + + +def _pending_yaml_content(version: str = "99.0.0") -> bytes: + return (f'version: "{version}"\nkind: app\napp:\n name: Loop Test\n mode: workflow\n').encode() + + +def _account_mock() -> MagicMock: + account = MagicMock(spec=Account) + account.current_tenant_id = "tenant-1" + return account + + +def test_import_app_yaml_url_user_attachments_keeps_original_url(monkeypatch): + yaml_url = "https://github.com/user-attachments/files/24290802/loop-test.yml" + raw_url = "https://raw.githubusercontent.com/user-attachments/files/24290802/loop-test.yml" + yaml_bytes = _pending_yaml_content() + + def fake_get(url: str, **kwargs): + if url == raw_url: + return _build_response(url, status_code=404) + assert url == yaml_url + return _build_response(url, status_code=200, content=yaml_bytes) + + monkeypatch.setattr(app_dsl_service.ssrf_proxy, "get", fake_get) + + service = AppDslService(MagicMock()) + result = service.import_app( + account=_account_mock(), + import_mode=ImportMode.YAML_URL, + yaml_url=yaml_url, + ) + + assert result.status == ImportStatus.PENDING + assert result.imported_dsl_version == "99.0.0" + + +def test_import_app_yaml_url_github_blob_rewrites_to_raw(monkeypatch): + yaml_url = "https://github.com/acme/repo/blob/main/app.yml" + raw_url = "https://raw.githubusercontent.com/acme/repo/main/app.yml" + yaml_bytes = _pending_yaml_content() + + requested_urls: list[str] = [] + + def fake_get(url: str, **kwargs): + requested_urls.append(url) + assert url == raw_url + return _build_response(url, status_code=200, content=yaml_bytes) + + monkeypatch.setattr(app_dsl_service.ssrf_proxy, "get", fake_get) + + service = AppDslService(MagicMock()) + result = service.import_app( + account=_account_mock(), + import_mode=ImportMode.YAML_URL, + yaml_url=yaml_url, + ) + + assert result.status == ImportStatus.PENDING + assert requested_urls == [raw_url] From a3d4f4f3bdce880bed7ef67bb3b780955a4a11f2 Mon Sep 17 00:00:00 2001 From: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Date: Tue, 23 Dec 2025 18:26:02 +0800 Subject: [PATCH 09/15] chore: enable ts/no-explicit-any, remove no-unused-vars (#30042) --- web/eslint.config.mjs | 2 +- web/i18n-config/check-i18n.js | 18 ------------------ 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index 9cd4d7831e..b085ea3619 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -19,6 +19,7 @@ export default antfu( typescript: { overrides: { 'ts/consistent-type-definitions': ['error', 'type'], + 'ts/no-explicit-any': 'warn', }, }, test: { @@ -40,7 +41,6 @@ export default antfu( 'next/inline-script-id': 'warn', 'no-console': 'warn', 'no-irregular-whitespace': 'warn', - 'no-unused-vars': 'warn', 'node/prefer-global/buffer': 'warn', 'node/prefer-global/process': 'warn', 'react/no-create-ref': 'warn', diff --git a/web/i18n-config/check-i18n.js b/web/i18n-config/check-i18n.js index 096a4d7afc..d69885e6f0 100644 --- a/web/i18n-config/check-i18n.js +++ b/web/i18n-config/check-i18n.js @@ -184,24 +184,6 @@ async function getKeysFromLanguage(language) { }) } -function removeKeysFromObject(obj, keysToRemove, prefix = '') { - let modified = false - for (const key in obj) { - const fullKey = prefix ? `${prefix}.${key}` : key - - if (keysToRemove.includes(fullKey)) { - delete obj[key] - modified = true - console.log(`🗑️ Removed key: ${fullKey}`) - } - else if (typeof obj[key] === 'object' && obj[key] !== null) { - const subModified = removeKeysFromObject(obj[key], keysToRemove, fullKey) - modified = modified || subModified - } - } - return modified -} - async function removeExtraKeysFromFile(language, fileName, extraKeys) { const filePath = path.resolve(__dirname, '../i18n', language, `${fileName}.ts`) From b321511518a95efd68fedf33019a6cd9ec3d0231 Mon Sep 17 00:00:00 2001 From: wangxiaolei <fatelei@gmail.com> Date: Tue, 23 Dec 2025 18:56:38 +0800 Subject: [PATCH 10/15] feat: grace ful close the connection (#30039) --- api/core/mcp/client/sse_client.py | 11 +++ api/core/mcp/client/streamable_client.py | 113 ++++++++++++++++++----- 2 files changed, 102 insertions(+), 22 deletions(-) diff --git a/api/core/mcp/client/sse_client.py b/api/core/mcp/client/sse_client.py index 24ca59ee45..1de1d5a073 100644 --- a/api/core/mcp/client/sse_client.py +++ b/api/core/mcp/client/sse_client.py @@ -61,6 +61,7 @@ class SSETransport: self.timeout = timeout self.sse_read_timeout = sse_read_timeout self.endpoint_url: str | None = None + self.event_source: EventSource | None = None def _validate_endpoint_url(self, endpoint_url: str) -> bool: """Validate that the endpoint URL matches the connection origin. @@ -237,6 +238,9 @@ class SSETransport: write_queue: WriteQueue = queue.Queue() status_queue: StatusQueue = queue.Queue() + # Store event_source for graceful shutdown + self.event_source = event_source + # Start SSE reader thread executor.submit(self.sse_reader, event_source, read_queue, status_queue) @@ -296,6 +300,13 @@ def sse_client( logger.exception("Error connecting to SSE endpoint") raise finally: + # Close the SSE connection to unblock the reader thread + if transport.event_source is not None: + try: + transport.event_source.response.close() + except RuntimeError: + pass + # Clean up queues if read_queue: read_queue.put(None) diff --git a/api/core/mcp/client/streamable_client.py b/api/core/mcp/client/streamable_client.py index 805c16c838..f81e7cead8 100644 --- a/api/core/mcp/client/streamable_client.py +++ b/api/core/mcp/client/streamable_client.py @@ -8,6 +8,7 @@ and session management. import logging import queue +import threading from collections.abc import Callable, Generator from concurrent.futures import ThreadPoolExecutor from contextlib import contextmanager @@ -103,6 +104,9 @@ class StreamableHTTPTransport: CONTENT_TYPE: JSON, **self.headers, } + self.stop_event = threading.Event() + self._active_responses: list[httpx.Response] = [] + self._lock = threading.Lock() def _update_headers_with_session(self, base_headers: dict[str, str]) -> dict[str, str]: """Update headers with session ID if available.""" @@ -111,6 +115,30 @@ class StreamableHTTPTransport: headers[MCP_SESSION_ID] = self.session_id return headers + def _register_response(self, response: httpx.Response): + """Register a response for cleanup on shutdown.""" + with self._lock: + self._active_responses.append(response) + + def _unregister_response(self, response: httpx.Response): + """Unregister a response after it's closed.""" + with self._lock: + try: + self._active_responses.remove(response) + except ValueError as e: + logger.debug("Ignoring error during response unregister: %s", e) + + def close_active_responses(self): + """Close all active SSE connections to unblock threads.""" + with self._lock: + responses_to_close = list(self._active_responses) + self._active_responses.clear() + for response in responses_to_close: + try: + response.close() + except RuntimeError as e: + logger.debug("Ignoring error during active response close: %s", e) + def _is_initialization_request(self, message: JSONRPCMessage) -> bool: """Check if the message is an initialization request.""" return isinstance(message.root, JSONRPCRequest) and message.root.method == "initialize" @@ -195,11 +223,21 @@ class StreamableHTTPTransport: event_source.response.raise_for_status() logger.debug("GET SSE connection established") - for sse in event_source.iter_sse(): - self._handle_sse_event(sse, server_to_client_queue) + # Register response for cleanup + self._register_response(event_source.response) + + try: + for sse in event_source.iter_sse(): + if self.stop_event.is_set(): + logger.debug("GET stream received stop signal") + break + self._handle_sse_event(sse, server_to_client_queue) + finally: + self._unregister_response(event_source.response) except Exception as exc: - logger.debug("GET stream error (non-fatal): %s", exc) + if not self.stop_event.is_set(): + logger.debug("GET stream error (non-fatal): %s", exc) def _handle_resumption_request(self, ctx: RequestContext): """Handle a resumption request using GET with SSE.""" @@ -224,15 +262,24 @@ class StreamableHTTPTransport: event_source.response.raise_for_status() logger.debug("Resumption GET SSE connection established") - for sse in event_source.iter_sse(): - is_complete = self._handle_sse_event( - sse, - ctx.server_to_client_queue, - original_request_id, - ctx.metadata.on_resumption_token_update if ctx.metadata else None, - ) - if is_complete: - break + # Register response for cleanup + self._register_response(event_source.response) + + try: + for sse in event_source.iter_sse(): + if self.stop_event.is_set(): + logger.debug("Resumption stream received stop signal") + break + is_complete = self._handle_sse_event( + sse, + ctx.server_to_client_queue, + original_request_id, + ctx.metadata.on_resumption_token_update if ctx.metadata else None, + ) + if is_complete: + break + finally: + self._unregister_response(event_source.response) def _handle_post_request(self, ctx: RequestContext): """Handle a POST request with response processing.""" @@ -295,17 +342,27 @@ class StreamableHTTPTransport: def _handle_sse_response(self, response: httpx.Response, ctx: RequestContext): """Handle SSE response from the server.""" try: + # Register response for cleanup + self._register_response(response) + event_source = EventSource(response) - for sse in event_source.iter_sse(): - is_complete = self._handle_sse_event( - sse, - ctx.server_to_client_queue, - resumption_callback=(ctx.metadata.on_resumption_token_update if ctx.metadata else None), - ) - if is_complete: - break + try: + for sse in event_source.iter_sse(): + if self.stop_event.is_set(): + logger.debug("SSE response stream received stop signal") + break + is_complete = self._handle_sse_event( + sse, + ctx.server_to_client_queue, + resumption_callback=(ctx.metadata.on_resumption_token_update if ctx.metadata else None), + ) + if is_complete: + break + finally: + self._unregister_response(response) except Exception as e: - ctx.server_to_client_queue.put(e) + if not self.stop_event.is_set(): + ctx.server_to_client_queue.put(e) def _handle_unexpected_content_type( self, @@ -345,6 +402,11 @@ class StreamableHTTPTransport: """ while True: try: + # Check if we should stop + if self.stop_event.is_set(): + logger.debug("Post writer received stop signal") + break + # Read message from client queue with timeout to check stop_event periodically session_message = client_to_server_queue.get(timeout=DEFAULT_QUEUE_READ_TIMEOUT) if session_message is None: @@ -381,7 +443,8 @@ class StreamableHTTPTransport: except queue.Empty: continue except Exception as exc: - server_to_client_queue.put(exc) + if not self.stop_event.is_set(): + server_to_client_queue.put(exc) def terminate_session(self, client: httpx.Client): """Terminate the session by sending a DELETE request.""" @@ -465,6 +528,12 @@ def streamablehttp_client( transport.get_session_id, ) finally: + # Set stop event to signal all threads to stop + transport.stop_event.set() + + # Close all active SSE connections to unblock threads + transport.close_active_responses() + if transport.session_id and terminate_on_close: transport.terminate_session(client) From 3f27b3f0b49f29993677aa5a9da9942412ed5cdd Mon Sep 17 00:00:00 2001 From: ericko-being <erickokurniadi@beingcorp.co.jp> Date: Tue, 23 Dec 2025 20:00:17 +0900 Subject: [PATCH 11/15] fix(ops): correct LangSmith dotted_order timestamp format (#30022) --- api/core/ops/utils.py | 2 +- api/tests/unit_tests/core/ops/test_utils.py | 53 ++++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/api/core/ops/utils.py b/api/core/ops/utils.py index c00f785034..631e3b77b2 100644 --- a/api/core/ops/utils.py +++ b/api/core/ops/utils.py @@ -54,7 +54,7 @@ def generate_dotted_order(run_id: str, start_time: Union[str, datetime], parent_ generate dotted_order for langsmith """ start_time = datetime.fromisoformat(start_time) if isinstance(start_time, str) else start_time - timestamp = start_time.strftime("%Y%m%dT%H%M%S%f")[:-3] + "Z" + timestamp = start_time.strftime("%Y%m%dT%H%M%S%f") + "Z" current_segment = f"{timestamp}{run_id}" if parent_dotted_order is None: diff --git a/api/tests/unit_tests/core/ops/test_utils.py b/api/tests/unit_tests/core/ops/test_utils.py index 7cc2772acf..e1084001b7 100644 --- a/api/tests/unit_tests/core/ops/test_utils.py +++ b/api/tests/unit_tests/core/ops/test_utils.py @@ -1,6 +1,9 @@ +import re +from datetime import datetime + import pytest -from core.ops.utils import validate_project_name, validate_url, validate_url_with_path +from core.ops.utils import generate_dotted_order, validate_project_name, validate_url, validate_url_with_path class TestValidateUrl: @@ -136,3 +139,51 @@ class TestValidateProjectName: """Test custom default name""" result = validate_project_name("", "Custom Default") assert result == "Custom Default" + + +class TestGenerateDottedOrder: + """Test cases for generate_dotted_order function""" + + def test_dotted_order_has_6_digit_microseconds(self): + """Test that timestamp includes full 6-digit microseconds for LangSmith API compatibility. + + LangSmith API expects timestamps in format: YYYYMMDDTHHMMSSffffffZ (6-digit microseconds). + Previously, the code truncated to 3 digits which caused API errors: + 'cannot parse .111 as .000000' + """ + start_time = datetime(2025, 12, 23, 4, 19, 55, 111000) + run_id = "test-run-id" + result = generate_dotted_order(run_id, start_time) + + # Extract timestamp portion (before the run_id) + timestamp_match = re.match(r"^(\d{8}T\d{6})(\d+)Z", result) + assert timestamp_match is not None, "Timestamp format should match YYYYMMDDTHHMMSSffffffZ" + + microseconds = timestamp_match.group(2) + assert len(microseconds) == 6, f"Microseconds should be 6 digits, got {len(microseconds)}: {microseconds}" + + def test_dotted_order_format_matches_langsmith_expected(self): + """Test that dotted_order format matches LangSmith API expected format.""" + start_time = datetime(2025, 1, 15, 10, 30, 45, 123456) + run_id = "abc123" + result = generate_dotted_order(run_id, start_time) + + # LangSmith expects: YYYYMMDDTHHMMSSffffffZ followed by run_id + assert result == "20250115T103045123456Zabc123" + + def test_dotted_order_with_parent(self): + """Test dotted_order generation with parent order uses dot separator.""" + start_time = datetime(2025, 12, 23, 4, 19, 55, 111000) + run_id = "child-run-id" + parent_order = "20251223T041955000000Zparent-run-id" + result = generate_dotted_order(run_id, start_time, parent_order) + + assert result == "20251223T041955000000Zparent-run-id.20251223T041955111000Zchild-run-id" + + def test_dotted_order_without_parent_has_no_dot(self): + """Test dotted_order generation without parent has no dot separator.""" + start_time = datetime(2025, 12, 23, 4, 19, 55, 111000) + run_id = "test-run-id" + result = generate_dotted_order(run_id, start_time, None) + + assert "." not in result From aea3a6f80c816aa67bae59150a1e49daebbf5e0c Mon Sep 17 00:00:00 2001 From: wangxiaolei <fatelei@gmail.com> Date: Tue, 23 Dec 2025 19:01:12 +0800 Subject: [PATCH 12/15] =?UTF-8?q?fix:=20when=20use=20forward=20proxy=20wit?= =?UTF-8?q?h=20httpx,=20httpx=20will=20overwrite=20the=20use=20=E2=80=A6?= =?UTF-8?q?=20(#30029)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/core/helper/ssrf_proxy.py | 34 +++- .../unit_tests/core/helper/test_ssrf_proxy.py | 154 +++++++++++++++--- 2 files changed, 165 insertions(+), 23 deletions(-) diff --git a/api/core/helper/ssrf_proxy.py b/api/core/helper/ssrf_proxy.py index 6c98aea1be..f2172e4e2f 100644 --- a/api/core/helper/ssrf_proxy.py +++ b/api/core/helper/ssrf_proxy.py @@ -72,6 +72,22 @@ def _get_ssrf_client(ssl_verify_enabled: bool) -> httpx.Client: ) +def _get_user_provided_host_header(headers: dict | None) -> str | None: + """ + Extract the user-provided Host header from the headers dict. + + This is needed because when using a forward proxy, httpx may override the Host header. + We preserve the user's explicit Host header to support virtual hosting and other use cases. + """ + if not headers: + return None + # Case-insensitive lookup for Host header + for key, value in headers.items(): + if key.lower() == "host": + return value + return None + + def make_request(method, url, max_retries=SSRF_DEFAULT_MAX_RETRIES, **kwargs): if "allow_redirects" in kwargs: allow_redirects = kwargs.pop("allow_redirects") @@ -90,10 +106,26 @@ def make_request(method, url, max_retries=SSRF_DEFAULT_MAX_RETRIES, **kwargs): verify_option = kwargs.pop("ssl_verify", dify_config.HTTP_REQUEST_NODE_SSL_VERIFY) client = _get_ssrf_client(verify_option) + # Preserve user-provided Host header + # When using a forward proxy, httpx may override the Host header based on the URL. + # We extract and preserve any explicitly set Host header to support virtual hosting. + headers = kwargs.get("headers", {}) + user_provided_host = _get_user_provided_host_header(headers) + retries = 0 while retries <= max_retries: try: - response = client.request(method=method, url=url, **kwargs) + # Build the request manually to preserve the Host header + # httpx may override the Host header when using a proxy, so we use + # the request API to explicitly set headers before sending + request = client.build_request(method=method, url=url, **kwargs) + + # If user explicitly provided a Host header, ensure it's preserved + if user_provided_host is not None: + request.headers["Host"] = user_provided_host + + response = client.send(request) + # Check for SSRF protection by Squid proxy if response.status_code in (401, 403): # Check if this is a Squid SSRF rejection 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 37749f0c66..d5bc3283fe 100644 --- a/api/tests/unit_tests/core/helper/test_ssrf_proxy.py +++ b/api/tests/unit_tests/core/helper/test_ssrf_proxy.py @@ -3,50 +3,160 @@ from unittest.mock import MagicMock, patch import pytest -from core.helper.ssrf_proxy import SSRF_DEFAULT_MAX_RETRIES, STATUS_FORCELIST, make_request +from core.helper.ssrf_proxy import ( + SSRF_DEFAULT_MAX_RETRIES, + STATUS_FORCELIST, + _get_user_provided_host_header, + make_request, +) -@patch("httpx.Client.request") -def test_successful_request(mock_request): +@patch("core.helper.ssrf_proxy._get_ssrf_client") +def test_successful_request(mock_get_client): + mock_client = MagicMock() + mock_request = MagicMock() mock_response = MagicMock() mock_response.status_code = 200 - mock_request.return_value = mock_response + mock_client.send.return_value = mock_response + mock_client.build_request.return_value = mock_request + mock_get_client.return_value = mock_client response = make_request("GET", "http://example.com") assert response.status_code == 200 -@patch("httpx.Client.request") -def test_retry_exceed_max_retries(mock_request): +@patch("core.helper.ssrf_proxy._get_ssrf_client") +def test_retry_exceed_max_retries(mock_get_client): + mock_client = MagicMock() + mock_request = MagicMock() mock_response = MagicMock() mock_response.status_code = 500 - - side_effects = [mock_response] * SSRF_DEFAULT_MAX_RETRIES - mock_request.side_effect = side_effects + mock_client.send.return_value = mock_response + mock_client.build_request.return_value = mock_request + mock_get_client.return_value = mock_client with pytest.raises(Exception) as e: make_request("GET", "http://example.com", max_retries=SSRF_DEFAULT_MAX_RETRIES - 1) assert str(e.value) == f"Reached maximum retries ({SSRF_DEFAULT_MAX_RETRIES - 1}) for URL http://example.com" -@patch("httpx.Client.request") -def test_retry_logic_success(mock_request): - side_effects = [] +@patch("core.helper.ssrf_proxy._get_ssrf_client") +def test_retry_logic_success(mock_get_client): + mock_client = MagicMock() + mock_request = MagicMock() + mock_response = MagicMock() + mock_response.status_code = 200 + side_effects = [] for _ in range(SSRF_DEFAULT_MAX_RETRIES): status_code = secrets.choice(STATUS_FORCELIST) - mock_response = MagicMock() - mock_response.status_code = status_code - side_effects.append(mock_response) + retry_response = MagicMock() + retry_response.status_code = status_code + side_effects.append(retry_response) - mock_response_200 = MagicMock() - mock_response_200.status_code = 200 - side_effects.append(mock_response_200) - - mock_request.side_effect = side_effects + side_effects.append(mock_response) + mock_client.send.side_effect = side_effects + mock_client.build_request.return_value = mock_request + mock_get_client.return_value = mock_client response = make_request("GET", "http://example.com", max_retries=SSRF_DEFAULT_MAX_RETRIES) assert response.status_code == 200 - assert mock_request.call_count == SSRF_DEFAULT_MAX_RETRIES + 1 - assert mock_request.call_args_list[0][1].get("method") == "GET" + assert mock_client.send.call_count == SSRF_DEFAULT_MAX_RETRIES + 1 + assert mock_client.build_request.call_count == SSRF_DEFAULT_MAX_RETRIES + 1 + + +class TestGetUserProvidedHostHeader: + """Tests for _get_user_provided_host_header function.""" + + def test_returns_none_when_headers_is_none(self): + assert _get_user_provided_host_header(None) is None + + def test_returns_none_when_headers_is_empty(self): + assert _get_user_provided_host_header({}) is None + + def test_returns_none_when_host_header_not_present(self): + headers = {"Content-Type": "application/json", "Authorization": "Bearer token"} + assert _get_user_provided_host_header(headers) is None + + def test_returns_host_header_lowercase(self): + headers = {"host": "example.com"} + assert _get_user_provided_host_header(headers) == "example.com" + + def test_returns_host_header_uppercase(self): + headers = {"HOST": "example.com"} + assert _get_user_provided_host_header(headers) == "example.com" + + def test_returns_host_header_mixed_case(self): + headers = {"HoSt": "example.com"} + assert _get_user_provided_host_header(headers) == "example.com" + + def test_returns_host_header_from_multiple_headers(self): + headers = {"Content-Type": "application/json", "Host": "api.example.com", "Authorization": "Bearer token"} + assert _get_user_provided_host_header(headers) == "api.example.com" + + def test_returns_first_host_header_when_duplicates(self): + headers = {"host": "first.com", "Host": "second.com"} + # Should return the first one encountered (iteration order is preserved in dict) + result = _get_user_provided_host_header(headers) + assert result in ("first.com", "second.com") + + +@patch("core.helper.ssrf_proxy._get_ssrf_client") +def test_host_header_preservation_without_user_header(mock_get_client): + """Test that when no Host header is provided, the default behavior is maintained.""" + mock_client = MagicMock() + mock_request = MagicMock() + mock_request.headers = {} + mock_response = MagicMock() + mock_response.status_code = 200 + mock_client.send.return_value = mock_response + mock_client.build_request.return_value = mock_request + mock_get_client.return_value = mock_client + + response = make_request("GET", "http://example.com") + + assert response.status_code == 200 + # build_request should be called without headers dict containing Host + mock_client.build_request.assert_called_once() + # Host should not be set if not provided by user + assert "Host" not in mock_request.headers or mock_request.headers.get("Host") is None + + +@patch("core.helper.ssrf_proxy._get_ssrf_client") +def test_host_header_preservation_with_user_header(mock_get_client): + """Test that user-provided Host header is preserved in the request.""" + mock_client = MagicMock() + mock_request = MagicMock() + mock_request.headers = {} + mock_response = MagicMock() + mock_response.status_code = 200 + mock_client.send.return_value = mock_response + mock_client.build_request.return_value = mock_request + mock_get_client.return_value = mock_client + + custom_host = "custom.example.com:8080" + response = make_request("GET", "http://example.com", headers={"Host": custom_host}) + + assert response.status_code == 200 + # Verify build_request was called + mock_client.build_request.assert_called_once() + # Verify the Host header was set on the request object + assert mock_request.headers.get("Host") == custom_host + mock_client.send.assert_called_once_with(mock_request) + + +@patch("core.helper.ssrf_proxy._get_ssrf_client") +@pytest.mark.parametrize("host_key", ["host", "HOST"]) +def test_host_header_preservation_case_insensitive(mock_get_client, host_key): + """Test that Host header is preserved regardless of case.""" + mock_client = MagicMock() + mock_request = MagicMock() + mock_request.headers = {} + mock_response = MagicMock() + mock_response.status_code = 200 + mock_client.send.return_value = mock_response + mock_client.build_request.return_value = mock_request + mock_get_client.return_value = mock_client + response = make_request("GET", "http://example.com", headers={host_key: "api.example.com"}) + assert mock_request.headers.get("Host") == "api.example.com" From 870a6427c99b8c69803aee147a7dd6162732014a Mon Sep 17 00:00:00 2001 From: wangxiaolei <fatelei@gmail.com> Date: Tue, 23 Dec 2025 19:01:29 +0800 Subject: [PATCH 13/15] feat: allow user close the tab to sync the draft (#30034) --- web/app/components/workflow/index.tsx | 16 +++++-- .../store/workflow/workflow-draft-slice.ts | 47 +++++++++++++------ 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx index ab31f36406..1d0c594c23 100644 --- a/web/app/components/workflow/index.tsx +++ b/web/app/components/workflow/index.tsx @@ -224,23 +224,31 @@ export const Workflow: FC<WorkflowProps> = memo(({ return () => { handleSyncWorkflowDraft(true, true) } - }, []) + }, [handleSyncWorkflowDraft]) const { handleRefreshWorkflowDraft } = useWorkflowRefreshDraft() const handleSyncWorkflowDraftWhenPageClose = useCallback(() => { if (document.visibilityState === 'hidden') syncWorkflowDraftWhenPageClose() + else if (document.visibilityState === 'visible') setTimeout(() => handleRefreshWorkflowDraft(), 500) - }, [syncWorkflowDraftWhenPageClose, handleRefreshWorkflowDraft]) + }, [syncWorkflowDraftWhenPageClose, handleRefreshWorkflowDraft, workflowStore]) + + // Also add beforeunload handler as additional safety net for tab close + const handleBeforeUnload = useCallback(() => { + syncWorkflowDraftWhenPageClose() + }, [syncWorkflowDraftWhenPageClose]) useEffect(() => { document.addEventListener('visibilitychange', handleSyncWorkflowDraftWhenPageClose) + window.addEventListener('beforeunload', handleBeforeUnload) return () => { document.removeEventListener('visibilitychange', handleSyncWorkflowDraftWhenPageClose) + window.removeEventListener('beforeunload', handleBeforeUnload) } - }, [handleSyncWorkflowDraftWhenPageClose]) + }, [handleSyncWorkflowDraftWhenPageClose, handleBeforeUnload]) useEventListener('keydown', (e) => { if ((e.key === 'd' || e.key === 'D') && (e.ctrlKey || e.metaKey)) @@ -419,7 +427,7 @@ export const Workflow: FC<WorkflowProps> = memo(({ onPaneContextMenu={handlePaneContextMenu} onSelectionContextMenu={handleSelectionContextMenu} connectionLineComponent={CustomConnectionLine} - // TODO: For LOOP node, how to distinguish between ITERATION and LOOP here? Maybe both are the same? + // NOTE: For LOOP node, how to distinguish between ITERATION and LOOP here? Maybe both are the same? connectionLineContainerStyle={{ zIndex: ITERATION_CHILDREN_Z_INDEX }} defaultViewport={viewport} multiSelectionKeyCode={null} diff --git a/web/app/components/workflow/store/workflow/workflow-draft-slice.ts b/web/app/components/workflow/store/workflow/workflow-draft-slice.ts index 6c08c50e4a..83792e84a6 100644 --- a/web/app/components/workflow/store/workflow/workflow-draft-slice.ts +++ b/web/app/components/workflow/store/workflow/workflow-draft-slice.ts @@ -7,6 +7,12 @@ import type { } from '@/app/components/workflow/types' import { debounce } from 'lodash-es' +type DebouncedFunc = { + (fn: () => void): void + cancel?: () => void + flush?: () => void +} + export type WorkflowDraftSliceShape = { backupDraft?: { nodes: Node[] @@ -16,7 +22,7 @@ export type WorkflowDraftSliceShape = { environmentVariables: EnvironmentVariable[] } setBackupDraft: (backupDraft?: WorkflowDraftSliceShape['backupDraft']) => void - debouncedSyncWorkflowDraft: (fn: () => void) => void + debouncedSyncWorkflowDraft: DebouncedFunc syncWorkflowDraftHash: string setSyncWorkflowDraftHash: (hash: string) => void isSyncingWorkflowDraft: boolean @@ -25,20 +31,31 @@ export type WorkflowDraftSliceShape = { setIsWorkflowDataLoaded: (loaded: boolean) => void nodes: Node[] setNodes: (nodes: Node[]) => void + flushPendingSync: () => void } -export const createWorkflowDraftSlice: StateCreator<WorkflowDraftSliceShape> = set => ({ - backupDraft: undefined, - setBackupDraft: backupDraft => set(() => ({ backupDraft })), - debouncedSyncWorkflowDraft: debounce((syncWorkflowDraft) => { +export const createWorkflowDraftSlice: StateCreator<WorkflowDraftSliceShape> = (set) => { + // Create the debounced function and store it with access to cancel/flush methods + const debouncedFn = debounce((syncWorkflowDraft) => { syncWorkflowDraft() - }, 5000), - syncWorkflowDraftHash: '', - setSyncWorkflowDraftHash: syncWorkflowDraftHash => set(() => ({ syncWorkflowDraftHash })), - isSyncingWorkflowDraft: false, - setIsSyncingWorkflowDraft: isSyncingWorkflowDraft => set(() => ({ isSyncingWorkflowDraft })), - isWorkflowDataLoaded: false, - setIsWorkflowDataLoaded: loaded => set(() => ({ isWorkflowDataLoaded: loaded })), - nodes: [], - setNodes: nodes => set(() => ({ nodes })), -}) + }, 5000) + + return { + backupDraft: undefined, + setBackupDraft: backupDraft => set(() => ({ backupDraft })), + debouncedSyncWorkflowDraft: debouncedFn, + syncWorkflowDraftHash: '', + setSyncWorkflowDraftHash: syncWorkflowDraftHash => set(() => ({ syncWorkflowDraftHash })), + isSyncingWorkflowDraft: false, + setIsSyncingWorkflowDraft: isSyncingWorkflowDraft => set(() => ({ isSyncingWorkflowDraft })), + isWorkflowDataLoaded: false, + setIsWorkflowDataLoaded: loaded => set(() => ({ isWorkflowDataLoaded: loaded })), + nodes: [], + setNodes: nodes => set(() => ({ nodes })), + flushPendingSync: () => { + // Flush any pending debounced sync operations + if (debouncedFn.flush) + debouncedFn.flush() + }, + } +} From de021ff3e0b56704878d3a3f062cd34f4eaa0d80 Mon Sep 17 00:00:00 2001 From: Asuka Minato <i@asukaminato.eu.org> Date: Tue, 23 Dec 2025 21:30:30 +0900 Subject: [PATCH 14/15] refactor: split changes for api/controllers/web/remote_files.py (#29853) --- api/controllers/web/remote_files.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/api/controllers/web/remote_files.py b/api/controllers/web/remote_files.py index dac4b3da94..c1f976829f 100644 --- a/api/controllers/web/remote_files.py +++ b/api/controllers/web/remote_files.py @@ -1,7 +1,8 @@ import urllib.parse import httpx -from flask_restx import marshal_with, reqparse +from flask_restx import marshal_with +from pydantic import BaseModel, Field, HttpUrl import services from controllers.common import helpers @@ -10,14 +11,23 @@ from controllers.common.errors import ( RemoteFileUploadError, UnsupportedFileTypeError, ) -from controllers.web import web_ns -from controllers.web.wraps import WebApiResource from core.file import helpers as file_helpers from core.helper import ssrf_proxy from extensions.ext_database import db from fields.file_fields import build_file_with_signed_url_model, build_remote_file_info_model from services.file_service import FileService +from ..common.schema import register_schema_models +from . import web_ns +from .wraps import WebApiResource + + +class RemoteFileUploadPayload(BaseModel): + url: HttpUrl = Field(description="Remote file URL") + + +register_schema_models(web_ns, RemoteFileUploadPayload) + @web_ns.route("/remote-files/<path:url>") class RemoteFileInfoApi(WebApiResource): @@ -97,10 +107,8 @@ class RemoteFileUploadApi(WebApiResource): FileTooLargeError: File exceeds size limit UnsupportedFileTypeError: File type not supported """ - parser = reqparse.RequestParser().add_argument("url", type=str, required=True, help="URL is required") - args = parser.parse_args() - - url = args["url"] + payload = RemoteFileUploadPayload.model_validate(web_ns.payload or {}) + url = str(payload.url) try: resp = ssrf_proxy.head(url=url) From 4d48791f3cda0d9ef207be52613d77f4134de168 Mon Sep 17 00:00:00 2001 From: yyh <92089059+lyzno1@users.noreply.github.com> Date: Tue, 23 Dec 2025 23:24:38 +0800 Subject: [PATCH 15/15] refactor: nodejs sdk (#30036) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- sdks/nodejs-client/.gitignore | 58 +- sdks/nodejs-client/LICENSE | 22 + sdks/nodejs-client/README.md | 118 +- sdks/nodejs-client/babel.config.cjs | 12 - sdks/nodejs-client/eslint.config.js | 45 + sdks/nodejs-client/index.d.ts | 107 - sdks/nodejs-client/index.js | 351 --- sdks/nodejs-client/index.test.js | 141 - sdks/nodejs-client/jest.config.cjs | 6 - sdks/nodejs-client/package.json | 62 +- sdks/nodejs-client/pnpm-lock.yaml | 2802 +++++++++++++++++ sdks/nodejs-client/scripts/publish.sh | 261 ++ sdks/nodejs-client/src/client/base.test.js | 175 + sdks/nodejs-client/src/client/base.ts | 284 ++ sdks/nodejs-client/src/client/chat.test.js | 239 ++ sdks/nodejs-client/src/client/chat.ts | 377 +++ .../src/client/completion.test.js | 83 + sdks/nodejs-client/src/client/completion.ts | 111 + .../src/client/knowledge-base.test.js | 249 ++ .../src/client/knowledge-base.ts | 706 +++++ .../src/client/validation.test.js | 91 + sdks/nodejs-client/src/client/validation.ts | 136 + .../nodejs-client/src/client/workflow.test.js | 119 + sdks/nodejs-client/src/client/workflow.ts | 165 + .../src/client/workspace.test.js | 21 + sdks/nodejs-client/src/client/workspace.ts | 16 + .../src/errors/dify-error.test.js | 37 + sdks/nodejs-client/src/errors/dify-error.ts | 75 + sdks/nodejs-client/src/http/client.test.js | 304 ++ sdks/nodejs-client/src/http/client.ts | 368 +++ sdks/nodejs-client/src/http/form-data.test.js | 23 + sdks/nodejs-client/src/http/form-data.ts | 31 + sdks/nodejs-client/src/http/retry.test.js | 38 + sdks/nodejs-client/src/http/retry.ts | 40 + sdks/nodejs-client/src/http/sse.test.js | 76 + sdks/nodejs-client/src/http/sse.ts | 133 + sdks/nodejs-client/src/index.test.js | 227 ++ sdks/nodejs-client/src/index.ts | 103 + sdks/nodejs-client/src/types/annotation.ts | 18 + sdks/nodejs-client/src/types/chat.ts | 17 + sdks/nodejs-client/src/types/common.ts | 71 + sdks/nodejs-client/src/types/completion.ts | 13 + .../nodejs-client/src/types/knowledge-base.ts | 184 ++ sdks/nodejs-client/src/types/workflow.ts | 12 + sdks/nodejs-client/src/types/workspace.ts | 2 + sdks/nodejs-client/tests/test-utils.js | 30 + sdks/nodejs-client/tsconfig.json | 17 + sdks/nodejs-client/tsup.config.ts | 12 + sdks/nodejs-client/vitest.config.ts | 14 + 49 files changed, 7901 insertions(+), 701 deletions(-) create mode 100644 sdks/nodejs-client/LICENSE delete mode 100644 sdks/nodejs-client/babel.config.cjs create mode 100644 sdks/nodejs-client/eslint.config.js delete mode 100644 sdks/nodejs-client/index.d.ts delete mode 100644 sdks/nodejs-client/index.js delete mode 100644 sdks/nodejs-client/index.test.js delete mode 100644 sdks/nodejs-client/jest.config.cjs create mode 100644 sdks/nodejs-client/pnpm-lock.yaml create mode 100755 sdks/nodejs-client/scripts/publish.sh create mode 100644 sdks/nodejs-client/src/client/base.test.js create mode 100644 sdks/nodejs-client/src/client/base.ts create mode 100644 sdks/nodejs-client/src/client/chat.test.js create mode 100644 sdks/nodejs-client/src/client/chat.ts create mode 100644 sdks/nodejs-client/src/client/completion.test.js create mode 100644 sdks/nodejs-client/src/client/completion.ts create mode 100644 sdks/nodejs-client/src/client/knowledge-base.test.js create mode 100644 sdks/nodejs-client/src/client/knowledge-base.ts create mode 100644 sdks/nodejs-client/src/client/validation.test.js create mode 100644 sdks/nodejs-client/src/client/validation.ts create mode 100644 sdks/nodejs-client/src/client/workflow.test.js create mode 100644 sdks/nodejs-client/src/client/workflow.ts create mode 100644 sdks/nodejs-client/src/client/workspace.test.js create mode 100644 sdks/nodejs-client/src/client/workspace.ts create mode 100644 sdks/nodejs-client/src/errors/dify-error.test.js create mode 100644 sdks/nodejs-client/src/errors/dify-error.ts create mode 100644 sdks/nodejs-client/src/http/client.test.js create mode 100644 sdks/nodejs-client/src/http/client.ts create mode 100644 sdks/nodejs-client/src/http/form-data.test.js create mode 100644 sdks/nodejs-client/src/http/form-data.ts create mode 100644 sdks/nodejs-client/src/http/retry.test.js create mode 100644 sdks/nodejs-client/src/http/retry.ts create mode 100644 sdks/nodejs-client/src/http/sse.test.js create mode 100644 sdks/nodejs-client/src/http/sse.ts create mode 100644 sdks/nodejs-client/src/index.test.js create mode 100644 sdks/nodejs-client/src/index.ts create mode 100644 sdks/nodejs-client/src/types/annotation.ts create mode 100644 sdks/nodejs-client/src/types/chat.ts create mode 100644 sdks/nodejs-client/src/types/common.ts create mode 100644 sdks/nodejs-client/src/types/completion.ts create mode 100644 sdks/nodejs-client/src/types/knowledge-base.ts create mode 100644 sdks/nodejs-client/src/types/workflow.ts create mode 100644 sdks/nodejs-client/src/types/workspace.ts create mode 100644 sdks/nodejs-client/tests/test-utils.js create mode 100644 sdks/nodejs-client/tsconfig.json create mode 100644 sdks/nodejs-client/tsup.config.ts create mode 100644 sdks/nodejs-client/vitest.config.ts diff --git a/sdks/nodejs-client/.gitignore b/sdks/nodejs-client/.gitignore index 1d40ff2ece..33cfbd3b18 100644 --- a/sdks/nodejs-client/.gitignore +++ b/sdks/nodejs-client/.gitignore @@ -1,48 +1,40 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +# Dependencies +node_modules/ -# dependencies -/node_modules -/.pnp -.pnp.js +# Build output +dist/ -# testing -/coverage +# Testing +coverage/ -# next.js -/.next/ -/out/ +# IDE +.idea/ +.vscode/ +*.swp +*.swo -# production -/build - -# misc +# OS .DS_Store -*.pem +Thumbs.db -# debug +# Debug logs npm-debug.log* yarn-debug.log* yarn-error.log* -.pnpm-debug.log* +pnpm-debug.log* -# local env files -.env*.local +# Environment +.env +.env.local +.env.*.local -# vercel -.vercel - -# typescript +# TypeScript *.tsbuildinfo -next-env.d.ts -# npm +# Lock files (use pnpm-lock.yaml in CI if needed) package-lock.json +yarn.lock -# yarn -.pnp.cjs -.pnp.loader.mjs -.yarn/ -.yarnrc.yml - -# pmpm -pnpm-lock.yaml +# Misc +*.pem +*.tgz diff --git a/sdks/nodejs-client/LICENSE b/sdks/nodejs-client/LICENSE new file mode 100644 index 0000000000..ff17417e1f --- /dev/null +++ b/sdks/nodejs-client/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2023 LangGenius + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/sdks/nodejs-client/README.md b/sdks/nodejs-client/README.md index 3a5688bcbe..f8c2803c08 100644 --- a/sdks/nodejs-client/README.md +++ b/sdks/nodejs-client/README.md @@ -13,54 +13,92 @@ npm install dify-client After installing the SDK, you can use it in your project like this: ```js -import { DifyClient, ChatClient, CompletionClient } from 'dify-client' +import { + DifyClient, + ChatClient, + CompletionClient, + WorkflowClient, + KnowledgeBaseClient, + WorkspaceClient +} from 'dify-client' -const API_KEY = 'your-api-key-here' -const user = `random-user-id` +const API_KEY = 'your-app-api-key' +const DATASET_API_KEY = 'your-dataset-api-key' +const user = 'random-user-id' const query = 'Please tell me a short story in 10 words or less.' -const remote_url_files = [{ - type: 'image', - transfer_method: 'remote_url', - url: 'your_url_address' -}] -// Create a completion client -const completionClient = new CompletionClient(API_KEY) -// Create a completion message -completionClient.createCompletionMessage({'query': query}, user) -// Create a completion message with vision model -completionClient.createCompletionMessage({'query': 'Describe the picture.'}, user, false, remote_url_files) - -// Create a chat client const chatClient = new ChatClient(API_KEY) -// Create a chat message in stream mode -const response = await chatClient.createChatMessage({}, query, user, true, null) -const stream = response.data; -stream.on('data', data => { - console.log(data); -}); -stream.on('end', () => { - console.log('stream done'); -}); -// Create a chat message with vision model -chatClient.createChatMessage({}, 'Describe the picture.', user, false, null, remote_url_files) -// Fetch conversations -chatClient.getConversations(user) -// Fetch conversation messages -chatClient.getConversationMessages(conversationId, user) -// Rename conversation -chatClient.renameConversation(conversationId, name, user) - - +const completionClient = new CompletionClient(API_KEY) +const workflowClient = new WorkflowClient(API_KEY) +const kbClient = new KnowledgeBaseClient(DATASET_API_KEY) +const workspaceClient = new WorkspaceClient(DATASET_API_KEY) const client = new DifyClient(API_KEY) -// Fetch application parameters -client.getApplicationParameters(user) -// Provide feedback for a message -client.messageFeedback(messageId, rating, user) + +// App core +await client.getApplicationParameters(user) +await client.messageFeedback('message-id', 'like', user) + +// Completion (blocking) +await completionClient.createCompletionMessage({ + inputs: { query }, + user, + response_mode: 'blocking' +}) + +// Chat (streaming) +const stream = await chatClient.createChatMessage({ + inputs: {}, + query, + user, + response_mode: 'streaming' +}) +for await (const event of stream) { + console.log(event.event, event.data) +} + +// Chatflow (advanced chat via workflow_id) +await chatClient.createChatMessage({ + inputs: {}, + query, + user, + workflow_id: 'workflow-id', + response_mode: 'blocking' +}) + +// Workflow run (blocking or streaming) +await workflowClient.run({ + inputs: { query }, + user, + response_mode: 'blocking' +}) + +// Knowledge base (dataset token required) +await kbClient.listDatasets({ page: 1, limit: 20 }) +await kbClient.createDataset({ name: 'KB', indexing_technique: 'economy' }) + +// RAG pipeline (may require service API route registration) +const pipelineStream = await kbClient.runPipeline('dataset-id', { + inputs: {}, + datasource_type: 'online_document', + datasource_info_list: [], + start_node_id: 'start-node-id', + is_published: true, + response_mode: 'streaming' +}) +for await (const event of pipelineStream) { + console.log(event.data) +} + +// Workspace models (dataset token required) +await workspaceClient.getModelsByType('text-embedding') ``` -Replace 'your-api-key-here' with your actual Dify API key.Replace 'your-app-id-here' with your actual Dify APP ID. +Notes: + +- App endpoints use an app API token; knowledge base and workspace endpoints use a dataset API token. +- Chat/completion require a stable `user` identifier in the request payload. +- For streaming responses, iterate the returned AsyncIterable. Use `stream.toText()` to collect text. ## License diff --git a/sdks/nodejs-client/babel.config.cjs b/sdks/nodejs-client/babel.config.cjs deleted file mode 100644 index 392abb66d8..0000000000 --- a/sdks/nodejs-client/babel.config.cjs +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - presets: [ - [ - "@babel/preset-env", - { - targets: { - node: "current", - }, - }, - ], - ], -}; diff --git a/sdks/nodejs-client/eslint.config.js b/sdks/nodejs-client/eslint.config.js new file mode 100644 index 0000000000..9e659f5d28 --- /dev/null +++ b/sdks/nodejs-client/eslint.config.js @@ -0,0 +1,45 @@ +import js from "@eslint/js"; +import tsParser from "@typescript-eslint/parser"; +import tsPlugin from "@typescript-eslint/eslint-plugin"; +import { fileURLToPath } from "node:url"; +import path from "node:path"; + +const tsconfigRootDir = path.dirname(fileURLToPath(import.meta.url)); +const typeCheckedRules = + tsPlugin.configs["recommended-type-checked"]?.rules ?? + tsPlugin.configs.recommendedTypeChecked?.rules ?? + {}; + +export default [ + { + ignores: ["dist", "node_modules", "scripts", "tests", "**/*.test.*", "**/*.spec.*"], + }, + js.configs.recommended, + { + files: ["src/**/*.ts"], + languageOptions: { + parser: tsParser, + ecmaVersion: "latest", + parserOptions: { + project: "./tsconfig.json", + tsconfigRootDir, + sourceType: "module", + }, + }, + plugins: { + "@typescript-eslint": tsPlugin, + }, + rules: { + ...tsPlugin.configs.recommended.rules, + ...typeCheckedRules, + "no-undef": "off", + "no-unused-vars": "off", + "@typescript-eslint/no-unsafe-call": "error", + "@typescript-eslint/no-unsafe-return": "error", + "@typescript-eslint/consistent-type-imports": [ + "error", + { prefer: "type-imports", fixStyle: "separate-type-imports" }, + ], + }, + }, +]; diff --git a/sdks/nodejs-client/index.d.ts b/sdks/nodejs-client/index.d.ts deleted file mode 100644 index 3ea4b9d153..0000000000 --- a/sdks/nodejs-client/index.d.ts +++ /dev/null @@ -1,107 +0,0 @@ -// Types.d.ts -export const BASE_URL: string; - -export type RequestMethods = 'GET' | 'POST' | 'PATCH' | 'DELETE'; - -interface Params { - [key: string]: any; -} - -interface HeaderParams { - [key: string]: string; -} - -interface User { -} - -interface DifyFileBase { - type: "image" -} - -export interface DifyRemoteFile extends DifyFileBase { - transfer_method: "remote_url" - url: string -} - -export interface DifyLocalFile extends DifyFileBase { - transfer_method: "local_file" - upload_file_id: string -} - -export type DifyFile = DifyRemoteFile | DifyLocalFile; - -export declare class DifyClient { - constructor(apiKey: string, baseUrl?: string); - - updateApiKey(apiKey: string): void; - - sendRequest( - method: RequestMethods, - endpoint: string, - data?: any, - params?: Params, - stream?: boolean, - headerParams?: HeaderParams - ): Promise<any>; - - messageFeedback(message_id: string, rating: number, user: User): Promise<any>; - - getApplicationParameters(user: User): Promise<any>; - - fileUpload(data: FormData): Promise<any>; - - textToAudio(text: string ,user: string, streaming?: boolean): Promise<any>; - - getMeta(user: User): Promise<any>; -} - -export declare class CompletionClient extends DifyClient { - createCompletionMessage( - inputs: any, - user: User, - stream?: boolean, - files?: DifyFile[] | null - ): Promise<any>; -} - -export declare class ChatClient extends DifyClient { - createChatMessage( - inputs: any, - query: string, - user: User, - stream?: boolean, - conversation_id?: string | null, - files?: DifyFile[] | null - ): Promise<any>; - - getSuggested(message_id: string, user: User): Promise<any>; - - stopMessage(task_id: string, user: User) : Promise<any>; - - - getConversations( - user: User, - first_id?: string | null, - limit?: number | null, - pinned?: boolean | null - ): Promise<any>; - - getConversationMessages( - user: User, - conversation_id?: string, - first_id?: string | null, - limit?: number | null - ): Promise<any>; - - renameConversation(conversation_id: string, name: string, user: User,auto_generate:boolean): Promise<any>; - - deleteConversation(conversation_id: string, user: User): Promise<any>; - - audioToText(data: FormData): Promise<any>; -} - -export declare class WorkflowClient extends DifyClient { - run(inputs: any, user: User, stream?: boolean,): Promise<any>; - - stop(task_id: string, user: User): Promise<any>; -} diff --git a/sdks/nodejs-client/index.js b/sdks/nodejs-client/index.js deleted file mode 100644 index 9743ae358c..0000000000 --- a/sdks/nodejs-client/index.js +++ /dev/null @@ -1,351 +0,0 @@ -import axios from "axios"; -export const BASE_URL = "https://api.dify.ai/v1"; - -export const routes = { - // app's - feedback: { - method: "POST", - url: (message_id) => `/messages/${message_id}/feedbacks`, - }, - application: { - method: "GET", - url: () => `/parameters`, - }, - fileUpload: { - method: "POST", - url: () => `/files/upload`, - }, - textToAudio: { - method: "POST", - url: () => `/text-to-audio`, - }, - getMeta: { - method: "GET", - url: () => `/meta`, - }, - - // completion's - createCompletionMessage: { - method: "POST", - url: () => `/completion-messages`, - }, - - // chat's - createChatMessage: { - method: "POST", - url: () => `/chat-messages`, - }, - getSuggested:{ - method: "GET", - url: (message_id) => `/messages/${message_id}/suggested`, - }, - stopChatMessage: { - method: "POST", - url: (task_id) => `/chat-messages/${task_id}/stop`, - }, - getConversations: { - method: "GET", - url: () => `/conversations`, - }, - getConversationMessages: { - method: "GET", - url: () => `/messages`, - }, - renameConversation: { - method: "POST", - url: (conversation_id) => `/conversations/${conversation_id}/name`, - }, - deleteConversation: { - method: "DELETE", - url: (conversation_id) => `/conversations/${conversation_id}`, - }, - audioToText: { - method: "POST", - url: () => `/audio-to-text`, - }, - - // workflow‘s - runWorkflow: { - method: "POST", - url: () => `/workflows/run`, - }, - stopWorkflow: { - method: "POST", - url: (task_id) => `/workflows/tasks/${task_id}/stop`, - } - -}; - -export class DifyClient { - constructor(apiKey, baseUrl = BASE_URL) { - this.apiKey = apiKey; - this.baseUrl = baseUrl; - } - - updateApiKey(apiKey) { - this.apiKey = apiKey; - } - - async sendRequest( - method, - endpoint, - data = null, - params = null, - stream = false, - headerParams = {} - ) { - const isFormData = - (typeof FormData !== "undefined" && data instanceof FormData) || - (data && data.constructor && data.constructor.name === "FormData"); - const headers = { - Authorization: `Bearer ${this.apiKey}`, - ...(isFormData ? {} : { "Content-Type": "application/json" }), - ...headerParams, - }; - - const url = `${this.baseUrl}${endpoint}`; - let response; - if (stream) { - response = await axios({ - method, - url, - data, - params, - headers, - responseType: "stream", - }); - } else { - response = await axios({ - method, - url, - ...(method !== "GET" && { data }), - params, - headers, - responseType: "json", - }); - } - - return response; - } - - messageFeedback(message_id, rating, user) { - const data = { - rating, - user, - }; - return this.sendRequest( - routes.feedback.method, - routes.feedback.url(message_id), - data - ); - } - - getApplicationParameters(user) { - const params = { user }; - return this.sendRequest( - routes.application.method, - routes.application.url(), - null, - params - ); - } - - fileUpload(data) { - return this.sendRequest( - routes.fileUpload.method, - routes.fileUpload.url(), - data - ); - } - - textToAudio(text, user, streaming = false) { - const data = { - text, - user, - streaming - }; - return this.sendRequest( - routes.textToAudio.method, - routes.textToAudio.url(), - data, - null, - streaming - ); - } - - getMeta(user) { - const params = { user }; - return this.sendRequest( - routes.getMeta.method, - routes.getMeta.url(), - null, - params - ); - } -} - -export class CompletionClient extends DifyClient { - createCompletionMessage(inputs, user, stream = false, files = null) { - const data = { - inputs, - user, - response_mode: stream ? "streaming" : "blocking", - files, - }; - return this.sendRequest( - routes.createCompletionMessage.method, - routes.createCompletionMessage.url(), - data, - null, - stream - ); - } - - runWorkflow(inputs, user, stream = false, files = null) { - const data = { - inputs, - user, - response_mode: stream ? "streaming" : "blocking", - }; - return this.sendRequest( - routes.runWorkflow.method, - routes.runWorkflow.url(), - data, - null, - stream - ); - } -} - -export class ChatClient extends DifyClient { - createChatMessage( - inputs, - query, - user, - stream = false, - conversation_id = null, - files = null - ) { - const data = { - inputs, - query, - user, - response_mode: stream ? "streaming" : "blocking", - files, - }; - if (conversation_id) data.conversation_id = conversation_id; - - return this.sendRequest( - routes.createChatMessage.method, - routes.createChatMessage.url(), - data, - null, - stream - ); - } - - getSuggested(message_id, user) { - const data = { user }; - return this.sendRequest( - routes.getSuggested.method, - routes.getSuggested.url(message_id), - data - ); - } - - stopMessage(task_id, user) { - const data = { user }; - return this.sendRequest( - routes.stopChatMessage.method, - routes.stopChatMessage.url(task_id), - data - ); - } - - getConversations(user, first_id = null, limit = null, pinned = null) { - const params = { user, first_id: first_id, limit, pinned }; - return this.sendRequest( - routes.getConversations.method, - routes.getConversations.url(), - null, - params - ); - } - - getConversationMessages( - user, - conversation_id = "", - first_id = null, - limit = null - ) { - const params = { user }; - - if (conversation_id) params.conversation_id = conversation_id; - - if (first_id) params.first_id = first_id; - - if (limit) params.limit = limit; - - return this.sendRequest( - routes.getConversationMessages.method, - routes.getConversationMessages.url(), - null, - params - ); - } - - renameConversation(conversation_id, name, user, auto_generate) { - const data = { name, user, auto_generate }; - return this.sendRequest( - routes.renameConversation.method, - routes.renameConversation.url(conversation_id), - data - ); - } - - deleteConversation(conversation_id, user) { - const data = { user }; - return this.sendRequest( - routes.deleteConversation.method, - routes.deleteConversation.url(conversation_id), - data - ); - } - - - audioToText(data) { - return this.sendRequest( - routes.audioToText.method, - routes.audioToText.url(), - data - ); - } - -} - -export class WorkflowClient extends DifyClient { - run(inputs,user,stream) { - const data = { - inputs, - response_mode: stream ? "streaming" : "blocking", - user - }; - - return this.sendRequest( - routes.runWorkflow.method, - routes.runWorkflow.url(), - data, - null, - stream - ); - } - - stop(task_id, user) { - const data = { user }; - return this.sendRequest( - routes.stopWorkflow.method, - routes.stopWorkflow.url(task_id), - data - ); - } -} diff --git a/sdks/nodejs-client/index.test.js b/sdks/nodejs-client/index.test.js deleted file mode 100644 index e3a1715238..0000000000 --- a/sdks/nodejs-client/index.test.js +++ /dev/null @@ -1,141 +0,0 @@ -import { DifyClient, WorkflowClient, BASE_URL, routes } from "."; - -import axios from 'axios' - -jest.mock('axios') - -afterEach(() => { - jest.resetAllMocks() -}) - -describe('Client', () => { - let difyClient - beforeEach(() => { - difyClient = new DifyClient('test') - }) - - test('should create a client', () => { - expect(difyClient).toBeDefined(); - }) - // test updateApiKey - test('should update the api key', () => { - difyClient.updateApiKey('test2'); - expect(difyClient.apiKey).toBe('test2'); - }) -}); - -describe('Send Requests', () => { - let difyClient - - beforeEach(() => { - difyClient = new DifyClient('test') - }) - - it('should make a successful request to the application parameter', async () => { - const method = 'GET' - const endpoint = routes.application.url() - const expectedResponse = { data: 'response' } - axios.mockResolvedValue(expectedResponse) - - await difyClient.sendRequest(method, endpoint) - - expect(axios).toHaveBeenCalledWith({ - method, - url: `${BASE_URL}${endpoint}`, - params: null, - headers: { - Authorization: `Bearer ${difyClient.apiKey}`, - 'Content-Type': 'application/json', - }, - responseType: 'json', - }) - - }) - - it('should handle errors from the API', async () => { - const method = 'GET' - const endpoint = '/test-endpoint' - const errorMessage = 'Request failed with status code 404' - axios.mockRejectedValue(new Error(errorMessage)) - - await expect(difyClient.sendRequest(method, endpoint)).rejects.toThrow( - errorMessage - ) - }) - - it('uses the getMeta route configuration', async () => { - axios.mockResolvedValue({ data: 'ok' }) - await difyClient.getMeta('end-user') - - expect(axios).toHaveBeenCalledWith({ - method: routes.getMeta.method, - url: `${BASE_URL}${routes.getMeta.url()}`, - params: { user: 'end-user' }, - headers: { - Authorization: `Bearer ${difyClient.apiKey}`, - 'Content-Type': 'application/json', - }, - responseType: 'json', - }) - }) -}) - -describe('File uploads', () => { - let difyClient - const OriginalFormData = global.FormData - - beforeAll(() => { - global.FormData = class FormDataMock {} - }) - - afterAll(() => { - global.FormData = OriginalFormData - }) - - beforeEach(() => { - difyClient = new DifyClient('test') - }) - - it('does not override multipart boundary headers for FormData', async () => { - const form = new FormData() - axios.mockResolvedValue({ data: 'ok' }) - - await difyClient.fileUpload(form) - - expect(axios).toHaveBeenCalledWith({ - method: routes.fileUpload.method, - url: `${BASE_URL}${routes.fileUpload.url()}`, - data: form, - params: null, - headers: { - Authorization: `Bearer ${difyClient.apiKey}`, - }, - responseType: 'json', - }) - }) -}) - -describe('Workflow client', () => { - let workflowClient - - beforeEach(() => { - workflowClient = new WorkflowClient('test') - }) - - it('uses tasks stop path for workflow stop', async () => { - axios.mockResolvedValue({ data: 'stopped' }) - await workflowClient.stop('task-1', 'end-user') - - expect(axios).toHaveBeenCalledWith({ - method: routes.stopWorkflow.method, - url: `${BASE_URL}${routes.stopWorkflow.url('task-1')}`, - data: { user: 'end-user' }, - params: null, - headers: { - Authorization: `Bearer ${workflowClient.apiKey}`, - 'Content-Type': 'application/json', - }, - responseType: 'json', - }) - }) -}) diff --git a/sdks/nodejs-client/jest.config.cjs b/sdks/nodejs-client/jest.config.cjs deleted file mode 100644 index ea0fb34ad1..0000000000 --- a/sdks/nodejs-client/jest.config.cjs +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - testEnvironment: "node", - transform: { - "^.+\\.[tj]sx?$": "babel-jest", - }, -}; diff --git a/sdks/nodejs-client/package.json b/sdks/nodejs-client/package.json index c6bb0a9c1f..554cb909ef 100644 --- a/sdks/nodejs-client/package.json +++ b/sdks/nodejs-client/package.json @@ -1,30 +1,70 @@ { "name": "dify-client", - "version": "2.3.2", + "version": "3.0.0", "description": "This is the Node.js SDK for the Dify.AI API, which allows you to easily integrate Dify.AI into your Node.js applications.", - "main": "index.js", "type": "module", - "types":"index.d.ts", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "engines": { + "node": ">=18.0.0" + }, + "files": [ + "dist", + "README.md", + "LICENSE" + ], "keywords": [ "Dify", "Dify.AI", - "LLM" + "LLM", + "AI", + "SDK", + "API" ], - "author": "Joel", + "author": "LangGenius", "contributors": [ - "<crazywoola> <<427733928@qq.com>> (https://github.com/crazywoola)" + "Joel <iamjoel007@gmail.com> (https://github.com/iamjoel)", + "lyzno1 <yuanyouhuilyz@gmail.com> (https://github.com/lyzno1)", + "crazywoola <427733928@qq.com> (https://github.com/crazywoola)" ], + "repository": { + "type": "git", + "url": "https://github.com/langgenius/dify.git", + "directory": "sdks/nodejs-client" + }, + "bugs": { + "url": "https://github.com/langgenius/dify/issues" + }, + "homepage": "https://dify.ai", "license": "MIT", "scripts": { - "test": "jest" + "build": "tsup", + "lint": "eslint", + "lint:fix": "eslint --fix", + "type-check": "tsc -p tsconfig.json --noEmit", + "test": "vitest run", + "test:coverage": "vitest run --coverage", + "publish:check": "./scripts/publish.sh --dry-run", + "publish:npm": "./scripts/publish.sh" }, "dependencies": { "axios": "^1.3.5" }, "devDependencies": { - "@babel/core": "^7.21.8", - "@babel/preset-env": "^7.21.5", - "babel-jest": "^29.5.0", - "jest": "^29.5.0" + "@eslint/js": "^9.2.0", + "@types/node": "^20.11.30", + "@typescript-eslint/eslint-plugin": "^8.50.1", + "@typescript-eslint/parser": "^8.50.1", + "@vitest/coverage-v8": "1.6.1", + "eslint": "^9.2.0", + "tsup": "^8.5.1", + "typescript": "^5.4.5", + "vitest": "^1.5.0" } } diff --git a/sdks/nodejs-client/pnpm-lock.yaml b/sdks/nodejs-client/pnpm-lock.yaml new file mode 100644 index 0000000000..3e4011c580 --- /dev/null +++ b/sdks/nodejs-client/pnpm-lock.yaml @@ -0,0 +1,2802 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + axios: + specifier: ^1.3.5 + version: 1.13.2 + devDependencies: + '@eslint/js': + specifier: ^9.2.0 + version: 9.39.2 + '@types/node': + specifier: ^20.11.30 + version: 20.19.27 + '@typescript-eslint/eslint-plugin': + specifier: ^8.50.1 + version: 8.50.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: ^8.50.1 + version: 8.50.1(eslint@9.39.2)(typescript@5.9.3) + '@vitest/coverage-v8': + specifier: 1.6.1 + version: 1.6.1(vitest@1.6.1(@types/node@20.19.27)) + eslint: + specifier: ^9.2.0 + version: 9.39.2 + tsup: + specifier: ^8.5.1 + version: 8.5.1(postcss@8.5.6)(typescript@5.9.3) + typescript: + specifier: ^5.4.5 + version: 5.9.3 + vitest: + specifier: ^1.5.0 + version: 1.6.1(@types/node@20.19.27) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@rollup/rollup-android-arm-eabi@4.54.0': + resolution: {integrity: sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.54.0': + resolution: {integrity: sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.54.0': + resolution: {integrity: sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.54.0': + resolution: {integrity: sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.54.0': + resolution: {integrity: sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.54.0': + resolution: {integrity: sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': + resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.54.0': + resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.54.0': + resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.54.0': + resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.54.0': + resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.54.0': + resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.54.0': + resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.54.0': + resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.54.0': + resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.54.0': + resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.54.0': + resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.54.0': + resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.54.0': + resolution: {integrity: sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.54.0': + resolution: {integrity: sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.54.0': + resolution: {integrity: sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.54.0': + resolution: {integrity: sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==} + cpu: [x64] + os: [win32] + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@20.19.27': + resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==} + + '@typescript-eslint/eslint-plugin@8.50.1': + resolution: {integrity: sha512-PKhLGDq3JAg0Jk/aK890knnqduuI/Qj+udH7wCf0217IGi4gt+acgCyPVe79qoT+qKUvHMDQkwJeKW9fwl8Cyw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.50.1 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.50.1': + resolution: {integrity: sha512-hM5faZwg7aVNa819m/5r7D0h0c9yC4DUlWAOvHAtISdFTc8xB86VmX5Xqabrama3wIPJ/q9RbGS1worb6JfnMg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.50.1': + resolution: {integrity: sha512-E1ur1MCVf+YiP89+o4Les/oBAVzmSbeRB0MQLfSlYtbWU17HPxZ6Bhs5iYmKZRALvEuBoXIZMOIRRc/P++Ortg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.50.1': + resolution: {integrity: sha512-mfRx06Myt3T4vuoHaKi8ZWNTPdzKPNBhiblze5N50//TSHOAQQevl/aolqA/BcqqbJ88GUnLqjjcBc8EWdBcVw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.50.1': + resolution: {integrity: sha512-ooHmotT/lCWLXi55G4mvaUF60aJa012QzvLK0Y+Mp4WdSt17QhMhWOaBWeGTFVkb2gDgBe19Cxy1elPXylslDw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.50.1': + resolution: {integrity: sha512-7J3bf022QZE42tYMO6SL+6lTPKFk/WphhRPe9Tw/el+cEwzLz1Jjz2PX3GtGQVxooLDKeMVmMt7fWpYRdG5Etg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.50.1': + resolution: {integrity: sha512-v5lFIS2feTkNyMhd7AucE/9j/4V9v5iIbpVRncjk/K0sQ6Sb+Np9fgYS/63n6nwqahHQvbmujeBL7mp07Q9mlA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.50.1': + resolution: {integrity: sha512-woHPdW+0gj53aM+cxchymJCrh0cyS7BTIdcDxWUNsclr9VDkOSbqC13juHzxOmQ22dDkMZEpZB+3X1WpUvzgVQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.50.1': + resolution: {integrity: sha512-lCLp8H1T9T7gPbEuJSnHwnSuO9mDf8mfK/Nion5mZmiEaQD9sWf9W4dfeFqRyqRjF06/kBuTmAqcs9sewM2NbQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.50.1': + resolution: {integrity: sha512-IrDKrw7pCRUR94zeuCSUWQ+w8JEf5ZX5jl/e6AHGSLi1/zIr0lgutfn/7JpfCey+urpgQEdrZVYzCaVVKiTwhQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitest/coverage-v8@1.6.1': + resolution: {integrity: sha512-6YeRZwuO4oTGKxD3bijok756oktHSIm3eczVVzNe3scqzuhLwltIF3S9ZL/vwOVIpURmU6SnZhziXXAfw8/Qlw==} + peerDependencies: + vitest: 1.6.1 + + '@vitest/expect@1.6.1': + resolution: {integrity: sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==} + + '@vitest/runner@1.6.1': + resolution: {integrity: sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==} + + '@vitest/snapshot@1.6.1': + resolution: {integrity: sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==} + + '@vitest/spy@1.6.1': + resolution: {integrity: sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==} + + '@vitest/utils@1.6.1': + resolution: {integrity: sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.13.2: + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.18' + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chai@4.5.0: + resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + local-pkg@0.5.1: + resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} + engines: {node: '>=14'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + mlly@1.8.0: + resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + rollup@4.54.0: + resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@2.1.1: + resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==} + + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinypool@0.8.4: + resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} + engines: {node: '>=14.0.0'} + + tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tsup@8.5.1: + resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + vite-node@1.6.1: + resolution: {integrity: sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + vite@5.4.21: + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@1.6.1: + resolution: {integrity: sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 1.6.1 + '@vitest/ui': 1.6.1 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + engines: {node: '>=12.20'} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@bcoe/v8-coverage@0.2.3': {} + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/aix-ppc64@0.27.2': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.27.2': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-arm@0.27.2': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/android-x64@0.27.2': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.27.2': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.27.2': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.27.2': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.27.2': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.27.2': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-arm@0.27.2': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.27.2': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.27.2': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.27.2': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.27.2': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.27.2': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.27.2': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/linux-x64@0.27.2': + optional: true + + '@esbuild/netbsd-arm64@0.27.2': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.27.2': + optional: true + + '@esbuild/openbsd-arm64@0.27.2': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.27.2': + optional: true + + '@esbuild/openharmony-arm64@0.27.2': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.27.2': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.27.2': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.27.2': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@esbuild/win32-x64@0.27.2': + optional: true + + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2)': + dependencies: + eslint: 9.39.2 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.3': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.2': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@istanbuljs/schema@0.1.3': {} + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.8 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@rollup/rollup-android-arm-eabi@4.54.0': + optional: true + + '@rollup/rollup-android-arm64@4.54.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.54.0': + optional: true + + '@rollup/rollup-darwin-x64@4.54.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.54.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.54.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.54.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.54.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.54.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.54.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.54.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.54.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.54.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.54.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.54.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.54.0': + optional: true + + '@sinclair/typebox@0.27.8': {} + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@20.19.27': + dependencies: + undici-types: 6.21.0 + + '@typescript-eslint/eslint-plugin@8.50.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.50.1(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.50.1 + '@typescript-eslint/type-utils': 8.50.1(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.1(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.1 + eslint: 9.39.2 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.50.1(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.50.1 + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.1 + debug: 4.4.3 + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.50.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.50.1(typescript@5.9.3) + '@typescript-eslint/types': 8.50.1 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.50.1': + dependencies: + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/visitor-keys': 8.50.1 + + '@typescript-eslint/tsconfig-utils@8.50.1(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.50.1(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.1(eslint@9.39.2)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.2 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.50.1': {} + + '@typescript-eslint/typescript-estree@8.50.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.50.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.50.1(typescript@5.9.3) + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/visitor-keys': 8.50.1 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.50.1(eslint@9.39.2)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) + '@typescript-eslint/scope-manager': 8.50.1 + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.50.1': + dependencies: + '@typescript-eslint/types': 8.50.1 + eslint-visitor-keys: 4.2.1 + + '@vitest/coverage-v8@1.6.1(vitest@1.6.1(@types/node@20.19.27))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 0.2.3 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + magic-string: 0.30.21 + magicast: 0.3.5 + picocolors: 1.1.1 + std-env: 3.10.0 + strip-literal: 2.1.1 + test-exclude: 6.0.0 + vitest: 1.6.1(@types/node@20.19.27) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@1.6.1': + dependencies: + '@vitest/spy': 1.6.1 + '@vitest/utils': 1.6.1 + chai: 4.5.0 + + '@vitest/runner@1.6.1': + dependencies: + '@vitest/utils': 1.6.1 + p-limit: 5.0.0 + pathe: 1.1.2 + + '@vitest/snapshot@1.6.1': + dependencies: + magic-string: 0.30.21 + pathe: 1.1.2 + pretty-format: 29.7.0 + + '@vitest/spy@1.6.1': + dependencies: + tinyspy: 2.2.1 + + '@vitest/utils@1.6.1': + dependencies: + diff-sequences: 29.6.3 + estree-walker: 3.0.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + any-promise@1.3.0: {} + + argparse@2.0.1: {} + + assertion-error@1.1.0: {} + + asynckit@0.4.0: {} + + axios@1.13.2: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + bundle-require@5.1.0(esbuild@0.27.2): + dependencies: + esbuild: 0.27.2 + load-tsconfig: 0.2.5 + + cac@6.7.14: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + callsites@3.1.0: {} + + chai@4.5.0: + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.4 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.1.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + check-error@1.0.3: + dependencies: + get-func-name: 2.0.2 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@4.1.1: {} + + concat-map@0.0.1: {} + + confbox@0.1.8: {} + + consola@3.4.2: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-eql@4.1.4: + dependencies: + type-detect: 4.1.0 + + deep-is@0.1.4: {} + + delayed-stream@1.0.0: {} + + diff-sequences@29.6.3: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + + esbuild@0.27.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + + escape-string-regexp@4.0.0: {} + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.39.2: + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + esutils@2.0.3: {} + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + fix-dts-default-cjs-exports@1.0.1: + dependencies: + magic-string: 0.30.21 + mlly: 1.8.0 + rollup: 4.54.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + follow-redirects@1.15.11: {} + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-func-name@2.0.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@8.0.1: {} + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@14.0.0: {} + + gopd@1.2.0: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + html-escaper@2.0.2: {} + + human-signals@5.0.0: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-stream@3.0.0: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + joycon@3.1.1: {} + + js-tokens@9.0.1: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + load-tsconfig@0.2.5: {} + + local-pkg@0.5.1: + dependencies: + mlly: 1.8.0 + pkg-types: 1.3.1 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + loupe@2.3.7: + dependencies: + get-func-name: 2.0.2 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.3.5: + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.3 + + math-intrinsics@1.1.0: {} + + merge-stream@2.0.0: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-fn@4.0.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + mlly@1.8.0: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + + ms@2.1.3: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + object-assign@4.1.1: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@5.0.0: + dependencies: + yocto-queue: 1.2.2 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + pathe@1.1.2: {} + + pathe@2.0.3: {} + + pathval@1.1.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + pirates@4.0.7: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.0 + pathe: 2.0.3 + + postcss-load-config@6.0.1(postcss@8.5.6): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + postcss: 8.5.6 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + + proxy-from-env@1.1.0: {} + + punycode@2.3.1: {} + + react-is@18.3.1: {} + + readdirp@4.1.2: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + rollup@4.54.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.54.0 + '@rollup/rollup-android-arm64': 4.54.0 + '@rollup/rollup-darwin-arm64': 4.54.0 + '@rollup/rollup-darwin-x64': 4.54.0 + '@rollup/rollup-freebsd-arm64': 4.54.0 + '@rollup/rollup-freebsd-x64': 4.54.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.54.0 + '@rollup/rollup-linux-arm-musleabihf': 4.54.0 + '@rollup/rollup-linux-arm64-gnu': 4.54.0 + '@rollup/rollup-linux-arm64-musl': 4.54.0 + '@rollup/rollup-linux-loong64-gnu': 4.54.0 + '@rollup/rollup-linux-ppc64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-musl': 4.54.0 + '@rollup/rollup-linux-s390x-gnu': 4.54.0 + '@rollup/rollup-linux-x64-gnu': 4.54.0 + '@rollup/rollup-linux-x64-musl': 4.54.0 + '@rollup/rollup-openharmony-arm64': 4.54.0 + '@rollup/rollup-win32-arm64-msvc': 4.54.0 + '@rollup/rollup-win32-ia32-msvc': 4.54.0 + '@rollup/rollup-win32-x64-gnu': 4.54.0 + '@rollup/rollup-win32-x64-msvc': 4.54.0 + fsevents: 2.3.3 + + semver@7.7.3: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + stackback@0.0.2: {} + + std-env@3.10.0: {} + + strip-final-newline@3.0.0: {} + + strip-json-comments@3.1.1: {} + + strip-literal@2.1.1: + dependencies: + js-tokens: 9.0.1 + + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.15 + ts-interface-checker: 0.1.13 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinypool@0.8.4: {} + + tinyspy@2.2.1: {} + + tree-kill@1.2.2: {} + + ts-api-utils@2.1.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-interface-checker@0.1.13: {} + + tsup@8.5.1(postcss@8.5.6)(typescript@5.9.3): + dependencies: + bundle-require: 5.1.0(esbuild@0.27.2) + cac: 6.7.14 + chokidar: 4.0.3 + consola: 3.4.2 + debug: 4.4.3 + esbuild: 0.27.2 + fix-dts-default-cjs-exports: 1.0.1 + joycon: 3.1.1 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(postcss@8.5.6) + resolve-from: 5.0.0 + rollup: 4.54.0 + source-map: 0.7.6 + sucrase: 3.35.1 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.6 + typescript: 5.9.3 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-detect@4.1.0: {} + + typescript@5.9.3: {} + + ufo@1.6.1: {} + + undici-types@6.21.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + vite-node@1.6.1(@types/node@20.19.27): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + pathe: 1.1.2 + picocolors: 1.1.1 + vite: 5.4.21(@types/node@20.19.27) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + vite@5.4.21(@types/node@20.19.27): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.6 + rollup: 4.54.0 + optionalDependencies: + '@types/node': 20.19.27 + fsevents: 2.3.3 + + vitest@1.6.1(@types/node@20.19.27): + dependencies: + '@vitest/expect': 1.6.1 + '@vitest/runner': 1.6.1 + '@vitest/snapshot': 1.6.1 + '@vitest/spy': 1.6.1 + '@vitest/utils': 1.6.1 + acorn-walk: 8.3.4 + chai: 4.5.0 + debug: 4.4.3 + execa: 8.0.1 + local-pkg: 0.5.1 + magic-string: 0.30.21 + pathe: 1.1.2 + picocolors: 1.1.1 + std-env: 3.10.0 + strip-literal: 2.1.1 + tinybench: 2.9.0 + tinypool: 0.8.4 + vite: 5.4.21(@types/node@20.19.27) + vite-node: 1.6.1(@types/node@20.19.27) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.19.27 + transitivePeerDependencies: + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + wrappy@1.0.2: {} + + yocto-queue@0.1.0: {} + + yocto-queue@1.2.2: {} diff --git a/sdks/nodejs-client/scripts/publish.sh b/sdks/nodejs-client/scripts/publish.sh new file mode 100755 index 0000000000..043cac046d --- /dev/null +++ b/sdks/nodejs-client/scripts/publish.sh @@ -0,0 +1,261 @@ +#!/usr/bin/env bash +# +# Dify Node.js SDK Publish Script +# ================================ +# A beautiful and reliable script to publish the SDK to npm +# +# Usage: +# ./scripts/publish.sh # Normal publish +# ./scripts/publish.sh --dry-run # Test without publishing +# ./scripts/publish.sh --skip-tests # Skip tests (not recommended) +# + +set -euo pipefail + +# ============================================================================ +# Colors and Formatting +# ============================================================================ +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +MAGENTA='\033[0;35m' +CYAN='\033[0;36m' +BOLD='\033[1m' +DIM='\033[2m' +NC='\033[0m' # No Color + +# ============================================================================ +# Helper Functions +# ============================================================================ +print_banner() { + echo -e "${CYAN}" + echo "╔═══════════════════════════════════════════════════════════════╗" + echo "║ ║" + echo "║ 🚀 Dify Node.js SDK Publish Script 🚀 ║" + echo "║ ║" + echo "╚═══════════════════════════════════════════════════════════════╝" + echo -e "${NC}" +} + +info() { + echo -e "${BLUE}ℹ ${NC}$1" +} + +success() { + echo -e "${GREEN}✔ ${NC}$1" +} + +warning() { + echo -e "${YELLOW}⚠ ${NC}$1" +} + +error() { + echo -e "${RED}✖ ${NC}$1" +} + +step() { + echo -e "\n${MAGENTA}▶ ${BOLD}$1${NC}" +} + +divider() { + echo -e "${DIM}─────────────────────────────────────────────────────────────────${NC}" +} + +# ============================================================================ +# Configuration +# ============================================================================ +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" + +DRY_RUN=false +SKIP_TESTS=false + +# Parse arguments +for arg in "$@"; do + case $arg in + --dry-run) + DRY_RUN=true + ;; + --skip-tests) + SKIP_TESTS=true + ;; + --help|-h) + echo "Usage: $0 [options]" + echo "" + echo "Options:" + echo " --dry-run Run without actually publishing" + echo " --skip-tests Skip running tests (not recommended)" + echo " --help, -h Show this help message" + exit 0 + ;; + esac +done + +# ============================================================================ +# Main Script +# ============================================================================ +main() { + print_banner + cd "$PROJECT_DIR" + + # Show mode + if [[ "$DRY_RUN" == true ]]; then + warning "Running in DRY-RUN mode - no actual publish will occur" + divider + fi + + # ======================================================================== + # Step 1: Environment Check + # ======================================================================== + step "Step 1/6: Checking environment..." + + # Check Node.js + if ! command -v node &> /dev/null; then + error "Node.js is not installed" + exit 1 + fi + NODE_VERSION=$(node -v) + success "Node.js: $NODE_VERSION" + + # Check npm + if ! command -v npm &> /dev/null; then + error "npm is not installed" + exit 1 + fi + NPM_VERSION=$(npm -v) + success "npm: v$NPM_VERSION" + + # Check pnpm (optional, for local dev) + if command -v pnpm &> /dev/null; then + PNPM_VERSION=$(pnpm -v) + success "pnpm: v$PNPM_VERSION" + else + info "pnpm not found (optional)" + fi + + # Check npm login status + if ! npm whoami &> /dev/null; then + error "Not logged in to npm. Run 'npm login' first." + exit 1 + fi + NPM_USER=$(npm whoami) + success "Logged in as: ${BOLD}$NPM_USER${NC}" + + # ======================================================================== + # Step 2: Read Package Info + # ======================================================================== + step "Step 2/6: Reading package info..." + + PACKAGE_NAME=$(node -p "require('./package.json').name") + PACKAGE_VERSION=$(node -p "require('./package.json').version") + + success "Package: ${BOLD}$PACKAGE_NAME${NC}" + success "Version: ${BOLD}$PACKAGE_VERSION${NC}" + + # Check if version already exists on npm + if npm view "$PACKAGE_NAME@$PACKAGE_VERSION" version &> /dev/null; then + error "Version $PACKAGE_VERSION already exists on npm!" + echo "" + info "Current published versions:" + npm view "$PACKAGE_NAME" versions --json 2>/dev/null | tail -5 + echo "" + warning "Please update the version in package.json before publishing." + exit 1 + fi + success "Version $PACKAGE_VERSION is available" + + # ======================================================================== + # Step 3: Install Dependencies + # ======================================================================== + step "Step 3/6: Installing dependencies..." + + if command -v pnpm &> /dev/null; then + pnpm install --frozen-lockfile 2>/dev/null || pnpm install + else + npm ci 2>/dev/null || npm install + fi + success "Dependencies installed" + + # ======================================================================== + # Step 4: Run Tests + # ======================================================================== + step "Step 4/6: Running tests..." + + if [[ "$SKIP_TESTS" == true ]]; then + warning "Skipping tests (--skip-tests flag)" + else + if command -v pnpm &> /dev/null; then + pnpm test + else + npm test + fi + success "All tests passed" + fi + + # ======================================================================== + # Step 5: Build + # ======================================================================== + step "Step 5/6: Building package..." + + # Clean previous build + rm -rf dist + + if command -v pnpm &> /dev/null; then + pnpm run build + else + npm run build + fi + success "Build completed" + + # Verify build output + if [[ ! -f "dist/index.js" ]]; then + error "Build failed - dist/index.js not found" + exit 1 + fi + if [[ ! -f "dist/index.d.ts" ]]; then + error "Build failed - dist/index.d.ts not found" + exit 1 + fi + success "Build output verified" + + # ======================================================================== + # Step 6: Publish + # ======================================================================== + step "Step 6/6: Publishing to npm..." + + divider + echo -e "${CYAN}Package contents:${NC}" + npm pack --dry-run 2>&1 | head -30 + divider + + if [[ "$DRY_RUN" == true ]]; then + warning "DRY-RUN: Skipping actual publish" + echo "" + info "To publish for real, run without --dry-run flag" + else + echo "" + echo -e "${YELLOW}About to publish ${BOLD}$PACKAGE_NAME@$PACKAGE_VERSION${NC}${YELLOW} to npm${NC}" + echo -e "${DIM}Press Enter to continue, or Ctrl+C to cancel...${NC}" + read -r + + npm publish --access public + + echo "" + success "🎉 Successfully published ${BOLD}$PACKAGE_NAME@$PACKAGE_VERSION${NC} to npm!" + echo "" + echo -e "${GREEN}Install with:${NC}" + echo -e " ${CYAN}npm install $PACKAGE_NAME${NC}" + echo -e " ${CYAN}pnpm add $PACKAGE_NAME${NC}" + echo -e " ${CYAN}yarn add $PACKAGE_NAME${NC}" + echo "" + echo -e "${GREEN}View on npm:${NC}" + echo -e " ${CYAN}https://www.npmjs.com/package/$PACKAGE_NAME${NC}" + fi + + divider + echo -e "${GREEN}${BOLD}✨ All done!${NC}" +} + +# Run main function +main "$@" diff --git a/sdks/nodejs-client/src/client/base.test.js b/sdks/nodejs-client/src/client/base.test.js new file mode 100644 index 0000000000..5e1b21d0f1 --- /dev/null +++ b/sdks/nodejs-client/src/client/base.test.js @@ -0,0 +1,175 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { DifyClient } from "./base"; +import { ValidationError } from "../errors/dify-error"; +import { createHttpClientWithSpies } from "../../tests/test-utils"; + +describe("DifyClient base", () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + + it("getRoot calls root endpoint", async () => { + const { client, request } = createHttpClientWithSpies(); + const dify = new DifyClient(client); + + await dify.getRoot(); + + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/", + }); + }); + + it("getApplicationParameters includes optional user", async () => { + const { client, request } = createHttpClientWithSpies(); + const dify = new DifyClient(client); + + await dify.getApplicationParameters(); + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/parameters", + query: undefined, + }); + + await dify.getApplicationParameters("user-1"); + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/parameters", + query: { user: "user-1" }, + }); + }); + + it("getMeta includes optional user", async () => { + const { client, request } = createHttpClientWithSpies(); + const dify = new DifyClient(client); + + await dify.getMeta("user-1"); + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/meta", + query: { user: "user-1" }, + }); + }); + + it("getInfo and getSite support optional user", async () => { + const { client, request } = createHttpClientWithSpies(); + const dify = new DifyClient(client); + + await dify.getInfo(); + await dify.getSite("user"); + + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/info", + query: undefined, + }); + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/site", + query: { user: "user" }, + }); + }); + + it("messageFeedback builds payload from request object", async () => { + const { client, request } = createHttpClientWithSpies(); + const dify = new DifyClient(client); + + await dify.messageFeedback({ + messageId: "msg", + user: "user", + rating: "like", + content: "good", + }); + + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/messages/msg/feedbacks", + data: { user: "user", rating: "like", content: "good" }, + }); + }); + + it("fileUpload appends user to form data", async () => { + const { client, request } = createHttpClientWithSpies(); + const dify = new DifyClient(client); + const form = { append: vi.fn(), getHeaders: () => ({}) }; + + await dify.fileUpload(form, "user"); + + expect(form.append).toHaveBeenCalledWith("user", "user"); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/files/upload", + data: form, + }); + }); + + it("filePreview uses arraybuffer response", async () => { + const { client, request } = createHttpClientWithSpies(); + const dify = new DifyClient(client); + + await dify.filePreview("file", "user", true); + + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/files/file/preview", + query: { user: "user", as_attachment: "true" }, + responseType: "arraybuffer", + }); + }); + + it("audioToText appends user and sends form", async () => { + const { client, request } = createHttpClientWithSpies(); + const dify = new DifyClient(client); + const form = { append: vi.fn(), getHeaders: () => ({}) }; + + await dify.audioToText(form, "user"); + + expect(form.append).toHaveBeenCalledWith("user", "user"); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/audio-to-text", + data: form, + }); + }); + + it("textToAudio supports streaming and message id", async () => { + const { client, request, requestBinaryStream } = createHttpClientWithSpies(); + const dify = new DifyClient(client); + + await dify.textToAudio({ + user: "user", + message_id: "msg", + streaming: true, + }); + + expect(requestBinaryStream).toHaveBeenCalledWith({ + method: "POST", + path: "/text-to-audio", + data: { + user: "user", + message_id: "msg", + streaming: true, + }, + }); + + await dify.textToAudio("hello", "user", false, "voice"); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/text-to-audio", + data: { + text: "hello", + user: "user", + streaming: false, + voice: "voice", + }, + responseType: "arraybuffer", + }); + }); + + it("textToAudio requires text or message id", async () => { + const { client } = createHttpClientWithSpies(); + const dify = new DifyClient(client); + + expect(() => dify.textToAudio({ user: "user" })).toThrow(ValidationError); + }); +}); diff --git a/sdks/nodejs-client/src/client/base.ts b/sdks/nodejs-client/src/client/base.ts new file mode 100644 index 0000000000..0fa535a488 --- /dev/null +++ b/sdks/nodejs-client/src/client/base.ts @@ -0,0 +1,284 @@ +import type { + BinaryStream, + DifyClientConfig, + DifyResponse, + MessageFeedbackRequest, + QueryParams, + RequestMethod, + TextToAudioRequest, +} from "../types/common"; +import { HttpClient } from "../http/client"; +import { ensureNonEmptyString, ensureRating } from "./validation"; +import { FileUploadError, ValidationError } from "../errors/dify-error"; +import { isFormData } from "../http/form-data"; + +const toConfig = ( + init: string | DifyClientConfig, + baseUrl?: string +): DifyClientConfig => { + if (typeof init === "string") { + return { + apiKey: init, + baseUrl, + }; + } + return init; +}; + +const appendUserToFormData = (form: unknown, user: string): void => { + if (!isFormData(form)) { + throw new FileUploadError("FormData is required for file uploads"); + } + if (typeof form.append === "function") { + form.append("user", user); + } +}; + +export class DifyClient { + protected http: HttpClient; + + constructor(config: string | DifyClientConfig | HttpClient, baseUrl?: string) { + if (config instanceof HttpClient) { + this.http = config; + } else { + this.http = new HttpClient(toConfig(config, baseUrl)); + } + } + + updateApiKey(apiKey: string): void { + ensureNonEmptyString(apiKey, "apiKey"); + this.http.updateApiKey(apiKey); + } + + getHttpClient(): HttpClient { + return this.http; + } + + sendRequest( + method: RequestMethod, + endpoint: string, + data: unknown = null, + params: QueryParams | null = null, + stream = false, + headerParams: Record<string, string> = {} + ): ReturnType<HttpClient["requestRaw"]> { + return this.http.requestRaw({ + method, + path: endpoint, + data, + query: params ?? undefined, + headers: headerParams, + responseType: stream ? "stream" : "json", + }); + } + + getRoot(): Promise<DifyResponse<unknown>> { + return this.http.request({ + method: "GET", + path: "/", + }); + } + + getApplicationParameters(user?: string): Promise<DifyResponse<unknown>> { + if (user) { + ensureNonEmptyString(user, "user"); + } + return this.http.request({ + method: "GET", + path: "/parameters", + query: user ? { user } : undefined, + }); + } + + async getParameters(user?: string): Promise<DifyResponse<unknown>> { + return this.getApplicationParameters(user); + } + + getMeta(user?: string): Promise<DifyResponse<unknown>> { + if (user) { + ensureNonEmptyString(user, "user"); + } + return this.http.request({ + method: "GET", + path: "/meta", + query: user ? { user } : undefined, + }); + } + + messageFeedback( + request: MessageFeedbackRequest + ): Promise<DifyResponse<Record<string, unknown>>>; + messageFeedback( + messageId: string, + rating: "like" | "dislike" | null, + user: string, + content?: string + ): Promise<DifyResponse<Record<string, unknown>>>; + messageFeedback( + messageIdOrRequest: string | MessageFeedbackRequest, + rating?: "like" | "dislike" | null, + user?: string, + content?: string + ): Promise<DifyResponse<Record<string, unknown>>> { + let messageId: string; + const payload: Record<string, unknown> = {}; + + if (typeof messageIdOrRequest === "string") { + messageId = messageIdOrRequest; + ensureNonEmptyString(messageId, "messageId"); + ensureNonEmptyString(user, "user"); + payload.user = user; + if (rating !== undefined && rating !== null) { + ensureRating(rating); + payload.rating = rating; + } + if (content !== undefined) { + payload.content = content; + } + } else { + const request = messageIdOrRequest; + messageId = request.messageId; + ensureNonEmptyString(messageId, "messageId"); + ensureNonEmptyString(request.user, "user"); + payload.user = request.user; + if (request.rating !== undefined && request.rating !== null) { + ensureRating(request.rating); + payload.rating = request.rating; + } + if (request.content !== undefined) { + payload.content = request.content; + } + } + + return this.http.request({ + method: "POST", + path: `/messages/${messageId}/feedbacks`, + data: payload, + }); + } + + getInfo(user?: string): Promise<DifyResponse<unknown>> { + if (user) { + ensureNonEmptyString(user, "user"); + } + return this.http.request({ + method: "GET", + path: "/info", + query: user ? { user } : undefined, + }); + } + + getSite(user?: string): Promise<DifyResponse<unknown>> { + if (user) { + ensureNonEmptyString(user, "user"); + } + return this.http.request({ + method: "GET", + path: "/site", + query: user ? { user } : undefined, + }); + } + + fileUpload(form: unknown, user: string): Promise<DifyResponse<unknown>> { + if (!isFormData(form)) { + throw new FileUploadError("FormData is required for file uploads"); + } + ensureNonEmptyString(user, "user"); + appendUserToFormData(form, user); + return this.http.request({ + method: "POST", + path: "/files/upload", + data: form, + }); + } + + filePreview( + fileId: string, + user: string, + asAttachment?: boolean + ): Promise<DifyResponse<Buffer>> { + ensureNonEmptyString(fileId, "fileId"); + ensureNonEmptyString(user, "user"); + return this.http.request<Buffer>({ + method: "GET", + path: `/files/${fileId}/preview`, + query: { + user, + as_attachment: asAttachment ? "true" : undefined, + }, + responseType: "arraybuffer", + }); + } + + audioToText(form: unknown, user: string): Promise<DifyResponse<unknown>> { + if (!isFormData(form)) { + throw new FileUploadError("FormData is required for audio uploads"); + } + ensureNonEmptyString(user, "user"); + appendUserToFormData(form, user); + return this.http.request({ + method: "POST", + path: "/audio-to-text", + data: form, + }); + } + + textToAudio( + request: TextToAudioRequest + ): Promise<DifyResponse<Buffer> | BinaryStream>; + textToAudio( + text: string, + user: string, + streaming?: boolean, + voice?: string + ): Promise<DifyResponse<Buffer> | BinaryStream>; + textToAudio( + textOrRequest: string | TextToAudioRequest, + user?: string, + streaming = false, + voice?: string + ): Promise<DifyResponse<Buffer> | BinaryStream> { + let payload: TextToAudioRequest; + + if (typeof textOrRequest === "string") { + ensureNonEmptyString(textOrRequest, "text"); + ensureNonEmptyString(user, "user"); + payload = { + text: textOrRequest, + user, + streaming, + }; + if (voice) { + payload.voice = voice; + } + } else { + payload = { ...textOrRequest }; + ensureNonEmptyString(payload.user, "user"); + if (payload.text !== undefined && payload.text !== null) { + ensureNonEmptyString(payload.text, "text"); + } + if (payload.message_id !== undefined && payload.message_id !== null) { + ensureNonEmptyString(payload.message_id, "messageId"); + } + if (!payload.text && !payload.message_id) { + throw new ValidationError("text or message_id is required"); + } + payload.streaming = payload.streaming ?? false; + } + + if (payload.streaming) { + return this.http.requestBinaryStream({ + method: "POST", + path: "/text-to-audio", + data: payload, + }); + } + + return this.http.request<Buffer>({ + method: "POST", + path: "/text-to-audio", + data: payload, + responseType: "arraybuffer", + }); + } +} diff --git a/sdks/nodejs-client/src/client/chat.test.js b/sdks/nodejs-client/src/client/chat.test.js new file mode 100644 index 0000000000..a97c9d4a5c --- /dev/null +++ b/sdks/nodejs-client/src/client/chat.test.js @@ -0,0 +1,239 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { ChatClient } from "./chat"; +import { ValidationError } from "../errors/dify-error"; +import { createHttpClientWithSpies } from "../../tests/test-utils"; + +describe("ChatClient", () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + + it("creates chat messages in blocking mode", async () => { + const { client, request } = createHttpClientWithSpies(); + const chat = new ChatClient(client); + + await chat.createChatMessage({ input: "x" }, "hello", "user", false, null); + + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/chat-messages", + data: { + inputs: { input: "x" }, + query: "hello", + user: "user", + response_mode: "blocking", + files: undefined, + }, + }); + }); + + it("creates chat messages in streaming mode", async () => { + const { client, requestStream } = createHttpClientWithSpies(); + const chat = new ChatClient(client); + + await chat.createChatMessage({ + inputs: { input: "x" }, + query: "hello", + user: "user", + response_mode: "streaming", + }); + + expect(requestStream).toHaveBeenCalledWith({ + method: "POST", + path: "/chat-messages", + data: { + inputs: { input: "x" }, + query: "hello", + user: "user", + response_mode: "streaming", + }, + }); + }); + + it("stops chat messages", async () => { + const { client, request } = createHttpClientWithSpies(); + const chat = new ChatClient(client); + + await chat.stopChatMessage("task", "user"); + await chat.stopMessage("task", "user"); + + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/chat-messages/task/stop", + data: { user: "user" }, + }); + }); + + it("gets suggested questions", async () => { + const { client, request } = createHttpClientWithSpies(); + const chat = new ChatClient(client); + + await chat.getSuggested("msg", "user"); + + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/messages/msg/suggested", + query: { user: "user" }, + }); + }); + + it("submits message feedback", async () => { + const { client, request } = createHttpClientWithSpies(); + const chat = new ChatClient(client); + + await chat.messageFeedback("msg", "like", "user", "good"); + await chat.messageFeedback({ + messageId: "msg", + user: "user", + rating: "dislike", + }); + + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/messages/msg/feedbacks", + data: { user: "user", rating: "like", content: "good" }, + }); + }); + + it("lists app feedbacks", async () => { + const { client, request } = createHttpClientWithSpies(); + const chat = new ChatClient(client); + + await chat.getAppFeedbacks(2, 5); + + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/app/feedbacks", + query: { page: 2, limit: 5 }, + }); + }); + + it("lists conversations and messages", async () => { + const { client, request } = createHttpClientWithSpies(); + const chat = new ChatClient(client); + + await chat.getConversations("user", "last", 10, "-updated_at"); + await chat.getConversationMessages("user", "conv", "first", 5); + + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/conversations", + query: { + user: "user", + last_id: "last", + limit: 10, + sort_by: "-updated_at", + }, + }); + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/messages", + query: { + user: "user", + conversation_id: "conv", + first_id: "first", + limit: 5, + }, + }); + }); + + it("renames conversations with optional auto-generate", async () => { + const { client, request } = createHttpClientWithSpies(); + const chat = new ChatClient(client); + + await chat.renameConversation("conv", "name", "user", false); + await chat.renameConversation("conv", "user", { autoGenerate: true }); + + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/conversations/conv/name", + data: { user: "user", auto_generate: false, name: "name" }, + }); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/conversations/conv/name", + data: { user: "user", auto_generate: true }, + }); + }); + + it("requires name when autoGenerate is false", async () => { + const { client } = createHttpClientWithSpies(); + const chat = new ChatClient(client); + + expect(() => + chat.renameConversation("conv", "", "user", false) + ).toThrow(ValidationError); + }); + + it("deletes conversations", async () => { + const { client, request } = createHttpClientWithSpies(); + const chat = new ChatClient(client); + + await chat.deleteConversation("conv", "user"); + + expect(request).toHaveBeenCalledWith({ + method: "DELETE", + path: "/conversations/conv", + data: { user: "user" }, + }); + }); + + it("manages conversation variables", async () => { + const { client, request } = createHttpClientWithSpies(); + const chat = new ChatClient(client); + + await chat.getConversationVariables("conv", "user", "last", 10, "name"); + await chat.updateConversationVariable("conv", "var", "user", "value"); + + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/conversations/conv/variables", + query: { + user: "user", + last_id: "last", + limit: 10, + variable_name: "name", + }, + }); + expect(request).toHaveBeenCalledWith({ + method: "PUT", + path: "/conversations/conv/variables/var", + data: { user: "user", value: "value" }, + }); + }); + + it("handles annotation APIs", async () => { + const { client, request } = createHttpClientWithSpies(); + const chat = new ChatClient(client); + + await chat.annotationReplyAction("enable", { + score_threshold: 0.5, + embedding_provider_name: "prov", + embedding_model_name: "model", + }); + await chat.getAnnotationReplyStatus("enable", "job"); + await chat.listAnnotations({ page: 1, limit: 10, keyword: "k" }); + await chat.createAnnotation({ question: "q", answer: "a" }); + await chat.updateAnnotation("id", { question: "q", answer: "a" }); + await chat.deleteAnnotation("id"); + + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/apps/annotation-reply/enable", + data: { + score_threshold: 0.5, + embedding_provider_name: "prov", + embedding_model_name: "model", + }, + }); + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/apps/annotation-reply/enable/status/job", + }); + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/apps/annotations", + query: { page: 1, limit: 10, keyword: "k" }, + }); + }); +}); diff --git a/sdks/nodejs-client/src/client/chat.ts b/sdks/nodejs-client/src/client/chat.ts new file mode 100644 index 0000000000..745c999552 --- /dev/null +++ b/sdks/nodejs-client/src/client/chat.ts @@ -0,0 +1,377 @@ +import { DifyClient } from "./base"; +import type { ChatMessageRequest, ChatMessageResponse } from "../types/chat"; +import type { + AnnotationCreateRequest, + AnnotationListOptions, + AnnotationReplyActionRequest, + AnnotationResponse, +} from "../types/annotation"; +import type { + DifyResponse, + DifyStream, + QueryParams, +} from "../types/common"; +import { + ensureNonEmptyString, + ensureOptionalInt, + ensureOptionalString, +} from "./validation"; + +export class ChatClient extends DifyClient { + createChatMessage( + request: ChatMessageRequest + ): Promise<DifyResponse<ChatMessageResponse> | DifyStream<ChatMessageResponse>>; + createChatMessage( + inputs: Record<string, unknown>, + query: string, + user: string, + stream?: boolean, + conversationId?: string | null, + files?: Array<Record<string, unknown>> | null + ): Promise<DifyResponse<ChatMessageResponse> | DifyStream<ChatMessageResponse>>; + createChatMessage( + inputOrRequest: ChatMessageRequest | Record<string, unknown>, + query?: string, + user?: string, + stream = false, + conversationId?: string | null, + files?: Array<Record<string, unknown>> | null + ): Promise<DifyResponse<ChatMessageResponse> | DifyStream<ChatMessageResponse>> { + let payload: ChatMessageRequest; + let shouldStream = stream; + + if (query === undefined && "user" in (inputOrRequest as ChatMessageRequest)) { + payload = inputOrRequest as ChatMessageRequest; + shouldStream = payload.response_mode === "streaming"; + } else { + ensureNonEmptyString(query, "query"); + ensureNonEmptyString(user, "user"); + payload = { + inputs: inputOrRequest as Record<string, unknown>, + query, + user, + response_mode: stream ? "streaming" : "blocking", + files, + }; + if (conversationId) { + payload.conversation_id = conversationId; + } + } + + ensureNonEmptyString(payload.user, "user"); + ensureNonEmptyString(payload.query, "query"); + + if (shouldStream) { + return this.http.requestStream<ChatMessageResponse>({ + method: "POST", + path: "/chat-messages", + data: payload, + }); + } + + return this.http.request<ChatMessageResponse>({ + method: "POST", + path: "/chat-messages", + data: payload, + }); + } + + stopChatMessage( + taskId: string, + user: string + ): Promise<DifyResponse<ChatMessageResponse>> { + ensureNonEmptyString(taskId, "taskId"); + ensureNonEmptyString(user, "user"); + return this.http.request<ChatMessageResponse>({ + method: "POST", + path: `/chat-messages/${taskId}/stop`, + data: { user }, + }); + } + + stopMessage( + taskId: string, + user: string + ): Promise<DifyResponse<ChatMessageResponse>> { + return this.stopChatMessage(taskId, user); + } + + getSuggested( + messageId: string, + user: string + ): Promise<DifyResponse<ChatMessageResponse>> { + ensureNonEmptyString(messageId, "messageId"); + ensureNonEmptyString(user, "user"); + return this.http.request<ChatMessageResponse>({ + method: "GET", + path: `/messages/${messageId}/suggested`, + query: { user }, + }); + } + + // Note: messageFeedback is inherited from DifyClient + + getAppFeedbacks( + page?: number, + limit?: number + ): Promise<DifyResponse<Record<string, unknown>>> { + ensureOptionalInt(page, "page"); + ensureOptionalInt(limit, "limit"); + return this.http.request({ + method: "GET", + path: "/app/feedbacks", + query: { + page, + limit, + }, + }); + } + + getConversations( + user: string, + lastId?: string | null, + limit?: number | null, + sortByOrPinned?: string | boolean | null + ): Promise<DifyResponse<Record<string, unknown>>> { + ensureNonEmptyString(user, "user"); + ensureOptionalString(lastId, "lastId"); + ensureOptionalInt(limit, "limit"); + + const params: QueryParams = { user }; + if (lastId) { + params.last_id = lastId; + } + if (limit) { + params.limit = limit; + } + if (typeof sortByOrPinned === "string") { + params.sort_by = sortByOrPinned; + } else if (typeof sortByOrPinned === "boolean") { + params.pinned = sortByOrPinned; + } + + return this.http.request({ + method: "GET", + path: "/conversations", + query: params, + }); + } + + getConversationMessages( + user: string, + conversationId: string, + firstId?: string | null, + limit?: number | null + ): Promise<DifyResponse<Record<string, unknown>>> { + ensureNonEmptyString(user, "user"); + ensureNonEmptyString(conversationId, "conversationId"); + ensureOptionalString(firstId, "firstId"); + ensureOptionalInt(limit, "limit"); + + const params: QueryParams = { user }; + params.conversation_id = conversationId; + if (firstId) { + params.first_id = firstId; + } + if (limit) { + params.limit = limit; + } + + return this.http.request({ + method: "GET", + path: "/messages", + query: params, + }); + } + + renameConversation( + conversationId: string, + name: string, + user: string, + autoGenerate?: boolean + ): Promise<DifyResponse<Record<string, unknown>>>; + renameConversation( + conversationId: string, + user: string, + options?: { name?: string | null; autoGenerate?: boolean } + ): Promise<DifyResponse<Record<string, unknown>>>; + renameConversation( + conversationId: string, + nameOrUser: string, + userOrOptions?: string | { name?: string | null; autoGenerate?: boolean }, + autoGenerate?: boolean + ): Promise<DifyResponse<Record<string, unknown>>> { + ensureNonEmptyString(conversationId, "conversationId"); + + let name: string | null | undefined; + let user: string; + let resolvedAutoGenerate: boolean; + + if (typeof userOrOptions === "string" || userOrOptions === undefined) { + name = nameOrUser; + user = userOrOptions ?? ""; + resolvedAutoGenerate = autoGenerate ?? false; + } else { + user = nameOrUser; + name = userOrOptions.name; + resolvedAutoGenerate = userOrOptions.autoGenerate ?? false; + } + + ensureNonEmptyString(user, "user"); + if (!resolvedAutoGenerate) { + ensureNonEmptyString(name, "name"); + } + + const payload: Record<string, unknown> = { + user, + auto_generate: resolvedAutoGenerate, + }; + if (typeof name === "string" && name.trim().length > 0) { + payload.name = name; + } + + return this.http.request({ + method: "POST", + path: `/conversations/${conversationId}/name`, + data: payload, + }); + } + + deleteConversation( + conversationId: string, + user: string + ): Promise<DifyResponse<Record<string, unknown>>> { + ensureNonEmptyString(conversationId, "conversationId"); + ensureNonEmptyString(user, "user"); + return this.http.request({ + method: "DELETE", + path: `/conversations/${conversationId}`, + data: { user }, + }); + } + + getConversationVariables( + conversationId: string, + user: string, + lastId?: string | null, + limit?: number | null, + variableName?: string | null + ): Promise<DifyResponse<Record<string, unknown>>> { + ensureNonEmptyString(conversationId, "conversationId"); + ensureNonEmptyString(user, "user"); + ensureOptionalString(lastId, "lastId"); + ensureOptionalInt(limit, "limit"); + ensureOptionalString(variableName, "variableName"); + + return this.http.request({ + method: "GET", + path: `/conversations/${conversationId}/variables`, + query: { + user, + last_id: lastId ?? undefined, + limit: limit ?? undefined, + variable_name: variableName ?? undefined, + }, + }); + } + + updateConversationVariable( + conversationId: string, + variableId: string, + user: string, + value: unknown + ): Promise<DifyResponse<Record<string, unknown>>> { + ensureNonEmptyString(conversationId, "conversationId"); + ensureNonEmptyString(variableId, "variableId"); + ensureNonEmptyString(user, "user"); + return this.http.request({ + method: "PUT", + path: `/conversations/${conversationId}/variables/${variableId}`, + data: { + user, + value, + }, + }); + } + + annotationReplyAction( + action: "enable" | "disable", + request: AnnotationReplyActionRequest + ): Promise<DifyResponse<AnnotationResponse>> { + ensureNonEmptyString(action, "action"); + ensureNonEmptyString(request.embedding_provider_name, "embedding_provider_name"); + ensureNonEmptyString(request.embedding_model_name, "embedding_model_name"); + return this.http.request({ + method: "POST", + path: `/apps/annotation-reply/${action}`, + data: request, + }); + } + + getAnnotationReplyStatus( + action: "enable" | "disable", + jobId: string + ): Promise<DifyResponse<AnnotationResponse>> { + ensureNonEmptyString(action, "action"); + ensureNonEmptyString(jobId, "jobId"); + return this.http.request({ + method: "GET", + path: `/apps/annotation-reply/${action}/status/${jobId}`, + }); + } + + listAnnotations( + options?: AnnotationListOptions + ): Promise<DifyResponse<AnnotationResponse>> { + ensureOptionalInt(options?.page, "page"); + ensureOptionalInt(options?.limit, "limit"); + ensureOptionalString(options?.keyword, "keyword"); + return this.http.request({ + method: "GET", + path: "/apps/annotations", + query: { + page: options?.page, + limit: options?.limit, + keyword: options?.keyword ?? undefined, + }, + }); + } + + createAnnotation( + request: AnnotationCreateRequest + ): Promise<DifyResponse<AnnotationResponse>> { + ensureNonEmptyString(request.question, "question"); + ensureNonEmptyString(request.answer, "answer"); + return this.http.request({ + method: "POST", + path: "/apps/annotations", + data: request, + }); + } + + updateAnnotation( + annotationId: string, + request: AnnotationCreateRequest + ): Promise<DifyResponse<AnnotationResponse>> { + ensureNonEmptyString(annotationId, "annotationId"); + ensureNonEmptyString(request.question, "question"); + ensureNonEmptyString(request.answer, "answer"); + return this.http.request({ + method: "PUT", + path: `/apps/annotations/${annotationId}`, + data: request, + }); + } + + deleteAnnotation( + annotationId: string + ): Promise<DifyResponse<AnnotationResponse>> { + ensureNonEmptyString(annotationId, "annotationId"); + return this.http.request({ + method: "DELETE", + path: `/apps/annotations/${annotationId}`, + }); + } + + // Note: audioToText is inherited from DifyClient +} diff --git a/sdks/nodejs-client/src/client/completion.test.js b/sdks/nodejs-client/src/client/completion.test.js new file mode 100644 index 0000000000..b79cf3fb8f --- /dev/null +++ b/sdks/nodejs-client/src/client/completion.test.js @@ -0,0 +1,83 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { CompletionClient } from "./completion"; +import { createHttpClientWithSpies } from "../../tests/test-utils"; + +describe("CompletionClient", () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + + it("creates completion messages in blocking mode", async () => { + const { client, request } = createHttpClientWithSpies(); + const completion = new CompletionClient(client); + + await completion.createCompletionMessage({ input: "x" }, "user", false); + + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/completion-messages", + data: { + inputs: { input: "x" }, + user: "user", + files: undefined, + response_mode: "blocking", + }, + }); + }); + + it("creates completion messages in streaming mode", async () => { + const { client, requestStream } = createHttpClientWithSpies(); + const completion = new CompletionClient(client); + + await completion.createCompletionMessage({ + inputs: { input: "x" }, + user: "user", + response_mode: "streaming", + }); + + expect(requestStream).toHaveBeenCalledWith({ + method: "POST", + path: "/completion-messages", + data: { + inputs: { input: "x" }, + user: "user", + response_mode: "streaming", + }, + }); + }); + + it("stops completion messages", async () => { + const { client, request } = createHttpClientWithSpies(); + const completion = new CompletionClient(client); + + await completion.stopCompletionMessage("task", "user"); + await completion.stop("task", "user"); + + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/completion-messages/task/stop", + data: { user: "user" }, + }); + }); + + it("supports deprecated runWorkflow", async () => { + const { client, request, requestStream } = createHttpClientWithSpies(); + const completion = new CompletionClient(client); + const warn = vi.spyOn(console, "warn").mockImplementation(() => {}); + + await completion.runWorkflow({ input: "x" }, "user", false); + await completion.runWorkflow({ input: "x" }, "user", true); + + expect(warn).toHaveBeenCalled(); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/workflows/run", + data: { inputs: { input: "x" }, user: "user", response_mode: "blocking" }, + }); + expect(requestStream).toHaveBeenCalledWith({ + method: "POST", + path: "/workflows/run", + data: { inputs: { input: "x" }, user: "user", response_mode: "streaming" }, + }); + }); +}); diff --git a/sdks/nodejs-client/src/client/completion.ts b/sdks/nodejs-client/src/client/completion.ts new file mode 100644 index 0000000000..9e39898e8b --- /dev/null +++ b/sdks/nodejs-client/src/client/completion.ts @@ -0,0 +1,111 @@ +import { DifyClient } from "./base"; +import type { CompletionRequest, CompletionResponse } from "../types/completion"; +import type { DifyResponse, DifyStream } from "../types/common"; +import { ensureNonEmptyString } from "./validation"; + +const warned = new Set<string>(); +const warnOnce = (message: string): void => { + if (warned.has(message)) { + return; + } + warned.add(message); + console.warn(message); +}; + +export class CompletionClient extends DifyClient { + createCompletionMessage( + request: CompletionRequest + ): Promise<DifyResponse<CompletionResponse> | DifyStream<CompletionResponse>>; + createCompletionMessage( + inputs: Record<string, unknown>, + user: string, + stream?: boolean, + files?: Array<Record<string, unknown>> | null + ): Promise<DifyResponse<CompletionResponse> | DifyStream<CompletionResponse>>; + createCompletionMessage( + inputOrRequest: CompletionRequest | Record<string, unknown>, + user?: string, + stream = false, + files?: Array<Record<string, unknown>> | null + ): Promise<DifyResponse<CompletionResponse> | DifyStream<CompletionResponse>> { + let payload: CompletionRequest; + let shouldStream = stream; + + if (user === undefined && "user" in (inputOrRequest as CompletionRequest)) { + payload = inputOrRequest as CompletionRequest; + shouldStream = payload.response_mode === "streaming"; + } else { + ensureNonEmptyString(user, "user"); + payload = { + inputs: inputOrRequest as Record<string, unknown>, + user, + files, + response_mode: stream ? "streaming" : "blocking", + }; + } + + ensureNonEmptyString(payload.user, "user"); + + if (shouldStream) { + return this.http.requestStream<CompletionResponse>({ + method: "POST", + path: "/completion-messages", + data: payload, + }); + } + + return this.http.request<CompletionResponse>({ + method: "POST", + path: "/completion-messages", + data: payload, + }); + } + + stopCompletionMessage( + taskId: string, + user: string + ): Promise<DifyResponse<CompletionResponse>> { + ensureNonEmptyString(taskId, "taskId"); + ensureNonEmptyString(user, "user"); + return this.http.request<CompletionResponse>({ + method: "POST", + path: `/completion-messages/${taskId}/stop`, + data: { user }, + }); + } + + stop( + taskId: string, + user: string + ): Promise<DifyResponse<CompletionResponse>> { + return this.stopCompletionMessage(taskId, user); + } + + runWorkflow( + inputs: Record<string, unknown>, + user: string, + stream = false + ): Promise<DifyResponse<Record<string, unknown>> | DifyStream<Record<string, unknown>>> { + warnOnce( + "CompletionClient.runWorkflow is deprecated. Use WorkflowClient.run instead." + ); + ensureNonEmptyString(user, "user"); + const payload = { + inputs, + user, + response_mode: stream ? "streaming" : "blocking", + }; + if (stream) { + return this.http.requestStream<Record<string, unknown>>({ + method: "POST", + path: "/workflows/run", + data: payload, + }); + } + return this.http.request<Record<string, unknown>>({ + method: "POST", + path: "/workflows/run", + data: payload, + }); + } +} diff --git a/sdks/nodejs-client/src/client/knowledge-base.test.js b/sdks/nodejs-client/src/client/knowledge-base.test.js new file mode 100644 index 0000000000..4381b39e56 --- /dev/null +++ b/sdks/nodejs-client/src/client/knowledge-base.test.js @@ -0,0 +1,249 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { KnowledgeBaseClient } from "./knowledge-base"; +import { createHttpClientWithSpies } from "../../tests/test-utils"; + +describe("KnowledgeBaseClient", () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + + it("handles dataset and tag operations", async () => { + const { client, request } = createHttpClientWithSpies(); + const kb = new KnowledgeBaseClient(client); + + await kb.listDatasets({ + page: 1, + limit: 2, + keyword: "k", + includeAll: true, + tagIds: ["t1"], + }); + await kb.createDataset({ name: "dataset" }); + await kb.getDataset("ds"); + await kb.updateDataset("ds", { name: "new" }); + await kb.deleteDataset("ds"); + await kb.updateDocumentStatus("ds", "enable", ["doc1"]); + + await kb.listTags(); + await kb.createTag({ name: "tag" }); + await kb.updateTag({ tag_id: "tag", name: "name" }); + await kb.deleteTag({ tag_id: "tag" }); + await kb.bindTags({ tag_ids: ["tag"], target_id: "doc" }); + await kb.unbindTags({ tag_id: "tag", target_id: "doc" }); + await kb.getDatasetTags("ds"); + + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/datasets", + query: { + page: 1, + limit: 2, + keyword: "k", + include_all: true, + tag_ids: ["t1"], + }, + }); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/datasets", + data: { name: "dataset" }, + }); + expect(request).toHaveBeenCalledWith({ + method: "PATCH", + path: "/datasets/ds", + data: { name: "new" }, + }); + expect(request).toHaveBeenCalledWith({ + method: "PATCH", + path: "/datasets/ds/documents/status/enable", + data: { document_ids: ["doc1"] }, + }); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/datasets/tags/binding", + data: { tag_ids: ["tag"], target_id: "doc" }, + }); + }); + + it("handles document operations", async () => { + const { client, request } = createHttpClientWithSpies(); + const kb = new KnowledgeBaseClient(client); + const form = { append: vi.fn(), getHeaders: () => ({}) }; + + await kb.createDocumentByText("ds", { name: "doc", text: "text" }); + await kb.updateDocumentByText("ds", "doc", { name: "doc2" }); + await kb.createDocumentByFile("ds", form); + await kb.updateDocumentByFile("ds", "doc", form); + await kb.listDocuments("ds", { page: 1, limit: 20, keyword: "k" }); + await kb.getDocument("ds", "doc", { metadata: "all" }); + await kb.deleteDocument("ds", "doc"); + await kb.getDocumentIndexingStatus("ds", "batch"); + + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/datasets/ds/document/create_by_text", + data: { name: "doc", text: "text" }, + }); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/datasets/ds/documents/doc/update_by_text", + data: { name: "doc2" }, + }); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/datasets/ds/document/create_by_file", + data: form, + }); + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/datasets/ds/documents", + query: { page: 1, limit: 20, keyword: "k", status: undefined }, + }); + }); + + it("handles segments and child chunks", async () => { + const { client, request } = createHttpClientWithSpies(); + const kb = new KnowledgeBaseClient(client); + + await kb.createSegments("ds", "doc", { segments: [{ content: "x" }] }); + await kb.listSegments("ds", "doc", { page: 1, limit: 10, keyword: "k" }); + await kb.getSegment("ds", "doc", "seg"); + await kb.updateSegment("ds", "doc", "seg", { + segment: { content: "y" }, + }); + await kb.deleteSegment("ds", "doc", "seg"); + + await kb.createChildChunk("ds", "doc", "seg", { content: "c" }); + await kb.listChildChunks("ds", "doc", "seg", { page: 1, limit: 10 }); + await kb.updateChildChunk("ds", "doc", "seg", "child", { + content: "c2", + }); + await kb.deleteChildChunk("ds", "doc", "seg", "child"); + + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/datasets/ds/documents/doc/segments", + data: { segments: [{ content: "x" }] }, + }); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/datasets/ds/documents/doc/segments/seg", + data: { segment: { content: "y" } }, + }); + expect(request).toHaveBeenCalledWith({ + method: "PATCH", + path: "/datasets/ds/documents/doc/segments/seg/child_chunks/child", + data: { content: "c2" }, + }); + }); + + it("handles metadata and retrieval", async () => { + const { client, request } = createHttpClientWithSpies(); + const kb = new KnowledgeBaseClient(client); + + await kb.listMetadata("ds"); + await kb.createMetadata("ds", { name: "m", type: "string" }); + await kb.updateMetadata("ds", "mid", { name: "m2" }); + await kb.deleteMetadata("ds", "mid"); + await kb.listBuiltInMetadata("ds"); + await kb.updateBuiltInMetadata("ds", "enable"); + await kb.updateDocumentsMetadata("ds", { + operation_data: [ + { document_id: "doc", metadata_list: [{ id: "m", name: "n" }] }, + ], + }); + await kb.hitTesting("ds", { query: "q" }); + await kb.retrieve("ds", { query: "q" }); + + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/datasets/ds/metadata", + }); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/datasets/ds/metadata", + data: { name: "m", type: "string" }, + }); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/datasets/ds/hit-testing", + data: { query: "q" }, + }); + }); + + it("handles pipeline operations", async () => { + const { client, request, requestStream } = createHttpClientWithSpies(); + const kb = new KnowledgeBaseClient(client); + const warn = vi.spyOn(console, "warn").mockImplementation(() => {}); + const form = { append: vi.fn(), getHeaders: () => ({}) }; + + await kb.listDatasourcePlugins("ds", { isPublished: true }); + await kb.runDatasourceNode("ds", "node", { + inputs: { input: "x" }, + datasource_type: "custom", + is_published: true, + }); + await kb.runPipeline("ds", { + inputs: { input: "x" }, + datasource_type: "custom", + datasource_info_list: [], + start_node_id: "start", + is_published: true, + response_mode: "streaming", + }); + await kb.runPipeline("ds", { + inputs: { input: "x" }, + datasource_type: "custom", + datasource_info_list: [], + start_node_id: "start", + is_published: true, + response_mode: "blocking", + }); + await kb.uploadPipelineFile(form); + + expect(warn).toHaveBeenCalled(); + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/datasets/ds/pipeline/datasource-plugins", + query: { is_published: true }, + }); + expect(requestStream).toHaveBeenCalledWith({ + method: "POST", + path: "/datasets/ds/pipeline/datasource/nodes/node/run", + data: { + inputs: { input: "x" }, + datasource_type: "custom", + is_published: true, + }, + }); + expect(requestStream).toHaveBeenCalledWith({ + method: "POST", + path: "/datasets/ds/pipeline/run", + data: { + inputs: { input: "x" }, + datasource_type: "custom", + datasource_info_list: [], + start_node_id: "start", + is_published: true, + response_mode: "streaming", + }, + }); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/datasets/ds/pipeline/run", + data: { + inputs: { input: "x" }, + datasource_type: "custom", + datasource_info_list: [], + start_node_id: "start", + is_published: true, + response_mode: "blocking", + }, + }); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/datasets/pipeline/file-upload", + data: form, + }); + }); +}); diff --git a/sdks/nodejs-client/src/client/knowledge-base.ts b/sdks/nodejs-client/src/client/knowledge-base.ts new file mode 100644 index 0000000000..7a0e39898b --- /dev/null +++ b/sdks/nodejs-client/src/client/knowledge-base.ts @@ -0,0 +1,706 @@ +import { DifyClient } from "./base"; +import type { + DatasetCreateRequest, + DatasetListOptions, + DatasetTagBindingRequest, + DatasetTagCreateRequest, + DatasetTagDeleteRequest, + DatasetTagUnbindingRequest, + DatasetTagUpdateRequest, + DatasetUpdateRequest, + DocumentGetOptions, + DocumentListOptions, + DocumentStatusAction, + DocumentTextCreateRequest, + DocumentTextUpdateRequest, + SegmentCreateRequest, + SegmentListOptions, + SegmentUpdateRequest, + ChildChunkCreateRequest, + ChildChunkListOptions, + ChildChunkUpdateRequest, + MetadataCreateRequest, + MetadataOperationRequest, + MetadataUpdateRequest, + HitTestingRequest, + DatasourcePluginListOptions, + DatasourceNodeRunRequest, + PipelineRunRequest, + KnowledgeBaseResponse, + PipelineStreamEvent, +} from "../types/knowledge-base"; +import type { DifyResponse, DifyStream, QueryParams } from "../types/common"; +import { + ensureNonEmptyString, + ensureOptionalBoolean, + ensureOptionalInt, + ensureOptionalString, + ensureStringArray, +} from "./validation"; +import { FileUploadError, ValidationError } from "../errors/dify-error"; +import { isFormData } from "../http/form-data"; + +const warned = new Set<string>(); +const warnOnce = (message: string): void => { + if (warned.has(message)) { + return; + } + warned.add(message); + console.warn(message); +}; + +const ensureFormData = (form: unknown, context: string): void => { + if (!isFormData(form)) { + throw new FileUploadError(`${context} requires FormData`); + } +}; + +const ensureNonEmptyArray = (value: unknown, name: string): void => { + if (!Array.isArray(value) || value.length === 0) { + throw new ValidationError(`${name} must be a non-empty array`); + } +}; + +const warnPipelineRoutes = (): void => { + warnOnce( + "RAG pipeline endpoints may be unavailable unless the service API registers dataset/rag_pipeline routes." + ); +}; + +export class KnowledgeBaseClient extends DifyClient { + async listDatasets( + options?: DatasetListOptions + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureOptionalInt(options?.page, "page"); + ensureOptionalInt(options?.limit, "limit"); + ensureOptionalString(options?.keyword, "keyword"); + ensureOptionalBoolean(options?.includeAll, "includeAll"); + + const query: QueryParams = { + page: options?.page, + limit: options?.limit, + keyword: options?.keyword ?? undefined, + include_all: options?.includeAll ?? undefined, + }; + + if (options?.tagIds && options.tagIds.length > 0) { + ensureStringArray(options.tagIds, "tagIds"); + query.tag_ids = options.tagIds; + } + + return this.http.request({ + method: "GET", + path: "/datasets", + query, + }); + } + + async createDataset( + request: DatasetCreateRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(request.name, "name"); + return this.http.request({ + method: "POST", + path: "/datasets", + data: request, + }); + } + + async getDataset(datasetId: string): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + return this.http.request({ + method: "GET", + path: `/datasets/${datasetId}`, + }); + } + + async updateDataset( + datasetId: string, + request: DatasetUpdateRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + if (request.name !== undefined && request.name !== null) { + ensureNonEmptyString(request.name, "name"); + } + return this.http.request({ + method: "PATCH", + path: `/datasets/${datasetId}`, + data: request, + }); + } + + async deleteDataset(datasetId: string): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + return this.http.request({ + method: "DELETE", + path: `/datasets/${datasetId}`, + }); + } + + async updateDocumentStatus( + datasetId: string, + action: DocumentStatusAction, + documentIds: string[] + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(action, "action"); + ensureStringArray(documentIds, "documentIds"); + return this.http.request({ + method: "PATCH", + path: `/datasets/${datasetId}/documents/status/${action}`, + data: { + document_ids: documentIds, + }, + }); + } + + async listTags(): Promise<DifyResponse<KnowledgeBaseResponse>> { + return this.http.request({ + method: "GET", + path: "/datasets/tags", + }); + } + + async createTag( + request: DatasetTagCreateRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(request.name, "name"); + return this.http.request({ + method: "POST", + path: "/datasets/tags", + data: request, + }); + } + + async updateTag( + request: DatasetTagUpdateRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(request.tag_id, "tag_id"); + ensureNonEmptyString(request.name, "name"); + return this.http.request({ + method: "PATCH", + path: "/datasets/tags", + data: request, + }); + } + + async deleteTag( + request: DatasetTagDeleteRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(request.tag_id, "tag_id"); + return this.http.request({ + method: "DELETE", + path: "/datasets/tags", + data: request, + }); + } + + async bindTags( + request: DatasetTagBindingRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureStringArray(request.tag_ids, "tag_ids"); + ensureNonEmptyString(request.target_id, "target_id"); + return this.http.request({ + method: "POST", + path: "/datasets/tags/binding", + data: request, + }); + } + + async unbindTags( + request: DatasetTagUnbindingRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(request.tag_id, "tag_id"); + ensureNonEmptyString(request.target_id, "target_id"); + return this.http.request({ + method: "POST", + path: "/datasets/tags/unbinding", + data: request, + }); + } + + async getDatasetTags( + datasetId: string + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + return this.http.request({ + method: "GET", + path: `/datasets/${datasetId}/tags`, + }); + } + + async createDocumentByText( + datasetId: string, + request: DocumentTextCreateRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(request.name, "name"); + ensureNonEmptyString(request.text, "text"); + return this.http.request({ + method: "POST", + path: `/datasets/${datasetId}/document/create_by_text`, + data: request, + }); + } + + async updateDocumentByText( + datasetId: string, + documentId: string, + request: DocumentTextUpdateRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(documentId, "documentId"); + if (request.name !== undefined && request.name !== null) { + ensureNonEmptyString(request.name, "name"); + } + return this.http.request({ + method: "POST", + path: `/datasets/${datasetId}/documents/${documentId}/update_by_text`, + data: request, + }); + } + + async createDocumentByFile( + datasetId: string, + form: unknown + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureFormData(form, "createDocumentByFile"); + return this.http.request({ + method: "POST", + path: `/datasets/${datasetId}/document/create_by_file`, + data: form, + }); + } + + async updateDocumentByFile( + datasetId: string, + documentId: string, + form: unknown + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(documentId, "documentId"); + ensureFormData(form, "updateDocumentByFile"); + return this.http.request({ + method: "POST", + path: `/datasets/${datasetId}/documents/${documentId}/update_by_file`, + data: form, + }); + } + + async listDocuments( + datasetId: string, + options?: DocumentListOptions + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureOptionalInt(options?.page, "page"); + ensureOptionalInt(options?.limit, "limit"); + ensureOptionalString(options?.keyword, "keyword"); + ensureOptionalString(options?.status, "status"); + + return this.http.request({ + method: "GET", + path: `/datasets/${datasetId}/documents`, + query: { + page: options?.page, + limit: options?.limit, + keyword: options?.keyword ?? undefined, + status: options?.status ?? undefined, + }, + }); + } + + async getDocument( + datasetId: string, + documentId: string, + options?: DocumentGetOptions + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(documentId, "documentId"); + if (options?.metadata) { + const allowed = new Set(["all", "only", "without"]); + if (!allowed.has(options.metadata)) { + throw new ValidationError("metadata must be one of all, only, without"); + } + } + return this.http.request({ + method: "GET", + path: `/datasets/${datasetId}/documents/${documentId}`, + query: { + metadata: options?.metadata ?? undefined, + }, + }); + } + + async deleteDocument( + datasetId: string, + documentId: string + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(documentId, "documentId"); + return this.http.request({ + method: "DELETE", + path: `/datasets/${datasetId}/documents/${documentId}`, + }); + } + + async getDocumentIndexingStatus( + datasetId: string, + batch: string + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(batch, "batch"); + return this.http.request({ + method: "GET", + path: `/datasets/${datasetId}/documents/${batch}/indexing-status`, + }); + } + + async createSegments( + datasetId: string, + documentId: string, + request: SegmentCreateRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(documentId, "documentId"); + ensureNonEmptyArray(request.segments, "segments"); + return this.http.request({ + method: "POST", + path: `/datasets/${datasetId}/documents/${documentId}/segments`, + data: request, + }); + } + + async listSegments( + datasetId: string, + documentId: string, + options?: SegmentListOptions + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(documentId, "documentId"); + ensureOptionalInt(options?.page, "page"); + ensureOptionalInt(options?.limit, "limit"); + ensureOptionalString(options?.keyword, "keyword"); + if (options?.status && options.status.length > 0) { + ensureStringArray(options.status, "status"); + } + + const query: QueryParams = { + page: options?.page, + limit: options?.limit, + keyword: options?.keyword ?? undefined, + }; + if (options?.status && options.status.length > 0) { + query.status = options.status; + } + + return this.http.request({ + method: "GET", + path: `/datasets/${datasetId}/documents/${documentId}/segments`, + query, + }); + } + + async getSegment( + datasetId: string, + documentId: string, + segmentId: string + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(documentId, "documentId"); + ensureNonEmptyString(segmentId, "segmentId"); + return this.http.request({ + method: "GET", + path: `/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}`, + }); + } + + async updateSegment( + datasetId: string, + documentId: string, + segmentId: string, + request: SegmentUpdateRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(documentId, "documentId"); + ensureNonEmptyString(segmentId, "segmentId"); + return this.http.request({ + method: "POST", + path: `/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}`, + data: request, + }); + } + + async deleteSegment( + datasetId: string, + documentId: string, + segmentId: string + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(documentId, "documentId"); + ensureNonEmptyString(segmentId, "segmentId"); + return this.http.request({ + method: "DELETE", + path: `/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}`, + }); + } + + async createChildChunk( + datasetId: string, + documentId: string, + segmentId: string, + request: ChildChunkCreateRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(documentId, "documentId"); + ensureNonEmptyString(segmentId, "segmentId"); + ensureNonEmptyString(request.content, "content"); + return this.http.request({ + method: "POST", + path: `/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/child_chunks`, + data: request, + }); + } + + async listChildChunks( + datasetId: string, + documentId: string, + segmentId: string, + options?: ChildChunkListOptions + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(documentId, "documentId"); + ensureNonEmptyString(segmentId, "segmentId"); + ensureOptionalInt(options?.page, "page"); + ensureOptionalInt(options?.limit, "limit"); + ensureOptionalString(options?.keyword, "keyword"); + + return this.http.request({ + method: "GET", + path: `/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/child_chunks`, + query: { + page: options?.page, + limit: options?.limit, + keyword: options?.keyword ?? undefined, + }, + }); + } + + async updateChildChunk( + datasetId: string, + documentId: string, + segmentId: string, + childChunkId: string, + request: ChildChunkUpdateRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(documentId, "documentId"); + ensureNonEmptyString(segmentId, "segmentId"); + ensureNonEmptyString(childChunkId, "childChunkId"); + ensureNonEmptyString(request.content, "content"); + return this.http.request({ + method: "PATCH", + path: `/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/child_chunks/${childChunkId}`, + data: request, + }); + } + + async deleteChildChunk( + datasetId: string, + documentId: string, + segmentId: string, + childChunkId: string + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(documentId, "documentId"); + ensureNonEmptyString(segmentId, "segmentId"); + ensureNonEmptyString(childChunkId, "childChunkId"); + return this.http.request({ + method: "DELETE", + path: `/datasets/${datasetId}/documents/${documentId}/segments/${segmentId}/child_chunks/${childChunkId}`, + }); + } + + async listMetadata( + datasetId: string + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + return this.http.request({ + method: "GET", + path: `/datasets/${datasetId}/metadata`, + }); + } + + async createMetadata( + datasetId: string, + request: MetadataCreateRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(request.name, "name"); + ensureNonEmptyString(request.type, "type"); + return this.http.request({ + method: "POST", + path: `/datasets/${datasetId}/metadata`, + data: request, + }); + } + + async updateMetadata( + datasetId: string, + metadataId: string, + request: MetadataUpdateRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(metadataId, "metadataId"); + ensureNonEmptyString(request.name, "name"); + return this.http.request({ + method: "PATCH", + path: `/datasets/${datasetId}/metadata/${metadataId}`, + data: request, + }); + } + + async deleteMetadata( + datasetId: string, + metadataId: string + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(metadataId, "metadataId"); + return this.http.request({ + method: "DELETE", + path: `/datasets/${datasetId}/metadata/${metadataId}`, + }); + } + + async listBuiltInMetadata( + datasetId: string + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + return this.http.request({ + method: "GET", + path: `/datasets/${datasetId}/metadata/built-in`, + }); + } + + async updateBuiltInMetadata( + datasetId: string, + action: "enable" | "disable" + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(action, "action"); + return this.http.request({ + method: "POST", + path: `/datasets/${datasetId}/metadata/built-in/${action}`, + }); + } + + async updateDocumentsMetadata( + datasetId: string, + request: MetadataOperationRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyArray(request.operation_data, "operation_data"); + return this.http.request({ + method: "POST", + path: `/datasets/${datasetId}/documents/metadata`, + data: request, + }); + } + + async hitTesting( + datasetId: string, + request: HitTestingRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + if (request.query !== undefined && request.query !== null) { + ensureOptionalString(request.query, "query"); + } + if (request.attachment_ids && request.attachment_ids.length > 0) { + ensureStringArray(request.attachment_ids, "attachment_ids"); + } + return this.http.request({ + method: "POST", + path: `/datasets/${datasetId}/hit-testing`, + data: request, + }); + } + + async retrieve( + datasetId: string, + request: HitTestingRequest + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + ensureNonEmptyString(datasetId, "datasetId"); + return this.http.request({ + method: "POST", + path: `/datasets/${datasetId}/retrieve`, + data: request, + }); + } + + async listDatasourcePlugins( + datasetId: string, + options?: DatasourcePluginListOptions + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + warnPipelineRoutes(); + ensureNonEmptyString(datasetId, "datasetId"); + ensureOptionalBoolean(options?.isPublished, "isPublished"); + return this.http.request({ + method: "GET", + path: `/datasets/${datasetId}/pipeline/datasource-plugins`, + query: { + is_published: options?.isPublished ?? undefined, + }, + }); + } + + async runDatasourceNode( + datasetId: string, + nodeId: string, + request: DatasourceNodeRunRequest + ): Promise<DifyStream<PipelineStreamEvent>> { + warnPipelineRoutes(); + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(nodeId, "nodeId"); + ensureNonEmptyString(request.datasource_type, "datasource_type"); + return this.http.requestStream<PipelineStreamEvent>({ + method: "POST", + path: `/datasets/${datasetId}/pipeline/datasource/nodes/${nodeId}/run`, + data: request, + }); + } + + async runPipeline( + datasetId: string, + request: PipelineRunRequest + ): Promise<DifyResponse<KnowledgeBaseResponse> | DifyStream<PipelineStreamEvent>> { + warnPipelineRoutes(); + ensureNonEmptyString(datasetId, "datasetId"); + ensureNonEmptyString(request.datasource_type, "datasource_type"); + ensureNonEmptyString(request.start_node_id, "start_node_id"); + const shouldStream = request.response_mode === "streaming"; + if (shouldStream) { + return this.http.requestStream<PipelineStreamEvent>({ + method: "POST", + path: `/datasets/${datasetId}/pipeline/run`, + data: request, + }); + } + return this.http.request<KnowledgeBaseResponse>({ + method: "POST", + path: `/datasets/${datasetId}/pipeline/run`, + data: request, + }); + } + + async uploadPipelineFile( + form: unknown + ): Promise<DifyResponse<KnowledgeBaseResponse>> { + warnPipelineRoutes(); + ensureFormData(form, "uploadPipelineFile"); + return this.http.request({ + method: "POST", + path: "/datasets/pipeline/file-upload", + data: form, + }); + } +} diff --git a/sdks/nodejs-client/src/client/validation.test.js b/sdks/nodejs-client/src/client/validation.test.js new file mode 100644 index 0000000000..65bfa471a6 --- /dev/null +++ b/sdks/nodejs-client/src/client/validation.test.js @@ -0,0 +1,91 @@ +import { describe, expect, it } from "vitest"; +import { + ensureNonEmptyString, + ensureOptionalBoolean, + ensureOptionalInt, + ensureOptionalString, + ensureOptionalStringArray, + ensureRating, + ensureStringArray, + validateParams, +} from "./validation"; + +const makeLongString = (length) => "a".repeat(length); + +describe("validation utilities", () => { + it("ensureNonEmptyString throws on empty or whitespace", () => { + expect(() => ensureNonEmptyString("", "name")).toThrow(); + expect(() => ensureNonEmptyString(" ", "name")).toThrow(); + }); + + it("ensureNonEmptyString throws on overly long strings", () => { + expect(() => + ensureNonEmptyString(makeLongString(10001), "name") + ).toThrow(); + }); + + it("ensureOptionalString ignores undefined and validates when set", () => { + expect(() => ensureOptionalString(undefined, "opt")).not.toThrow(); + expect(() => ensureOptionalString("", "opt")).toThrow(); + }); + + it("ensureOptionalString throws on overly long strings", () => { + expect(() => ensureOptionalString(makeLongString(10001), "opt")).toThrow(); + }); + + it("ensureOptionalInt validates integer", () => { + expect(() => ensureOptionalInt(undefined, "limit")).not.toThrow(); + expect(() => ensureOptionalInt(1.2, "limit")).toThrow(); + }); + + it("ensureOptionalBoolean validates boolean", () => { + expect(() => ensureOptionalBoolean(undefined, "flag")).not.toThrow(); + expect(() => ensureOptionalBoolean("yes", "flag")).toThrow(); + }); + + it("ensureStringArray enforces size and content", () => { + expect(() => ensureStringArray([], "items")).toThrow(); + expect(() => ensureStringArray([""], "items")).toThrow(); + expect(() => + ensureStringArray(Array.from({ length: 1001 }, () => "a"), "items") + ).toThrow(); + expect(() => ensureStringArray(["ok"], "items")).not.toThrow(); + }); + + it("ensureOptionalStringArray ignores undefined", () => { + expect(() => ensureOptionalStringArray(undefined, "tags")).not.toThrow(); + }); + + it("ensureOptionalStringArray validates when set", () => { + expect(() => ensureOptionalStringArray(["valid"], "tags")).not.toThrow(); + expect(() => ensureOptionalStringArray([], "tags")).toThrow(); + expect(() => ensureOptionalStringArray([""], "tags")).toThrow(); + }); + + it("ensureRating validates allowed values", () => { + expect(() => ensureRating(undefined)).not.toThrow(); + expect(() => ensureRating("like")).not.toThrow(); + expect(() => ensureRating("bad")).toThrow(); + }); + + it("validateParams enforces generic rules", () => { + expect(() => validateParams({ user: 123 })).toThrow(); + expect(() => validateParams({ rating: "bad" })).toThrow(); + expect(() => validateParams({ page: 1.1 })).toThrow(); + expect(() => validateParams({ files: "bad" })).toThrow(); + // Empty strings are allowed for optional params (e.g., keyword: "" means no filter) + expect(() => validateParams({ keyword: "" })).not.toThrow(); + expect(() => validateParams({ name: makeLongString(10001) })).toThrow(); + expect(() => + validateParams({ items: Array.from({ length: 1001 }, () => "a") }) + ).toThrow(); + expect(() => + validateParams({ + data: Object.fromEntries( + Array.from({ length: 101 }, (_, i) => [String(i), i]) + ), + }) + ).toThrow(); + expect(() => validateParams({ user: "u", page: 1 })).not.toThrow(); + }); +}); diff --git a/sdks/nodejs-client/src/client/validation.ts b/sdks/nodejs-client/src/client/validation.ts new file mode 100644 index 0000000000..6aeec36bdc --- /dev/null +++ b/sdks/nodejs-client/src/client/validation.ts @@ -0,0 +1,136 @@ +import { ValidationError } from "../errors/dify-error"; + +const MAX_STRING_LENGTH = 10000; +const MAX_LIST_LENGTH = 1000; +const MAX_DICT_LENGTH = 100; + +export function ensureNonEmptyString( + value: unknown, + name: string +): asserts value is string { + if (typeof value !== "string" || value.trim().length === 0) { + throw new ValidationError(`${name} must be a non-empty string`); + } + if (value.length > MAX_STRING_LENGTH) { + throw new ValidationError( + `${name} exceeds maximum length of ${MAX_STRING_LENGTH} characters` + ); + } +} + +/** + * Validates optional string fields that must be non-empty when provided. + * Use this for fields like `name` that are optional but should not be empty strings. + * + * For filter parameters that accept empty strings (e.g., `keyword: ""`), + * use `validateParams` which allows empty strings for optional params. + */ +export function ensureOptionalString(value: unknown, name: string): void { + if (value === undefined || value === null) { + return; + } + if (typeof value !== "string" || value.trim().length === 0) { + throw new ValidationError(`${name} must be a non-empty string when set`); + } + if (value.length > MAX_STRING_LENGTH) { + throw new ValidationError( + `${name} exceeds maximum length of ${MAX_STRING_LENGTH} characters` + ); + } +} + +export function ensureOptionalInt(value: unknown, name: string): void { + if (value === undefined || value === null) { + return; + } + if (!Number.isInteger(value)) { + throw new ValidationError(`${name} must be an integer when set`); + } +} + +export function ensureOptionalBoolean(value: unknown, name: string): void { + if (value === undefined || value === null) { + return; + } + if (typeof value !== "boolean") { + throw new ValidationError(`${name} must be a boolean when set`); + } +} + +export function ensureStringArray(value: unknown, name: string): void { + if (!Array.isArray(value) || value.length === 0) { + throw new ValidationError(`${name} must be a non-empty string array`); + } + if (value.length > MAX_LIST_LENGTH) { + throw new ValidationError( + `${name} exceeds maximum size of ${MAX_LIST_LENGTH} items` + ); + } + value.forEach((item) => { + if (typeof item !== "string" || item.trim().length === 0) { + throw new ValidationError(`${name} must contain non-empty strings`); + } + }); +} + +export function ensureOptionalStringArray(value: unknown, name: string): void { + if (value === undefined || value === null) { + return; + } + ensureStringArray(value, name); +} + +export function ensureRating(value: unknown): void { + if (value === undefined || value === null) { + return; + } + if (value !== "like" && value !== "dislike") { + throw new ValidationError("rating must be either 'like' or 'dislike'"); + } +} + +export function validateParams(params: Record<string, unknown>): void { + Object.entries(params).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + + // Only check max length for strings; empty strings are allowed for optional params + // Required fields are validated at method level via ensureNonEmptyString + if (typeof value === "string") { + if (value.length > MAX_STRING_LENGTH) { + throw new ValidationError( + `Parameter '${key}' exceeds maximum length of ${MAX_STRING_LENGTH} characters` + ); + } + } else if (Array.isArray(value)) { + if (value.length > MAX_LIST_LENGTH) { + throw new ValidationError( + `Parameter '${key}' exceeds maximum size of ${MAX_LIST_LENGTH} items` + ); + } + } else if (typeof value === "object") { + if (Object.keys(value as Record<string, unknown>).length > MAX_DICT_LENGTH) { + throw new ValidationError( + `Parameter '${key}' exceeds maximum size of ${MAX_DICT_LENGTH} items` + ); + } + } + + if (key === "user" && typeof value !== "string") { + throw new ValidationError(`Parameter '${key}' must be a string`); + } + if ( + (key === "page" || key === "limit" || key === "page_size") && + !Number.isInteger(value) + ) { + throw new ValidationError(`Parameter '${key}' must be an integer`); + } + if (key === "files" && !Array.isArray(value) && typeof value !== "object") { + throw new ValidationError(`Parameter '${key}' must be a list or dict`); + } + if (key === "rating" && value !== "like" && value !== "dislike") { + throw new ValidationError(`Parameter '${key}' must be 'like' or 'dislike'`); + } + }); +} diff --git a/sdks/nodejs-client/src/client/workflow.test.js b/sdks/nodejs-client/src/client/workflow.test.js new file mode 100644 index 0000000000..79c419b55a --- /dev/null +++ b/sdks/nodejs-client/src/client/workflow.test.js @@ -0,0 +1,119 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { WorkflowClient } from "./workflow"; +import { createHttpClientWithSpies } from "../../tests/test-utils"; + +describe("WorkflowClient", () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + + it("runs workflows with blocking and streaming modes", async () => { + const { client, request, requestStream } = createHttpClientWithSpies(); + const workflow = new WorkflowClient(client); + + await workflow.run({ inputs: { input: "x" }, user: "user" }); + await workflow.run({ input: "x" }, "user", true); + + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/workflows/run", + data: { + inputs: { input: "x" }, + user: "user", + }, + }); + expect(requestStream).toHaveBeenCalledWith({ + method: "POST", + path: "/workflows/run", + data: { + inputs: { input: "x" }, + user: "user", + response_mode: "streaming", + }, + }); + }); + + it("runs workflow by id", async () => { + const { client, request, requestStream } = createHttpClientWithSpies(); + const workflow = new WorkflowClient(client); + + await workflow.runById("wf", { + inputs: { input: "x" }, + user: "user", + response_mode: "blocking", + }); + await workflow.runById("wf", { + inputs: { input: "x" }, + user: "user", + response_mode: "streaming", + }); + + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/workflows/wf/run", + data: { + inputs: { input: "x" }, + user: "user", + response_mode: "blocking", + }, + }); + expect(requestStream).toHaveBeenCalledWith({ + method: "POST", + path: "/workflows/wf/run", + data: { + inputs: { input: "x" }, + user: "user", + response_mode: "streaming", + }, + }); + }); + + it("gets run details and stops workflow", async () => { + const { client, request } = createHttpClientWithSpies(); + const workflow = new WorkflowClient(client); + + await workflow.getRun("run"); + await workflow.stop("task", "user"); + + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/workflows/run/run", + }); + expect(request).toHaveBeenCalledWith({ + method: "POST", + path: "/workflows/tasks/task/stop", + data: { user: "user" }, + }); + }); + + it("fetches workflow logs", async () => { + const { client, request } = createHttpClientWithSpies(); + const workflow = new WorkflowClient(client); + + // Use createdByEndUserSessionId to filter by user session (backend API parameter) + await workflow.getLogs({ + keyword: "k", + status: "succeeded", + startTime: "2024-01-01", + endTime: "2024-01-02", + createdByEndUserSessionId: "session-123", + page: 1, + limit: 20, + }); + + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/workflows/logs", + query: { + keyword: "k", + status: "succeeded", + created_at__before: "2024-01-02", + created_at__after: "2024-01-01", + created_by_end_user_session_id: "session-123", + created_by_account: undefined, + page: 1, + limit: 20, + }, + }); + }); +}); diff --git a/sdks/nodejs-client/src/client/workflow.ts b/sdks/nodejs-client/src/client/workflow.ts new file mode 100644 index 0000000000..ae4d5861fa --- /dev/null +++ b/sdks/nodejs-client/src/client/workflow.ts @@ -0,0 +1,165 @@ +import { DifyClient } from "./base"; +import type { WorkflowRunRequest, WorkflowRunResponse } from "../types/workflow"; +import type { DifyResponse, DifyStream, QueryParams } from "../types/common"; +import { + ensureNonEmptyString, + ensureOptionalInt, + ensureOptionalString, +} from "./validation"; + +export class WorkflowClient extends DifyClient { + run( + request: WorkflowRunRequest + ): Promise<DifyResponse<WorkflowRunResponse> | DifyStream<WorkflowRunResponse>>; + run( + inputs: Record<string, unknown>, + user: string, + stream?: boolean + ): Promise<DifyResponse<WorkflowRunResponse> | DifyStream<WorkflowRunResponse>>; + run( + inputOrRequest: WorkflowRunRequest | Record<string, unknown>, + user?: string, + stream = false + ): Promise<DifyResponse<WorkflowRunResponse> | DifyStream<WorkflowRunResponse>> { + let payload: WorkflowRunRequest; + let shouldStream = stream; + + if (user === undefined && "user" in (inputOrRequest as WorkflowRunRequest)) { + payload = inputOrRequest as WorkflowRunRequest; + shouldStream = payload.response_mode === "streaming"; + } else { + ensureNonEmptyString(user, "user"); + payload = { + inputs: inputOrRequest as Record<string, unknown>, + user, + response_mode: stream ? "streaming" : "blocking", + }; + } + + ensureNonEmptyString(payload.user, "user"); + + if (shouldStream) { + return this.http.requestStream<WorkflowRunResponse>({ + method: "POST", + path: "/workflows/run", + data: payload, + }); + } + + return this.http.request<WorkflowRunResponse>({ + method: "POST", + path: "/workflows/run", + data: payload, + }); + } + + runById( + workflowId: string, + request: WorkflowRunRequest + ): Promise<DifyResponse<WorkflowRunResponse> | DifyStream<WorkflowRunResponse>> { + ensureNonEmptyString(workflowId, "workflowId"); + ensureNonEmptyString(request.user, "user"); + if (request.response_mode === "streaming") { + return this.http.requestStream<WorkflowRunResponse>({ + method: "POST", + path: `/workflows/${workflowId}/run`, + data: request, + }); + } + return this.http.request<WorkflowRunResponse>({ + method: "POST", + path: `/workflows/${workflowId}/run`, + data: request, + }); + } + + getRun(workflowRunId: string): Promise<DifyResponse<WorkflowRunResponse>> { + ensureNonEmptyString(workflowRunId, "workflowRunId"); + return this.http.request({ + method: "GET", + path: `/workflows/run/${workflowRunId}`, + }); + } + + stop( + taskId: string, + user: string + ): Promise<DifyResponse<WorkflowRunResponse>> { + ensureNonEmptyString(taskId, "taskId"); + ensureNonEmptyString(user, "user"); + return this.http.request<WorkflowRunResponse>({ + method: "POST", + path: `/workflows/tasks/${taskId}/stop`, + data: { user }, + }); + } + + /** + * Get workflow execution logs with filtering options. + * + * Note: The backend API filters by `createdByEndUserSessionId` (end user session ID) + * or `createdByAccount` (account ID), not by a generic `user` parameter. + */ + getLogs(options?: { + keyword?: string; + status?: string; + createdAtBefore?: string; + createdAtAfter?: string; + createdByEndUserSessionId?: string; + createdByAccount?: string; + page?: number; + limit?: number; + startTime?: string; + endTime?: string; + }): Promise<DifyResponse<Record<string, unknown>>> { + if (options?.keyword) { + ensureOptionalString(options.keyword, "keyword"); + } + if (options?.status) { + ensureOptionalString(options.status, "status"); + } + if (options?.createdAtBefore) { + ensureOptionalString(options.createdAtBefore, "createdAtBefore"); + } + if (options?.createdAtAfter) { + ensureOptionalString(options.createdAtAfter, "createdAtAfter"); + } + if (options?.createdByEndUserSessionId) { + ensureOptionalString( + options.createdByEndUserSessionId, + "createdByEndUserSessionId" + ); + } + if (options?.createdByAccount) { + ensureOptionalString(options.createdByAccount, "createdByAccount"); + } + if (options?.startTime) { + ensureOptionalString(options.startTime, "startTime"); + } + if (options?.endTime) { + ensureOptionalString(options.endTime, "endTime"); + } + ensureOptionalInt(options?.page, "page"); + ensureOptionalInt(options?.limit, "limit"); + + const createdAtAfter = options?.createdAtAfter ?? options?.startTime; + const createdAtBefore = options?.createdAtBefore ?? options?.endTime; + + const query: QueryParams = { + keyword: options?.keyword, + status: options?.status, + created_at__before: createdAtBefore, + created_at__after: createdAtAfter, + created_by_end_user_session_id: options?.createdByEndUserSessionId, + created_by_account: options?.createdByAccount, + page: options?.page, + limit: options?.limit, + }; + + return this.http.request({ + method: "GET", + path: "/workflows/logs", + query, + }); + } +} diff --git a/sdks/nodejs-client/src/client/workspace.test.js b/sdks/nodejs-client/src/client/workspace.test.js new file mode 100644 index 0000000000..f8fb6e375a --- /dev/null +++ b/sdks/nodejs-client/src/client/workspace.test.js @@ -0,0 +1,21 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { WorkspaceClient } from "./workspace"; +import { createHttpClientWithSpies } from "../../tests/test-utils"; + +describe("WorkspaceClient", () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + + it("gets models by type", async () => { + const { client, request } = createHttpClientWithSpies(); + const workspace = new WorkspaceClient(client); + + await workspace.getModelsByType("llm"); + + expect(request).toHaveBeenCalledWith({ + method: "GET", + path: "/workspaces/current/models/model-types/llm", + }); + }); +}); diff --git a/sdks/nodejs-client/src/client/workspace.ts b/sdks/nodejs-client/src/client/workspace.ts new file mode 100644 index 0000000000..daee540c99 --- /dev/null +++ b/sdks/nodejs-client/src/client/workspace.ts @@ -0,0 +1,16 @@ +import { DifyClient } from "./base"; +import type { WorkspaceModelType, WorkspaceModelsResponse } from "../types/workspace"; +import type { DifyResponse } from "../types/common"; +import { ensureNonEmptyString } from "./validation"; + +export class WorkspaceClient extends DifyClient { + async getModelsByType( + modelType: WorkspaceModelType + ): Promise<DifyResponse<WorkspaceModelsResponse>> { + ensureNonEmptyString(modelType, "modelType"); + return this.http.request({ + method: "GET", + path: `/workspaces/current/models/model-types/${modelType}`, + }); + } +} diff --git a/sdks/nodejs-client/src/errors/dify-error.test.js b/sdks/nodejs-client/src/errors/dify-error.test.js new file mode 100644 index 0000000000..d151eca391 --- /dev/null +++ b/sdks/nodejs-client/src/errors/dify-error.test.js @@ -0,0 +1,37 @@ +import { describe, expect, it } from "vitest"; +import { + APIError, + AuthenticationError, + DifyError, + FileUploadError, + NetworkError, + RateLimitError, + TimeoutError, + ValidationError, +} from "./dify-error"; + +describe("Dify errors", () => { + it("sets base error fields", () => { + const err = new DifyError("base", { + statusCode: 400, + responseBody: { message: "bad" }, + requestId: "req", + retryAfter: 1, + }); + expect(err.name).toBe("DifyError"); + expect(err.statusCode).toBe(400); + expect(err.responseBody).toEqual({ message: "bad" }); + expect(err.requestId).toBe("req"); + expect(err.retryAfter).toBe(1); + }); + + it("creates specific error types", () => { + expect(new APIError("api").name).toBe("APIError"); + expect(new AuthenticationError("auth").name).toBe("AuthenticationError"); + expect(new RateLimitError("rate").name).toBe("RateLimitError"); + expect(new ValidationError("val").name).toBe("ValidationError"); + expect(new NetworkError("net").name).toBe("NetworkError"); + expect(new TimeoutError("timeout").name).toBe("TimeoutError"); + expect(new FileUploadError("upload").name).toBe("FileUploadError"); + }); +}); diff --git a/sdks/nodejs-client/src/errors/dify-error.ts b/sdks/nodejs-client/src/errors/dify-error.ts new file mode 100644 index 0000000000..e393e1b132 --- /dev/null +++ b/sdks/nodejs-client/src/errors/dify-error.ts @@ -0,0 +1,75 @@ +export type DifyErrorOptions = { + statusCode?: number; + responseBody?: unknown; + requestId?: string; + retryAfter?: number; + cause?: unknown; +}; + +export class DifyError extends Error { + statusCode?: number; + responseBody?: unknown; + requestId?: string; + retryAfter?: number; + + constructor(message: string, options: DifyErrorOptions = {}) { + super(message); + this.name = "DifyError"; + this.statusCode = options.statusCode; + this.responseBody = options.responseBody; + this.requestId = options.requestId; + this.retryAfter = options.retryAfter; + if (options.cause) { + (this as { cause?: unknown }).cause = options.cause; + } + } +} + +export class APIError extends DifyError { + constructor(message: string, options: DifyErrorOptions = {}) { + super(message, options); + this.name = "APIError"; + } +} + +export class AuthenticationError extends APIError { + constructor(message: string, options: DifyErrorOptions = {}) { + super(message, options); + this.name = "AuthenticationError"; + } +} + +export class RateLimitError extends APIError { + constructor(message: string, options: DifyErrorOptions = {}) { + super(message, options); + this.name = "RateLimitError"; + } +} + +export class ValidationError extends APIError { + constructor(message: string, options: DifyErrorOptions = {}) { + super(message, options); + this.name = "ValidationError"; + } +} + +export class NetworkError extends DifyError { + constructor(message: string, options: DifyErrorOptions = {}) { + super(message, options); + this.name = "NetworkError"; + } +} + +export class TimeoutError extends DifyError { + constructor(message: string, options: DifyErrorOptions = {}) { + super(message, options); + this.name = "TimeoutError"; + } +} + +export class FileUploadError extends DifyError { + constructor(message: string, options: DifyErrorOptions = {}) { + super(message, options); + this.name = "FileUploadError"; + } +} diff --git a/sdks/nodejs-client/src/http/client.test.js b/sdks/nodejs-client/src/http/client.test.js new file mode 100644 index 0000000000..05892547ed --- /dev/null +++ b/sdks/nodejs-client/src/http/client.test.js @@ -0,0 +1,304 @@ +import axios from "axios"; +import { Readable } from "node:stream"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { + APIError, + AuthenticationError, + FileUploadError, + NetworkError, + RateLimitError, + TimeoutError, + ValidationError, +} from "../errors/dify-error"; +import { HttpClient } from "./client"; + +describe("HttpClient", () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + it("builds requests with auth headers and JSON content type", async () => { + const mockRequest = vi.fn().mockResolvedValue({ + status: 200, + data: { ok: true }, + headers: { "x-request-id": "req" }, + }); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + + const client = new HttpClient({ apiKey: "test" }); + const response = await client.request({ + method: "POST", + path: "/chat-messages", + data: { user: "u" }, + }); + + expect(response.requestId).toBe("req"); + const config = mockRequest.mock.calls[0][0]; + expect(config.headers.Authorization).toBe("Bearer test"); + expect(config.headers["Content-Type"]).toBe("application/json"); + expect(config.responseType).toBe("json"); + }); + + it("serializes array query params", async () => { + const mockRequest = vi.fn().mockResolvedValue({ + status: 200, + data: "ok", + headers: {}, + }); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + + const client = new HttpClient({ apiKey: "test" }); + await client.requestRaw({ + method: "GET", + path: "/datasets", + query: { tag_ids: ["a", "b"], limit: 2 }, + }); + + const config = mockRequest.mock.calls[0][0]; + const queryString = config.paramsSerializer.serialize({ + tag_ids: ["a", "b"], + limit: 2, + }); + expect(queryString).toBe("tag_ids=a&tag_ids=b&limit=2"); + }); + + it("returns SSE stream helpers", async () => { + const mockRequest = vi.fn().mockResolvedValue({ + status: 200, + data: Readable.from(["data: {\"text\":\"hi\"}\n\n"]), + headers: { "x-request-id": "req" }, + }); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + + const client = new HttpClient({ apiKey: "test" }); + const stream = await client.requestStream({ + method: "POST", + path: "/chat-messages", + data: { user: "u" }, + }); + + expect(stream.status).toBe(200); + expect(stream.requestId).toBe("req"); + await expect(stream.toText()).resolves.toBe("hi"); + }); + + it("returns binary stream helpers", async () => { + const mockRequest = vi.fn().mockResolvedValue({ + status: 200, + data: Readable.from(["chunk"]), + headers: { "x-request-id": "req" }, + }); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + + const client = new HttpClient({ apiKey: "test" }); + const stream = await client.requestBinaryStream({ + method: "POST", + path: "/text-to-audio", + data: { user: "u", text: "hi" }, + }); + + expect(stream.status).toBe(200); + expect(stream.requestId).toBe("req"); + }); + + it("respects form-data headers", async () => { + const mockRequest = vi.fn().mockResolvedValue({ + status: 200, + data: "ok", + headers: {}, + }); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + + const client = new HttpClient({ apiKey: "test" }); + const form = { + append: () => {}, + getHeaders: () => ({ "content-type": "multipart/form-data; boundary=abc" }), + }; + + await client.requestRaw({ + method: "POST", + path: "/files/upload", + data: form, + }); + + const config = mockRequest.mock.calls[0][0]; + expect(config.headers["content-type"]).toBe( + "multipart/form-data; boundary=abc" + ); + expect(config.headers["Content-Type"]).toBeUndefined(); + }); + + it("maps 401 and 429 errors", async () => { + const mockRequest = vi.fn(); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + const client = new HttpClient({ apiKey: "test", maxRetries: 0 }); + + mockRequest.mockRejectedValueOnce({ + isAxiosError: true, + response: { + status: 401, + data: { message: "unauthorized" }, + headers: {}, + }, + }); + await expect( + client.requestRaw({ method: "GET", path: "/meta" }) + ).rejects.toBeInstanceOf(AuthenticationError); + + mockRequest.mockRejectedValueOnce({ + isAxiosError: true, + response: { + status: 429, + data: { message: "rate" }, + headers: { "retry-after": "2" }, + }, + }); + const error = await client + .requestRaw({ method: "GET", path: "/meta" }) + .catch((err) => err); + expect(error).toBeInstanceOf(RateLimitError); + expect(error.retryAfter).toBe(2); + }); + + it("maps validation and upload errors", async () => { + const mockRequest = vi.fn(); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + const client = new HttpClient({ apiKey: "test", maxRetries: 0 }); + + mockRequest.mockRejectedValueOnce({ + isAxiosError: true, + response: { + status: 422, + data: { message: "invalid" }, + headers: {}, + }, + }); + await expect( + client.requestRaw({ method: "POST", path: "/chat-messages", data: { user: "u" } }) + ).rejects.toBeInstanceOf(ValidationError); + + mockRequest.mockRejectedValueOnce({ + isAxiosError: true, + config: { url: "/files/upload" }, + response: { + status: 400, + data: { message: "bad upload" }, + headers: {}, + }, + }); + await expect( + client.requestRaw({ method: "POST", path: "/files/upload", data: { user: "u" } }) + ).rejects.toBeInstanceOf(FileUploadError); + }); + + it("maps timeout and network errors", async () => { + const mockRequest = vi.fn(); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + const client = new HttpClient({ apiKey: "test", maxRetries: 0 }); + + mockRequest.mockRejectedValueOnce({ + isAxiosError: true, + code: "ECONNABORTED", + message: "timeout", + }); + await expect( + client.requestRaw({ method: "GET", path: "/meta" }) + ).rejects.toBeInstanceOf(TimeoutError); + + mockRequest.mockRejectedValueOnce({ + isAxiosError: true, + message: "network", + }); + await expect( + client.requestRaw({ method: "GET", path: "/meta" }) + ).rejects.toBeInstanceOf(NetworkError); + }); + + it("retries on timeout errors", async () => { + const mockRequest = vi.fn(); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + const client = new HttpClient({ apiKey: "test", maxRetries: 1, retryDelay: 0 }); + + mockRequest + .mockRejectedValueOnce({ + isAxiosError: true, + code: "ECONNABORTED", + message: "timeout", + }) + .mockResolvedValueOnce({ status: 200, data: "ok", headers: {} }); + + await client.requestRaw({ method: "GET", path: "/meta" }); + expect(mockRequest).toHaveBeenCalledTimes(2); + }); + + it("validates query parameters before request", async () => { + const mockRequest = vi.fn(); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + const client = new HttpClient({ apiKey: "test" }); + + await expect( + client.requestRaw({ method: "GET", path: "/meta", query: { user: 1 } }) + ).rejects.toBeInstanceOf(ValidationError); + expect(mockRequest).not.toHaveBeenCalled(); + }); + + it("returns APIError for other http failures", async () => { + const mockRequest = vi.fn(); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + const client = new HttpClient({ apiKey: "test", maxRetries: 0 }); + + mockRequest.mockRejectedValueOnce({ + isAxiosError: true, + response: { status: 500, data: { message: "server" }, headers: {} }, + }); + + await expect( + client.requestRaw({ method: "GET", path: "/meta" }) + ).rejects.toBeInstanceOf(APIError); + }); + + it("logs requests and responses when enableLogging is true", async () => { + const mockRequest = vi.fn().mockResolvedValue({ + status: 200, + data: { ok: true }, + headers: {}, + }); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + const consoleInfo = vi.spyOn(console, "info").mockImplementation(() => {}); + + const client = new HttpClient({ apiKey: "test", enableLogging: true }); + await client.requestRaw({ method: "GET", path: "/meta" }); + + expect(consoleInfo).toHaveBeenCalledWith( + expect.stringContaining("dify-client-node response 200 GET") + ); + consoleInfo.mockRestore(); + }); + + it("logs retry attempts when enableLogging is true", async () => { + const mockRequest = vi.fn(); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + const consoleInfo = vi.spyOn(console, "info").mockImplementation(() => {}); + + const client = new HttpClient({ + apiKey: "test", + maxRetries: 1, + retryDelay: 0, + enableLogging: true, + }); + + mockRequest + .mockRejectedValueOnce({ + isAxiosError: true, + code: "ECONNABORTED", + message: "timeout", + }) + .mockResolvedValueOnce({ status: 200, data: "ok", headers: {} }); + + await client.requestRaw({ method: "GET", path: "/meta" }); + + expect(consoleInfo).toHaveBeenCalledWith( + expect.stringContaining("dify-client-node retry") + ); + consoleInfo.mockRestore(); + }); +}); diff --git a/sdks/nodejs-client/src/http/client.ts b/sdks/nodejs-client/src/http/client.ts new file mode 100644 index 0000000000..44b63c9903 --- /dev/null +++ b/sdks/nodejs-client/src/http/client.ts @@ -0,0 +1,368 @@ +import axios from "axios"; +import type { + AxiosError, + AxiosInstance, + AxiosRequestConfig, + AxiosResponse, +} from "axios"; +import type { Readable } from "node:stream"; +import { + DEFAULT_BASE_URL, + DEFAULT_MAX_RETRIES, + DEFAULT_RETRY_DELAY_SECONDS, + DEFAULT_TIMEOUT_SECONDS, +} from "../types/common"; +import type { + DifyClientConfig, + DifyResponse, + Headers, + QueryParams, + RequestMethod, +} from "../types/common"; +import type { DifyError } from "../errors/dify-error"; +import { + APIError, + AuthenticationError, + FileUploadError, + NetworkError, + RateLimitError, + TimeoutError, + ValidationError, +} from "../errors/dify-error"; +import { getFormDataHeaders, isFormData } from "./form-data"; +import { createBinaryStream, createSseStream } from "./sse"; +import { getRetryDelayMs, shouldRetry, sleep } from "./retry"; +import { validateParams } from "../client/validation"; + +const DEFAULT_USER_AGENT = "dify-client-node"; + +export type RequestOptions = { + method: RequestMethod; + path: string; + query?: QueryParams; + data?: unknown; + headers?: Headers; + responseType?: AxiosRequestConfig["responseType"]; +}; + +export type HttpClientSettings = Required< + Omit<DifyClientConfig, "apiKey"> +> & { + apiKey: string; +}; + +const normalizeSettings = (config: DifyClientConfig): HttpClientSettings => ({ + apiKey: config.apiKey, + baseUrl: config.baseUrl ?? DEFAULT_BASE_URL, + timeout: config.timeout ?? DEFAULT_TIMEOUT_SECONDS, + maxRetries: config.maxRetries ?? DEFAULT_MAX_RETRIES, + retryDelay: config.retryDelay ?? DEFAULT_RETRY_DELAY_SECONDS, + enableLogging: config.enableLogging ?? false, +}); + +const normalizeHeaders = (headers: AxiosResponse["headers"]): Headers => { + const result: Headers = {}; + if (!headers) { + return result; + } + Object.entries(headers).forEach(([key, value]) => { + if (Array.isArray(value)) { + result[key.toLowerCase()] = value.join(", "); + } else if (typeof value === "string") { + result[key.toLowerCase()] = value; + } else if (typeof value === "number") { + result[key.toLowerCase()] = value.toString(); + } + }); + return result; +}; + +const resolveRequestId = (headers: Headers): string | undefined => + headers["x-request-id"] ?? headers["x-requestid"]; + +const buildRequestUrl = (baseUrl: string, path: string): string => { + const trimmed = baseUrl.replace(/\/+$/, ""); + return `${trimmed}${path}`; +}; + +const buildQueryString = (params?: QueryParams): string => { + if (!params) { + return ""; + } + const searchParams = new URLSearchParams(); + Object.entries(params).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((item) => { + searchParams.append(key, String(item)); + }); + return; + } + searchParams.append(key, String(value)); + }); + return searchParams.toString(); +}; + +const parseRetryAfterSeconds = (headerValue?: string): number | undefined => { + if (!headerValue) { + return undefined; + } + const asNumber = Number.parseInt(headerValue, 10); + if (!Number.isNaN(asNumber)) { + return asNumber; + } + const asDate = Date.parse(headerValue); + if (!Number.isNaN(asDate)) { + const diff = asDate - Date.now(); + return diff > 0 ? Math.ceil(diff / 1000) : 0; + } + return undefined; +}; + +const isReadableStream = (value: unknown): value is Readable => { + if (!value || typeof value !== "object") { + return false; + } + return typeof (value as { pipe?: unknown }).pipe === "function"; +}; + +const isUploadLikeRequest = (config?: AxiosRequestConfig): boolean => { + const url = (config?.url ?? "").toLowerCase(); + if (!url) { + return false; + } + return ( + url.includes("upload") || + url.includes("/files/") || + url.includes("audio-to-text") || + url.includes("create_by_file") || + url.includes("update_by_file") + ); +}; + +const resolveErrorMessage = (status: number, responseBody: unknown): string => { + if (typeof responseBody === "string" && responseBody.trim().length > 0) { + return responseBody; + } + if ( + responseBody && + typeof responseBody === "object" && + "message" in responseBody + ) { + const message = (responseBody as Record<string, unknown>).message; + if (typeof message === "string" && message.trim().length > 0) { + return message; + } + } + return `Request failed with status code ${status}`; +}; + +const mapAxiosError = (error: unknown): DifyError => { + if (axios.isAxiosError(error)) { + const axiosError = error as AxiosError; + if (axiosError.response) { + const status = axiosError.response.status; + const headers = normalizeHeaders(axiosError.response.headers); + const requestId = resolveRequestId(headers); + const responseBody = axiosError.response.data; + const message = resolveErrorMessage(status, responseBody); + + if (status === 401) { + return new AuthenticationError(message, { + statusCode: status, + responseBody, + requestId, + }); + } + if (status === 429) { + const retryAfter = parseRetryAfterSeconds(headers["retry-after"]); + return new RateLimitError(message, { + statusCode: status, + responseBody, + requestId, + retryAfter, + }); + } + if (status === 422) { + return new ValidationError(message, { + statusCode: status, + responseBody, + requestId, + }); + } + if (status === 400) { + if (isUploadLikeRequest(axiosError.config)) { + return new FileUploadError(message, { + statusCode: status, + responseBody, + requestId, + }); + } + } + return new APIError(message, { + statusCode: status, + responseBody, + requestId, + }); + } + if (axiosError.code === "ECONNABORTED") { + return new TimeoutError("Request timed out", { cause: axiosError }); + } + return new NetworkError(axiosError.message, { cause: axiosError }); + } + if (error instanceof Error) { + return new NetworkError(error.message, { cause: error }); + } + return new NetworkError("Unexpected network error", { cause: error }); +}; + +export class HttpClient { + private axios: AxiosInstance; + private settings: HttpClientSettings; + + constructor(config: DifyClientConfig) { + this.settings = normalizeSettings(config); + this.axios = axios.create({ + baseURL: this.settings.baseUrl, + timeout: this.settings.timeout * 1000, + }); + } + + updateApiKey(apiKey: string): void { + this.settings.apiKey = apiKey; + } + + getSettings(): HttpClientSettings { + return { ...this.settings }; + } + + async request<T>(options: RequestOptions): Promise<DifyResponse<T>> { + const response = await this.requestRaw(options); + const headers = normalizeHeaders(response.headers); + return { + data: response.data as T, + status: response.status, + headers, + requestId: resolveRequestId(headers), + }; + } + + async requestStream<T>(options: RequestOptions) { + const response = await this.requestRaw({ + ...options, + responseType: "stream", + }); + const headers = normalizeHeaders(response.headers); + return createSseStream<T>(response.data as Readable, { + status: response.status, + headers, + requestId: resolveRequestId(headers), + }); + } + + async requestBinaryStream(options: RequestOptions) { + const response = await this.requestRaw({ + ...options, + responseType: "stream", + }); + const headers = normalizeHeaders(response.headers); + return createBinaryStream(response.data as Readable, { + status: response.status, + headers, + requestId: resolveRequestId(headers), + }); + } + + async requestRaw(options: RequestOptions): Promise<AxiosResponse> { + const { method, path, query, data, headers, responseType } = options; + const { apiKey, enableLogging, maxRetries, retryDelay, timeout } = + this.settings; + + if (query) { + validateParams(query as Record<string, unknown>); + } + if ( + data && + typeof data === "object" && + !Array.isArray(data) && + !isFormData(data) && + !isReadableStream(data) + ) { + validateParams(data as Record<string, unknown>); + } + + const requestHeaders: Headers = { + Authorization: `Bearer ${apiKey}`, + ...headers, + }; + if ( + typeof process !== "undefined" && + !!process.versions?.node && + !requestHeaders["User-Agent"] && + !requestHeaders["user-agent"] + ) { + requestHeaders["User-Agent"] = DEFAULT_USER_AGENT; + } + + if (isFormData(data)) { + Object.assign(requestHeaders, getFormDataHeaders(data)); + } else if (data && method !== "GET") { + requestHeaders["Content-Type"] = "application/json"; + } + + const url = buildRequestUrl(this.settings.baseUrl, path); + + if (enableLogging) { + console.info(`dify-client-node request ${method} ${url}`); + } + + const axiosConfig: AxiosRequestConfig = { + method, + url: path, + params: query, + paramsSerializer: { + serialize: (params) => buildQueryString(params as QueryParams), + }, + headers: requestHeaders, + responseType: responseType ?? "json", + timeout: timeout * 1000, + }; + + if (method !== "GET" && data !== undefined) { + axiosConfig.data = data; + } + + let attempt = 0; + // `attempt` is a zero-based retry counter + // Total attempts = 1 (initial) + maxRetries + // e.g., maxRetries=3 means: attempt 0 (initial), then retries at 1, 2, 3 + while (true) { + try { + const response = await this.axios.request(axiosConfig); + if (enableLogging) { + console.info( + `dify-client-node response ${response.status} ${method} ${url}` + ); + } + return response; + } catch (error) { + const mapped = mapAxiosError(error); + if (!shouldRetry(mapped, attempt, maxRetries)) { + throw mapped; + } + const retryAfterSeconds = + mapped instanceof RateLimitError ? mapped.retryAfter : undefined; + const delay = getRetryDelayMs(attempt + 1, retryDelay, retryAfterSeconds); + if (enableLogging) { + console.info( + `dify-client-node retry ${attempt + 1} in ${delay}ms for ${method} ${url}` + ); + } + attempt += 1; + await sleep(delay); + } + } + } +} diff --git a/sdks/nodejs-client/src/http/form-data.test.js b/sdks/nodejs-client/src/http/form-data.test.js new file mode 100644 index 0000000000..2938e41435 --- /dev/null +++ b/sdks/nodejs-client/src/http/form-data.test.js @@ -0,0 +1,23 @@ +import { describe, expect, it } from "vitest"; +import { getFormDataHeaders, isFormData } from "./form-data"; + +describe("form-data helpers", () => { + it("detects form-data like objects", () => { + const formLike = { + append: () => {}, + getHeaders: () => ({ "content-type": "multipart/form-data" }), + }; + expect(isFormData(formLike)).toBe(true); + expect(isFormData({})).toBe(false); + }); + + it("returns headers from form-data", () => { + const formLike = { + append: () => {}, + getHeaders: () => ({ "content-type": "multipart/form-data" }), + }; + expect(getFormDataHeaders(formLike)).toEqual({ + "content-type": "multipart/form-data", + }); + }); +}); diff --git a/sdks/nodejs-client/src/http/form-data.ts b/sdks/nodejs-client/src/http/form-data.ts new file mode 100644 index 0000000000..2efa23e54e --- /dev/null +++ b/sdks/nodejs-client/src/http/form-data.ts @@ -0,0 +1,31 @@ +import type { Headers } from "../types/common"; + +export type FormDataLike = { + append: (...args: unknown[]) => void; + getHeaders?: () => Headers; + constructor?: { name?: string }; +}; + +export const isFormData = (value: unknown): value is FormDataLike => { + if (!value || typeof value !== "object") { + return false; + } + if (typeof FormData !== "undefined" && value instanceof FormData) { + return true; + } + const candidate = value as FormDataLike; + if (typeof candidate.append !== "function") { + return false; + } + if (typeof candidate.getHeaders === "function") { + return true; + } + return candidate.constructor?.name === "FormData"; +}; + +export const getFormDataHeaders = (form: FormDataLike): Headers => { + if (typeof form.getHeaders === "function") { + return form.getHeaders(); + } + return {}; +}; diff --git a/sdks/nodejs-client/src/http/retry.test.js b/sdks/nodejs-client/src/http/retry.test.js new file mode 100644 index 0000000000..fc017f631b --- /dev/null +++ b/sdks/nodejs-client/src/http/retry.test.js @@ -0,0 +1,38 @@ +import { describe, expect, it } from "vitest"; +import { getRetryDelayMs, shouldRetry } from "./retry"; +import { NetworkError, RateLimitError, TimeoutError } from "../errors/dify-error"; + +const withMockedRandom = (value, fn) => { + const original = Math.random; + Math.random = () => value; + try { + fn(); + } finally { + Math.random = original; + } +}; + +describe("retry helpers", () => { + it("getRetryDelayMs honors retry-after header", () => { + expect(getRetryDelayMs(1, 1, 3)).toBe(3000); + }); + + it("getRetryDelayMs uses exponential backoff with jitter", () => { + withMockedRandom(0, () => { + expect(getRetryDelayMs(1, 1)).toBe(1000); + expect(getRetryDelayMs(2, 1)).toBe(2000); + expect(getRetryDelayMs(3, 1)).toBe(4000); + }); + }); + + it("shouldRetry respects max retries", () => { + expect(shouldRetry(new TimeoutError("timeout"), 3, 3)).toBe(false); + }); + + it("shouldRetry retries on network, timeout, and rate limit", () => { + expect(shouldRetry(new TimeoutError("timeout"), 0, 3)).toBe(true); + expect(shouldRetry(new NetworkError("network"), 0, 3)).toBe(true); + expect(shouldRetry(new RateLimitError("limit"), 0, 3)).toBe(true); + expect(shouldRetry(new Error("other"), 0, 3)).toBe(false); + }); +}); diff --git a/sdks/nodejs-client/src/http/retry.ts b/sdks/nodejs-client/src/http/retry.ts new file mode 100644 index 0000000000..3776b78d5f --- /dev/null +++ b/sdks/nodejs-client/src/http/retry.ts @@ -0,0 +1,40 @@ +import { RateLimitError, NetworkError, TimeoutError } from "../errors/dify-error"; + +export const sleep = (ms: number): Promise<void> => + new Promise((resolve) => { + setTimeout(resolve, ms); + }); + +export const getRetryDelayMs = ( + attempt: number, + retryDelaySeconds: number, + retryAfterSeconds?: number +): number => { + if (retryAfterSeconds && retryAfterSeconds > 0) { + return retryAfterSeconds * 1000; + } + const base = retryDelaySeconds * 1000; + const exponential = base * Math.pow(2, Math.max(0, attempt - 1)); + const jitter = Math.random() * base; + return exponential + jitter; +}; + +export const shouldRetry = ( + error: unknown, + attempt: number, + maxRetries: number +): boolean => { + if (attempt >= maxRetries) { + return false; + } + if (error instanceof TimeoutError) { + return true; + } + if (error instanceof NetworkError) { + return true; + } + if (error instanceof RateLimitError) { + return true; + } + return false; +}; diff --git a/sdks/nodejs-client/src/http/sse.test.js b/sdks/nodejs-client/src/http/sse.test.js new file mode 100644 index 0000000000..fff85fd29b --- /dev/null +++ b/sdks/nodejs-client/src/http/sse.test.js @@ -0,0 +1,76 @@ +import { Readable } from "node:stream"; +import { describe, expect, it } from "vitest"; +import { createBinaryStream, createSseStream, parseSseStream } from "./sse"; + +describe("sse parsing", () => { + it("parses event and data lines", async () => { + const stream = Readable.from([ + "event: message\n", + "data: {\"answer\":\"hi\"}\n", + "\n", + ]); + const events = []; + for await (const event of parseSseStream(stream)) { + events.push(event); + } + expect(events).toHaveLength(1); + expect(events[0].event).toBe("message"); + expect(events[0].data).toEqual({ answer: "hi" }); + }); + + it("handles multi-line data payloads", async () => { + const stream = Readable.from(["data: line1\n", "data: line2\n", "\n"]); + const events = []; + for await (const event of parseSseStream(stream)) { + events.push(event); + } + expect(events[0].raw).toBe("line1\nline2"); + expect(events[0].data).toBe("line1\nline2"); + }); + + it("createSseStream exposes toText", async () => { + const stream = Readable.from([ + "data: {\"answer\":\"hello\"}\n\n", + "data: {\"delta\":\" world\"}\n\n", + ]); + const sseStream = createSseStream(stream, { + status: 200, + headers: {}, + requestId: "req", + }); + const text = await sseStream.toText(); + expect(text).toBe("hello world"); + }); + + it("toText extracts text from string data", async () => { + const stream = Readable.from(["data: plain text\n\n"]); + const sseStream = createSseStream(stream, { status: 200, headers: {} }); + const text = await sseStream.toText(); + expect(text).toBe("plain text"); + }); + + it("toText extracts text field from object", async () => { + const stream = Readable.from(['data: {"text":"hello"}\n\n']); + const sseStream = createSseStream(stream, { status: 200, headers: {} }); + const text = await sseStream.toText(); + expect(text).toBe("hello"); + }); + + it("toText returns empty for invalid data", async () => { + const stream = Readable.from(["data: null\n\n", "data: 123\n\n"]); + const sseStream = createSseStream(stream, { status: 200, headers: {} }); + const text = await sseStream.toText(); + expect(text).toBe(""); + }); + + it("createBinaryStream exposes metadata", () => { + const stream = Readable.from(["chunk"]); + const binary = createBinaryStream(stream, { + status: 200, + headers: { "content-type": "audio/mpeg" }, + requestId: "req", + }); + expect(binary.status).toBe(200); + expect(binary.headers["content-type"]).toBe("audio/mpeg"); + }); +}); diff --git a/sdks/nodejs-client/src/http/sse.ts b/sdks/nodejs-client/src/http/sse.ts new file mode 100644 index 0000000000..ed5a17fe39 --- /dev/null +++ b/sdks/nodejs-client/src/http/sse.ts @@ -0,0 +1,133 @@ +import type { Readable } from "node:stream"; +import { StringDecoder } from "node:string_decoder"; +import type { BinaryStream, DifyStream, Headers, StreamEvent } from "../types/common"; + +const readLines = async function* (stream: Readable): AsyncIterable<string> { + const decoder = new StringDecoder("utf8"); + let buffered = ""; + for await (const chunk of stream) { + buffered += decoder.write(chunk as Buffer); + let index = buffered.indexOf("\n"); + while (index >= 0) { + let line = buffered.slice(0, index); + buffered = buffered.slice(index + 1); + if (line.endsWith("\r")) { + line = line.slice(0, -1); + } + yield line; + index = buffered.indexOf("\n"); + } + } + buffered += decoder.end(); + if (buffered) { + yield buffered; + } +}; + +const parseMaybeJson = (value: string): unknown => { + if (!value) { + return null; + } + try { + return JSON.parse(value); + } catch { + return value; + } +}; + +export const parseSseStream = async function* <T>( + stream: Readable +): AsyncIterable<StreamEvent<T>> { + let eventName: string | undefined; + const dataLines: string[] = []; + + const emitEvent = function* (): Iterable<StreamEvent<T>> { + if (!eventName && dataLines.length === 0) { + return; + } + const raw = dataLines.join("\n"); + const parsed = parseMaybeJson(raw) as T | string | null; + yield { + event: eventName, + data: parsed, + raw, + }; + eventName = undefined; + dataLines.length = 0; + }; + + for await (const line of readLines(stream)) { + if (!line) { + yield* emitEvent(); + continue; + } + if (line.startsWith(":")) { + continue; + } + if (line.startsWith("event:")) { + eventName = line.slice("event:".length).trim(); + continue; + } + if (line.startsWith("data:")) { + dataLines.push(line.slice("data:".length).trimStart()); + continue; + } + } + + yield* emitEvent(); +}; + +const extractTextFromEvent = (data: unknown): string => { + if (typeof data === "string") { + return data; + } + if (!data || typeof data !== "object") { + return ""; + } + const record = data as Record<string, unknown>; + if (typeof record.answer === "string") { + return record.answer; + } + if (typeof record.text === "string") { + return record.text; + } + if (typeof record.delta === "string") { + return record.delta; + } + return ""; +}; + +export const createSseStream = <T>( + stream: Readable, + meta: { status: number; headers: Headers; requestId?: string } +): DifyStream<T> => { + const iterator = parseSseStream<T>(stream)[Symbol.asyncIterator](); + const iterable = { + [Symbol.asyncIterator]: () => iterator, + data: stream, + status: meta.status, + headers: meta.headers, + requestId: meta.requestId, + toReadable: () => stream, + toText: async () => { + let text = ""; + for await (const event of iterable) { + text += extractTextFromEvent(event.data); + } + return text; + }, + } satisfies DifyStream<T>; + + return iterable; +}; + +export const createBinaryStream = ( + stream: Readable, + meta: { status: number; headers: Headers; requestId?: string } +): BinaryStream => ({ + data: stream, + status: meta.status, + headers: meta.headers, + requestId: meta.requestId, + toReadable: () => stream, +}); diff --git a/sdks/nodejs-client/src/index.test.js b/sdks/nodejs-client/src/index.test.js new file mode 100644 index 0000000000..289f4d9b1b --- /dev/null +++ b/sdks/nodejs-client/src/index.test.js @@ -0,0 +1,227 @@ +import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { ChatClient, DifyClient, WorkflowClient, BASE_URL, routes } from "./index"; +import axios from "axios"; + +const mockRequest = vi.fn(); + +const setupAxiosMock = () => { + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); +}; + +beforeEach(() => { + vi.restoreAllMocks(); + mockRequest.mockReset(); + setupAxiosMock(); +}); + +describe("Client", () => { + it("should create a client", () => { + new DifyClient("test"); + + expect(axios.create).toHaveBeenCalledWith({ + baseURL: BASE_URL, + timeout: 60000, + }); + }); + + it("should update the api key", () => { + const difyClient = new DifyClient("test"); + difyClient.updateApiKey("test2"); + + expect(difyClient.getHttpClient().getSettings().apiKey).toBe("test2"); + }); +}); + +describe("Send Requests", () => { + it("should make a successful request to the application parameter", async () => { + const difyClient = new DifyClient("test"); + const method = "GET"; + const endpoint = routes.application.url(); + mockRequest.mockResolvedValue({ + status: 200, + data: "response", + headers: {}, + }); + + await difyClient.sendRequest(method, endpoint); + + const requestConfig = mockRequest.mock.calls[0][0]; + expect(requestConfig).toMatchObject({ + method, + url: endpoint, + params: undefined, + responseType: "json", + timeout: 60000, + }); + expect(requestConfig.headers.Authorization).toBe("Bearer test"); + }); + + it("uses the getMeta route configuration", async () => { + const difyClient = new DifyClient("test"); + mockRequest.mockResolvedValue({ status: 200, data: "ok", headers: {} }); + + await difyClient.getMeta("end-user"); + + expect(mockRequest).toHaveBeenCalledWith(expect.objectContaining({ + method: routes.getMeta.method, + url: routes.getMeta.url(), + params: { user: "end-user" }, + headers: expect.objectContaining({ + Authorization: "Bearer test", + }), + responseType: "json", + timeout: 60000, + })); + }); +}); + +describe("File uploads", () => { + const OriginalFormData = globalThis.FormData; + + beforeAll(() => { + globalThis.FormData = class FormDataMock { + append() {} + + getHeaders() { + return { + "content-type": "multipart/form-data; boundary=test", + }; + } + }; + }); + + afterAll(() => { + globalThis.FormData = OriginalFormData; + }); + + it("does not override multipart boundary headers for FormData", async () => { + const difyClient = new DifyClient("test"); + const form = new globalThis.FormData(); + mockRequest.mockResolvedValue({ status: 200, data: "ok", headers: {} }); + + await difyClient.fileUpload(form, "end-user"); + + expect(mockRequest).toHaveBeenCalledWith(expect.objectContaining({ + method: routes.fileUpload.method, + url: routes.fileUpload.url(), + params: undefined, + headers: expect.objectContaining({ + Authorization: "Bearer test", + "content-type": "multipart/form-data; boundary=test", + }), + responseType: "json", + timeout: 60000, + data: form, + })); + }); +}); + +describe("Workflow client", () => { + it("uses tasks stop path for workflow stop", async () => { + const workflowClient = new WorkflowClient("test"); + mockRequest.mockResolvedValue({ status: 200, data: "stopped", headers: {} }); + + await workflowClient.stop("task-1", "end-user"); + + expect(mockRequest).toHaveBeenCalledWith(expect.objectContaining({ + method: routes.stopWorkflow.method, + url: routes.stopWorkflow.url("task-1"), + params: undefined, + headers: expect.objectContaining({ + Authorization: "Bearer test", + "Content-Type": "application/json", + }), + responseType: "json", + timeout: 60000, + data: { user: "end-user" }, + })); + }); + + it("maps workflow log filters to service api params", async () => { + const workflowClient = new WorkflowClient("test"); + mockRequest.mockResolvedValue({ status: 200, data: "ok", headers: {} }); + + await workflowClient.getLogs({ + createdAtAfter: "2024-01-01T00:00:00Z", + createdAtBefore: "2024-01-02T00:00:00Z", + createdByEndUserSessionId: "sess-1", + createdByAccount: "acc-1", + page: 2, + limit: 10, + }); + + expect(mockRequest).toHaveBeenCalledWith(expect.objectContaining({ + method: "GET", + url: "/workflows/logs", + params: { + created_at__after: "2024-01-01T00:00:00Z", + created_at__before: "2024-01-02T00:00:00Z", + created_by_end_user_session_id: "sess-1", + created_by_account: "acc-1", + page: 2, + limit: 10, + }, + headers: expect.objectContaining({ + Authorization: "Bearer test", + }), + responseType: "json", + timeout: 60000, + })); + }); +}); + +describe("Chat client", () => { + it("places user in query for suggested messages", async () => { + const chatClient = new ChatClient("test"); + mockRequest.mockResolvedValue({ status: 200, data: "ok", headers: {} }); + + await chatClient.getSuggested("msg-1", "end-user"); + + expect(mockRequest).toHaveBeenCalledWith(expect.objectContaining({ + method: routes.getSuggested.method, + url: routes.getSuggested.url("msg-1"), + params: { user: "end-user" }, + headers: expect.objectContaining({ + Authorization: "Bearer test", + }), + responseType: "json", + timeout: 60000, + })); + }); + + it("uses last_id when listing conversations", async () => { + const chatClient = new ChatClient("test"); + mockRequest.mockResolvedValue({ status: 200, data: "ok", headers: {} }); + + await chatClient.getConversations("end-user", "last-1", 10); + + expect(mockRequest).toHaveBeenCalledWith(expect.objectContaining({ + method: routes.getConversations.method, + url: routes.getConversations.url(), + params: { user: "end-user", last_id: "last-1", limit: 10 }, + headers: expect.objectContaining({ + Authorization: "Bearer test", + }), + responseType: "json", + timeout: 60000, + })); + }); + + it("lists app feedbacks without user params", async () => { + const chatClient = new ChatClient("test"); + mockRequest.mockResolvedValue({ status: 200, data: "ok", headers: {} }); + + await chatClient.getAppFeedbacks(1, 20); + + expect(mockRequest).toHaveBeenCalledWith(expect.objectContaining({ + method: "GET", + url: "/app/feedbacks", + params: { page: 1, limit: 20 }, + headers: expect.objectContaining({ + Authorization: "Bearer test", + }), + responseType: "json", + timeout: 60000, + })); + }); +}); diff --git a/sdks/nodejs-client/src/index.ts b/sdks/nodejs-client/src/index.ts new file mode 100644 index 0000000000..3ba3757baa --- /dev/null +++ b/sdks/nodejs-client/src/index.ts @@ -0,0 +1,103 @@ +import { DEFAULT_BASE_URL } from "./types/common"; + +export const BASE_URL = DEFAULT_BASE_URL; + +export const routes = { + feedback: { + method: "POST", + url: (messageId: string) => `/messages/${messageId}/feedbacks`, + }, + application: { + method: "GET", + url: () => "/parameters", + }, + fileUpload: { + method: "POST", + url: () => "/files/upload", + }, + filePreview: { + method: "GET", + url: (fileId: string) => `/files/${fileId}/preview`, + }, + textToAudio: { + method: "POST", + url: () => "/text-to-audio", + }, + audioToText: { + method: "POST", + url: () => "/audio-to-text", + }, + getMeta: { + method: "GET", + url: () => "/meta", + }, + getInfo: { + method: "GET", + url: () => "/info", + }, + getSite: { + method: "GET", + url: () => "/site", + }, + createCompletionMessage: { + method: "POST", + url: () => "/completion-messages", + }, + stopCompletionMessage: { + method: "POST", + url: (taskId: string) => `/completion-messages/${taskId}/stop`, + }, + createChatMessage: { + method: "POST", + url: () => "/chat-messages", + }, + getSuggested: { + method: "GET", + url: (messageId: string) => `/messages/${messageId}/suggested`, + }, + stopChatMessage: { + method: "POST", + url: (taskId: string) => `/chat-messages/${taskId}/stop`, + }, + getConversations: { + method: "GET", + url: () => "/conversations", + }, + getConversationMessages: { + method: "GET", + url: () => "/messages", + }, + renameConversation: { + method: "POST", + url: (conversationId: string) => `/conversations/${conversationId}/name`, + }, + deleteConversation: { + method: "DELETE", + url: (conversationId: string) => `/conversations/${conversationId}`, + }, + runWorkflow: { + method: "POST", + url: () => "/workflows/run", + }, + stopWorkflow: { + method: "POST", + url: (taskId: string) => `/workflows/tasks/${taskId}/stop`, + }, +}; + +export { DifyClient } from "./client/base"; +export { ChatClient } from "./client/chat"; +export { CompletionClient } from "./client/completion"; +export { WorkflowClient } from "./client/workflow"; +export { KnowledgeBaseClient } from "./client/knowledge-base"; +export { WorkspaceClient } from "./client/workspace"; + +export * from "./errors/dify-error"; +export * from "./types/common"; +export * from "./types/annotation"; +export * from "./types/chat"; +export * from "./types/completion"; +export * from "./types/knowledge-base"; +export * from "./types/workflow"; +export * from "./types/workspace"; +export { HttpClient } from "./http/client"; diff --git a/sdks/nodejs-client/src/types/annotation.ts b/sdks/nodejs-client/src/types/annotation.ts new file mode 100644 index 0000000000..dcbd644dab --- /dev/null +++ b/sdks/nodejs-client/src/types/annotation.ts @@ -0,0 +1,18 @@ +export type AnnotationCreateRequest = { + question: string; + answer: string; +}; + +export type AnnotationReplyActionRequest = { + score_threshold: number; + embedding_provider_name: string; + embedding_model_name: string; +}; + +export type AnnotationListOptions = { + page?: number; + limit?: number; + keyword?: string; +}; + +export type AnnotationResponse = Record<string, unknown>; diff --git a/sdks/nodejs-client/src/types/chat.ts b/sdks/nodejs-client/src/types/chat.ts new file mode 100644 index 0000000000..5b627f6cf6 --- /dev/null +++ b/sdks/nodejs-client/src/types/chat.ts @@ -0,0 +1,17 @@ +import type { StreamEvent } from "./common"; + +export type ChatMessageRequest = { + inputs?: Record<string, unknown>; + query: string; + user: string; + response_mode?: "blocking" | "streaming"; + files?: Array<Record<string, unknown>> | null; + conversation_id?: string; + auto_generate_name?: boolean; + workflow_id?: string; + retriever_from?: "app" | "dataset"; +}; + +export type ChatMessageResponse = Record<string, unknown>; + +export type ChatStreamEvent = StreamEvent<Record<string, unknown>>; diff --git a/sdks/nodejs-client/src/types/common.ts b/sdks/nodejs-client/src/types/common.ts new file mode 100644 index 0000000000..00b0fcc756 --- /dev/null +++ b/sdks/nodejs-client/src/types/common.ts @@ -0,0 +1,71 @@ +export const DEFAULT_BASE_URL = "https://api.dify.ai/v1"; +export const DEFAULT_TIMEOUT_SECONDS = 60; +export const DEFAULT_MAX_RETRIES = 3; +export const DEFAULT_RETRY_DELAY_SECONDS = 1; + +export type RequestMethod = "GET" | "POST" | "PATCH" | "PUT" | "DELETE"; + +export type QueryParamValue = + | string + | number + | boolean + | Array<string | number | boolean> + | undefined; + +export type QueryParams = Record<string, QueryParamValue>; + +export type Headers = Record<string, string>; + +export type DifyClientConfig = { + apiKey: string; + baseUrl?: string; + timeout?: number; + maxRetries?: number; + retryDelay?: number; + enableLogging?: boolean; +}; + +export type DifyResponse<T> = { + data: T; + status: number; + headers: Headers; + requestId?: string; +}; + +export type MessageFeedbackRequest = { + messageId: string; + user: string; + rating?: "like" | "dislike" | null; + content?: string | null; +}; + +export type TextToAudioRequest = { + user: string; + text?: string; + message_id?: string; + streaming?: boolean; + voice?: string; +}; + +export type StreamEvent<T = unknown> = { + event?: string; + data: T | string | null; + raw: string; +}; + +export type DifyStream<T = unknown> = AsyncIterable<StreamEvent<T>> & { + data: NodeJS.ReadableStream; + status: number; + headers: Headers; + requestId?: string; + toText(): Promise<string>; + toReadable(): NodeJS.ReadableStream; +}; + +export type BinaryStream = { + data: NodeJS.ReadableStream; + status: number; + headers: Headers; + requestId?: string; + toReadable(): NodeJS.ReadableStream; +}; diff --git a/sdks/nodejs-client/src/types/completion.ts b/sdks/nodejs-client/src/types/completion.ts new file mode 100644 index 0000000000..4074137c5d --- /dev/null +++ b/sdks/nodejs-client/src/types/completion.ts @@ -0,0 +1,13 @@ +import type { StreamEvent } from "./common"; + +export type CompletionRequest = { + inputs?: Record<string, unknown>; + response_mode?: "blocking" | "streaming"; + user: string; + files?: Array<Record<string, unknown>> | null; + retriever_from?: "app" | "dataset"; +}; + +export type CompletionResponse = Record<string, unknown>; + +export type CompletionStreamEvent = StreamEvent<Record<string, unknown>>; diff --git a/sdks/nodejs-client/src/types/knowledge-base.ts b/sdks/nodejs-client/src/types/knowledge-base.ts new file mode 100644 index 0000000000..a4ddef50ea --- /dev/null +++ b/sdks/nodejs-client/src/types/knowledge-base.ts @@ -0,0 +1,184 @@ +export type DatasetListOptions = { + page?: number; + limit?: number; + keyword?: string | null; + tagIds?: string[]; + includeAll?: boolean; +}; + +export type DatasetCreateRequest = { + name: string; + description?: string; + indexing_technique?: "high_quality" | "economy"; + permission?: string | null; + external_knowledge_api_id?: string | null; + provider?: string; + external_knowledge_id?: string | null; + retrieval_model?: Record<string, unknown> | null; + embedding_model?: string | null; + embedding_model_provider?: string | null; +}; + +export type DatasetUpdateRequest = { + name?: string; + description?: string | null; + indexing_technique?: "high_quality" | "economy" | null; + permission?: string | null; + embedding_model?: string | null; + embedding_model_provider?: string | null; + retrieval_model?: Record<string, unknown> | null; + partial_member_list?: Array<Record<string, string>> | null; + external_retrieval_model?: Record<string, unknown> | null; + external_knowledge_id?: string | null; + external_knowledge_api_id?: string | null; +}; + +export type DocumentStatusAction = "enable" | "disable" | "archive" | "un_archive"; + +export type DatasetTagCreateRequest = { + name: string; +}; + +export type DatasetTagUpdateRequest = { + tag_id: string; + name: string; +}; + +export type DatasetTagDeleteRequest = { + tag_id: string; +}; + +export type DatasetTagBindingRequest = { + tag_ids: string[]; + target_id: string; +}; + +export type DatasetTagUnbindingRequest = { + tag_id: string; + target_id: string; +}; + +export type DocumentTextCreateRequest = { + name: string; + text: string; + process_rule?: Record<string, unknown> | null; + original_document_id?: string | null; + doc_form?: string; + doc_language?: string; + indexing_technique?: string | null; + retrieval_model?: Record<string, unknown> | null; + embedding_model?: string | null; + embedding_model_provider?: string | null; +}; + +export type DocumentTextUpdateRequest = { + name?: string | null; + text?: string | null; + process_rule?: Record<string, unknown> | null; + doc_form?: string; + doc_language?: string; + retrieval_model?: Record<string, unknown> | null; +}; + +export type DocumentListOptions = { + page?: number; + limit?: number; + keyword?: string | null; + status?: string | null; +}; + +export type DocumentGetOptions = { + metadata?: "all" | "only" | "without"; +}; + +export type SegmentCreateRequest = { + segments: Array<Record<string, unknown>>; +}; + +export type SegmentUpdateRequest = { + segment: { + content?: string | null; + answer?: string | null; + keywords?: string[] | null; + regenerate_child_chunks?: boolean; + enabled?: boolean | null; + attachment_ids?: string[] | null; + }; +}; + +export type SegmentListOptions = { + page?: number; + limit?: number; + status?: string[]; + keyword?: string | null; +}; + +export type ChildChunkCreateRequest = { + content: string; +}; + +export type ChildChunkUpdateRequest = { + content: string; +}; + +export type ChildChunkListOptions = { + page?: number; + limit?: number; + keyword?: string | null; +}; + +export type MetadataCreateRequest = { + type: "string" | "number" | "time"; + name: string; +}; + +export type MetadataUpdateRequest = { + name: string; + value?: string | number | null; +}; + +export type DocumentMetadataDetail = { + id: string; + name: string; + value?: string | number | null; +}; + +export type DocumentMetadataOperation = { + document_id: string; + metadata_list: DocumentMetadataDetail[]; + partial_update?: boolean; +}; + +export type MetadataOperationRequest = { + operation_data: DocumentMetadataOperation[]; +}; + +export type HitTestingRequest = { + query?: string | null; + retrieval_model?: Record<string, unknown> | null; + external_retrieval_model?: Record<string, unknown> | null; + attachment_ids?: string[] | null; +}; + +export type DatasourcePluginListOptions = { + isPublished?: boolean; +}; + +export type DatasourceNodeRunRequest = { + inputs: Record<string, unknown>; + datasource_type: string; + credential_id?: string | null; + is_published: boolean; +}; + +export type PipelineRunRequest = { + inputs: Record<string, unknown>; + datasource_type: string; + datasource_info_list: Array<Record<string, unknown>>; + start_node_id: string; + is_published: boolean; + response_mode: "streaming" | "blocking"; +}; + +export type KnowledgeBaseResponse = Record<string, unknown>; +export type PipelineStreamEvent = Record<string, unknown>; diff --git a/sdks/nodejs-client/src/types/workflow.ts b/sdks/nodejs-client/src/types/workflow.ts new file mode 100644 index 0000000000..2b507c7352 --- /dev/null +++ b/sdks/nodejs-client/src/types/workflow.ts @@ -0,0 +1,12 @@ +import type { StreamEvent } from "./common"; + +export type WorkflowRunRequest = { + inputs?: Record<string, unknown>; + user: string; + response_mode?: "blocking" | "streaming"; + files?: Array<Record<string, unknown>> | null; +}; + +export type WorkflowRunResponse = Record<string, unknown>; + +export type WorkflowStreamEvent = StreamEvent<Record<string, unknown>>; diff --git a/sdks/nodejs-client/src/types/workspace.ts b/sdks/nodejs-client/src/types/workspace.ts new file mode 100644 index 0000000000..0ab6743063 --- /dev/null +++ b/sdks/nodejs-client/src/types/workspace.ts @@ -0,0 +1,2 @@ +export type WorkspaceModelType = string; +export type WorkspaceModelsResponse = Record<string, unknown>; diff --git a/sdks/nodejs-client/tests/test-utils.js b/sdks/nodejs-client/tests/test-utils.js new file mode 100644 index 0000000000..0d42514e9a --- /dev/null +++ b/sdks/nodejs-client/tests/test-utils.js @@ -0,0 +1,30 @@ +import axios from "axios"; +import { vi } from "vitest"; +import { HttpClient } from "../src/http/client"; + +export const createHttpClient = (configOverrides = {}) => { + const mockRequest = vi.fn(); + vi.spyOn(axios, "create").mockReturnValue({ request: mockRequest }); + const client = new HttpClient({ apiKey: "test", ...configOverrides }); + return { client, mockRequest }; +}; + +export const createHttpClientWithSpies = (configOverrides = {}) => { + const { client, mockRequest } = createHttpClient(configOverrides); + const request = vi + .spyOn(client, "request") + .mockResolvedValue({ data: "ok", status: 200, headers: {} }); + const requestStream = vi + .spyOn(client, "requestStream") + .mockResolvedValue({ data: null }); + const requestBinaryStream = vi + .spyOn(client, "requestBinaryStream") + .mockResolvedValue({ data: null }); + return { + client, + mockRequest, + request, + requestStream, + requestBinaryStream, + }; +}; diff --git a/sdks/nodejs-client/tsconfig.json b/sdks/nodejs-client/tsconfig.json new file mode 100644 index 0000000000..d2da9a2a59 --- /dev/null +++ b/sdks/nodejs-client/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "rootDir": "src", + "outDir": "dist", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "strict": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +} diff --git a/sdks/nodejs-client/tsup.config.ts b/sdks/nodejs-client/tsup.config.ts new file mode 100644 index 0000000000..522382c2a5 --- /dev/null +++ b/sdks/nodejs-client/tsup.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["esm"], + dts: true, + clean: true, + sourcemap: true, + splitting: false, + treeshake: true, + outDir: "dist", +}); diff --git a/sdks/nodejs-client/vitest.config.ts b/sdks/nodejs-client/vitest.config.ts new file mode 100644 index 0000000000..5a0a8637a2 --- /dev/null +++ b/sdks/nodejs-client/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: "node", + include: ["**/*.test.js"], + coverage: { + provider: "v8", + reporter: ["text", "text-summary"], + include: ["src/**/*.ts"], + exclude: ["src/**/*.test.*", "src/**/*.spec.*"], + }, + }, +});