mirror of
https://github.com/basketikun/infinite-canvas.git
synced 2026-08-06 01:14:36 +08:00
feat(canvas): enhance configuration node with new input handling and reference selection features
This commit is contained in:
@@ -24,6 +24,7 @@ import { fitNodeSize, nodeSizeFromRatio } from "../utils/canvas-node-size";
|
||||
import { App, Button, Dropdown, Modal } from "antd";
|
||||
import { NODE_DEFAULT_SIZE, getNodeSpec } from "../constants";
|
||||
import { ActiveConnectionPath, ConnectionPath } from "../components/canvas-connections";
|
||||
import { CanvasConfigComposer } from "../components/canvas-config-composer";
|
||||
import { CanvasConfigNodePanel } from "../components/canvas-config-node-panel";
|
||||
import { CanvasAssistantPanel } from "../components/canvas-assistant-panel";
|
||||
import { CanvasNodeContextMenu } from "../components/canvas-context-menu";
|
||||
@@ -41,7 +42,7 @@ import { CanvasToolbar } from "../components/canvas-toolbar";
|
||||
import { AssetPickerModal, type AssetPickerTab, type InsertAssetPayload } from "../components/asset-picker-modal";
|
||||
import { CanvasZoomControls } from "../components/canvas-zoom-controls";
|
||||
import { useCanvasStore } from "../stores/use-canvas-store";
|
||||
import { buildCanvasResourceReferences, buildInputMentionReferences, buildNodeMentionReferences } from "../utils/canvas-resource-references";
|
||||
import { buildCanvasResourceReferences, buildNodeMentionReferences } from "../utils/canvas-resource-references";
|
||||
import {
|
||||
CanvasNodeType,
|
||||
type CanvasAssistantImage,
|
||||
@@ -638,12 +639,6 @@ function InfiniteCanvasPage() {
|
||||
nodes.forEach((node) => map.set(node.id, buildNodeMentionReferences(node, nodes, connections)));
|
||||
return map;
|
||||
}, [connections, nodes]);
|
||||
const configMentionReferencesById = useMemo(() => {
|
||||
const map = new Map<string, ReturnType<typeof buildInputMentionReferences>>();
|
||||
configInputsById.forEach((inputs, nodeId) => map.set(nodeId, buildInputMentionReferences(inputs)));
|
||||
return map;
|
||||
}, [configInputsById]);
|
||||
|
||||
const createNode = useCallback(
|
||||
(type: CanvasNodeType, position?: Position) => {
|
||||
const targetPosition = position || getCanvasCenter();
|
||||
@@ -1231,7 +1226,8 @@ function InfiniteCanvasPage() {
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement || event.target instanceof HTMLSelectElement) return;
|
||||
const target = event.target instanceof Element ? event.target : null;
|
||||
if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement || event.target instanceof HTMLSelectElement || target?.closest("[contenteditable='true'],[data-canvas-no-zoom]")) return;
|
||||
|
||||
const key = event.key.toLowerCase();
|
||||
const isModifierShortcut = event.metaKey || event.ctrlKey;
|
||||
@@ -1735,12 +1731,13 @@ function InfiniteCanvasPage() {
|
||||
);
|
||||
const effectivePrompt = generationContext.prompt.trim();
|
||||
const markSourceStatus = sourceNode?.type !== CanvasNodeType.Image && !editingTextNode;
|
||||
const statusPrompt = sourceNode?.type === CanvasNodeType.Config ? effectivePrompt : prompt;
|
||||
if (!effectivePrompt && (mode === "text" || mode === "audio")) {
|
||||
setRunningNodeId(null);
|
||||
return;
|
||||
}
|
||||
let pendingChildIds: string[] = [];
|
||||
if (markSourceStatus) setNodes((prev) => prev.map((node) => (node.id === nodeId ? { ...node, metadata: { ...node.metadata, prompt, status: NODE_STATUS_LOADING, errorDetails: undefined } } : node)));
|
||||
if (markSourceStatus) setNodes((prev) => prev.map((node) => (node.id === nodeId ? { ...node, metadata: { ...node.metadata, prompt: statusPrompt, status: NODE_STATUS_LOADING, errorDetails: undefined } } : node)));
|
||||
|
||||
try {
|
||||
if (mode === "image") {
|
||||
@@ -1804,7 +1801,7 @@ function InfiniteCanvasPage() {
|
||||
? isConfigNode
|
||||
? {
|
||||
...node,
|
||||
metadata: { ...node.metadata, prompt, status: NODE_STATUS_LOADING, errorDetails: undefined },
|
||||
metadata: { ...node.metadata, prompt: effectivePrompt, status: NODE_STATUS_LOADING, errorDetails: undefined },
|
||||
}
|
||||
: isEmptyImageNode
|
||||
? {
|
||||
@@ -1955,16 +1952,16 @@ function InfiniteCanvasPage() {
|
||||
const childNodes: CanvasNodeData[] = childIds.map((id, index) => ({
|
||||
id,
|
||||
type: CanvasNodeType.Text,
|
||||
title: prompt.slice(0, 32) || "Generated Text",
|
||||
title: effectivePrompt.slice(0, 32) || "Generated Text",
|
||||
position: {
|
||||
x: parentPosition.x + parentConfig.width + 96,
|
||||
y: parentPosition.y + parentConfig.height / 2 - textConfig.height / 2 + (index - (textCount - 1) / 2) * (textConfig.height + 36),
|
||||
},
|
||||
width: textConfig.width,
|
||||
height: textConfig.height,
|
||||
metadata: { prompt, status: NODE_STATUS_LOADING, fontSize: 14 },
|
||||
metadata: { prompt: effectivePrompt, status: NODE_STATUS_LOADING, fontSize: 14 },
|
||||
}));
|
||||
setNodes((prev) => [...prev.map((node) => (node.id === nodeId && isConfigNode ? { ...node, metadata: { ...node.metadata, prompt, status: NODE_STATUS_LOADING, errorDetails: undefined } } : node)), ...childNodes]);
|
||||
setNodes((prev) => [...prev.map((node) => (node.id === nodeId && isConfigNode ? { ...node, metadata: { ...node.metadata, prompt: effectivePrompt, status: NODE_STATUS_LOADING, errorDetails: undefined } } : node)), ...childNodes]);
|
||||
setConnections((prev) => [...prev, ...childIds.map((childId) => ({ id: nanoid(), fromNodeId: nodeId, toNodeId: childId }))]);
|
||||
}
|
||||
|
||||
@@ -2293,32 +2290,39 @@ function InfiniteCanvasPage() {
|
||||
showImageInfo={showImageInfo}
|
||||
resourceLabel={resourceReferenceByNodeId.get(node.id)}
|
||||
mentionReferences={mentionReferencesByNodeId.get(node.id) || []}
|
||||
renderPanel={(panelNode) => (
|
||||
<CanvasNodePromptPanel
|
||||
node={panelNode}
|
||||
isRunning={runningNodeId === panelNode.id}
|
||||
mentionReferences={mentionReferencesByNodeId.get(panelNode.id) || []}
|
||||
onPromptChange={handleNodePromptChange}
|
||||
onConfigChange={handleConfigNodeChange}
|
||||
onGenerate={handleGenerateNode}
|
||||
onImageSettingsOpenChange={(open) => {
|
||||
setNodeImageSettingsOpen(open);
|
||||
if (open) setToolbarNodeId(null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
renderPanel={(panelNode) =>
|
||||
panelNode.type === CanvasNodeType.Config ? (
|
||||
<CanvasConfigComposer
|
||||
value={panelNode.metadata?.composerContent ?? panelNode.metadata?.prompt ?? ""}
|
||||
inputs={configInputsById.get(panelNode.id) || []}
|
||||
onChange={(composerContent) => handleConfigNodeChange(panelNode.id, { composerContent })}
|
||||
onClose={() => setDialogNodeId(null)}
|
||||
/>
|
||||
) : (
|
||||
<CanvasNodePromptPanel
|
||||
node={panelNode}
|
||||
isRunning={runningNodeId === panelNode.id}
|
||||
mentionReferences={mentionReferencesByNodeId.get(panelNode.id) || []}
|
||||
onPromptChange={handleNodePromptChange}
|
||||
onConfigChange={handleConfigNodeChange}
|
||||
onGenerate={handleGenerateNode}
|
||||
onImageSettingsOpenChange={(open) => {
|
||||
setNodeImageSettingsOpen(open);
|
||||
if (open) setToolbarNodeId(null);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
renderNodeContent={(contentNode) => (
|
||||
<CanvasConfigNodePanel
|
||||
node={contentNode}
|
||||
isRunning={runningNodeId === contentNode.id}
|
||||
inputSummary={getInputSummary(configInputsById.get(contentNode.id) || [])}
|
||||
inputs={configInputsById.get(contentNode.id) || []}
|
||||
mentionReferences={configMentionReferencesById.get(contentNode.id) || []}
|
||||
onConfigChange={handleConfigNodeChange}
|
||||
onTextInputChange={handleNodeContentChange}
|
||||
onComposerToggle={() => setDialogNodeId((current) => (current === contentNode.id ? null : contentNode.id))}
|
||||
onGenerate={(nodeId) => {
|
||||
const target = nodesRef.current.find((item) => item.id === nodeId);
|
||||
void handleGenerateNode(nodeId, target?.metadata?.generationMode || "image", target?.metadata?.prompt || "");
|
||||
void handleGenerateNode(nodeId, target?.metadata?.generationMode || "image", target?.metadata?.composerContent ?? target?.metadata?.prompt ?? "");
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user