Skip to content

Commit

Permalink
fix: [WIP]修复app builder复制问题
Browse files Browse the repository at this point in the history
  • Loading branch information
myshell-joe committed Sep 20, 2024
1 parent 2d7faba commit f110613
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 39 deletions.
14 changes: 10 additions & 4 deletions web/apps/web/src/components/app/context-menu/copy-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ import React from 'react';
import { useDuplicateState } from '@/components/app/nodes/state-node/hook/use-duplicate-state';
// import ShortcutsName from '@/components/common/shortcuts-name';
import { useAppState } from '@/stores/app/use-app-state';
import { useAppStore } from '@/stores/app/app-provider';

const CopyMenu: React.FC<{ id: string }> = ({ id }) => {
const setCurrentCopyId = useAppState(state => state.setCurrentCopyId);

const CopyMenu: React.FC<{ id: string; name: string }> = ({ id, name }) => {
const setCurrentCopyStateData = useAppState(
state => state.setCurrentCopyStateData,
);
const nodeData = useAppStore(state => state.nodeData);
const { duplicateState } = useDuplicateState(id);

const handleCopy = () => {
setCurrentCopyId(id);
setCurrentCopyStateData({
...nodeData[id],
name,
});
};

const handleDuplicate = () => {
Expand Down
5 changes: 3 additions & 2 deletions web/apps/web/src/components/app/context-menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,18 @@ import { useSchemaContext } from '@/stores/app/schema-provider';
import CopyMenu from './copy-menu';

const ContextMenuProvider: React.FC<PropsWithChildren> = ({ children }) => {
const { type, id } = useSchemaContext(state => ({
const { type, id, name } = useSchemaContext(state => ({
type: state.type,
id: state.id,
name: state.name,
}));

return (
<ContextMenu>
<ContextMenuTrigger>{children}</ContextMenuTrigger>
{type === NodeTypeEnum.state && id && (
<ContextMenuContent className="w-48">
<CopyMenu id={id} />
<CopyMenu id={id} name={name} />
</ContextMenuContent>
)}
</ContextMenu>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
isEventTargetInputArea,
} from '@/utils/common-helper';

import { initData } from '../utils';

export const usePasteState = ({
enabeKeyboard = false,
}: {
Expand All @@ -31,35 +33,36 @@ export const usePasteState = ({
}),
);

const { currentCopyId } = useAppState(state => ({
currentCopyId: state.currentCopyId,
const { currentCopyStateData: data } = useAppState(state => ({
currentCopyStateData: state.currentCopyStateData,
}));

// TODO 需要把nodeData里的key都换掉
const pasteState = useCallback(
(id: string) => {
const newId = uuid();
setNodeData({ id: newId, data: nodeData[id] });
const position = getCanvasCenter(reactFlowWrapper, viewport);
onAddNode({
type: nodeData[id].type,
position,
data: {
id: newId,
name: nodeData[id].name || '',
display_name: 'State',
},
});
},
[setNodeData, nodeData, reactFlowWrapper, viewport, onAddNode],
);
const pasteState = useCallback(() => {
const newId = uuid();
const newData = initData(data);
console.log('data>>>>', data);
console.log('newData>>>>', newData);
setNodeData({ id: newId, data: newData });

const position = getCanvasCenter(reactFlowWrapper, viewport);
onAddNode({
type: data.type || 'state',
position,
data: {
id: newId,
name: data.name || '',
display_name: 'State',
},
});
}, [setNodeData, data, reactFlowWrapper, viewport, onAddNode]);

useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.v`, e => {
if (isEventTargetInputArea(e.target as HTMLElement)) {
return;
}
if (currentCopyId && enabeKeyboard) {
pasteState(currentCopyId);
if (data && enabeKeyboard) {
pasteState();
}
});

Expand Down
13 changes: 8 additions & 5 deletions web/apps/web/src/components/app/nodes/state-node/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
NodeProps,
StartNode as StartNodeType,
StateNode as StateNodeType,
useReactFlowStore,
NodeTypeEnum,
Node,
Expand Down Expand Up @@ -33,7 +33,7 @@ import {
import { useDuplicateState } from './hook/use-duplicate-state';
import emitter, { EventType, useEventEmitter } from '../../emitter';

const StateNode: React.FC<NodeProps<StartNodeType>> = ({
const StateNode: React.FC<NodeProps<StateNodeType>> = ({
id,
selected,
data,
Expand Down Expand Up @@ -65,12 +65,12 @@ const StateNode: React.FC<NodeProps<StartNodeType>> = ({
setStateConfigSheetOpen,
currentStateId,
setSelectedNode,
setCurrentCopyId,
setCurrentCopyStateData,
} = useAppState(state => ({
setStateConfigSheetOpen: state.setStateConfigSheetOpen,
currentStateId: state.currentStateId,
setSelectedNode: state.setSelectedNode,
setCurrentCopyId: state.setCurrentCopyId,
setCurrentCopyStateData: state.setCurrentCopyStateData,
}));

const { duplicateState } = useDuplicateState(id);
Expand Down Expand Up @@ -121,7 +121,10 @@ const StateNode: React.FC<NodeProps<StartNodeType>> = ({
return;
}
if (selected) {
setCurrentCopyId(id);
setCurrentCopyStateData({
...nodeData[id],
...data,
});
}
},
{
Expand Down
71 changes: 71 additions & 0 deletions web/apps/web/src/components/app/nodes/state-node/utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { FieldValues } from '@shellagent/ui';
import { uuid } from '@shellagent/flow-engine';
import { generateUUID } from '@/utils/common-helper';
import { produce } from 'immer';
import { IButtonType } from '@/components/app/node-form/widgets/button-editor';

export const initData = (data: FieldValues) => {
const newData = produce(data, draft => {
// 更新 render.buttons 下的 id 和 on_click
if (draft?.render?.buttons) {
draft.render.buttons = draft?.render?.buttons?.map(
(button: IButtonType) => ({
...button,
id: generateUUID(),
on_click: {
event: '',
payload: {},
},
}),
);
}

// 更新 input 和 output 中的 key
const updateKeys = (obj: { [key: string]: FieldValues }) => {
const keyMap: { [oldKey: string]: string } = {};

Object.entries(obj).forEach(([key, value], index) => {
const newKey = uuid(index);
keyMap[key] = newKey;
delete obj[key];
obj[newKey] = value;
});

return keyMap;
};

const replaceKeyInData = (
data: any,
keyMap: { [oldKey: string]: string },
): any => {
if (typeof data === 'string') {
for (const oldKey in keyMap) {
data = data.replace(
new RegExp(`{{.*(${oldKey})(.*)}}`, 'g'),
`{{${keyMap[oldKey]}}}`,
);
}
return data;
} else if (Array.isArray(data)) {
return data.map(item => replaceKeyInData(item, keyMap));
} else if (typeof data === 'object' && data !== null) {
for (const k in data) {
data[k] = replaceKeyInData(data[k], keyMap);
}
}
return data;
};

const inputKeyMap = updateKeys(draft?.input || {});
const outputKeyMap = updateKeys(draft?.output || {});

const combinedKeyMap = { ...inputKeyMap, ...outputKeyMap };

draft.input = replaceKeyInData(draft?.input || {}, combinedKeyMap);
draft.output = replaceKeyInData(draft?.output || {}, combinedKeyMap);
draft.render = replaceKeyInData(draft?.render || {}, combinedKeyMap);
draft.blocks = replaceKeyInData(draft?.blocks || [], combinedKeyMap);
});

return newData;
};
4 changes: 4 additions & 0 deletions web/apps/web/src/stores/app/schema-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { startSchema } from './utils/schema';

type SchemaState = {
id: string;
name: string;
schema: ISchema;
schemaMode: string;
fieldMode: Record<string, TFieldMode>;
Expand All @@ -34,6 +35,7 @@ type SchemaStore = SchemaState & SchemaAction;

export const initState: SchemaStore = {
id: '',
name: '',
schema: {},
schemaMode: 'basic',
setSchemaMode: () => {},
Expand Down Expand Up @@ -147,6 +149,7 @@ export const SchemaProvider: React.FC<SchemaProviderProps> = ({
const schemaValue = useMemo<SchemaStore>(
() => ({
id,
name,
schema,
schemaMode,
setSchemaMode,
Expand All @@ -160,6 +163,7 @@ export const SchemaProvider: React.FC<SchemaProviderProps> = ({
}),
[
id,
name,
schema,
schemaMode,
setSchemaMode,
Expand Down
11 changes: 6 additions & 5 deletions web/apps/web/src/stores/app/use-app-state.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Node } from '@shellagent/flow-engine';
import { FieldValues } from '@shellagent/ui';
import { create } from 'zustand';

import { EdgeDataTypeEnum, CustomEdgeData } from '@/components/app/edges';
Expand All @@ -18,8 +19,7 @@ type State = {
currentTransitionSourceHandle: string;
currentEdegData: CustomEdgeData;
targetInputsSheetOpen: boolean;
// 当前copy的id
currentCopyId?: string;
currentCopyStateData: FieldValues;
};

type Action = {
Expand All @@ -42,7 +42,7 @@ type Action = {
setTargetInputsSheetOpen: (open: State['targetInputsSheetOpen']) => void;
setRunDrawerWidth: (width: number) => void;
resetState: () => void;
setCurrentCopyId: (id: string) => void;
setCurrentCopyStateData: (data: FieldValues) => void;
};

const initialState: State = {
Expand All @@ -58,7 +58,6 @@ const initialState: State = {
currentTransitionSourceHandle: '',
runDrawerWidth: 715,
transitionSheetOpen: false,
currentCopyId: '',
currentEdegData: {
id: '',
custom: true,
Expand All @@ -67,6 +66,7 @@ const initialState: State = {
target: '',
conditions: [],
},
currentCopyStateData: {},
};

export const useAppState = create<State & Action>(set => ({
Expand All @@ -75,7 +75,6 @@ export const useAppState = create<State & Action>(set => ({
setEditing: editing => set(() => ({ editing })),
setRunDrawerWidth: runDrawerWidth => set(() => ({ runDrawerWidth })),
setSelectedNode: selectedNode => set(() => ({ selectedNode })),
setCurrentCopyId: (id: string) => set(() => ({ currentCopyId: id })),
setStateConfigSheetOpen: (stateId, open) =>
set(state => {
if (open && state.currentStateId !== stateId) {
Expand Down Expand Up @@ -164,4 +163,6 @@ export const useAppState = create<State & Action>(set => ({
...state,
targetInputsSheetOpen: open,
})),
setCurrentCopyStateData: (data: FieldValues) =>
set(() => ({ currentCopyStateData: data })),
}));
1 change: 1 addition & 0 deletions web/packages/flow-engine/src/types/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export enum NodeIdEnum {

// 节点id通过uuid生成
export type WidgetNodeId = `key_${string}`;
export type StateNodeId = `key_${string}`;
// 节点名称
export type NodeName = `${string}#${number}`;

Expand Down
4 changes: 2 additions & 2 deletions web/packages/flow-engine/src/utils/uuid.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function uuid(): Lowercase<string> {
const timestamp = new Date().getTime().toString();
function uuid(offset: number = 0): Lowercase<string> {
const timestamp = (new Date().getTime() + offset).toString();
const randomString = timestamp.slice(-16);

return `key_${randomString}` as Lowercase<string>;
Expand Down

0 comments on commit f110613

Please sign in to comment.