From 4a3342c795c5a646d491ea144cc278754d56cc8a Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 9 Jun 2026 13:56:20 +0800 Subject: [PATCH] style: refresh canvas composer UI to mirror Lovart dialog --- components/canvas/CanvasChatPanel.tsx | 167 +++++++++++++------------- 1 file changed, 83 insertions(+), 84 deletions(-) diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index 8812c76f8..66239c741 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -9883,7 +9883,7 @@ export function CanvasChatPanel({ {/* 混合输入区域:使用 contenteditable 实现真正的混合编辑 */}
@@ -10207,79 +10207,81 @@ export function CanvasChatPanel({ ) : null}
) : null} -
{ - // 从 contenteditable 中提取纯文本(排除附件标签文本) - const target = e.target as HTMLDivElement; - const clone = target.cloneNode(true) as HTMLDivElement; - clone.querySelectorAll("[data-attachment-url]").forEach((el) => el.remove()); - const textContent = (clone.textContent || "") - .replace(/\u200b/g, " ") - .replace(/\s+/g, " ") - .trim(); - setInput(textContent); - if ((!isVideoModel || videoFrameSlots.length === 0) && !hasAgentUploadSlots) { - syncAttachmentsWithEditor(target); - } - }} - onKeyDown={(e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - // 🆕 详情模式下,Enter 键默认不直接发送,防止误触。除非使用 Meta+Enter 或 Ctrl+Enter(这里先不处理组合键,仅禁用单键发送) - // 或者如果用户习惯了 Enter 发送,可以保持,但考虑到详情生成成本较高,也许需要确认? - // 暂且保持一致,如果用户在详情模式下按 Enter,也触发 handleSend(内部会做校验) - handleSend(); - } - // 处理 Backspace 删除图片标签(只删除聊天框中的标签,不影响画布) - if (e.key === 'Backspace' && (!isVideoModel || videoFrameSlots.length === 0) && !hasAgentUploadSlots) { - const selection = window.getSelection(); - if (selection && selection.rangeCount > 0) { - const range = selection.getRangeAt(0); - if (range.collapsed && range.startOffset === 0) { - // 检查前一个兄弟节点是否是图片标签 - const prevSibling = range.startContainer.previousSibling as HTMLElement; - if (prevSibling && prevSibling.getAttribute?.('data-attachment-url')) { - e.preventDefault(); - e.stopPropagation(); // 阻止事件冒泡,防止触发全局删除 - // 使用 URL 来删除附件,更可靠 - removeAttachmentByTagElement(prevSibling); - prevSibling.remove(); +
+
{ + // 从 contenteditable 中提取纯文本(排除附件标签文本) + const target = e.target as HTMLDivElement; + const clone = target.cloneNode(true) as HTMLDivElement; + clone.querySelectorAll("[data-attachment-url]").forEach((el) => el.remove()); + const textContent = (clone.textContent || "") + .replace(/\u200b/g, " ") + .replace(/\s+/g, " ") + .trim(); + setInput(textContent); + if ((!isVideoModel || videoFrameSlots.length === 0) && !hasAgentUploadSlots) { + syncAttachmentsWithEditor(target); + } + }} + onKeyDown={(e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + // 🆕 详情模式下,Enter 键默认不直接发送,防止误触。除非使用 Meta+Enter 或 Ctrl+Enter(这里先不处理组合键,仅禁用单键发送) + // 或者如果用户习惯了 Enter 发送,可以保持,但考虑到详情生成成本较高,也许需要确认? + // 暂且保持一致,如果用户在详情模式下按 Enter,也触发 handleSend(内部会做校验) + handleSend(); + } + // 处理 Backspace 删除图片标签(只删除聊天框中的标签,不影响画布) + if (e.key === 'Backspace' && (!isVideoModel || videoFrameSlots.length === 0) && !hasAgentUploadSlots) { + const selection = window.getSelection(); + if (selection && selection.rangeCount > 0) { + const range = selection.getRangeAt(0); + if (range.collapsed && range.startOffset === 0) { + // 检查前一个兄弟节点是否是图片标签 + const prevSibling = range.startContainer.previousSibling as HTMLElement; + if (prevSibling && prevSibling.getAttribute?.('data-attachment-url')) { + e.preventDefault(); + e.stopPropagation(); // 阻止事件冒泡,防止触发全局删除 + // 使用 URL 来删除附件,更可靠 + removeAttachmentByTagElement(prevSibling); + prevSibling.remove(); + } } } } - } - }} - onPaste={(e) => { - const clipboardItems = Array.from(e.clipboardData?.items || []); - const imageFiles = clipboardItems - .filter((item) => item.type.startsWith("image/")) - .map((item) => item.getAsFile()) - .filter((file): file is File => Boolean(file)); + }} + onPaste={(e) => { + const clipboardItems = Array.from(e.clipboardData?.items || []); + const imageFiles = clipboardItems + .filter((item) => item.type.startsWith("image/")) + .map((item) => item.getAsFile()) + .filter((file): file is File => Boolean(file)); + + if (imageFiles.length > 0) { + e.preventDefault(); + void handlePastedImages(imageFiles); + return; + } - if (imageFiles.length > 0) { e.preventDefault(); - void handlePastedImages(imageFiles); - return; - } - - e.preventDefault(); - const text = e.clipboardData.getData('text/plain'); - document.execCommand('insertText', false, text); - }} - style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }} - /> + const text = e.clipboardData.getData('text/plain'); + document.execCommand('insertText', false, text); + }} + style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }} + /> +
{hasAgentUploadSlots && ( -
+
{agentUploadSlots.map((slot, index) => { const slotAttachment = attachments[index]; @@ -10347,7 +10349,7 @@ export function CanvasChatPanel({ )} {isVideoModel && videoFrameSlots.length > 0 && ( -
+
{videoFrameSlots.map((label, index) => { const slotAttachment = attachments[index]; @@ -10417,10 +10419,7 @@ export function CanvasChatPanel({ {/* Bottom Toolbar */}
@@ -10463,13 +10462,13 @@ export function CanvasChatPanel({ {!isWorkspacePresentation ? ( -
) : ( -
+
-
+
{!isWorkspacePresentation && activeComposerMode === "image" ? (