fix: auto save ui and env var (#3384)
* Added new confirmation modal for saving
* Fixed save button
* fixed header classes
* updated docs link
* Added different message to auto saving
* Changed tooltip to appear in saved text, not in button
* Changed tooltip back to previous when auto saving is enabled
* changed auto_save to auto_saving
* Fixed build not appearing and icons
* Changed modal when autosave is enabled
* 🐛 (menuBar/index.tsx): fix condition for disabling save button to include isBuilding flag to prevent saving during build process
* fix current flow not being updated on set nodes and edges and fix modal not letting user leave when flow is empty
* Removed console log
* Fix add flow not adding the flow that comes from the backend
---------
Co-authored-by: cristhianzl <cristhian.lousa@gmail.com>
Co-authored-by: Cristhian Zanforlin Lousa <72977554+Cristhianzl@users.noreply.github.com>
This commit is contained in:
parent
14ca9c9f9d
commit
00d2cffe46
13 changed files with 157 additions and 80 deletions
|
|
@ -49,7 +49,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
const isBuilding = useFlowStore((state) => state.isBuilding);
|
const isBuilding = useFlowStore((state) => state.isBuilding);
|
||||||
const getTypes = useTypesStore((state) => state.getTypes);
|
const getTypes = useTypesStore((state) => state.getTypes);
|
||||||
const saveFlow = useSaveFlow();
|
const saveFlow = useSaveFlow();
|
||||||
const shouldAutosave = process.env.LANGFLOW_AUTO_SAVE !== "false";
|
const shouldAutosave = process.env.LANGFLOW_AUTO_SAVING !== "false";
|
||||||
const currentFlow = useFlowStore((state) => state.currentFlow);
|
const currentFlow = useFlowStore((state) => state.currentFlow);
|
||||||
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
|
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||||
const updatedAt = currentSavedFlow?.updated_at;
|
const updatedAt = currentSavedFlow?.updated_at;
|
||||||
|
|
@ -105,7 +105,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
useHotkeys(changes, handleSave, { preventDefault: true });
|
useHotkeys(changes, handleSave, { preventDefault: true });
|
||||||
|
|
||||||
return currentFlow && onFlowPage ? (
|
return currentFlow && onFlowPage ? (
|
||||||
<div className="round-button-div">
|
<div className="flex items-center">
|
||||||
<div className="header-menu-bar">
|
<div className="header-menu-bar">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
|
|
@ -251,39 +251,53 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
></FlowSettingsModal>
|
></FlowSettingsModal>
|
||||||
<FlowLogsModal open={openLogs} setOpen={setOpenLogs}></FlowLogsModal>
|
<FlowLogsModal open={openLogs} setOpen={setOpenLogs}></FlowLogsModal>
|
||||||
</div>
|
</div>
|
||||||
{(updatedAt || saveLoading) && (
|
<div className="flex items-center">
|
||||||
|
{!shouldAutosave && (
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
size="icon"
|
||||||
|
disabled={shouldAutosave || !changesNotSaved || isBuilding}
|
||||||
|
className={cn("mr-1 h-9 px-2")}
|
||||||
|
onClick={handleSave}
|
||||||
|
>
|
||||||
|
<IconComponent name={"Save"} className={cn("h-5 w-5")} />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<ShadTooltip
|
<ShadTooltip
|
||||||
content={
|
content={
|
||||||
SAVED_HOVER +
|
shouldAutosave ? (
|
||||||
new Date(updatedAt ?? "").toLocaleString("en-US", {
|
SAVED_HOVER +
|
||||||
hour: "numeric",
|
(updatedAt
|
||||||
minute: "numeric",
|
? new Date(updatedAt).toLocaleString("en-US", {
|
||||||
second: "numeric",
|
hour: "numeric",
|
||||||
})
|
minute: "numeric",
|
||||||
|
})
|
||||||
|
: "Never")
|
||||||
|
) : (
|
||||||
|
<div className="flex w-48 flex-col gap-1 py-1">
|
||||||
|
<h2 className="text-base font-semibold">
|
||||||
|
Auto-saving is disabled
|
||||||
|
</h2>
|
||||||
|
<p className="text-muted-foreground">
|
||||||
|
<a
|
||||||
|
href="https://docs.langflow.org/configuration-auto-saving"
|
||||||
|
className="text-primary underline"
|
||||||
|
>
|
||||||
|
Enable auto-saving
|
||||||
|
</a>{" "}
|
||||||
|
to avoid losing progress.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
side="bottom"
|
side="bottom"
|
||||||
styleClasses="cursor-default"
|
styleClasses="cursor-default"
|
||||||
>
|
>
|
||||||
<div className="flex cursor-default items-center gap-2 text-sm text-muted-foreground transition-all">
|
<div className="ml-2 flex cursor-default items-center gap-2 text-sm text-muted-foreground transition-all">
|
||||||
<div className="flex cursor-default items-center gap-1.5 text-sm text-muted-foreground transition-all">
|
<div className="flex cursor-default items-center gap-2 text-sm text-muted-foreground transition-all">
|
||||||
<Button
|
{(saveLoading || !changesNotSaved || isBuilding) && (
|
||||||
unstyled
|
|
||||||
disabled={shouldAutosave || !changesNotSaved}
|
|
||||||
className={cn(
|
|
||||||
!shouldAutosave && changesNotSaved
|
|
||||||
? "hover:text-primary"
|
|
||||||
: "",
|
|
||||||
)}
|
|
||||||
onClick={handleSave}
|
|
||||||
>
|
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name={
|
name={isBuilding || saveLoading ? "Loader2" : "CheckCircle2"}
|
||||||
isBuilding || saveLoading
|
|
||||||
? "Loader2"
|
|
||||||
: changesNotSaved
|
|
||||||
? "Save"
|
|
||||||
: "CheckCircle2"
|
|
||||||
}
|
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-4 w-4",
|
"h-4 w-4",
|
||||||
isBuilding || saveLoading
|
isBuilding || saveLoading
|
||||||
|
|
@ -291,7 +305,8 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
: "animate-wiggle",
|
: "animate-wiggle",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</Button>
|
)}
|
||||||
|
|
||||||
<div>{printByBuildStatus()}</div>
|
<div>{printByBuildStatus()}</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
|
|
@ -306,8 +321,8 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
}}
|
}}
|
||||||
className={
|
className={
|
||||||
isBuilding
|
isBuilding
|
||||||
? "flex items-center gap-1.5 text-status-red opacity-100 transition-all"
|
? "flex items-center gap-1.5 text-status-red transition-all"
|
||||||
: "opacity-0"
|
: "hidden"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<IconComponent name="Square" className="h-4 w-4" />
|
<IconComponent name="Square" className="h-4 w-4" />
|
||||||
|
|
@ -315,7 +330,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</ShadTooltip>
|
</ShadTooltip>
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,8 @@ export default function Header(): JSX.Element {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="header-arrangement">
|
<div className="header-arrangement relative">
|
||||||
<div className="header-start-display lg:w-[450px]">
|
<div className="header-start-display">
|
||||||
<Link to="/all" className="cursor-pointer">
|
<Link to="/all" className="cursor-pointer">
|
||||||
<span className="ml-4 text-2xl">⛓️</span>
|
<span className="ml-4 text-2xl">⛓️</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
@ -103,7 +103,7 @@ export default function Header(): JSX.Element {
|
||||||
<MenuBar />
|
<MenuBar />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="round-button-div">
|
<div className="flex items-center xl:absolute xl:left-1/2 xl:-translate-x-1/2">
|
||||||
<Link to="/all">
|
<Link to="/all">
|
||||||
<Button
|
<Button
|
||||||
className="gap-2"
|
className="gap-2"
|
||||||
|
|
@ -116,7 +116,7 @@ export default function Header(): JSX.Element {
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
<IconComponent name="Home" className="h-4 w-4" />
|
<IconComponent name="Home" className="h-4 w-4" />
|
||||||
<div className="hidden flex-1 md:block">{USER_PROJECTS_HEADER}</div>
|
<div className="hidden flex-1 lg:block">{USER_PROJECTS_HEADER}</div>
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|
@ -129,7 +129,7 @@ export default function Header(): JSX.Element {
|
||||||
data-testid="button-store"
|
data-testid="button-store"
|
||||||
>
|
>
|
||||||
<IconComponent name="Store" className="h-4 w-4" />
|
<IconComponent name="Store" className="h-4 w-4" />
|
||||||
<div className="flex-1">Store</div>
|
<div className="hidden flex-1 lg:block">Store</div>
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -709,7 +709,7 @@ export const CREATE_API_KEY = `Don’t have an API key? Sign up at`;
|
||||||
export const STATUS_BUILD = "Build to validate status.";
|
export const STATUS_BUILD = "Build to validate status.";
|
||||||
export const STATUS_INACTIVE = "Execution blocked";
|
export const STATUS_INACTIVE = "Execution blocked";
|
||||||
export const STATUS_BUILDING = "Building...";
|
export const STATUS_BUILDING = "Building...";
|
||||||
export const SAVED_HOVER = "Last saved at ";
|
export const SAVED_HOVER = "Last saved: ";
|
||||||
export const RUN_TIMESTAMP_PREFIX = "Last Run: ";
|
export const RUN_TIMESTAMP_PREFIX = "Last Run: ";
|
||||||
export const STARTER_FOLDER_NAME = "Starter Projects";
|
export const STARTER_FOLDER_NAME = "Starter Projects";
|
||||||
export const PRIORITY_SIDEBAR_ORDER = [
|
export const PRIORITY_SIDEBAR_ORDER = [
|
||||||
|
|
|
||||||
|
|
@ -64,11 +64,10 @@ const useAddFlow = () => {
|
||||||
newFlow.folder_id = folder_id;
|
newFlow.folder_id = folder_id;
|
||||||
|
|
||||||
postAddFlow(newFlow, {
|
postAddFlow(newFlow, {
|
||||||
onSuccess: ({ id }) => {
|
onSuccess: (createdFlow) => {
|
||||||
newFlow.id = id;
|
|
||||||
// Add the new flow to the list of flows.
|
// Add the new flow to the list of flows.
|
||||||
const { data, flows: myFlows } = processFlows([
|
const { data, flows: myFlows } = processFlows([
|
||||||
newFlow,
|
createdFlow,
|
||||||
...(flows ?? []),
|
...(flows ?? []),
|
||||||
]);
|
]);
|
||||||
setFlows(myFlows);
|
setFlows(myFlows);
|
||||||
|
|
@ -79,7 +78,7 @@ const useAddFlow = () => {
|
||||||
["saved_components"]: data,
|
["saved_components"]: data,
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
resolve(id);
|
resolve(createdFlow.id);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
if (error.response?.data?.detail) {
|
if (error.response?.data?.detail) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import useSaveFlow from "./use-save-flow";
|
||||||
|
|
||||||
const useAutoSaveFlow = () => {
|
const useAutoSaveFlow = () => {
|
||||||
const saveFlow = useSaveFlow();
|
const saveFlow = useSaveFlow();
|
||||||
const shouldAutosave = process.env.LANGFLOW_AUTO_SAVE !== "false";
|
const shouldAutosave = process.env.LANGFLOW_AUTO_SAVING !== "false";
|
||||||
|
|
||||||
const autoSaveFlow = shouldAutosave
|
const autoSaveFlow = shouldAutosave
|
||||||
? useDebounce((flow?: FlowType) => {
|
? useDebounce((flow?: FlowType) => {
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import {
|
||||||
ContentProps,
|
ContentProps,
|
||||||
TriggerProps,
|
TriggerProps,
|
||||||
} from "../../types/components";
|
} from "../../types/components";
|
||||||
import { nodeIconsLucide } from "../../utils/styleUtils";
|
|
||||||
import BaseModal from "../baseModal";
|
import BaseModal from "../baseModal";
|
||||||
|
|
||||||
const Content: React.FC<ContentProps> = ({ children }) => {
|
const Content: React.FC<ContentProps> = ({ children }) => {
|
||||||
|
|
@ -36,6 +35,7 @@ function ConfirmationModal({
|
||||||
destructive = false,
|
destructive = false,
|
||||||
destructiveCancel = false,
|
destructiveCancel = false,
|
||||||
icon,
|
icon,
|
||||||
|
loading,
|
||||||
data,
|
data,
|
||||||
index,
|
index,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
|
|
@ -71,11 +71,13 @@ function ConfirmationModal({
|
||||||
<BaseModal.Trigger>{triggerChild}</BaseModal.Trigger>
|
<BaseModal.Trigger>{triggerChild}</BaseModal.Trigger>
|
||||||
<BaseModal.Header description={titleHeader ?? null}>
|
<BaseModal.Header description={titleHeader ?? null}>
|
||||||
<span className="pr-2">{title}</span>
|
<span className="pr-2">{title}</span>
|
||||||
<GenericIconComponent
|
{icon && (
|
||||||
name={icon}
|
<GenericIconComponent
|
||||||
className="h-6 w-6 pl-1 text-foreground"
|
name={icon}
|
||||||
aria-hidden="true"
|
className="h-6 w-6 pl-1 text-foreground"
|
||||||
/>
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</BaseModal.Header>
|
</BaseModal.Header>
|
||||||
<BaseModal.Content>
|
<BaseModal.Content>
|
||||||
{modalContentTitle && modalContentTitle != "" && (
|
{modalContentTitle && modalContentTitle != "" && (
|
||||||
|
|
@ -96,22 +98,24 @@ function ConfirmationModal({
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
onConfirm(index, data);
|
onConfirm(index, data);
|
||||||
}}
|
}}
|
||||||
|
loading={loading}
|
||||||
data-testid="replace-button"
|
data-testid="replace-button"
|
||||||
>
|
>
|
||||||
{confirmationText}
|
{confirmationText}
|
||||||
</Button>
|
</Button>
|
||||||
|
{cancelText && onCancel && (
|
||||||
<Button
|
<Button
|
||||||
className=""
|
className=""
|
||||||
variant={destructiveCancel ? "destructive" : "outline"}
|
variant={destructiveCancel ? "destructive" : "outline"}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setFlag(true);
|
setFlag(true);
|
||||||
if (onCancel) onCancel();
|
if (onCancel) onCancel();
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{cancelText}
|
{cancelText}
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
</BaseModal.Footer>
|
</BaseModal.Footer>
|
||||||
</BaseModal>
|
</BaseModal>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ export default function FlowSettingsModal({
|
||||||
);
|
);
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const [disableSave, setDisableSave] = useState(true);
|
const [disableSave, setDisableSave] = useState(true);
|
||||||
const shouldAutosave = process.env.LANGFLOW_AUTO_SAVE !== "false";
|
const shouldAutosave = process.env.LANGFLOW_AUTO_SAVING !== "false";
|
||||||
function handleClick(): void {
|
function handleClick(): void {
|
||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
if (!currentFlow) return;
|
if (!currentFlow) return;
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,61 @@
|
||||||
|
import ForwardedIconComponent from "@/components/genericIconComponent";
|
||||||
|
import { truncate } from "lodash";
|
||||||
import ConfirmationModal from "../confirmationModal";
|
import ConfirmationModal from "../confirmationModal";
|
||||||
|
|
||||||
export function SaveChangesModal({ onSave, onProceed, onCancel }) {
|
export function SaveChangesModal({
|
||||||
|
onSave,
|
||||||
|
onProceed,
|
||||||
|
onCancel,
|
||||||
|
flowName,
|
||||||
|
unsavedChanges,
|
||||||
|
lastSaved,
|
||||||
|
autoSave,
|
||||||
|
}: {
|
||||||
|
onSave: () => void;
|
||||||
|
onProceed: () => void;
|
||||||
|
onCancel: () => void;
|
||||||
|
flowName: string;
|
||||||
|
unsavedChanges: boolean;
|
||||||
|
lastSaved: string | undefined;
|
||||||
|
autoSave: boolean;
|
||||||
|
}): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
open={true}
|
open={true}
|
||||||
onClose={onCancel}
|
onClose={onCancel}
|
||||||
destructiveCancel
|
destructiveCancel
|
||||||
title={"Exit without saving?"}
|
title={truncate(flowName, { length: 32 }) + " has unsaved changes"}
|
||||||
cancelText={"Exit anyway"}
|
cancelText={autoSave ? undefined : "Exit anyway"}
|
||||||
confirmationText={"Save and Exit"}
|
confirmationText={autoSave ? "Exit" : "Save and Exit"}
|
||||||
icon={"Save"}
|
onConfirm={autoSave ? onProceed : onSave}
|
||||||
onConfirm={onSave}
|
|
||||||
onCancel={onProceed}
|
onCancel={onProceed}
|
||||||
|
loading={autoSave ? unsavedChanges : false}
|
||||||
size="x-small"
|
size="x-small"
|
||||||
>
|
>
|
||||||
<ConfirmationModal.Content>
|
<ConfirmationModal.Content>
|
||||||
You have unsaved changes. Would you like to save them before exiting?
|
{autoSave ? (
|
||||||
|
unsavedChanges ? (
|
||||||
|
"Saving flow automatically..."
|
||||||
|
) : (
|
||||||
|
"Flow saved! Click 'Exit' to leave the page."
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="mb-4 flex w-full items-center gap-3 rounded-md bg-yellow-100 px-4 py-2 text-yellow-800">
|
||||||
|
<ForwardedIconComponent name="info" className="h-5 w-5" />
|
||||||
|
Last saved: {lastSaved ?? "Never"}
|
||||||
|
</div>
|
||||||
|
Unsaved changes will be permanently lost.{" "}
|
||||||
|
<a
|
||||||
|
target="_blank"
|
||||||
|
className="underline"
|
||||||
|
href="https://docs.langflow.org/configuration-auto-saving"
|
||||||
|
>
|
||||||
|
Enable auto-saving
|
||||||
|
</a>{" "}
|
||||||
|
to avoid losing progress.
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</ConfirmationModal.Content>
|
</ConfirmationModal.Content>
|
||||||
</ConfirmationModal>
|
</ConfirmationModal>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
|
||||||
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
|
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||||
|
|
||||||
const changesNotSaved =
|
const changesNotSaved =
|
||||||
customStringify(currentFlow) !== customStringify(currentSavedFlow);
|
customStringify(currentFlow) !== customStringify(currentSavedFlow) &&
|
||||||
|
(currentFlow?.data?.nodes?.length ?? 0) > 0;
|
||||||
|
|
||||||
const blocker = useBlocker(changesNotSaved);
|
const blocker = useBlocker(changesNotSaved);
|
||||||
const version = useDarkStore((state) => state.version);
|
const version = useDarkStore((state) => state.version);
|
||||||
|
|
@ -36,6 +37,10 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
|
||||||
const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading);
|
const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading);
|
||||||
const getTypes = useTypesStore((state) => state.getTypes);
|
const getTypes = useTypesStore((state) => state.getTypes);
|
||||||
|
|
||||||
|
const updatedAt = currentSavedFlow?.updated_at;
|
||||||
|
|
||||||
|
const shouldAutosave = process.env.LANGFLOW_AUTO_SAVING !== "false";
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
saveFlow().then(() => (blocker.proceed ? blocker.proceed() : null));
|
saveFlow().then(() => (blocker.proceed ? blocker.proceed() : null));
|
||||||
};
|
};
|
||||||
|
|
@ -113,11 +118,25 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
|
||||||
<div className={version ? "mt-2" : "mt-1"}>⛓️ v{version}</div>
|
<div className={version ? "mt-2" : "mt-1"}>⛓️ v{version}</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{blocker.state === "blocked" && (
|
{blocker.state === "blocked" && currentSavedFlow && (
|
||||||
<SaveChangesModal
|
<SaveChangesModal
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
onCancel={() => (blocker.reset ? blocker.reset() : null)}
|
onCancel={() => (blocker.reset ? blocker.reset() : null)}
|
||||||
onProceed={() => (blocker.proceed ? blocker.proceed() : null)}
|
onProceed={() => (blocker.proceed ? blocker.proceed() : null)}
|
||||||
|
flowName={currentSavedFlow.name}
|
||||||
|
unsavedChanges={changesNotSaved}
|
||||||
|
lastSaved={
|
||||||
|
updatedAt
|
||||||
|
? new Date(updatedAt).toLocaleString("en-US", {
|
||||||
|
hour: "numeric",
|
||||||
|
minute: "numeric",
|
||||||
|
second: "numeric",
|
||||||
|
month: "numeric",
|
||||||
|
day: "numeric",
|
||||||
|
})
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
autoSave={shouldAutosave}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
outputs,
|
outputs,
|
||||||
hasIO: inputs.length > 0 || outputs.length > 0,
|
hasIO: inputs.length > 0 || outputs.length > 0,
|
||||||
});
|
});
|
||||||
|
get().updateCurrentFlow({ nodes: newChange, edges: newEdges });
|
||||||
if (get().autoSaveFlow) {
|
if (get().autoSaveFlow) {
|
||||||
get().autoSaveFlow!();
|
get().autoSaveFlow!();
|
||||||
}
|
}
|
||||||
|
|
@ -238,6 +239,7 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
edges: newChange,
|
edges: newChange,
|
||||||
flowState: undefined,
|
flowState: undefined,
|
||||||
});
|
});
|
||||||
|
get().updateCurrentFlow({ edges: newChange });
|
||||||
if (get().autoSaveFlow) {
|
if (get().autoSaveFlow) {
|
||||||
get().autoSaveFlow!();
|
get().autoSaveFlow!();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -218,9 +218,6 @@
|
||||||
.round-button-form {
|
.round-button-form {
|
||||||
@apply flex h-12 w-12 cursor-pointer justify-center rounded-full bg-border px-3 py-1 shadow-md;
|
@apply flex h-12 w-12 cursor-pointer justify-center rounded-full bg-border px-3 py-1 shadow-md;
|
||||||
}
|
}
|
||||||
.round-button-div {
|
|
||||||
@apply flex items-center gap-3;
|
|
||||||
}
|
|
||||||
.build-trigger-loading-icon {
|
.build-trigger-loading-icon {
|
||||||
@apply stroke-build-trigger;
|
@apply stroke-build-trigger;
|
||||||
}
|
}
|
||||||
|
|
@ -568,7 +565,7 @@
|
||||||
@apply flex items-center gap-0.5 rounded-md px-1.5 py-1 text-sm font-medium;
|
@apply flex items-center gap-0.5 rounded-md px-1.5 py-1 text-sm font-medium;
|
||||||
}
|
}
|
||||||
.header-menu-bar-display {
|
.header-menu-bar-display {
|
||||||
@apply flex max-w-[115px] cursor-pointer items-center gap-2 lg:max-w-[145px];
|
@apply flex max-w-[110px] cursor-pointer items-center gap-2 lg:max-w-[150px];
|
||||||
}
|
}
|
||||||
.header-menu-flow-name {
|
.header-menu-flow-name {
|
||||||
@apply flex-1 truncate;
|
@apply flex-1 truncate;
|
||||||
|
|
@ -581,7 +578,7 @@
|
||||||
@apply flex-max-width h-12 items-center justify-between border-b border-border bg-muted;
|
@apply flex-max-width h-12 items-center justify-between border-b border-border bg-muted;
|
||||||
}
|
}
|
||||||
.header-start-display {
|
.header-start-display {
|
||||||
@apply flex items-center justify-start gap-2;
|
@apply flex items-center gap-2;
|
||||||
}
|
}
|
||||||
.header-end-division {
|
.header-end-division {
|
||||||
@apply flex justify-end px-2;
|
@apply flex justify-end px-2;
|
||||||
|
|
|
||||||
|
|
@ -437,12 +437,13 @@ export type ConfirmationModalType = {
|
||||||
destructive?: boolean;
|
destructive?: boolean;
|
||||||
destructiveCancel?: boolean;
|
destructiveCancel?: boolean;
|
||||||
modalContentTitle?: string;
|
modalContentTitle?: string;
|
||||||
cancelText: string;
|
loading?: boolean;
|
||||||
|
cancelText?: string;
|
||||||
confirmationText: string;
|
confirmationText: string;
|
||||||
children:
|
children:
|
||||||
| [React.ReactElement<ContentProps>, React.ReactElement<TriggerProps>]
|
| [React.ReactElement<ContentProps>, React.ReactElement<TriggerProps>]
|
||||||
| React.ReactElement<ContentProps>;
|
| React.ReactElement<ContentProps>;
|
||||||
icon: string;
|
icon?: string;
|
||||||
data?: any;
|
data?: any;
|
||||||
index?: number;
|
index?: number;
|
||||||
onConfirm: (index, data) => void;
|
onConfirm: (index, data) => void;
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@ export default defineConfig(({ mode }) => {
|
||||||
outDir: "build",
|
outDir: "build",
|
||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
"process.env.LANGFLOW_AUTO_SAVE": JSON.stringify(
|
"process.env.LANGFLOW_AUTO_SAVING": JSON.stringify(
|
||||||
process.env.LANGFLOW_AUTO_SAVE,
|
process.env.LANGFLOW_AUTO_SAVING,
|
||||||
),
|
),
|
||||||
"process.env.BACKEND_URL": JSON.stringify(process.env.BACKEND_URL),
|
"process.env.BACKEND_URL": JSON.stringify(process.env.BACKEND_URL),
|
||||||
"process.env.ACCESS_TOKEN_EXPIRE_SECONDS": JSON.stringify(
|
"process.env.ACCESS_TOKEN_EXPIRE_SECONDS": JSON.stringify(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue