perf(*): 创作工作台 - 优化首页按需加载与画布交互并修复历史迁移冲突

This commit is contained in:
ddCat committed 2026-09-05 13:24:10 +08:00
1 parent ca7ad1f9b2
commit 711d1b79eb
107 files changed
+3025 -1089

No files matched your search

@@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef, useState, type CSSProperties, type KeyboardEvent as ReactKeyboardEvent, type PointerEvent as ReactPointerEvent, type ReactNode } from "react";
import { ArrowUp, AtSign, Boxes, ChevronDown, FileText, ImageIcon, ImagePlus, LoaderCircle, Maximize2, Music2, Pencil, SlidersHorizontal, UserRound, Video, WandSparkles, X } from "lucide-react";
import { useEffect, useMemo, useRef, useState, type CSSProperties, type KeyboardEvent as ReactKeyboardEvent, type PointerEvent as ReactPointerEvent } from "react";
import { ArrowUp, AtSign, ChevronDown, FileText, ImageIcon, ImagePlus, LoaderCircle, Maximize2, Music2, Pencil, SlidersHorizontal, UserRound, Video, WandSparkles, X } from "lucide-react";
import { Button, Image as AntImage, InputNumber, Modal, Tooltip } from "antd";
import { ModelPicker } from "@/components/model-picker";
@@ -244,32 +244,29 @@ export function CanvasNodePromptPanel({ node, isRunning, onPromptChange, onConfi
<span className="grid size-3.5 shrink-0 place-items-center" style={{ color: monochromeAccent }}>
<GenerationModeIcon mode={mode} />
</span>
<span className="truncate text-[var(--fs-tiny)] font-medium">{modeDisplayName(mode)}创作</span>
<span className="truncate text-[var(--fs-tiny)] font-medium">{modeDisplayName(mode)}生成</span>
</div>
)}
{!simpleMode ? <CanvasPresetPicker mode={mode} skillReferences={skillReferences} open={expanded ? expandedPresetOpen : presetOpen} onOpenChange={expanded ? setExpandedPresetOpen : setPresetOpen} onSelect={applyPreset} dense /> : null}
<div className="ml-auto flex shrink-0 items-center justify-end gap-1">
{!simpleMode ? <CanvasPresetPicker mode={mode} skillReferences={skillReferences} open={expanded ? expandedPresetOpen : presetOpen} onOpenChange={expanded ? setExpandedPresetOpen : setPresetOpen} onSelect={applyPreset} dense appearance="quiet" /> : null}
{canOptimizePrompt ? (
<Tooltip title="用 AI 优化提示词">
<Tooltip title="用 AI 润色提示词">
<button
type="button"
className="canvas-node-composer-icon-button inline-flex h-6 shrink-0 items-center gap-1 rounded-md px-1.5 transition-[background-color,filter] hover:brightness-125 focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-1 motion-reduce:hover:translate-y-0"
style={{ background: controlSurface, color: theme.node.text, outlineColor: monochromeAccent }}
className="canvas-node-composer-header-action inline-flex h-6 shrink-0 items-center gap-1 rounded-md px-1.5"
onClick={() => setPromptOptimizerOpen(true)}
aria-label="优化提示词"
aria-label="润色提示词"
>
<WandSparkles className="size-3" />
<span className="hidden text-[var(--fs-micro)] font-medium sm:inline">优化</span>
<span className="text-[var(--fs-tiny)] font-medium">润色</span>
</button>
</Tooltip>
) : null}
<div className="ml-auto flex shrink-0 items-center justify-end gap-1">
{activeReferenceCount ? <ComposerPill theme={theme} icon={<Boxes className="size-2.5" />} label={`参考 ${activeReferenceCount}`} /> : null}
{!expanded && canExpandPrompt ? (
<Tooltip title="放大编辑">
<button
type="button"
className="canvas-node-composer-icon-button grid size-6 shrink-0 place-items-center rounded-md transition-[background-color,filter] hover:brightness-125 focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-1 motion-reduce:hover:translate-y-0"
style={{ background: controlSurface, color: theme.node.text, outlineColor: monochromeAccent }}
className="canvas-node-composer-header-action grid size-6 shrink-0 place-items-center rounded-md"
onClick={() => setExpandedPromptOpen(true)}
aria-label="放大编辑提示词"
>
@@ -281,8 +278,7 @@ export function CanvasNodePromptPanel({ node, isRunning, onPromptChange, onConfi
<Tooltip title="关闭">
<button
type="button"
className="canvas-node-composer-icon-button grid size-6 shrink-0 place-items-center rounded-md transition-[background-color,filter] hover:brightness-125 focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-1 motion-reduce:hover:translate-y-0"
style={{ background: controlSurface, color: theme.node.text, outlineColor: monochromeAccent }}
className="canvas-node-composer-header-action grid size-6 shrink-0 place-items-center rounded-md"
onClick={onClose}
aria-label="关闭创作面板"
>
@@ -508,15 +504,6 @@ export function CanvasNodePromptPanel({ node, isRunning, onPromptChange, onConfi
);
}
function ComposerPill({ theme, icon, label }: { theme: CanvasTheme; icon: ReactNode; label: string }) {
return (
<span className="inline-flex h-6 shrink-0 items-center gap-1 rounded-[var(--r-sm)] px-1.5 text-[var(--fs-micro)] font-medium" style={{ background: "var(--canvas-composer-control-surface)", color: theme.node.activeStroke }}>
{icon}
{label}
</span>
);
}
function GenerationModeIcon({ mode }: { mode: CanvasNodeGenerationMode }) {
if (mode === "image") return <ImagePlus className="size-3" />;
if (mode === "video") return <Video className="size-3" />;
@@ -539,6 +526,9 @@ function ConnectedReferenceShelf({ references, theme, onInsert, onRemove }: { re
return (
<>
<div className="canvas-node-composer-references thin-scrollbar" role="group" aria-label="已连接素材">
<span className="canvas-node-composer-reference-heading">
{activeReferences.every((reference) => reference.kind === "image" || reference.kind === "character") ? "参考图" : "参考素材"} · {activeReferences.length}
</span>
{activeReferences.map((reference) => {
const canPreview = (reference.kind === "image" || reference.kind === "character") && Boolean(reference.previewUrl);
return (