style: refine realtime canvas dialog ui
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-02-22 03:24:44 +08:00
parent 153efc8f96
commit 080328fbeb

View File

@ -612,75 +612,89 @@ export function RealtimeCanvasDialog({
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="h-[88vh] w-[95vw] max-w-[1400px] overflow-hidden rounded-2xl border border-zinc-200 bg-[#f6f7f8] p-0">
<DialogHeader className="border-b border-zinc-200 px-5 py-3">
<div className="flex items-center justify-between gap-3">
<DialogContent className="h-[90vh] w-[96vw] max-w-[1540px] overflow-hidden rounded-[24px] border border-zinc-200/80 bg-[#f2f3f5] p-0 shadow-2xl">
<DialogHeader className="border-b border-zinc-200/80 bg-white/70 px-6 py-4 backdrop-blur">
<div className="flex items-start justify-between gap-4">
<div>
<DialogTitle className="text-[17px] font-semibold text-zinc-900">实时画布</DialogTitle>
<DialogDescription className="text-[12px] text-zinc-500">
左侧画草图,右侧实时预览,底部输入提示词后持续渲染
<DialogTitle className="text-[24px] font-semibold tracking-tight text-zinc-900">
实时画布
</DialogTitle>
<DialogDescription className="mt-1 text-[13px] text-zinc-500">
左侧绘制草图,右侧实时渲染,整体风格与无限画布保持一致
</DialogDescription>
<div className="mt-2 text-[12px] text-zinc-500">{statusMessage}</div>
</div>
<div className="flex items-center gap-2">
<span
className={cn(
"inline-flex items-center rounded-full px-2.5 py-1 text-[11px] font-medium",
"inline-flex h-9 items-center gap-1.5 rounded-full px-3 text-[12px] font-medium",
statusBadge.className
)}
>
{status === "error" ? <WifiOff className="mr-1 h-3.5 w-3.5" /> : <Wifi className="mr-1 h-3.5 w-3.5" />}
{status === "error" ? <WifiOff className="h-3.5 w-3.5" /> : <Wifi className="h-3.5 w-3.5" />}
{statusBadge.label}
</span>
<Button type="button" variant="outline" size="sm" onClick={forceReconnect}>
<RefreshCw className="mr-1 h-3.5 w-3.5" />
<Button
type="button"
variant="outline"
size="sm"
onClick={forceReconnect}
className="h-9 rounded-xl border-zinc-300 bg-white px-3"
>
<RefreshCw className="mr-1.5 h-3.5 w-3.5" />
重连
</Button>
</div>
</div>
<div className="mt-1 text-[11px] text-zinc-500">{statusMessage}</div>
</DialogHeader>
<div className="flex min-h-0 flex-1 flex-col">
<div className="grid min-h-0 flex-1 grid-cols-2 gap-3 px-4 py-3">
<div className="flex min-h-0 flex-col rounded-xl border border-zinc-200 bg-white/85">
<div className="flex items-center justify-between border-b border-zinc-200 px-3 py-2">
<span className="text-[12px] font-medium text-zinc-700">草图画布</span>
<div className="flex min-h-0 flex-1 flex-col px-5 pb-5 pt-4">
<div className="grid min-h-0 flex-1 grid-cols-2 gap-4">
<div className="flex min-h-0 flex-col overflow-hidden rounded-2xl border border-zinc-200 bg-white/95 shadow-sm">
<div className="flex h-12 items-center justify-between border-b border-zinc-200 px-4">
<span className="text-[15px] font-medium text-zinc-800">草图画布</span>
<div className="flex items-center gap-1.5">
<Button
type="button"
size="icon"
variant={brushMode === "brush" ? "default" : "outline"}
className="h-7 w-7"
className="h-8 w-8 rounded-lg"
onClick={() => setBrushMode("brush")}
>
<Paintbrush className="h-3.5 w-3.5" />
<Paintbrush className="h-4 w-4" />
</Button>
<Button
type="button"
size="icon"
variant={brushMode === "eraser" ? "default" : "outline"}
className="h-7 w-7"
className="h-8 w-8 rounded-lg"
onClick={() => setBrushMode("eraser")}
>
<Eraser className="h-3.5 w-3.5" />
<Eraser className="h-4 w-4" />
</Button>
<Button type="button" size="icon" variant="outline" className="h-7 w-7" onClick={clearSketch}>
<Trash2 className="h-3.5 w-3.5" />
<Button
type="button"
size="icon"
variant="outline"
className="h-8 w-8 rounded-lg"
onClick={clearSketch}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
</div>
<div ref={canvasWrapRef} className="min-h-0 flex-1 bg-[#d8d8db] p-2">
<div ref={canvasWrapRef} className="min-h-0 flex-1 bg-[#d0d1d4] p-3">
<canvas
ref={canvasRef}
className="h-full w-full touch-none rounded-md border border-zinc-300 bg-[#d8d8db]"
className="h-full w-full touch-none rounded-xl border border-zinc-300/80 bg-[#d0d1d4]"
onPointerDown={handlePointerDown}
onPointerMove={handlePointerMove}
onPointerUp={handlePointerUp}
onPointerCancel={handlePointerUp}
/>
</div>
<div className="flex items-center gap-2 border-t border-zinc-200 px-3 py-2">
<Label htmlFor="rt-brush-size" className="text-[11px] text-zinc-500">
<div className="flex h-12 items-center gap-3 border-t border-zinc-200 px-4">
<Label htmlFor="rt-brush-size" className="text-[12px] font-medium text-zinc-500">
画笔粗细
</Label>
<input
@ -691,60 +705,72 @@ export function RealtimeCanvasDialog({
step={1}
value={brushSize}
onChange={(event) => setBrushSize(Number(event.target.value))}
className="h-2 w-32 accent-zinc-800"
className="h-2 w-40 accent-zinc-800"
/>
<span className="text-[11px] text-zinc-500">{brushSize}px</span>
<span className="text-[12px] text-zinc-500">{brushSize}px</span>
</div>
</div>
<div className="flex min-h-0 flex-col rounded-xl border border-zinc-200 bg-white/85">
<div className="flex items-center justify-between border-b border-zinc-200 px-3 py-2">
<span className="text-[12px] font-medium text-zinc-700">实时预览</span>
<Button type="button" variant="outline" size="sm" onClick={() => scheduleFlush(true)}>
<Sparkles className="mr-1 h-3.5 w-3.5" />
<div className="flex min-h-0 flex-col overflow-hidden rounded-2xl border border-zinc-200 bg-white/95 shadow-sm">
<div className="flex h-12 items-center justify-between border-b border-zinc-200 px-4">
<span className="text-[15px] font-medium text-zinc-800">实时预览</span>
<Button
type="button"
variant="outline"
size="sm"
className="h-8 rounded-lg border-zinc-300 bg-white px-3 text-[12px]"
onClick={() => scheduleFlush(true)}
>
<Sparkles className="mr-1.5 h-3.5 w-3.5" />
刷新
</Button>
</div>
<div className="relative min-h-0 flex-1 overflow-hidden rounded-b-xl bg-[#d2d3d6]">
{previewUrl ? (
<img src={previewUrl} alt="实时预览" className="h-full w-full object-contain" />
) : (
<div className="flex h-full items-center justify-center text-[12px] text-zinc-500">
{status === "connecting" ? (
<span className="inline-flex items-center">
<Loader2 className="mr-1.5 h-3.5 w-3.5 animate-spin" />
正在等待首帧...
</span>
) : (
<span>开始绘制后将显示实时预览</span>
)}
</div>
)}
<div className="relative min-h-0 flex-1 overflow-hidden bg-[#d0d1d4] p-3">
<div className="h-full w-full overflow-hidden rounded-xl border border-zinc-300/80 bg-[#d0d1d4]">
{previewUrl ? (
<img src={previewUrl} alt="实时预览" className="h-full w-full object-contain" />
) : (
<div className="flex h-full items-center justify-center text-[13px] text-zinc-500">
{status === "connecting" ? (
<span className="inline-flex items-center rounded-full bg-white/70 px-3 py-1.5">
<Loader2 className="mr-2 h-3.5 w-3.5 animate-spin" />
正在等待首帧...
</span>
) : (
<span>开始绘制后将显示实时预览</span>
)}
</div>
)}
</div>
</div>
</div>
</div>
<div className="border-t border-zinc-200 bg-white/90 px-4 py-3">
<div className="mt-4 rounded-2xl border border-zinc-200 bg-white/95 p-3 shadow-sm">
<div className="flex items-center gap-2">
<Input
value={prompt}
onChange={(event) => setPrompt(event.target.value)}
onBlur={() => scheduleFlush(true)}
placeholder="输入提示词,例如:将草图渲染为高端工业设计风格..."
className="h-10 flex-1 rounded-xl border-zinc-300 bg-white"
className="h-11 flex-1 rounded-xl border-zinc-300 bg-white text-[15px]"
/>
<Button
type="button"
className="h-10 rounded-xl"
className="h-11 rounded-xl px-5"
disabled={!previewUrl || isApplying}
onClick={uploadPreviewToCanvas}
>
{isApplying ? <Loader2 className="mr-1.5 h-4 w-4 animate-spin" /> : <Send className="mr-1.5 h-4 w-4" />}
{isApplying ? (
<Loader2 className="mr-1.5 h-4 w-4 animate-spin" />
) : (
<Send className="mr-1.5 h-4 w-4" />
)}
发送到画布
</Button>
</div>
<div className="mt-2 text-[11px] text-zinc-500">
提示:绘制过程会自动触发实时渲染。当前发送节流 {adaptiveThrottleMs}ms。
<div className="mt-2 text-[12px] text-zinc-500">
绘制会自动触发实时渲染,当前节流间隔 {adaptiveThrottleMs}ms。
</div>
</div>
</div>