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("_")