From cad751c00ca1d074d807dab943c6db56093b2f78 Mon Sep 17 00:00:00 2001 From: Anubhav Singh Date: Fri, 14 Nov 2025 07:17:21 +0530 Subject: [PATCH 1/9] Upgrade weave version to fix weave configuration failure (#28197) --- api/pyproject.toml | 4 ++-- api/uv.lock | 20 +++++--------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/api/pyproject.toml b/api/pyproject.toml index 9ff4c3f5de..1cf7d719ea 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -37,7 +37,7 @@ dependencies = [ "numpy~=1.26.4", "openpyxl~=3.1.5", "opik~=1.8.72", - "litellm==1.77.1", # Pinned to avoid madoka dependency issue + "litellm==1.77.1", # Pinned to avoid madoka dependency issue "opentelemetry-api==1.27.0", "opentelemetry-distro==0.48b0", "opentelemetry-exporter-otlp==1.27.0", @@ -79,7 +79,6 @@ dependencies = [ "tiktoken~=0.9.0", "transformers~=4.56.1", "unstructured[docx,epub,md,ppt,pptx]~=0.16.1", - "weave~=0.51.0", "yarl~=1.18.3", "webvtt-py~=0.5.1", "sseclient-py~=1.8.0", @@ -90,6 +89,7 @@ dependencies = [ "croniter>=6.0.0", "weaviate-client==4.17.0", "apscheduler>=3.11.0", + "weave>=0.52.16", ] # Before adding new dependency, consider place it in # alphabet order (a-z) and suitable group. diff --git a/api/uv.lock b/api/uv.lock index 439a0566e8..6300adae61 100644 --- a/api/uv.lock +++ b/api/uv.lock @@ -1588,7 +1588,7 @@ requires-dist = [ { name = "tiktoken", specifier = "~=0.9.0" }, { name = "transformers", specifier = "~=4.56.1" }, { name = "unstructured", extras = ["docx", "epub", "md", "ppt", "pptx"], specifier = "~=0.16.1" }, - { name = "weave", specifier = "~=0.51.0" }, + { name = "weave", specifier = ">=0.52.16" }, { name = "weaviate-client", specifier = "==4.17.0" }, { name = "webvtt-py", specifier = "~=0.5.1" }, { name = "yarl", specifier = "~=1.18.3" }, @@ -3538,15 +3538,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/95/e1/45373c06781340c7b74fe9b88b85278ac05321889a307eaa5be079a997d4/mysql_connector_python-9.5.0-py2.py3-none-any.whl", hash = "sha256:ace137b88eb6fdafa1e5b2e03ac76ce1b8b1844b3a4af1192a02ae7c1a45bdee", size = 479047, upload-time = "2025-10-22T09:02:27.809Z" }, ] -[[package]] -name = "nest-asyncio" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418, upload-time = "2024-01-21T14:25:19.227Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" }, -] - [[package]] name = "networkx" version = "3.5" @@ -6906,7 +6897,7 @@ wheels = [ [[package]] name = "weave" -version = "0.51.59" +version = "0.52.16" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -6914,18 +6905,17 @@ dependencies = [ { name = "eval-type-backport" }, { name = "gql", extra = ["aiohttp", "requests"] }, { name = "jsonschema" }, - { name = "nest-asyncio" }, { name = "packaging" }, { name = "polyfile-weave" }, { name = "pydantic" }, - { name = "rich" }, { name = "sentry-sdk" }, { name = "tenacity" }, + { name = "tzdata", marker = "sys_platform == 'win32'" }, { name = "wandb" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0e/53/1b0350a64837df3e29eda6149a542f3a51e706122086f82547153820e982/weave-0.51.59.tar.gz", hash = "sha256:fad34c0478f3470401274cba8fa2bfd45d14a187db0a5724bd507e356761b349", size = 480572, upload-time = "2025-07-25T22:05:07.458Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/30/b795b5a857e8a908e68f3ed969587bb2bc63527ef2260f72ac1a6fd983e8/weave-0.52.16.tar.gz", hash = "sha256:7bb8fdce0393007e9c40fb1769d0606bfe55401c4cd13146457ccac4b49c695d", size = 607024, upload-time = "2025-11-07T19:45:30.898Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1d/bc/fa5ffb887a1ee28109b29c62416c9e0f41da8e75e6871671208b3d42b392/weave-0.51.59-py3-none-any.whl", hash = "sha256:2238578574ecdf6285efdf028c78987769720242ac75b7b84b1dbc59060468ce", size = 612468, upload-time = "2025-07-25T22:05:05.088Z" }, + { url = "https://files.pythonhosted.org/packages/e5/87/a54513796605dfaef2c3c23c2733bcb4b24866a623635c057b2ffdb74052/weave-0.52.16-py3-none-any.whl", hash = "sha256:85985b8cf233032c6d915dfac95b3bcccb1304444d99a6b4a61f3666b58146ce", size = 764366, upload-time = "2025-11-07T19:45:28.878Z" }, ] [[package]] From 1e6d0de48bbc08b069f721fd615b18ed277b695b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Fri, 14 Nov 2025 09:47:37 +0800 Subject: [PATCH 2/9] fix: knowledge pipeline can not published (#28203) --- .../workflow/hooks/use-checklist.ts | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/web/app/components/workflow/hooks/use-checklist.ts b/web/app/components/workflow/hooks/use-checklist.ts index 8c4ec7299e..be7aabbc68 100644 --- a/web/app/components/workflow/hooks/use-checklist.ts +++ b/web/app/components/workflow/hooks/use-checklist.ts @@ -249,6 +249,8 @@ export const useChecklistBeforePublish = () => { const { data: buildInTools } = useAllBuiltInTools() const { data: customTools } = useAllCustomTools() const { data: workflowTools } = useAllWorkflowTools() + const appMode = useAppStore.getState().appDetail?.mode + const shouldCheckStartNode = appMode === AppModeEnum.WORKFLOW || appMode === AppModeEnum.ADVANCED_CHAT const getCheckData = useCallback((data: CommonNodeType<{}>, datasets: DataSet[]) => { let checkData = data @@ -366,17 +368,22 @@ export const useChecklistBeforePublish = () => { } } - if (!validNodes.find(n => n.id === node.id)) { + const isStartNodeMeta = nodesExtraData?.[node.data.type as BlockEnum]?.metaData.isStart ?? false + const canSkipConnectionCheck = shouldCheckStartNode ? isStartNodeMeta : true + const isUnconnected = !validNodes.find(n => n.id === node.id) + + if (isUnconnected && !canSkipConnectionCheck) { notify({ type: 'error', message: `[${node.data.title}] ${t('workflow.common.needConnectTip')}` }) return false } } - const startNodesFiltered = nodes.filter(node => START_NODE_TYPES.includes(node.data.type as BlockEnum)) - - if (startNodesFiltered.length === 0) { - notify({ type: 'error', message: t('workflow.common.needStartNode') }) - return false + if (shouldCheckStartNode) { + const startNodesFiltered = nodes.filter(node => START_NODE_TYPES.includes(node.data.type as BlockEnum)) + if (startNodesFiltered.length === 0) { + notify({ type: 'error', message: t('workflow.common.needStartNode') }) + return false + } } const isRequiredNodesType = Object.keys(nodesExtraData!).filter((key: any) => (nodesExtraData as any)[key].metaData.isRequired) @@ -391,7 +398,7 @@ export const useChecklistBeforePublish = () => { } return true - }, [store, notify, t, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData, workflowStore, buildInTools, customTools, workflowTools]) + }, [store, notify, t, language, nodesExtraData, strategyProviders, updateDatasetsDetail, getCheckData, workflowStore, buildInTools, customTools, workflowTools, shouldCheckStartNode]) return { handleCheckBeforePublish, From 15fbedfcadd46f0e78a165adf3b8047eba2de94f Mon Sep 17 00:00:00 2001 From: lyzno1 <92089059+lyzno1@users.noreply.github.com> Date: Fri, 14 Nov 2025 13:34:23 +0800 Subject: [PATCH 3/9] feat: add icon gallery stories (#28214) Signed-off-by: lyzno1 --- .../base/icons/icon-gallery.stories.tsx | 258 ++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 web/app/components/base/icons/icon-gallery.stories.tsx diff --git a/web/app/components/base/icons/icon-gallery.stories.tsx b/web/app/components/base/icons/icon-gallery.stories.tsx new file mode 100644 index 0000000000..7da71b3b0b --- /dev/null +++ b/web/app/components/base/icons/icon-gallery.stories.tsx @@ -0,0 +1,258 @@ +import type { Meta, StoryObj } from '@storybook/nextjs' +import React from 'react' + +declare const require: any + +type IconComponent = React.ComponentType> + +type IconEntry = { + name: string + category: string + path: string + Component: IconComponent +} + +const iconContext = require.context('./src', true, /\.tsx$/) + +const iconEntries: IconEntry[] = iconContext + .keys() + .filter((key: string) => !key.endsWith('.stories.tsx') && !key.endsWith('.spec.tsx')) + .map((key: string) => { + const mod = iconContext(key) + const Component = mod.default as IconComponent | undefined + if (!Component) + return null + + const relativePath = key.replace(/^\.\//, '') + const path = `app/components/base/icons/src/${relativePath}` + const parts = relativePath.split('/') + const fileName = parts.pop() || '' + const category = parts.length ? parts.join('/') : '(root)' + const name = Component.displayName || fileName.replace(/\.tsx$/, '') + + return { + name, + category, + path, + Component, + } + }) + .filter(Boolean) as IconEntry[] + +const sortedEntries = [...iconEntries].sort((a, b) => { + if (a.category === b.category) + return a.name.localeCompare(b.name) + return a.category.localeCompare(b.category) +}) + +const filterEntries = (entries: IconEntry[], query: string) => { + const normalized = query.trim().toLowerCase() + if (!normalized) + return entries + + return entries.filter(entry => + entry.name.toLowerCase().includes(normalized) + || entry.path.toLowerCase().includes(normalized) + || entry.category.toLowerCase().includes(normalized), + ) +} + +const groupByCategory = (entries: IconEntry[]) => entries.reduce((acc, entry) => { + if (!acc[entry.category]) + acc[entry.category] = [] + + acc[entry.category].push(entry) + return acc +}, {} as Record) + +const containerStyle: React.CSSProperties = { + padding: 24, + display: 'flex', + flexDirection: 'column', + gap: 24, +} + +const headerStyle: React.CSSProperties = { + display: 'flex', + flexDirection: 'column', + gap: 8, +} + +const controlsStyle: React.CSSProperties = { + display: 'flex', + alignItems: 'center', + gap: 12, + flexWrap: 'wrap', +} + +const searchInputStyle: React.CSSProperties = { + padding: '8px 12px', + minWidth: 280, + borderRadius: 6, + border: '1px solid #d0d0d5', +} + +const toggleButtonStyle: React.CSSProperties = { + padding: '8px 12px', + borderRadius: 6, + border: '1px solid #d0d0d5', + background: '#fff', + cursor: 'pointer', +} + +const emptyTextStyle: React.CSSProperties = { color: '#5f5f66' } + +const sectionStyle: React.CSSProperties = { + display: 'flex', + flexDirection: 'column', + gap: 12, +} + +const gridStyle: React.CSSProperties = { + display: 'grid', + gap: 12, + gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', +} + +const cardStyle: React.CSSProperties = { + border: '1px solid #e1e1e8', + borderRadius: 8, + padding: 12, + display: 'flex', + flexDirection: 'column', + gap: 8, + minHeight: 140, +} + +const previewBaseStyle: React.CSSProperties = { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + minHeight: 48, + borderRadius: 6, +} + +const nameButtonBaseStyle: React.CSSProperties = { + display: 'inline-flex', + padding: 0, + border: 'none', + background: 'transparent', + font: 'inherit', + cursor: 'pointer', + textAlign: 'left', + fontWeight: 600, +} + +const PREVIEW_SIZE = 40 + +const IconGalleryStory = () => { + const [query, setQuery] = React.useState('') + const [copiedPath, setCopiedPath] = React.useState(null) + const [previewTheme, setPreviewTheme] = React.useState<'light' | 'dark'>('light') + + const filtered = React.useMemo(() => filterEntries(sortedEntries, query), [query]) + + const grouped = React.useMemo(() => groupByCategory(filtered), [filtered]) + + const categoryOrder = React.useMemo( + () => Object.keys(grouped).sort((a, b) => a.localeCompare(b)), + [grouped], + ) + + React.useEffect(() => { + if (!copiedPath) + return undefined + + const timerId = window.setTimeout(() => { + setCopiedPath(null) + }, 1200) + + return () => window.clearTimeout(timerId) + }, [copiedPath]) + + const handleCopy = React.useCallback((text: string) => { + navigator.clipboard?.writeText(text) + .then(() => { + setCopiedPath(text) + }) + .catch((err) => { + console.error('Failed to copy icon path:', err) + }) + }, []) + + return ( +
+
+

Icon Gallery

+

+ Browse all icon components sourced from app/components/base/icons/src. Use the search bar + to filter by name or path. +

+
+ setQuery(event.target.value)} + /> + {filtered.length} icons + +
+
+ {categoryOrder.length === 0 && ( +

No icons match the current filter.

+ )} + {categoryOrder.map(category => ( +
+

{category}

+
+ {grouped[category].map(entry => ( +
+
+ +
+ +
+ ))} +
+
+ ))} +
+ ) +} + +const meta: Meta = { + title: 'Base/Icons/Icon Gallery', + component: IconGalleryStory, + parameters: { + layout: 'fullscreen', + }, +} + +export default meta + +type Story = StoryObj + +export const All: Story = { + render: () => , +} From 85b99580ea4b2bb9eb42691a00554be92233280d Mon Sep 17 00:00:00 2001 From: lyzno1 <92089059+lyzno1@users.noreply.github.com> Date: Fri, 14 Nov 2025 14:16:11 +0800 Subject: [PATCH 4/9] fix: card view render (#28189) --- .../[appId]/overview/card-view.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx index 7e592729a5..57f3ef6881 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx @@ -37,18 +37,22 @@ const CardView: FC = ({ appId, isInPanel, className }) => { const appDetail = useAppStore(state => state.appDetail) const setAppDetail = useAppStore(state => state.setAppDetail) + const isWorkflowApp = appDetail?.mode === AppModeEnum.WORKFLOW const showMCPCard = isInPanel - const showTriggerCard = isInPanel && appDetail?.mode === AppModeEnum.WORKFLOW - const { data: currentWorkflow } = useAppWorkflow(appDetail?.mode === AppModeEnum.WORKFLOW ? appDetail.id : '') - const hasTriggerNode = useMemo(() => { - if (appDetail?.mode !== AppModeEnum.WORKFLOW) + const showTriggerCard = isInPanel && isWorkflowApp + const { data: currentWorkflow } = useAppWorkflow(isWorkflowApp ? appDetail.id : '') + const hasTriggerNode = useMemo(() => { + if (!isWorkflowApp) return false - const nodes = currentWorkflow?.graph?.nodes || [] + if (!currentWorkflow) + return null + const nodes = currentWorkflow.graph?.nodes || [] return nodes.some((node) => { const nodeType = node.data?.type as BlockEnum | undefined return !!nodeType && isTriggerNode(nodeType) }) - }, [appDetail?.mode, currentWorkflow]) + }, [isWorkflowApp, currentWorkflow]) + const shouldRenderAppCards = !isWorkflowApp || hasTriggerNode === false const updateAppDetail = async () => { try { @@ -123,7 +127,7 @@ const CardView: FC = ({ appId, isInPanel, className }) => { return (
{ - !hasTriggerNode && ( + shouldRenderAppCards && ( <> Date: Fri, 14 Nov 2025 15:38:19 +0800 Subject: [PATCH 5/9] add icon for forum (#28164) --- web/app/components/apps/footer.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/app/components/apps/footer.tsx b/web/app/components/apps/footer.tsx index 9fed4c8757..16411ffc57 100644 --- a/web/app/components/apps/footer.tsx +++ b/web/app/components/apps/footer.tsx @@ -1,6 +1,6 @@ import React from 'react' import Link from 'next/link' -import { RiDiscordFill, RiGithubFill } from '@remixicon/react' +import { RiDiscordFill, RiDiscussLine, RiGithubFill } from '@remixicon/react' import { useTranslation } from 'react-i18next' type CustomLinkProps = { @@ -38,6 +38,9 @@ const Footer = () => { + + +
) From 4a89403566c85eff76a959bc62be76f1e8af5ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Fri, 14 Nov 2025 15:38:43 +0800 Subject: [PATCH 6/9] fix: click log panel of log page cause whole page crash (#28218) --- web/app/components/app/log/list.tsx | 21 +++++++++++-------- .../base/message-log-modal/index.stories.tsx | 11 ++++++---- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/web/app/components/app/log/list.tsx b/web/app/components/app/log/list.tsx index 5de86be7b9..54cc345d2e 100644 --- a/web/app/components/app/log/list.tsx +++ b/web/app/components/app/log/list.tsx @@ -42,6 +42,7 @@ import { getProcessedFilesFromResponse } from '@/app/components/base/file-upload import cn from '@/utils/classnames' import { noop } from 'lodash-es' import PromptLogModal from '../../base/prompt-log-modal' +import { WorkflowContextProvider } from '@/app/components/workflow/context' type AppStoreState = ReturnType type ConversationListItem = ChatConversationGeneralDetail | CompletionConversationGeneralDetail @@ -779,15 +780,17 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) { } {showMessageLogModal && ( - { - setCurrentLogItem() - setShowMessageLogModal(false) - }} - defaultTab={currentLogModalActiveTab} - /> + + { + setCurrentLogItem() + setShowMessageLogModal(false) + }} + defaultTab={currentLogModalActiveTab} + /> + )} {!isChatMode && showPromptLogModal && ( { return (
- + + +
) } From 5d2fbf5215806b5afae40704069865d2bd67b336 Mon Sep 17 00:00:00 2001 From: zhsama Date: Mon, 17 Nov 2025 16:23:04 +0800 Subject: [PATCH 7/9] Perf/mutual node UI (#28282) --- .../[appId]/overview/card-view.tsx | 102 ++++++++++++------ web/app/components/app/overview/app-card.tsx | 58 +++++++--- .../components/tools/mcp/mcp-service-card.tsx | 21 +++- web/i18n/en-US/app-overview.ts | 3 + web/i18n/ja-JP/app-overview.ts | 3 + web/i18n/zh-Hans/app-overview.ts | 3 + 6 files changed, 139 insertions(+), 51 deletions(-) diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx index 57f3ef6881..fb431c5ac8 100644 --- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx +++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React, { useMemo } from 'react' +import React, { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import AppCard from '@/app/components/app/overview/app-card' @@ -24,6 +24,7 @@ import { useStore as useAppStore } from '@/app/components/app/store' import { useAppWorkflow } from '@/service/use-workflow' import type { BlockEnum } from '@/app/components/workflow/types' import { isTriggerNode } from '@/app/components/workflow/types' +import { useDocLink } from '@/context/i18n' export type ICardViewProps = { appId: string @@ -33,6 +34,7 @@ export type ICardViewProps = { const CardView: FC = ({ appId, isInPanel, className }) => { const { t } = useTranslation() + const docLink = useDocLink() const { notify } = useContext(ToastContext) const appDetail = useAppStore(state => state.appDetail) const setAppDetail = useAppStore(state => state.setAppDetail) @@ -53,6 +55,35 @@ const CardView: FC = ({ appId, isInPanel, className }) => { }) }, [isWorkflowApp, currentWorkflow]) const shouldRenderAppCards = !isWorkflowApp || hasTriggerNode === false + const disableAppCards = !shouldRenderAppCards + + const triggerDocUrl = docLink('/guides/workflow/node/start') + const buildTriggerModeMessage = useCallback((featureName: string) => ( +
+
+ {t('appOverview.overview.disableTooltip.triggerMode', { feature: featureName })} +
+
{ + event.stopPropagation() + window.open(triggerDocUrl, '_blank') + }} + > + {t('appOverview.overview.appInfo.enableTooltip.learnMore')} +
+
+ ), [t, triggerDocUrl]) + + const disableWebAppTooltip = disableAppCards + ? buildTriggerModeMessage(t('appOverview.overview.appInfo.title')) + : null + const disableApiTooltip = disableAppCards + ? buildTriggerModeMessage(t('appOverview.overview.apiInfo.title')) + : null + const disableMcpTooltip = disableAppCards + ? buildTriggerModeMessage(t('tools.mcp.server.title')) + : null const updateAppDetail = async () => { try { @@ -124,39 +155,48 @@ const CardView: FC = ({ appId, isInPanel, className }) => { if (!appDetail) return - return ( -
- { - shouldRenderAppCards && ( - <> - - - {showMCPCard && ( - - )} - - ) - } - {showTriggerCard && ( - + + + {showMCPCard && ( + )} + + ) + + const triggerCardNode = showTriggerCard ? ( + + ) : null + + return ( +
+ {disableAppCards && triggerCardNode} + {appCards} + {!disableAppCards && triggerCardNode}
) } diff --git a/web/app/components/app/overview/app-card.tsx b/web/app/components/app/overview/app-card.tsx index dcb6ae6b4d..a0f5780b71 100644 --- a/web/app/components/app/overview/app-card.tsx +++ b/web/app/components/app/overview/app-card.tsx @@ -51,6 +51,8 @@ export type IAppCardProps = { isInPanel?: boolean cardType?: 'api' | 'webapp' customBgColor?: string + triggerModeDisabled?: boolean // true when Trigger Node mode needs UI locked to avoid conflicting actions + triggerModeMessage?: React.ReactNode // contextual copy explaining why the card is disabled in trigger mode onChangeStatus: (val: boolean) => Promise onSaveSiteConfig?: (params: ConfigParams) => Promise onGenerateCode?: () => Promise @@ -61,6 +63,8 @@ function AppCard({ isInPanel, cardType = 'webapp', customBgColor, + triggerModeDisabled = false, + triggerModeMessage = '', onChangeStatus, onSaveSiteConfig, onGenerateCode, @@ -111,7 +115,7 @@ function AppCard({ const hasStartNode = currentWorkflow?.graph?.nodes?.some(node => node.data.type === BlockEnum.Start) const missingStartNode = isWorkflowApp && !hasStartNode const hasInsufficientPermissions = isApp ? !isCurrentWorkspaceEditor : !isCurrentWorkspaceManager - const toggleDisabled = hasInsufficientPermissions || appUnpublished || missingStartNode + const toggleDisabled = hasInsufficientPermissions || appUnpublished || missingStartNode || triggerModeDisabled const runningStatus = (appUnpublished || missingStartNode) ? false : (isApp ? appInfo.enable_site : appInfo.enable_api) const isMinimalState = appUnpublished || missingStartNode const { app_base_url, access_token } = appInfo.site ?? {} @@ -189,7 +193,20 @@ function AppCard({ className={ `${isInPanel ? 'border-l-[0.5px] border-t' : 'border-[0.5px] shadow-xs'} w-full max-w-full rounded-xl border-effects-highlight ${className ?? ''} ${isMinimalState ? 'h-12' : ''}`} > -
+
+ {triggerModeDisabled && ( + triggerModeMessage + ? ( + + + + ) + : + )}
-
- {t('appOverview.overview.appInfo.enableTooltip.description')} -
-
window.open(docLink('/guides/workflow/node/user-input'), '_blank')} - > - {t('appOverview.overview.appInfo.enableTooltip.learnMore')} -
- + toggleDisabled ? ( + triggerModeDisabled && triggerModeMessage + ? triggerModeMessage + : (appUnpublished || missingStartNode) ? ( + <> +
+ {t('appOverview.overview.appInfo.enableTooltip.description')} +
+
window.open(docLink('/guides/workflow/node/user-input'), '_blank')} + > + {t('appOverview.overview.appInfo.enableTooltip.learnMore')} +
+ + ) + : '' ) : '' } position="right" @@ -329,9 +351,11 @@ function AppCard({ {!isApp && } {OPERATIONS_MAP[cardType].map((op) => { const disabled - = op.opName === t('appOverview.overview.appInfo.settings.entry') - ? false - : !runningStatus + = triggerModeDisabled + ? true + : op.opName === t('appOverview.overview.appInfo.settings.entry') + ? false + : !runningStatus return (