From e883e051bb5d1ff7db3c34ef5ae3fb043decbaaa Mon Sep 17 00:00:00 2001 From: waleed Date: Thu, 30 Apr 2026 01:15:19 -0700 Subject: [PATCH 1/3] fix(sidebar): show skeleton on tasks error state, not 'New task' placeholder --- .../[workspaceId]/w/components/sidebar/sidebar.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 1688600954c..c0252dc7cf4 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx @@ -781,7 +781,12 @@ export const Sidebar = memo(function Sidebar() { ) }, [isOnWorkflowPage]) - const { data: fetchedTasks = [], isLoading: tasksLoading } = useTasks(workspaceId) + const { + data: fetchedTasks = [], + isPending: tasksPending, + isError: tasksError, + } = useTasks(workspaceId) + const tasksLoading = tasksPending || tasksError useTaskEvents(workspaceId) From e316c44fc7e46cb862eec482a7d830873fe7a640 Mon Sep 17 00:00:00 2001 From: waleed Date: Thu, 30 Apr 2026 01:36:34 -0700 Subject: [PATCH 2/3] improvement(sidebar): remove unnecessary useCallback and useMemo wrappers --- .../w/components/sidebar/sidebar.tsx | 134 +++++++----------- 1 file changed, 55 insertions(+), 79 deletions(-) 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 c0252dc7cf4..2954c5a7365 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx @@ -321,6 +321,14 @@ const SidebarNavItem = memo(function SidebarNavItem({ /** Event name for sidebar scroll operations - centralized for consistency */ export const SIDEBAR_SCROLL_EVENT = 'sidebar-scroll-to-item' +const HIDDEN_STYLE = { display: 'none' } as const + +const WORKFLOW_ICON_STYLE: React.CSSProperties = { + backgroundColor: 'var(--text-icon)', + borderColor: 'color-mix(in srgb, var(--text-icon) 60%, transparent)', + backgroundClip: 'padding-box', +} + /** * Sidebar component with resizable width that persists across page refreshes. * @@ -368,7 +376,7 @@ export const Sidebar = memo(function Sidebar() { isCollapsedRef.current = isCollapsed }, [isCollapsed]) - const isMac = useMemo(() => isMacPlatform(), []) + const isMac = isMacPlatform() const [showCollapsedTooltips, setShowCollapsedTooltips] = useState(isCollapsed) @@ -775,11 +783,11 @@ export const Sidebar = memo(function Sidebar() { [navigateToSettings, getSettingsHref, setSidebarWidth] ) - const handleStartTour = useCallback(() => { + const handleStartTour = () => { window.dispatchEvent( new CustomEvent(isOnWorkflowPage ? START_WORKFLOW_TOUR_EVENT : START_NAV_TOUR_EVENT) ) - }, [isOnWorkflowPage]) + } const { data: fetchedTasks = [], @@ -879,7 +887,7 @@ export const Sidebar = memo(function Sidebar() { [setSidebarWidth, router] ) - const handleConfirmDeleteTasks = useCallback(() => { + const handleConfirmDeleteTasks = () => { const { taskIds: taskIdsToDelete } = contextMenuSelectionRef.current if (taskIdsToDelete.length === 0) return @@ -901,7 +909,7 @@ export const Sidebar = memo(function Sidebar() { deleteTasksMutation.mutate(taskIdsToDelete, { onSuccess: onDeleteSuccess }) } setIsTaskDeleteModalOpen(false) - }, [pathname, workspaceId, navigateToPage]) + } const [visibleTaskCount, setVisibleTaskCount] = useState(5) const taskFlyoutRename = useFlyoutInlineRename({ @@ -1038,9 +1046,9 @@ export const Sidebar = memo(function Sidebar() { } }, [createFolder]) - const handleImportWorkflow = useCallback(() => { + const handleImportWorkflow = () => { fileInputRef.current?.click() - }, []) + } const handleWorkspaceSwitch = useCallback( async (workspace: Workspace) => { @@ -1054,17 +1062,14 @@ export const Sidebar = memo(function Sidebar() { [workspaceId, switchWorkspace] ) - const handleSidebarClick = useCallback( - (e: React.MouseEvent) => { - const target = e.target as HTMLElement - if (target.tagName === 'BUTTON' || target.closest('button, [role="button"], a')) { - return - } - const { selectOnly, clearAllSelection } = useFolderStore.getState() - workflowId ? selectOnly(workflowId) : clearAllSelection() - }, - [workflowId] - ) + const handleSidebarClick = (e: React.MouseEvent) => { + const target = e.target as HTMLElement + if (target.tagName === 'BUTTON' || target.closest('button, [role="button"], a')) { + return + } + const { selectOnly, clearAllSelection } = useFolderStore.getState() + workflowId ? selectOnly(workflowId) : clearAllSelection() + } const handleRenameWorkspace = useCallback( async (workspaceIdToRename: string, newName: string) => { @@ -1127,43 +1132,31 @@ export const Sidebar = memo(function Sidebar() { workspaceFileInputRef.current?.click() }, []) - const handleWorkspaceFileChange = useCallback( - async (event: React.ChangeEvent) => { - const files = event.target.files - if (!files || files.length === 0) return + const handleWorkspaceFileChange = async (event: React.ChangeEvent) => { + const files = event.target.files + if (!files || files.length === 0) return - const zipFile = files[0] - await importWorkspace(zipFile) + const zipFile = files[0] + await importWorkspace(zipFile) - if (event.target) { - event.target.value = '' - } - }, - [importWorkspace] - ) + if (event.target) { + event.target.value = '' + } + } const tasksCollapsedIcon = useMemo( () => , [] ) - const workflowIconStyle = useMemo( - () => ({ - backgroundColor: 'var(--text-icon)', - borderColor: 'color-mix(in srgb, var(--text-icon) 60%, transparent)', - backgroundClip: 'padding-box', - }), - [] - ) - const workflowsCollapsedIcon = useMemo( () => (
), - [workflowIconStyle] + [] ) const tasksPrimaryAction = useMemo( @@ -1182,53 +1175,36 @@ export const Sidebar = memo(function Sidebar() { [handleCreateWorkflow] ) - const handleExpandSidebar = useCallback( - (e: React.MouseEvent) => { - e.preventDefault() - toggleCollapsed() - }, - [toggleCollapsed] - ) + const handleExpandSidebar = (e: React.MouseEvent) => { + e.preventDefault() + toggleCollapsed() + } - const handleNewTask = useCallback( - () => navigateToPage(`/workspace/${workspaceId}/home`), - [navigateToPage, workspaceId] - ) + const handleNewTask = () => navigateToPage(`/workspace/${workspaceId}/home`) - const handleSeeMoreTasks = useCallback(() => setVisibleTaskCount((prev) => prev + 5), []) + const handleSeeMoreTasks = () => setVisibleTaskCount((prev) => prev + 5) - const handleCloseTaskDeleteModal = useCallback(() => setIsTaskDeleteModalOpen(false), []) + const handleCloseTaskDeleteModal = () => setIsTaskDeleteModalOpen(false) - const handleEdgeKeyDown = useCallback( - (e: React.KeyboardEvent) => { - if (isCollapsed && (e.key === 'Enter' || e.key === ' ')) { - e.preventDefault() - toggleCollapsed() - } - }, - [isCollapsed, toggleCollapsed] - ) + const handleEdgeKeyDown = (e: React.KeyboardEvent) => { + if (isCollapsed && (e.key === 'Enter' || e.key === ' ')) { + e.preventDefault() + toggleCollapsed() + } + } - const handleOpenHelpFromMenu = useCallback(() => setIsHelpModalOpen(true), []) + const handleOpenHelpFromMenu = () => setIsHelpModalOpen(true) - const handleOpenDocs = useCallback(() => { + const handleOpenDocs = () => { window.open('https://docs.sim.ai', '_blank', 'noopener,noreferrer') captureEvent(posthog, 'docs_opened', { source: 'help_menu' }) - }, [posthog]) - - const handleTaskRenameBlur = useCallback( - () => void taskFlyoutRename.saveRename(), - [taskFlyoutRename.saveRename] - ) + } - const handleWorkflowRenameBlur = useCallback( - () => void workflowFlyoutRename.saveRename(), - [workflowFlyoutRename.saveRename] - ) + const handleTaskRenameBlur = () => void taskFlyoutRename.saveRename() - const hiddenStyle = useMemo(() => ({ display: 'none' }) as const, []) + const handleWorkflowRenameBlur = () => void workflowFlyoutRename.saveRename() - const resolveWorkspaceIdFromPath = useCallback((): string | undefined => { + const resolveWorkspaceIdFromPath = (): string | undefined => { if (workspaceId) return workspaceId if (typeof window === 'undefined') return undefined @@ -1237,7 +1213,7 @@ export const Sidebar = memo(function Sidebar() { if (idx === -1) return undefined return parts[idx + 1] - }, [workspaceId]) + } useRegisterGlobalCommands(() => createCommands([ @@ -1893,7 +1869,7 @@ export const Sidebar = memo(function Sidebar() { ref={workspaceFileInputRef} type='file' accept='.zip' - style={hiddenStyle} + style={HIDDEN_STYLE} onChange={handleWorkspaceFileChange} /> From 1e7da6dec61aa1f4274eb1045b0ee6711444336a Mon Sep 17 00:00:00 2001 From: waleed Date: Thu, 30 Apr 2026 01:43:50 -0700 Subject: [PATCH 3/3] fix(sidebar): use data-first pattern for tasks loading guard --- .../[workspaceId]/w/components/sidebar/sidebar.tsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) 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 2954c5a7365..21078b9e4fa 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx @@ -789,18 +789,14 @@ export const Sidebar = memo(function Sidebar() { ) } - const { - data: fetchedTasks = [], - isPending: tasksPending, - isError: tasksError, - } = useTasks(workspaceId) - const tasksLoading = tasksPending || tasksError + const { data: fetchedTasks } = useTasks(workspaceId) + const tasksLoading = fetchedTasks === undefined useTaskEvents(workspaceId) const tasks = useMemo( () => - fetchedTasks.length > 0 + fetchedTasks && fetchedTasks.length > 0 ? fetchedTasks.map((t) => ({ ...t, href: `/workspace/${workspaceId}/task/${t.id}`,