Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/support edit node title #497

Merged
merged 3 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions packages/ai-workspace-common/src/components/canvas/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { useCallback, useMemo, useEffect, useState, useRef, memo } from 'react';
import { useTranslation } from 'react-i18next';
import { ReactFlow, Background, MiniMap, ReactFlowProvider, useReactFlow } from '@xyflow/react';
import {
ReactFlow,
Background,
MiniMap,
ReactFlowProvider,
useReactFlow,
Node,
} from '@xyflow/react';
import { Button } from 'antd';
import { nodeTypes, CanvasNode } from './nodes';
import { LaunchPad } from './launchpad';
Expand Down Expand Up @@ -460,15 +467,20 @@ const Flow = memo(({ canvasId }: { canvasId: string }) => {
const memoizedNodeTypes = useMemo(() => nodeTypes, []);

// Optimize node dragging performance
const { setIsNodeDragging } = useEditorPerformance();
const { setIsNodeDragging, setDraggingNodeId } = useEditorPerformance();

const onNodeDragStart = useCallback(() => {
setIsNodeDragging(true);
}, [setIsNodeDragging]);
const onNodeDragStart = useCallback(
(_: React.MouseEvent, node: Node) => {
setIsNodeDragging(true);
setDraggingNodeId(node.id);
},
[setIsNodeDragging, setDraggingNodeId],
);

const onNodeDragStop = useCallback(() => {
setIsNodeDragging(false);
}, [setIsNodeDragging]);
setDraggingNodeId(null);
}, [setIsNodeDragging, setDraggingNodeId]);

const onSelectionContextMenu = useCallback(
(event: React.MouseEvent) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { NodeHeader } from './shared/node-header';
import { ContentPreview } from './shared/content-preview';
import { useCreateDocument } from '@refly-packages/ai-workspace-common/hooks/canvas/use-create-document';
import { useDeleteDocument } from '@refly-packages/ai-workspace-common/hooks/canvas/use-delete-document';
import { useEditorPerformance } from '@refly-packages/ai-workspace-common/context/editor-performance';

export const DocumentNode = memo(
({
Expand All @@ -50,7 +51,9 @@ export const DocumentNode = memo(
operatingNodeId: state.operatingNodeId,
}));

const { draggingNodeId } = useEditorPerformance();
const isOperating = operatingNodeId === id;
const isDragging = draggingNodeId === id;
const sizeMode = data?.metadata?.sizeMode || 'adaptive';
const node = useMemo(() => getNode(id), [id, getNode]);

Expand Down Expand Up @@ -199,7 +202,7 @@ export const DocumentNode = memo(
onClick={onNodeClick}
style={isPreview ? { width: 288, height: 200 } : containerStyle}
>
{!isPreview && !hideActions && (
{!isPreview && !hideActions && !isDragging && (
<ActionButtons type="document" nodeId={id} isNodeHovered={isHovered} />
)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { CanvasNodeType } from '@refly/openapi-schema';
import { useAddNode } from '@refly-packages/ai-workspace-common/hooks/canvas/use-add-node';
import { useNodeCluster } from '@refly-packages/ai-workspace-common/hooks/canvas/use-node-cluster';
import Moveable from 'react-moveable';
import { useEditorPerformance } from '@refly-packages/ai-workspace-common/context/editor-performance';

interface GroupMetadata {
label?: string;
Expand Down Expand Up @@ -67,6 +68,8 @@ export const GroupNode = memo(

// Memoize node and its measurements
const node = useMemo(() => getNode(id), [id, getNode]);
const { draggingNodeId } = useEditorPerformance();
const isDragging = draggingNodeId === id;

const initialSize = useMemo(
() => ({
Expand Down Expand Up @@ -317,7 +320,9 @@ export const GroupNode = memo(

{!isPreview && !hideActions && (
<>
<ActionButtons type="group" nodeId={id} isNodeHovered={isHovered} />
{!isDragging && (
<ActionButtons type="group" nodeId={id} isNodeHovered={isHovered} />
)}
<GroupActionButtons
nodeId={id}
isTemporary={data.metadata?.isTemporary}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { useAddToContext } from '@refly-packages/ai-workspace-common/hooks/canva
import { useDeleteNode } from '@refly-packages/ai-workspace-common/hooks/canvas/use-delete-node';
import Moveable from 'react-moveable';
import { useSetNodeDataByEntity } from '@refly-packages/ai-workspace-common/hooks/canvas/use-set-node-data-by-entity';
import { useEditorPerformance } from '@refly-packages/ai-workspace-common/context/editor-performance';

export const ImageNode = memo(
({ id, data, isPreview, selected, hideActions, hideHandles, onNodeClick }: ImageNodeProps) => {
Expand All @@ -43,7 +44,9 @@ export const ImageNode = memo(
operatingNodeId: state.operatingNodeId,
}));

const { draggingNodeId } = useEditorPerformance();
const isOperating = operatingNodeId === id;
const isDragging = draggingNodeId === id;
const node = useMemo(() => getNode(id), [id, getNode]);

const { containerStyle, handleResize } = useNodeSize({
Expand Down Expand Up @@ -182,7 +185,7 @@ export const ImageNode = memo(
'nodrag nopan select-text': isOperating,
})}
>
{!isPreview && !hideActions && (
{!isPreview && !hideActions && !isDragging && (
<ActionButtons type="image" nodeId={id} isNodeHovered={isHovered} />
)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { ContentPreview } from './shared/content-preview';
import { useCreateDocument } from '@refly-packages/ai-workspace-common/hooks/canvas/use-create-document';
import { message } from 'antd';
import getClient from '@refly-packages/ai-workspace-common/requests/proxiedRequest';

import { useEditorPerformance } from '@refly-packages/ai-workspace-common/context/editor-performance';
export const ResourceNode = memo(
({ id, data, isPreview, selected, hideActions, hideHandles, onNodeClick }: ResourceNodeProps) => {
const [isHovered, setIsHovered] = useState(false);
Expand All @@ -54,7 +54,9 @@ export const ResourceNode = memo(
operatingNodeId: state.operatingNodeId,
}));

const { draggingNodeId } = useEditorPerformance();
const isOperating = operatingNodeId === id;
const isDragging = draggingNodeId === id;
const sizeMode = data?.metadata?.sizeMode || 'adaptive';
const node = useMemo(() => getNode(id), [id, getNode]);

Expand Down Expand Up @@ -249,7 +251,7 @@ export const ResourceNode = memo(
'nodrag nopan select-text': isOperating,
})}
>
{!isPreview && !hideActions && (
{!isPreview && !hideActions && !isDragging && (
<ActionButtons type="resource" nodeId={id} isNodeHovered={isHovered} />
)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Position, useReactFlow } from '@xyflow/react';
import { useTranslation } from 'react-i18next';
import Moveable from 'react-moveable';
import classNames from 'classnames';
import { Divider, message } from 'antd';
import { Divider, Input, message } from 'antd';
import { CanvasNode, SkillResponseNodeProps } from './shared/types';
import { useState, useCallback, useRef, useEffect, useMemo, memo } from 'react';
import { CustomHandle } from './shared/custom-handle';
Expand Down Expand Up @@ -47,24 +47,39 @@ import { NodeResizer as NodeResizerComponent } from './shared/node-resizer';
import { useNodeSize } from '@refly-packages/ai-workspace-common/hooks/canvas/use-node-size';
import { ContentPreview } from './shared/content-preview';
import { useActionPolling } from '@refly-packages/ai-workspace-common/hooks/canvas/use-action-polling';

import { useSetNodeDataByEntity } from '@refly-packages/ai-workspace-common/hooks/canvas/use-set-node-data-by-entity';
import { useEditorPerformance } from '@refly-packages/ai-workspace-common/context/editor-performance';
const POLLING_WAIT_TIME = 15000;

const NodeHeader = memo(
({ query, skillName, skill }: { query: string; skillName: string; skill: any }) => {
({
query,
skillName,
skill,
updateTitle,
}: { query: string; skillName: string; skill: any; updateTitle: (title: string) => void }) => {
const [editTitle, setEditTitle] = useState(query);
return (
<>
<div className="flex-shrink-0 mb-3">
<div className="flex items-center gap-2">
<div className="w-6 h-6 rounded bg-[#F79009] shadow-lg flex items-center justify-center flex-shrink-0">
<IconResponse className="w-4 h-4 text-white" />
</div>
<span
<Input
className="!border-transparent font-bold focus:!bg-transparent px-0.5 py-0"
value={editTitle}
onChange={(e) => {
setEditTitle(e.target.value);
updateTitle?.(e.target.value);
}}
/>
{/* <span
className="text-sm font-medium leading-normal truncate block cursor-pointer"
title={query}
>
{query}
</span>
</span> */}
</div>
</div>
{skillName && skillName !== 'commonQnA' && (
Expand Down Expand Up @@ -146,12 +161,14 @@ export const SkillResponseNode = memo(
onNodeClick,
}: SkillResponseNodeProps) => {
const [isHovered, setIsHovered] = useState(false);
const { draggingNodeId } = useEditorPerformance();
const isDragging = draggingNodeId === id;

const { edges, operatingNodeId } = useCanvasStoreShallow((state) => ({
edges: state.data[state.currentCanvasId]?.edges ?? [],
operatingNodeId: state.operatingNodeId,
}));

const setNodeDataByEntity = useSetNodeDataByEntity();
const patchNodeData = usePatchNodeData();
const { getNode } = useReactFlow();
const { handleMouseEnter: onHoverStart, handleMouseLeave: onHoverEnd } = useNodeHoverEffect(id);
Expand Down Expand Up @@ -402,6 +419,18 @@ export const SkillResponseNode = memo(
nodeActionEmitter.emit(createNodeEventName(id, 'cloneAskAI.completed'));
}, [id, data?.entityId, addNode, t]);

const onTitleChange = (newTitle: string) => {
setNodeDataByEntity(
{
entityId: data.entityId,
type: 'skillResponse',
},
{
title: newTitle,
},
);
};

// Update size when content changes
useEffect(() => {
if (!targetRef.current) return;
Expand Down Expand Up @@ -470,7 +499,7 @@ export const SkillResponseNode = memo(
onMouseLeave={handleMouseLeave}
onClick={onNodeClick}
>
{!isPreview && !hideActions && (
{!isPreview && !hideActions && !isDragging && (
<ActionButtons type="skillResponse" nodeId={id} isNodeHovered={isHovered} />
)}

Expand Down Expand Up @@ -499,7 +528,12 @@ export const SkillResponseNode = memo(
<div className="absolute bottom-0 left-0 right-0 h-[15%] bg-gradient-to-t from-white to-transparent pointer-events-none z-10" />

<div className="flex flex-col h-full">
<NodeHeader query={query} skillName={skillName} skill={skill} />
<NodeHeader
query={query}
skillName={skillName}
skill={skill}
updateTitle={onTitleChange}
/>

<div className={'flex-grow overflow-y-auto pr-2 -mr-2'}>
<div className="flex flex-col gap-3">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { NodeResizer as NodeResizerComponent } from './shared/node-resizer';
import classNames from 'classnames';
import Moveable from 'react-moveable';
import { useUploadImage } from '@refly-packages/ai-workspace-common/hooks/use-upload-image';
import { useEditorPerformance } from '@refly-packages/ai-workspace-common/context/editor-performance';

type SkillNode = Node<CanvasNodeData<SkillNodeMeta>, 'skill'>;

Expand Down Expand Up @@ -115,7 +116,9 @@ export const SkillNode = memo(
const { operatingNodeId } = useCanvasStoreShallow((state) => ({
operatingNodeId: state.operatingNodeId,
}));
const { draggingNodeId } = useEditorPerformance();
const isOperating = operatingNodeId === id;
const isDragging = draggingNodeId === id;
const node = useMemo(() => getNode(id), [id, getNode]);
const { containerStyle, handleResize, updateSize } = useNodeSize({
id,
Expand Down Expand Up @@ -344,7 +347,7 @@ export const SkillNode = memo(
onMouseLeave={handleMouseLeave}
style={containerStyle}
>
<ActionButtons type="skill" nodeId={id} isNodeHovered={isHovered} />
{!isDragging && <ActionButtons type="skill" nodeId={id} isNodeHovered={isHovered} />}
<div className={`w-full h-full ${getNodeCommonStyles({ selected, isHovered })}`}>
<CustomHandle
type="target"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@ import { createContext, useContext, useState } from 'react';
interface EditorPerformanceContextType {
isNodeDragging: boolean;
setIsNodeDragging: (dragging: boolean) => void;
draggingNodeId: string | null;
setDraggingNodeId: (nodeId: string | null) => void;
}

const EditorPerformanceContext = createContext<EditorPerformanceContextType>({
isNodeDragging: false,
setIsNodeDragging: () => {},
draggingNodeId: null,
setDraggingNodeId: () => {},
});

export const EditorPerformanceProvider = ({ children }) => {
const [isNodeDragging, setIsNodeDragging] = useState(false);
const [draggingNodeId, setDraggingNodeId] = useState<string | null>(null);

return (
<EditorPerformanceContext.Provider value={{ isNodeDragging, setIsNodeDragging }}>
<EditorPerformanceContext.Provider
value={{ isNodeDragging, setIsNodeDragging, draggingNodeId, setDraggingNodeId }}
>
{children}
</EditorPerformanceContext.Provider>
);
Expand Down