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
This commit is contained in:
Lucas Oliveira 2024-11-27 17:49:42 -03:00 • committed by GitHub
commit 2f629c63a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 115 additions and 94 deletions

View file

@ -19,18 +19,20 @@ export default function RenderKey({
/> />
) : check === "ctrl" && IS_MAC ? ( ) : check === "ctrl" && IS_MAC ? (
<span>⌃</span> <span>⌃</span>
) : check === "mod" && !IS_MAC ? (
<span>Ctrl</span>
) : check === "alt" && IS_MAC ? ( ) : check === "alt" && IS_MAC ? (
<ForwardedIconComponent <ForwardedIconComponent
name="OptionIcon" name="OptionIcon"
className={cn(tableRender ? "h-4 w-4" : "h-3 w-3")} className={cn(tableRender ? "h-4 w-4" : "h-3 w-3")}
/> />
) : check === "cmd" ? ( ) : (check === "mod" && IS_MAC) || check === "cmd" ? (
<ForwardedIconComponent <ForwardedIconComponent
name="Command" name="Command"
className={cn(tableRender ? "h-4 w-4" : "h-3 w-3")} className={cn(tableRender ? "h-4 w-4" : "h-3 w-3")}
/> />
) : ( ) : (
<span>{value}</span> <span>{value.toUpperCase()}</span>
)} )}
</div> </div>
); );

View file

@ -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 }); useHotkeys(changes, handleSave, { preventDefault: true });
return currentFlow && onFlowPage ? ( return currentFlow && onFlowPage ? (

View file

@ -38,7 +38,7 @@ export default function FlowToolbar(): JSX.Element {
setOpenShareModal((oldState) => !oldState); setOpenShareModal((oldState) => !oldState);
} }
const openPlayground = useShortcutsStore((state) => state.open); const openPlayground = useShortcutsStore((state) => state.openPlayground);
const api = useShortcutsStore((state) => state.api); const api = useShortcutsStore((state) => state.api);
const flow = useShortcutsStore((state) => state.flow); const flow = useShortcutsStore((state) => state.flow);

View file

@ -6,6 +6,9 @@ import { PanelLeft } from "lucide-react";
import * as React from "react"; import * as React from "react";
import { useIsMobile } from "@/hooks/use-mobile"; 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 { cn } from "../../utils/utils";
import ShadTooltip from "../common/shadTooltipComponent"; import ShadTooltip from "../common/shadTooltipComponent";
import { Button } from "./button"; 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_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
const SIDEBAR_WIDTH = "19rem"; const SIDEBAR_WIDTH = "19rem";
const SIDEBAR_WIDTH_ICON = "4rem"; const SIDEBAR_WIDTH_ICON = "4rem";
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
type SidebarContext = { type SidebarContext = {
state: "expanded" | "collapsed"; state: "expanded" | "collapsed";
@ -84,23 +86,7 @@ const SidebarProvider = React.forwardRef<
// Helper to toggle the sidebar. // Helper to toggle the sidebar.
const toggleSidebar = React.useCallback(() => { const toggleSidebar = React.useCallback(() => {
return setOpen((open) => !open); return setOpen((open) => !open);
}, [setOpen]); }, [setOpen, open]);
// 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]);
// We add a state so that we can do data-state="expanded" or "collapsed". // 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. // This makes it easier to style the sidebar with Tailwind classes.
@ -117,6 +103,22 @@ const SidebarProvider = React.forwardRef<
[state, open, setOpen, toggleSidebar, defaultOpen], [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 ( return (
<SidebarContext.Provider value={contextValue}> <SidebarContext.Provider value={contextValue}>
<TooltipProvider delayDuration={0}> <TooltipProvider delayDuration={0}>

View file

@ -761,103 +761,107 @@ export const IS_MAC = navigator.userAgent.toUpperCase().includes("MAC");
export const defaultShortcuts = [ export const defaultShortcuts = [
{ {
name: "Advanced Settings", name: "Advanced Settings",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Shift + A`, shortcut: "mod+shift+a",
}, },
{ {
name: "Minimize", name: "Minimize",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Q`, shortcut: "mod+q",
}, },
{ {
name: "Code", name: "Code",
shortcut: `Space`, shortcut: "space",
}, },
{ {
name: "Copy", name: "Copy",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + C`, shortcut: "mod+c",
}, },
{ {
name: "Duplicate", name: "Duplicate",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + D`, shortcut: "mod+d",
}, },
{ {
name: "Component Share", name: "Component Share",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Shift + S`, shortcut: "mod+shift+s",
}, },
{ {
name: "Docs", name: "Docs",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Shift + D`, shortcut: "mod+shift+d",
}, },
{ {
name: "Changes Save", name: "Changes Save",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + S`, shortcut: "mod+s",
}, },
{ {
name: "Save Component", name: "Save Component",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Alt + S`, shortcut: "mod+alt+s",
}, },
{ {
name: "Delete", name: "Delete",
shortcut: "Backspace", shortcut: "backspace",
}, },
{ {
name: "Open playground", name: "Open playground",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + K`, shortcut: "mod+k",
}, },
{ {
name: "Undo", name: "Undo",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Z`, shortcut: "mod+z",
}, },
{ {
name: "Redo", name: "Redo",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Y`, shortcut: "mod+y",
}, },
{ {
name: "Group", name: "Group",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + G`, shortcut: "mod+g",
}, },
{ {
name: "Cut", name: "Cut",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + X`, shortcut: "mod+x",
}, },
{ {
name: "Paste", name: "Paste",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + V`, shortcut: "mod+v",
}, },
{ {
name: "API", name: "API",
shortcut: `R`, shortcut: "r",
}, },
{ {
name: "Download", name: "Download",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + J`, shortcut: "mod+j",
}, },
{ {
name: "Update", name: "Update",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + U`, shortcut: "mod+u",
}, },
{ {
name: "Freeze", name: "Freeze",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + F`, shortcut: "mod+f",
}, },
{ {
name: "Freeze Path", name: "Freeze Path",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Shift + F`, shortcut: "mod+shift+f",
}, },
{ {
name: "Flow Share", name: "Flow Share",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + B`, shortcut: "mod+shift+b",
}, },
{ {
name: "Play", name: "Play",
shortcut: `P`, shortcut: "p",
}, },
{ {
name: "Output Inspection", name: "Output Inspection",
shortcut: `O`, shortcut: "o",
}, },
{ {
name: "Tool Mode", 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_WIDTH = 384;
export const NODE_HEIGHT = NODE_WIDTH * 3; 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 SERVER_HEALTH_INTERVAL = 10000;
export const REFETCH_SERVER_HEALTH_INTERVAL = 20000; export const REFETCH_SERVER_HEALTH_INTERVAL = 20000;

View file

@ -156,8 +156,11 @@ export function FlowSidebarComponent() {
}, [search, getFilterEdge]); }, [search, getFilterEdge]);
const hasResults = useMemo(() => { const hasResults = useMemo(() => {
return Object.values(dataFilter).some( return Object.entries(dataFilter).some(
(category) => Object.keys(category).length > 0, ([category, items]) =>
Object.keys(items).length > 0 &&
(CATEGORIES.find((c) => c.name === category) ||
BUNDLES.find((b) => b.name === category)),
); );
}, [dataFilter]); }, [dataFilter]);
const [sortedCategories, setSortedCategories] = useState<string[]>([]); const [sortedCategories, setSortedCategories] = useState<string[]>([]);
@ -191,13 +194,13 @@ export function FlowSidebarComponent() {
setSortedCategories( setSortedCategories(
Object.keys(filteredData) Object.keys(filteredData)
.filter( .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( .toSorted((a, b) =>
(a, b) => fuseCategories.indexOf(b) < fuseCategories.indexOf(a) ? 1 : -1,
fuseCategories.findIndex((value) => value === a) ??
0 - fuseCategories.findIndex((value) => value === b) ??
0,
), ),
); );
} }

View file

@ -35,16 +35,16 @@ export default function useShortcuts({
minimizeFunction?: () => void; minimizeFunction?: () => void;
activateToolMode?: () => void; activateToolMode?: () => void;
}) { }) {
const advanced = useShortcutsStore((state) => state.advanced); const advancedSettings = useShortcutsStore((state) => state.advancedSettings);
const minimize = useShortcutsStore((state) => state.minimize); const minimize = useShortcutsStore((state) => state.minimize);
const component = useShortcutsStore((state) => state.component); const componentShare = useShortcutsStore((state) => state.componentShare);
const save = useShortcutsStore((state) => state.save); const save = useShortcutsStore((state) => state.saveComponent);
const docs = useShortcutsStore((state) => state.docs); const docs = useShortcutsStore((state) => state.docs);
const code = useShortcutsStore((state) => state.code); const code = useShortcutsStore((state) => state.code);
const group = useShortcutsStore((state) => state.group); const group = useShortcutsStore((state) => state.group);
const download = useShortcutsStore((state) => state.download); const download = useShortcutsStore((state) => state.download);
const freeze = useShortcutsStore((state) => state.freeze); const freeze = useShortcutsStore((state) => state.freeze);
const freezeAll = useShortcutsStore((state) => state.FreezePath); const freezeAll = useShortcutsStore((state) => state.freezePath);
const toolMode = useShortcutsStore((state) => state.toolMode); const toolMode = useShortcutsStore((state) => state.toolMode);
function handleFreezeAll(e: KeyboardEvent) { function handleFreezeAll(e: KeyboardEvent) {
@ -125,9 +125,11 @@ export default function useShortcuts({
useHotkeys(minimize, handleMinimizeWShortcut, { preventDefault: true }); useHotkeys(minimize, handleMinimizeWShortcut, { preventDefault: true });
useHotkeys(group, handleGroupWShortcut, { preventDefault: true }); useHotkeys(group, handleGroupWShortcut, { preventDefault: true });
useHotkeys(component, handleShareWShortcut, { preventDefault: true }); useHotkeys(componentShare, handleShareWShortcut, { preventDefault: true });
useHotkeys(code, handleCodeWShortcut, { preventDefault: true }); useHotkeys(code, handleCodeWShortcut, { preventDefault: true });
useHotkeys(advanced, handleAdvancedWShortcut, { preventDefault: true }); useHotkeys(advancedSettings, handleAdvancedWShortcut, {
preventDefault: true,
});
useHotkeys(save, handleSaveWShortcut, { preventDefault: true }); useHotkeys(save, handleSaveWShortcut, { preventDefault: true });
useHotkeys(docs, handleDocsWShortcut, { preventDefault: true }); useHotkeys(docs, handleDocsWShortcut, { preventDefault: true });
useHotkeys(download, handleDownloadWShortcut, { preventDefault: true }); useHotkeys(download, handleDownloadWShortcut, { preventDefault: true });

View file

@ -6,7 +6,7 @@ import ForwardedIconComponent from "../../../../../components/common/genericIcon
import { Button } from "../../../../../components/ui/button"; import { Button } from "../../../../../components/ui/button";
import BaseModal from "../../../../../modals/baseModal"; import BaseModal from "../../../../../modals/baseModal";
import { useShortcutsStore } from "../../../../../stores/shortcuts"; import { useShortcutsStore } from "../../../../../stores/shortcuts";
import { toTitleCase } from "../../../../../utils/utils"; import { toCamelCase, toTitleCase } from "../../../../../utils/utils";
export default function EditShortcutButton({ export default function EditShortcutButton({
children, children,
@ -28,9 +28,7 @@ export default function EditShortcutButton({
let shortcutInitialValue = let shortcutInitialValue =
defaultShortcuts.length > 0 defaultShortcuts.length > 0
? defaultShortcuts.find( ? defaultShortcuts.find(
(s) => (s) => toCamelCase(s.name) === toCamelCase(shortcut[0]),
s.name.split(" ")[0].toLowerCase().toLowerCase() ===
shortcut[0]?.split(" ")[0].toLowerCase(),
)?.shortcut )?.shortcut
: ""; : "";
const [key, setKey] = useState<string | null>(null); const [key, setKey] = useState<string | null>(null);
@ -55,12 +53,6 @@ export default function EditShortcutButton({
function editCombination(): void { function editCombination(): void {
if (key) { if (key) {
if (canEditCombination(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(" "); const fixCombination = key.split(" ");
if ( if (
fixCombination[0].toLowerCase().includes("ctrl") || fixCombination[0].toLowerCase().includes("ctrl") ||
@ -68,7 +60,16 @@ export default function EditShortcutButton({
) { ) {
fixCombination[0] = "mod"; 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()); setUniqueShortcut(shortcutName, fixCombination.join("").toLowerCase());
setShortcuts(newCombination); setShortcuts(newCombination);
localStorage.setItem( localStorage.setItem(

View file

@ -1,3 +1,4 @@
import { toCamelCase } from "@/utils/utils";
import { ColDef } from "ag-grid-community"; import { ColDef } from "ag-grid-community";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import ForwardedIconComponent from "../../../../components/common/genericIconComponent"; import ForwardedIconComponent from "../../../../components/common/genericIconComponent";
@ -40,7 +41,6 @@ export default function ShortcutsPage() {
setNodesRowData(shortcuts); setNodesRowData(shortcuts);
}, [shortcuts]); }, [shortcuts]);
const combinationToEdit = shortcuts.filter((s) => s.name === selectedRows[0]);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const updateUniqueShortcut = useShortcutsStore( const updateUniqueShortcut = useShortcutsStore(
(state) => state.updateUniqueShortcut, (state) => state.updateUniqueShortcut,
@ -49,7 +49,7 @@ export default function ShortcutsPage() {
function handleRestore() { function handleRestore() {
setShortcuts(defaultShortcuts); setShortcuts(defaultShortcuts);
defaultShortcuts.forEach(({ name, shortcut }) => { defaultShortcuts.forEach(({ name, shortcut }) => {
const fixedName = name.split(" ")[0].toLowerCase(); const fixedName = toCamelCase(name);
updateUniqueShortcut(fixedName, shortcut); updateUniqueShortcut(fixedName, shortcut);
}); });
localStorage.removeItem("langflow-shortcuts"); localStorage.removeItem("langflow-shortcuts");

View file

@ -1,3 +1,4 @@
import { toCamelCase } from "@/utils/utils";
import { create } from "zustand"; import { create } from "zustand";
import { defaultShortcuts } from "../constants/constants"; import { defaultShortcuts } from "../constants/constants";
import { shortcutsStoreType } from "../types/store"; import { shortcutsStoreType } from "../types/store";
@ -7,21 +8,21 @@ export const useShortcutsStore = create<shortcutsStoreType>((set, get) => ({
setShortcuts: (newShortcuts) => { setShortcuts: (newShortcuts) => {
set({ shortcuts: newShortcuts }); set({ shortcuts: newShortcuts });
}, },
output: "o", outputInspection: "o",
play: "p", play: "p",
flow: "mod+b", flow: "mod+shift+b",
undo: "mod+z", undo: "mod+z",
redo: "mod+y", redo: "mod+y",
open: "mod+k", openPlayground: "mod+k",
advanced: "mod+shift+a", advancedSettings: "mod+shift+a",
minimize: "mod+shift+q", minimize: "mod+shift+q",
code: "space", code: "space",
copy: "mod+c", copy: "mod+c",
duplicate: "mod+d", duplicate: "mod+d",
component: "mod+shift+s", componentShare: "mod+shift+s",
docs: "mod+shift+d", docs: "mod+shift+d",
changes: "mod+s", changesSave: "mod+s",
save: "mod+alt+s", saveComponent: "mod+alt+s",
delete: "backspace", delete: "backspace",
group: "mod+g", group: "mod+g",
cut: "mod+x", cut: "mod+x",
@ -30,8 +31,9 @@ export const useShortcutsStore = create<shortcutsStoreType>((set, get) => ({
update: "mod+u", update: "mod+u",
download: "mod+j", download: "mod+j",
freeze: "mod+f", freeze: "mod+f",
FreezePath: "mod+shift+f", freezePath: "mod+shift+f",
toolMode: "mod+shift+m", toolMode: "mod+shift+m",
toggleSidebar: "mod+b",
updateUniqueShortcut: (name, combination) => { updateUniqueShortcut: (name, combination) => {
set({ set({
[name]: combination, [name]: combination,
@ -42,7 +44,7 @@ export const useShortcutsStore = create<shortcutsStoreType>((set, get) => ({
const savedShortcuts = localStorage.getItem("langflow-shortcuts"); const savedShortcuts = localStorage.getItem("langflow-shortcuts");
const savedArr = JSON.parse(savedShortcuts!); const savedArr = JSON.parse(savedShortcuts!);
savedArr.forEach(({ name, shortcut }) => { savedArr.forEach(({ name, shortcut }) => {
let shortcutName = name.split(" ")[0].toLowerCase(); let shortcutName = toCamelCase(name);
set({ set({
[shortcutName]: shortcut, [shortcutName]: shortcut,
}); });

View file

@ -21,30 +21,31 @@ export type StoreComponentResponse = {
export type shortcutsStoreType = { export type shortcutsStoreType = {
updateUniqueShortcut: (name: string, combination: string) => void; updateUniqueShortcut: (name: string, combination: string) => void;
output: string; outputInspection: string;
play: string; play: string;
flow: string; flow: string;
group: string; group: string;
cut: string; cut: string;
paste: string; paste: string;
api: string; api: string;
open: string; openPlayground: string;
undo: string; undo: string;
redo: string; redo: string;
advanced: string; advancedSettings: string;
minimize: string; minimize: string;
code: string; code: string;
copy: string; copy: string;
duplicate: string; duplicate: string;
component: string; componentShare: string;
docs: string; docs: string;
changes: string; changesSave: string;
save: string; saveComponent: string;
delete: string; delete: string;
update: string; update: string;
download: string; download: string;
freeze: string; freeze: string;
FreezePath: string; toggleSidebar: string;
freezePath: string;
toolMode: string; toolMode: string;
shortcuts: Array<{ shortcuts: Array<{
name: string; name: string;

View file

@ -200,9 +200,6 @@ export async function buildFlowVertices({
ids.forEach((id) => verticesStartTimeMs.set(id, Date.now())); ids.forEach((id) => verticesStartTimeMs.set(id, Date.now()));
}; };
console.log("type", type);
console.log("data", data);
switch (type) { switch (type) {
case "vertices_sorted": { case "vertices_sorted": {
const verticesToRun = data.to_run; const verticesToRun = data.to_run;

View file

@ -27,6 +27,13 @@ export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs)); 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 { export function toNormalCase(str: string): string {
let result = str let result = str
.split("_") .split("_")