diff --git a/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx b/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx index 96daffc21d07..be769e856653 100644 --- a/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx +++ b/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx @@ -142,10 +142,6 @@ export const MenuBar = memo((): JSX.Element => { > {currentFlowName || "Untitled Flow"} - {isFlowLocked && ( - - )} - {/* Left Section */} @@ -68,7 +68,7 @@ export default function AppHeader(): JSX.Element { {ENABLE_DATASTAX_LANGFLOW ? ( ) : ( - + )} {ENABLE_DATASTAX_LANGFLOW && ( diff --git a/src/frontend/src/components/core/canvasControlsComponent/CanvasControlsDropdown.tsx b/src/frontend/src/components/core/canvasControlsComponent/CanvasControlsDropdown.tsx index 37f7a9b81f46..250622b95a5e 100644 --- a/src/frontend/src/components/core/canvasControlsComponent/CanvasControlsDropdown.tsx +++ b/src/frontend/src/components/core/canvasControlsComponent/CanvasControlsDropdown.tsx @@ -89,13 +89,13 @@ const CanvasControlsDropdown = () => { title="Canvas Controls" >
-
+
{formatZoomPercentage(zoom)}
diff --git a/src/frontend/src/components/ui/sidebar.tsx b/src/frontend/src/components/ui/sidebar.tsx index 637814551989..8533ca74760f 100644 --- a/src/frontend/src/components/ui/sidebar.tsx +++ b/src/frontend/src/components/ui/sidebar.tsx @@ -23,7 +23,12 @@ const SIDEBAR_WIDTH = "19rem"; const SIDEBAR_WIDTH_ICON = "4rem"; const SEGMENTED_SIDEBAR_ICON_WIDTH = "40px"; -export type SidebarSection = "search" | "components" | "bundles" | "mcp"; +export type SidebarSection = + | "search" + | "components" + | "bundles" + | "mcp" + | "add_note"; // Helper function to get cookie value function getCookie(name: string): string | null { @@ -466,7 +471,7 @@ const SidebarHeader = React.forwardRef<
); @@ -551,8 +556,8 @@ const SidebarGroupLabel = React.memo( ref={ref} data-sidebar="group-label" className={cn( - "flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-semibold text-foreground/70 outline-none ring-ring transition-[margin,opa] duration-200 ease-linear focus-visible:ring-1 [&>svg]:size-4 [&>svg]:shrink-0", - "group-data-[collapsible=icon]:pointer-events-none group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0", + "flex shrink-0 items-center rounded-md text-xs font-semibold text-foreground/70 outline-none ring-ring transition-[margin,opa] duration-200 ease-linear focus-visible:ring-1 [&>svg]:size-4 [&>svg]:shrink-0", + "group-data-[collapsible=icon]:pointer-events-none group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 px-2 pb-3", className, )} {...props} diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/MemoizedComponents.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/MemoizedComponents.tsx index 60843cc62bd5..f2b120ec3120 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/MemoizedComponents.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/MemoizedComponents.tsx @@ -1,5 +1,6 @@ import { Background, Panel } from "@xyflow/react"; import { memo } from "react"; +import { useShallow } from "zustand/react/shallow"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; import CanvasControlButton from "@/components/core/canvasControlsComponent/CanvasControlButton"; import CanvasControls from "@/components/core/canvasControlsComponent/CanvasControls"; @@ -7,6 +8,7 @@ import LogCanvasControls from "@/components/core/logCanvasControlsComponent"; import { Button } from "@/components/ui/button"; import { SidebarTrigger, useSidebar } from "@/components/ui/sidebar"; import { ENABLE_NEW_SIDEBAR } from "@/customization/feature-flags"; +import useFlowStore from "@/stores/flowStore"; import { cn } from "@/utils/utils"; import { useSearchContext } from "../flowSidebarComponent"; import { NAV_ITEMS } from "../flowSidebarComponent/components/sidebarSegmentedNav"; @@ -28,32 +30,35 @@ export const MemoizedCanvasControls = memo( setIsAddingNote, shadowBoxWidth, shadowBoxHeight, - }: MemoizedCanvasControlsProps) => ( - - - - ), + }: MemoizedCanvasControlsProps) => { + const isLocked = useFlowStore( + useShallow((state) => state.currentFlow?.locked), + ); + + return ( + + + + ); + }, ); export const MemoizedSidebarTrigger = memo(() => { diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index aae515f6faec..aac7334f9780 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -615,6 +615,8 @@ export default function Page({ }; setNodes((nds) => nds.concat(newNode)); setIsAddingNote(false); + // Signal sidebar to revert add_note active state + window.dispatchEvent(new Event("lf:end-add-note")); } }, [ @@ -666,6 +668,24 @@ export default function Page({ }; }, [isAddingNote, shadowBoxWidth, shadowBoxHeight]); + // Listen for a global event to start the add-note flow from outside components + useEffect(() => { + const handleStartAddNote = () => { + setIsAddingNote(true); + const shadowBox = document.getElementById("shadow-box"); + if (shadowBox) { + shadowBox.style.display = "block"; + shadowBox.style.left = `${position.current.x - shadowBoxWidth / 2}px`; + shadowBox.style.top = `${position.current.y - shadowBoxHeight / 2}px`; + } + }; + + window.addEventListener("lf:start-add-note", handleStartAddNote); + return () => { + window.removeEventListener("lf:start-add-note", handleStartAddNote); + }; + }, [shadowBoxWidth, shadowBoxHeight]); + const MIN_ZOOM = 0.25; const MAX_ZOOM = 2; const fitViewOptions = { diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/categoryDisclouse.tsx b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/categoryDisclouse.tsx index a46f22404cb9..a7158f99ff10 100644 --- a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/categoryDisclouse.tsx +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/categoryDisclouse.tsx @@ -73,7 +73,7 @@ export const CategoryDisclosure = memo(function CategoryDisclosure({
diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/categoryGroup.tsx b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/categoryGroup.tsx index 76d9a08f8daa..a631a19e0cb4 100644 --- a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/categoryGroup.tsx +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/categoryGroup.tsx @@ -27,7 +27,7 @@ export const CategoryGroup = memo(function CategoryGroup({ return ( {ENABLE_NEW_SIDEBAR && ( - + Components +
{toggles.map((toggle) => (
diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/searchConfigTrigger.tsx b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/searchConfigTrigger.tsx index 0a10bf6427d0..881b0726555c 100644 --- a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/searchConfigTrigger.tsx +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/searchConfigTrigger.tsx @@ -12,18 +12,17 @@ export const SearchConfigTrigger = ({ setShowConfig, }: SearchConfigTriggerProps) => { return ( -
+
diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/searchInput.tsx b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/searchInput.tsx index c5c568b51159..99c6cb00642e 100644 --- a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/searchInput.tsx +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/searchInput.tsx @@ -1,6 +1,6 @@ import { memo } from "react"; -import { ForwardedIconComponent } from "@/components/common/genericIconComponent"; import { Input } from "@/components/ui/input"; +import { ENABLE_NEW_SIDEBAR } from "@/customization/feature-flags"; import ShortcutDisplay from "../../nodeToolbarComponent/shortcutDisplay"; export const SearchInput = memo(function SearchInput({ @@ -19,7 +19,7 @@ export const SearchInput = memo(function SearchInput({ handleInputChange: (event: React.ChangeEvent) => void; }) { return ( -
+
{!isInputFocused && search === "" && ( -
+
diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarDraggableComponent.tsx b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarDraggableComponent.tsx index 683e4e06fa59..9525241e8b5e 100644 --- a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarDraggableComponent.tsx +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarDraggableComponent.tsx @@ -131,17 +131,17 @@ export const SidebarDraggableComponent = forwardRef( data-tooltip-id={itemName} tabIndex={0} onKeyDown={handleKeyDown} - className="m-[1px] rounded-md outline-none ring-ring focus-visible:ring-1" + className="rounded-md outline-none ring-ring focus-visible:ring-1" data-testid={`${sectionName.toLowerCase()}_${display_name.toLowerCase()}_draggable`} >
diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarFooterButtons.tsx b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarFooterButtons.tsx index 0cec6d12fa76..eb8f291d3b8f 100644 --- a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarFooterButtons.tsx +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarFooterButtons.tsx @@ -20,7 +20,7 @@ const SidebarMenuButtons = ({ }; return ( - <> +
{ENABLE_NEW_SIDEBAR && activeSection === "mcp" ? ( <> @@ -29,7 +29,7 @@ const SidebarMenuButtons = ({ disabled={isLoading} onClick={handleAddMcpServerClick} data-testid="sidebar-add-mcp-server-button" - className="flex items-center gap-2" + className="flex items-center gap-2 w-full p-0" > ) : ( - - - + // + + // )} - +
); }; diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarHeader.tsx b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarHeader.tsx index 2329f47b47cd..d9e1b5fad91a 100644 --- a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarHeader.tsx +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarHeader.tsx @@ -33,7 +33,7 @@ export const SidebarHeaderComponent = memo(function SidebarHeaderComponent({ resetFilters, }: SidebarHeaderComponentProps) { return ( - + {!ENABLE_NEW_SIDEBAR && (
diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarItemsList.tsx b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarItemsList.tsx index 945b07c50d39..7dfde7838838 100644 --- a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarItemsList.tsx +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarItemsList.tsx @@ -16,7 +16,7 @@ const SidebarItemsList = ({ sensitiveSort, }) => { return ( -
+
{Object.keys(dataFilter[item.name]) .sort((a, b) => { const itemA = dataFilter[item.name][a]; diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarSegmentedNav.tsx b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarSegmentedNav.tsx index ecc1abc06665..e1c37e934821 100644 --- a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarSegmentedNav.tsx +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/components/sidebarSegmentedNav.tsx @@ -1,5 +1,7 @@ +import { useEffect, useState } from "react"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; import ShadTooltip from "@/components/common/shadTooltipComponent"; +import { Separator } from "@/components/ui/separator"; import { SidebarMenu, SidebarMenuButton, @@ -44,51 +46,89 @@ export const NAV_ITEMS: NavItem[] = [ label: "Bundles", tooltip: "Bundles", }, + { + id: "add_note", + icon: "sticky-note", + label: "Sticky Notes", + tooltip: "Add Sticky Notes", + }, ]; -export default function SidebarSegmentedNav() { +const SidebarSegmentedNav = () => { const { activeSection, setActiveSection, toggleSidebar, open } = useSidebar(); const { focusSearch, setSearch } = useSearchContext(); + const [isAddNoteActive, setIsAddNoteActive] = useState(false); + const handleAddNote = () => { + window.dispatchEvent(new Event("lf:start-add-note")); + setIsAddNoteActive(true); + }; + + useEffect(() => { + const onEnd = () => setIsAddNoteActive(false); + window.addEventListener("lf:end-add-note", onEnd); + return () => window.removeEventListener("lf:end-add-note", onEnd); + }, []); + return (
- + {NAV_ITEMS.map((item) => ( - - - { - setSearch?.(""); - if (activeSection === item.id && open) { - toggleSidebar(); - } else { - setActiveSection(item.id); - if (!open) { - toggleSidebar(); + <> + {item.id === "add_note" && } + + + { + if (item.id === "add_note") { + e.stopPropagation(); + handleAddNote(); + return; } - // Focus search input when search section is selected - if (item.id === "search") { - // Add a small delay to ensure the sidebar is open and input is rendered - setTimeout(() => focusSearch(), 100); + + setSearch?.(""); + if (activeSection === item.id && open) { + toggleSidebar(); + } else { + setActiveSection(item.id); + if (!open) { + toggleSidebar(); + } + if (item.id === "search") { + setTimeout(() => focusSearch(), 100); + } } + }} + isActive={ + item.id === "add_note" + ? isAddNoteActive + : activeSection === item.id } - }} - isActive={activeSection === item.id} - className={cn( - "flex h-8 w-8 items-center justify-center rounded-md p-0 transition-all duration-200", - activeSection === item.id - ? "bg-accent text-accent-foreground" - : "text-muted-foreground hover:bg-accent hover:text-accent-foreground", - )} - data-testid={`sidebar-nav-${item.id}`} - > - - {item.label} - - - + className={cn( + "flex h-8 w-8 items-center justify-center rounded-md p-0 transition-all duration-200", + ( + item.id === "add_note" + ? isAddNoteActive + : activeSection === item.id + ) + ? "bg-accent text-accent-foreground" + : "text-muted-foreground hover:bg-accent hover:text-accent-foreground", + )} + data-testid={`sidebar-nav-${item.id}`} + > + + {item.label} + + + + ))}
); -} +}; + +export default SidebarSegmentedNav; diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx index 60dcf6148ad3..6fdf4a4a4c23 100644 --- a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx @@ -12,6 +12,8 @@ import { } from "react"; import { useHotkeys } from "react-hotkeys-hook"; import { useShallow } from "zustand/react/shallow"; +import ForwardedIconComponent from "@/components/common/genericIconComponent"; +import { Button } from "@/components/ui/button"; import { Sidebar, SidebarContent, @@ -669,6 +671,21 @@ export function FlowSidebarComponent({ isLoading }: FlowSidebarComponentProps) { setShowConfig={setShowConfig} /> )} + {showComponents && ( + + )} ) : ( +