From 57f5bf886603a1fa060e34672300f1e47e3038d7 Mon Sep 17 00:00:00 2001 From: waleed Date: Fri, 27 Mar 2026 11:29:53 -0700 Subject: [PATCH 1/3] improvement(sidebar): collapsed sidebar UX, quick-create, hover consistency, and UI polish Made-with: Cursor --- apps/sim/app/_styles/globals.css | 6 +- .../knowledge/[id]/[documentId]/document.tsx | 9 +- .../[workspaceId]/knowledge/[id]/base.tsx | 15 +- .../base-tags-modal/base-tags-modal.tsx | 8 +- .../connectors-section/connectors-section.tsx | 9 +- .../delete-knowledge-base-modal.tsx | 11 +- .../notifications/notifications.tsx | 4 +- .../settings/components/api-keys/api-keys.tsx | 6 +- .../settings/components/byok/byok.tsx | 5 +- .../settings/components/copilot/copilot.tsx | 4 +- .../components/row-modal/row-modal.tsx | 6 +- .../[tableId]/components/table/table.tsx | 9 +- .../workspace/[workspaceId]/tables/tables.tsx | 6 +- .../w/[workflowId]/components/chat/chat.tsx | 2 +- .../deploy-modal/components/a2a/a2a.tsx | 2 +- .../sub-block/components/env-var-dropdown.tsx | 2 +- .../components/file-upload/file-upload.tsx | 2 +- .../grouped-checkbox-list.tsx | 2 +- .../custom-tool-modal/custom-tool-modal.tsx | 8 +- .../w/[workflowId]/components/panel/panel.tsx | 5 +- .../hooks/use-workflow-execution.ts | 2 +- .../collapsed-sidebar-menu.tsx | 131 ++++++++---- .../settings-sidebar/settings-sidebar.tsx | 5 +- .../components/delete-modal/delete-modal.tsx | 95 +++++++-- .../components/folder-item/folder-item.tsx | 12 +- .../workflow-item/workflow-item.tsx | 12 +- .../workspace-header/workspace-header.tsx | 48 ++++- .../w/components/sidebar/sidebar.tsx | 193 ++++++------------ .../emcn/components/combobox/combobox.tsx | 16 +- .../components/date-picker/date-picker.tsx | 2 +- .../emcn/components/input/input.tsx | 2 +- .../emcn/components/popover/popover.tsx | 4 +- .../emcn/components/textarea/textarea.tsx | 2 +- .../components/time-picker/time-picker.tsx | 8 +- .../components/access-control.tsx | 4 +- 35 files changed, 395 insertions(+), 262 deletions(-) diff --git a/apps/sim/app/_styles/globals.css b/apps/sim/app/_styles/globals.css index 7bfc3ed967e..6e1889d0277 100644 --- a/apps/sim/app/_styles/globals.css +++ b/apps/sim/app/_styles/globals.css @@ -188,7 +188,8 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn { --border-1: #e0e0e0; /* stronger border */ --surface-6: #e5e5e5; /* popovers, elevated surfaces */ --surface-7: #d9d9d9; - --surface-active: #ececec; /* hover/active state */ + --surface-hover: #f2f2f2; /* hover state */ + --surface-active: #ececec; /* active/selected state */ --workflow-edge: #e0e0e0; /* workflow handles/edges - matches border-1 */ @@ -342,7 +343,8 @@ html[data-sidebar-collapsed] .sidebar-container .sidebar-collapse-btn { --border-1: #3d3d3d; --surface-6: #454545; --surface-7: #505050; - --surface-active: #2c2c2c; /* hover/active state */ + --surface-hover: #262626; /* hover state */ + --surface-active: #2c2c2c; /* active/selected state */ --workflow-edge: #454545; /* workflow handles/edges - same as surface-6 in dark */ diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx index e088225863b..2bb41401cfc 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx @@ -1138,9 +1138,12 @@ export function Document({ {effectiveDocumentName} - ? This will permanently delete the document and all {documentData?.chunkCount ?? 0}{' '} - chunk - {documentData?.chunkCount === 1 ? '' : 's'} within it.{' '} + ?{' '} + + This will permanently delete the document and all {documentData?.chunkCount ?? 0}{' '} + chunk + {documentData?.chunkCount === 1 ? '' : 's'} within it. + {' '} {documentData?.connectorId ? ( This document is synced from a connector. Deleting it will permanently exclude it diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx index 3a018ce5e1b..5cd3c25a243 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx @@ -1106,8 +1106,10 @@ export function KnowledgeBase({

Are you sure you want to delete{' '} {knowledgeBaseName}? - The knowledge base and all {pagination.total} document - {pagination.total === 1 ? '' : 's'} within it will be removed.{' '} + + The knowledge base and all {pagination.total} document + {pagination.total === 1 ? '' : 's'} within it will be removed. + {' '} You can restore it from Recently Deleted in Settings. @@ -1147,7 +1149,9 @@ export function KnowledgeBase({ it from future syncs. To temporarily hide it from search, disable it instead. ) : ( - This action cannot be undone. + + This will permanently delete the document. + )}

) @@ -1177,7 +1181,10 @@ export function KnowledgeBase({

Are you sure you want to delete {selectedDocuments.size} document {selectedDocuments.size === 1 ? '' : 's'}?{' '} - This action cannot be undone. + + This will permanently delete the selected document + {selectedDocuments.size === 1 ? '' : 's'}. +

diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/base-tags-modal/base-tags-modal.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/base-tags-modal/base-tags-modal.tsx index dc57496db05..e15dbf0a679 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/base-tags-modal/base-tags-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/base-tags-modal/base-tags-modal.tsx @@ -416,9 +416,11 @@ export function BaseTagsModal({ open, onOpenChange, knowledgeBaseId }: BaseTagsM

- Are you sure you want to delete the "{selectedTag?.displayName}" tag? This will - remove this tag from {selectedTagUsage?.documentCount || 0} document - {selectedTagUsage?.documentCount !== 1 ? 's' : ''}.{' '} + Are you sure you want to delete the "{selectedTag?.displayName}" tag?{' '} + + This will remove this tag from {selectedTagUsage?.documentCount || 0} document + {selectedTagUsage?.documentCount !== 1 ? 's' : ''}. + {' '} This action cannot be undone.

diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx index 5e80e1f8cd3..5be2c56ad02 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx @@ -206,8 +206,13 @@ export function ConnectorsSection({ Delete Connector

- Are you sure you want to remove this connected source? Documents already synced will - remain in the knowledge base. + Are you sure you want to remove this connected source?{' '} + + This will stop future syncs from this source. + {' '} + + Documents already synced will remain in the knowledge base. +

diff --git a/apps/sim/app/workspace/[workspaceId]/knowledge/components/delete-knowledge-base-modal/delete-knowledge-base-modal.tsx b/apps/sim/app/workspace/[workspaceId]/knowledge/components/delete-knowledge-base-modal/delete-knowledge-base-modal.tsx index 7d1655c3dc5..bfc3b29de17 100644 --- a/apps/sim/app/workspace/[workspaceId]/knowledge/components/delete-knowledge-base-modal/delete-knowledge-base-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/knowledge/components/delete-knowledge-base-modal/delete-knowledge-base-modal.tsx @@ -46,10 +46,17 @@ export function DeleteKnowledgeBaseModal({ <> Are you sure you want to delete{' '} {knowledgeBaseName}? - All associated documents, chunks, and embeddings will be removed. + + All associated documents, chunks, and embeddings will be removed. + ) : ( - 'Are you sure you want to delete this knowledge base? All associated documents, chunks, and embeddings will be removed.' + <> + Are you sure you want to delete this knowledge base?{' '} + + All associated documents, chunks, and embeddings will be removed. + + )}{' '} You can restore it from Recently Deleted in Settings. diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/notifications.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/notifications.tsx index 277b202a5a9..4691b0c6f98 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/notifications.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/logs-toolbar/components/notifications/notifications.tsx @@ -1268,7 +1268,9 @@ export const NotificationSettings = memo(function NotificationSettings({ Delete Notification

- This will permanently remove the notification and stop all deliveries.{' '} + + This will permanently remove the notification and stop all deliveries. + {' '} This action cannot be undone.

diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/api-keys/api-keys.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/api-keys/api-keys.tsx index f4ec9d68063..d60b76eeffd 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/api-keys/api-keys.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/api-keys/api-keys.tsx @@ -371,8 +371,10 @@ export function ApiKeys() {

Deleting{' '} - {deleteKey?.name} will - immediately revoke access for any integrations using it.{' '} + {deleteKey?.name}{' '} + + will immediately revoke access for any integrations using it. + {' '} This action cannot be undone.

diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.tsx index f65b58c333f..c6050f18800 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.tsx @@ -404,7 +404,10 @@ export function BYOK() { {PROVIDERS.find((p) => p.id === deleteConfirmProvider)?.name} {' '} - API key? This workspace will revert to using platform hosted keys. + API key?{' '} + + This workspace will revert to using platform hosted keys. +

diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/copilot/copilot.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/copilot/copilot.tsx index cb9085af9be..bc135bfe17c 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/copilot/copilot.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/copilot/copilot.tsx @@ -366,7 +366,9 @@ export function Copilot() { {deleteKey?.name || 'Unnamed Key'} {' '} - will immediately revoke access for any integrations using it.{' '} + + will immediately revoke access for any integrations using it. + {' '} This action cannot be undone.

diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/row-modal/row-modal.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/row-modal/row-modal.tsx index 1e08d230b13..b506f79230c 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/row-modal/row-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/row-modal/row-modal.tsx @@ -164,8 +164,10 @@ export function RowModal({ mode, isOpen, onClose, table, row, rowIds, onSuccess )}

Are you sure you want to delete{' '} - {isSingleRow ? 'this row' : `these ${deleteCount} rows`}? This will permanently remove - all data in {isSingleRow ? 'this row' : 'these rows'}.{' '} + {isSingleRow ? 'this row' : `these ${deleteCount} rows`}?{' '} + + This will permanently remove all data in {isSingleRow ? 'this row' : 'these rows'}. + {' '} This action cannot be undone.

diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table/table.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table/table.tsx index 70556717975..74f116454d2 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table/table.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table/table.tsx @@ -1809,6 +1809,9 @@ export function Table({

Are you sure you want to delete{' '} {tableData?.name}?{' '} + + All {tableData?.rowCount ?? 0} rows will be removed. + {' '} You can restore it from Recently Deleted in Settings. @@ -1845,8 +1848,10 @@ export function Table({

Are you sure you want to delete{' '} - {deletingColumn}? This - will remove all data in this column.{' '} + {deletingColumn}?{' '} + + This will remove all data in this column. + {' '} This action cannot be undone.

diff --git a/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx b/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx index 9a2a76e5d4e..8b8a1178122 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/tables.tsx @@ -320,8 +320,10 @@ export function Tables() {

Are you sure you want to delete{' '} - {activeTable?.name}? - All {activeTable?.rowCount} rows will be removed.{' '} + {activeTable?.name}?{' '} + + All {activeTable?.rowCount} rows will be removed. + {' '} You can restore it from Recently Deleted in Settings. 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 3f0bd88c821..863db7529a0 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 @@ -929,7 +929,7 @@ export function Chat() { > {shouldShowConfigureStartInputsButton && (

{ e.stopPropagation() diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/a2a/a2a.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/a2a/a2a.tsx index 1d4e5844b49..b4afb2fca26 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/a2a/a2a.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/a2a/a2a.tsx @@ -883,7 +883,7 @@ console.log(data);`

{missingFields.any && (
diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown.tsx index b804d9d23a1..b26afd20752 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/env-var-dropdown.tsx @@ -280,7 +280,7 @@ export const EnvVarDropdown: React.FC = ({ } return ( - !open && onClose?.()}> + !open && onClose?.()} colorScheme='inverted'>
{truncateMiddle(file.name)} diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/grouped-checkbox-list/grouped-checkbox-list.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/grouped-checkbox-list/grouped-checkbox-list.tsx index 5f93afb87e4..dd296e21e47 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/grouped-checkbox-list/grouped-checkbox-list.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/grouped-checkbox-list/grouped-checkbox-list.tsx @@ -108,7 +108,7 @@ export function GroupedCheckboxList({ disabled={disabled} className={cn( 'flex w-full cursor-pointer items-center justify-between rounded-sm border border-[var(--border-1)] bg-[var(--surface-5)] px-2 py-1.5 font-medium font-sans text-[var(--text-primary)] text-sm outline-none focus:outline-none focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0 dark:bg-[var(--surface-5)]', - 'hover-hover:border-[var(--surface-7)] hover-hover:bg-[var(--surface-5)] dark:hover-hover:border-[var(--surface-7)] dark:hover-hover:bg-[var(--border-1)]' + 'hover-hover:bg-[var(--surface-active)]' )} > diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/custom-tool-modal/custom-tool-modal.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/custom-tool-modal/custom-tool-modal.tsx index a39a1cf2107..7f700f9c0f9 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/custom-tool-modal/custom-tool-modal.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tool-input/components/custom-tool-modal/custom-tool-modal.tsx @@ -1061,6 +1061,7 @@ try { setShowSchemaParams(false) } }} + colorScheme='inverted' >
Delete Custom Tool

- This will permanently delete the tool and remove it from any workflows that are using - it. This action cannot be undone. + + This will permanently delete the tool and remove it from any workflows that are + using it. + {' '} + This action cannot be undone.

diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx index fb81604e6e5..ecaf8796d84 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/panel.tsx @@ -874,7 +874,10 @@ export const Panel = memo(function Panel() { {currentWorkflow?.name ?? 'this workflow'} - ? All associated blocks, executions, and configuration will be removed.{' '} + ?{' '} + + All associated blocks, executions, and configuration will be removed. + {' '} You can restore it from Recently Deleted in Settings. diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts index 75c119565f2..9bd258da935 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution.ts @@ -1822,7 +1822,7 @@ export function useWorkflowExecution() { try { const pointer = await loadExecutionPointer(reconnectWorkflowId) if (cleanupRan) return - if (pointer && pointer.executionId) { + if (pointer?.executionId) { executionId = pointer.executionId fromEventId = pointer.lastEventId } diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/collapsed-sidebar-menu.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/collapsed-sidebar-menu.tsx index 565c441d64f..4ad02e3f36d 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/collapsed-sidebar-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/collapsed-sidebar-menu/collapsed-sidebar-menu.tsx @@ -1,4 +1,4 @@ -import type { MouseEvent as ReactMouseEvent } from 'react' +import { type MouseEvent as ReactMouseEvent, useState } from 'react' import { Folder, MoreHorizontal, Plus } from 'lucide-react' import Link from 'next/link' import { @@ -12,6 +12,7 @@ import { DropdownMenuSubTrigger, DropdownMenuTrigger, } from '@/components/emcn' +import { Pencil, SquareArrowUpRight } from '@/components/emcn/icons' import { cn } from '@/lib/core/utils/cn' import { ConversationListItem } from '@/app/workspace/[workspaceId]/components' import type { useHoverMenu } from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks' @@ -33,6 +34,7 @@ interface CollapsedSidebarMenuProps { interface CollapsedTaskFlyoutItemProps { task: { id: string; href: string; name: string; isActive?: boolean; isUnread?: boolean } isCurrentRoute: boolean + isMenuOpen?: boolean isEditing?: boolean editValue?: string inputRef?: React.RefObject @@ -56,9 +58,9 @@ interface CollapsedWorkflowFlyoutItemProps { onEditValueChange?: (value: string) => void onEditKeyDown?: (e: React.KeyboardEvent) => void onEditBlur?: () => void - onContextMenu?: (e: ReactMouseEvent, workflow: WorkflowMetadata) => void - onMorePointerDown?: () => void - onMoreClick?: (e: ReactMouseEvent, workflow: WorkflowMetadata) => void + onOpenInNewTab?: () => void + onRename?: () => void + canRename?: boolean } const EDIT_ROW_CLASS = @@ -68,10 +70,12 @@ function FlyoutMoreButton({ ariaLabel, onPointerDown, onClick, + isVisible, }: { ariaLabel: string onPointerDown?: () => void onClick: (e: ReactMouseEvent) => void + isVisible?: boolean }) { return ( @@ -154,7 +161,7 @@ export function CollapsedSidebarMenu({ @@ -180,6 +187,7 @@ export function CollapsedSidebarMenu({ export function CollapsedTaskFlyoutItem({ task, isCurrentRoute, + isMenuOpen = false, isEditing = false, editValue, inputRef, @@ -221,12 +229,13 @@ export function CollapsedTaskFlyoutItem({ } return ( -
+
onContextMenu(e, task.id) : undefined @@ -236,7 +245,9 @@ export function CollapsedTaskFlyoutItem({ title={task.name} isActive={!!task.isActive} isUnread={!!task.isUnread} - statusIndicatorClassName={!isCurrentRoute ? 'group-hover:hidden' : undefined} + statusIndicatorClassName={ + !(isCurrentRoute || isMenuOpen) ? 'group-hover:hidden' : undefined + } /> {showActions && ( @@ -248,6 +259,7 @@ export function CollapsedTaskFlyoutItem({ e.stopPropagation() onMoreClick?.(e, task.id) }} + isVisible={isMenuOpen} /> )}
@@ -265,11 +277,12 @@ export function CollapsedWorkflowFlyoutItem({ onEditValueChange, onEditKeyDown, onEditBlur, - onContextMenu, - onMorePointerDown, - onMoreClick, + onOpenInNewTab, + onRename, + canRename = true, }: CollapsedWorkflowFlyoutItemProps) { - const showActions = !!onMoreClick + const hasActions = !!onOpenInNewTab || !!onRename + const [actionsOpen, setActionsOpen] = useState(false) if (isEditing) { return ( @@ -299,28 +312,64 @@ export function CollapsedWorkflowFlyoutItem({ } return ( -
+
onContextMenu(e, workflow) : undefined} + onContextMenu={ + hasActions + ? (e) => { + e.preventDefault() + setActionsOpen(true) + } + : undefined + } > {workflow.name} - {showActions && ( - { - e.preventDefault() - e.stopPropagation() - onMoreClick?.(e, workflow) + {hasActions && ( + { + if (!open) setActionsOpen(false) }} - /> + > + { + e.stopPropagation() + setActionsOpen((prev) => !prev) + }} + > + + + + {onOpenInNewTab && ( + + + Open in new tab + + )} + {onRename && ( + { + e.preventDefault() + onRename() + }} + > + + Rename + + )} + + )}
) @@ -338,9 +387,9 @@ export function CollapsedFolderItems({ onEditValueChange, onEditKeyDown, onEditBlur, - onWorkflowContextMenu, - onWorkflowMorePointerDown, - onWorkflowMoreClick, + onWorkflowOpenInNewTab, + onWorkflowRename, + canRenameWorkflow, }: { nodes: FolderTreeNode[] workflowsByFolder: Record @@ -353,9 +402,9 @@ export function CollapsedFolderItems({ onEditValueChange?: (value: string) => void onEditKeyDown?: (e: React.KeyboardEvent) => void onEditBlur?: () => void - onWorkflowContextMenu?: (e: ReactMouseEvent, workflow: WorkflowMetadata) => void - onWorkflowMorePointerDown?: () => void - onWorkflowMoreClick?: (e: ReactMouseEvent, workflow: WorkflowMetadata) => void + onWorkflowOpenInNewTab?: (workflow: WorkflowMetadata) => void + onWorkflowRename?: (workflow: WorkflowMetadata) => void + canRenameWorkflow?: boolean }) { return ( <> @@ -374,7 +423,7 @@ export function CollapsedFolderItems({ return ( - + {folder.name} @@ -391,9 +440,9 @@ export function CollapsedFolderItems({ onEditValueChange={onEditValueChange} onEditKeyDown={onEditKeyDown} onEditBlur={onEditBlur} - onWorkflowContextMenu={onWorkflowContextMenu} - onWorkflowMorePointerDown={onWorkflowMorePointerDown} - onWorkflowMoreClick={onWorkflowMoreClick} + onWorkflowOpenInNewTab={onWorkflowOpenInNewTab} + onWorkflowRename={onWorkflowRename} + canRenameWorkflow={canRenameWorkflow} /> {folderWorkflows.map((workflow) => ( onWorkflowOpenInNewTab(workflow) : undefined + } + onRename={onWorkflowRename ? () => onWorkflowRename(workflow) : undefined} + canRename={canRenameWorkflow} /> ))} diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-sidebar/settings-sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-sidebar/settings-sidebar.tsx index 0c65c2ef48b..9792e3ed574 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-sidebar/settings-sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-sidebar/settings-sidebar.tsx @@ -191,7 +191,7 @@ export function SettingsSidebar({ + )} +
+ )}
@@ -463,7 +489,9 @@ export function WorkspaceHeader({ ) : (
onWorkspaceSwitch(workspace)} @@ -482,7 +510,7 @@ export function WorkspaceHeader({ const rect = e.currentTarget.getBoundingClientRect() openContextMenuAt(workspace, rect.right, rect.top) }} - className='flex h-[18px] w-[18px] flex-shrink-0 items-center justify-center rounded-sm opacity-0 transition-opacity hover-hover:bg-[var(--surface-7)] group-hover:opacity-100' + className='flex h-[18px] w-[18px] flex-shrink-0 items-center justify-center rounded-sm opacity-0 transition-opacity group-hover:opacity-100' > @@ -496,7 +524,7 @@ export function WorkspaceHeader({
@@ -214,8 +221,8 @@ const SidebarNavItem = memo(function SidebarNavItem({ onContextMenu?: (e: React.MouseEvent, href: string) => void }) { const Icon = item.icon - const baseClasses = - 'group flex h-[30px] items-center gap-2 rounded-lg mx-0.5 px-2 text-sm hover-hover:bg-[var(--surface-active)]' + const baseClasses = 'group flex h-[30px] items-center gap-2 rounded-lg mx-0.5 px-2 text-sm' + const hoverClasses = !active ? 'hover-hover:bg-[var(--surface-hover)]' : '' const activeClasses = active ? 'bg-[var(--surface-active)]' : '' const content = ( @@ -230,7 +237,7 @@ const SidebarNavItem = memo(function SidebarNavItem({ href={item.href} data-item-id={item.id} data-tour={`nav-${item.id}`} - className={`${baseClasses} ${activeClasses}`} + className={`${baseClasses} ${hoverClasses} ${activeClasses}`} onClick={ item.onClick ? (e) => { @@ -249,7 +256,7 @@ const SidebarNavItem = memo(function SidebarNavItem({ type='button' data-item-id={item.id} data-tour={`nav-${item.id}`} - className={`${baseClasses} ${activeClasses}`} + className={`${baseClasses} ${hoverClasses} ${activeClasses}`} onClick={item.onClick} > {content} @@ -485,6 +492,11 @@ export const Sidebar = memo(function Sidebar() { taskIds: [], names: [], }) + const [menuOpenTaskId, setMenuOpenTaskId] = useState(null) + + useEffect(() => { + if (!isTaskContextMenuOpen) setMenuOpenTaskId(null) + }, [isTaskContextMenuOpen]) const captureTaskSelection = useCallback((taskId: string) => { const { selectedTasks, selectTaskOnly } = useFolderStore.getState() @@ -502,6 +514,7 @@ export const Sidebar = memo(function Sidebar() { const handleTaskContextMenu = useCallback( (e: React.MouseEvent, taskId: string) => { captureTaskSelection(taskId) + setMenuOpenTaskId(taskId) tasksHover.setLocked(true) preventTaskDismiss() handleTaskContextMenuBase(e) @@ -523,6 +536,7 @@ export const Sidebar = memo(function Sidebar() { } tasksHover.setLocked(true) captureTaskSelection(taskId) + setMenuOpenTaskId(taskId) const rect = e.currentTarget.getBoundingClientRect() handleTaskContextMenuBase({ preventDefault: () => {}, @@ -540,77 +554,6 @@ export const Sidebar = memo(function Sidebar() { ] ) - const { - isOpen: isCollapsedWorkflowContextMenuOpen, - position: collapsedWorkflowContextMenuPosition, - menuRef: collapsedWorkflowMenuRef, - handleContextMenu: handleCollapsedWorkflowContextMenuBase, - closeMenu: closeCollapsedWorkflowContextMenu, - preventDismiss: preventCollapsedWorkflowDismiss, - } = useContextMenu() - - const collapsedWorkflowContextMenuRef = useRef<{ - workflowId: string - workflowName: string - } | null>(null) - - const captureCollapsedWorkflowSelection = useCallback( - (workflow: { id: string; name: string }) => { - collapsedWorkflowContextMenuRef.current = { - workflowId: workflow.id, - workflowName: workflow.name, - } - }, - [] - ) - - const handleCollapsedWorkflowContextMenu = useCallback( - (e: React.MouseEvent, workflow: { id: string; name: string }) => { - captureCollapsedWorkflowSelection(workflow) - workflowsHover.setLocked(true) - preventCollapsedWorkflowDismiss() - handleCollapsedWorkflowContextMenuBase(e) - }, - [ - captureCollapsedWorkflowSelection, - handleCollapsedWorkflowContextMenuBase, - preventCollapsedWorkflowDismiss, - workflowsHover, - ] - ) - - const handleCollapsedWorkflowMorePointerDown = useCallback(() => { - if (isCollapsedWorkflowContextMenuOpen) { - preventCollapsedWorkflowDismiss() - } - }, [isCollapsedWorkflowContextMenuOpen, preventCollapsedWorkflowDismiss]) - - const handleCollapsedWorkflowMoreClick = useCallback( - (e: React.MouseEvent, workflow: { id: string; name: string }) => { - if (isCollapsedWorkflowContextMenuOpen) { - closeCollapsedWorkflowContextMenu() - return - } - - workflowsHover.setLocked(true) - captureCollapsedWorkflowSelection(workflow) - const rect = e.currentTarget.getBoundingClientRect() - handleCollapsedWorkflowContextMenuBase({ - preventDefault: () => {}, - stopPropagation: () => {}, - clientX: rect.right, - clientY: rect.top, - } as React.MouseEvent) - }, - [ - isCollapsedWorkflowContextMenuOpen, - closeCollapsedWorkflowContextMenu, - captureCollapsedWorkflowSelection, - handleCollapsedWorkflowContextMenuBase, - workflowsHover, - ] - ) - const { handleDuplicateWorkspace: duplicateWorkspace } = useDuplicateWorkspace({ workspaceId, }) @@ -854,10 +797,6 @@ export const Sidebar = memo(function Sidebar() { itemType: 'workflow', onSave: async (workflowIdToRename, name) => { await updateWorkflow(workflowIdToRename, { name }) - collapsedWorkflowContextMenuRef.current = { - workflowId: workflowIdToRename, - workflowName: name, - } }, }) @@ -866,8 +805,8 @@ export const Sidebar = memo(function Sidebar() { }, [isTaskContextMenuOpen, taskFlyoutRename.editingId, tasksHover.setLocked]) useEffect(() => { - workflowsHover.setLocked(isCollapsedWorkflowContextMenuOpen || !!workflowFlyoutRename.editingId) - }, [isCollapsedWorkflowContextMenuOpen, workflowFlyoutRename.editingId, workflowsHover.setLocked]) + workflowsHover.setLocked(!!workflowFlyoutRename.editingId) + }, [workflowFlyoutRename.editingId, workflowsHover.setLocked]) const handleTaskOpenInNewTab = useCallback(() => { const { taskIds: ids } = contextMenuSelectionRef.current @@ -897,22 +836,20 @@ export const Sidebar = memo(function Sidebar() { taskFlyoutRename.startRename({ id: taskId, name: task.name }) }, [taskFlyoutRename, tasks, tasksHover]) - const handleCollapsedWorkflowOpenInNewTab = useCallback(() => { - const workflow = collapsedWorkflowContextMenuRef.current - if (!workflow) return - window.open( - `/workspace/${workspaceId}/w/${workflow.workflowId}`, - '_blank', - 'noopener,noreferrer' - ) - }, [workspaceId]) + const handleCollapsedWorkflowOpenInNewTab = useCallback( + (workflow: { id: string }) => { + window.open(`/workspace/${workspaceId}/w/${workflow.id}`, '_blank', 'noopener,noreferrer') + }, + [workspaceId] + ) - const handleStartCollapsedWorkflowRename = useCallback(() => { - const workflow = collapsedWorkflowContextMenuRef.current - if (!workflow) return - workflowsHover.setLocked(true) - workflowFlyoutRename.startRename({ id: workflow.workflowId, name: workflow.workflowName }) - }, [workflowFlyoutRename, workflowsHover]) + const handleCollapsedWorkflowRename = useCallback( + (workflow: { id: string; name: string }) => { + workflowsHover.setLocked(true) + workflowFlyoutRename.startRename({ id: workflow.id, name: workflow.name }) + }, + [workflowFlyoutRename, workflowsHover] + ) const [hasOverflowTop, setHasOverflowTop] = useState(false) const [hasOverflowBottom, setHasOverflowBottom] = useState(false) @@ -1150,7 +1087,7 @@ export const Sidebar = memo(function Sidebar() {
{brand.logoUrl ? ( @@ -1172,7 +1109,7 @@ export const Sidebar = memo(function Sidebar() {