From b754bf80aeb04c3409e6a09132574fa9b5d3c73c Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 8 Nov 2024 14:24:28 +0800 Subject: [PATCH 01/14] fix: custom tools not show --- web/app/components/workflow/block-selector/all-tools.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/app/components/workflow/block-selector/all-tools.tsx b/web/app/components/workflow/block-selector/all-tools.tsx index 43d887a4d5..29c7238113 100644 --- a/web/app/components/workflow/block-selector/all-tools.tsx +++ b/web/app/components/workflow/block-selector/all-tools.tsx @@ -57,6 +57,9 @@ const AllTools = ({ if (activeTab === ToolTypeEnum.Workflow) mergedTools = workflowTools + if (!searchText) + return mergedTools.filter(toolWithProvider => toolWithProvider.tools.length > 0) + return mergedTools.filter((toolWithProvider) => { return toolWithProvider.tools.some((tool) => { return tool.label[language].toLowerCase().includes(searchText.toLowerCase()) @@ -64,6 +67,7 @@ const AllTools = ({ }) }, [activeTab, buildInTools, customTools, workflowTools, searchText, language]) + console.log(activeTab, customTools, tools) const { queryPluginsWithDebounced: fetchPlugins, plugins: notInstalledPlugins = [], From 6fcebf3ecdd3f0079279432c767a2222fbdd7c45 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 8 Nov 2024 14:26:52 +0800 Subject: [PATCH 02/14] chore: remove log --- web/app/components/workflow/block-selector/all-tools.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/app/components/workflow/block-selector/all-tools.tsx b/web/app/components/workflow/block-selector/all-tools.tsx index 29c7238113..da7640439f 100644 --- a/web/app/components/workflow/block-selector/all-tools.tsx +++ b/web/app/components/workflow/block-selector/all-tools.tsx @@ -67,7 +67,6 @@ const AllTools = ({ }) }, [activeTab, buildInTools, customTools, workflowTools, searchText, language]) - console.log(activeTab, customTools, tools) const { queryPluginsWithDebounced: fetchPlugins, plugins: notInstalledPlugins = [], From edc2fe050a4b3b7a695f2419bd1578dbe77abb1c Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 8 Nov 2024 15:10:06 +0800 Subject: [PATCH 03/14] chore: debug info use query --- .../plugins/plugin-page/debug-info.tsx | 17 +++++++---------- web/service/use-plugins.ts | 9 ++++++++- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/web/app/components/plugins/plugin-page/debug-info.tsx b/web/app/components/plugins/plugin-page/debug-info.tsx index de8149fcd2..9c777c5dd9 100644 --- a/web/app/components/plugins/plugin-page/debug-info.tsx +++ b/web/app/components/plugins/plugin-page/debug-info.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React, { useEffect } from 'react' +import React from 'react' import { RiArrowRightUpLine, RiBugLine, @@ -9,19 +9,16 @@ import { useTranslation } from 'react-i18next' import KeyValueItem from '../base/key-value-item' import Tooltip from '@/app/components/base/tooltip' import Button from '@/app/components/base/button' -import type { DebugInfo as DebugInfoTypes } from '../types' -import { fetchDebugKey } from '@/service/plugins' +import { useDebugKey } from '@/service/use-plugins' const i18nPrefix = 'plugin.debugInfo' const DebugInfo: FC = () => { const { t } = useTranslation() - const [info, setInfo] = React.useState(null) - useEffect(() => { - fetchDebugKey().then((res) => { - setInfo(res) - }) - }, []) + const { data: info, isLoading } = useDebugKey() + + if (isLoading) return null + return ( {
{ }) } } + +export const useDebugKey = () => { + return useQuery({ + queryKey: [NAME_SPACE, 'debugKey'], + queryFn: () => get('/workspaces/current/plugin/debugging-key'), + }) +} From c6a6c5308438466d50a12fc75f142278ed2e56c4 Mon Sep 17 00:00:00 2001 From: twwu Date: Fri, 8 Nov 2024 15:26:59 +0800 Subject: [PATCH 04/14] chore: update theme var define --- web/themes/dark.css | 113 +++++++++++++++++++++--- web/themes/light.css | 111 ++++++++++++++++++++--- web/themes/tailwind-theme-var-define.ts | 101 +++++++++++++++++++-- 3 files changed, 299 insertions(+), 26 deletions(-) diff --git a/web/themes/dark.css b/web/themes/dark.css index 3440a1a7a8..6b69342730 100644 --- a/web/themes/dark.css +++ b/web/themes/dark.css @@ -95,10 +95,11 @@ html[data-theme="dark"] { --color-components-checkbox-border-disabled: #FFFFFF03; --color-components-checkbox-bg-unchecked: #FFFFFF08; --color-components-checkbox-bg-unchecked-hover: #FFFFFF0D; + --color-components-checkbox-bg-disabled-checked: #155AEF33; --color-components-radio-border-checked: #296DFF; --color-components-radio-border-checked-hover: #5289FF; - --color-components-radio-border-checked-disabled: #FFFFFF14; + --color-components-radio-border-checked-disabled: #155AEF33; --color-components-radio-bg-disabled: #FFFFFF08; --color-components-radio-border: #FFFFFF66; --color-components-radio-border-hover: #FFFFFF99; @@ -135,6 +136,9 @@ html[data-theme="dark"] { --color-components-panel-on-panel-item-bg: #27272B; --color-components-panel-on-panel-item-bg-hover: #3A3A40; --color-components-panel-on-panel-item-bg-alt: #3A3A40; + --color-components-panel-on-panel-item-bg-transparent: #2C2C30F2; + --color-components-panel-on-panel-item-bg-hover-transparent: #3A3A4000; + --color-components-panel-on-panel-item-bg-destructive-hover-transparent: #FFFBFA00; --color-components-panel-bg-transparent: #22222500; @@ -208,10 +212,12 @@ html[data-theme="dark"] { --color-components-actionbar-bg: #222225; --color-components-actionbar-border: #C8CEDA14; + --color-components-actionbar-bg-accent: #27272B; + --color-components-actionbar-border-accent: #5289FF; --color-components-dropzone-bg-alt: #18181BCC; --color-components-dropzone-bg: #18181B66; - --color-components-dropzone-bg-accent: #155AEF24; + --color-components-dropzone-bg-accent: #155AEF33; --color-components-dropzone-border: #C8CEDA24; --color-components-dropzone-border-alt: #C8CEDA33; --color-components-dropzone-border-accent: #84ABFF; @@ -228,6 +234,14 @@ html[data-theme="dark"] { --color-components-progress-gray-border: #98A2B2; --color-components-progress-gray-bg: #C8CEDA05; + --color-components-progress-warning-progress: #FDB022; + --color-components-progress-warning-border: #FDB022; + --color-components-progress-warning-bg: #F790090A; + + --color-components-progress-error-progress: #F97066; + --color-components-progress-error-border: #F97066; + --color-components-progress-error-bg: #F044380A; + --color-components-chat-input-audio-bg: #155AEF33; --color-components-chat-input-audio-wave-default: #C8CEDA24; --color-components-chat-input-bg-mask-1: #18181B0A; @@ -236,13 +250,70 @@ html[data-theme="dark"] { --color-components-chat-input-audio-wave-active: #84ABFF; --color-components-chat-input-audio-bg-alt: #18181BE5; - --color-components-Avatar-shape-fill-stop-0: #FFFFFFF2; - --color-components-Avatar-shape-fill-stop-100: #FFFFFFCC; + --color-components-avatar-shape-fill-stop-0: #FFFFFFF2; + --color-components-avatar-shape-fill-stop-100: #FFFFFFCC; - --color-components-Avatar-bg-mask-stop-0: #FFFFFF33; - --color-components-Avatar-bg-mask-stop-100: #FFFFFF08; + --color-components-avatar-bg-mask-stop-0: #FFFFFF33; + --color-components-avatar-bg-mask-stop-100: #FFFFFF08; - --color-components-Avatar-default-avatar-bg: #222225; + --color-components-avatar-default-avatar-bg: #222225; + --color-components-avatar-mask-darkmode-dimmed: #0000001F; + + --color-components-label-gray: #C8CEDA24; + + --color-components-premium-badge-blue-bg-stop-0: #5289FF; + --color-components-premium-badge-blue-bg-stop-100: #296DFF; + --color-components-premium-badge-blue-stroke-stop-0: #FFFFFF33; + --color-components-premium-badge-blue-stroke-stop-100: #296DFF; + --color-components-premium-badge-blue-text-stop-0: #EFF4FF; + --color-components-premium-badge-blue-text-stop-100: #B2CAFF; + --color-components-premium-badge-blue-glow: #004AEB; + --color-components-premium-badge-blue-bg-stop-0-hover: #84ABFF; + --color-components-premium-badge-blue-bg-stop-100-hover: #004AEB; + --color-components-premium-badge-blue-glow-hover: #D1E0FF; + --color-components-premium-badge-blue-stroke-stop-0-hover: #FFFFFF80; + --color-components-premium-badge-blue-stroke-stop-100-hover: #296DFF; + + --color-components-premium-badge-highlight-stop-0: #FFFFFF1F; + --color-components-premium-badge-highlight-stop-100: #FFFFFF33; + --color-components-premium-badge-indigo-bg-stop-0: #6172F3; + --color-components-premium-badge-indigo-bg-stop-100: #3538CD; + --color-components-premium-badge-indigo-stroke-stop-0: #FFFFFF33; + --color-components-premium-badge-indigo-stroke-stop-100: #444CE7; + --color-components-premium-badge-indigo-text-stop-0: #EEF4FF; + --color-components-premium-badge-indigo-text-stop-100: #C7D7FE; + --color-components-premium-badge-indigo-glow: #3538CD; + --color-components-premium-badge-indigo-glow-hover: #E0EAFF; + --color-components-premium-badge-indigo-bg-stop-0-hover: #A4BCFD; + --color-components-premium-badge-indigo-bg-stop-100-hover: #3538CD; + --color-components-premium-badge-indigo-stroke-stop-0-hover: #FFFFFF80; + --color-components-premium-badge-indigo-stroke-stop-100-hover: #444CE7; + + --color-components-premium-badge-grey-bg-stop-0: #676F83; + --color-components-premium-badge-grey-bg-stop-100: #495464; + --color-components-premium-badge-grey-stroke-stop-0: #FFFFFF1F; + --color-components-premium-badge-grey-stroke-stop-100: #495464; + --color-components-premium-badge-grey-text-stop-0: #F9FAFB; + --color-components-premium-badge-grey-text-stop-100: #E9EBF0; + --color-components-premium-badge-grey-glow: #354052; + --color-components-premium-badge-grey-glow-hover: #F2F4F7; + --color-components-premium-badge-grey-bg-stop-0-hover: #98A2B2; + --color-components-premium-badge-grey-bg-stop-100-hover: #354052; + --color-components-premium-badge-grey-stroke-stop-0-hover: #FFFFFF80; + --color-components-premium-badge-grey-stroke-stop-100-hover: #676F83; + + --color-components-premium-badge-orange-bg-stop-0: #FF692E; + --color-components-premium-badge-orange-bg-stop-100: #E04F16; + --color-components-premium-badge-orange-stroke-stop-0: #FFFFFF33; + --color-components-premium-badge-orange-stroke-stop-100: #FF4405; + --color-components-premium-badge-orange-text-stop-0: #FEF6EE; + --color-components-premium-badge-orange-text-stop-100: #F9DBAF; + --color-components-premium-badge-orange-glow: #B93815; + --color-components-premium-badge-orange-glow-hover: #FDEAD7; + --color-components-premium-badge-orange-bg-stop-0-hover: #FF692E; + --color-components-premium-badge-orange-bg-stop-100-hover: #B93815; + --color-components-premium-badge-orange-stroke-stop-0-hover: #FFFFFF80; + --color-components-premium-badge-orange-stroke-stop-100-hover: #FF4405; --color-text-primary: #FBFBFC; --color-text-secondary: #D9D9DE; @@ -265,6 +336,7 @@ html[data-theme="dark"] { --color-text-logo-text: #E9E9EC; --color-text-empty-state-icon: #C8CEDA4D; --color-text-inverted: #FFFFFF; + --color-text-inverted-dimm: #FFFFFFCC; --color-background-body: #1D1D20; --color-background-default-subtle: #222225; @@ -301,6 +373,7 @@ html[data-theme="dark"] { --color-background-overlay-alt: #18181B66; --color-background-surface-white: #FFFFFFE5; --color-background-overlay-destructive: #F044384D; + --color-background-overlay-backdrop: #18181BF2; --color-shadow-shadow-1: #0000000D; --color-shadow-shadow-3: #0000001A; @@ -316,14 +389,26 @@ html[data-theme="dark"] { --color-workflow-block-border: #FFFFFF14; --color-workflow-block-parma-bg: #FFFFFF0D; --color-workflow-block-bg: #27272B; + --color-workflow-block-bg-transparent: #27272BF5; --color-workflow-block-border-highlight: #C8CEDA33; --color-workflow-canvas-workflow-dot-color: #8585AD26; --color-workflow-canvas-workflow-bg: #1D1D20; - --color-workflow-link-line-active: #296DFF; + --color-workflow-link-line-active: #5289FF; --color-workflow-link-line-normal: #676F83; - --color-workflow-link-line-handle: #296DFF; + --color-workflow-link-line-handle: #5289FF; + --color-workflow-link-line-normal-transparent: #676F8333; + --color-workflow-link-line-failure-active: #FDB022; + --color-workflow-link-line-failure-handle: #FDB022; + --color-workflow-link-line-failure-button-bg: #F79009; + --color-workflow-link-line-failure-button-hover: #DC6803; + + --color-workflow-link-line-success-active: #47CD89; + --color-workflow-link-line-success-handle: #47CD89; + + --color-workflow-link-line-error-active: #F97066; + --color-workflow-link-line-error-handle: #F97066; --color-workflow-minimap-bg: #27272B; --color-workflow-minimap-block: #C8CEDA14; @@ -334,8 +419,8 @@ html[data-theme="dark"] { --color-workflow-display-success-vignette-color: #17B26A40; --color-workflow-display-success-bg-line-pattern: #18181BCC; - --color-workflow-display-glass-1: #FFFFFF03; - --color-workflow-display-glass-2: #FFFFFF08; + --color-workflow-display-glass-1: #FFFFFF08; + --color-workflow-display-glass-2: #FFFFFF0D; --color-workflow-display-vignette-dark: #00000066; --color-workflow-display-highlight: #FFFFFF1F; --color-workflow-display-outline: #18181BF2; @@ -424,6 +509,7 @@ html[data-theme="dark"] { --color-util-colors-orange-orange-500: #EF6820; --color-util-colors-orange-orange-600: #F38744; --color-util-colors-orange-orange-700: #F7B27A; + --color-util-colors-orange-orange-100-transparent: #77291700; --color-util-colors-pink-pink-50: #4E0D30; --color-util-colors-pink-pink-100: #851651; @@ -602,5 +688,10 @@ html[data-theme="dark"] { --color-chatbot-bg: linear-gradient(180deg, rgba(34, 34, 37, 0.90) 0%, rgba(29, 29, 32, 0.90) 90.48%); --color-chat-bubble-bg: linear-gradient(180deg, rgba(200, 206, 218, 0.08) 0%, rgba(200, 206, 218, 0.02) 100%); --color-third-party-Github: #FFFFFF; + --color-third-party-Github-tertiary: #C8CEDA99; + --color-third-party-Github-secondary: #D9D9DE; + --color-third-party-model-bg-openai: #121212; + --color-third-party-model-bg-anthropic: #1D1917; + --color-third-party-model-bg-default: #0B0B0E; --color-workflow-process-bg: linear-gradient(90deg, rgba(24, 24, 27, 0.25) 0%, rgba(24, 24, 27, 0.04) 100%); } \ No newline at end of file diff --git a/web/themes/light.css b/web/themes/light.css index 717226e462..0d73875c29 100644 --- a/web/themes/light.css +++ b/web/themes/light.css @@ -86,7 +86,7 @@ html[data-theme="light"] { --color-components-button-secondary-accent-border-disabled: #1018280A; --color-components-checkbox-icon: #FFFFFF; - --color-components-checkbox-icon-disabled: #D0D5DC; + --color-components-checkbox-icon-disabled: #FFFFFF80; --color-components-checkbox-bg: #155AEF; --color-components-checkbox-bg-hover: #004AEB; --color-components-checkbox-bg-disabled: #F2F4F7; @@ -95,10 +95,11 @@ html[data-theme="light"] { --color-components-checkbox-border-disabled: #18181B0A; --color-components-checkbox-bg-unchecked: #FFFFFF; --color-components-checkbox-bg-unchecked-hover: #FFFFFF; + --color-components-checkbox-bg-disabled-checked: #B2CAFF; --color-components-radio-border-checked: #155AEF; --color-components-radio-border-checked-hover: #004AEB; - --color-components-radio-border-checked-disabled: #F2F4F7; + --color-components-radio-border-checked-disabled: #B2CAFF; --color-components-radio-bg-disabled: #FFFFFF00; --color-components-radio-border: #D0D5DC; --color-components-radio-border-hover: #98A2B2; @@ -135,6 +136,9 @@ html[data-theme="light"] { --color-components-panel-on-panel-item-bg: #FFFFFF; --color-components-panel-on-panel-item-bg-hover: #F9FAFB; --color-components-panel-on-panel-item-bg-alt: #F9FAFB; + --color-components-panel-on-panel-item-bg-transparent: #FFFFFFF2; + --color-components-panel-on-panel-item-bg-hover-transparent: #F9FAFB00; + --color-components-panel-on-panel-item-bg-destructive-hover-transparent: #FEF3F200; --color-components-panel-bg-transparent: #FFFFFF00; @@ -161,10 +165,10 @@ html[data-theme="light"] { --color-components-segmented-control-item-active-accent-bg: #FFFFFF; --color-components-segmented-control-item-active-accent-border: #FFFFFF; - --color-components-option-card-option-bg: #F9FAFB; + --color-components-option-card-option-bg: #FCFCFD; --color-components-option-card-option-selected-bg: #FFFFFF; --color-components-option-card-option-selected-border: #296DFF; - --color-components-option-card-option-border: #F2F4F7; + --color-components-option-card-option-border: #E9EBF0; --color-components-option-card-option-bg-hover: #FFFFFF; --color-components-option-card-option-border-hover: #D0D5DC; @@ -208,10 +212,12 @@ html[data-theme="light"] { --color-components-actionbar-bg: #FFFFFFF2; --color-components-actionbar-border: #1018280A; + --color-components-actionbar-bg-accent: #F5F7FF; + --color-components-actionbar-border-accent: #B2CAFF; --color-components-dropzone-bg-alt: #F2F4F7; --color-components-dropzone-bg: #F9FAFB; - --color-components-dropzone-bg-accent: #EFF4FF; + --color-components-dropzone-bg-accent: #155AEF24; --color-components-dropzone-border: #10182814; --color-components-dropzone-border-alt: #10182833; --color-components-dropzone-border-accent: #84ABFF; @@ -228,6 +234,14 @@ html[data-theme="light"] { --color-components-progress-gray-border: #98A2B2; --color-components-progress-gray-bg: #C8CEDA05; + --color-components-progress-warning-progress: #F79009; + --color-components-progress-warning-border: #F79009; + --color-components-progress-warning-bg: #F790090A; + + --color-components-progress-error-progress: #F04438; + --color-components-progress-error-border: #F04438; + --color-components-progress-error-bg: #F044380A; + --color-components-chat-input-audio-bg: #EFF4FF; --color-components-chat-input-audio-wave-default: #155AEF33; --color-components-chat-input-bg-mask-1: #FFFFFF03; @@ -236,13 +250,70 @@ html[data-theme="light"] { --color-components-chat-input-audio-wave-active: #296DFF; --color-components-chat-input-audio-bg-alt: #FCFCFD; - --color-components-Avatar-shape-fill-stop-0: #FFFFFF; - --color-components-Avatar-shape-fill-stop-100: #FFFFFFE5; + --color-components-avatar-shape-fill-stop-0: #FFFFFF; + --color-components-avatar-shape-fill-stop-100: #FFFFFFE5; - --color-components-Avatar-bg-mask-stop-0: #FFFFFF1F; - --color-components-Avatar-bg-mask-stop-100: #FFFFFF14; + --color-components-avatar-bg-mask-stop-0: #FFFFFF1F; + --color-components-avatar-bg-mask-stop-100: #FFFFFF14; - --color-components-Avatar-default-avatar-bg: #D0D5DC; + --color-components-avatar-default-avatar-bg: #D0D5DC; + --color-components-avatar-mask-darkmode-dimmed: #FFFFFF00; + + --color-components-label-gray: #F2F4F7; + + --color-components-premium-badge-blue-bg-stop-0: #5289FF; + --color-components-premium-badge-blue-bg-stop-100: #155AEF; + --color-components-premium-badge-blue-stroke-stop-0: #FFFFFFF2; + --color-components-premium-badge-blue-stroke-stop-100: #155AEF; + --color-components-premium-badge-blue-text-stop-0: #F5F7FF; + --color-components-premium-badge-blue-text-stop-100: #D1E0FF; + --color-components-premium-badge-blue-glow: #00329E; + --color-components-premium-badge-blue-bg-stop-0-hover: #296DFF; + --color-components-premium-badge-blue-bg-stop-100-hover: #004AEB; + --color-components-premium-badge-blue-glow-hover: #84ABFF; + --color-components-premium-badge-blue-stroke-stop-0-hover: #FFFFFFF2; + --color-components-premium-badge-blue-stroke-stop-100-hover: #00329E; + + --color-components-premium-badge-highlight-stop-0: #FFFFFF1F; + --color-components-premium-badge-highlight-stop-100: #FFFFFF4D; + --color-components-premium-badge-indigo-bg-stop-0: #8098F9; + --color-components-premium-badge-indigo-bg-stop-100: #444CE7; + --color-components-premium-badge-indigo-stroke-stop-0: #FFFFFFF2; + --color-components-premium-badge-indigo-stroke-stop-100: #6172F3; + --color-components-premium-badge-indigo-text-stop-0: #F5F8FF; + --color-components-premium-badge-indigo-text-stop-100: #E0EAFF; + --color-components-premium-badge-indigo-glow: #2D3282; + --color-components-premium-badge-indigo-glow-hover: #A4BCFD; + --color-components-premium-badge-indigo-bg-stop-0-hover: #6172F3; + --color-components-premium-badge-indigo-bg-stop-100-hover: #2D31A6; + --color-components-premium-badge-indigo-stroke-stop-0-hover: #FFFFFFF2; + --color-components-premium-badge-indigo-stroke-stop-100-hover: #2D31A6; + + --color-components-premium-badge-grey-bg-stop-0: #98A2B2; + --color-components-premium-badge-grey-bg-stop-100: #676F83; + --color-components-premium-badge-grey-stroke-stop-0: #FFFFFFF2; + --color-components-premium-badge-grey-stroke-stop-100: #676F83; + --color-components-premium-badge-grey-text-stop-0: #FCFCFD; + --color-components-premium-badge-grey-text-stop-100: #F2F4F7; + --color-components-premium-badge-grey-glow: #101828; + --color-components-premium-badge-grey-glow-hover: #D0D5DC; + --color-components-premium-badge-grey-bg-stop-0-hover: #676F83; + --color-components-premium-badge-grey-bg-stop-100-hover: #354052; + --color-components-premium-badge-grey-stroke-stop-0-hover: #FFFFFFF2; + --color-components-premium-badge-grey-stroke-stop-100-hover: #354052; + + --color-components-premium-badge-orange-bg-stop-0: #FF692E; + --color-components-premium-badge-orange-bg-stop-100: #E04F16; + --color-components-premium-badge-orange-stroke-stop-0: #FFFFFFF2; + --color-components-premium-badge-orange-stroke-stop-100: #E62E05; + --color-components-premium-badge-orange-text-stop-0: #FEFAF5; + --color-components-premium-badge-orange-text-stop-100: #FDEAD7; + --color-components-premium-badge-orange-glow: #772917; + --color-components-premium-badge-orange-glow-hover: #F7B27A; + --color-components-premium-badge-orange-bg-stop-0-hover: #FF4405; + --color-components-premium-badge-orange-bg-stop-100-hover: #B93815; + --color-components-premium-badge-orange-stroke-stop-0-hover: #FFFFFFF2; + --color-components-premium-badge-orange-stroke-stop-100-hover: #BC1B06; --color-text-primary: #101828; --color-text-secondary: #354052; @@ -265,6 +336,7 @@ html[data-theme="light"] { --color-text-logo-text: #18222F; --color-text-empty-state-icon: #D0D5DC; --color-text-inverted: #000000; + --color-text-inverted-dimm: #000000F2; --color-background-body: #F2F4F7; --color-background-default-subtle: #FCFCFD; @@ -301,6 +373,7 @@ html[data-theme="light"] { --color-background-overlay-alt: #10182866; --color-background-surface-white: #FFFFFFF2; --color-background-overlay-destructive: #F044384D; + --color-background-overlay-backdrop: #F2F4F7F2; --color-shadow-shadow-1: #09090B08; --color-shadow-shadow-3: #09090B0D; @@ -316,6 +389,7 @@ html[data-theme="light"] { --color-workflow-block-border: #FFFFFF; --color-workflow-block-parma-bg: #F2F4F7; --color-workflow-block-bg: #FCFCFD; + --color-workflow-block-bg-transparent: #FCFCFDE5; --color-workflow-block-border-highlight: #155AEF24; --color-workflow-canvas-workflow-dot-color: #8585AD26; @@ -324,6 +398,17 @@ html[data-theme="light"] { --color-workflow-link-line-active: #296DFF; --color-workflow-link-line-normal: #D0D5DC; --color-workflow-link-line-handle: #296DFF; + --color-workflow-link-line-normal-transparent: #D0D5DC33; + --color-workflow-link-line-failure-active: #F79009; + --color-workflow-link-line-failure-handle: #F79009; + --color-workflow-link-line-failure-button-bg: #DC6803; + --color-workflow-link-line-failure-button-hover: #B54708; + + --color-workflow-link-line-success-active: #17B26A; + --color-workflow-link-line-success-handle: #17B26A; + + --color-workflow-link-line-error-active: #F04438; + --color-workflow-link-line-error-handle: #F04438; --color-workflow-minimap-bg: #E9EBF0; --color-workflow-minimap-block: #C8CEDA4D; @@ -424,6 +509,7 @@ html[data-theme="light"] { --color-util-colors-orange-orange-500: #EF6820; --color-util-colors-orange-orange-600: #E04F16; --color-util-colors-orange-orange-700: #B93815; + --color-util-colors-orange-orange-100-transparent: #FDEAD700; --color-util-colors-pink-pink-50: #FDF2FA; --color-util-colors-pink-pink-100: #FCE7F6; @@ -602,5 +688,10 @@ html[data-theme="light"] { --color-chatbot-bg: linear-gradient(180deg, rgba(249, 250, 251, 0.90) 0%, rgba(242, 244, 247, 0.90) 90.48%); --color-chat-bubble-bg: linear-gradient(180deg, #FFF 0%, rgba(255, 255, 255, 0.60) 100%); --color-third-party-Github: #1B1F24; + --color-third-party-Github-tertiary: #1B1F24; + --color-third-party-Github-secondary: #1B1F24; + --color-third-party-model-bg-openai: #E3E5E8; + --color-third-party-model-bg-anthropic: #EEEDE7; + --color-third-party-model-bg-default: #F9FAFB; --color-workflow-process-bg: linear-gradient(90deg, rgba(200, 206, 218, 0.20) 0%, rgba(200, 206, 218, 0.04) 100%); } \ No newline at end of file diff --git a/web/themes/tailwind-theme-var-define.ts b/web/themes/tailwind-theme-var-define.ts index 643c96d1a1..0cd3559283 100644 --- a/web/themes/tailwind-theme-var-define.ts +++ b/web/themes/tailwind-theme-var-define.ts @@ -95,6 +95,7 @@ const vars = { 'components-checkbox-border-disabled': 'var(--color-components-checkbox-border-disabled)', 'components-checkbox-bg-unchecked': 'var(--color-components-checkbox-bg-unchecked)', 'components-checkbox-bg-unchecked-hover': 'var(--color-components-checkbox-bg-unchecked-hover)', + 'components-checkbox-bg-disabled-checked': 'var(--color-components-checkbox-bg-disabled-checked)', 'components-radio-border-checked': 'var(--color-components-radio-border-checked)', 'components-radio-border-checked-hover': 'var(--color-components-radio-border-checked-hover)', @@ -135,6 +136,9 @@ const vars = { 'components-panel-on-panel-item-bg': 'var(--color-components-panel-on-panel-item-bg)', 'components-panel-on-panel-item-bg-hover': 'var(--color-components-panel-on-panel-item-bg-hover)', 'components-panel-on-panel-item-bg-alt': 'var(--color-components-panel-on-panel-item-bg-alt)', + 'components-panel-on-panel-item-bg-transparent': 'var(--color-components-panel-on-panel-item-bg-transparent)', + 'components-panel-on-panel-item-bg-hover-transparent': 'var(--color-components-panel-on-panel-item-bg-hover-transparent)', + 'components-panel-on-panel-item-bg-destructive-hover-transparent': 'var(--color-components-panel-on-panel-item-bg-destructive-hover-transparent)', 'components-panel-bg-transparent': 'var(--color-components-panel-bg-transparent)', @@ -208,6 +212,8 @@ const vars = { 'components-actionbar-bg': 'var(--color-components-actionbar-bg)', 'components-actionbar-border': 'var(--color-components-actionbar-border)', + 'components-actionbar-bg-accent': 'var(--color-components-actionbar-bg-accent)', + 'components-actionbar-border-accent': 'var(--color-components-actionbar-border-accent)', 'components-dropzone-bg-alt': 'var(--color-components-dropzone-bg-alt)', 'components-dropzone-bg': 'var(--color-components-dropzone-bg)', @@ -228,6 +234,14 @@ const vars = { 'components-progress-gray-border': 'var(--color-components-progress-gray-border)', 'components-progress-gray-bg': 'var(--color-components-progress-gray-bg)', + 'components-progress-warning-progress': 'var(--color-components-progress-warning-progress)', + 'components-progress-warning-border': 'var(--color-components-progress-warning-border)', + 'components-progress-warning-bg': 'var(--color-components-progress-warning-bg)', + + 'components-progress-error-progress': 'var(--color-components-progress-error-progress)', + 'components-progress-error-border': 'var(--color-components-progress-error-border)', + 'components-progress-error-bg': 'var(--color-components-progress-error-bg)', + 'components-chat-input-audio-bg': 'var(--color-components-chat-input-audio-bg)', 'components-chat-input-audio-wave-default': 'var(--color-components-chat-input-audio-wave-default)', 'components-chat-input-bg-mask-1': 'var(--color-components-chat-input-bg-mask-1)', @@ -236,13 +250,70 @@ const vars = { 'components-chat-input-audio-wave-active': 'var(--color-components-chat-input-audio-wave-active)', 'components-chat-input-audio-bg-alt': 'var(--color-components-chat-input-audio-bg-alt)', - 'components-Avatar-shape-fill-stop-0': 'var(--color-components-Avatar-shape-fill-stop-0)', - 'components-Avatar-shape-fill-stop-100': 'var(--color-components-Avatar-shape-fill-stop-100)', + 'components-avatar-shape-fill-stop-0': 'var(--color-components-avatar-shape-fill-stop-0)', + 'components-avatar-shape-fill-stop-100': 'var(--color-components-avatar-shape-fill-stop-100)', - 'components-Avatar-bg-mask-stop-0': 'var(--color-components-Avatar-bg-mask-stop-0)', - 'components-Avatar-bg-mask-stop-100': 'var(--color-components-Avatar-bg-mask-stop-100)', + 'components-avatar-bg-mask-stop-0': 'var(--color-components-avatar-bg-mask-stop-0)', + 'components-avatar-bg-mask-stop-100': 'var(--color-components-avatar-bg-mask-stop-100)', - 'components-Avatar-default-avatar-bg': 'var(--color-components-Avatar-default-avatar-bg)', + 'components-avatar-default-avatar-bg': 'var(--color-components-avatar-default-avatar-bg)', + 'components-avatar-mask-darkmode-dimmed': 'var(--color-components-avatar-mask-darkmode-dimmed)', + + 'components-label-gray': 'var(--color-components-label-gray)', + + 'components-premium-badge-blue-bg-stop-0': 'var(--color-components-premium-badge-blue-bg-stop-0)', + 'components-premium-badge-blue-bg-stop-100': 'var(--color-components-premium-badge-blue-bg-stop-100)', + 'components-premium-badge-blue-stroke-stop-0': 'var(--color-components-premium-badge-blue-stroke-stop-0)', + 'components-premium-badge-blue-stroke-stop-100': 'var(--color-components-premium-badge-blue-stroke-stop-100)', + 'components-premium-badge-blue-text-stop-0': 'var(--color-components-premium-badge-blue-text-stop-0)', + 'components-premium-badge-blue-text-stop-100': 'var(--color-components-premium-badge-blue-text-stop-100)', + 'components-premium-badge-blue-glow': 'var(--color-components-premium-badge-blue-glow)', + 'components-premium-badge-blue-bg-stop-0-hover': 'var(--color-components-premium-badge-blue-bg-stop-0-hover)', + 'components-premium-badge-blue-bg-stop-100-hover': 'var(--color-components-premium-badge-blue-bg-stop-100-hover)', + 'components-premium-badge-blue-glow-hover': 'var(--color-components-premium-badge-blue-glow-hover)', + 'components-premium-badge-blue-stroke-stop-0-hover': 'var(--color-components-premium-badge-blue-stroke-stop-0-hover)', + 'components-premium-badge-blue-stroke-stop-100-hover': 'var(--color-components-premium-badge-blue-stroke-stop-100-hover)', + + 'components-premium-badge-highlight-stop-0': 'var(--color-components-premium-badge-highlight-stop-0)', + 'components-premium-badge-highlight-stop-100': 'var(--color-components-premium-badge-highlight-stop-100)', + 'components-premium-badge-indigo-bg-stop-0': 'var(--color-components-premium-badge-indigo-bg-stop-0)', + 'components-premium-badge-indigo-bg-stop-100': 'var(--color-components-premium-badge-indigo-bg-stop-100)', + 'components-premium-badge-indigo-stroke-stop-0': 'var(--color-components-premium-badge-indigo-stroke-stop-0)', + 'components-premium-badge-indigo-stroke-stop-100': 'var(--color-components-premium-badge-indigo-stroke-stop-100)', + 'components-premium-badge-indigo-text-stop-0': 'var(--color-components-premium-badge-indigo-text-stop-0)', + 'components-premium-badge-indigo-text-stop-100': 'var(--color-components-premium-badge-indigo-text-stop-100)', + 'components-premium-badge-indigo-glow': 'var(--color-components-premium-badge-indigo-glow)', + 'components-premium-badge-indigo-glow-hover': 'var(--color-components-premium-badge-indigo-glow-hover)', + 'components-premium-badge-indigo-bg-stop-0-hover': 'var(--color-components-premium-badge-indigo-bg-stop-0-hover)', + 'components-premium-badge-indigo-bg-stop-100-hover': 'var(--color-components-premium-badge-indigo-bg-stop-100-hover)', + 'components-premium-badge-indigo-stroke-stop-0-hover': 'var(--color-components-premium-badge-indigo-stroke-stop-0-hover)', + 'components-premium-badge-indigo-stroke-stop-100-hover': 'var(--color-components-premium-badge-indigo-stroke-stop-100-hover)', + + 'components-premium-badge-grey-bg-stop-0': 'var(--color-components-premium-badge-grey-bg-stop-0)', + 'components-premium-badge-grey-bg-stop-100': 'var(--color-components-premium-badge-grey-bg-stop-100)', + 'components-premium-badge-grey-stroke-stop-0': 'var(--color-components-premium-badge-grey-stroke-stop-0)', + 'components-premium-badge-grey-stroke-stop-100': 'var(--color-components-premium-badge-grey-stroke-stop-100)', + 'components-premium-badge-grey-text-stop-0': 'var(--color-components-premium-badge-grey-text-stop-0)', + 'components-premium-badge-grey-text-stop-100': 'var(--color-components-premium-badge-grey-text-stop-100)', + 'components-premium-badge-grey-glow': 'var(--color-components-premium-badge-grey-glow)', + 'components-premium-badge-grey-glow-hover': 'var(--color-components-premium-badge-grey-glow-hover)', + 'components-premium-badge-grey-bg-stop-0-hover': 'var(--color-components-premium-badge-grey-bg-stop-0-hover)', + 'components-premium-badge-grey-bg-stop-100-hover': 'var(--color-components-premium-badge-grey-bg-stop-100-hover)', + 'components-premium-badge-grey-stroke-stop-0-hover': 'var(--color-components-premium-badge-grey-stroke-stop-0-hover)', + 'components-premium-badge-grey-stroke-stop-100-hover': 'var(--color-components-premium-badge-grey-stroke-stop-100-hover)', + + 'components-premium-badge-orange-bg-stop-0': 'var(--color-components-premium-badge-orange-bg-stop-0)', + 'components-premium-badge-orange-bg-stop-100': 'var(--color-components-premium-badge-orange-bg-stop-100)', + 'components-premium-badge-orange-stroke-stop-0': 'var(--color-components-premium-badge-orange-stroke-stop-0)', + 'components-premium-badge-orange-stroke-stop-100': 'var(--color-components-premium-badge-orange-stroke-stop-100)', + 'components-premium-badge-orange-text-stop-0': 'var(--color-components-premium-badge-orange-text-stop-0)', + 'components-premium-badge-orange-text-stop-100': 'var(--color-components-premium-badge-orange-text-stop-100)', + 'components-premium-badge-orange-glow': 'var(--color-components-premium-badge-orange-glow)', + 'components-premium-badge-orange-glow-hover': 'var(--color-components-premium-badge-orange-glow-hover)', + 'components-premium-badge-orange-bg-stop-0-hover': 'var(--color-components-premium-badge-orange-bg-stop-0-hover)', + 'components-premium-badge-orange-bg-stop-100-hover': 'var(--color-components-premium-badge-orange-bg-stop-100-hover)', + 'components-premium-badge-orange-stroke-stop-0-hover': 'var(--color-components-premium-badge-orange-stroke-stop-0-hover)', + 'components-premium-badge-orange-stroke-stop-100-hover': 'var(--color-components-premium-badge-orange-stroke-stop-100-hover)', 'text-primary': 'var(--color-text-primary)', 'text-secondary': 'var(--color-text-secondary)', @@ -265,6 +336,7 @@ const vars = { 'text-logo-text': 'var(--color-text-logo-text)', 'text-empty-state-icon': 'var(--color-text-empty-state-icon)', 'text-inverted': 'var(--color-text-inverted)', + 'text-inverted-dimm': 'var(--color-text-inverted-dimm)', 'background-body': 'var(--color-background-body)', 'background-default-subtle': 'var(--color-background-default-subtle)', @@ -301,6 +373,7 @@ const vars = { 'background-overlay-alt': 'var(--color-background-overlay-alt)', 'background-surface-white': 'var(--color-background-surface-white)', 'background-overlay-destructive': 'var(--color-background-overlay-destructive)', + 'background-overlay-backdrop': 'var(--color-background-overlay-backdrop)', 'shadow-shadow-1': 'var(--color-shadow-shadow-1)', 'shadow-shadow-3': 'var(--color-shadow-shadow-3)', @@ -316,6 +389,7 @@ const vars = { 'workflow-block-border': 'var(--color-workflow-block-border)', 'workflow-block-parma-bg': 'var(--color-workflow-block-parma-bg)', 'workflow-block-bg': 'var(--color-workflow-block-bg)', + 'workflow-block-bg-transparent': 'var(--color-workflow-block-bg-transparent)', 'workflow-block-border-highlight': 'var(--color-workflow-block-border-highlight)', 'workflow-canvas-workflow-dot-color': 'var(--color-workflow-canvas-workflow-dot-color)', @@ -324,6 +398,17 @@ const vars = { 'workflow-link-line-active': 'var(--color-workflow-link-line-active)', 'workflow-link-line-normal': 'var(--color-workflow-link-line-normal)', 'workflow-link-line-handle': 'var(--color-workflow-link-line-handle)', + 'workflow-link-line-normal-transparent': 'var(--color-workflow-link-line-normal-transparent)', + 'workflow-link-line-failure-active': 'var(--color-workflow-link-line-failure-active)', + 'workflow-link-line-failure-handle': 'var(--color-workflow-link-line-failure-handle)', + 'workflow-link-line-failure-button-bg': 'var(--color-workflow-link-line-failure-button-bg)', + 'workflow-link-line-failure-button-hover': 'var(--color-workflow-link-line-failure-button-hover)', + + 'workflow-link-line-success-active': 'var(--color-workflow-link-line-success-active)', + 'workflow-link-line-success-handle': 'var(--color-workflow-link-line-success-handle)', + + 'workflow-link-line-error-active': 'var(--color-workflow-link-line-error-active)', + 'workflow-link-line-error-handle': 'var(--color-workflow-link-line-error-handle)', 'workflow-minimap-bg': 'var(--color-workflow-minimap-bg)', 'workflow-minimap-block': 'var(--color-workflow-minimap-block)', @@ -424,6 +509,7 @@ const vars = { 'util-colors-orange-orange-500': 'var(--color-util-colors-orange-orange-500)', 'util-colors-orange-orange-600': 'var(--color-util-colors-orange-orange-600)', 'util-colors-orange-orange-700': 'var(--color-util-colors-orange-orange-700)', + 'util-colors-orange-orange-100-transparent': 'var(--color-util-colors-orange-orange-100-transparent)', 'util-colors-pink-pink-50': 'var(--color-util-colors-pink-pink-50)', 'util-colors-pink-pink-100': 'var(--color-util-colors-pink-pink-100)', @@ -599,6 +685,11 @@ const vars = { 'third-party-LangChain': 'var(--color-third-party-LangChain)', 'third-party-Langfuse': 'var(--color-third-party-Langfuse)', 'third-party-Github': 'var(--color-third-party-Github)', + 'third-party-Github-tertiary': 'var(--color-third-party-Github-tertiary)', + 'third-party-Github-secondary': 'var(--color-third-party-Github-secondary)', + 'third-party-model-bg-openai': 'var(--color-third-party-model-bg-openai)', + 'third-party-model-bg-anthropic': 'var(--color-third-party-model-bg-anthropic)', + 'third-party-model-bg-default': 'var(--color-third-party-model-bg-default)', } export default vars From 324437b3f1446a1c203213255c5cce28e5197f14 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 8 Nov 2024 16:11:50 +0800 Subject: [PATCH 05/14] feat: mutation permersions --- .../permission-setting-modal/modal.tsx | 2 +- .../plugins/plugin-page/context.tsx | 22 ++-------- .../components/plugins/plugin-page/index.tsx | 2 +- .../plugins/plugin-page/use-permission.ts | 42 ++++++++----------- web/service/plugins.ts | 9 ---- web/service/use-plugins.ts | 36 +++++++++++++++- 6 files changed, 58 insertions(+), 55 deletions(-) diff --git a/web/app/components/plugins/permission-setting-modal/modal.tsx b/web/app/components/plugins/permission-setting-modal/modal.tsx index c9e2d2da61..b5eaa4765c 100644 --- a/web/app/components/plugins/permission-setting-modal/modal.tsx +++ b/web/app/components/plugins/permission-setting-modal/modal.tsx @@ -34,7 +34,7 @@ const PluginSettingModal: FC = ({ const handleSave = useCallback(async () => { await onSave(tempPrivilege) onHide() - }, [tempPrivilege]) + }, [onHide, onSave, tempPrivilege]) return ( - permissions: Permissions - setPermissions: (permissions: PluginPageContextValue['permissions']) => void currentPluginDetail: PluginDetail | undefined setCurrentPluginDetail: (plugin: PluginDetail) => void filters: FilterState @@ -32,21 +29,16 @@ export type PluginPageContextValue = { export const PluginPageContext = createContext({ containerRef: { current: null }, - permissions: { - install_permission: PermissionType.noOne, - debug_permission: PermissionType.noOne, - }, - setPermissions: () => {}, currentPluginDetail: undefined, - setCurrentPluginDetail: () => {}, + setCurrentPluginDetail: () => { }, filters: { categories: [], tags: [], searchQuery: '', }, - setFilters: () => {}, + setFilters: () => { }, activeTab: '', - setActiveTab: () => {}, + setActiveTab: () => { }, options: [], }) @@ -63,10 +55,6 @@ export const PluginPageContextProvider = ({ }: PluginPageContextProviderProps) => { const { t } = useTranslation() const containerRef = useRef(null) - const [permissions, setPermissions] = useState({ - install_permission: PermissionType.noOne, - debug_permission: PermissionType.noOne, - }) const [filters, setFilters] = useState({ categories: [], tags: [], @@ -93,8 +81,6 @@ export const PluginPageContextProvider = ({ diff --git a/web/app/components/plugins/plugin-page/use-permission.ts b/web/app/components/plugins/plugin-page/use-permission.ts index 4a8cdc29c1..c4fc01f2c3 100644 --- a/web/app/components/plugins/plugin-page/use-permission.ts +++ b/web/app/components/plugins/plugin-page/use-permission.ts @@ -1,15 +1,12 @@ -import { useEffect } from 'react' -import type { Permissions } from '../types' import { PermissionType } from '../types' -import { - usePluginPageContext, -} from './context' import { useAppContext } from '@/context/app-context' -import { updatePermission as doUpdatePermission, fetchPermission } from '@/service/plugins' import Toast from '../../base/toast' import { useTranslation } from 'react-i18next' +import { useInvalidatePermissions, useMutationPermissions, usePermissions } from '@/service/use-plugins' -const hasPermission = (permission: PermissionType, isAdmin: boolean) => { +const hasPermission = (permission: PermissionType | undefined, isAdmin: boolean) => { + if (!permission) + return false if (permission === PermissionType.noOne) return false @@ -22,29 +19,26 @@ const hasPermission = (permission: PermissionType, isAdmin: boolean) => { const usePermission = () => { const { t } = useTranslation() const { isCurrentWorkspaceManager, isCurrentWorkspaceOwner } = useAppContext() - const [permissions, setPermissions] = usePluginPageContext(v => [v.permissions, v.setPermissions]) + const { data: permissions } = usePermissions() + const invalidatePermissions = useInvalidatePermissions() + const { mutate: updatePermission, isPending: isUpdatePending } = useMutationPermissions({ + onSuccess: () => { + invalidatePermissions() + Toast.notify({ + type: 'success', + message: t('common.api.actionSuccess'), + }) + }, + }) const isAdmin = isCurrentWorkspaceManager || isCurrentWorkspaceOwner - const updatePermission = async (permission: Permissions) => { - await doUpdatePermission(permission) - setPermissions(permission) - Toast.notify({ - type: 'success', - message: t('common.api.actionSuccess'), - }) - } - useEffect(() => { - (async () => { - const permission = await fetchPermission() - setPermissions(permission) - })() - }, []) return { - canManagement: hasPermission(permissions.install_permission, isAdmin), - canDebugger: hasPermission(permissions.debug_permission, isAdmin), + canManagement: hasPermission(permissions?.install_permission, isAdmin), + canDebugger: hasPermission(permissions?.debug_permission, isAdmin), canSetPermissions: isAdmin, permissions, setPermissions: updatePermission, + isUpdatePending, } } diff --git a/web/service/plugins.ts b/web/service/plugins.ts index e9de724256..6d246e9239 100644 --- a/web/service/plugins.ts +++ b/web/service/plugins.ts @@ -15,7 +15,6 @@ import type { UpdateEndpointRequest, uploadGitHubResponse, } from '@/app/components/plugins/types' -import type { DebugInfo as DebugInfoTypes } from '@/app/components/plugins/types' import type { MarketplaceCollectionPluginsResponse, MarketplaceCollectionsResponse, @@ -51,10 +50,6 @@ export const disableEndpoint: Fetcher(url, { body: { endpoint_id: endpointID } }) } -export const fetchDebugKey = async () => { - return get('/workspaces/current/plugin/debugging-key') -} - export const uploadPackageFile = async (file: File) => { const formData = new FormData() formData.append('pkg', file) @@ -131,10 +126,6 @@ export const checkTaskStatus = async (taskId: string) => { return get(`/workspaces/current/plugin/tasks/${taskId}`) } -export const fetchPermission = async () => { - return get('/workspaces/current/plugin/permission/fetch') -} - export const updatePermission = async (permissions: Permissions) => { return post('/workspaces/current/plugin/permission/change', { body: permissions }) } diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts index 0f069f65cc..754cd1cc2d 100644 --- a/web/service/use-plugins.ts +++ b/web/service/use-plugins.ts @@ -1,6 +1,7 @@ -import type { DebugInfo as DebugInfoTypes, InstalledPluginListResponse } from '@/app/components/plugins/types' -import { get } from './base' +import type { DebugInfo as DebugInfoTypes, InstalledPluginListResponse, Permissions } from '@/app/components/plugins/types' +import { get, post } from './base' import { + useMutation, useQueryClient, } from '@tanstack/react-query' @@ -34,3 +35,34 @@ export const useDebugKey = () => { queryFn: () => get('/workspaces/current/plugin/debugging-key'), }) } + +const usePermissionsKey = [NAME_SPACE, 'permissions'] +export const usePermissions = () => { + return useQuery({ + queryKey: usePermissionsKey, + queryFn: () => get('/workspaces/current/plugin/permission/fetch'), + }) +} + +export const useInvalidatePermissions = () => { + const queryClient = useQueryClient() + return () => { + queryClient.invalidateQueries( + { + queryKey: usePermissionsKey, + }) + } +} + +export const useMutationPermissions = ({ + onSuccess, +}: { + onSuccess?: () => void +}) => { + return useMutation({ + mutationFn: (payload: Permissions) => { + return post('/workspaces/current/plugin/permission/change', { body: payload }) + }, + onSuccess, + }) +} From d13169934da1206fbebcf400384a74688dc02579 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Fri, 8 Nov 2024 16:13:52 +0800 Subject: [PATCH 06/14] fix: marketplace i18n --- .../plugins/marketplace/description/index.tsx | 8 ++++---- .../plugins/marketplace/plugin-type-switch.tsx | 2 +- web/app/components/tools/marketplace/index.tsx | 10 ++++++---- web/i18n/en-US/marketplace.ts | 10 ---------- web/i18n/en-US/plugin.ts | 8 ++++++++ web/i18n/zh-Hans/marketplace.ts | 10 ---------- web/i18n/zh-Hans/plugin.ts | 8 ++++++++ 7 files changed, 27 insertions(+), 29 deletions(-) delete mode 100644 web/i18n/en-US/marketplace.ts delete mode 100644 web/i18n/zh-Hans/marketplace.ts diff --git a/web/app/components/plugins/marketplace/description/index.tsx b/web/app/components/plugins/marketplace/description/index.tsx index 3b0454c3c6..9e3f9774b5 100644 --- a/web/app/components/plugins/marketplace/description/index.tsx +++ b/web/app/components/plugins/marketplace/description/index.tsx @@ -15,10 +15,10 @@ const Description = async ({ return ( <>

- Empower your AI development + {t('marketplace.empower')}

- Discover + {t('marketplace.discover')} {t('category.models')} @@ -30,11 +30,11 @@ const Description = async ({ {t('category.extensions')} - and + {t('marketplace.and')} {t('category.bundles')} - in Dify Marketplace + {t('marketplace.inDifyMarketplace')}

) diff --git a/web/app/components/plugins/marketplace/plugin-type-switch.tsx b/web/app/components/plugins/marketplace/plugin-type-switch.tsx index 6a44524a0c..82758ad87d 100644 --- a/web/app/components/plugins/marketplace/plugin-type-switch.tsx +++ b/web/app/components/plugins/marketplace/plugin-type-switch.tsx @@ -30,7 +30,7 @@ const PluginTypeSwitch = ({ const options = [ { value: PLUGIN_TYPE_SEARCH_MAP.all, - text: 'All', + text: t('plugin.category.all'), icon: null, }, { diff --git a/web/app/components/tools/marketplace/index.tsx b/web/app/components/tools/marketplace/index.tsx index f2092227a0..c50b898362 100644 --- a/web/app/components/tools/marketplace/index.tsx +++ b/web/app/components/tools/marketplace/index.tsx @@ -31,9 +31,11 @@ const Marketplace = ({ onClick={() => onMarketplaceScroll()} />
-
More from Marketplace
+
+ {t('plugin.marketplace.moreFrom')} +
- Discover + {t('plugin.marketplace.discover')} {t('plugin.category.models')} @@ -45,11 +47,11 @@ const Marketplace = ({ {t('plugin.category.extensions')} - and + {t('plugin.marketplace.and')} {t('plugin.category.bundles')} - in Dify Marketplace + {t('plugin.marketplace.inDifyMarketplace')}
{ diff --git a/web/i18n/en-US/marketplace.ts b/web/i18n/en-US/marketplace.ts deleted file mode 100644 index 4a53d94e57..0000000000 --- a/web/i18n/en-US/marketplace.ts +++ /dev/null @@ -1,10 +0,0 @@ -const translation = { - plugins: { - title: 'Plugins', - }, - discover: { - title: 'Explore Marketplace', - }, -} - -export default translation diff --git a/web/i18n/en-US/plugin.ts b/web/i18n/en-US/plugin.ts index 6d6e0d57c0..5c3bdc6f29 100644 --- a/web/i18n/en-US/plugin.ts +++ b/web/i18n/en-US/plugin.ts @@ -1,5 +1,6 @@ const translation = { category: { + all: 'All', models: 'models', tools: 'tools', extensions: 'extensions', @@ -116,6 +117,13 @@ const translation = { error: { inValidGitHubUrl: 'Invalid GitHub URL. Please enter a valid URL in the format: https://github.com/owner/repo', }, + marketplace: { + empower: 'Empower your AI development', + discover: 'Discover', + and: 'and', + inDifyMarketplace: 'in Dify Marketplace', + moreFrom: 'More from Marketplace', + }, } export default translation diff --git a/web/i18n/zh-Hans/marketplace.ts b/web/i18n/zh-Hans/marketplace.ts deleted file mode 100644 index ba3b2a192f..0000000000 --- a/web/i18n/zh-Hans/marketplace.ts +++ /dev/null @@ -1,10 +0,0 @@ -const translation = { - plugins: { - title: '插件', - }, - discover: { - title: '探索市场', - }, -} - -export default translation diff --git a/web/i18n/zh-Hans/plugin.ts b/web/i18n/zh-Hans/plugin.ts index 8b13e63aee..c1f7bb7600 100644 --- a/web/i18n/zh-Hans/plugin.ts +++ b/web/i18n/zh-Hans/plugin.ts @@ -1,5 +1,6 @@ const translation = { category: { + all: '全部', models: '模型', tools: '工具', extensions: '扩展', @@ -116,6 +117,13 @@ const translation = { error: { inValidGitHubUrl: '无效的 GitHub URL。请输入格式为 https://github.com/owner/repo 的有效 URL', }, + marketplace: { + empower: '助力您的 AI 开发', + discover: '探索', + and: '和', + inDifyMarketplace: '在 Dify 市场中', + moreFrom: '更多来自市场', + }, } export default translation From f498686c3a903583688922285757db12bd870aa6 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Fri, 8 Nov 2024 13:16:02 +0800 Subject: [PATCH 07/14] model list style fix --- web/app/components/plugins/plugin-detail-panel/model-list.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0d79d73020..2704ea741f 100644 --- a/web/app/components/plugins/plugin-detail-panel/model-list.tsx +++ b/web/app/components/plugins/plugin-detail-panel/model-list.tsx @@ -21,7 +21,7 @@ const ModelList = () => { return (
{t('plugin.detailPanel.modelNum', { num: res.data.length })}
-
+
{res.data.map(model => (
Date: Fri, 8 Nov 2024 16:11:19 +0800 Subject: [PATCH 08/14] endpoints api --- .../plugin-detail-panel/endpoint-card.tsx | 37 ++++-- .../plugin-detail-panel/endpoint-list.tsx | 28 +++-- .../plugins/plugin-detail-panel/index.tsx | 2 +- .../plugins/plugin-detail-panel/mock.ts | 105 ------------------ .../plugins/plugin-detail-panel/utils.ts | 21 ++++ web/app/components/tools/types.ts | 2 +- web/service/plugins.ts | 4 +- 7 files changed, 74 insertions(+), 125 deletions(-) delete mode 100644 web/app/components/plugins/plugin-detail-panel/mock.ts create mode 100644 web/app/components/plugins/plugin-detail-panel/utils.ts 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 948cbdd225..1a984b4eda 100644 --- a/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx +++ b/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx @@ -4,12 +4,14 @@ import { useBoolean } from 'ahooks' import { RiDeleteBinLine, RiEditLine, RiLoginCircleLine } from '@remixicon/react' import type { EndpointListItem } from '../types' import EndpointModal from './endpoint-modal' +import { NAME_FIELD } from './utils' import { addDefaultValue, toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema' import ActionButton from '@/app/components/base/action-button' import CopyBtn from '@/app/components/base/copy-btn' import Confirm from '@/app/components/base/confirm' import Indicator from '@/app/components/header/indicator' import Switch from '@/app/components/base/switch' +import Toast from '@/app/components/base/toast' import { deleteEndpoint, disableEndpoint, @@ -19,10 +21,12 @@ import { type Props = { data: EndpointListItem + handleChange: () => void } const EndpointCard = ({ data, + handleChange, }: Props) => { const { t } = useTranslation() const [active, setActive] = useState(data.enabled) @@ -38,9 +42,11 @@ const EndpointCard = ({ url: '/workspaces/current/endpoints/enable', endpointID, }) + await handleChange() } - catch (error) { + catch (error: any) { console.error(error) + Toast.notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) setActive(false) } } @@ -50,9 +56,12 @@ const EndpointCard = ({ url: '/workspaces/current/endpoints/disable', endpointID, }) + await handleChange() + hideDisableConfirm() } catch (error) { console.error(error) + Toast.notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) setActive(true) } } @@ -77,9 +86,12 @@ const EndpointCard = ({ url: '/workspaces/current/endpoints/delete', endpointID, }) + await handleChange() + hideDeleteConfirm() } catch (error) { console.error(error) + Toast.notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) } } @@ -89,25 +101,34 @@ const EndpointCard = ({ }] = useBoolean(false) const formSchemas = useMemo(() => { - return toolCredentialToFormSchemas(data.declaration.settings) + return toolCredentialToFormSchemas([NAME_FIELD, ...data.declaration.settings]) }, [data.declaration.settings]) const formValue = useMemo(() => { - return addDefaultValue(data.settings, formSchemas) - }, [data.settings, formSchemas]) + const formValue = { + name: data.name, + ...data.settings, + } + return addDefaultValue(formValue, formSchemas) + }, [data.name, data.settings, formSchemas]) - const handleUpdate = (state: any) => { + const handleUpdate = async (state: any) => { + const newName = state.name + delete state.name try { - updateEndpoint({ - url: '/workspaces/current/endpoints', + await updateEndpoint({ + url: '/workspaces/current/endpoints/update', body: { endpoint_id: data.id, settings: state, - name: state.name, + name: newName, }, }) + await handleChange() + hideEndpointModalConfirm() } catch (error) { console.error(error) + Toast.notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) } } 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 495d451f90..6323e42365 100644 --- a/web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx +++ b/web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx @@ -5,21 +5,27 @@ import { useBoolean } from 'ahooks' import { RiAddLine } from '@remixicon/react' import EndpointModal from './endpoint-modal' import EndpointCard from './endpoint-card' +import { NAME_FIELD } from './utils' import { toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema' import ActionButton from '@/app/components/base/action-button' import Tooltip from '@/app/components/base/tooltip' +import Toast from '@/app/components/base/toast' import { usePluginPageContext } from '@/app/components/plugins/plugin-page/context' import { createEndpoint, fetchEndpointList, } from '@/service/plugins' +import cn from '@/utils/classnames' -const EndpointList = () => { +type Props = { + showTopBorder?: boolean +} +const EndpointList = ({ showTopBorder }: Props) => { const { t } = useTranslation() const pluginDetail = usePluginPageContext(v => v.currentPluginDetail) const pluginUniqueID = pluginDetail.plugin_unique_identifier const declaration = pluginDetail.declaration.endpoint - const { data } = useSWR( + const { data, mutate } = useSWR( { url: '/workspaces/current/endpoints/list/plugin', params: { @@ -36,22 +42,27 @@ const EndpointList = () => { }] = useBoolean(false) const formSchemas = useMemo(() => { - return toolCredentialToFormSchemas(declaration.settings) + return toolCredentialToFormSchemas([NAME_FIELD, ...declaration.settings]) }, [declaration.settings]) - const handleCreate = (state: any) => { + const handleCreate = async (state: any) => { + const newName = state.name + delete state.name try { - createEndpoint({ - url: '/workspaces/current/endpoints', + await createEndpoint({ + url: '/workspaces/current/endpoints/create', body: { plugin_unique_identifier: pluginUniqueID, settings: state, - name: state.name, + name: newName, }, }) + await mutate() + hideEndpointModal() } catch (error) { console.error(error) + Toast.notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) } } @@ -59,7 +70,7 @@ const EndpointList = () => { return null return ( -
+
{t('plugin.detailPanel.endpoints')} @@ -81,6 +92,7 @@ const EndpointList = () => { ))}
diff --git a/web/app/components/plugins/plugin-detail-panel/index.tsx b/web/app/components/plugins/plugin-detail-panel/index.tsx index 0dd85a4f87..7bd1849625 100644 --- a/web/app/components/plugins/plugin-detail-panel/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/index.tsx @@ -42,8 +42,8 @@ const PluginDetailPanel: FC = ({ onDelete={onDelete} />
- {!!pluginDetail.declaration.endpoint && } {!!pluginDetail.declaration.tool && } + {!!pluginDetail.declaration.endpoint && } {!!pluginDetail.declaration.model && }
diff --git a/web/app/components/plugins/plugin-detail-panel/mock.ts b/web/app/components/plugins/plugin-detail-panel/mock.ts deleted file mode 100644 index dffe753922..0000000000 --- a/web/app/components/plugins/plugin-detail-panel/mock.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { PluginSource, PluginType } from '../types' - -export const toolNotion = { - id: 'dlfajkgjdga-dfjalksjfglkds-dfjakld', - created_at: '2024-10-16 16:05:33', - updated_at: '2024-10-16 16:05:33', - name: 'notion page search', - plugin_id: 'Notion/notion-page-search', - plugin_unique_identifier: 'Notion/notion-page-search:1.2.0@fldsjflkdsajfldsakajfkls', - declaration: { - version: '1.2.0', - author: 'Notion', - name: 'notion page search', - category: PluginType.tool, - icon: 'https://via.placeholder.com/150', - label: { - 'en-US': 'Notion Page Search', - 'zh-Hans': 'Notion 页面搜索', - }, - description: { - 'en-US': 'Description: Search Notion pages and open visited ones faster. No admin access required.More and more info...More and more info...More and more info...', - 'zh-Hans': '搜索 Notion 页面并更快地打开已访问的页面。无需管理员访问权限。More and more info...More and more info...More and more info...', - }, - created_at: '2024-10-16 16:05:33', - resource: {}, - plugins: {}, - endpoint: { - settings: [ - { - type: 'secret-input', - name: 'api-key', - required: true, - default: null, - options: null, - label: { - en_US: 'API-key', - zh_Hans: 'API-key', - }, - help: null, - url: null, - placeholder: { - en_US: 'Please input your API key', - zh_Hans: '请输入你的 API key', - }, - }, - ], - endpoints: [ - { path: '/duck/', method: 'GET' }, - { path: '/neko', method: 'GET' }, - ], - }, - tool: null, // TODO - verified: true, - }, - installation_id: 'jflkdsjoewingljlsadjgoijg-dkfjldajglkajglask-dlfkajdg', - tenant_id: 'jflkdsjoewingljlsadjgoijg', - endpoints_setups: 2, - endpoints_active: 1, - version: '1.2.0', - source: PluginSource.marketplace, - meta: null, -} - -export const toolNotionEndpoints = [ - { - id: 'dlfajkgjdga-dfjalksjfglkds-dfjakld', - created_at: '2024-10-16 16:05:33', - updated_at: '2024-10-16 16:05:33', - settings: { - 'api-key': '*******', - }, - tenant_id: 'jflkdsjoewingljlsadjgoijg', - plugin_id: 'Notion/notion-page-search', - expired_at: '2024-10-16 16:05:33', - declaration: { - settings: [ - { - type: 'secret-input', - name: 'api-key', - required: true, - default: null, - options: null, - label: { - en_US: 'API-key', - zh_Hans: 'API-key', - }, - help: null, - url: null, - placeholder: { - en_US: 'Please input your API key', - zh_Hans: '请输入你的 API key', - }, - }, - ], - endpoints: [ - { path: '/duck/', method: 'GET' }, - { path: '/neko', method: 'GET' }, - ], - }, - name: 'default', - enabled: true, - url: 'http://localhost:5002/e/45rj9V4TRxAjL0I2wXRZgZdXjdHEKBh8', - hook_id: '45rj9V4TRxAjL0I2wXRZgZdXjdHEKBh8', - }, -] diff --git a/web/app/components/plugins/plugin-detail-panel/utils.ts b/web/app/components/plugins/plugin-detail-panel/utils.ts new file mode 100644 index 0000000000..fd51142a38 --- /dev/null +++ b/web/app/components/plugins/plugin-detail-panel/utils.ts @@ -0,0 +1,21 @@ +import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' + +export const NAME_FIELD = { + type: FormTypeEnum.textInput, + name: 'name', + label: { + en_US: 'Endpoint Name', + zh_Hans: '端点名称', + ja_JP: 'エンドポイント名', + pt_BR: 'Nome do ponto final', + }, + placeholder: { + en_US: 'Endpoint Name', + zh_Hans: '端点名称', + ja_JP: 'エンドポイント名', + pt_BR: 'Nome do ponto final', + }, + required: true, + default: '', + help: null, +} diff --git a/web/app/components/tools/types.ts b/web/app/components/tools/types.ts index 9e899489bb..34cc491481 100644 --- a/web/app/components/tools/types.ts +++ b/web/app/components/tools/types.ts @@ -80,7 +80,7 @@ export type Tool = { export type ToolCredential = { name: string label: TypeWithI18N - help: TypeWithI18N + help: TypeWithI18N | null placeholder: TypeWithI18N type: string required: boolean diff --git a/web/service/plugins.ts b/web/service/plugins.ts index 6d246e9239..b7d5125f2e 100644 --- a/web/service/plugins.ts +++ b/web/service/plugins.ts @@ -1,5 +1,5 @@ import type { Fetcher } from 'swr' -import { del, get, getMarketplace, post, upload } from './base' +import { get, getMarketplace, post, upload } from './base' import type { CreateEndpointRequest, EndpointOperationResponse, @@ -32,7 +32,7 @@ export const fetchEndpointList: Fetcher = ({ url, endpointID }) => { // url = /workspaces/current/endpoints/delete - return del(url, { body: { endpoint_id: endpointID } }) + return post(url, { body: { endpoint_id: endpointID } }) } export const updateEndpoint: Fetcher = ({ url, body }) => { From 1f1c61541e821dd0a05a1d64f05dc83c9670cb57 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Fri, 8 Nov 2024 16:33:00 +0800 Subject: [PATCH 09/14] install from settings --- .../header/account-setting/model-provider-page/index.tsx | 2 +- web/app/components/plugins/provider-card.tsx | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/index.tsx b/web/app/components/header/account-setting/model-provider-page/index.tsx index 7faf3f3de7..f807bd7922 100644 --- a/web/app/components/header/account-setting/model-provider-page/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/index.tsx @@ -222,7 +222,7 @@ const ModelProviderPage = ({ searchText }: Props) => { {!collapse && !isPluginsLoading && (
{plugins.map(plugin => ( - + ))}
)} diff --git a/web/app/components/plugins/provider-card.tsx b/web/app/components/plugins/provider-card.tsx index 5c8ab1891e..e2a45fc24d 100644 --- a/web/app/components/plugins/provider-card.tsx +++ b/web/app/components/plugins/provider-card.tsx @@ -19,11 +19,13 @@ import { useBoolean } from 'ahooks' type Props = { className?: string payload: Plugin + onSuccess: () => void } const ProviderCard: FC = ({ className, payload, + onSuccess, }) => { const { t } = useTranslation() const [isShowInstallFromMarketplace, { @@ -84,7 +86,10 @@ const ProviderCard: FC = ({ manifest={payload as any} uniqueIdentifier={payload.latest_package_identifier} onClose={hideInstallFromMarketplace} - onSuccess={hideInstallFromMarketplace} + onSuccess={() => { + onSuccess() + hideInstallFromMarketplace() + }} /> ) } From ebdf72fffcbd4332974fbc77114597cdad9ba2ef Mon Sep 17 00:00:00 2001 From: JzoNg Date: Fri, 8 Nov 2024 17:01:49 +0800 Subject: [PATCH 10/14] check update --- .../plugin-detail-panel/detail-header.tsx | 60 ++++++++++++++++--- .../plugins/plugin-detail-panel/index.tsx | 6 +- .../operation-dropdown.tsx | 51 +++++++++------- .../plugins/plugin-page/plugins-panel.tsx | 2 +- 4 files changed, 88 insertions(+), 31 deletions(-) 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 ad9760feff..b40f264967 100644 --- a/web/app/components/plugins/plugin-detail-panel/detail-header.tsx +++ b/web/app/components/plugins/plugin-detail-panel/detail-header.tsx @@ -13,6 +13,8 @@ import Description from '../card/base/description' import Icon from '../card/base/card-icon' import Title from '../card/base/title' import OrgInfo from '../card/base/org-info' +import { useGitHubReleases } from '../install-plugin/hooks' +import { compareVersion, getLatestVersion } from '@/utils/semver' import OperationDropdown from './operation-dropdown' import PluginInfo from '@/app/components/plugins/plugin-page/plugin-info' import ActionButton from '@/app/components/base/action-button' @@ -20,10 +22,13 @@ import Button from '@/app/components/base/button' import Badge from '@/app/components/base/badge' import Confirm from '@/app/components/base/confirm' import Tooltip from '@/app/components/base/tooltip' +import Toast from '@/app/components/base/toast' import { BoxSparkleFill } from '@/app/components/base/icons/src/vender/plugin' import { Github } from '@/app/components/base/icons/src/public/common' import { uninstallPlugin } from '@/service/plugins' import { useGetLanguage } from '@/context/i18n' +import { useModalContext } from '@/context/modal-context' + import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config' import cn from '@/utils/classnames' @@ -32,16 +37,18 @@ const i18nPrefix = 'plugin.action' type Props = { detail: PluginDetail onHide: () => void - onDelete: () => void + onUpdate: () => void } const DetailHeader = ({ detail, onHide, - onDelete, + onUpdate, }: Props) => { const { t } = useTranslation() const locale = useGetLanguage() + const { fetchReleases } = useGitHubReleases() + const { setShowUpdatePluginModal } = useModalContext() const { installation_id, @@ -53,13 +60,51 @@ const DetailHeader = ({ } = detail const { author, name, label, description, icon, verified } = detail.declaration const isFromGitHub = source === PluginSource.github - // Only plugin installed from GitHub need to check if it's the new version + const hasNewVersion = useMemo(() => { return source === PluginSource.github && latest_version !== version }, [source, latest_version, version]) - // #plugin TODO# update plugin - const handleUpdate = () => { } + const handleUpdate = async () => { + try { + const fetchedReleases = await fetchReleases(author, name) + if (fetchedReleases.length === 0) + return + const versions = fetchedReleases.map(release => release.tag_name) + const latestVersion = getLatestVersion(versions) + if (compareVersion(latestVersion, version) === 1) { + setShowUpdatePluginModal({ + onSaveCallback: () => { + onUpdate() + }, + payload: { + type: PluginSource.github, + github: { + originalPackageInfo: { + id: installation_id, + repo: meta!.repo, + version: meta!.version, + package: meta!.package, + releases: fetchedReleases, + }, + }, + }, + }) + } + else { + Toast.notify({ + type: 'info', + message: 'No new version available', + }) + } + } + catch { + Toast.notify({ + type: 'error', + message: 'Failed to compare versions', + }) + } + } const [isShowPluginInfo, { setTrue: showPluginInfo, @@ -82,9 +127,9 @@ const DetailHeader = ({ hideDeleting() if (res.success) { hideDeleteConfirm() - onDelete() + onUpdate() } - }, [hideDeleteConfirm, hideDeleting, installation_id, showDeleting, onDelete]) + }, [hideDeleteConfirm, hideDeleting, installation_id, showDeleting, onUpdate]) // #plugin TODO# used in apps // const usedInApps = 3 @@ -141,6 +186,7 @@ const DetailHeader = ({
void + onUpdate: () => void } const PluginDetailPanel: FC = ({ - onDelete, + onUpdate, }) => { const pluginDetail = usePluginPageContext(v => v.currentPluginDetail) const setCurrentPluginDetail = usePluginPageContext(v => v.setCurrentPluginDetail) @@ -39,7 +39,7 @@ const PluginDetailPanel: FC = ({
{!!pluginDetail.declaration.tool && } 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 b23b29d462..05988c181d 100644 --- a/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx +++ b/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx @@ -2,6 +2,7 @@ import type { FC } from 'react' import React, { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' +import { PluginSource } from '../types' import { RiArrowRightUpLine, RiMoreFill } from '@remixicon/react' import ActionButton from '@/app/components/base/action-button' // import Button from '@/app/components/base/button' @@ -13,6 +14,7 @@ import { import cn from '@/utils/classnames' type Props = { + source: PluginSource onInfo: () => void onCheckVersion: () => void onRemove: () => void @@ -20,10 +22,11 @@ type Props = { } const OperationDropdown: FC = ({ + source, + detailUrl, onInfo, onCheckVersion, onRemove, - detailUrl, }) => { const { t } = useTranslation() const [open, doSetOpen] = useState(false) @@ -56,25 +59,33 @@ const OperationDropdown: FC = ({
-
{ - onInfo() - handleTrigger() - }} - className='px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover' - >{t('plugin.detailPanel.operation.info')}
-
{ - onCheckVersion() - handleTrigger() - }} - className='px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover' - >{t('plugin.detailPanel.operation.checkUpdate')}
- - {t('plugin.detailPanel.operation.viewDetail')} - - -
+ {source === PluginSource.github && ( +
{ + onInfo() + handleTrigger() + }} + className='px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover' + >{t('plugin.detailPanel.operation.info')}
+ )} + {source === PluginSource.github && ( +
{ + onCheckVersion() + handleTrigger() + }} + className='px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover' + >{t('plugin.detailPanel.operation.checkUpdate')}
+ )} + {source === PluginSource.marketplace && ( + + {t('plugin.detailPanel.operation.viewDetail')} + + + )} + {(source === PluginSource.marketplace || source === PluginSource.github) && ( +
+ )}
{ onRemove() diff --git a/web/app/components/plugins/plugin-page/plugins-panel.tsx b/web/app/components/plugins/plugin-page/plugins-panel.tsx index 466df72066..198b418f7c 100644 --- a/web/app/components/plugins/plugin-page/plugins-panel.tsx +++ b/web/app/components/plugins/plugin-page/plugins-panel.tsx @@ -48,7 +48,7 @@ const PluginsPanel = () => { ) : ( )} - invalidateInstalledPluginList()}/> + invalidateInstalledPluginList()}/> ) } From d4f7ebfd2e82daf58dddd217a3a88366de824d5e Mon Sep 17 00:00:00 2001 From: AkaraChen Date: Fri, 8 Nov 2024 17:21:55 +0800 Subject: [PATCH 11/14] feat: refactor http client --- web/package.json | 3 +- web/pnpm-lock.yaml | 9 ++ web/service/base.ts | 191 +++---------------------------------------- web/service/fetch.ts | 187 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 209 insertions(+), 181 deletions(-) create mode 100644 web/service/fetch.ts diff --git a/web/package.json b/web/package.json index 8d69bbc209..7cbe778790 100644 --- a/web/package.json +++ b/web/package.json @@ -64,6 +64,7 @@ "js-cookie": "^3.0.5", "jwt-decode": "^4.0.0", "katex": "^0.16.11", + "ky": "^1.7.2", "lamejs": "^1.2.1", "lexical": "^0.18.0", "lodash-es": "^4.17.21", @@ -84,9 +85,9 @@ "react-hook-form": "^7.53.1", "react-i18next": "^15.1.0", "react-infinite-scroll-component": "^6.1.0", + "react-markdown": "^9.0.1", "react-multi-email": "^1.0.25", "react-papaparse": "^4.4.0", - "react-markdown": "^9.0.1", "react-slider": "^2.0.6", "react-sortablejs": "^6.1.4", "react-syntax-highlighter": "^15.6.1", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 1a98266fdc..236c23c9c6 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -133,6 +133,9 @@ importers: katex: specifier: ^0.16.11 version: 0.16.11 + ky: + specifier: ^1.7.2 + version: 1.7.2 lamejs: specifier: ^1.2.1 version: 1.2.1 @@ -5612,6 +5615,10 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} + ky@1.7.2: + resolution: {integrity: sha512-OzIvbHKKDpi60TnF9t7UUVAF1B4mcqc02z5PIvrm08Wyb+yOcz63GRvEuVxNT18a9E1SrNouhB4W2NNLeD7Ykg==} + engines: {node: '>=18'} + lamejs@1.2.1: resolution: {integrity: sha512-s7bxvjvYthw6oPLCm5pFxvA84wUROODB8jEO2+CE1adhKgrIvVOlmMgY8zyugxGrvRaDHNJanOiS21/emty6dQ==} @@ -14744,6 +14751,8 @@ snapshots: kleur@4.1.5: {} + ky@1.7.2: {} + lamejs@1.2.1: dependencies: use-strict: 1.0.1 diff --git a/web/service/base.ts b/web/service/base.ts index e1a04217c7..0f9059e617 100644 --- a/web/service/base.ts +++ b/web/service/base.ts @@ -1,4 +1,4 @@ -import { API_PREFIX, IS_CE_EDITION, MARKETPLACE_API_PREFIX, PUBLIC_API_PREFIX } from '@/config' +import { API_PREFIX, IS_CE_EDITION, PUBLIC_API_PREFIX } from '@/config' import { refreshAccessTokenOrRelogin } from './refresh-token' import Toast from '@/app/components/base/toast' import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/base/chat/chat/type' @@ -17,27 +17,10 @@ import type { WorkflowStartedResponse, } from '@/types/workflow' import { removeAccessToken } from '@/app/components/share/utils' +import type { FetchOptionType, ResponseError } from './fetch' +import { ContentType, base, baseOptions, getPublicToken } from './fetch' const TIME_OUT = 100000 -const ContentType = { - json: 'application/json', - stream: 'text/event-stream', - audio: 'audio/mpeg', - form: 'application/x-www-form-urlencoded; charset=UTF-8', - download: 'application/octet-stream', // for download - upload: 'multipart/form-data', // for upload -} - -const baseOptions = { - method: 'GET', - mode: 'cors', - credentials: 'include', // always send cookies、HTTP Basic authentication. - headers: new Headers({ - 'Content-Type': ContentType.json, - }), - redirect: 'follow', -} - export type IOnDataMoreInfo = { conversationId?: string taskId?: string @@ -100,17 +83,6 @@ export type IOtherOptions = { onTextReplace?: IOnTextReplace } -type ResponseError = { - code: string - message: string - status: number -} - -type FetchOptionType = Omit & { - params?: Record - body?: BodyInit | Record | null -} - function unicodeToChar(text: string) { if (!text) return '' @@ -277,153 +249,13 @@ const handleStream = ( read() } -const baseFetch = ( - url: string, - fetchOptions: FetchOptionType, - { - isPublicAPI = false, - isMarketplaceAPI = false, - bodyStringify = true, - needAllResponseContent, - deleteContentType, - getAbortController, - silent, - }: IOtherOptions, -): Promise => { - const options: typeof baseOptions & FetchOptionType = Object.assign({}, baseOptions, fetchOptions) - if (isMarketplaceAPI) - options.credentials = 'omit' - - if (getAbortController) { - const abortController = new AbortController() - getAbortController(abortController) - options.signal = abortController.signal - } - - if (isPublicAPI) { - const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0] - const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' }) - let accessTokenJson = { [sharedToken]: '' } - try { - accessTokenJson = JSON.parse(accessToken) - } - catch (e) { - - } - options.headers.set('Authorization', `Bearer ${accessTokenJson[sharedToken]}`) - } - else if (!isMarketplaceAPI) { - const accessToken = localStorage.getItem('console_token') || '' - options.headers.set('Authorization', `Bearer ${accessToken}`) - } - - if (deleteContentType) { - options.headers.delete('Content-Type') - } - else { - const contentType = options.headers.get('Content-Type') - if (!contentType) - options.headers.set('Content-Type', ContentType.json) - } - - const urlPrefix = (() => { - if (isMarketplaceAPI) - return MARKETPLACE_API_PREFIX - if (isPublicAPI) - return PUBLIC_API_PREFIX - return API_PREFIX - })() - let urlWithPrefix = `${urlPrefix}${url.startsWith('/') ? url : `/${url}`}` - - const { method, params, body } = options - // handle query - if (method === 'GET' && params) { - const paramsArray: string[] = [] - Object.keys(params).forEach(key => - paramsArray.push(`${key}=${encodeURIComponent(params[key])}`), - ) - if (urlWithPrefix.search(/\?/) === -1) - urlWithPrefix += `?${paramsArray.join('&')}` - - else - urlWithPrefix += `&${paramsArray.join('&')}` - - delete options.params - } - - if (body && bodyStringify) - options.body = JSON.stringify(body) - - // Handle timeout - return Promise.race([ - new Promise((resolve, reject) => { - setTimeout(() => { - reject(new Error('request timeout')) - }, TIME_OUT) - }), - new Promise((resolve, reject) => { - globalThis.fetch(urlWithPrefix, options as RequestInit) - .then((res) => { - const resClone = res.clone() - // Error handler - if (!/^(2|3)\d{2}$/.test(String(res.status))) { - const bodyJson = res.json() - switch (res.status) { - case 401: - return Promise.reject(resClone) - case 403: - bodyJson.then((data: ResponseError) => { - if (!silent) - Toast.notify({ type: 'error', message: data.message }) - if (data.code === 'already_setup') - globalThis.location.href = `${globalThis.location.origin}/signin` - }) - break - // fall through - default: - bodyJson.then((data: ResponseError) => { - if (!silent) - Toast.notify({ type: 'error', message: data.message }) - }) - } - return Promise.reject(resClone) - } - - // handle delete api. Delete api not return content. - if (res.status === 204) { - resolve({ result: 'success' }) - return - } - - // return data - if (options.headers.get('Content-type') === ContentType.download || options.headers.get('Content-type') === ContentType.audio) - resolve(needAllResponseContent ? resClone : res.blob()) - - else resolve(needAllResponseContent ? resClone : res.json()) - }) - .catch((err) => { - if (!silent) - Toast.notify({ type: 'error', message: err }) - reject(err) - }) - }), - ]) as Promise -} +const baseFetch = base export const upload = (options: any, isPublicAPI?: boolean, url?: string, searchParams?: string): Promise => { const urlPrefix = isPublicAPI ? PUBLIC_API_PREFIX : API_PREFIX let token = '' if (isPublicAPI) { - const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0] - const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' }) - let accessTokenJson = { [sharedToken]: '' } - try { - accessTokenJson = JSON.parse(accessToken) - } - catch (e) { - - } - token = accessTokenJson[sharedToken] + token = getPublicToken() } else { const accessToken = localStorage.getItem('console_token') || '' @@ -499,9 +331,9 @@ export const ssePost = ( signal: abortController.signal, }, fetchOptions) - const contentType = options.headers.get('Content-Type') + const contentType = (options.headers as Headers).get('Content-Type') if (!contentType) - options.headers.set('Content-Type', ContentType.json) + (options.headers as Headers).set('Content-Type', ContentType.json) getAbortController?.(abortController) @@ -559,18 +391,17 @@ export const ssePost = ( } // base request -export const request = (url: string, options = {}, otherOptions?: IOtherOptions) => { +export const request = (url: string, options = {}, otherOptions: IOtherOptions = {}) => { return new Promise((resolve, reject) => { - const otherOptionsForBaseFetch = otherOptions || {} - baseFetch(url, options, otherOptionsForBaseFetch).then(resolve).catch((errResp) => { + baseFetch(url, options, otherOptions).then(resolve).catch((errResp) => { if (errResp?.status === 401) { return refreshAccessTokenOrRelogin(TIME_OUT).then(() => { - baseFetch(url, options, otherOptionsForBaseFetch).then(resolve).catch(reject) + baseFetch(url, options, otherOptions).then(resolve).catch(reject) }).catch(() => { const { isPublicAPI = false, silent, - } = otherOptionsForBaseFetch + } = otherOptions const bodyJson = errResp.json() if (isPublicAPI) { return bodyJson.then((data: ResponseError) => { diff --git a/web/service/fetch.ts b/web/service/fetch.ts new file mode 100644 index 0000000000..46e0e6295d --- /dev/null +++ b/web/service/fetch.ts @@ -0,0 +1,187 @@ +import type { AfterResponseHook, BeforeErrorHook, BeforeRequestHook, Hooks } from 'ky' +import ky from 'ky' +import type { IOtherOptions } from './base' +import Toast from '@/app/components/base/toast' +import { API_PREFIX, MARKETPLACE_API_PREFIX, PUBLIC_API_PREFIX } from '@/config' + +const TIME_OUT = 100000 + +export const ContentType = { + json: 'application/json', + stream: 'text/event-stream', + audio: 'audio/mpeg', + form: 'application/x-www-form-urlencoded; charset=UTF-8', + download: 'application/octet-stream', // for download + upload: 'multipart/form-data', // for upload +} + +export type FetchOptionType = Omit & { + params?: Record + body?: BodyInit | Record | null +} + +const afterResponse204: AfterResponseHook = async (_request, _options, response) => { + if (response.status === 204) return Response.json({ result: 'success' }) +} + +export type ResponseError = { + code: string + message: string + status: number +} + +const afterResponseErrorCode = (otherOptions: IOtherOptions): AfterResponseHook => { + return async (_request, _options, response) => { + if (!/^(2|3)\d{2}$/.test(String(response.status))) { + const bodyJson = response.json() as Promise + switch (response.status) { + case 401: + return Promise.reject(response) + case 403: + bodyJson.then((data: ResponseError) => { + if (!otherOptions.silent) + Toast.notify({ type: 'error', message: data.message }) + if (data.code === 'already_setup') + globalThis.location.href = `${globalThis.location.origin}/signin` + }) + break + // fall through + default: + bodyJson.then((data: ResponseError) => { + if (!otherOptions.silent) + Toast.notify({ type: 'error', message: data.message }) + }) + } + throw response + } + } +} + +const beforeErrorToast = (otherOptions: IOtherOptions): BeforeErrorHook => { + return (error) => { + if (!otherOptions.silent) + Toast.notify({ type: 'error', message: error.message }) + return error + } +} + +export const getPublicToken = () => { + let token = '' + const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0] + const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' }) + let accessTokenJson = { [sharedToken]: '' } + try { + accessTokenJson = JSON.parse(accessToken) + } + catch {} + token = accessTokenJson[sharedToken] + return token || '' +} + +const beforeRequestPublicAuthorization: BeforeRequestHook = (request) => { + const token = getPublicToken() + request.headers.set('Authorization', `Bearer ${token}`) +} + +const beforeRequestAuthorization: BeforeRequestHook = (request) => { + const accessToken = localStorage.getItem('console_token') || '' + request.headers.set('Authorization', `Bearer ${accessToken}`) +} + +const beforeRequestDeleteContentType: BeforeRequestHook = (request) => { + request.headers.delete('Content-Type') +} + +const baseHooks: Hooks = { + afterResponse: [ + afterResponse204, + ], +} + +const client = ky.create({ + hooks: baseHooks, + timeout: TIME_OUT, +}) + +export const baseOptions: RequestInit = { + method: 'GET', + mode: 'cors', + credentials: 'include', // always send cookies、HTTP Basic authentication. + headers: new Headers({ + 'Content-Type': ContentType.json, + }), + redirect: 'follow', +} + +async function base(url: string, options: FetchOptionType = {}, otherOptions: IOtherOptions = {}): Promise { + const { params, body, ...init } = Object.assign({}, baseOptions, options) + const { + isPublicAPI = false, + isMarketplaceAPI = false, + bodyStringify = true, + needAllResponseContent, + deleteContentType, + getAbortController, + } = otherOptions + + const base + = isMarketplaceAPI + ? MARKETPLACE_API_PREFIX + : isPublicAPI + ? PUBLIC_API_PREFIX + : API_PREFIX + + if (getAbortController) { + const abortController = new AbortController() + getAbortController(abortController) + options.signal = abortController.signal + } + + const fetchPathname = `${base}${url.startsWith('/') ? url : `/${url}`}` + + const res = await client.extend({ + hooks: { + ...baseHooks, + beforeError: [ + ...baseHooks.beforeError || [], + beforeErrorToast(otherOptions), + ], + beforeRequest: [ + ...baseHooks.beforeRequest || [], + isPublicAPI && beforeRequestPublicAuthorization, + !isPublicAPI && !isMarketplaceAPI && beforeRequestAuthorization, + deleteContentType && beforeRequestDeleteContentType, + ].filter(i => !!i), + afterResponse: [ + ...baseHooks.afterResponse || [], + afterResponseErrorCode(otherOptions), + ], + }, + })(fetchPathname, { + credentials: isMarketplaceAPI + ? 'omit' + : (options.credentials || 'include'), + ...init, + retry: { + methods: [], + }, + ...(bodyStringify ? { json: body } : { body: body as BodyInit }), + searchParams: params, + }) + + if (needAllResponseContent) + return res as T + const contentType = res.headers.get('content-type') + if ( + contentType + && [ContentType.download, ContentType.audio].includes(contentType) + ) + return await res.blob() as T + + return await res.json() as T +} + +export { + client, + base, +} From 33349191e9e5cd88c24bfe7ef0d53c28e2d3c5c7 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Fri, 8 Nov 2024 18:21:39 +0800 Subject: [PATCH 12/14] marketplace usemutation --- web/app/(commonLayout)/layout.tsx | 26 ++++++------ .../plugins/marketplace/context.tsx | 20 ++++----- .../components/plugins/marketplace/hooks.ts | 35 +++++++++------- .../components/plugins/marketplace/index.tsx | 27 ++++++------ web/app/components/plugins/types.ts | 4 ++ web/app/components/tools/marketplace/hooks.ts | 6 +-- web/service/base.ts | 5 +++ web/service/use-plugins.ts | 42 ++++++++++++++++--- 8 files changed, 105 insertions(+), 60 deletions(-) diff --git a/web/app/(commonLayout)/layout.tsx b/web/app/(commonLayout)/layout.tsx index ef07732997..f0f7e0321d 100644 --- a/web/app/(commonLayout)/layout.tsx +++ b/web/app/(commonLayout)/layout.tsx @@ -15,20 +15,20 @@ const Layout = ({ children }: { children: ReactNode }) => { <> - - - - - -
- - + + + + + + +
+ {children} - - - - - + + + + + ) diff --git a/web/app/components/plugins/marketplace/context.tsx b/web/app/components/plugins/marketplace/context.tsx index 0c87e32919..4c5752d45b 100644 --- a/web/app/components/plugins/marketplace/context.tsx +++ b/web/app/components/plugins/marketplace/context.tsx @@ -34,7 +34,7 @@ export type MarketplaceContextValue = { activePluginType: string handleActivePluginTypeChange: (type: string) => void plugins?: Plugin[] - setPlugins: (plugins: Plugin[]) => void + resetPlugins: () => void sort: PluginsSort handleSortChange: (sort: PluginsSort) => void marketplaceCollectionsFromClient?: MarketplaceCollection[] @@ -53,7 +53,7 @@ export const MarketplaceContext = createContext({ activePluginType: PLUGIN_TYPE_SEARCH_MAP.all, handleActivePluginTypeChange: () => {}, plugins: undefined, - setPlugins: () => {}, + resetPlugins: () => {}, sort: DEFAULT_SORT, handleSortChange: () => {}, marketplaceCollectionsFromClient: [], @@ -91,7 +91,7 @@ export const MarketplaceContextProvider = ({ } = useMarketplaceCollectionsAndPlugins() const { plugins, - setPlugins, + resetPlugins, queryPlugins, queryPluginsWithDebounced, } = useMarketplacePlugins() @@ -104,7 +104,7 @@ export const MarketplaceContextProvider = ({ queryMarketplaceCollectionsAndPlugins({ category: activePluginTypeRef.current === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginTypeRef.current, }) - setPlugins(undefined) + resetPlugins() return } @@ -116,7 +116,7 @@ export const MarketplaceContextProvider = ({ sortBy: sortRef.current.sortBy, sortOrder: sortRef.current.sortOrder, }) - }, [queryPluginsWithDebounced, queryMarketplaceCollectionsAndPlugins, setPlugins]) + }, [queryPluginsWithDebounced, queryMarketplaceCollectionsAndPlugins, resetPlugins]) const handleFilterPluginTagsChange = useCallback((tags: string[]) => { setFilterPluginTags(tags) @@ -126,7 +126,7 @@ export const MarketplaceContextProvider = ({ queryMarketplaceCollectionsAndPlugins({ category: activePluginTypeRef.current === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginTypeRef.current, }) - setPlugins(undefined) + resetPlugins() return } @@ -138,7 +138,7 @@ export const MarketplaceContextProvider = ({ sortBy: sortRef.current.sortBy, sortOrder: sortRef.current.sortOrder, }) - }, [queryPlugins, setPlugins, queryMarketplaceCollectionsAndPlugins]) + }, [queryPlugins, resetPlugins, queryMarketplaceCollectionsAndPlugins]) const handleActivePluginTypeChange = useCallback((type: string) => { setActivePluginType(type) @@ -148,7 +148,7 @@ export const MarketplaceContextProvider = ({ queryMarketplaceCollectionsAndPlugins({ category: type === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : type, }) - setPlugins(undefined) + resetPlugins() return } @@ -160,7 +160,7 @@ export const MarketplaceContextProvider = ({ sortBy: sortRef.current.sortBy, sortOrder: sortRef.current.sortOrder, }) - }, [queryPlugins, setPlugins, queryMarketplaceCollectionsAndPlugins]) + }, [queryPlugins, resetPlugins, queryMarketplaceCollectionsAndPlugins]) const handleSortChange = useCallback((sort: PluginsSort) => { setSort(sort) @@ -187,7 +187,7 @@ export const MarketplaceContextProvider = ({ activePluginType, handleActivePluginTypeChange, plugins, - setPlugins, + resetPlugins, sort, handleSortChange, marketplaceCollectionsFromClient, diff --git a/web/app/components/plugins/marketplace/hooks.ts b/web/app/components/plugins/marketplace/hooks.ts index 47ad603276..1cbc035972 100644 --- a/web/app/components/plugins/marketplace/hooks.ts +++ b/web/app/components/plugins/marketplace/hooks.ts @@ -4,7 +4,9 @@ import { } from 'react' import { useTranslation } from 'react-i18next' import { useDebounceFn } from 'ahooks' -import type { Plugin } from '../types' +import type { + Plugin, +} from '../types' import type { CollectionsAndPluginsSearchParams, MarketplaceCollection, @@ -12,9 +14,9 @@ import type { } from './types' import { getMarketplaceCollectionsAndPlugins, - getMarketplacePlugins, } from './utils' import i18n from '@/i18n/i18next-config' +import { useMutationPluginsFromMarketplace } from '@/service/use-plugins' export const useMarketplaceCollectionsAndPlugins = () => { const [isLoading, setIsLoading] = useState(false) @@ -41,28 +43,29 @@ export const useMarketplaceCollectionsAndPlugins = () => { } export const useMarketplacePlugins = () => { - const [isLoading, setIsLoading] = useState(false) - const [plugins, setPlugins] = useState() + const { + data, + mutate, + reset, + isPending, + } = useMutationPluginsFromMarketplace() - const queryPlugins = useCallback(async (query: PluginsSearchParams) => { - setIsLoading(true) - const { marketplacePlugins } = await getMarketplacePlugins(query) - setIsLoading(false) + const queryPlugins = useCallback((pluginsSearchParams: PluginsSearchParams) => { + mutate(pluginsSearchParams) + }, [mutate]) - setPlugins(marketplacePlugins) - }, []) - - const { run: queryPluginsWithDebounced } = useDebounceFn(queryPlugins, { + const { run: queryPluginsWithDebounced } = useDebounceFn((pluginsSearchParams) => { + mutate(pluginsSearchParams) + }, { wait: 500, }) return { - plugins, - setPlugins, + plugins: data?.data?.plugins, + resetPlugins: reset, queryPlugins, queryPluginsWithDebounced, - isLoading, - setIsLoading, + isLoading: isPending, } } diff --git a/web/app/components/plugins/marketplace/index.tsx b/web/app/components/plugins/marketplace/index.tsx index 742df86ea0..10e623710e 100644 --- a/web/app/components/plugins/marketplace/index.tsx +++ b/web/app/components/plugins/marketplace/index.tsx @@ -5,6 +5,7 @@ import SearchBoxWrapper from './search-box/search-box-wrapper' import PluginTypeSwitch from './plugin-type-switch' import ListWrapper from './list/list-wrapper' import { getMarketplaceCollectionsAndPlugins } from './utils' +import { TanstackQueryIniter } from '@/context/query-client' type MarketplaceProps = { locale?: string @@ -17,18 +18,20 @@ const Marketplace = async ({ const { marketplaceCollections, marketplaceCollectionPluginsMap } = await getMarketplaceCollectionsAndPlugins() return ( - - - - - - - + + + + + + + + + ) } diff --git a/web/app/components/plugins/types.ts b/web/app/components/plugins/types.ts index 95b255de62..629b9b7582 100644 --- a/web/app/components/plugins/types.ts +++ b/web/app/components/plugins/types.ts @@ -301,3 +301,7 @@ export type InstalledPluginListResponse = { export type UninstallPluginResponse = { success: boolean } + +export type PluginsFromMarketplaceResponse = { + plugins: Plugin[] +} diff --git a/web/app/components/tools/marketplace/hooks.ts b/web/app/components/tools/marketplace/hooks.ts index 82f019ef14..5d27e10043 100644 --- a/web/app/components/tools/marketplace/hooks.ts +++ b/web/app/components/tools/marketplace/hooks.ts @@ -15,7 +15,7 @@ export const useMarketplace = (searchPluginText: string, filterPluginTags: strin } = useMarketplaceCollectionsAndPlugins() const { plugins, - setPlugins, + resetPlugins, queryPlugins, queryPluginsWithDebounced, isLoading: isPluginsLoading, @@ -37,9 +37,9 @@ export const useMarketplace = (searchPluginText: string, filterPluginTags: strin } else { queryMarketplaceCollectionsAndPlugins() - setPlugins(undefined) + resetPlugins() } - }, [searchPluginText, filterPluginTags, queryPlugins, queryMarketplaceCollectionsAndPlugins, queryPluginsWithDebounced, setPlugins]) + }, [searchPluginText, filterPluginTags, queryPlugins, queryMarketplaceCollectionsAndPlugins, queryPluginsWithDebounced, resetPlugins]) return { isLoading: isLoading || isPluginsLoading, diff --git a/web/service/base.ts b/web/service/base.ts index 0f9059e617..061e8dcd2c 100644 --- a/web/service/base.ts +++ b/web/service/base.ts @@ -460,6 +460,11 @@ export const post = (url: string, options = {}, otherOptions?: IOtherOptions) return request(url, Object.assign({}, options, { method: 'POST' }), otherOptions) } +// For Marketplace API +export const postMarketplace = (url: string, options = {}, otherOptions?: IOtherOptions) => { + return post(url, options, { ...otherOptions, isMarketplaceAPI: true }) +} + export const postPublic = (url: string, options = {}, otherOptions?: IOtherOptions) => { return post(url, options, { ...otherOptions, isPublicAPI: true }) } diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts index 754cd1cc2d..251e3a40cd 100644 --- a/web/service/use-plugins.ts +++ b/web/service/use-plugins.ts @@ -1,12 +1,17 @@ -import type { DebugInfo as DebugInfoTypes, InstalledPluginListResponse, Permissions } from '@/app/components/plugins/types' -import { get, post } from './base' +import type { + DebugInfo as DebugInfoTypes, + InstalledPluginListResponse, + Permissions, + PluginsFromMarketplaceResponse, +} from '@/app/components/plugins/types' +import type { + PluginsSearchParams, +} from '@/app/components/plugins/marketplace/types' +import { get, post, postMarketplace } from './base' import { useMutation, - useQueryClient, -} from '@tanstack/react-query' - -import { useQuery, + useQueryClient, } from '@tanstack/react-query' const NAME_SPACE = 'plugins' @@ -66,3 +71,28 @@ export const useMutationPermissions = ({ onSuccess, }) } + +export const useMutationPluginsFromMarketplace = () => { + return useMutation({ + mutationFn: (pluginsSearchParams: PluginsSearchParams) => { + const { + query, + sortBy, + sortOrder, + category, + tags, + } = pluginsSearchParams + return postMarketplace<{ data: PluginsFromMarketplaceResponse }>('/plugins/search/basic', { + body: { + page: 1, + page_size: 10, + query, + sort_by: sortBy, + sort_order: sortOrder, + category: category !== 'all' ? category : '', + tags, + }, + }) + }, + }) +} From c77b38b97db4ca7c7e5d2703090ebd76c7ca9ae5 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 8 Nov 2024 18:25:15 +0800 Subject: [PATCH 13/14] chore: install from marketplace --- .../install-from-marketplace/steps/install.tsx | 3 ++- web/service/plugins.ts | 6 ------ web/service/use-plugins.ts | 9 +++++++++ 3 files changed, 11 insertions(+), 7 deletions(-) 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 bc32e642a5..27ae871d97 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 @@ -9,7 +9,7 @@ import Button from '@/app/components/base/button' import { useTranslation } from 'react-i18next' import { RiLoader2Line } from '@remixicon/react' import Badge, { BadgeState } from '@/app/components/base/badge/index' -import { installPackageFromMarketPlace } from '@/service/plugins' +import { useInstallPackageFromMarketPlace } from '@/service/use-plugins' import checkTaskStatus from '../../base/check-task-status' const i18nPrefix = 'plugin.installModal' @@ -32,6 +32,7 @@ const Installed: FC = ({ onFailed, }) => { const { t } = useTranslation() + const { mutateAsync: installPackageFromMarketPlace } = useInstallPackageFromMarketPlace() const [isInstalling, setIsInstalling] = React.useState(false) const { check, diff --git a/web/service/plugins.ts b/web/service/plugins.ts index b7d5125f2e..3e5d872bf2 100644 --- a/web/service/plugins.ts +++ b/web/service/plugins.ts @@ -104,12 +104,6 @@ export const fetchManifestFromMarketPlace = async (uniqueIdentifier: string) => return getMarketplace<{ data: { plugin: PluginManifestInMarket } }>(`/plugins/identifier?unique_identifier=${uniqueIdentifier}`) } -export const installPackageFromMarketPlace = async (uniqueIdentifier: string) => { - return post('/workspaces/current/plugin/install/marketplace', { - body: { plugin_unique_identifiers: [uniqueIdentifier] }, - }) -} - export const fetchMarketplaceCollections: Fetcher = ({ url }) => { return get(url) } diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts index 251e3a40cd..2274250b58 100644 --- a/web/service/use-plugins.ts +++ b/web/service/use-plugins.ts @@ -1,5 +1,6 @@ import type { DebugInfo as DebugInfoTypes, + InstallPackageResponse, InstalledPluginListResponse, Permissions, PluginsFromMarketplaceResponse, @@ -34,6 +35,14 @@ export const useInvalidateInstalledPluginList = () => { } } +export const useInstallPackageFromMarketPlace = () => { + return useMutation({ + mutationFn: (uniqueIdentifier: string) => { + return post('/workspaces/current/plugin/install/marketplace', { body: { plugin_unique_identifiers: [uniqueIdentifier] } }) + }, + }) +} + export const useDebugKey = () => { return useQuery({ queryKey: [NAME_SPACE, 'debugKey'], From f2bf2e4470bb0f26af93c469f5a2fb3c8e4367e9 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Fri, 8 Nov 2024 21:31:44 +0800 Subject: [PATCH 14/14] fix style of provider added card --- .../model-provider-page/provider-added-card/model-list.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx index 5e70a0def1..b4b331c4bc 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list.tsx @@ -50,15 +50,15 @@ const ModelList: FC = ({ return (
-
+
- + {t('common.modelProvider.modelsNum', { num: models.length })} onCollapse()} > {t('common.modelProvider.modelsNum', { num: models.length })}