+
diff --git a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts
index cd7701f2e2..5d131fba5b 100644
--- a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts
+++ b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts
@@ -5,7 +5,6 @@ import { useParams } from 'next/navigation'
import {
useWorkflowStore,
} from '@/app/components/workflow/store'
-import { BlockEnum } from '@/app/components/workflow/types'
import {
useNodesReadOnly,
} from '@/app/components/workflow/hooks/use-workflow'
@@ -38,16 +37,7 @@ export const useNodesSyncDraft = () => {
if (appId) {
const nodes = getNodes()
- const startNodeTypes = [
- BlockEnum.Start,
- BlockEnum.TriggerSchedule,
- BlockEnum.TriggerWebhook,
- BlockEnum.TriggerPlugin,
- ]
- const hasStartNode = nodes.some(node => startNodeTypes.includes(node.data.type))
-
- if (!hasStartNode)
- return
+ // Allow empty workflows - sync restrictions removed to support empty workflow editing
const features = featuresStore!.getState().features
const producedNodes = produce(nodes, (draft) => {
diff --git a/web/app/components/workflow/hooks/use-checklist.ts b/web/app/components/workflow/hooks/use-checklist.ts
index 335662a322..87d0fa83c9 100644
--- a/web/app/components/workflow/hooks/use-checklist.ts
+++ b/web/app/components/workflow/hooks/use-checklist.ts
@@ -166,7 +166,7 @@ export const useChecklist = (nodes: Node[], edges: Edge[]) => {
list.push({
id: 'start-node-required',
type: BlockEnum.Start,
- title: t('workflow.blocks.start'),
+ title: t('workflow.panel.startNode'),
errorMessage: t('workflow.common.needStartNode'),
})
}
diff --git a/web/app/components/workflow/hooks/use-nodes-interactions.ts b/web/app/components/workflow/hooks/use-nodes-interactions.ts
index 2bbd675a19..1ecdcf2ed9 100644
--- a/web/app/components/workflow/hooks/use-nodes-interactions.ts
+++ b/web/app/components/workflow/hooks/use-nodes-interactions.ts
@@ -18,7 +18,6 @@ import {
} from 'reactflow'
import { unionBy } from 'lodash-es'
import type { ToolDefaultValue } from '../block-selector/types'
-import { ENTRY_NODE_TYPES } from '../block-selector/constants'
import type {
Edge,
Node,
@@ -64,23 +63,7 @@ import { WorkflowHistoryEvent, useWorkflowHistory } from './use-workflow-history
import useInspectVarsCrud from './use-inspect-vars-crud'
import { getNodeUsedVars } from '../nodes/_base/components/variable/utils'
-// Helper function to check if a node is an entry node
-const isEntryNode = (nodeType: BlockEnum): boolean => {
- return ENTRY_NODE_TYPES.includes(nodeType as any)
-}
-
-// Helper function to check if entry node can be deleted
-const canDeleteEntryNode = (nodes: Node[], nodeId: string): boolean => {
- const targetNode = nodes.find(node => node.id === nodeId)
- if (!targetNode || !isEntryNode(targetNode.data.type))
- return true // Non-entry nodes can always be deleted
-
- // Count all entry nodes
- const entryNodes = nodes.filter(node => isEntryNode(node.data.type))
-
- // Can delete if there's more than one entry node
- return entryNodes.length > 1
-}
+// Entry node deletion restriction has been removed to allow empty workflows
export const useNodesInteractions = () => {
const { t } = useTranslation()
@@ -568,9 +551,7 @@ export const useNodesInteractions = () => {
const nodes = getNodes()
- // Check if entry node can be deleted (must keep at least one entry node)
- if (!canDeleteEntryNode(nodes, nodeId))
- return // Cannot delete the last entry node
+ // Allow deleting any node including the last entry node
const currentNodeIndex = nodes.findIndex(node => node.id === nodeId)
const currentNode = nodes[currentNodeIndex]
@@ -1410,7 +1391,7 @@ export const useNodesInteractions = () => {
const nodes = getNodes()
const bundledNodes = nodes.filter(node =>
- node.data._isBundled && canDeleteEntryNode(nodes, node.id),
+ node.data._isBundled,
)
if (bundledNodes.length) {
@@ -1424,7 +1405,7 @@ export const useNodesInteractions = () => {
return
const selectedNode = nodes.find(node =>
- node.data.selected && canDeleteEntryNode(nodes, node.id),
+ node.data.selected,
)
if (selectedNode)
diff --git a/web/i18n/en-US/app-overview.ts b/web/i18n/en-US/app-overview.ts
index feedc32e6b..078f1a1aea 100644
--- a/web/i18n/en-US/app-overview.ts
+++ b/web/i18n/en-US/app-overview.ts
@@ -30,6 +30,7 @@ const translation = {
overview: {
title: 'Overview',
appInfo: {
+ title: 'Web App',
explanation: 'Ready-to-use AI web app',
accessibleAddress: 'Public URL',
preview: 'Preview',
diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts
index 5c64d4307e..f7680a1147 100644
--- a/web/i18n/en-US/workflow.ts
+++ b/web/i18n/en-US/workflow.ts
@@ -46,7 +46,7 @@ const translation = {
needConnectTip: 'This step is not connected to anything',
maxTreeDepth: 'Maximum limit of {{depth}} nodes per branch',
needEndNode: 'The End node must be added',
- needStartNode: 'An entry node (User Input or Trigger) must be added',
+ needStartNode: 'At least one start node must be added',
needAnswerNode: 'The Answer node must be added',
workflowProcess: 'Workflow Process',
notRunning: 'Not running yet',
@@ -332,6 +332,7 @@ const translation = {
checklist: 'Checklist',
checklistTip: 'Make sure all issues are resolved before publishing',
checklistResolved: 'All issues are resolved',
+ startNode: 'Start Node',
organizeBlocks: 'Organize nodes',
change: 'Change',
optional: '(optional)',
diff --git a/web/i18n/ja-JP/app-overview.ts b/web/i18n/ja-JP/app-overview.ts
index d948bc3b28..3de41b2049 100644
--- a/web/i18n/ja-JP/app-overview.ts
+++ b/web/i18n/ja-JP/app-overview.ts
@@ -30,6 +30,7 @@ const translation = {
overview: {
title: '概要',
appInfo: {
+ title: 'Web App',
explanation: '使いやすい AI Web アプリ',
accessibleAddress: '公開 URL',
preview: 'プレビュー',
diff --git a/web/i18n/ja-JP/workflow.ts b/web/i18n/ja-JP/workflow.ts
index f7a2822315..6b7b1045d6 100644
--- a/web/i18n/ja-JP/workflow.ts
+++ b/web/i18n/ja-JP/workflow.ts
@@ -46,7 +46,7 @@ const translation = {
needConnectTip: '接続されていないステップがあります',
maxTreeDepth: '1 ブランチあたりの最大ノード数:{{depth}}',
needEndNode: '終了ブロックを追加する必要があります',
- needStartNode: '開始ノード(スタートまたはトリガー)を追加する必要があります',
+ needStartNode: '少なくとも1つのスタートノードを追加する必要があります',
needAnswerNode: '回答ブロックを追加する必要があります',
workflowProcess: 'ワークフロー処理',
notRunning: 'まだ実行されていません',
@@ -332,6 +332,7 @@ const translation = {
checklist: 'チェックリスト',
checklistTip: '公開前に全ての項目を確認してください',
checklistResolved: '全てのチェックが完了しました',
+ startNode: '開始ノード',
organizeBlocks: 'ノード整理',
change: '変更',
optional: '(任意)',
diff --git a/web/i18n/zh-Hans/app-overview.ts b/web/i18n/zh-Hans/app-overview.ts
index a41a86975a..16d455bc27 100644
--- a/web/i18n/zh-Hans/app-overview.ts
+++ b/web/i18n/zh-Hans/app-overview.ts
@@ -30,6 +30,7 @@ const translation = {
overview: {
title: '概览',
appInfo: {
+ title: 'Web App',
explanation: '开箱即用的 AI web app',
accessibleAddress: '公开访问 URL',
preview: '预览',
diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts
index 2b403dd794..b55ddfa17c 100644
--- a/web/i18n/zh-Hans/workflow.ts
+++ b/web/i18n/zh-Hans/workflow.ts
@@ -45,7 +45,7 @@ const translation = {
needConnectTip: '此节点尚未连接到其他节点',
maxTreeDepth: '每个分支最大限制 {{depth}} 个节点',
needEndNode: '必须添加结束节点',
- needStartNode: '必须添加开始节点(开始或触发器)',
+ needStartNode: '必须添加至少一个开始节点',
needAnswerNode: '必须添加直接回复节点',
workflowProcess: '工作流',
notRunning: '尚未运行',
@@ -332,6 +332,7 @@ const translation = {
checklist: '检查清单',
checklistTip: '发布前确保所有问题均已解决',
checklistResolved: '所有问题均已解决',
+ startNode: '开始节点',
organizeBlocks: '整理节点',
change: '更改',
optional: '(选填)',