fix(canvas): 创作界面 - 统一素材选择并修复画布顶栏与暗色交互
This commit is contained in:
@@ -473,10 +473,10 @@ export function CanvasNodeInfoModal({ node, open, onClose, onMetadataChange, rea
|
||||
};
|
||||
|
||||
const title = (
|
||||
<div className="flex items-center justify-between gap-4 pr-10">
|
||||
<div className="canvas-node-inspector-title">
|
||||
<div className="min-w-0">
|
||||
<div className="text-[var(--fs-heading-lg)] font-semibold tracking-[-0.02em]">节点信息</div>
|
||||
{node ? <div className="mt-0.5 truncate text-xs opacity-45">{node.id}</div> : null}
|
||||
<div className="text-[var(--fs-heading-lg)] font-semibold">节点信息</div>
|
||||
{node ? <div className="canvas-node-inspector-id">{node.id}</div> : null}
|
||||
</div>
|
||||
<Segmented
|
||||
size="small"
|
||||
@@ -497,70 +497,66 @@ export function CanvasNodeInfoModal({ node, open, onClose, onMetadataChange, rea
|
||||
open={open && Boolean(node)}
|
||||
centered
|
||||
footer={null}
|
||||
width={720}
|
||||
width={760}
|
||||
onCancel={onClose}
|
||||
styles={{ body: { paddingTop: 8 } }}
|
||||
styles={{ body: { paddingTop: 4 } }}
|
||||
>
|
||||
{node ? (
|
||||
<div className="h-[min(68vh,640px)] min-h-[420px] text-sm" style={{ color: theme.node.text }}>
|
||||
<div className="canvas-node-inspector" style={{ color: theme.node.text }}>
|
||||
{view === "info" ? (
|
||||
<div className="thin-scrollbar h-full space-y-4 overflow-auto pr-1">
|
||||
<div className="grid gap-2 rounded-2xl border p-3" style={{ background: theme.node.fill, borderColor: theme.node.stroke }}>
|
||||
<div className="grid grid-cols-2 gap-2 max-sm:grid-cols-1">
|
||||
<div className="thin-scrollbar canvas-node-inspector-scroll">
|
||||
<section className="canvas-node-inspector-section">
|
||||
<div className="canvas-node-inspector-section-heading"><span>基础信息</span><em>{node.metadata?.status || "idle"}</em></div>
|
||||
<div className="canvas-node-inspector-facts">
|
||||
<InfoRow label="类型" value={nodeTypeLabel} />
|
||||
<InfoRow label="状态" value={node.metadata?.status || "idle"} />
|
||||
<InfoRow label="尺寸" value={`${Math.round(node.width)} x ${Math.round(node.height)}`} />
|
||||
<InfoRow label="位置" value={`${Math.round(node.position.x)}, ${Math.round(node.position.y)}`} />
|
||||
{batchCount > 1 ? <InfoRow label="图片组" value={`${batchCount} 张`} /> : null}
|
||||
{imageBytes ? <InfoRow label="图片大小" value={formatBytes(imageBytes)} /> : null}
|
||||
</div>
|
||||
{node.type === CanvasNodeType.Image ? (
|
||||
<div className="border-t pt-3" style={{ borderColor: theme.toolbar.border }}>
|
||||
<div className="mb-2 text-xs font-medium opacity-45">项目资产分类</div>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{assetCategoryOptions.map((option) => {
|
||||
const active = assetCategory === option.value;
|
||||
return <button key={option.value} type="button" disabled={readOnly} onClick={() => saveAssetCategory(option.value)} className="h-7 rounded-md border px-2 text-xs font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-60" style={{ borderColor: active ? theme.accent.primary : theme.toolbar.border, background: active ? theme.accent.primarySoft : theme.toolbar.panel, color: active ? theme.accent.primary : theme.node.muted }}>{option.label}</button>;
|
||||
})}
|
||||
</div>
|
||||
<div className="mt-2 text-[var(--fs-label)] leading-5 opacity-45">生成后会按此分类进入项目资产;角色、场景和画风工作流会自动预填。</div>
|
||||
</div>
|
||||
) : null}
|
||||
{node.metadata?.prompt ? (
|
||||
<div className="rounded-xl border px-3 py-2" style={{ borderColor: theme.toolbar.border, background: theme.toolbar.panel }}>
|
||||
<div className="mb-1 text-xs font-medium opacity-45">提示词</div>
|
||||
<div className="whitespace-pre-wrap break-words leading-6">{node.metadata.prompt}</div>
|
||||
</div>
|
||||
) : null}
|
||||
{node.type === CanvasNodeType.Skill && node.metadata?.skillSnapshot ? (
|
||||
<div className="rounded-xl border px-3 py-2" style={{ borderColor: theme.toolbar.border, background: theme.toolbar.panel }}>
|
||||
<div className="mb-1 text-xs font-medium opacity-45">技能模板</div>
|
||||
<div className="whitespace-pre-wrap break-words leading-6">{node.metadata.skillSnapshot.template}</div>
|
||||
{node.metadata.skillSnapshot.outputContract ? (
|
||||
<>
|
||||
<div className="mb-1 mt-3 text-xs font-medium opacity-45">输出约束</div>
|
||||
<div className="whitespace-pre-wrap break-words leading-6">{node.metadata.skillSnapshot.outputContract}</div>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{node.type === CanvasNodeType.Image ? (
|
||||
<div className="rounded-2xl border p-3" style={{ background: theme.toolbar.panel, borderColor: theme.node.stroke }}>
|
||||
<div className="mb-2 flex items-center justify-between gap-3">
|
||||
<section className="canvas-node-inspector-section">
|
||||
<div className="canvas-node-inspector-section-heading"><span>项目资产分类</span></div>
|
||||
<div className="canvas-node-inspector-options">
|
||||
{assetCategoryOptions.map((option) => {
|
||||
const active = assetCategory === option.value;
|
||||
return <button key={option.value} type="button" disabled={readOnly} onClick={() => saveAssetCategory(option.value)} className={active ? "is-active" : ""} style={{ "--inspector-accent": theme.accent.primary, "--inspector-accent-soft": theme.accent.primarySoft } as CSSProperties}>{option.label}</button>;
|
||||
})}
|
||||
</div>
|
||||
<p className="canvas-node-inspector-help">生成后会按此分类进入项目资产;角色、场景和画风工作流会自动预填。</p>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{node.metadata?.prompt ? (
|
||||
<section className="canvas-node-inspector-section">
|
||||
<div className="canvas-node-inspector-section-heading"><span>提示词</span></div>
|
||||
<div className="canvas-node-inspector-copy">{node.metadata.prompt}</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{node.type === CanvasNodeType.Skill && node.metadata?.skillSnapshot ? (
|
||||
<section className="canvas-node-inspector-section">
|
||||
<div className="canvas-node-inspector-section-heading"><span>技能模板</span></div>
|
||||
<div className="canvas-node-inspector-copy">{node.metadata.skillSnapshot.template}</div>
|
||||
{node.metadata.skillSnapshot.outputContract ? <><div className="canvas-node-inspector-subheading">输出约束</div><div className="canvas-node-inspector-copy">{node.metadata.skillSnapshot.outputContract}</div></> : null}
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{node.type === CanvasNodeType.Image ? (
|
||||
<section className="canvas-node-inspector-section">
|
||||
<div className="canvas-node-inspector-section-heading">
|
||||
<div>
|
||||
<div className="text-sm font-semibold">资产标签</div>
|
||||
<div className="mt-0.5 text-xs opacity-45">一条标签描述一个角色、环境、道具或镜头用途。</div>
|
||||
<span>资产标签</span>
|
||||
<p>一条标签描述一个角色、环境、道具或镜头用途。</p>
|
||||
</div>
|
||||
<span className="shrink-0 text-xs opacity-45">{assetTags.length} 条</span>
|
||||
<em>{assetTags.length} 条</em>
|
||||
</div>
|
||||
{readOnly ? (
|
||||
<div className="mb-2 rounded-lg border px-3 py-2 text-xs opacity-55" style={{ borderColor: theme.toolbar.border }}>
|
||||
分享画布为只读,标签无法编辑。
|
||||
</div>
|
||||
<div className="canvas-node-inspector-notice">分享画布为只读,标签无法编辑。</div>
|
||||
) : (
|
||||
<div className="flex gap-2">
|
||||
<div className="canvas-node-inspector-tag-editor">
|
||||
<Input
|
||||
value={assetTagInput}
|
||||
placeholder="例如:角色: 张三"
|
||||
@@ -572,7 +568,7 @@ export function CanvasNodeInfoModal({ node, open, onClose, onMetadataChange, rea
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-3 flex min-h-10 flex-wrap gap-2 rounded-xl border px-2 py-2" style={{ borderColor: theme.toolbar.border, background: theme.node.fill }}>
|
||||
<div className="canvas-node-inspector-tags">
|
||||
{assetTags.length ? (
|
||||
assetTags.map((tag) => (
|
||||
<Tag key={tag} closable={!readOnly} onClose={() => (readOnly ? onUnauthorized?.() : removeAssetTag(tag))} className="!m-0 !rounded-lg !px-2 !py-1 !text-sm">
|
||||
@@ -580,20 +576,20 @@ export function CanvasNodeInfoModal({ node, open, onClose, onMetadataChange, rea
|
||||
</Tag>
|
||||
))
|
||||
) : (
|
||||
<span className="px-1 py-1 text-xs opacity-40">{readOnly ? "暂无标签" : "还没有标签,输入后点击“加入”或按 Enter。"}</span>
|
||||
<span className="canvas-node-inspector-empty-label">{readOnly ? "暂无标签" : "还没有标签,输入后点击“加入”或按 Enter。"}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
{node.metadata?.errorDetails ? (
|
||||
<div className="rounded-2xl border p-3 text-red-500" style={{ borderColor: theme.node.stroke }}>
|
||||
<section className="canvas-node-inspector-error">
|
||||
{generationErrorMessage(node.metadata.errorDetails)}
|
||||
</div>
|
||||
</section>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<pre className="thin-scrollbar h-full overflow-auto rounded-2xl border p-3 text-xs leading-5" style={{ background: theme.node.fill, borderColor: theme.node.stroke, color: theme.node.text }}>
|
||||
<pre className="thin-scrollbar canvas-node-inspector-json" style={{ color: theme.node.text }}>
|
||||
{json}
|
||||
</pre>
|
||||
)}
|
||||
@@ -605,9 +601,9 @@ export function CanvasNodeInfoModal({ node, open, onClose, onMetadataChange, rea
|
||||
|
||||
function InfoRow({ label, value }: { label: string; value: ReactNode }) {
|
||||
return (
|
||||
<div className="rounded-xl border px-3 py-2" style={{ borderColor: "rgba(148,163,184,.22)" }}>
|
||||
<div className="mb-1 text-xs font-medium opacity-45">{label}</div>
|
||||
<div className="min-w-0 whitespace-pre-wrap break-words text-sm font-medium leading-5">{value}</div>
|
||||
<div className="canvas-node-inspector-fact">
|
||||
<div>{label}</div>
|
||||
<strong>{value}</strong>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user