From 31046eae5852f859e36613afa0fae0a562ef284d Mon Sep 17 00:00:00 2001 From: Stringadmin Date: Fri, 5 Jun 2026 18:08:16 +0800 Subject: [PATCH] refactor: extract canvas text prompt composer --- src/features/canvas/CanvasPage.tsx | 131 ++----------- .../canvas/CanvasTextPromptComposer.tsx | 174 ++++++++++++++++++ 2 files changed, 190 insertions(+), 115 deletions(-) create mode 100644 src/features/canvas/CanvasTextPromptComposer.tsx diff --git a/src/features/canvas/CanvasPage.tsx b/src/features/canvas/CanvasPage.tsx index b965207..5597763 100644 --- a/src/features/canvas/CanvasPage.tsx +++ b/src/features/canvas/CanvasPage.tsx @@ -186,6 +186,7 @@ import { } from "./canvasWorkflowDeserialize"; import { CanvasNodeToolbar, CanvasNodeVideoPlayer, CanvasSelectChip } from "./canvasComponents"; import type { CanvasNodeToolbarAction } from "./canvasComponents"; +import { CanvasTextPromptComposer } from "./CanvasTextPromptComposer"; import { CanvasMultiGridPanel, CanvasUpscalePanel, CanvasInpaintPanel } from "./canvasToolPanels"; import { CanvasSmoothedProgressRing } from "./CanvasSmoothedProgressRing"; @@ -4156,121 +4157,21 @@ function CanvasPage({ onMouseDown={(event) => handleNodeResizeStart(event, "text", textNode.id, textNode.size)} /> - {textNodeActive && !isCanvasNodeMoving ? (() => { - const mentionOptions = buildNodeMentionOptions("text", textNode.id, imageNodes, videoNodes, textNodes, getIncomingCanvasPorts, manualLinks); - const mentionState = textNodeMentionStates[textNode.id] || { open: false, query: "", start: 0, caret: 0, activeIndex: 0 }; - const filteredMentions = mentionState.open - ? mentionOptions.filter((o) => !mentionState.query || o.searchText.includes(mentionState.query.toLowerCase())) - : []; - - const handlePromptChange = (e: React.ChangeEvent) => { - const value = e.target.value; - const caret = e.target.selectionStart || 0; - updateTextNodePrompt(textNode.id, value); - - // Detect @-mention trigger - const beforeCaret = value.slice(0, caret); - const atIdx = beforeCaret.lastIndexOf("@"); - if (atIdx >= 0) { - const query = beforeCaret.slice(atIdx + 1); - if (!MENTION_BOUNDARY_RE.test(query) && !query.includes(" ")) { - setTextNodeMentionStates((prev) => ({ ...prev, [textNode.id]: { open: true, query, start: atIdx, caret, activeIndex: 0 } })); - return; - } - } - closeTextNodeMention(textNode.id); - }; - - const handlePromptKeyDown = (e: React.KeyboardEvent) => { - if (!mentionState.open || filteredMentions.length === 0) return; - if (e.key === "ArrowDown") { - e.preventDefault(); - setTextNodeMentionStates((prev) => ({ ...prev, [textNode.id]: { ...mentionState, activeIndex: (mentionState.activeIndex + 1) % filteredMentions.length } })); - } else if (e.key === "ArrowUp") { - e.preventDefault(); - setTextNodeMentionStates((prev) => ({ ...prev, [textNode.id]: { ...mentionState, activeIndex: (mentionState.activeIndex - 1 + filteredMentions.length) % filteredMentions.length } })); - } else if (e.key === "Enter" || e.key === "Tab") { - e.preventDefault(); - const opt = filteredMentions[mentionState.activeIndex]; - if (opt) { - const ta = e.currentTarget; - insertTextNodeMention(textNode.id, opt, ta); - } - } else if (e.key === "Escape") { - e.preventDefault(); - closeTextNodeMention(textNode.id); - } - }; - - const handlePromptSelect = (e: React.SyntheticEvent) => { - const ta = e.currentTarget; - const caret = ta.selectionStart || 0; - setTextNodeMentionStates((prev) => { - const cur = prev[textNode.id]; - if (!cur?.open) return prev; - return { ...prev, [textNode.id]: { ...cur, caret } }; - }); - }; - - return ( -
-
-