Browse Source

固定 下面的输入框

feature/071
yun 1 week ago
parent
commit
71d6dc2fc9
  1. 39
      src/components/WorkflowCanvas.tsx
  2. 22
      src/components/composer/NodeGenerationComposer.tsx
  3. 2
      src/components/nodes/BaseNode.tsx

39
src/components/WorkflowCanvas.tsx

@ -34,7 +34,6 @@ import { ConnectionDropMenu, type ConnectionIntent } from "./ConnectionDropMenu"
import { MultiSelectToolbar } from "./MultiSelectToolbar"; import { MultiSelectToolbar } from "./MultiSelectToolbar";
import { EdgeToolbar } from "./EdgeToolbar"; import { EdgeToolbar } from "./EdgeToolbar";
import { GlobalImageHistory } from "./GlobalImageHistory"; import { GlobalImageHistory } from "./GlobalImageHistory";
import { NodeGenerationComposer } from "./composer/NodeGenerationComposer";
import { NodeType, NanoBananaNodeData, HandleType, PromptNodeData, SmartTextNodeData, LLMGenerateNodeData, PromptConstructorNodeData, AvailableVariable, SelectedModel, WorkflowNode } from "@/types"; import { NodeType, NanoBananaNodeData, HandleType, PromptNodeData, SmartTextNodeData, LLMGenerateNodeData, PromptConstructorNodeData, AvailableVariable, SelectedModel, WorkflowNode } from "@/types";
import { DEFAULT_NODE_DIMENSIONS, defaultNodeDimensions } from "@/store/utils/nodeDefaults"; import { DEFAULT_NODE_DIMENSIONS, defaultNodeDimensions } from "@/store/utils/nodeDefaults";
import { NodeHeaderProvider } from "./nodes/NodeHeaderContext"; import { NodeHeaderProvider } from "./nodes/NodeHeaderContext";
@ -76,7 +75,6 @@ import { useI18n } from "@/i18n";
import { buildDefaultGenerationNodeCreation } from "@/utils/defaultGenerationNodeModel"; import { buildDefaultGenerationNodeCreation } from "@/utils/defaultGenerationNodeModel";
import { isSmartImageGenerateMode } from "@/utils/smartImageMode"; import { isSmartImageGenerateMode } from "@/utils/smartImageMode";
import { deriveSmartTextMode } from "@/utils/smartTextMode"; import { deriveSmartTextMode } from "@/utils/smartTextMode";
import { useCanvasPreviewMode } from "@/components/CanvasPreviewModeContext";
import { getNodePrimaryImageUrl, getNodePrimaryMediaUrl, isTextOnlyContextMenuNode } from "@/utils/canvasNodeMedia"; import { getNodePrimaryImageUrl, getNodePrimaryMediaUrl, isTextOnlyContextMenuNode } from "@/utils/canvasNodeMedia";
import { import {
canConnectOutputMediaToNode, canConnectOutputMediaToNode,
@ -524,7 +522,6 @@ function getCanvasPanOnDrag(disabled: boolean): PanOnDragValue {
export function WorkflowCanvas() { export function WorkflowCanvas() {
const { t } = useI18n(); const { t } = useI18n();
const isPreviewMode = useCanvasPreviewMode();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const urlWorkflowId = searchParams?.get("workflowId") ?? null; const urlWorkflowId = searchParams?.get("workflowId") ?? null;
const { nodes, edges, groups, isModalOpen, navigationTarget, canvasNavigationSettings, dimmedNodeIds, skippedNodeIds, providerSettings, isRunning } = const { nodes, edges, groups, isModalOpen, navigationTarget, canvasNavigationSettings, dimmedNodeIds, skippedNodeIds, providerSettings, isRunning } =
@ -1003,35 +1000,6 @@ export function WorkflowCanvas() {
() => allNodes.filter((node) => node.selected), () => allNodes.filter((node) => node.selected),
[allNodes] [allNodes]
); );
const isSingleCanvasSelection = selectedCanvasNodes.length === 1;
const selectedComposerTarget = useMemo(() => {
if (isPreviewMode) return null;
if (!isSingleCanvasSelection) return null;
const node = selectedCanvasNodes[0];
if (!node || node.type === "group") return null;
const position = absoluteNodePositions.get(node.id) ?? node.position;
const dimensions = getWorkflowNodeDimensions(node);
return {
nodeId: node.id,
position: {
x: position.x + dimensions.width / 2,
y: position.y + dimensions.height,
},
};
}, [absoluteNodePositions, isPreviewMode, isSingleCanvasSelection, selectedCanvasNodes]);
const [globalComposerTarget, setGlobalComposerTarget] = useState<{
nodeId: string;
selected: boolean;
position: { x: number; y: number };
} | null>(null);
useEffect(() => {
if (selectedComposerTarget) {
setGlobalComposerTarget({ ...selectedComposerTarget, selected: true });
return;
}
setGlobalComposerTarget((current) => current ? { ...current, selected: false } : null);
}, [selectedComposerTarget]);
const selectedBatchSourceNodeIds = useMemo(() => { const selectedBatchSourceNodeIds = useMemo(() => {
const selectedGroupIds = selectedCanvasNodes const selectedGroupIds = selectedCanvasNodes
@ -3193,13 +3161,6 @@ export function WorkflowCanvas() {
}} }}
/> />
)} )}
{globalComposerTarget ? (
<NodeGenerationComposer
nodeId={globalComposerTarget.nodeId}
selected={globalComposerTarget.selected}
position={globalComposerTarget.position}
/>
) : null}
</ViewportPortal> </ViewportPortal>
</ReactFlow> </ReactFlow>
</NodeHeaderProvider> </NodeHeaderProvider>

22
src/components/composer/NodeGenerationComposer.tsx

@ -14,7 +14,6 @@ import { isSmartImageAutoTaskNode } from "@/utils/smartImageMode";
interface NodeGenerationComposerProps { interface NodeGenerationComposerProps {
nodeId: string; nodeId: string;
selected: boolean; selected: boolean;
position?: { x: number; y: number };
} }
function getNodeImageSource(node: WorkflowNode | null): string | null { function getNodeImageSource(node: WorkflowNode | null): string | null {
@ -49,18 +48,15 @@ function isAutoImageTaskNode(node: WorkflowNode | null): boolean {
export function NodeGenerationComposer({ export function NodeGenerationComposer({
nodeId, nodeId,
selected, selected,
position,
}: NodeGenerationComposerProps) { }: NodeGenerationComposerProps) {
return <EditableNodeGenerationComposer nodeId={nodeId} selected={selected} position={position} />; return <EditableNodeGenerationComposer nodeId={nodeId} selected={selected} />;
} }
function NodeGenerationComposerFrame({ function NodeGenerationComposerFrame({
hidden, hidden,
position,
children, children,
}: { }: {
hidden: boolean; hidden: boolean;
position: { x: number; y: number };
children: ReactNode; children: ReactNode;
}) { }) {
const { zoom } = useViewport(); const { zoom } = useViewport();
@ -70,14 +66,10 @@ function NodeGenerationComposerFrame({
<div <div
data-canvas-generator-root data-canvas-generator-root
hidden={hidden} hidden={hidden}
className="pointer-events-none absolute overflow-visible" className="pointer-events-none absolute left-0 top-0 h-full w-full overflow-visible"
style={{
left: `${position.x}px`,
top: `${position.y}px`,
}}
> >
<div <div
className="node-floating-ui nodrag nopan nowheel pointer-events-auto absolute z-[10020]" className="node-floating-ui nodrag nopan nowheel pointer-events-auto absolute left-1/2 top-full z-[10020]"
style={{ style={{
transform: `translateX(-50%) translateY(${12 / safeZoom}px) scale(${1 / safeZoom})`, transform: `translateX(-50%) translateY(${12 / safeZoom}px) scale(${1 / safeZoom})`,
transformOrigin: "top center", transformOrigin: "top center",
@ -89,11 +81,7 @@ function NodeGenerationComposerFrame({
); );
} }
function EditableNodeGenerationComposer({ function EditableNodeGenerationComposer({ nodeId, selected }: Pick<NodeGenerationComposerProps, "nodeId" | "selected">) {
nodeId,
selected,
position = { x: 0, y: 0 },
}: NodeGenerationComposerProps) {
const selectedNodeCount = useSelectedNodeCount(); const selectedNodeCount = useSelectedNodeCount();
const nodes = useWorkflowStore((state) => state.nodes); const nodes = useWorkflowStore((state) => state.nodes);
const edges = useWorkflowStore((state) => state.edges); const edges = useWorkflowStore((state) => state.edges);
@ -157,7 +145,7 @@ function EditableNodeGenerationComposer({
} }
return ( return (
<NodeGenerationComposerFrame hidden={isFrameHidden} position={position}> <NodeGenerationComposerFrame hidden={isFrameHidden}>
{canUseMultiAngle && context.node && imageSource ? ( {canUseMultiAngle && context.node && imageSource ? (
<NodeMultiAnglePanel node={context.node} imageSource={imageSource} onClose={closePanel} /> <NodeMultiAnglePanel node={context.node} imageSource={imageSource} onClose={closePanel} />
) : null} ) : null}

2
src/components/nodes/BaseNode.tsx

@ -4,6 +4,7 @@ import { ReactNode, useRef, useLayoutEffect } from "react";
import { Node, useReactFlow } from "@xyflow/react"; import { Node, useReactFlow } from "@xyflow/react";
import { useWorkflowStore } from "@/store/workflowStore"; import { useWorkflowStore } from "@/store/workflowStore";
import { isPanningRef, isDraggingNodeRef } from "@/components/WorkflowCanvas"; import { isPanningRef, isDraggingNodeRef } from "@/components/WorkflowCanvas";
import { NodeGenerationComposer } from "@/components/composer/NodeGenerationComposer";
import { useCanvasPreviewMode } from "@/components/CanvasPreviewModeContext"; import { useCanvasPreviewMode } from "@/components/CanvasPreviewModeContext";
import { useI18n } from "@/i18n"; import { useI18n } from "@/i18n";
import { isPopiProviderMode } from "@/lib/providerMode"; import { isPopiProviderMode } from "@/lib/providerMode";
@ -326,6 +327,7 @@ export function BaseNode({
{header} {header}
<div ref={contentRef} style={{ contain: "layout style" }} className={contentClassName ?? (autoSizeContent ? "relative h-full w-full" : fullBleed ? "flex-1 min-h-0 relative" : "px-3 pb-4 flex-1 min-h-0 overflow-visible flex flex-col")}>{children}</div> <div ref={contentRef} style={{ contain: "layout style" }} className={contentClassName ?? (autoSizeContent ? "relative h-full w-full" : fullBleed ? "flex-1 min-h-0 relative" : "px-3 pb-4 flex-1 min-h-0 overflow-visible flex flex-col")}>{children}</div>
{floatingPanel} {floatingPanel}
{!isPreviewMode && <NodeGenerationComposer nodeId={id} selected={selected} />}
</div> </div>
{settingsPanel && ( {settingsPanel && (
<div ref={settingsPanelRef}> <div ref={settingsPanelRef}>

Loading…
Cancel
Save