Browse Source

删除控制台功能,快捷键修改 增加拖拽组的功能

feature/new_node
TianYun 2 months ago
parent
commit
4e4f916e8c
  1. 59
      src/components/GroupsOverlay.tsx
  2. 25
      src/components/WorkflowCanvas.tsx
  3. 2
      src/components/__tests__/GenerationComposer.test.tsx
  4. 29
      src/components/__tests__/GroupsOverlay.test.tsx
  5. 5
      src/components/composer/GenerationComposer.tsx
  6. 2
      src/i18n/index.tsx

59
src/components/GroupsOverlay.tsx

@ -30,8 +30,58 @@ interface GroupBackgroundProps {
// Renders just the group background - displayed below nodes (z-index 1)
function GroupBackground({ groupId }: GroupBackgroundProps) {
const { groups } = useWorkflowStore();
const { groups, updateGroup, moveGroupNodes } = useWorkflowStore();
const group = groups[groupId];
const { zoom } = useViewport();
const [isDragging, setIsDragging] = useState(false);
const dragStartRef = useRef<{ x: number; y: number } | null>(null);
const handleBackgroundMouseDown = useCallback(
(e: React.MouseEvent) => {
if (e.button !== 0) return;
e.stopPropagation();
e.preventDefault();
setIsDragging(true);
dragStartRef.current = { x: e.clientX, y: e.clientY };
},
[]
);
useEffect(() => {
if (!isDragging || !group) return;
const handleMouseMove = (e: MouseEvent) => {
if (!dragStartRef.current) return;
const deltaX = (e.clientX - dragStartRef.current.x) / zoom;
const deltaY = (e.clientY - dragStartRef.current.y) / zoom;
if (Math.abs(deltaX) > 2 || Math.abs(deltaY) > 2) {
updateGroup(groupId, {
position: {
x: group.position.x + deltaX,
y: group.position.y + deltaY,
},
});
moveGroupNodes(groupId, { x: deltaX, y: deltaY });
dragStartRef.current = { x: e.clientX, y: e.clientY };
}
};
const handleMouseUp = () => {
setIsDragging(false);
dragStartRef.current = null;
};
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
};
}, [isDragging, group, groupId, moveGroupNodes, updateGroup, zoom]);
if (!group) return null;
@ -39,7 +89,8 @@ function GroupBackground({ groupId }: GroupBackgroundProps) {
return (
<div
className="absolute rounded-xl"
className="absolute rounded-xl cursor-grab active:cursor-grabbing"
onMouseDown={handleBackgroundMouseDown}
style={{
left: group.position.x,
top: group.position.y,
@ -47,7 +98,7 @@ function GroupBackground({ groupId }: GroupBackgroundProps) {
height: group.size.height,
backgroundColor: `${bgColor}60`,
border: group.isNbpInput ? `3px dashed rgba(255,255,255,0.25)` : `1px solid ${bgColor}`,
pointerEvents: "none",
pointerEvents: "auto",
}}
/>
);
@ -531,7 +582,7 @@ export function GroupBackgroundsPortal() {
return (
<ViewportPortal>
<div style={{ position: "absolute", top: 0, left: 0, zIndex: -1, pointerEvents: "none" }}>
<div style={{ position: "absolute", top: 0, left: 0, zIndex: -1, pointerEvents: "auto" }}>
{groupIds.map((groupId) => (
<GroupBackground key={groupId} groupId={groupId} />
))}

25
src/components/WorkflowCanvas.tsx

@ -2586,29 +2586,14 @@ export function WorkflowCanvas() {
fitViewOptions={FIT_VIEW_OPTIONS}
deleteKeyCode={["Backspace", "Delete"]}
multiSelectionKeyCode="Shift"
selectionOnDrag={
canvasNavigationSettings.selectionMode === "altDrag" || canvasNavigationSettings.selectionMode === "shiftDrag"
? false
: canvasNavigationSettings.panMode === "always"
? false
: isMacOSClient && !isModalOpen
}
selectionKeyCode={
isModalOpen ? null
: canvasNavigationSettings.selectionMode === "altDrag" ? "Alt"
: canvasNavigationSettings.selectionMode === "shiftDrag" ? "Shift"
: "Shift"
}
selectionOnDrag={tutorialActive ? false : !isModalOpen}
selectionKeyCode={null}
panOnDrag={
tutorialActive
? false
: isModalOpen
? false
: canvasNavigationSettings.panMode === "always"
? true
: canvasNavigationSettings.panMode === "middleMouse"
? [2]
: !isMacOSClient
: false
}
selectNodesOnDrag={false}
nodeDragThreshold={5}
@ -2623,9 +2608,7 @@ export function WorkflowCanvas() {
? null
: isModalOpen
? null
: canvasNavigationSettings.panMode === "space"
? "Space"
: null
: "Space"
}
nodesDraggable={!isModalOpen}
nodesConnectable={!isModalOpen}

2
src/components/__tests__/GenerationComposer.test.tsx

@ -1187,7 +1187,6 @@ describe("GenerationComposer", () => {
});
render(<GenerationComposer />);
expect(screen.getByText("Video Stitch")).toBeInTheDocument();
expect(screen.getByText("2 video clips")).toBeInTheDocument();
expect(screen.getByText("Video inputs")).toBeInTheDocument();
expect(screen.getByText("Clip 1")).toBeInTheDocument();
@ -1224,7 +1223,6 @@ describe("GenerationComposer", () => {
});
render(<GenerationComposer />);
expect(screen.getByText("Ease Curve")).toBeInTheDocument();
expect(screen.getByText("Video connected")).toBeInTheDocument();
expect(screen.getByText("Curve settings")).toBeInTheDocument();
expect(screen.getByText("easeInOutSine")).toBeInTheDocument();

29
src/components/__tests__/GroupsOverlay.test.tsx

@ -147,7 +147,7 @@ describe("GroupBackgroundsPortal", () => {
expect(style).toContain("border");
});
it("should set pointerEvents to none on backgrounds", () => {
it("should make backgrounds interactive for group dragging", () => {
mockUseWorkflowStore.mockImplementation((selector) => {
return selector(createDefaultState({
groups: { "group-1": createMockGroup() },
@ -156,7 +156,32 @@ describe("GroupBackgroundsPortal", () => {
const { container } = render(<GroupBackgroundsPortal />);
const background = container.querySelector(".rounded-xl");
expect(background).toHaveStyle({ pointerEvents: "none" });
expect(background).toHaveStyle({ pointerEvents: "auto" });
});
it("should drag a group from its background", () => {
mockUseWorkflowStore.mockImplementation((selector) => {
return selector(createDefaultState({
groups: { "group-1": createMockGroup() },
}));
});
const { container } = render(<GroupBackgroundsPortal />);
const background = container.querySelector(".rounded-xl") as HTMLElement;
fireEvent.mouseDown(background, { button: 0, clientX: 100, clientY: 100 });
act(() => {
fireEvent.mouseMove(window, { clientX: 130, clientY: 120 });
});
expect(mockUpdateGroup).toHaveBeenCalledWith("group-1", {
position: { x: 130, y: 120 },
});
expect(mockMoveGroupNodes).toHaveBeenCalledWith("group-1", { x: 30, y: 20 });
act(() => {
fireEvent.mouseUp(window);
});
});
});
});

5
src/components/composer/GenerationComposer.tsx

@ -1996,11 +1996,6 @@ export function GenerationComposer() {
) : (
<>
<div className="flex items-center gap-2 px-4 pt-4">
<div className="mr-1 flex h-14 min-w-24 flex-col justify-center rounded-lg border border-neutral-700 bg-neutral-900/60 px-3">
<span className="text-[10px] text-neutral-500">{t("composer.console")}</span>
<span className="text-xs font-medium text-neutral-100">{modeLabel}</span>
</div>
{isProcessNode ? (
isVideoStitchNode ? (
<div className="flex h-14 items-center gap-3 rounded-lg border border-neutral-700 bg-neutral-900/50 px-3 text-xs text-neutral-300">

2
src/i18n/index.tsx

@ -592,7 +592,6 @@ const en = {
"composer.uploadReferenceImage": "Upload reference image",
"composer.expand": "Expand composer",
"composer.collapse": "Collapse composer",
"composer.console": "Console",
"composer.style": "Style",
"composer.mark": "Mark",
"composer.focus": "Focus",
@ -1422,7 +1421,6 @@ const zhCN: Dictionary = {
"composer.uploadReferenceImage": "上传参考图片",
"composer.expand": "展开底部栏",
"composer.collapse": "收起底部栏",
"composer.console": "控制台",
"composer.style": "风格",
"composer.mark": "标记",
"composer.focus": "聚焦",

Loading…
Cancel
Save