diff --git a/web/app/components/apps/__tests__/list.spec.tsx b/web/app/components/apps/__tests__/list.spec.tsx
index f5ac48c3a46..47f328d50d6 100644
--- a/web/app/components/apps/__tests__/list.spec.tsx
+++ b/web/app/components/apps/__tests__/list.spec.tsx
@@ -421,18 +421,16 @@ describe('List', () => {
expect(screen.getByRole('button', { name: 'common.operation.create' }))!.toBeInTheDocument()
})
- it('should render sort filter before search and the snippets link', () => {
+ it('should render sort filter before search and hide the snippets link', () => {
renderList()
const sortButton = screen.getByRole('button', { name: 'Sort by Last modified' })
const searchInput = screen.getByRole('searchbox', { name: 'app.gotoAnything.actions.searchApplications' })
- const snippetsLink = screen.getByRole('link', { name: 'app.studio.viewSnippets' })
const createButton = screen.getByRole('button', { name: 'common.operation.create' })
- expect(snippetsLink).toHaveAttribute('href', '/snippets')
expect(sortButton.compareDocumentPosition(searchInput) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy()
- expect(searchInput.compareDocumentPosition(snippetsLink) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy()
- expect(snippetsLink.compareDocumentPosition(createButton) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy()
+ expect(searchInput.compareDocumentPosition(createButton) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy()
+ expect(screen.queryByRole('link', { name: 'app.studio.viewSnippets' })).not.toBeInTheDocument()
})
it('should render app cards when apps exist', () => {
diff --git a/web/app/components/apps/app-list-header-filters.tsx b/web/app/components/apps/app-list-header-filters.tsx
index df32f57d7ba..ceccb7575f6 100644
--- a/web/app/components/apps/app-list-header-filters.tsx
+++ b/web/app/components/apps/app-list-header-filters.tsx
@@ -8,7 +8,6 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge
import { useTranslation } from 'react-i18next'
import { SearchInput } from '@/app/components/base/search-input'
import { TagFilter } from '@/features/tag-management/components/tag-filter'
-import Link from '@/next/link'
import { AppSortFilter } from './app-sort-filter'
import { AppTypeFilter } from './app-type-filter'
import CreatorsFilter from './creators-filter'
@@ -71,12 +70,6 @@ export function AppListHeaderFilters({
/>
-
- {t('studio.viewSnippets', { ns: 'app' })}
-
{showCreateButton && (
({
- value: ['snippets.create_and_modify'] as string[],
-}))
-
-vi.mock('@/context/app-context', () => ({
- useSelector: (selector: (state: { workspacePermissionKeys: string[] }) => T): T => selector({
- workspacePermissionKeys: mockWorkspacePermissionKeys.value,
- }),
-}))
-
-vi.mock('@/app/components/snippets/hooks/use-create-snippet', async () => {
- const React = await vi.importActual('react')
-
- return {
- useCreateSnippet: () => {
- const [isOpen, setIsOpen] = React.useState(false)
-
- return {
- createSnippetMutation: { isPending: false },
- handleCloseCreateSnippetDialog: () => setIsOpen(false),
- handleCreateSnippet: mockHandleCreateSnippet,
- handleOpenCreateSnippetDialog: () => setIsOpen(true),
- isCreateSnippetDialogOpen: isOpen,
- isCreatingSnippet: false,
- }
- },
- }
-})
-
-vi.mock('@/app/components/snippets/create-snippet-dialog', () => ({
- default: (props: {
- isOpen: boolean
- selectedGraph?: { nodes: Node[], edges: Edge[], viewport: { x: number, y: number, zoom: number } }
- inputFields?: Array<{ variable: string }>
- }) => {
- mockCreateSnippetDialogRender(props)
-
- return props.isOpen ? : null
- },
-}))
vi.mock('../hooks', async () => {
const actual = await vi.importActual('../hooks')
@@ -142,9 +98,6 @@ describe('SelectionContextmenu', () => {
mockHandleNodesCopy.mockReset()
mockHandleNodesDuplicate.mockReset()
mockHandleNodesDelete.mockReset()
- mockHandleCreateSnippet.mockReset()
- mockCreateSnippetDialogRender.mockReset()
- mockWorkspacePermissionKeys.value = ['snippets.create_and_modify']
})
it('should not render when selection context menu target is absent', () => {
@@ -203,41 +156,7 @@ describe('SelectionContextmenu', () => {
expect(store.getState().contextMenuTarget).toBeUndefined()
})
- it('should open create snippet dialog with selected graph from the top menu item', async () => {
- const nodes = [
- createNode({ id: 'n1', selected: true, width: 80, height: 40 }),
- createNode({ id: 'n2', selected: true, position: { x: 140, y: 0 }, width: 80, height: 40 }),
- createNode({ id: 'n3', selected: false, position: { x: 260, y: 0 }, width: 80, height: 40 }),
- ]
- const edges = [
- createEdge({ source: 'n1', target: 'n2' }),
- createEdge({ source: 'n2', target: 'n3' }),
- ]
- const { store } = renderSelectionMenu({ nodes, edges })
-
- act(() => {
- store.setState({ contextMenuTarget: { type: 'selection' } })
- })
-
- fireEvent.click(await screen.findByRole('menuitem', { name: /Create Snippet|snippet\.createDialogTitle/ }))
-
- expect(screen.getByTestId('create-snippet-dialog')).toBeInTheDocument()
- expect(store.getState().contextMenuTarget).toBeUndefined()
-
- const dialogProps = mockCreateSnippetDialogRender.mock.calls.at(-1)?.[0]
- expect(dialogProps.selectedGraph.nodes.map((node: Node) => node.id)).toEqual(['n1', 'n2'])
- expect(dialogProps.selectedGraph.nodes.every((node: Node) => node.selected === false)).toBe(true)
- expect(dialogProps.selectedGraph.edges).toHaveLength(1)
- expect(dialogProps.selectedGraph.viewport).toEqual({ x: 490, y: 380, zoom: 1 })
- expect(dialogProps.selectedGraph.edges[0]).toEqual(expect.objectContaining({
- source: 'n1',
- target: 'n2',
- selected: false,
- }))
- })
-
- it('should hide create snippet action without snippets create-and-modify permission', async () => {
- mockWorkspacePermissionKeys.value = []
+ it('should hide create snippet action for selected nodes', async () => {
const nodes = [
createNode({ id: 'n1', selected: true, width: 80, height: 40 }),
createNode({ id: 'n2', selected: true, position: { x: 140, y: 0 }, width: 80, height: 40 }),
@@ -252,76 +171,7 @@ describe('SelectionContextmenu', () => {
expect(screen.getByRole('menuitem', { name: /common.copy/ })).toBeInTheDocument()
})
expect(screen.queryByRole('menuitem', { name: /Create Snippet|snippet\.createDialogTitle/ })).not.toBeInTheDocument()
- })
-
- it('should add input fields for variable references outside of the selected graph', async () => {
- const nodes = [
- createNode({
- id: 'n1',
- selected: true,
- width: 80,
- height: 40,
- data: {
- prompt_template: 'Use {{#source-node.topic#}} and {{#n2.answer#}}',
- query_variable_selector: ['source-node', 'topic'],
- env_reference: '{{#env.API_KEY#}}',
- },
- }),
- createNode({
- id: 'n2',
- selected: true,
- position: { x: 140, y: 0 },
- width: 80,
- height: 40,
- }),
- ]
- const { store } = renderSelectionMenu({ nodes })
-
- act(() => {
- store.setState({ contextMenuTarget: { type: 'selection' } })
- })
-
- fireEvent.click(await screen.findByRole('menuitem', { name: /Create Snippet|snippet\.createDialogTitle/ }))
-
- const dialogProps = mockCreateSnippetDialogRender.mock.calls.at(-1)?.[0]
- expect(dialogProps.inputFields).toEqual([
- {
- label: 'topic',
- variable: 'topic',
- type: PipelineInputVarType.textInput,
- required: true,
- },
- {
- label: 'API_KEY',
- variable: 'API_KEY',
- type: PipelineInputVarType.textInput,
- required: true,
- },
- ])
- expect(dialogProps.selectedGraph.nodes[0].data.prompt_template).toBe('Use {{#start.topic#}} and {{#n2.answer#}}')
- expect(dialogProps.selectedGraph.nodes[0].data.query_variable_selector).toEqual(['start', 'topic'])
- expect(dialogProps.selectedGraph.nodes[0].data.env_reference).toBe('{{#start.API_KEY#}}')
- })
-
- it.each([
- BlockEnum.Answer,
- BlockEnum.End,
- BlockEnum.Start,
- ])('should hide create snippet when selection contains %s node', async (nodeType) => {
- const nodes = [
- createNode({ id: 'n1', selected: true, width: 80, height: 40, data: { type: nodeType } }),
- createNode({ id: 'n2', selected: true, position: { x: 140, y: 0 }, width: 80, height: 40 }),
- ]
- const { store } = renderSelectionMenu({ nodes })
-
- act(() => {
- store.setState({ contextMenuTarget: { type: 'selection' } })
- })
-
- await waitFor(() => {
- expect(screen.getByRole('menuitem', { name: /common.copy/ })).toBeInTheDocument()
- })
- expect(screen.queryByRole('menuitem', { name: /Create Snippet|snippet\.createDialogTitle/ })).not.toBeInTheDocument()
+ expect(screen.queryByTestId('create-snippet-dialog')).not.toBeInTheDocument()
})
it('should stay hidden when only one node is selected', async () => {
diff --git a/web/app/components/workflow/block-selector/__tests__/main.spec.tsx b/web/app/components/workflow/block-selector/__tests__/main.spec.tsx
index aaf26eedc56..fb56beda545 100644
--- a/web/app/components/workflow/block-selector/__tests__/main.spec.tsx
+++ b/web/app/components/workflow/block-selector/__tests__/main.spec.tsx
@@ -106,6 +106,7 @@ describe('NodeSelector', () => {
await user.click(trigger)
const searchInput = screen.getByPlaceholderText('workflow.tabs.searchBlock')
+ expect(screen.queryByText('workflow.tabs.snippets')).not.toBeInTheDocument()
expect(screen.getByText('LLM')).toBeInTheDocument()
expect(screen.getByText('End')).toBeInTheDocument()
diff --git a/web/app/components/workflow/block-selector/main.tsx b/web/app/components/workflow/block-selector/main.tsx
index 595426a262b..6678f081fe9 100644
--- a/web/app/components/workflow/block-selector/main.tsx
+++ b/web/app/components/workflow/block-selector/main.tsx
@@ -132,7 +132,7 @@ function NodeSelector({
const defaultAllowUserInputSelection = !hasUserInputNode && !hasTriggerNode
const canSelectUserInput = allowUserInputSelection ?? defaultAllowUserInputSelection
const disableStartTab = flowType === FlowType.snippet
- const disableSnippetsTab = flowType === FlowType.snippet
+ const disableSnippetsTab = true
const {
activeTab,
resetActiveTab,
diff --git a/web/app/components/workflow/selection-contextmenu.tsx b/web/app/components/workflow/selection-contextmenu.tsx
index dffbd31a5e1..0478b0e12f5 100644
--- a/web/app/components/workflow/selection-contextmenu.tsx
+++ b/web/app/components/workflow/selection-contextmenu.tsx
@@ -12,15 +12,11 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
import { useStore as useReactFlowStore } from 'reactflow'
-import { useCreateSnippetFromSelection } from '@/app/components/snippets/hooks/use-create-snippet-from-selection'
-import { canCreateAndModifySnippets } from '@/app/components/snippets/utils/permission'
import { useCollaborativeWorkflow } from '@/app/components/workflow/hooks/use-collaborative-workflow'
-import { useSelector as useAppContextWithSelector } from '@/context/app-context'
import { useNodesInteractions, useNodesReadOnly, useNodesSyncDraft } from './hooks'
import { useWorkflowHistory, WorkflowHistoryEvent } from './hooks/use-workflow-history'
import { ShortcutKbd } from './shortcuts/shortcut-kbd'
import { useStore, useWorkflowStore } from './store'
-import { BlockEnum } from './types'
const AlignType = {
Bottom: 'bottom',
@@ -75,14 +71,6 @@ const menuSections: MenuSection[] = [
},
]
-const unsupportedSnippetNodeTypes = new Set([
- BlockEnum.Answer,
- BlockEnum.End,
- BlockEnum.Start,
- BlockEnum.HumanInput,
- BlockEnum.KnowledgeRetrieval,
-])
-
const getAlignableNodes = (nodes: Node[], selectedNodes: Node[]) => {
const selectedNodeIds = new Set(selectedNodes.map(node => node.id))
const childNodeIds = new Set()
@@ -235,7 +223,6 @@ export function SelectionContextmenu({
}) {
const { t } = useTranslation()
const { getNodesReadOnly } = useNodesReadOnly()
- const workspacePermissionKeys = useAppContextWithSelector(state => state.workspacePermissionKeys)
const { handleNodesCopy, handleNodesDelete, handleNodesDuplicate } = useNodesInteractions()
const isSelectionContextMenu = useStore(s => s.contextMenuTarget?.type === 'selection')
@@ -247,20 +234,8 @@ export function SelectionContextmenu({
const selectedNodes = useReactFlowStore(state =>
state.getNodes().filter(node => node.selected),
)
- const edges = useReactFlowStore(state => state.edges)
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
const { saveStateToHistory } = useWorkflowHistory()
- const {
- createSnippetDialog,
- handleOpenCreateSnippet,
- isCreateSnippetDialogOpen,
- } = useCreateSnippetFromSelection({
- edges,
- selectedNodes,
- onClose,
- })
- const canCreateSnippet = canCreateAndModifySnippets(workspacePermissionKeys)
- && selectedNodes.every(node => !unsupportedSnippetNodeTypes.has(node.data.type))
const handleCopyNodes = useCallback(() => {
handleNodesCopy()
@@ -370,24 +345,11 @@ export function SelectionContextmenu({
}, [collaborativeWorkflow, workflowStore, selectedNodes, getNodesReadOnly, handleSyncWorkflowDraft, saveStateToHistory, onClose])
if (!isSelectionContextMenu || selectedNodes.length <= 1)
- return isCreateSnippetDialogOpen ? createSnippetDialog : null
+ return null
return (
<>
- {canCreateSnippet && (
- <>
-
-
- {t('snippet.createDialogTitle', { defaultValue: 'Create Snippet', ns: 'workflow' })}
-
-
-
- >
- )}
))}
- {createSnippetDialog}
>
)
}