Skip to content

Commit 4f43e06

Browse files
committed
feat(sidebar): workflow list handling updated
1 parent 71d9dde commit 4f43e06

File tree

10 files changed

+333
-187
lines changed

10 files changed

+333
-187
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use client'
2+
3+
import { useEffect } from 'react'
4+
import { createLogger } from '@/lib/logs/console/logger'
5+
6+
const logger = createLogger('RootLayout')
7+
8+
const BROWSER_EXTENSION_ATTRIBUTES = [
9+
'data-new-gr-c-s-check-loaded',
10+
'data-gr-ext-installed',
11+
'data-gr-ext-disabled',
12+
'data-grammarly',
13+
'data-fgm',
14+
'data-lt-installed',
15+
]
16+
17+
/**
18+
* Client component that intercepts console.error to filter and log hydration errors
19+
* while ignoring errors caused by browser extensions.
20+
*/
21+
export function HydrationErrorHandler() {
22+
useEffect(() => {
23+
const originalError = console.error
24+
console.error = (...args) => {
25+
if (args[0].includes('Hydration')) {
26+
const isExtensionError = BROWSER_EXTENSION_ATTRIBUTES.some((attr) =>
27+
args.some((arg) => typeof arg === 'string' && arg.includes(attr))
28+
)
29+
30+
if (!isExtensionError) {
31+
logger.error('Hydration Error', {
32+
details: args,
33+
componentStack: args.find(
34+
(arg) => typeof arg === 'string' && arg.includes('component stack')
35+
),
36+
})
37+
}
38+
}
39+
originalError.apply(console, args)
40+
}
41+
42+
return () => {
43+
console.error = originalError
44+
}
45+
}, [])
46+
47+
return null
48+
}

apps/sim/app/layout.tsx

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,15 @@ import { PublicEnvScript } from 'next-runtime-env'
33
import { BrandedLayout } from '@/components/branded-layout'
44
import { generateThemeCSS } from '@/lib/branding/inject-theme'
55
import { generateBrandedMetadata, generateStructuredData } from '@/lib/branding/metadata'
6-
import { createLogger } from '@/lib/logs/console/logger'
76
import { PostHogProvider } from '@/lib/posthog/provider'
87
import '@/app/globals.css'
98

109
import { SessionProvider } from '@/lib/session/session-context'
1110
import { season } from '@/app/fonts/season/season'
11+
import { HydrationErrorHandler } from '@/app/hydration-error-handler'
1212
import { ThemeProvider } from '@/app/theme-provider'
1313
import { ZoomPrevention } from '@/app/zoom-prevention'
1414

15-
const logger = createLogger('RootLayout')
16-
17-
const BROWSER_EXTENSION_ATTRIBUTES = [
18-
'data-new-gr-c-s-check-loaded',
19-
'data-gr-ext-installed',
20-
'data-gr-ext-disabled',
21-
'data-grammarly',
22-
'data-fgm',
23-
'data-lt-installed',
24-
]
25-
26-
if (typeof window !== 'undefined') {
27-
const originalError = console.error
28-
console.error = (...args) => {
29-
if (args[0].includes('Hydration')) {
30-
const isExtensionError = BROWSER_EXTENSION_ATTRIBUTES.some((attr) =>
31-
args.some((arg) => typeof arg === 'string' && arg.includes(attr))
32-
)
33-
34-
if (!isExtensionError) {
35-
logger.error('Hydration Error', {
36-
details: args,
37-
componentStack: args.find(
38-
(arg) => typeof arg === 'string' && arg.includes('component stack')
39-
),
40-
})
41-
}
42-
}
43-
originalError.apply(console, args)
44-
}
45-
}
46-
4715
export const viewport: Viewport = {
4816
width: 'device-width',
4917
initialScale: 1,
@@ -130,6 +98,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
13098
<PublicEnvScript />
13199
</head>
132100
<body className={`${season.variable} font-season`} suppressHydrationWarning>
101+
<HydrationErrorHandler />
133102
<PostHogProvider>
134103
<ThemeProvider>
135104
<SessionProvider>

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/blocks/blocks.tsx

Lines changed: 7 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
'use client'
22

3-
import { useCallback, useMemo, useRef } from 'react'
3+
import { useMemo, useRef } from 'react'
44
import clsx from 'clsx'
55
import { ChevronDown } from 'lucide-react'
66
import { getBlocksForSidebar } from '@/lib/workflows/trigger-utils'
77
import { LoopTool } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/loop/loop-config'
88
import { ParallelTool } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/subflows/parallel/parallel-config'
99
import type { BlockConfig } from '@/blocks/types'
1010
import { usePanelResize } from '../../hooks/use-panel-resize'
11+
import { useSidebarItemInteractions } from '../../hooks/use-sidebar-item-interactions'
1112

1213
interface BlocksProps {
1314
disabled?: boolean
@@ -38,6 +39,9 @@ export function Blocks({ disabled = false }: BlocksProps) {
3839
containerRef,
3940
})
4041

42+
// Sidebar item interactions hook
43+
const { handleDragStart, handleItemClick } = useSidebarItemInteractions({ disabled })
44+
4145
const blocks = useMemo(() => {
4246
const allBlocks = getBlocksForSidebar()
4347

@@ -90,59 +94,6 @@ export function Blocks({ disabled = false }: BlocksProps) {
9094
return [...regularBlockItems, ...toolItems]
9195
}, [])
9296

93-
/**
94-
* Handle drag start for block items
95-
*
96-
* @param e - React drag event
97-
* @param item - Block item configuration
98-
*/
99-
const handleDragStart = useCallback(
100-
(e: React.DragEvent<HTMLElement>, item: BlockItem) => {
101-
if (disabled) {
102-
e.preventDefault()
103-
return
104-
}
105-
106-
try {
107-
e.dataTransfer.setData(
108-
'application/json',
109-
JSON.stringify({
110-
type: item.type,
111-
enableTriggerMode: false,
112-
})
113-
)
114-
e.dataTransfer.effectAllowed = 'move'
115-
} catch (error) {
116-
console.error('Failed to set drag data:', error)
117-
}
118-
},
119-
[disabled]
120-
)
121-
122-
/**
123-
* Handle click on block item to add to canvas
124-
*
125-
* @param item - Block item configuration
126-
*/
127-
const handleClick = useCallback(
128-
(item: BlockItem) => {
129-
if (item.type === 'connectionBlock' || disabled) return
130-
131-
try {
132-
const event = new CustomEvent('add-block-from-toolbar', {
133-
detail: {
134-
type: item.type,
135-
enableTriggerMode: false,
136-
},
137-
})
138-
window.dispatchEvent(event)
139-
} catch (error) {
140-
console.error('Failed to dispatch add-block event:', error)
141-
}
142-
},
143-
[disabled]
144-
)
145-
14697
return (
14798
<div
14899
ref={containerRef}
@@ -174,8 +125,8 @@ export function Blocks({ disabled = false }: BlocksProps) {
174125
<div
175126
key={block.type}
176127
draggable={!disabled}
177-
onDragStart={(e) => handleDragStart(e, block)}
178-
onClick={() => handleClick(block)}
128+
onDragStart={(e) => handleDragStart(e, block.type, false)}
129+
onClick={() => handleItemClick(block.type, false)}
179130
className={clsx(
180131
'group flex h-[25px] items-center gap-[8px] rounded-[8px] px-[5px] text-[14px]',
181132
disabled

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/triggers/triggers.tsx

Lines changed: 7 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
'use client'
22

3-
import { useCallback, useMemo, useRef } from 'react'
3+
import { useMemo, useRef } from 'react'
44
import clsx from 'clsx'
55
import { ChevronDown } from 'lucide-react'
66
import { getTriggersForSidebar, hasTriggerCapability } from '@/lib/workflows/trigger-utils'
7-
import type { BlockConfig } from '@/blocks/types'
87
import { usePanelResize } from '../../hooks/use-panel-resize'
8+
import { useSidebarItemInteractions } from '../../hooks/use-sidebar-item-interactions'
99

1010
interface TriggersProps {
1111
disabled?: boolean
@@ -27,66 +27,16 @@ export function Triggers({ disabled = false }: TriggersProps) {
2727
containerRef,
2828
})
2929

30+
// Sidebar item interactions hook
31+
const { handleDragStart, handleItemClick } = useSidebarItemInteractions({ disabled })
32+
3033
const triggers = useMemo(() => {
3134
const allTriggers = getTriggersForSidebar()
3235

3336
// Sort alphabetically
3437
return allTriggers.sort((a, b) => a.name.localeCompare(b.name))
3538
}, [])
3639

37-
/**
38-
* Handle drag start for trigger blocks
39-
*
40-
* @param e - React drag event
41-
* @param config - Block configuration
42-
*/
43-
const handleDragStart = useCallback(
44-
(e: React.DragEvent<HTMLElement>, config: BlockConfig) => {
45-
if (disabled) {
46-
e.preventDefault()
47-
return
48-
}
49-
50-
try {
51-
e.dataTransfer.setData(
52-
'application/json',
53-
JSON.stringify({
54-
type: config.type,
55-
enableTriggerMode: hasTriggerCapability(config),
56-
})
57-
)
58-
e.dataTransfer.effectAllowed = 'move'
59-
} catch (error) {
60-
console.error('Failed to set drag data:', error)
61-
}
62-
},
63-
[disabled]
64-
)
65-
66-
/**
67-
* Handle click on trigger block to add to canvas
68-
*
69-
* @param config - Block configuration
70-
*/
71-
const handleClick = useCallback(
72-
(config: BlockConfig) => {
73-
if (config.type === 'connectionBlock' || disabled) return
74-
75-
try {
76-
const event = new CustomEvent('add-block-from-toolbar', {
77-
detail: {
78-
type: config.type,
79-
enableTriggerMode: hasTriggerCapability(config),
80-
},
81-
})
82-
window.dispatchEvent(event)
83-
} catch (error) {
84-
console.error('Failed to dispatch add-block event:', error)
85-
}
86-
},
87-
[disabled]
88-
)
89-
9040
return (
9141
<div
9242
ref={containerRef}
@@ -118,8 +68,8 @@ export function Triggers({ disabled = false }: TriggersProps) {
11868
<div
11969
key={trigger.type}
12070
draggable={!disabled}
121-
onDragStart={(e) => handleDragStart(e, trigger)}
122-
onClick={() => handleClick(trigger)}
71+
onDragStart={(e) => handleDragStart(e, trigger.type, hasTriggerCapability(trigger))}
72+
onClick={() => handleItemClick(trigger.type, hasTriggerCapability(trigger))}
12373
className={clsx(
12474
'group flex h-[25px] items-center gap-[8px] rounded-[8px] px-[5px] text-[14px]',
12575
disabled

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/workflow-list/components/workflow-item/workflow-item.tsx

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ interface WorkflowItemProps {
1212
workflow: WorkflowMetadata
1313
active: boolean
1414
level: number
15+
onWorkflowClick: (workflowId: string, shiftKey: boolean, metaKey: boolean) => void
1516
}
1617

1718
/**
@@ -21,10 +22,10 @@ interface WorkflowItemProps {
2122
* @param props - Component props
2223
* @returns Workflow item with drag and selection support
2324
*/
24-
export function WorkflowItem({ workflow, active, level }: WorkflowItemProps) {
25+
export function WorkflowItem({ workflow, active, level, onWorkflowClick }: WorkflowItemProps) {
2526
const params = useParams()
2627
const workspaceId = params.workspaceId as string
27-
const { selectedWorkflows, selectOnly, toggleWorkflowSelection } = useFolderStore()
28+
const { selectedWorkflows } = useFolderStore()
2829
const isSelected = selectedWorkflows.has(workflow.id)
2930

3031
/**
@@ -49,7 +50,7 @@ export function WorkflowItem({ workflow, active, level }: WorkflowItemProps) {
4950
})
5051

5152
/**
52-
* Handle click - manages workflow selection with shift-key support
53+
* Handle click - manages workflow selection with shift-key and cmd/ctrl-key support
5354
*
5455
* @param e - React mouse event
5556
*/
@@ -62,23 +63,17 @@ export function WorkflowItem({ workflow, active, level }: WorkflowItemProps) {
6263
return
6364
}
6465

65-
if (e.shiftKey) {
66+
const isModifierClick = e.shiftKey || e.metaKey || e.ctrlKey
67+
68+
// Prevent default link behavior when using modifier keys
69+
if (isModifierClick) {
6670
e.preventDefault()
67-
toggleWorkflowSelection(workflow.id)
68-
} else {
69-
if (!isSelected || selectedWorkflows.size > 1) {
70-
selectOnly(workflow.id)
71-
}
7271
}
72+
73+
// Use metaKey (Cmd on Mac) or ctrlKey (Ctrl on Windows/Linux)
74+
onWorkflowClick(workflow.id, e.shiftKey, e.metaKey || e.ctrlKey)
7375
},
74-
[
75-
shouldPreventClickRef,
76-
workflow.id,
77-
isSelected,
78-
selectedWorkflows.size,
79-
toggleWorkflowSelection,
80-
selectOnly,
81-
]
76+
[shouldPreventClickRef, workflow.id, onWorkflowClick]
8277
)
8378

8479
return (

0 commit comments

Comments
 (0)