Commit 9b2c3b24 by heheer Committed by GitHub

refactor: snapshot store to diff (#3155)

* refactor: snapshot store to diff

* change initial state position

* fix old snapshot format

* encapsulate json diff
parent 4f550259
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
"jest": "^29.5.0", "jest": "^29.5.0",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"json5": "^2.2.3", "json5": "^2.2.3",
"jsondiffpatch": "^0.6.0",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mermaid": "^10.2.3", "mermaid": "^10.2.3",
......
...@@ -13,7 +13,11 @@ import { useTranslation } from 'next-i18next'; ...@@ -13,7 +13,11 @@ import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { WorkflowContext, WorkflowSnapshotsType } from '../WorkflowComponents/context'; import {
WorkflowContext,
WorkflowSnapshotsType,
WorkflowStateType
} from '../WorkflowComponents/context';
import { AppContext, TabEnum } from '../context'; import { AppContext, TabEnum } from '../context';
import RouteTab from '../RouteTab'; import RouteTab from '../RouteTab';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
...@@ -34,6 +38,7 @@ import { ...@@ -34,6 +38,7 @@ import {
WorkflowInitContext WorkflowInitContext
} from '../WorkflowComponents/context/workflowInitContext'; } from '../WorkflowComponents/context/workflowInitContext';
import { WorkflowEventContext } from '../WorkflowComponents/context/workflowEventContext'; import { WorkflowEventContext } from '../WorkflowComponents/context/workflowEventContext';
import { applyDiff } from '@/web/core/app/diff';
const Header = () => { const Header = () => {
const { t } = useTranslation(); const { t } = useTranslation();
...@@ -76,11 +81,14 @@ const Header = () => { ...@@ -76,11 +81,14 @@ const Header = () => {
[...future].reverse().find((snapshot) => snapshot.isSaved) || [...future].reverse().find((snapshot) => snapshot.isSaved) ||
past.find((snapshot) => snapshot.isSaved); past.find((snapshot) => snapshot.isSaved);
const initialState = past[past.length - 1]?.state;
const savedSnapshotState = applyDiff(initialState, savedSnapshot?.diff);
const val = compareSnapshot( const val = compareSnapshot(
{ {
nodes: savedSnapshot?.nodes, nodes: savedSnapshotState?.nodes,
edges: savedSnapshot?.edges, edges: savedSnapshotState?.edges,
chatConfig: savedSnapshot?.chatConfig chatConfig: savedSnapshotState?.chatConfig
}, },
{ {
nodes: nodes, nodes: nodes,
......
...@@ -17,6 +17,7 @@ import styles from './styles.module.scss'; ...@@ -17,6 +17,7 @@ import styles from './styles.module.scss';
import { useSystem } from '@fastgpt/web/hooks/useSystem'; import { useSystem } from '@fastgpt/web/hooks/useSystem';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { onSaveSnapshotFnType, SimpleAppSnapshotType } from './useSnapshots'; import { onSaveSnapshotFnType, SimpleAppSnapshotType } from './useSnapshots';
import { applyDiff } from '@/web/core/app/diff';
const Edit = ({ const Edit = ({
appForm, appForm,
...@@ -39,16 +40,19 @@ const Edit = ({ ...@@ -39,16 +40,19 @@ const Edit = ({
// show selected dataset // show selected dataset
loadAllDatasets(); loadAllDatasets();
// Get the latest snapshot
if (past?.[0]?.appForm) {
return setAppForm(past[0].appForm);
}
const appForm = appWorkflow2Form({ const appForm = appWorkflow2Form({
nodes: appDetail.modules, nodes: appDetail.modules,
chatConfig: appDetail.chatConfig chatConfig: appDetail.chatConfig
}); });
// Get the latest snapshot
if (past?.[0]?.diff) {
const pastState = applyDiff(past[past.length - 1].state, past[0].diff);
return setAppForm(pastState);
}
setAppForm(appForm);
// Set the first snapshot // Set the first snapshot
if (past.length === 0) { if (past.length === 0) {
saveSnapshot({ saveSnapshot({
...@@ -58,8 +62,6 @@ const Edit = ({ ...@@ -58,8 +62,6 @@ const Edit = ({
}); });
} }
setAppForm(appForm);
if (appDetail.version !== 'v2') { if (appDetail.version !== 'v2') {
setAppForm( setAppForm(
appWorkflow2Form({ appWorkflow2Form({
......
import React, { useCallback, useState } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { AppContext } from '../context'; import { AppContext } from '../context';
import FolderPath from '@/components/common/folder/Path'; import FolderPath from '@/components/common/folder/Path';
...@@ -29,6 +29,7 @@ import { ...@@ -29,6 +29,7 @@ import {
} from './useSnapshots'; } from './useSnapshots';
import PublishHistories from '../PublishHistoriesSlider'; import PublishHistories from '../PublishHistoriesSlider';
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version'; import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
import { applyDiff } from '@/web/core/app/diff';
const Header = ({ const Header = ({
forbiddenSaveSnapshot, forbiddenSaveSnapshot,
...@@ -48,9 +49,20 @@ const Header = ({ ...@@ -48,9 +49,20 @@ const Header = ({
const { t } = useTranslation(); const { t } = useTranslation();
const { isPc } = useSystem(); const { isPc } = useSystem();
const router = useRouter(); const router = useRouter();
const { appId, onSaveApp, currentTab } = useContextSelector(AppContext, (v) => v); const { appId, onSaveApp, currentTab, appLatestVersion } = useContextSelector(
AppContext,
(v) => v
);
const { lastAppListRouteType } = useSystemStore(); const { lastAppListRouteType } = useSystemStore();
const { allDatasets } = useDatasetStore(); const { allDatasets } = useDatasetStore();
const initialAppForm = useMemo(
() =>
appWorkflow2Form({
nodes: appLatestVersion?.nodes || [],
chatConfig: appLatestVersion?.chatConfig || {}
}),
[appLatestVersion]
);
const { data: paths = [] } = useRequest2(() => getAppFolderPath(appId), { const { data: paths = [] } = useRequest2(() => getAppFolderPath(appId), {
manual: false, manual: false,
...@@ -104,7 +116,8 @@ const Header = ({ ...@@ -104,7 +116,8 @@ const Header = ({
const onSwitchTmpVersion = useCallback( const onSwitchTmpVersion = useCallback(
(data: SimpleAppSnapshotType, customTitle: string) => { (data: SimpleAppSnapshotType, customTitle: string) => {
setAppForm(data.appForm); const pastState = applyDiff(initialAppForm, data.diff);
setAppForm(pastState);
// Remove multiple "copy-" // Remove multiple "copy-"
const copyText = t('app:version_copy'); const copyText = t('app:version_copy');
...@@ -112,11 +125,11 @@ const Header = ({ ...@@ -112,11 +125,11 @@ const Header = ({
const title = customTitle.replace(regex, `$1`); const title = customTitle.replace(regex, `$1`);
return saveSnapshot({ return saveSnapshot({
appForm: data.appForm, appForm: pastState,
title title
}); });
}, },
[saveSnapshot, setAppForm, t] [initialAppForm, saveSnapshot, setAppForm, t]
); );
const onSwitchCloudVersion = useCallback( const onSwitchCloudVersion = useCallback(
(appVersion: AppVersionSchemaType) => { (appVersion: AppVersionSchemaType) => {
...@@ -143,7 +156,8 @@ const Header = ({ ...@@ -143,7 +156,8 @@ const Header = ({
useDebounceEffect( useDebounceEffect(
() => { () => {
const savedSnapshot = past.find((snapshot) => snapshot.isSaved); const savedSnapshot = past.find((snapshot) => snapshot.isSaved);
const val = compareSimpleAppSnapshot(savedSnapshot?.appForm, appForm); const pastState = applyDiff(initialAppForm, savedSnapshot?.diff);
const val = compareSimpleAppSnapshot(pastState, appForm);
setIsPublished(val); setIsPublished(val);
}, },
[past, allDatasets], [past, allDatasets],
......
...@@ -3,11 +3,13 @@ import { SetStateAction, useEffect, useRef } from 'react'; ...@@ -3,11 +3,13 @@ import { SetStateAction, useEffect, useRef } from 'react';
import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time'; import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time';
import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type'; import { AppSimpleEditFormType } from '@fastgpt/global/core/app/type';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { applyDiff, createDiff } from '@/web/core/app/diff';
export type SimpleAppSnapshotType = { export type SimpleAppSnapshotType = {
appForm: AppSimpleEditFormType; diff?: Record<string, any>;
title: string; title: string;
isSaved?: boolean; isSaved?: boolean;
state?: AppSimpleEditFormType;
}; };
export type onSaveSnapshotFnType = (props: { export type onSaveSnapshotFnType = (props: {
appForm: AppSimpleEditFormType; appForm: AppSimpleEditFormType;
...@@ -66,14 +68,32 @@ export const useSimpleAppSnapshots = (appId: string) => { ...@@ -66,14 +68,32 @@ export const useSimpleAppSnapshots = (appId: string) => {
return false; return false;
} }
const pastState = past[0]; if (past.length === 0) {
setPast([
{
title: title || formatTime2YMDHMS(new Date()),
isSaved,
state: appForm
}
]);
return true;
}
const initialState = past[past.length - 1].state;
if (!initialState) return false;
if (past.length > 0) {
const pastState = applyDiff(initialState, past[0].diff);
const isPastEqual = compareSimpleAppSnapshot(pastState, appForm);
if (isPastEqual) return false;
}
const isPastEqual = compareSimpleAppSnapshot(pastState?.appForm, appForm); const diff = createDiff(initialState, appForm);
if (isPastEqual) return false;
setPast((past) => [ setPast((past) => [
{ {
appForm, diff,
title: title || formatTime2YMDHMS(new Date()), title: title || formatTime2YMDHMS(new Date()),
isSaved isSaved
}, },
......
...@@ -13,7 +13,7 @@ import { useTranslation } from 'next-i18next'; ...@@ -13,7 +13,7 @@ import { useTranslation } from 'next-i18next';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../WorkflowComponents/context'; import { WorkflowContext, WorkflowStateType } from '../WorkflowComponents/context';
import { AppContext, TabEnum } from '../context'; import { AppContext, TabEnum } from '../context';
import RouteTab from '../RouteTab'; import RouteTab from '../RouteTab';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
...@@ -34,6 +34,7 @@ import { ...@@ -34,6 +34,7 @@ import {
WorkflowInitContext WorkflowInitContext
} from '../WorkflowComponents/context/workflowInitContext'; } from '../WorkflowComponents/context/workflowInitContext';
import { WorkflowEventContext } from '../WorkflowComponents/context/workflowEventContext'; import { WorkflowEventContext } from '../WorkflowComponents/context/workflowEventContext';
import { applyDiff } from '@/web/core/app/diff';
const Header = () => { const Header = () => {
const { t } = useTranslation(); const { t } = useTranslation();
...@@ -81,11 +82,14 @@ const Header = () => { ...@@ -81,11 +82,14 @@ const Header = () => {
[...future].reverse().find((snapshot) => snapshot.isSaved) || [...future].reverse().find((snapshot) => snapshot.isSaved) ||
past.find((snapshot) => snapshot.isSaved); past.find((snapshot) => snapshot.isSaved);
const initialState = past[past.length - 1]?.state;
const savedSnapshotState = applyDiff(initialState, savedSnapshot?.diff);
const val = compareSnapshot( const val = compareSnapshot(
{ {
nodes: savedSnapshot?.nodes, nodes: savedSnapshotState?.nodes,
edges: savedSnapshot?.edges, edges: savedSnapshotState?.edges,
chatConfig: savedSnapshot?.chatConfig chatConfig: savedSnapshotState?.chatConfig
}, },
{ {
nodes: nodes, nodes: nodes,
......
...@@ -5,7 +5,6 @@ import { useTranslation } from 'react-i18next'; ...@@ -5,7 +5,6 @@ import { useTranslation } from 'react-i18next';
import { nodeTemplate2FlowNode } from '@/web/core/workflow/utils'; import { nodeTemplate2FlowNode } from '@/web/core/workflow/utils';
import { CommentNode } from '@fastgpt/global/core/workflow/template/system/comment'; import { CommentNode } from '@fastgpt/global/core/workflow/template/system/comment';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../context';
import { useReactFlow } from 'reactflow'; import { useReactFlow } from 'reactflow';
import { WorkflowNodeEdgeContext } from '../../context/workflowInitContext'; import { WorkflowNodeEdgeContext } from '../../context/workflowInitContext';
import { WorkflowEventContext } from '../../context/workflowEventContext'; import { WorkflowEventContext } from '../../context/workflowEventContext';
......
...@@ -2,6 +2,7 @@ import { postWorkflowDebug } from '@/web/core/workflow/api'; ...@@ -2,6 +2,7 @@ import { postWorkflowDebug } from '@/web/core/workflow/api';
import { import {
checkWorkflowNodeAndConnection, checkWorkflowNodeAndConnection,
compareSnapshot, compareSnapshot,
simplifyNodes,
storeEdgesRenderEdge, storeEdgesRenderEdge,
storeNode2FlowNode storeNode2FlowNode
} from '@/web/core/workflow/utils'; } from '@/web/core/workflow/utils';
...@@ -41,6 +42,7 @@ import { cloneDeep } from 'lodash'; ...@@ -41,6 +42,7 @@ import { cloneDeep } from 'lodash';
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version'; import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
import WorkflowInitContextProvider, { WorkflowNodeEdgeContext } from './workflowInitContext'; import WorkflowInitContextProvider, { WorkflowNodeEdgeContext } from './workflowInitContext';
import WorkflowEventContextProvider from './workflowEventContext'; import WorkflowEventContextProvider from './workflowEventContext';
import { applyDiff, createDiff } from '@/web/core/app/diff';
/* /*
Context Context
...@@ -67,14 +69,22 @@ export const ReactFlowCustomProvider = ({ ...@@ -67,14 +69,22 @@ export const ReactFlowCustomProvider = ({
); );
}; };
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
export type WorkflowSnapshotsType = { export type WorkflowSnapshotsType = {
diff?: any;
title: string;
isSaved?: boolean;
state?: WorkflowStateType;
// old format
nodes?: Node[];
edges?: Edge[];
chatConfig?: AppChatConfigType;
};
export type WorkflowStateType = {
nodes: Node[]; nodes: Node[];
edges: Edge[]; edges: Edge[];
title: string;
chatConfig: AppChatConfigType; chatConfig: AppChatConfigType;
isSaved?: boolean;
}; };
type WorkflowContextType = { type WorkflowContextType = {
...@@ -751,7 +761,7 @@ const WorkflowContextProvider = ({ ...@@ -751,7 +761,7 @@ const WorkflowContextProvider = ({
defaultValue: [] defaultValue: []
}) as [WorkflowSnapshotsType[], (value: SetStateAction<WorkflowSnapshotsType[]>) => void]; }) as [WorkflowSnapshotsType[], (value: SetStateAction<WorkflowSnapshotsType[]>) => void];
const resetSnapshot = useMemoizedFn((state: Omit<WorkflowSnapshotsType, 'title' | 'isSaved'>) => { const resetSnapshot = useMemoizedFn((state: WorkflowStateType) => {
setNodes(state.nodes); setNodes(state.nodes);
setEdges(state.edges); setEdges(state.edges);
setAppDetail((detail) => ({ setAppDetail((detail) => ({
...@@ -759,20 +769,9 @@ const WorkflowContextProvider = ({ ...@@ -759,20 +769,9 @@ const WorkflowContextProvider = ({
chatConfig: state.chatConfig chatConfig: state.chatConfig
})); }));
}); });
const pushPastSnapshot = useMemoizedFn( const pushPastSnapshot = useMemoizedFn(
({ ({ pastNodes, pastEdges, chatConfig, customTitle, isSaved }) => {
pastNodes,
pastEdges,
customTitle,
chatConfig,
isSaved
}: {
pastNodes: Node[];
pastEdges: Edge[];
customTitle?: string;
chatConfig: AppChatConfigType;
isSaved?: boolean;
}) => {
if (!pastNodes || !pastEdges || !chatConfig) return false; if (!pastNodes || !pastEdges || !chatConfig) return false;
if (forbiddenSaveSnapshot.current) { if (forbiddenSaveSnapshot.current) {
...@@ -780,7 +779,13 @@ const WorkflowContextProvider = ({ ...@@ -780,7 +779,13 @@ const WorkflowContextProvider = ({
return false; return false;
} }
const pastState = past[0]; // Get initial state
const initialState = past[past.length - 1]?.state;
if (!initialState) return false;
// Apply latest diff to get past state
const pastState = applyDiff(initialState, past[0].diff);
const isPastEqual = compareSnapshot( const isPastEqual = compareSnapshot(
{ {
nodes: pastNodes, nodes: pastNodes,
...@@ -796,13 +801,21 @@ const WorkflowContextProvider = ({ ...@@ -796,13 +801,21 @@ const WorkflowContextProvider = ({
if (isPastEqual) return false; if (isPastEqual) return false;
// Create current state object
const newState = {
nodes: simplifyNodes(pastNodes),
edges: pastEdges,
chatConfig
};
// Calculate diff from initial state
const diff = createDiff(initialState, newState);
setFuture([]); setFuture([]);
setPast((past) => [ setPast((past) => [
{ {
nodes: pastNodes, diff,
edges: pastEdges,
title: customTitle || formatTime2YMDHMS(new Date()), title: customTitle || formatTime2YMDHMS(new Date()),
chatConfig,
isSaved isSaved
}, },
...past.slice(0, 199) ...past.slice(0, 199)
...@@ -811,18 +824,20 @@ const WorkflowContextProvider = ({ ...@@ -811,18 +824,20 @@ const WorkflowContextProvider = ({
return true; return true;
} }
); );
const onSwitchTmpVersion = useMemoizedFn((params: WorkflowSnapshotsType, customTitle: string) => { const onSwitchTmpVersion = useMemoizedFn((params: WorkflowSnapshotsType, customTitle: string) => {
// Remove multiple "copy-" // Remove multiple "copy-"
const copyText = t('app:version_copy'); const copyText = t('app:version_copy');
const regex = new RegExp(`(${copyText}-)\\1+`, 'g'); const regex = new RegExp(`(${copyText}-)\\1+`, 'g');
const title = customTitle.replace(regex, `$1`); const title = customTitle.replace(regex, `$1`);
const pastState = applyDiff(past[past.length - 1].state, params.diff);
resetSnapshot(params); resetSnapshot(pastState);
return pushPastSnapshot({ return pushPastSnapshot({
pastNodes: params.nodes, pastNodes: pastState.nodes,
pastEdges: params.edges, pastEdges: pastState.edges,
chatConfig: params.chatConfig, chatConfig: pastState.chatConfig,
customTitle: title customTitle: title
}); });
}); });
...@@ -848,15 +863,19 @@ const WorkflowContextProvider = ({ ...@@ -848,15 +863,19 @@ const WorkflowContextProvider = ({
if (past[1]) { if (past[1]) {
setFuture((future) => [past[0], ...future]); setFuture((future) => [past[0], ...future]);
setPast((past) => past.slice(1)); setPast((past) => past.slice(1));
resetSnapshot(past[1]); const pastState = applyDiff(past[past.length - 1].state, past[1].diff);
resetSnapshot(pastState);
} }
}); });
const redo = useMemoizedFn(() => { const redo = useMemoizedFn(() => {
const futureState = future[0]; if (!future[0]) return;
const futureState = applyDiff(past[past.length - 1].state, future[0].diff);
if (futureState) { if (futureState) {
setPast((past) => [future[0], ...past]); setPast((past) => [future[0], ...past]);
setFuture((future) => future.slice(1)); setFuture((future) => future.slice(1));
resetSnapshot(futureState); resetSnapshot(futureState);
} }
}); });
...@@ -873,45 +892,113 @@ const WorkflowContextProvider = ({ ...@@ -873,45 +892,113 @@ const WorkflowContextProvider = ({
}); });
}, [appId]); }, [appId]);
// Convert old history format to new format
const convertOldFormatHistory = (past: WorkflowSnapshotsType[]) => {
const baseState = {
nodes: past[past.length - 1].state?.nodes || [],
edges: past[past.length - 1].state?.edges || [],
chatConfig: past[past.length - 1].state?.chatConfig || {}
};
return past.map((item, index) => {
if (index === past.length - 1) {
return {
title: item.title,
isSaved: item.isSaved,
state: baseState
};
}
const currentState = {
nodes: item.nodes || [],
edges: item.edges || [],
chatConfig: item.chatConfig || {}
};
const diff = createDiff(baseState, currentState);
return {
title: item.title || formatTime2YMDHMS(new Date()),
isSaved: item.isSaved,
diff
};
});
};
const initData = useCallback( const initData = useCallback(
async (e: Parameters<WorkflowContextType['initData']>[0], isInit?: boolean) => { async (
// Refresh web page, load init e: {
nodes: StoreNodeItemType[];
edges: StoreEdgeItemType[];
chatConfig?: AppChatConfigType;
},
isInit?: boolean
) => {
const nodes = e.nodes?.map((item) => storeNode2FlowNode({ item, t })) || [];
const edges = e.edges?.map((item) => storeEdgesRenderEdge({ edge: item })) || [];
const initialState = {
nodes: simplifyNodes(nodes),
edges,
chatConfig: e.chatConfig || appDetail.chatConfig
};
if (isInit && past.length > 0) { if (isInit && past.length > 0) {
return resetSnapshot(past[0]); // new format
} if (past[0].diff) {
// If it is the initial data, save the initial snapshot const targetState = applyDiff(
if (isInit && past.length === 0) { past[past.length - 1].state,
pushPastSnapshot({ past[0].diff
pastNodes: e.nodes?.map((item) => storeNode2FlowNode({ item, t })) || [], ) as WorkflowStateType;
pastEdges: e.edges?.map((item) => storeEdgesRenderEdge({ edge: item })) || [],
customTitle: t(`app:app.version_initial`), setNodes(targetState.nodes);
chatConfig: appDetail.chatConfig, setEdges(targetState.edges);
isSaved: true setAppDetail((state) => ({
}); ...state,
forbiddenSaveSnapshot.current = true; chatConfig: targetState.chatConfig
}));
return;
}
// old format
if (past.some((item) => !item.state && (item.nodes || item.edges))) {
const newPast = convertOldFormatHistory(past);
setPast(newPast);
const latestState = applyDiff(
newPast[newPast.length - 1].state,
newPast[0].diff
) as WorkflowStateType;
setNodes(latestState.nodes);
setEdges(latestState.edges);
setAppDetail((state) => ({
...state,
chatConfig: latestState.chatConfig
}));
return;
}
} }
setNodes(e.nodes?.map((item) => storeNode2FlowNode({ item, t })) || []); setNodes(nodes);
setEdges(e.edges?.map((item) => storeEdgesRenderEdge({ edge: item })) || []); setEdges(edges);
if (e.chatConfig) {
setAppDetail((state) => ({ ...state, chatConfig: e.chatConfig as AppChatConfigType }));
}
const chatConfig = e.chatConfig; if (isInit && past.length === 0) {
if (chatConfig) { setPast([
setAppDetail((state) => ({ {
...state, title: t(`app:app.version_initial`),
chatConfig isSaved: true,
})); state: initialState
}
]);
forbiddenSaveSnapshot.current = true;
} }
}, },
[ [appDetail.chatConfig, past, setAppDetail, setEdges, setNodes, setPast, t]
appDetail.chatConfig,
past,
resetSnapshot,
pushPastSnapshot,
setAppDetail,
setEdges,
setNodes,
t
]
); );
const value = useMemo( const value = useMemo(
......
import { create } from 'jsondiffpatch';
const createWorkflowDiffPatcher = () =>
create({
objectHash: (obj: any) => obj.id || obj.nodeId || obj._id,
propertyFilter: (name: string) => name !== 'selected'
});
const diffPatcher = createWorkflowDiffPatcher();
export const createDiff = <T extends Record<string, unknown>>(initialState?: T, newState?: T) => {
return diffPatcher.diff(initialState, newState);
};
export const applyDiff = <T extends Record<string, unknown>>(
initialState?: T,
diff?: ReturnType<typeof diffPatcher.diff>
) => {
return diffPatcher.patch(structuredClone(initialState), diff) as T;
};
...@@ -631,3 +631,14 @@ export const compareSnapshot = ( ...@@ -631,3 +631,14 @@ export const compareSnapshot = (
return isEqual(node1, node2); return isEqual(node1, node2);
}; };
// remove node size
export const simplifyNodes = (nodes: Node[]) => {
return nodes.map((node) => ({
id: node.id,
type: node.type,
position: node.position,
data: node.data,
zIndex: node.zIndex
}));
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment