feat: add new freeze function to component (#2624)

* feat: Add new API endpoint for retrieving vertex order

This commit adds a new API endpoint, , which allows for retrieving the order of vertices in a graph. It includes the necessary functions and types to make the API request and process the response. This feature enhances the functionality of the application by providing the ability to retrieve and work with vertex order data.

* feat: Add missing semicolon in constants.ts

The commit adds a missing semicolon in the  file in the  directory. This fix ensures that the code is syntactically correct and prevents any potential issues that may arise from the missing semicolon.

* feat: Refactor usePostRetrieveVertexOrder to improve code readability and maintainability

* feat: Add FreezeAllSvg component for displaying a freeze all icon

This commit adds a new component called FreezeAllSvg, which is responsible for rendering an SVG icon representing the freeze all feature. The component accepts props for customizing the size and color of the icon. This addition enhances the visual representation of the freeze all functionality in the application.

* feat: Add freezeMultipleNodes method to FlowStoreType

This commit adds the `freezeMultipleNodes` method to the `FlowStoreType` interface in the `index.ts` file. The method takes an array of node IDs as input and freezes the corresponding nodes in the flow. This addition enhances the functionality of the flow store by providing the ability to freeze multiple nodes at once.

* feat: Update freezeMultipleNodes to use updateFreezeStatus and updateNodeInternals

Refactor the NodeToolbarComponent to use the updated freezeMultipleNodes method in the FlowStore. Instead of directly freezing the nodes, the method now calls updateFreezeStatus to update the freeze status of the nodes and updateNodeInternals to update the node internals. This change improves the functionality and maintainability of the code.

* feat: Update FreezeAllSvg component to use currentColor for fill

The commit updates the FreezeAllSvg component to use the CSS `currentColor` value for the fill property. This change ensures that the fill color of the SVG icon will match the current text color, providing a consistent and customizable visual representation of the freeze all feature.

* feat: Add freezeAll shortcut and component

This commit adds the `freezeAll` shortcut and component to the application. The `freezeAll` shortcut allows users to freeze all nodes in the flow at once, while the `FreezeAllSvg` component is responsible for rendering an SVG icon representing the freeze all feature. These additions enhance the functionality and user experience of the application by providing a convenient way to freeze multiple nodes and a visual representation of the freeze all feature.

* [autofix.ci] apply automated fixes

* feat: Refactor get_vertex method to include silent parameter

This commit refactors the `get_vertex` method in the `Graph` class to include a `silent` parameter. The `silent` parameter allows the method to be called without raising an exception if the vertex is not found. This change improves the flexibility and usability of the method, as it can now be used in scenarios where the absence of a vertex is expected.

* feat: check if parent vertex is frozen and cache children

* feat: change ungroup_node function to add frozen status to nodes

This commit refactors the `ungroup_node` function in the `utils.py` file to add the `frozen` status to each node in the list of nodes. The `frozen` status is obtained from the `group_node_data` parameter and is assigned to each node in the loop. This change improves the functionality and maintainability of the code by ensuring that the frozen status is correctly applied to the nodes during the ungrouping process.

* Refactor code to check if parent vertex is frozen before caching children

* feat: Rename "Freeze All" to "Freeze Path" in shortcuts and components

This commit updates the codebase to rename the "Freeze All" feature to "Freeze Path" in the shortcuts and components. The changes include modifying the constant name, updating the shortcut key, and adjusting the component names and labels. This renaming improves the clarity and accuracy of the feature, aligning it with its intended functionality of freezing a specific path in the flow.

* [autofix.ci] apply automated fixes

* fix: refactor get_vertex method to handle silent parameter

* fix(base.py): remove unnecessary silent parameter in get_vertex method and remove conditional check for silent parameter in get_vertex method to simplify code and improve readability

* Added new freezeAll icon

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
Co-authored-by: Lucas Oliveira <lucas.edu.oli@hotmail.com>
This commit is contained in:
anovazzi1 2024-07-16 20:48:20 -03:00 committed by GitHub
commit 23874b81ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 235 additions and 55 deletions

View file

@ -815,6 +815,10 @@ export const defaultShortcuts = [
name: "Freeze",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + F`,
},
{
name: "Freeze Path",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Shift + F`,
},
{
name: "Flow Share",
shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + B`,

View file

@ -7,6 +7,7 @@ export const URLs = {
VERSION: `version`,
MESSAGES: `monitor/messages`,
STORE: `store`,
BUILD: `build`,
} as const;
export function getURL(key: keyof typeof URLs, params: any = {}) {

View file

@ -0,0 +1 @@
export * from "./use-post-retrieve-vertex-order";

View file

@ -0,0 +1,68 @@
import { useMutationFunctionType } from "@/types/api";
import { AxiosRequestConfig } from "axios";
import { ReactFlowJsonObject } from "reactflow";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";
interface retrieveGetVerticesOrder {
flowId: string;
data?: ReactFlowJsonObject;
stopNodeId?: string;
startNodeId?: string;
}
interface retrieveGetVerticesOrderResponse {
ids: string[];
rund_id: string;
vertices_to_run: string[];
}
// add types for error handling and success
export const usePostRetrieveVertexOrder: useMutationFunctionType<
retrieveGetVerticesOrder,
retrieveGetVerticesOrderResponse
> = (options) => {
const { mutate } = UseRequestProcessor();
const postRetrieveVertexOrder = async ({
flowId,
data: flow,
startNodeId,
stopNodeId,
}: retrieveGetVerticesOrder): Promise<retrieveGetVerticesOrderResponse> => {
// nodeId is optional and is a query parameter
// if nodeId is not provided, the API will return all vertices
const config: AxiosRequestConfig<any> = {};
if (stopNodeId) {
config["params"] = { stop_component_id: decodeURIComponent(stopNodeId) };
} else if (startNodeId) {
config["params"] = {
start_component_id: decodeURIComponent(startNodeId),
};
}
const data = {
data: {},
};
if (flow && flow.nodes && flow.edges) {
const { nodes, edges } = flow;
data["data"]["nodes"] = nodes;
data["data"]["edges"] = edges;
}
const response = await api.post(
`${getURL("BUILD")}/${flowId}/vertices`,
data,
config,
);
return response.data;
};
const mutation = mutate(
["usePostRetrieveVertexOrder"],
postRetrieveVertexOrder,
options,
);
return mutation;
};

View file

@ -0,0 +1,46 @@
const FreezeAllSvg = (props) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.2"
viewBox="0 0 24 24"
className="h-4 w-4 stroke-[1.5]"
>
<title>snowflake-svg</title>
<path
id="Layer copy"
className="fill-none stroke-current"
d="m6 22.3l-4.4-4.4 4.4-4.3"
/>
<path id="Layer" className="fill-none stroke-current" d="m11 17.9h-9.4" />
<path id="Layer" className="fill-none stroke-current" d="m7.8 8.9h14.6" />
<path
id="Layer"
className="fill-none stroke-current"
d="m15.1 1.6v14.6"
/>
<path
id="Layer"
className="fill-none stroke-current"
d="m21 11.8l-2.9-2.9 2.9-2.9"
/>
<path
id="Layer"
className="fill-none stroke-current"
d="m9.3 6l2.9 2.9-2.9 2.9"
/>
<path
id="Layer"
className="fill-none stroke-current"
d="m18.1 3.1l-3 2.9-2.9-2.9"
/>
<path
id="Layer"
className="fill-none stroke-current"
d="m12.2 14.8l2.9-3 3 3"
/>
</svg>
);
};
export default FreezeAllSvg;

View file

@ -0,0 +1,10 @@
import React, { forwardRef } from "react";
import SvgFreezeAll from "./freezeAll";
("./freezeAll.jsx");
export const freezeAllIcon = forwardRef<
SVGSVGElement,
React.PropsWithChildren<{}>
>((props, ref) => {
return <SvgFreezeAll ref={ref} {...props} />;
});

View file

@ -1,3 +1,4 @@
import { usePostRetrieveVertexOrder } from "@/controllers/API/queries/vertex";
import _, { cloneDeep } from "lodash";
import { useEffect, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
@ -71,12 +72,14 @@ export default function NodeToolbarComponent({
data.node.template[templateField]?.type === "dict" ||
data.node.template[templateField]?.type === "NestedDict"),
).length;
const updateFreezeStatus = useFlowStore((state) => state.updateFreezeStatus);
const hasStore = useStoreStore((state) => state.hasStore);
const hasApiKey = useStoreStore((state) => state.hasApiKey);
const validApiKey = useStoreStore((state) => state.validApiKey);
const shortcuts = useShortcutsStore((state) => state.shortcuts);
const unselectAll = useFlowStore((state) => state.unselectAll);
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
function handleMinimizeWShortcut(e: KeyboardEvent) {
if (isWrappedWithClass(e, "noflow")) return;
e.preventDefault();
@ -153,7 +156,6 @@ export default function NodeToolbarComponent({
function handleFreeze(e: KeyboardEvent) {
if (isWrappedWithClass(e, "noflow")) return;
e.preventDefault();
if (data.node?.flow) return;
setNode(data.id, (old) => ({
...old,
data: {
@ -166,6 +168,12 @@ export default function NodeToolbarComponent({
}));
}
function handleFreezeAll(e: KeyboardEvent) {
if (isWrappedWithClass(e, "noflow")) return;
e.preventDefault();
FreezeAllVertices({ flowId: currentFlow!.id, stopNodeId: data.id });
}
const advanced = useShortcutsStore((state) => state.advanced);
const minimize = useShortcutsStore((state) => state.minimize);
const component = useShortcutsStore((state) => state.component);
@ -175,6 +183,7 @@ export default function NodeToolbarComponent({
const group = useShortcutsStore((state) => state.group);
const download = useShortcutsStore((state) => state.download);
const freeze = useShortcutsStore((state) => state.freeze);
const freezeAll = useShortcutsStore((state) => state.FreezePath);
useHotkeys(minimize, handleMinimizeWShortcut, { preventDefault });
useHotkeys(group, handleGroupWShortcut, { preventDefault });
@ -185,6 +194,7 @@ export default function NodeToolbarComponent({
useHotkeys(docs, handleDocsWShortcut, { preventDefault });
useHotkeys(download, handleDownloadWShortcut, { preventDefault });
useHotkeys(freeze, handleFreeze);
useHotkeys(freezeAll, handleFreezeAll);
const isMinimal = numberOfHandles <= 1 && numberOfOutputHandles <= 1;
const isGroup = data.node?.flow ? true : false;
@ -200,6 +210,14 @@ export default function NodeToolbarComponent({
const getNodePosition = useFlowStore((state) => state.getNodePosition);
const flows = useFlowsManagerStore((state) => state.flows);
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
const { mutate: FreezeAllVertices } = usePostRetrieveVertexOrder({
onSuccess: ({ vertices_to_run }) => {
updateFreezeStatus(vertices_to_run, !data.node?.frozen);
vertices_to_run.forEach((vertex) => {
updateNodeInternals(vertex);
});
},
});
// useEffect(() => {
// if (openWDoubleClick) setShowModalAdvanced(true);
@ -244,7 +262,6 @@ export default function NodeToolbarComponent({
saveComponent(cloneDeep(data), false);
break;
case "freeze":
if (data.node?.flow) return;
setNode(data.id, (old) => ({
...old,
data: {
@ -256,6 +273,9 @@ export default function NodeToolbarComponent({
},
}));
break;
case "freezeAll":
FreezeAllVertices({ flowId: currentFlow!.id, stopNodeId: data.id });
break;
case "code":
setOpenModal(!openModal);
break;
@ -490,44 +510,37 @@ export default function NodeToolbarComponent({
<IconComponent name="SaveAll" className="h-4 w-4" />
</button>
</ShadTooltip>*/}
{!data.node?.flow && (
<ShadTooltip
content={displayShortcut(
shortcuts.find(
({ name }) => name.split(" ")[0].toLowerCase() === "freeze",
)!,
<ShadTooltip
content={displayShortcut(
shortcuts.find(
({ name }) => name.toLowerCase() === "freeze path",
)!,
)}
side="top"
>
<button
className={classNames(
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
)}
side="top"
onClick={(event) => {
event.preventDefault();
takeSnapshot();
FreezeAllVertices({
flowId: currentFlow!.id,
stopNodeId: data.id,
});
}}
>
<button
className={classNames(
"relative -ml-px inline-flex items-center bg-background px-2 py-2 text-foreground shadow-md ring-1 ring-inset ring-ring transition-all duration-500 ease-in-out hover:bg-muted focus:z-10",
<IconComponent
name="FreezeAll"
className={cn(
"h-4 w-4 transition-all",
// TODO UPDATE THIS COLOR TO BE A VARIABLE
frozen ? "animate-wiggle text-ice" : "",
)}
onClick={(event) => {
event.preventDefault();
setNode(data.id, (old) => ({
...old,
data: {
...old.data,
node: {
...old.data.node,
frozen: old.data?.node?.frozen ? false : true,
},
},
}));
}}
>
<IconComponent
name="Snowflake"
className={cn(
"h-4 w-4 transition-all",
// TODO UPDATE THIS COLOR TO BE A VARIABLE
frozen ? "animate-wiggle text-ice" : "",
)}
/>
</button>
</ShadTooltip>
)}
/>
</button>
</ShadTooltip>
{/*<ShadTooltip content={"Duplicate"} side="top">
<button
@ -697,19 +710,29 @@ export default function NodeToolbarComponent({
/>
</SelectItem>
)}
{!data.node?.flow && (
<SelectItem value="freeze">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Freeze")?.shortcut!
}
value={"Freeze"}
icon={"Snowflake"}
dataTestId="group-button-modal"
style={`${frozen ? " text-ice" : ""} transition-all`}
/>
</SelectItem>
)}
<SelectItem value="freeze">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Freeze")?.shortcut!
}
value={"Freeze"}
icon={"Snowflake"}
dataTestId="group-button-modal"
style={`${frozen ? " text-ice" : ""} transition-all`}
/>
</SelectItem>
<SelectItem value="freezeAll">
<ToolbarSelectItem
shortcut={
shortcuts.find((obj) => obj.name === "Freeze Path")
?.shortcut!
}
value={"Freeze Path"}
icon={"FreezeAll"}
dataTestId="group-button-modal"
style={`${frozen ? " text-ice" : ""} transition-all`}
/>
</SelectItem>
<SelectItem value="Download">
<ToolbarSelectItem
shortcut={

View file

@ -65,6 +65,17 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
setFlowPool: (flowPool) => {
set({ flowPool });
},
updateFreezeStatus: (nodeIds: string[], freeze: boolean) => {
get().setNodes((oldNodes) => {
const newNodes = cloneDeep(oldNodes);
return newNodes.map((node) => {
if (nodeIds.includes(node.id)) {
(node.data as NodeDataType).node!.frozen = freeze;
}
return node;
});
});
},
addDataToFlowPool: (data: VertexBuildTypeAPI, nodeId: string) => {
let newFlowPool = cloneDeep({ ...get().flowPool });
if (!newFlowPool[nodeId]) newFlowPool[nodeId] = [data];

View file

@ -29,6 +29,7 @@ export const useShortcutsStore = create<shortcutsStoreType>((set, get) => ({
update: "mod+u",
download: "mod+j",
freeze: "mod+f",
FreezePath: "mod+shift+f",
updateUniqueShortcut: (name, combination) => {
set({
[name]: combination,

View file

@ -43,6 +43,7 @@ export type shortcutsStoreType = {
update: string;
download: string;
freeze: string;
FreezePath: string;
shortcuts: Array<{
name: string;
shortcut: string;

View file

@ -163,4 +163,5 @@ export type FlowStoreType = {
getNodePosition: (nodeId: string) => { x: number; y: number };
setLockChat: (lock: boolean) => void;
lockChat: boolean;
updateFreezeStatus: (nodeIds: string[], freeze: boolean) => void;
};

View file

@ -1,3 +1,4 @@
import { freezeAllIcon } from "@/icons/freezeAll";
import {
AlertCircle,
AlertTriangle,
@ -574,4 +575,5 @@ export const nodeIconsLucide: iconsType = {
MistralAI: MistralIcon,
Upstash: UpstashSvgIcon,
PGVector: CpuIcon,
FreezeAll: freezeAllIcon,
};