From 2f629c63a6cb4e3bd5160c71ef9cd21bd3be4b23 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Wed, 27 Nov 2024 17:49:42 -0300 Subject: [PATCH] fix: make shortcut change work for shortcuts with two words, added sidebar shortcut, fix categories order (#4831) * Removed sidebar shortcut * Fixed restore default shortcut * Added new shortcut and fixed the freezePath one * Added shortcut to sidebar opening * Added shortcut_mod for correct restore, added shortcut for sidebar * Fixed freezePath shortcut name * Refactored the shortcutName get to get the correct shortcut * Added new toggleSidebar and fixed freezePath name * Fix categories order * Fix custom_component not showing the correct empty results * Add toCamelCase function * Refactor shortcut to work with more than one word * Removed unused console.log * fix ctrl not appearing --- .../components/renderKey/index.tsx | 6 +- .../components/FlowMenu/index.tsx | 2 +- .../core/flowToolbarComponent/index.tsx | 2 +- src/frontend/src/components/ui/sidebar.tsx | 38 +++++++------ src/frontend/src/constants/constants.ts | 56 ++++++++++--------- .../components/flowSidebarComponent/index.tsx | 19 ++++--- .../hooks/use-shortcuts.tsx | 14 +++-- .../EditShortcutButton/index.tsx | 23 ++++---- .../pages/ShortcutsPage/index.tsx | 4 +- src/frontend/src/stores/shortcuts.ts | 20 ++++--- src/frontend/src/types/store/index.ts | 15 ++--- src/frontend/src/utils/buildUtils.ts | 3 - src/frontend/src/utils/utils.ts | 7 +++ 13 files changed, 115 insertions(+), 94 deletions(-) diff --git a/src/frontend/src/components/common/renderIconComponent/components/renderKey/index.tsx b/src/frontend/src/components/common/renderIconComponent/components/renderKey/index.tsx index 384ce5f22..4793596c2 100644 --- a/src/frontend/src/components/common/renderIconComponent/components/renderKey/index.tsx +++ b/src/frontend/src/components/common/renderIconComponent/components/renderKey/index.tsx @@ -19,18 +19,20 @@ export default function RenderKey({ /> ) : check === "ctrl" && IS_MAC ? ( + ) : check === "mod" && !IS_MAC ? ( + Ctrl ) : check === "alt" && IS_MAC ? ( - ) : check === "cmd" ? ( + ) : (check === "mod" && IS_MAC) || check === "cmd" ? ( ) : ( - {value} + {value.toUpperCase()} )} ); 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 271c9a616..e94c1647a 100644 --- a/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx +++ b/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx @@ -100,7 +100,7 @@ export const MenuBar = ({}: {}): JSX.Element => { }); }; - const changes = useShortcutsStore((state) => state.changes); + const changes = useShortcutsStore((state) => state.changesSave); useHotkeys(changes, handleSave, { preventDefault: true }); return currentFlow && onFlowPage ? ( diff --git a/src/frontend/src/components/core/flowToolbarComponent/index.tsx b/src/frontend/src/components/core/flowToolbarComponent/index.tsx index e15cc8539..88211cb74 100644 --- a/src/frontend/src/components/core/flowToolbarComponent/index.tsx +++ b/src/frontend/src/components/core/flowToolbarComponent/index.tsx @@ -38,7 +38,7 @@ export default function FlowToolbar(): JSX.Element { setOpenShareModal((oldState) => !oldState); } - const openPlayground = useShortcutsStore((state) => state.open); + const openPlayground = useShortcutsStore((state) => state.openPlayground); const api = useShortcutsStore((state) => state.api); const flow = useShortcutsStore((state) => state.flow); diff --git a/src/frontend/src/components/ui/sidebar.tsx b/src/frontend/src/components/ui/sidebar.tsx index 4563a45b2..1ea333ed9 100644 --- a/src/frontend/src/components/ui/sidebar.tsx +++ b/src/frontend/src/components/ui/sidebar.tsx @@ -6,6 +6,9 @@ import { PanelLeft } from "lucide-react"; import * as React from "react"; import { useIsMobile } from "@/hooks/use-mobile"; +import { useHotkeys } from "react-hotkeys-hook"; +import isWrappedWithClass from "../../pages/FlowPage/components/PageComponent/utils/is-wrapped-with-class"; +import { useShortcutsStore } from "../../stores/shortcuts"; import { cn } from "../../utils/utils"; import ShadTooltip from "../common/shadTooltipComponent"; import { Button } from "./button"; @@ -18,7 +21,6 @@ const SIDEBAR_COOKIE_NAME = "sidebar:state"; const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; const SIDEBAR_WIDTH = "19rem"; const SIDEBAR_WIDTH_ICON = "4rem"; -const SIDEBAR_KEYBOARD_SHORTCUT = "b"; type SidebarContext = { state: "expanded" | "collapsed"; @@ -84,23 +86,7 @@ const SidebarProvider = React.forwardRef< // Helper to toggle the sidebar. const toggleSidebar = React.useCallback(() => { return setOpen((open) => !open); - }, [setOpen]); - - // Adds a keyboard shortcut to toggle the sidebar. - React.useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - if ( - event.key === SIDEBAR_KEYBOARD_SHORTCUT && - (event.metaKey || event.ctrlKey) - ) { - event.preventDefault(); - toggleSidebar(); - } - }; - - window.addEventListener("keydown", handleKeyDown); - return () => window.removeEventListener("keydown", handleKeyDown); - }, [toggleSidebar]); + }, [setOpen, open]); // We add a state so that we can do data-state="expanded" or "collapsed". // This makes it easier to style the sidebar with Tailwind classes. @@ -117,6 +103,22 @@ const SidebarProvider = React.forwardRef< [state, open, setOpen, toggleSidebar, defaultOpen], ); + const toggleSidebarShortcut = useShortcutsStore( + (state) => state.toggleSidebar, + ); + + useHotkeys( + toggleSidebarShortcut, + (e: KeyboardEvent) => { + if (isWrappedWithClass(e, "noflow")) return; + e.preventDefault(); + toggleSidebar(); + }, + { + preventDefault: true, + }, + ); + return ( diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts index 96743d29b..1a79b617d 100644 --- a/src/frontend/src/constants/constants.ts +++ b/src/frontend/src/constants/constants.ts @@ -761,103 +761,107 @@ export const IS_MAC = navigator.userAgent.toUpperCase().includes("MAC"); export const defaultShortcuts = [ { name: "Advanced Settings", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Shift + A`, + shortcut: "mod+shift+a", }, { name: "Minimize", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Q`, + shortcut: "mod+q", }, { name: "Code", - shortcut: `Space`, + shortcut: "space", }, { name: "Copy", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + C`, + shortcut: "mod+c", }, { name: "Duplicate", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + D`, + shortcut: "mod+d", }, { name: "Component Share", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Shift + S`, + shortcut: "mod+shift+s", }, { name: "Docs", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Shift + D`, + shortcut: "mod+shift+d", }, { name: "Changes Save", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + S`, + shortcut: "mod+s", }, { name: "Save Component", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Alt + S`, + shortcut: "mod+alt+s", }, { name: "Delete", - shortcut: "Backspace", + shortcut: "backspace", }, { name: "Open playground", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + K`, + shortcut: "mod+k", }, { name: "Undo", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Z`, + shortcut: "mod+z", }, { name: "Redo", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Y`, + shortcut: "mod+y", }, { name: "Group", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + G`, + shortcut: "mod+g", }, { name: "Cut", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + X`, + shortcut: "mod+x", }, { name: "Paste", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + V`, + shortcut: "mod+v", }, { name: "API", - shortcut: `R`, + shortcut: "r", }, { name: "Download", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + J`, + shortcut: "mod+j", }, { name: "Update", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + U`, + shortcut: "mod+u", }, { name: "Freeze", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + F`, + shortcut: "mod+f", }, { name: "Freeze Path", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Shift + F`, + shortcut: "mod+shift+f", }, { name: "Flow Share", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + B`, + shortcut: "mod+shift+b", }, { name: "Play", - shortcut: `P`, + shortcut: "p", }, { name: "Output Inspection", - shortcut: `O`, + shortcut: "o", }, { name: "Tool Mode", - shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Shift + M`, + shortcut: "mod+shift+m", + }, + { + name: "Toggle Sidebar", + shortcut: "mod+b", }, ]; @@ -915,7 +919,7 @@ export const TEXT_FIELD_TYPES: string[] = ["str", "SecretStr"]; export const NODE_WIDTH = 384; export const NODE_HEIGHT = NODE_WIDTH * 3; -export const SHORTCUT_KEYS = ["cmd", "ctrl", "alt", "shift"]; +export const SHORTCUT_KEYS = ["cmd", "ctrl", "mod", "alt", "shift"]; export const SERVER_HEALTH_INTERVAL = 10000; export const REFETCH_SERVER_HEALTH_INTERVAL = 20000; diff --git a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx index 869e643d8..170970fc4 100644 --- a/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx @@ -156,8 +156,11 @@ export function FlowSidebarComponent() { }, [search, getFilterEdge]); const hasResults = useMemo(() => { - return Object.values(dataFilter).some( - (category) => Object.keys(category).length > 0, + return Object.entries(dataFilter).some( + ([category, items]) => + Object.keys(items).length > 0 && + (CATEGORIES.find((c) => c.name === category) || + BUNDLES.find((b) => b.name === category)), ); }, [dataFilter]); const [sortedCategories, setSortedCategories] = useState([]); @@ -191,13 +194,13 @@ export function FlowSidebarComponent() { setSortedCategories( Object.keys(filteredData) .filter( - (category) => Object.keys(filteredData[category]).length > 0, + (category) => + Object.keys(filteredData[category]).length > 0 && + (CATEGORIES.find((c) => c.name === category) || + BUNDLES.find((b) => b.name === category)), ) - .toSorted( - (a, b) => - fuseCategories.findIndex((value) => value === a) ?? - 0 - fuseCategories.findIndex((value) => value === b) ?? - 0, + .toSorted((a, b) => + fuseCategories.indexOf(b) < fuseCategories.indexOf(a) ? 1 : -1, ), ); } diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/hooks/use-shortcuts.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/hooks/use-shortcuts.tsx index 9c427e777..495bc33e3 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/hooks/use-shortcuts.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/hooks/use-shortcuts.tsx @@ -35,16 +35,16 @@ export default function useShortcuts({ minimizeFunction?: () => void; activateToolMode?: () => void; }) { - const advanced = useShortcutsStore((state) => state.advanced); + const advancedSettings = useShortcutsStore((state) => state.advancedSettings); const minimize = useShortcutsStore((state) => state.minimize); - const component = useShortcutsStore((state) => state.component); - const save = useShortcutsStore((state) => state.save); + const componentShare = useShortcutsStore((state) => state.componentShare); + const save = useShortcutsStore((state) => state.saveComponent); const docs = useShortcutsStore((state) => state.docs); const code = useShortcutsStore((state) => state.code); const group = useShortcutsStore((state) => state.group); const download = useShortcutsStore((state) => state.download); const freeze = useShortcutsStore((state) => state.freeze); - const freezeAll = useShortcutsStore((state) => state.FreezePath); + const freezeAll = useShortcutsStore((state) => state.freezePath); const toolMode = useShortcutsStore((state) => state.toolMode); function handleFreezeAll(e: KeyboardEvent) { @@ -125,9 +125,11 @@ export default function useShortcuts({ useHotkeys(minimize, handleMinimizeWShortcut, { preventDefault: true }); useHotkeys(group, handleGroupWShortcut, { preventDefault: true }); - useHotkeys(component, handleShareWShortcut, { preventDefault: true }); + useHotkeys(componentShare, handleShareWShortcut, { preventDefault: true }); useHotkeys(code, handleCodeWShortcut, { preventDefault: true }); - useHotkeys(advanced, handleAdvancedWShortcut, { preventDefault: true }); + useHotkeys(advancedSettings, handleAdvancedWShortcut, { + preventDefault: true, + }); useHotkeys(save, handleSaveWShortcut, { preventDefault: true }); useHotkeys(docs, handleDocsWShortcut, { preventDefault: true }); useHotkeys(download, handleDownloadWShortcut, { preventDefault: true }); diff --git a/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/EditShortcutButton/index.tsx b/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/EditShortcutButton/index.tsx index 44883d07c..df6012a1a 100644 --- a/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/EditShortcutButton/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/EditShortcutButton/index.tsx @@ -6,7 +6,7 @@ import ForwardedIconComponent from "../../../../../components/common/genericIcon import { Button } from "../../../../../components/ui/button"; import BaseModal from "../../../../../modals/baseModal"; import { useShortcutsStore } from "../../../../../stores/shortcuts"; -import { toTitleCase } from "../../../../../utils/utils"; +import { toCamelCase, toTitleCase } from "../../../../../utils/utils"; export default function EditShortcutButton({ children, @@ -28,9 +28,7 @@ export default function EditShortcutButton({ let shortcutInitialValue = defaultShortcuts.length > 0 ? defaultShortcuts.find( - (s) => - s.name.split(" ")[0].toLowerCase().toLowerCase() === - shortcut[0]?.split(" ")[0].toLowerCase(), + (s) => toCamelCase(s.name) === toCamelCase(shortcut[0]), )?.shortcut : ""; const [key, setKey] = useState(null); @@ -55,12 +53,6 @@ export default function EditShortcutButton({ function editCombination(): void { if (key) { if (canEditCombination(key)) { - const newCombination = defaultShortcuts.map((s) => { - if (s.name === shortcut[0]) { - return { name: s.name, shortcut: key }; - } - return { name: s.name, shortcut: s.shortcut }; - }); const fixCombination = key.split(" "); if ( fixCombination[0].toLowerCase().includes("ctrl") || @@ -68,7 +60,16 @@ export default function EditShortcutButton({ ) { fixCombination[0] = "mod"; } - const shortcutName = shortcut[0].split(" ")[0].toLowerCase(); + const newCombination = defaultShortcuts.map((s) => { + if (s.name === shortcut[0]) { + return { + name: s.name, + shortcut: fixCombination.join("").toLowerCase(), + }; + } + return { name: s.name, shortcut: s.shortcut }; + }); + const shortcutName = toCamelCase(shortcut[0]); setUniqueShortcut(shortcutName, fixCombination.join("").toLowerCase()); setShortcuts(newCombination); localStorage.setItem( diff --git a/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/index.tsx index 4e292c68e..04415db31 100644 --- a/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/ShortcutsPage/index.tsx @@ -1,3 +1,4 @@ +import { toCamelCase } from "@/utils/utils"; import { ColDef } from "ag-grid-community"; import { useEffect, useState } from "react"; import ForwardedIconComponent from "../../../../components/common/genericIconComponent"; @@ -40,7 +41,6 @@ export default function ShortcutsPage() { setNodesRowData(shortcuts); }, [shortcuts]); - const combinationToEdit = shortcuts.filter((s) => s.name === selectedRows[0]); const [open, setOpen] = useState(false); const updateUniqueShortcut = useShortcutsStore( (state) => state.updateUniqueShortcut, @@ -49,7 +49,7 @@ export default function ShortcutsPage() { function handleRestore() { setShortcuts(defaultShortcuts); defaultShortcuts.forEach(({ name, shortcut }) => { - const fixedName = name.split(" ")[0].toLowerCase(); + const fixedName = toCamelCase(name); updateUniqueShortcut(fixedName, shortcut); }); localStorage.removeItem("langflow-shortcuts"); diff --git a/src/frontend/src/stores/shortcuts.ts b/src/frontend/src/stores/shortcuts.ts index 13ee63105..5df54c092 100644 --- a/src/frontend/src/stores/shortcuts.ts +++ b/src/frontend/src/stores/shortcuts.ts @@ -1,3 +1,4 @@ +import { toCamelCase } from "@/utils/utils"; import { create } from "zustand"; import { defaultShortcuts } from "../constants/constants"; import { shortcutsStoreType } from "../types/store"; @@ -7,21 +8,21 @@ export const useShortcutsStore = create((set, get) => ({ setShortcuts: (newShortcuts) => { set({ shortcuts: newShortcuts }); }, - output: "o", + outputInspection: "o", play: "p", - flow: "mod+b", + flow: "mod+shift+b", undo: "mod+z", redo: "mod+y", - open: "mod+k", - advanced: "mod+shift+a", + openPlayground: "mod+k", + advancedSettings: "mod+shift+a", minimize: "mod+shift+q", code: "space", copy: "mod+c", duplicate: "mod+d", - component: "mod+shift+s", + componentShare: "mod+shift+s", docs: "mod+shift+d", - changes: "mod+s", - save: "mod+alt+s", + changesSave: "mod+s", + saveComponent: "mod+alt+s", delete: "backspace", group: "mod+g", cut: "mod+x", @@ -30,8 +31,9 @@ export const useShortcutsStore = create((set, get) => ({ update: "mod+u", download: "mod+j", freeze: "mod+f", - FreezePath: "mod+shift+f", + freezePath: "mod+shift+f", toolMode: "mod+shift+m", + toggleSidebar: "mod+b", updateUniqueShortcut: (name, combination) => { set({ [name]: combination, @@ -42,7 +44,7 @@ export const useShortcutsStore = create((set, get) => ({ const savedShortcuts = localStorage.getItem("langflow-shortcuts"); const savedArr = JSON.parse(savedShortcuts!); savedArr.forEach(({ name, shortcut }) => { - let shortcutName = name.split(" ")[0].toLowerCase(); + let shortcutName = toCamelCase(name); set({ [shortcutName]: shortcut, }); diff --git a/src/frontend/src/types/store/index.ts b/src/frontend/src/types/store/index.ts index dbe67074e..e26f100c0 100644 --- a/src/frontend/src/types/store/index.ts +++ b/src/frontend/src/types/store/index.ts @@ -21,30 +21,31 @@ export type StoreComponentResponse = { export type shortcutsStoreType = { updateUniqueShortcut: (name: string, combination: string) => void; - output: string; + outputInspection: string; play: string; flow: string; group: string; cut: string; paste: string; api: string; - open: string; + openPlayground: string; undo: string; redo: string; - advanced: string; + advancedSettings: string; minimize: string; code: string; copy: string; duplicate: string; - component: string; + componentShare: string; docs: string; - changes: string; - save: string; + changesSave: string; + saveComponent: string; delete: string; update: string; download: string; freeze: string; - FreezePath: string; + toggleSidebar: string; + freezePath: string; toolMode: string; shortcuts: Array<{ name: string; diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index 0702f5717..76251f998 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -200,9 +200,6 @@ export async function buildFlowVertices({ ids.forEach((id) => verticesStartTimeMs.set(id, Date.now())); }; - console.log("type", type); - console.log("data", data); - switch (type) { case "vertices_sorted": { const verticesToRun = data.to_run; diff --git a/src/frontend/src/utils/utils.ts b/src/frontend/src/utils/utils.ts index 880875cdd..91f17616a 100644 --- a/src/frontend/src/utils/utils.ts +++ b/src/frontend/src/utils/utils.ts @@ -27,6 +27,13 @@ export function cn(...inputs: ClassValue[]): string { return twMerge(clsx(inputs)); } +export function toCamelCase(str: string): string { + return str + .split(" ") + .map((s, index) => (index !== 0 ? toNormalCase(s) : s.toLowerCase())) + .join(""); +} + export function toNormalCase(str: string): string { let result = str .split("_")