diff --git a/apps/sim/app/_shell/providers/tooltip-provider.tsx b/apps/sim/app/_shell/providers/tooltip-provider.tsx
new file mode 100644
index 0000000000..84274ddb8c
--- /dev/null
+++ b/apps/sim/app/_shell/providers/tooltip-provider.tsx
@@ -0,0 +1,11 @@
+'use client'
+
+import { Tooltip } from '@/components/emcn'
+
+interface TooltipProviderProps {
+ children: React.ReactNode
+}
+
+export function TooltipProvider({ children }: TooltipProviderProps) {
+ return {children}
+}
diff --git a/apps/sim/app/_styles/globals.css b/apps/sim/app/_styles/globals.css
index b9dcb8c71c..c260032460 100644
--- a/apps/sim/app/_styles/globals.css
+++ b/apps/sim/app/_styles/globals.css
@@ -58,6 +58,25 @@
pointer-events: none !important;
}
+/**
+ * Workflow canvas cursor styles
+ * Override React Flow's default selection cursor based on canvas mode
+ */
+.workflow-container.canvas-mode-cursor .react-flow__pane,
+.workflow-container.canvas-mode-cursor .react-flow__selectionpane {
+ cursor: default !important;
+}
+
+.workflow-container.canvas-mode-hand .react-flow__pane,
+.workflow-container.canvas-mode-hand .react-flow__selectionpane {
+ cursor: grab !important;
+}
+
+.workflow-container.canvas-mode-hand .react-flow__pane:active,
+.workflow-container.canvas-mode-hand .react-flow__selectionpane:active {
+ cursor: grabbing !important;
+}
+
/**
* Selected node ring indicator
* Uses a pseudo-element overlay to match the original behavior (absolute inset-0 z-40)
@@ -657,6 +676,20 @@ input[type="search"]::-ms-clear {
}
}
+/**
+ * Notification toast enter animation
+ */
+@keyframes notification-enter {
+ from {
+ opacity: 0;
+ transform: translateX(-16px);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(var(--stack-offset, 0px));
+ }
+}
+
/**
* @depricated
* Legacy globals (light/dark) kept for backward-compat with old classes.
diff --git a/apps/sim/app/api/users/me/settings/route.ts b/apps/sim/app/api/users/me/settings/route.ts
index 6f6094558f..c8de2b0568 100644
--- a/apps/sim/app/api/users/me/settings/route.ts
+++ b/apps/sim/app/api/users/me/settings/route.ts
@@ -27,10 +27,11 @@ const SettingsSchema = z.object({
superUserModeEnabled: z.boolean().optional(),
errorNotificationsEnabled: z.boolean().optional(),
snapToGridSize: z.number().min(0).max(50).optional(),
+ showActionBar: z.boolean().optional(),
})
const defaultSettings = {
- theme: 'system',
+ theme: 'dark',
autoConnect: true,
telemetryEnabled: true,
emailPreferences: {},
@@ -39,6 +40,7 @@ const defaultSettings = {
superUserModeEnabled: false,
errorNotificationsEnabled: true,
snapToGridSize: 0,
+ showActionBar: true,
}
export async function GET() {
@@ -73,6 +75,7 @@ export async function GET() {
superUserModeEnabled: userSettings.superUserModeEnabled ?? true,
errorNotificationsEnabled: userSettings.errorNotificationsEnabled ?? true,
snapToGridSize: userSettings.snapToGridSize ?? 0,
+ showActionBar: userSettings.showActionBar ?? true,
},
},
{ status: 200 }
diff --git a/apps/sim/app/layout.tsx b/apps/sim/app/layout.tsx
index 327a519291..166b260af8 100644
--- a/apps/sim/app/layout.tsx
+++ b/apps/sim/app/layout.tsx
@@ -12,6 +12,7 @@ import { HydrationErrorHandler } from '@/app/_shell/hydration-error-handler'
import { QueryProvider } from '@/app/_shell/providers/query-provider'
import { SessionProvider } from '@/app/_shell/providers/session-provider'
import { ThemeProvider } from '@/app/_shell/providers/theme-provider'
+import { TooltipProvider } from '@/app/_shell/providers/tooltip-provider'
import { season } from '@/app/_styles/fonts/season/season'
export const viewport: Viewport = {
@@ -208,7 +209,9 @@ export default function RootLayout({ children }: { children: React.ReactNode })
- {children}
+
+ {children}
+
diff --git a/apps/sim/app/playground/page.tsx b/apps/sim/app/playground/page.tsx
index 4670b805e0..d380256a21 100644
--- a/apps/sim/app/playground/page.tsx
+++ b/apps/sim/app/playground/page.tsx
@@ -21,12 +21,15 @@ import {
Combobox,
Connections,
Copy,
+ Cursor,
DatePicker,
DocumentAttachment,
Duplicate,
+ Expand,
Eye,
FolderCode,
FolderPlus,
+ Hand,
HexSimple,
Input,
Key as KeyIcon,
@@ -991,11 +994,14 @@ export default function PlaygroundPage() {
{ Icon: ChevronDown, name: 'ChevronDown' },
{ Icon: Connections, name: 'Connections' },
{ Icon: Copy, name: 'Copy' },
+ { Icon: Cursor, name: 'Cursor' },
{ Icon: DocumentAttachment, name: 'DocumentAttachment' },
{ Icon: Duplicate, name: 'Duplicate' },
+ { Icon: Expand, name: 'Expand' },
{ Icon: Eye, name: 'Eye' },
{ Icon: FolderCode, name: 'FolderCode' },
{ Icon: FolderPlus, name: 'FolderPlus' },
+ { Icon: Hand, name: 'Hand' },
{ Icon: HexSimple, name: 'HexSimple' },
{ Icon: KeyIcon, name: 'Key' },
{ Icon: Layout, name: 'Layout' },
diff --git a/apps/sim/app/templates/layout-client.tsx b/apps/sim/app/templates/layout-client.tsx
index d886b6c379..f49b81c6c6 100644
--- a/apps/sim/app/templates/layout-client.tsx
+++ b/apps/sim/app/templates/layout-client.tsx
@@ -1,15 +1,12 @@
'use client'
-import { Tooltip } from '@/components/emcn'
import { season } from '@/app/_styles/fonts/season/season'
export default function TemplatesLayoutClient({ children }: { children: React.ReactNode }) {
return (
-
-
-
+
)
}
diff --git a/apps/sim/app/workspace/[workspaceId]/layout.tsx b/apps/sim/app/workspace/[workspaceId]/layout.tsx
index 8b5d1093a4..8cf43aa40a 100644
--- a/apps/sim/app/workspace/[workspaceId]/layout.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/layout.tsx
@@ -1,6 +1,5 @@
'use client'
-import { Tooltip } from '@/components/emcn'
import { GlobalCommandsProvider } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
import { ProviderModelsLoader } from '@/app/workspace/[workspaceId]/providers/provider-models-loader'
import { SettingsLoader } from '@/app/workspace/[workspaceId]/providers/settings-loader'
@@ -13,16 +12,14 @@ export default function WorkspaceLayout({ children }: { children: React.ReactNod
-
-
-
+
>
)
diff --git a/apps/sim/app/workspace/[workspaceId]/utils/commands-utils.ts b/apps/sim/app/workspace/[workspaceId]/utils/commands-utils.ts
index da68876e21..eda6cdda0e 100644
--- a/apps/sim/app/workspace/[workspaceId]/utils/commands-utils.ts
+++ b/apps/sim/app/workspace/[workspaceId]/utils/commands-utils.ts
@@ -19,6 +19,7 @@ export type CommandId =
| 'clear-terminal-console'
| 'focus-toolbar-search'
| 'clear-notifications'
+ | 'fit-to-view'
/**
* Static metadata for a global command.
@@ -104,6 +105,11 @@ export const COMMAND_DEFINITIONS: Record = {
shortcut: 'Mod+E',
allowInEditable: false,
},
+ 'fit-to-view': {
+ id: 'fit-to-view',
+ shortcut: 'Mod+Shift+F',
+ allowInEditable: false,
+ },
}
/**
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/context-menu/block-context-menu.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/block-menu/block-menu.tsx
similarity index 84%
rename from apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/context-menu/block-context-menu.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/block-menu/block-menu.tsx
index 8945b13dc8..e0f7fac613 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/context-menu/block-context-menu.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/block-menu/block-menu.tsx
@@ -1,5 +1,6 @@
'use client'
+import type { RefObject } from 'react'
import {
Popover,
PopoverAnchor,
@@ -7,14 +8,48 @@ import {
PopoverDivider,
PopoverItem,
} from '@/components/emcn'
-import type { BlockContextMenuProps } from './types'
+
+/**
+ * Block information for context menu actions
+ */
+export interface BlockInfo {
+ id: string
+ type: string
+ enabled: boolean
+ horizontalHandles: boolean
+ parentId?: string
+ parentType?: string
+}
+
+/**
+ * Props for BlockMenu component
+ */
+export interface BlockMenuProps {
+ isOpen: boolean
+ position: { x: number; y: number }
+ menuRef: RefObject
+ onClose: () => void
+ selectedBlocks: BlockInfo[]
+ onCopy: () => void
+ onPaste: () => void
+ onDuplicate: () => void
+ onDelete: () => void
+ onToggleEnabled: () => void
+ onToggleHandles: () => void
+ onRemoveFromSubflow: () => void
+ onOpenEditor: () => void
+ onRename: () => void
+ hasClipboard?: boolean
+ showRemoveFromSubflow?: boolean
+ disableEdit?: boolean
+}
/**
* Context menu for workflow block(s).
* Displays block-specific actions in a popover at right-click position.
* Supports multi-selection - actions apply to all selected blocks.
*/
-export function BlockContextMenu({
+export function BlockMenu({
isOpen,
position,
menuRef,
@@ -32,7 +67,7 @@ export function BlockContextMenu({
hasClipboard = false,
showRemoveFromSubflow = false,
disableEdit = false,
-}: BlockContextMenuProps) {
+}: BlockMenuProps) {
const isSingleBlock = selectedBlocks.length === 1
const allEnabled = selectedBlocks.every((b) => b.enabled)
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/block-menu/index.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/block-menu/index.ts
new file mode 100644
index 0000000000..5016029014
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/block-menu/index.ts
@@ -0,0 +1,2 @@
+export type { BlockInfo, BlockMenuProps } from './block-menu'
+export { BlockMenu } from './block-menu'
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/canvas-menu/canvas-menu.tsx
similarity index 80%
rename from apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/context-menu/pane-context-menu.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/canvas-menu/canvas-menu.tsx
index a5bba68b46..7cd5294f32 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/canvas-menu/canvas-menu.tsx
@@ -1,5 +1,6 @@
'use client'
+import type { RefObject } from 'react'
import {
Popover,
PopoverAnchor,
@@ -7,13 +8,40 @@ import {
PopoverDivider,
PopoverItem,
} from '@/components/emcn'
-import type { PaneContextMenuProps } from './types'
/**
- * Context menu for workflow canvas pane.
+ * Props for CanvasMenu component
+ */
+export interface CanvasMenuProps {
+ isOpen: boolean
+ position: { x: number; y: number }
+ menuRef: RefObject
+ onClose: () => void
+ onUndo: () => void
+ onRedo: () => void
+ onPaste: () => void
+ onAddBlock: () => void
+ onAutoLayout: () => void
+ onFitToView: () => void
+ onOpenLogs: () => void
+ onToggleVariables: () => void
+ onToggleChat: () => void
+ onInvite: () => void
+ isVariablesOpen?: boolean
+ isChatOpen?: boolean
+ hasClipboard?: boolean
+ disableEdit?: boolean
+ disableAdmin?: boolean
+ canUndo?: boolean
+ canRedo?: boolean
+ isInvitationsDisabled?: boolean
+}
+
+/**
+ * Context menu for workflow canvas.
* Displays canvas-level actions when right-clicking empty space.
*/
-export function PaneContextMenu({
+export function CanvasMenu({
isOpen,
position,
menuRef,
@@ -23,6 +51,7 @@ export function PaneContextMenu({
onPaste,
onAddBlock,
onAutoLayout,
+ onFitToView,
onOpenLogs,
onToggleVariables,
onToggleChat,
@@ -35,7 +64,7 @@ export function PaneContextMenu({
canUndo = false,
canRedo = false,
isInvitationsDisabled = false,
-}: PaneContextMenuProps) {
+}: CanvasMenuProps) {
return (
Auto-layout
⇧L
+ {
+ onFitToView()
+ onClose()
+ }}
+ >
+ Fit to View
+
{/* Navigation actions */}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/canvas-menu/index.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/canvas-menu/index.ts
new file mode 100644
index 0000000000..ac5ef3e74f
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/canvas-menu/index.ts
@@ -0,0 +1,2 @@
+export type { CanvasMenuProps } from './canvas-menu'
+export { CanvasMenu } from './canvas-menu'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.tsx
index 7518a35c4d..b2a3349272 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/chat/chat.tsx
@@ -20,6 +20,7 @@ import {
PopoverItem,
PopoverScrollArea,
PopoverTrigger,
+ Tooltip,
Trash,
} from '@/components/emcn'
import { useSession } from '@/lib/auth/auth-client'
@@ -869,7 +870,7 @@ export function Chat() {
{/* More menu with actions */}
-
+