diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/components/chunk-context-menu/chunk-context-menu.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/components/chunk-context-menu/chunk-context-menu.tsx index b4d2b664bee..4586b3306fd 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/components/chunk-context-menu/chunk-context-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/components/chunk-context-menu/chunk-context-menu.tsx @@ -95,7 +95,12 @@ export function ChunkContextMenu({ } return ( - + !open && onClose()} + variant='secondary' + size='sm' + > + !open && onClose()} + variant='secondary' + size='sm' + > + !open && onClose()} + variant='secondary' + size='sm' + > + !open && onClose()} + variant='secondary' + size='sm' + > !open && onClose()} variant='secondary' size='sm' colorScheme='inverted' diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/log-row-context-menu/log-row-context-menu.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/log-row-context-menu/log-row-context-menu.tsx index 56c8cdab005..41deea199ed 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/log-row-context-menu/log-row-context-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/log-row-context-menu/log-row-context-menu.tsx @@ -47,7 +47,12 @@ export function LogRowContextMenu({ const hasWorkflow = Boolean(log?.workflow?.id || log?.workflowId) return ( - + !open && onClose()} + variant='secondary' + size='sm' + > !open && onClose()} variant='secondary' size='sm' colorScheme='inverted' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/context-menu/pane-context-menu.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/context-menu/pane-context-menu.tsx index 96b36e3bb0e..77101d1dc60 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/context-menu/pane-context-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/context-menu/pane-context-menu.tsx @@ -38,7 +38,7 @@ export function PaneContextMenu({ return ( !open && onClose()} variant='secondary' size='sm' colorScheme='inverted' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx index ac23aa5df77..bc14069563d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/tool-input.tsx @@ -673,7 +673,7 @@ function WorkflowInputMapperSyncWrapper({ if (!workflowId) { return ( -
+
Select a workflow to configure its inputs
) @@ -681,15 +681,15 @@ function WorkflowInputMapperSyncWrapper({ if (isLoading) { return ( -
- +
+
) } if (inputFields.length === 0) { return ( -
+
This workflow has no custom input fields
) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx index 1392cfdafd6..60b7a7b13f3 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/editor.tsx @@ -1,7 +1,7 @@ 'use client' import { useCallback, useEffect, useRef, useState } from 'react' -import { BookOpen, Check, ChevronUp, Pencil, RepeatIcon, Settings, SplitIcon } from 'lucide-react' +import { BookOpen, Check, ChevronUp, Pencil, Settings } from 'lucide-react' import { Button, Tooltip } from '@/components/emcn' import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' import { @@ -15,6 +15,8 @@ import { useEditorBlockProperties, useEditorSubblockLayout, } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/hooks' +import { LoopTool } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/loop/loop-config' +import { ParallelTool } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/parallel/parallel-config' import { getSubBlockStableKey } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/utils' import { useCurrentWorkflow } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks' import { getBlock } from '@/blocks/registry' @@ -58,9 +60,8 @@ export function Editor() { const isSubflow = currentBlock && (currentBlock.type === 'loop' || currentBlock.type === 'parallel') - // Get subflow display properties - const subflowIcon = isSubflow && currentBlock.type === 'loop' ? RepeatIcon : SplitIcon - const subflowBgColor = isSubflow && currentBlock.type === 'loop' ? '#2FB3FF' : '#FEE12B' + // Get subflow display properties from configs + const subflowConfig = isSubflow ? (currentBlock.type === 'loop' ? LoopTool : ParallelTool) : null // Refs for resize functionality const subBlocksRef = useRef(null) @@ -176,8 +177,9 @@ export function Editor() { * Handles opening documentation link in a new secure tab. */ const handleOpenDocs = () => { - if (blockConfig?.docsLink) { - window.open(blockConfig.docsLink, '_blank', 'noopener,noreferrer') + const docsLink = isSubflow ? subflowConfig?.docsLink : blockConfig?.docsLink + if (docsLink) { + window.open(docsLink, '_blank', 'noopener,noreferrer') } } @@ -195,10 +197,10 @@ export function Editor() { {(blockConfig || isSubflow) && currentBlock?.type !== 'note' && (
@@ -295,7 +297,7 @@ export function Editor() { )} - {currentBlock && !isSubflow && blockConfig?.docsLink && ( + {currentBlock && (isSubflow ? subflowConfig?.docsLink : blockConfig?.docsLink) && (
+ + {/* Toolbar Item Context Menu */} + ) }) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/loop/loop-config.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/loop/loop-config.ts index 393d5f73f5e..0e8395d5b85 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/loop/loop-config.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/loop/loop-config.ts @@ -9,4 +9,5 @@ export const LoopTool = { name: 'Loop', icon: RepeatIcon, bgColor: '#2FB3FF', + docsLink: 'https://docs.sim.ai/blocks/loop', } as const diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/parallel/parallel-config.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/parallel/parallel-config.ts index 52c6af69c81..758dd084ea7 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/parallel/parallel-config.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/parallel/parallel-config.ts @@ -9,4 +9,5 @@ export const ParallelTool = { name: 'Parallel', icon: SplitIcon, bgColor: '#FEE12B', + docsLink: 'https://docs.sim.ai/blocks/parallel', } as const diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/log-row-context-menu.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/log-row-context-menu.tsx index 06c654dcf14..009b24b8fbb 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/log-row-context-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/log-row-context-menu.tsx @@ -66,7 +66,7 @@ export function LogRowContextMenu({ return ( !open && onClose()} variant='secondary' size='sm' colorScheme='inverted' diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/output-context-menu.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/output-context-menu.tsx index 8746a5bc39d..2cb59f9f9a6 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/output-context-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/terminal/components/output-context-menu.tsx @@ -52,7 +52,7 @@ export function OutputContextMenu({ return ( !open && onClose()} variant='secondary' size='sm' colorScheme='inverted' diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/index.ts index 188d882e900..ef1aa6391c1 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/index.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/index.ts @@ -1,4 +1,5 @@ export { HelpModal } from './help-modal/help-modal' +export { NavItemContextMenu } from './nav-item-context-menu' export { SearchModal } from './search-modal/search-modal' export { SettingsModal } from './settings-modal/settings-modal' export { UsageIndicator } from './usage-indicator/usage-indicator' diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/nav-item-context-menu/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/nav-item-context-menu/index.ts new file mode 100644 index 00000000000..f64d3d54517 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/nav-item-context-menu/index.ts @@ -0,0 +1 @@ +export { NavItemContextMenu } from './nav-item-context-menu' diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/nav-item-context-menu/nav-item-context-menu.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/nav-item-context-menu/nav-item-context-menu.tsx new file mode 100644 index 00000000000..060f76d6c10 --- /dev/null +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/nav-item-context-menu/nav-item-context-menu.tsx @@ -0,0 +1,81 @@ +'use client' + +import { Popover, PopoverAnchor, PopoverContent, PopoverItem } from '@/components/emcn' + +interface NavItemContextMenuProps { + /** + * Whether the context menu is open + */ + isOpen: boolean + /** + * Position of the context menu + */ + position: { x: number; y: number } + /** + * Ref for the menu element + */ + menuRef: React.RefObject + /** + * Callback when menu should close + */ + onClose: () => void + /** + * Callback when open in new tab is clicked + */ + onOpenInNewTab: () => void + /** + * Callback when copy link is clicked + */ + onCopyLink: () => void +} + +/** + * Context menu component for sidebar navigation items. + * Displays navigation-appropriate options (open in new tab, copy link) in a popover at the right-click position. + */ +export function NavItemContextMenu({ + isOpen, + position, + menuRef, + onClose, + onOpenInNewTab, + onCopyLink, +}: NavItemContextMenuProps) { + return ( + !open && onClose()} + variant='secondary' + size='sm' + colorScheme='inverted' + > + + + { + onOpenInNewTab() + onClose() + }} + > + Open in new tab + + { + onCopyLink() + onClose() + }} + > + Copy link + + + + ) +} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu.tsx index e14c1d59974..078801141f0 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu.tsx @@ -150,7 +150,7 @@ export function ContextMenu({ return ( !open && onClose()} variant='secondary' size='sm' colorScheme='inverted' diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx index 13fd4aa42fb..a7f23db0cfe 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx @@ -13,6 +13,7 @@ import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/provide import { createCommands } from '@/app/workspace/[workspaceId]/utils/commands-utils' import { HelpModal, + NavItemContextMenu, SearchModal, SettingsModal, UsageIndicator, @@ -20,6 +21,7 @@ import { WorkspaceHeader, } from '@/app/workspace/[workspaceId]/w/components/sidebar/components' import { + useContextMenu, useFolderOperations, useSidebarResize, useWorkflowOperations, @@ -168,6 +170,46 @@ export function Sidebar() { workspaceId, }) + /** Context menu state for navigation items */ + const [activeNavItemHref, setActiveNavItemHref] = useState(null) + const { + isOpen: isNavContextMenuOpen, + position: navContextMenuPosition, + menuRef: navMenuRef, + handleContextMenu: handleNavContextMenuBase, + closeMenu: closeNavContextMenu, + } = useContextMenu() + + const handleNavItemContextMenu = useCallback( + (e: React.MouseEvent, href: string) => { + setActiveNavItemHref(href) + handleNavContextMenuBase(e) + }, + [handleNavContextMenuBase] + ) + + const handleNavContextMenuClose = useCallback(() => { + closeNavContextMenu() + setActiveNavItemHref(null) + }, [closeNavContextMenu]) + + const handleNavOpenInNewTab = useCallback(() => { + if (activeNavItemHref) { + window.open(activeNavItemHref, '_blank', 'noopener,noreferrer') + } + }, [activeNavItemHref]) + + const handleNavCopyLink = useCallback(async () => { + if (activeNavItemHref) { + const fullUrl = `${window.location.origin}${activeNavItemHref}` + try { + await navigator.clipboard.writeText(fullUrl) + } catch (error) { + logger.error('Failed to copy link to clipboard', { error }) + } + } + }, [activeNavItemHref]) + const { handleDuplicateWorkspace: duplicateWorkspace } = useDuplicateWorkspace({ getWorkspaceId: () => workspaceId, }) @@ -629,12 +671,23 @@ export function Sidebar() { href={item.href!} data-item-id={item.id} className={`${baseClasses} ${activeClasses}`} + onContextMenu={(e) => handleNavItemContextMenu(e, item.href!)} > {content} ) })} + + {/* Nav Item Context Menu */} + diff --git a/apps/sim/blocks/blocks/discord.ts b/apps/sim/blocks/blocks/discord.ts index 37e0f30f1e3..0d1108a0970 100644 --- a/apps/sim/blocks/blocks/discord.ts +++ b/apps/sim/blocks/blocks/discord.ts @@ -13,6 +13,7 @@ export const DiscordBlock: BlockConfig = { category: 'tools', bgColor: '#5865F2', icon: DiscordIcon, + docsLink: 'https://docs.sim.ai/tools/discord', subBlocks: [ { id: 'operation', diff --git a/apps/sim/blocks/blocks/fireflies.ts b/apps/sim/blocks/blocks/fireflies.ts index 6c4ccb8e371..b0924719028 100644 --- a/apps/sim/blocks/blocks/fireflies.ts +++ b/apps/sim/blocks/blocks/fireflies.ts @@ -12,7 +12,7 @@ export const FirefliesBlock: BlockConfig = { triggerAllowed: true, longDescription: 'Integrate Fireflies.ai into the workflow. Manage meeting transcripts, add bot to live meetings, create soundbites, and more. Can also trigger workflows when transcriptions complete.', - docsLink: 'https://docs.fireflies.ai', + docsLink: 'https://docs.sim.ai/tools/fireflies', category: 'tools', icon: FirefliesIcon, bgColor: '#100730', diff --git a/apps/sim/blocks/blocks/generic_webhook.ts b/apps/sim/blocks/blocks/generic_webhook.ts index 6ae41dbc885..97ed9c8ec43 100644 --- a/apps/sim/blocks/blocks/generic_webhook.ts +++ b/apps/sim/blocks/blocks/generic_webhook.ts @@ -13,6 +13,7 @@ export const GenericWebhookBlock: BlockConfig = { category: 'triggers', icon: WebhookIcon, bgColor: '#10B981', // Green color for triggers + docsLink: 'https://docs.sim.ai/triggers/webhook', triggerAllowed: true, bestPractices: ` - You can test the webhook by sending a request to the webhook URL. E.g. depending on authorization: curl -X POST http://localhost:3000/api/webhooks/trigger/d8abcf0d-1ee5-4b77-bb07-b1e8142ea4e9 -H "Content-Type: application/json" -H "X-Sim-Secret: 1234" -d '{"message": "Test webhook trigger", "data": {"key": "v"}}' diff --git a/apps/sim/blocks/blocks/grain.ts b/apps/sim/blocks/blocks/grain.ts index 54fe53f1206..86fdbf544e3 100644 --- a/apps/sim/blocks/blocks/grain.ts +++ b/apps/sim/blocks/blocks/grain.ts @@ -13,6 +13,7 @@ export const GrainBlock: BlockConfig = { longDescription: 'Integrate Grain into your workflow. Access meeting recordings, transcripts, highlights, and AI-generated summaries. Can also trigger workflows based on Grain webhook events.', category: 'tools', + docsLink: 'https://docs.sim.ai/tools/grain', icon: GrainIcon, bgColor: '#F6FAF9', subBlocks: [ diff --git a/apps/sim/blocks/blocks/imap.ts b/apps/sim/blocks/blocks/imap.ts index 683928f197e..33cc6e0ec6a 100644 --- a/apps/sim/blocks/blocks/imap.ts +++ b/apps/sim/blocks/blocks/imap.ts @@ -12,6 +12,7 @@ export const ImapBlock: BlockConfig = { bgColor: '#6366F1', icon: MailServerIcon, triggerAllowed: true, + docsLink: 'https://docs.sim.ai/tools/imap', hideFromToolbar: false, subBlocks: [...getTrigger('imap_poller').subBlocks], tools: { diff --git a/apps/sim/blocks/blocks/router.ts b/apps/sim/blocks/blocks/router.ts index ae6672a3099..5f8422c13c7 100644 --- a/apps/sim/blocks/blocks/router.ts +++ b/apps/sim/blocks/blocks/router.ts @@ -164,6 +164,7 @@ export const RouterBlock: BlockConfig = { name: 'Router (Legacy)', description: 'Route workflow', authMode: AuthMode.ApiKey, + docsLink: 'https://docs.sim.ai/blocks/router', longDescription: 'This is a core workflow block. Intelligently direct workflow execution to different paths based on input analysis. Use natural language to instruct the router to route to certain blocks based on the input.', bestPractices: ` @@ -283,6 +284,7 @@ export const RouterV2Block: BlockConfig = { name: 'Router', description: 'Route workflow based on context', authMode: AuthMode.ApiKey, + docsLink: 'https://docs.sim.ai/blocks/router', longDescription: 'Intelligently route workflow execution to different paths based on context analysis. Define multiple routes with descriptions, and an LLM will determine which route to take based on the provided context.', bestPractices: ` diff --git a/apps/sim/blocks/blocks/rss.ts b/apps/sim/blocks/blocks/rss.ts index a066e1312ea..d91f8a6b82a 100644 --- a/apps/sim/blocks/blocks/rss.ts +++ b/apps/sim/blocks/blocks/rss.ts @@ -12,6 +12,7 @@ export const RssBlock: BlockConfig = { bgColor: '#F97316', icon: RssIcon, triggerAllowed: true, + docsLink: 'https://docs.sim.ai/triggers/rss', subBlocks: [...getTrigger('rss_poller').subBlocks], diff --git a/apps/sim/blocks/blocks/start_trigger.ts b/apps/sim/blocks/blocks/start_trigger.ts index c32a8eb0f99..32fe5991745 100644 --- a/apps/sim/blocks/blocks/start_trigger.ts +++ b/apps/sim/blocks/blocks/start_trigger.ts @@ -15,6 +15,7 @@ export const StartTriggerBlock: BlockConfig = { `, category: 'triggers', bgColor: '#34B5FF', + docsLink: 'https://docs.sim.ai/triggers/start', icon: StartIcon, hideFromToolbar: false, subBlocks: [ diff --git a/apps/sim/blocks/blocks/twilio.ts b/apps/sim/blocks/blocks/twilio.ts index 09e5f7a0fe9..8f5db2d9b08 100644 --- a/apps/sim/blocks/blocks/twilio.ts +++ b/apps/sim/blocks/blocks/twilio.ts @@ -10,6 +10,7 @@ export const TwilioSMSBlock: BlockConfig = { authMode: AuthMode.ApiKey, longDescription: 'Integrate Twilio into the workflow. Can send SMS messages.', category: 'tools', + docsLink: 'https://docs.sim.ai/tools/twilio', bgColor: '#F22F46', // Twilio brand color icon: TwilioIcon, subBlocks: [ diff --git a/apps/sim/blocks/blocks/twilio_voice.ts b/apps/sim/blocks/blocks/twilio_voice.ts index cd3d0682612..d879545b13b 100644 --- a/apps/sim/blocks/blocks/twilio_voice.ts +++ b/apps/sim/blocks/blocks/twilio_voice.ts @@ -12,6 +12,7 @@ export const TwilioVoiceBlock: BlockConfig = { longDescription: 'Integrate Twilio Voice into the workflow. Make outbound calls and retrieve call recordings.', category: 'tools', + docsLink: 'https://docs.sim.ai/tools/twilio_voice', bgColor: '#F22F46', // Twilio brand color icon: TwilioIcon, triggerAllowed: true,