diff --git a/web/app/components/main-nav/__tests__/index.spec.tsx b/web/app/components/main-nav/__tests__/index.spec.tsx
index 0494d4de1bf..4a67158c27e 100644
--- a/web/app/components/main-nav/__tests__/index.spec.tsx
+++ b/web/app/components/main-nav/__tests__/index.spec.tsx
@@ -28,13 +28,12 @@ import { DETAIL_SIDEBAR_STORAGE_KEY } from '../storage'
const activeEdgeClassName = 'before:pointer-events-none'
type SnippetNavigationTestState = {
- fields: SnippetInputField[]
onFieldsChange?: (fields: SnippetInputField[]) => void
readonly: boolean
snippet?: SnippetDetail
}
-const { mockIsAgentV2Enabled, mockSnippetFieldsChange, mockSwitchWorkspace, mockToastSuccess, hotkeyRegistrations, snippetNavigationState } = vi.hoisted(() => ({
+const { mockIsAgentV2Enabled, mockSnippetFieldsChange, mockSwitchWorkspace, mockToastSuccess, hotkeyRegistrations, snippetDraftState, snippetNavigationState } = vi.hoisted(() => ({
mockSwitchWorkspace: vi.fn(),
mockSnippetFieldsChange: vi.fn(),
mockToastSuccess: vi.fn(),
@@ -43,8 +42,10 @@ const { mockIsAgentV2Enabled, mockSnippetFieldsChange, mockSwitchWorkspace, mock
handler: (event: { preventDefault: () => void }) => void
options?: { ignoreInputs?: boolean }
}>(),
+ snippetDraftState: {
+ inputFields: [],
+ } as { inputFields: SnippetInputField[] },
snippetNavigationState: {
- fields: [],
readonly: true,
snippet: undefined,
onFieldsChange: undefined,
@@ -204,6 +205,10 @@ vi.mock('@/app/components/snippets/store', () => ({
useSnippetDetailStore: (selector: (state: SnippetNavigationTestState) => unknown) => selector(snippetNavigationState),
}))
+vi.mock('@/app/components/snippets/draft-store', () => ({
+ useSnippetDraftStore: (selector: (state: typeof snippetDraftState) => unknown) => selector(snippetDraftState),
+}))
+
vi.mock('@/app/components/snippets/components/snippet-sidebar', () => ({
SnippetSidebarContent: ({
fields,
@@ -423,7 +428,7 @@ describe('MainNav', () => {
})
mockSwitchWorkspace.mockReturnValue(new Promise(() => {}))
hotkeyRegistrations.clear()
- snippetNavigationState.fields = []
+ snippetDraftState.inputFields = []
snippetNavigationState.onFieldsChange = undefined
snippetNavigationState.readonly = true
snippetNavigationState.snippet = undefined
@@ -634,7 +639,7 @@ describe('MainNav', () => {
it('replaces global navigation with snippet detail navigation on snippet routes', () => {
mockPathname = '/snippets/snippet-1/orchestrate'
- snippetNavigationState.fields = snippetFields
+ snippetDraftState.inputFields = snippetFields
snippetNavigationState.onFieldsChange = mockSnippetFieldsChange
snippetNavigationState.readonly = false
snippetNavigationState.snippet = snippet
@@ -661,7 +666,7 @@ describe('MainNav', () => {
it('collapses snippet detail navigation from the top-right toggle', () => {
mockPathname = '/snippets/snippet-1/orchestrate'
- snippetNavigationState.fields = snippetFields
+ snippetDraftState.inputFields = snippetFields
snippetNavigationState.onFieldsChange = mockSnippetFieldsChange
snippetNavigationState.snippet = snippet
diff --git a/web/app/components/main-nav/index.tsx b/web/app/components/main-nav/index.tsx
index 11934d8605d..a629498a7ed 100644
--- a/web/app/components/main-nav/index.tsx
+++ b/web/app/components/main-nav/index.tsx
@@ -15,6 +15,7 @@ import { useStore as useAppStore } from '@/app/components/app/store'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import EnvNav from '@/app/components/header/env-nav'
import { SnippetSidebarContent } from '@/app/components/snippets/components/snippet-sidebar'
+import { useSnippetDraftStore } from '@/app/components/snippets/draft-store'
import { useSnippetDetailStore } from '@/app/components/snippets/store'
import { useAppContext } from '@/context/app-context'
import { AgentDetailSection, AgentDetailTop } from '@/features/agent-v2/agent-detail/navigation'
@@ -101,11 +102,11 @@ const MainNav = ({
const showSnippetDetailNavigation = isSnippetDetailPathname(pathname)
const showDetailNavigation = showAppDetailNavigation || showDatasetDetailNavigation || showAgentDetailNavigation || showDeploymentDetailNavigation || showSnippetDetailNavigation
const snippetNavigation = useSnippetDetailStore(useShallow(state => ({
- fields: state.fields,
onFieldsChange: state.onFieldsChange,
readonly: state.readonly,
snippet: state.snippet,
})))
+ const snippetInputFields = useSnippetDraftStore(state => state.inputFields)
const { hasAppDetail, setAppDetail } = useAppStore(useShallow(state => ({
hasAppDetail: !!state.appDetail,
setAppDetail: state.setAppDetail,
@@ -307,7 +308,7 @@ const MainNav = ({
? (
diff --git a/web/app/components/snippets/components/__tests__/snippet-main.spec.tsx b/web/app/components/snippets/components/__tests__/snippet-main.spec.tsx
index 7e0cf7494e5..3deef732ca1 100644
--- a/web/app/components/snippets/components/__tests__/snippet-main.spec.tsx
+++ b/web/app/components/snippets/components/__tests__/snippet-main.spec.tsx
@@ -6,13 +6,13 @@ import { act, fireEvent, screen, waitFor } from '@testing-library/react'
import { renderWorkflowComponent } from '@/app/components/workflow/__tests__/workflow-test-env'
import { BlockEnum } from '@/app/components/workflow/types'
import { PipelineInputVarType } from '@/models/pipeline'
+import { useSnippetDraftStore } from '../../draft-store'
import SnippetMain from '../snippet-main'
const mockSyncInputFieldsDraft = vi.fn()
const mockDoSyncWorkflowDraft = vi.fn()
const mockSyncWorkflowDraftWhenPageClose = vi.fn()
const mockReset = vi.fn()
-const mockSetFields = vi.fn()
const mockSetNavigationState = vi.fn()
const mockPublishSnippetMutateAsync = vi.fn()
const mockFetchInspectVars = vi.fn()
@@ -52,11 +52,9 @@ vi.mock('@langgenius/dify-ui/toast', () => ({
let capturedHooksStore: Record | undefined
let capturedWorkflowNodes: WorkflowProps['nodes'] | undefined
let snippetDetailStoreState: {
- fields: SnippetInputField[]
onFieldsChange?: (fields: SnippetInputField[]) => void
readonly: boolean
reset: typeof mockReset
- setFields: typeof mockSetFields
setNavigationState: typeof mockSetNavigationState
snippet?: SnippetDetail
snippetId?: string
@@ -277,11 +275,10 @@ describe('SnippetMain', () => {
})
capturedHooksStore = undefined
capturedWorkflowNodes = undefined
+ useSnippetDraftStore.getState().reset()
snippetDetailStoreState = {
- fields: [...payload.inputFields],
readonly: true,
reset: mockReset,
- setFields: mockSetFields,
setNavigationState: mockSetNavigationState,
}
})
diff --git a/web/app/components/snippets/components/hooks/__tests__/use-snippet-input-field-actions.spec.ts b/web/app/components/snippets/components/hooks/__tests__/use-snippet-input-field-actions.spec.ts
index d879042338b..ae21558f2ab 100644
--- a/web/app/components/snippets/components/hooks/__tests__/use-snippet-input-field-actions.spec.ts
+++ b/web/app/components/snippets/components/hooks/__tests__/use-snippet-input-field-actions.spec.ts
@@ -1,15 +1,10 @@
import type { SnippetInputField } from '@/models/snippet'
import { act, renderHook } from '@testing-library/react'
import { PipelineInputVarType } from '@/models/pipeline'
+import { useSnippetDraftStore } from '../../../draft-store'
import { useSnippetInputFieldActions } from '../use-snippet-input-field-actions'
const mockSyncInputFieldsDraft = vi.fn()
-const mockSetFields = vi.fn()
-
-let snippetDetailStoreState: {
- fields: SnippetInputField[]
- setFields: typeof mockSetFields
-}
vi.mock('../../../hooks/use-nodes-sync-draft', () => ({
useNodesSyncDraft: () => ({
@@ -17,10 +12,6 @@ vi.mock('../../../hooks/use-nodes-sync-draft', () => ({
}),
}))
-vi.mock('../../../store', () => ({
- useSnippetDetailStore: (selector: (state: typeof snippetDetailStoreState) => unknown) => selector(snippetDetailStoreState),
-}))
-
const createField = (overrides: Partial = {}): SnippetInputField => ({
type: PipelineInputVarType.textInput,
label: 'Blog URL',
@@ -32,19 +23,13 @@ const createField = (overrides: Partial = {}): SnippetInputFi
describe('useSnippetInputFieldActions', () => {
beforeEach(() => {
vi.clearAllMocks()
- snippetDetailStoreState = {
- fields: [],
- setFields: mockSetFields,
- }
- mockSetFields.mockImplementation((fields: SnippetInputField[]) => {
- snippetDetailStoreState.fields = fields
- })
+ useSnippetDraftStore.getState().reset()
mockSyncInputFieldsDraft.mockResolvedValue(undefined)
})
describe('Field sync', () => {
it('should update fields and sync the draft', () => {
- snippetDetailStoreState.fields = [createField()]
+ useSnippetDraftStore.getState().setInputFields([createField()])
const { result } = renderHook(() => useSnippetInputFieldActions({
snippetId: 'snippet-1',
}))
@@ -60,8 +45,8 @@ describe('useSnippetInputFieldActions', () => {
result.current.handleFieldsChange(nextFields)
})
- expect(result.current.fields).toEqual([createField()])
- expect(mockSetFields).toHaveBeenCalledWith(nextFields)
+ expect(result.current.fields).toEqual(nextFields)
+ expect(useSnippetDraftStore.getState().inputFields).toEqual(nextFields)
expect(mockSyncInputFieldsDraft).toHaveBeenCalledWith(nextFields, {
onRefresh: expect.any(Function),
})
diff --git a/web/app/components/snippets/components/hooks/use-snippet-input-field-actions.ts b/web/app/components/snippets/components/hooks/use-snippet-input-field-actions.ts
index 862cb3cfc29..853b96663c1 100644
--- a/web/app/components/snippets/components/hooks/use-snippet-input-field-actions.ts
+++ b/web/app/components/snippets/components/hooks/use-snippet-input-field-actions.ts
@@ -1,8 +1,8 @@
import type { SnippetInputField } from '@/models/snippet'
import { useCallback } from 'react'
import { useShallow } from 'zustand/react/shallow'
+import { useSnippetDraftStore } from '../../draft-store'
import { useNodesSyncDraft } from '../../hooks/use-nodes-sync-draft'
-import { useSnippetDetailStore } from '../../store'
type UseSnippetInputFieldActionsOptions = {
canEdit?: boolean
@@ -15,25 +15,25 @@ export const useSnippetInputFieldActions = ({
}: UseSnippetInputFieldActionsOptions) => {
const { syncInputFieldsDraft } = useNodesSyncDraft(snippetId)
const {
- fields,
- setFields,
- } = useSnippetDetailStore(useShallow(state => ({
- fields: state.fields,
- setFields: state.setFields,
+ inputFields,
+ setInputFields,
+ } = useSnippetDraftStore(useShallow(state => ({
+ inputFields: state.inputFields,
+ setInputFields: state.setInputFields,
})))
const handleFieldsChange = useCallback((newFields: SnippetInputField[]) => {
if (!canEdit)
return
- setFields(newFields)
+ setInputFields(newFields)
void syncInputFieldsDraft(newFields, {
- onRefresh: setFields,
+ onRefresh: setInputFields,
})
- }, [canEdit, setFields, syncInputFieldsDraft])
+ }, [canEdit, setInputFields, syncInputFieldsDraft])
return {
- fields,
+ fields: inputFields,
handleFieldsChange,
}
}
diff --git a/web/app/components/snippets/components/snippet-main.tsx b/web/app/components/snippets/components/snippet-main.tsx
index 19327b5faad..770d3e2e0c3 100644
--- a/web/app/components/snippets/components/snippet-main.tsx
+++ b/web/app/components/snippets/components/snippet-main.tsx
@@ -8,8 +8,8 @@ import { toast } from '@langgenius/dify-ui/toast'
import {
useCallback,
useEffect,
+ useLayoutEffect,
useMemo,
- useRef,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
@@ -23,6 +23,7 @@ import {
initialEdges,
initialNodes,
} from '@/app/components/workflow/utils'
+import { useSnippetDraftStore } from '../draft-store'
import { useConfigsMap } from '../hooks/use-configs-map'
import { useGetRunAndTraceUrl } from '../hooks/use-get-run-and-trace-url'
import { useInspectVarsCrud } from '../hooks/use-inspect-vars-crud'
@@ -136,16 +137,12 @@ const SnippetMain = ({
const effectiveDraftNodes = localDraftState?.nodes ?? draftNodes
const effectiveDraftEdges = localDraftState?.edges ?? draftEdges
const effectiveDraftViewport = localDraftState?.viewport ?? draftViewport
- const currentInputFieldsRef = useRef(effectiveDraftPayload.inputFields)
const { graph, snippet } = effectiveDraftPayload
const canSave = currentCanvasNodeCount > 0
- const getCurrentInputFields = useCallback(() => currentInputFieldsRef.current, [])
const {
doSyncWorkflowDraft: syncWorkflowDraft,
syncWorkflowDraftWhenPageClose,
- } = useNodesSyncDraft(snippetId, {
- getInputFields: getCurrentInputFields,
- })
+ } = useNodesSyncDraft(snippetId)
const workflowStore = useWorkflowStore()
const { handleRefreshWorkflowDraft } = useSnippetRefreshDraft(snippetId)
const {
@@ -197,13 +194,18 @@ const SnippetMain = ({
}, [workflowAvailableNodesMetaData])
const {
reset,
- setFields,
setNavigationState,
} = useSnippetDetailStore(useShallow(state => ({
reset: state.reset,
- setFields: state.setFields,
setNavigationState: state.setNavigationState,
})))
+ const {
+ hydrateDraft,
+ setInputFields,
+ } = useSnippetDraftStore(useShallow(state => ({
+ hydrateDraft: state.hydrateDraft,
+ setInputFields: state.setInputFields,
+ })))
const {
fields,
handleFieldsChange: handleSnippetFieldsChange,
@@ -224,10 +226,12 @@ const SnippetMain = ({
return () => reset()
}, [reset, snippetId])
- useEffect(() => {
- currentInputFieldsRef.current = effectiveDraftPayload.inputFields
- setFields(effectiveDraftPayload.inputFields)
- }, [effectiveDraftPayload.inputFields, setFields, snippetId])
+ useLayoutEffect(() => {
+ hydrateDraft({
+ snippetId,
+ inputFields: effectiveDraftPayload.inputFields,
+ })
+ }, [effectiveDraftPayload.inputFields, hydrateDraft, snippetId])
useEffect(() => {
workflowStore.setState({ canvasReadOnly: false })
@@ -254,7 +258,6 @@ const SnippetMain = ({
) => syncWorkflowDraft(...args), [syncWorkflowDraft])
const handleFieldsChange = useCallback((nextFields: SnippetInputField[]) => {
- currentInputFieldsRef.current = nextFields
handleSnippetFieldsChange(nextFields)
}, [handleSnippetFieldsChange])
@@ -286,7 +289,6 @@ const SnippetMain = ({
? syncedDraftPayload.input_fields as SnippetInputField[]
: fields
- currentInputFieldsRef.current = inputFields
setLocalDraftState({
payload: {
...draftPayload,
@@ -297,8 +299,8 @@ const SnippetMain = ({
edges: initialEdges(draftGraph.edges, draftGraph.nodes),
viewport: draftGraph.viewport,
})
- setFields(inputFields)
- }, [draftPayload, fields, setFields])
+ setInputFields(inputFields)
+ }, [draftPayload, fields, setInputFields])
const hooksStore = useMemo(() => {
return {
diff --git a/web/app/components/snippets/draft-store/__tests__/index.spec.ts b/web/app/components/snippets/draft-store/__tests__/index.spec.ts
new file mode 100644
index 00000000000..d50067f2033
--- /dev/null
+++ b/web/app/components/snippets/draft-store/__tests__/index.spec.ts
@@ -0,0 +1,36 @@
+import type { SnippetInputField } from '@/models/snippet'
+import { PipelineInputVarType } from '@/models/pipeline'
+import { useSnippetDraftStore } from '..'
+
+const createField = (variable: string): SnippetInputField => ({
+ label: variable,
+ variable,
+ type: PipelineInputVarType.textInput,
+ required: true,
+})
+
+describe('useSnippetDraftStore', () => {
+ beforeEach(() => {
+ useSnippetDraftStore.getState().reset()
+ })
+
+ it('should store and reset snippet input fields', () => {
+ const inputFields = [
+ createField('topic'),
+ createField('audience'),
+ ]
+
+ useSnippetDraftStore.getState().hydrateDraft({
+ snippetId: 'snippet-1',
+ inputFields,
+ })
+
+ expect(useSnippetDraftStore.getState().snippetId).toBe('snippet-1')
+ expect(useSnippetDraftStore.getState().inputFields).toEqual(inputFields)
+
+ useSnippetDraftStore.getState().reset()
+
+ expect(useSnippetDraftStore.getState().snippetId).toBeUndefined()
+ expect(useSnippetDraftStore.getState().inputFields).toEqual([])
+ })
+})
diff --git a/web/app/components/snippets/draft-store/index.ts b/web/app/components/snippets/draft-store/index.ts
new file mode 100644
index 00000000000..2cc9081b016
--- /dev/null
+++ b/web/app/components/snippets/draft-store/index.ts
@@ -0,0 +1,24 @@
+'use client'
+
+import type { SnippetInputField } from '@/models/snippet'
+import { create } from 'zustand'
+
+type SnippetDraftState = {
+ snippetId?: string
+ inputFields: SnippetInputField[]
+ hydrateDraft: (payload: { snippetId: string, inputFields: SnippetInputField[] }) => void
+ setInputFields: (inputFields: SnippetInputField[]) => void
+ reset: () => void
+}
+
+const initialState = {
+ snippetId: undefined,
+ inputFields: [] as SnippetInputField[],
+}
+
+export const useSnippetDraftStore = create(set => ({
+ ...initialState,
+ hydrateDraft: ({ snippetId, inputFields }) => set({ snippetId, inputFields }),
+ setInputFields: inputFields => set({ inputFields }),
+ reset: () => set(initialState),
+}))
diff --git a/web/app/components/snippets/hooks/__tests__/use-nodes-sync-draft.spec.ts b/web/app/components/snippets/hooks/__tests__/use-nodes-sync-draft.spec.ts
index df75ec1714b..dc955e7f6cb 100644
--- a/web/app/components/snippets/hooks/__tests__/use-nodes-sync-draft.spec.ts
+++ b/web/app/components/snippets/hooks/__tests__/use-nodes-sync-draft.spec.ts
@@ -1,7 +1,7 @@
import type { SnippetInputField } from '@/models/snippet'
import { act, renderHook } from '@testing-library/react'
import { PipelineInputVarType } from '@/models/pipeline'
-import { useSnippetDetailStore } from '../../store'
+import { useSnippetDraftStore } from '../../draft-store'
import { useNodesSyncDraft } from '../use-nodes-sync-draft'
const mockGetNodes = vi.fn()
@@ -112,9 +112,7 @@ describe('snippet/use-nodes-sync-draft', () => {
mockSetSyncWorkflowDraftHash.mockImplementation((hash: string) => {
workflowStoreState.syncWorkflowDraftHash = hash
})
- useSnippetDetailStore.setState({
- fields: [createInputField('topic')],
- })
+ useSnippetDraftStore.getState().setInputFields([createInputField('topic')])
})
it('should include current input_fields when syncing the draft graph', async () => {
@@ -139,25 +137,16 @@ describe('snippet/use-nodes-sync-draft', () => {
expect(mockUseNodesReadOnlyByCanEdit).toHaveBeenCalledWith(true)
})
- it('should use provided input_fields when the snippet store is not initialized yet', async () => {
- useSnippetDetailStore.setState({
- fields: [],
+ it('should keep draft input_fields when the navigation store is reset during route leave', () => {
+ const { result } = renderHook(() => useNodesSyncDraft('snippet-1'))
+
+ act(() => {
+ result.current.syncWorkflowDraftWhenPageClose()
})
- const inputFields = [createInputField('topic')]
- const { result } = renderHook(() => useNodesSyncDraft('snippet-1', {
- getInputFields: () => inputFields,
+
+ expect(mockPostWithKeepalive).toHaveBeenCalledWith('/api/snippets/snippet-1/workflows/draft', expect.objectContaining({
+ input_fields: [createInputField('topic')],
}))
-
- await act(async () => {
- await result.current.doSyncWorkflowDraft()
- })
-
- expect(mockSyncDraftWorkflow).toHaveBeenCalledWith({
- params: { snippetId: 'snippet-1' },
- body: expect.objectContaining({
- input_fields: inputFields,
- }),
- })
})
it('should snapshot graph before queued draft sync executes', async () => {
@@ -267,22 +256,4 @@ describe('snippet/use-nodes-sync-draft', () => {
hash: 'draft-hash',
})
})
-
- it('should use provided input_fields when syncing on page close before the snippet store initializes', () => {
- useSnippetDetailStore.setState({
- fields: [],
- })
- const inputFields = [createInputField('topic')]
- const { result } = renderHook(() => useNodesSyncDraft('snippet-1', {
- getInputFields: () => inputFields,
- }))
-
- act(() => {
- result.current.syncWorkflowDraftWhenPageClose()
- })
-
- expect(mockPostWithKeepalive).toHaveBeenCalledWith('/api/snippets/snippet-1/workflows/draft', expect.objectContaining({
- input_fields: inputFields,
- }))
- })
})
diff --git a/web/app/components/snippets/hooks/__tests__/use-snippet-refresh-draft.spec.ts b/web/app/components/snippets/hooks/__tests__/use-snippet-refresh-draft.spec.ts
index c85bfe3bc5d..eb500839efb 100644
--- a/web/app/components/snippets/hooks/__tests__/use-snippet-refresh-draft.spec.ts
+++ b/web/app/components/snippets/hooks/__tests__/use-snippet-refresh-draft.spec.ts
@@ -31,8 +31,8 @@ vi.mock('@/app/components/workflow/store', () => ({
}),
}))
-vi.mock('../../store', () => ({
- useSnippetDetailStore: {
+vi.mock('../../draft-store', () => ({
+ useSnippetDraftStore: {
setState: (...args: unknown[]) => mockSnippetSetState(...args),
},
}))
@@ -75,7 +75,7 @@ describe('useSnippetRefreshDraft', () => {
})
expect(mockFetchSnippetDraftWorkflow).toHaveBeenCalledWith('snippet-1')
expect(mockSnippetSetState).toHaveBeenCalledWith({
- fields: [],
+ inputFields: [],
})
expect(mockSetSyncWorkflowDraftHash).toHaveBeenCalledWith('draft-hash')
expect(mockSetDraftUpdatedAt).toHaveBeenCalledWith(1_712_345_678)
diff --git a/web/app/components/snippets/hooks/__tests__/use-snippet-start-run.spec.ts b/web/app/components/snippets/hooks/__tests__/use-snippet-start-run.spec.ts
index 58edaccbbfb..cc8cbb9f536 100644
--- a/web/app/components/snippets/hooks/__tests__/use-snippet-start-run.spec.ts
+++ b/web/app/components/snippets/hooks/__tests__/use-snippet-start-run.spec.ts
@@ -4,7 +4,7 @@ import { act } from 'react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
import { PipelineInputVarType } from '@/models/pipeline'
-import { useSnippetDetailStore } from '../../store'
+import { useSnippetDraftStore } from '../../draft-store'
import { useSnippetStartRun } from '../use-snippet-start-run'
const mockWorkflowStoreGetState = vi.fn()
@@ -39,7 +39,7 @@ const inputFields: SnippetInputField[] = [
describe('useSnippetStartRun', () => {
beforeEach(() => {
vi.clearAllMocks()
- useSnippetDetailStore.getState().reset()
+ useSnippetDraftStore.getState().reset()
mockWorkflowStoreGetState.mockReturnValue({
workflowRunningData: undefined,
showDebugAndPreviewPanel: false,
@@ -51,7 +51,7 @@ describe('useSnippetStartRun', () => {
})
it('should open the debug panel and input form when snippet has input fields', () => {
- useSnippetDetailStore.setState({ fields: inputFields })
+ useSnippetDraftStore.getState().setInputFields(inputFields)
const { result } = renderHook(() => useSnippetStartRun({
handleRun: mockHandleRun,
@@ -83,7 +83,7 @@ describe('useSnippetStartRun', () => {
})
it('should use current snippet input fields from the store before starting a run', () => {
- useSnippetDetailStore.setState({ fields: inputFields })
+ useSnippetDraftStore.getState().setInputFields(inputFields)
const { result } = renderHook(() => useSnippetStartRun({
handleRun: mockHandleRun,
@@ -99,7 +99,7 @@ describe('useSnippetStartRun', () => {
})
it('should close the panel when debug panel is already open', () => {
- useSnippetDetailStore.setState({ fields: inputFields })
+ useSnippetDraftStore.getState().setInputFields(inputFields)
mockWorkflowStoreGetState.mockReturnValue({
workflowRunningData: undefined,
@@ -122,7 +122,7 @@ describe('useSnippetStartRun', () => {
})
it('should do nothing when workflow is already running', () => {
- useSnippetDetailStore.setState({ fields: inputFields })
+ useSnippetDraftStore.getState().setInputFields(inputFields)
mockWorkflowStoreGetState.mockReturnValue({
workflowRunningData: {
diff --git a/web/app/components/snippets/hooks/use-nodes-sync-draft.ts b/web/app/components/snippets/hooks/use-nodes-sync-draft.ts
index 8550a5200c2..3cdab0254b2 100644
--- a/web/app/components/snippets/hooks/use-nodes-sync-draft.ts
+++ b/web/app/components/snippets/hooks/use-nodes-sync-draft.ts
@@ -10,7 +10,7 @@ import { API_PREFIX } from '@/config'
import { consoleClient } from '@/service/client'
// eslint-disable-next-line no-restricted-imports
import { postWithKeepalive } from '@/service/fetch'
-import { useSnippetDetailStore } from '../store'
+import { useSnippetDraftStore } from '../draft-store'
import { useSnippetRefreshDraft } from './use-snippet-refresh-draft'
const isSyncConflictError = (error: unknown): error is { bodyUsed: boolean, json: () => Promise<{ code?: string }> } => {
@@ -25,10 +25,6 @@ type SyncInputFieldsDraftCallback = SyncDraftCallback & {
onRefresh?: (inputFields: SnippetInputField[]) => void
}
-type UseNodesSyncDraftOptions = {
- getInputFields?: () => SnippetInputField[]
-}
-
const snippetDraftSyncQueues = new Map>()
const enqueueSnippetDraftSync = (
@@ -48,18 +44,17 @@ const enqueueSnippetDraftSync = (
return nextTask
}
-export const useNodesSyncDraft = (snippetId: string, options: UseNodesSyncDraftOptions = {}) => {
+export const useNodesSyncDraft = (snippetId: string) => {
const store = useStoreApi()
const workflowStore = useWorkflowStore()
const { getNodesReadOnly } = useNodesReadOnlyByCanEdit(true)
const { handleRefreshWorkflowDraft } = useSnippetRefreshDraft(snippetId)
- const { getInputFields } = options
const getInputFieldsSyncPayload = useCallback((inputFields?: SnippetInputField[]) => {
return {
- input_fields: inputFields ?? getInputFields?.() ?? useSnippetDetailStore.getState().fields,
+ input_fields: inputFields ?? useSnippetDraftStore.getState().inputFields,
}
- }, [getInputFields])
+ }, [])
const getDraftSyncPayload = useCallback((inputFields?: SnippetInputField[]) => {
const {
diff --git a/web/app/components/snippets/hooks/use-snippet-refresh-draft.ts b/web/app/components/snippets/hooks/use-snippet-refresh-draft.ts
index 12174e731ea..e310c919fba 100644
--- a/web/app/components/snippets/hooks/use-snippet-refresh-draft.ts
+++ b/web/app/components/snippets/hooks/use-snippet-refresh-draft.ts
@@ -5,7 +5,7 @@ import { useCallback } from 'react'
import { useWorkflowUpdate } from '@/app/components/workflow/hooks'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { fetchSnippetDraftWorkflow } from '@/service/use-snippet-workflows'
-import { useSnippetDetailStore } from '../store'
+import { useSnippetDraftStore } from '../draft-store'
export const useSnippetRefreshDraft = (snippetId: string) => {
const workflowStore = useWorkflowStore()
@@ -36,8 +36,8 @@ export const useSnippetRefreshDraft = (snippetId: string) => {
edges: response.graph?.edges || [],
viewport: response.graph?.viewport || { x: 0, y: 0, zoom: 1 },
} as WorkflowDataUpdater)
- useSnippetDetailStore.setState({
- fields: inputFields,
+ useSnippetDraftStore.setState({
+ inputFields,
})
setSyncWorkflowDraftHash(response.hash)
setDraftUpdatedAt(response.updated_at)
diff --git a/web/app/components/snippets/hooks/use-snippet-start-run.ts b/web/app/components/snippets/hooks/use-snippet-start-run.ts
index 9657391bf2a..3e7ec5f4ac2 100644
--- a/web/app/components/snippets/hooks/use-snippet-start-run.ts
+++ b/web/app/components/snippets/hooks/use-snippet-start-run.ts
@@ -3,7 +3,7 @@ import { useCallback } from 'react'
import { useWorkflowInteractions } from '@/app/components/workflow/hooks'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
-import { useSnippetDetailStore } from '../store'
+import { useSnippetDraftStore } from '../draft-store'
type UseSnippetStartRunOptions = {
handleRun: (params: SnippetDraftRunPayload) => void
@@ -38,7 +38,7 @@ export const useSnippetStartRun = ({
setShowDebugAndPreviewPanel(true)
- const currentInputFields = useSnippetDetailStore.getState().fields
+ const currentInputFields = useSnippetDraftStore.getState().inputFields
if (currentInputFields.length > 0) {
setShowInputsPanel(true)
diff --git a/web/app/components/snippets/store/__tests__/index.spec.ts b/web/app/components/snippets/store/__tests__/index.spec.ts
index 06005c97a57..aa7611bae2a 100644
--- a/web/app/components/snippets/store/__tests__/index.spec.ts
+++ b/web/app/components/snippets/store/__tests__/index.spec.ts
@@ -1,14 +1,6 @@
-import type { SnippetDetail, SnippetInputField } from '@/models/snippet'
-import { PipelineInputVarType } from '@/models/pipeline'
+import type { SnippetDetail } from '@/models/snippet'
import { useSnippetDetailStore } from '..'
-const createField = (variable: string): SnippetInputField => ({
- label: variable,
- variable,
- type: PipelineInputVarType.textInput,
- required: true,
-})
-
const snippet: SnippetDetail = {
id: 'snippet-1',
name: 'Snippet',
@@ -23,21 +15,6 @@ describe('useSnippetDetailStore', () => {
useSnippetDetailStore.getState().reset()
})
- it('should store and reset snippet input fields', () => {
- const fields = [
- createField('topic'),
- createField('audience'),
- ]
-
- useSnippetDetailStore.getState().setFields(fields)
-
- expect(useSnippetDetailStore.getState().fields).toEqual(fields)
-
- useSnippetDetailStore.getState().reset()
-
- expect(useSnippetDetailStore.getState().fields).toEqual([])
- })
-
it('should store and reset snippet navigation state', () => {
const onFieldsChange = vi.fn()
@@ -58,7 +35,6 @@ describe('useSnippetDetailStore', () => {
useSnippetDetailStore.getState().reset()
expect(useSnippetDetailStore.getState()).toMatchObject({
- fields: [],
readonly: true,
snippet: undefined,
snippetId: undefined,
diff --git a/web/app/components/snippets/store/index.ts b/web/app/components/snippets/store/index.ts
index ee4006f6d7a..09ec732aae1 100644
--- a/web/app/components/snippets/store/index.ts
+++ b/web/app/components/snippets/store/index.ts
@@ -11,14 +11,11 @@ type SnippetNavigationState = {
}
type SnippetDetailUIState = {
- fields: SnippetInputField[]
- setFields: (fields: SnippetInputField[]) => void
setNavigationState: (state: SnippetNavigationState) => void
reset: () => void
} & SnippetNavigationState
const initialState = {
- fields: [] as SnippetInputField[],
readonly: true,
snippet: undefined,
snippetId: undefined,
@@ -27,7 +24,6 @@ const initialState = {
export const useSnippetDetailStore = create(set => ({
...initialState,
- setFields: fields => set({ fields }),
setNavigationState: state => set(state),
reset: () => set(initialState),
}))