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:
parent
0897ee542a
commit
23874b81ed
14 changed files with 235 additions and 55 deletions
|
|
@ -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`,
|
||||
|
|
|
|||
|
|
@ -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 = {}) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
export * from "./use-post-retrieve-vertex-order";
|
||||
|
|
@ -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;
|
||||
};
|
||||
46
src/frontend/src/icons/freezeAll/freezeAll.jsx
Normal file
46
src/frontend/src/icons/freezeAll/freezeAll.jsx
Normal 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;
|
||||
10
src/frontend/src/icons/freezeAll/index.tsx
Normal file
10
src/frontend/src/icons/freezeAll/index.tsx
Normal 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} />;
|
||||
});
|
||||
|
|
@ -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={
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export type shortcutsStoreType = {
|
|||
update: string;
|
||||
download: string;
|
||||
freeze: string;
|
||||
FreezePath: string;
|
||||
shortcuts: Array<{
|
||||
name: string;
|
||||
shortcut: string;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue