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 ? (
<span></span>
) : check === "mod" && !IS_MAC ? (
<span>Ctrl</span>
) : check === "alt" && IS_MAC ? (
<ForwardedIconComponent
name="OptionIcon"
className={cn(tableRender ? "h-4 w-4" : "h-3 w-3")}
/>
) : check === "cmd" ? (
) : (check === "mod" && IS_MAC) || check === "cmd" ? (
<ForwardedIconComponent
name="Command"
className={cn(tableRender ? "h-4 w-4" : "h-3 w-3")}
/>
) : (
<span>{value}</span>
<span>{value.toUpperCase()}</span>
)}
</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 });
return currentFlow && onFlowPage ? (

View file

@ -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);

View file

@ -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 (
<SidebarContext.Provider value={contextValue}>
<TooltipProvider delayDuration={0}>

View file

@ -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;

View file

@ -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<string[]>([]);
@ -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,
),
);
}

View file

@ -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 });

View file

@ -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<string | null>(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(

View file

@ -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");

View file

@ -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<shortcutsStoreType>((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<shortcutsStoreType>((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<shortcutsStoreType>((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,
});

View file

@ -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;

View file

@ -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;

View file

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