From f8cb3d02013627debb63f01401e8b9e0076b14ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=88=B1=E7=AC=91=E7=9A=84=E6=AF=9B=E6=AF=9B=E8=99=AB?= <315515767@qq.com> Date: Mon, 3 Aug 2026 08:40:04 +0800 Subject: [PATCH] =?UTF-8?q?fix(canvas):=20=E5=88=86=E9=95=9C=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E8=8A=82=E7=82=B9=E6=96=87=E5=AD=97=E6=BA=A2=E5=87=BA?= =?UTF-8?q?=E4=B8=8E=E8=A7=86=E9=A2=91=E6=A8=A1=E5=9E=8B=E4=B8=8B=E6=8B=89?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=20#84=20(#85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(canvas): 分镜脚本节点文字溢出与视频模型下拉异常 #84 - CompactInput 补齐 overflow-y-auto/whitespace-pre-wrap/break-words,长文本不再横向溢出撑开列宽 - filterModelsByCapability 增加 modelCosts[].protocol 协议层回退,未显式配置 capability 时通过协议识别能力 - isVideoModelName 扩展 pika/runway/gen-3/hunyuan-video 等主流视频模型名关键字 - ModelPicker.options 保留当前选中 value,避免过滤后 options 为空触发跳转设置页 * fix(canvas): 模型过滤协议反向排除 + 分镜脚本节点模型选择提示 #84 - filterModelsByCapability 协议层提升至最高优先级,协议明确属于其他能力时直接排除, 防止 video/image/audio 协议的模型被误标为 text 后混入文本下拉 - isVideoModelName 补充 cogvideo/mochi/latte/stable-video/svd/animatediff/ltx-video/minimax-video/abab-video 等关键字 - isImageModelName 补充 nano-banana/ideogram/recraft/playground/leonardo 等关键字 - 分镜脚本节点 ModelPicker 包裹 Tooltip,提示"脚本生成仅支持文本模型" * fix(canvas): 节点标题截断修复 — flex min-width 约束 + 宽度边界 + hover tooltip - NodeExternalHeader: 容器增加 max-w-full 双重约束,窄节点时不越界,宽节点时上限 240px - NodeExternalHeader: 标题 span 增加 min-w-0,解决 flex min-width:auto 导致 truncate 失效 - 所有节点标题(绘图/技能/角色参考/音频/分镜脚本)增加 title 属性,hover 显示完整标题 - AudioNodeContent: 标题 span 增加 min-w-0,修复 flex 子项截断 * fix(canvas): 节点顶部标题真正截断 — JS 计算 maxWidth + overflow-hidden + flex-1 - NodeExternalHeader 容器: 移除 Tailwind 级联冲突的 max-w-[240px]/max-w-full, 改用 JS 计算 Math.min(240, node.width) 作为 inline style maxWidth, 从根本上解决两个 max-width 声明的优先级不确定问题 - 容器增加 overflow-hidden 作为第二道防线 - 内部 text/input/button 元素改用 flex-1 + min-w-0 替代固定 max-w-[Npx], 让 flex 子项自动填充可用空间,文本在剩余空间内截断 * fix(canvas): 节点标题缩放溢出 — 考虑 canvas zoom scale - maxHeaderWidth 改为 Math.min(240, node.width * scale), 因为 header 的视觉宽度 = maxHeaderWidth(inverseScale 与 canvas scale 抵消), 而节点的视觉宽度 = node.width * scale,两者必须比较同一维度 - 修复 zoom 缩小时(scale < 1)header 视觉宽度超过节点视觉宽度的溢出问题 --------- Co-authored-by: sikongyue <3155157671@qq.com> --- web/src/components/canvas/canvas-node.tsx | 21 ++++---- .../components/canvas/canvas-script-node.tsx | 32 +++++++------ web/src/components/model-picker.tsx | 7 ++- web/src/stores/use-config-store.ts | 48 +++++++++++++++++-- 4 files changed, 77 insertions(+), 31 deletions(-) diff --git a/web/src/components/canvas/canvas-node.tsx b/web/src/components/canvas/canvas-node.tsx index 3e0ec360d..c7fb6cf78 100644 --- a/web/src/components/canvas/canvas-node.tsx +++ b/web/src/components/canvas/canvas-node.tsx @@ -559,7 +559,7 @@ function DrawingContent({ node, theme, drawingProjectId }: NodeContentRendererPr )}
-
{node.title || "绘图"}
+
{node.title || "绘图"}
{shapeCount} 个图形 · {pageCount} 个页面
@@ -723,7 +723,7 @@ function SkillContent({ node, theme }: NodeContentRendererProps) {
-
{skill?.name || node.title || "技能"}
+
{skill?.name || node.title || "技能"}
{skillCategoryLabel(skill?.category)} · @@ -870,7 +870,7 @@ function EmptyImageContent({ node, theme, isBatchRoot, batchCount, batchExpanded
{isCharacterReference ? (
-
{node.metadata?.characterName || node.title}
+
{node.metadata?.characterName || node.title}
多视角参考 · 待生成
) : 空图片节点} @@ -924,7 +924,7 @@ function AudioNodeContent({ node, theme }: NodeContentRendererProps) {
- {node.title || "音频"} + {node.title || "音频"}
@@ -1137,11 +1137,12 @@ function NodeExternalHeader({ node, scale, active, editable, editing, draft, the if (scale < NODE_EXTERNAL_HEADER_MIN_SCALE && !editing) return null; const inverseScale = 1 / Math.max(scale, 0.05); const Icon = nodeTypeIcon(node.type); + const maxHeaderWidth = Math.min(240, node.width * scale); return (
event.stopPropagation()} onPointerDown={(event) => event.stopPropagation()} > @@ -1150,7 +1151,7 @@ function NodeExternalHeader({ node, scale, active, editable, editing, draft, the onDraftChange(event.target.value)} onFocus={(event) => event.currentTarget.select()} @@ -1162,12 +1163,12 @@ function NodeExternalHeader({ node, scale, active, editable, editing, draft, the aria-label="节点名称" /> ) : editable ? ( - ) : ( - {node.title} + {node.title} )}
); diff --git a/web/src/components/canvas/canvas-script-node.tsx b/web/src/components/canvas/canvas-script-node.tsx index f676549af..7e072910b 100644 --- a/web/src/components/canvas/canvas-script-node.tsx +++ b/web/src/components/canvas/canvas-script-node.tsx @@ -126,7 +126,7 @@ export function CanvasScriptNodeContent({ node, batch, pipeline, scale, mentionR
event.stopPropagation()}>
- {node.title || "分镜脚本"} + {node.title || "分镜脚本"} {batchSummary ? {batchSummary} : taskFeedback ? {taskFeedback} : null} {rows.length} 镜 · {totalDuration}s {batch ? <> @@ -210,19 +210,21 @@ export function CanvasScriptNodeContent({ node, batch, pipeline, scale, mentionR onWheel={(event) => event.stopPropagation()} />
event.stopPropagation()} onPointerDown={(event) => event.stopPropagation()}> -
- navigateToSettings({ continueCreation: true })} - /> -
+ +
+ navigateToSettings({ continueCreation: true })} + /> +
+
{simpleMode ? 自动拆分 · 默认时长 : className="min-w-32" size="small" @@ -448,7 +450,7 @@ export function CanvasScriptEditor({ node, open, onClose, onUpdateRows, onVisibl } function CompactInput({ value, placeholder, borderColor, onChange }: { value: string; placeholder: string; borderColor: string; onChange: (value: string) => void }) { - return