polish realtime canvas toolbar layout
This commit is contained in:
parent
356c46f480
commit
d4ff127fb3
@ -2254,13 +2254,13 @@ export function RealtimeCanvasDialog({
|
||||
</DialogHeader>
|
||||
)}
|
||||
<div className="absolute left-4 right-4 top-4 z-50 pointer-events-none">
|
||||
<div className="pointer-events-auto rounded-[28px] border border-black/10 bg-white/96 shadow-[0_22px_70px_rgba(15,23,42,0.14)] backdrop-blur-sm">
|
||||
<div className="flex flex-wrap items-center gap-2 px-3 py-3">
|
||||
<div className="flex items-center gap-3 rounded-full border border-black/10 bg-zinc-50 px-4 py-2">
|
||||
<div className="pointer-events-auto rounded-[24px] border border-black/10 bg-white/98 shadow-[0_12px_36px_rgba(15,23,42,0.1)]">
|
||||
<div className="flex items-center gap-2 overflow-x-auto px-2 py-2 [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
|
||||
<div className="flex h-10 shrink-0 items-center gap-2 rounded-full border border-black/10 bg-zinc-50 px-3">
|
||||
<span className="text-sm font-semibold text-zinc-900">Realtime Canvas</span>
|
||||
<span
|
||||
className={cn(
|
||||
"flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium",
|
||||
"flex items-center gap-1.5 rounded-full px-2 py-1 text-xs font-medium",
|
||||
statusBadge.className
|
||||
)}
|
||||
>
|
||||
@ -2281,12 +2281,12 @@ export function RealtimeCanvasDialog({
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-1 rounded-full border border-black/10 bg-white px-1.5 py-1">
|
||||
<div className="flex h-10 shrink-0 items-center gap-0.5 rounded-full border border-black/10 bg-white px-1">
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-9 rounded-full px-3 text-xs"
|
||||
className="h-8 rounded-full px-2.5 text-xs"
|
||||
onClick={() => void saveCurrentProject()}
|
||||
>
|
||||
<Save className="mr-1.5 h-3.5 w-3.5" />
|
||||
@ -2296,7 +2296,7 @@ export function RealtimeCanvasDialog({
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-9 rounded-full px-3 text-xs"
|
||||
className="h-8 rounded-full px-2.5 text-xs"
|
||||
onClick={() => {
|
||||
refreshSavedProjects();
|
||||
setProjectPanelOpen((openState) => !openState);
|
||||
@ -2309,7 +2309,7 @@ export function RealtimeCanvasDialog({
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
className="h-9 rounded-full px-3 text-xs"
|
||||
className="h-8 rounded-full px-2.5 text-xs"
|
||||
onClick={startBlankProject}
|
||||
>
|
||||
<Plus className="mr-1.5 h-3.5 w-3.5" />
|
||||
@ -2320,7 +2320,7 @@ export function RealtimeCanvasDialog({
|
||||
type="button"
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-9 w-9 rounded-full"
|
||||
className="h-8 w-8 rounded-full"
|
||||
onClick={() => onOpenChange(false)}
|
||||
title="返回画布"
|
||||
>
|
||||
@ -2329,12 +2329,12 @@ export function RealtimeCanvasDialog({
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-1 rounded-full border border-black/10 bg-white px-1.5 py-1">
|
||||
<div className="flex h-10 shrink-0 items-center gap-0.5 rounded-full border border-black/10 bg-white px-1">
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className={cn(
|
||||
"h-10 w-10 rounded-full text-zinc-600 hover:bg-zinc-100",
|
||||
"h-8 w-8 rounded-full text-zinc-600 hover:bg-zinc-100",
|
||||
brushMode === "brush" && "bg-black text-white hover:bg-black"
|
||||
)}
|
||||
onClick={() => setBrushMode("brush")}
|
||||
@ -2346,7 +2346,7 @@ export function RealtimeCanvasDialog({
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className={cn(
|
||||
"h-10 w-10 rounded-full text-zinc-600 hover:bg-zinc-100",
|
||||
"h-8 w-8 rounded-full text-zinc-600 hover:bg-zinc-100",
|
||||
brushMode === "eraser" && "bg-black text-white hover:bg-black"
|
||||
)}
|
||||
onClick={() => setBrushMode("eraser")}
|
||||
@ -2358,7 +2358,7 @@ export function RealtimeCanvasDialog({
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className={cn(
|
||||
"h-10 w-10 rounded-full text-zinc-600 hover:bg-zinc-100",
|
||||
"h-8 w-8 rounded-full text-zinc-600 hover:bg-zinc-100",
|
||||
brushMode === "rect" && "bg-black text-white hover:bg-black"
|
||||
)}
|
||||
onClick={() => setBrushMode("rect")}
|
||||
@ -2370,7 +2370,7 @@ export function RealtimeCanvasDialog({
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className={cn(
|
||||
"h-10 w-10 rounded-full text-zinc-600 hover:bg-zinc-100",
|
||||
"h-8 w-8 rounded-full text-zinc-600 hover:bg-zinc-100",
|
||||
brushMode === "circle" && "bg-black text-white hover:bg-black"
|
||||
)}
|
||||
onClick={() => setBrushMode("circle")}
|
||||
@ -2382,7 +2382,7 @@ export function RealtimeCanvasDialog({
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className={cn(
|
||||
"h-10 w-10 rounded-full text-zinc-600 hover:bg-zinc-100",
|
||||
"h-8 w-8 rounded-full text-zinc-600 hover:bg-zinc-100",
|
||||
brushMode === "line" && "bg-black text-white hover:bg-black"
|
||||
)}
|
||||
onClick={() => setBrushMode("line")}
|
||||
@ -2392,10 +2392,10 @@ export function RealtimeCanvasDialog({
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-2 rounded-full border border-black/10 bg-zinc-50 px-3 py-2">
|
||||
<div className="flex h-10 shrink-0 items-center gap-2 rounded-full border border-black/10 bg-zinc-50 px-3">
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-10 items-center gap-2 rounded-full border border-black/10 bg-white px-3 text-xs font-medium text-zinc-700 transition hover:bg-zinc-50"
|
||||
className="flex h-8 items-center gap-2 rounded-full border border-black/10 bg-white px-3 text-xs font-medium text-zinc-700 transition hover:bg-zinc-50"
|
||||
onClick={() => colorInputRef.current?.click()}
|
||||
title="选择画笔颜色"
|
||||
>
|
||||
@ -2416,7 +2416,7 @@ export function RealtimeCanvasDialog({
|
||||
<span className="min-w-[64px] text-[11px] uppercase tracking-wide text-zinc-500">
|
||||
{brushColor}
|
||||
</span>
|
||||
<div className="flex min-w-[210px] flex-1 items-center gap-3 rounded-full border border-black/10 bg-white px-3 py-2">
|
||||
<div className="flex w-[210px] items-center gap-3 rounded-full border border-black/10 bg-white px-3 py-1.5">
|
||||
<span className="whitespace-nowrap text-xs font-medium text-zinc-600">
|
||||
粗细 {brushSize}
|
||||
</span>
|
||||
@ -2429,7 +2429,7 @@ export function RealtimeCanvasDialog({
|
||||
className="min-w-[140px] flex-1 [&>span:first-child]:h-2.5 [&>span:first-child]:bg-zinc-200 [&>span:first-child]:shadow-inner [&>span:first-child>span]:bg-sky-500 [&>span:last-child]:h-5 [&>span:last-child]:w-5 [&>span:last-child]:border-2 [&>span:last-child]:border-sky-500 [&>span:last-child]:bg-white"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 rounded-full border border-black/10 bg-white px-2 py-2">
|
||||
<div className="flex h-8 items-center gap-1 rounded-full border border-black/10 bg-white px-2">
|
||||
{CANVAS_BACKGROUND_PRESETS.map((color) => (
|
||||
<button
|
||||
key={color}
|
||||
@ -2446,11 +2446,11 @@ export function RealtimeCanvasDialog({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-1 rounded-full border border-black/10 bg-white px-1.5 py-1">
|
||||
<div className="flex h-10 shrink-0 items-center gap-0.5 rounded-full border border-black/10 bg-white px-1">
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-10 w-10 rounded-full text-zinc-600 hover:bg-zinc-100"
|
||||
className="h-8 w-8 rounded-full text-zinc-600 hover:bg-zinc-100"
|
||||
onClick={() => uploadInputRef.current?.click()}
|
||||
title="上传图片"
|
||||
>
|
||||
@ -2459,7 +2459,7 @@ export function RealtimeCanvasDialog({
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-10 w-10 rounded-full text-zinc-600 hover:bg-zinc-100"
|
||||
className="h-8 w-8 rounded-full text-zinc-600 hover:bg-zinc-100"
|
||||
onClick={clearSketch}
|
||||
title="清空"
|
||||
>
|
||||
@ -2468,7 +2468,7 @@ export function RealtimeCanvasDialog({
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-10 w-10 rounded-full text-zinc-600 hover:bg-zinc-100"
|
||||
className="h-8 w-8 rounded-full text-zinc-600 hover:bg-zinc-100"
|
||||
onClick={undo}
|
||||
disabled={!canUndo}
|
||||
title="撤销"
|
||||
@ -2478,7 +2478,7 @@ export function RealtimeCanvasDialog({
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-10 w-10 rounded-full text-zinc-600 hover:bg-zinc-100"
|
||||
className="h-8 w-8 rounded-full text-zinc-600 hover:bg-zinc-100"
|
||||
onClick={redo}
|
||||
disabled={!canRedo}
|
||||
title="重做"
|
||||
@ -2488,7 +2488,7 @@ export function RealtimeCanvasDialog({
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-10 w-10 rounded-full text-zinc-600 hover:bg-zinc-100"
|
||||
className="h-8 w-8 rounded-full text-zinc-600 hover:bg-zinc-100"
|
||||
onClick={handleDownloadCanvas}
|
||||
title="保存图片"
|
||||
>
|
||||
@ -2497,7 +2497,7 @@ export function RealtimeCanvasDialog({
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
className="h-10 w-10 rounded-full text-zinc-600 hover:bg-zinc-100"
|
||||
className="h-8 w-8 rounded-full text-zinc-600 hover:bg-zinc-100"
|
||||
onClick={forceReconnect}
|
||||
title="重连"
|
||||
>
|
||||
@ -2505,48 +2505,11 @@ export function RealtimeCanvasDialog({
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex min-w-[280px] flex-1 flex-wrap items-center gap-2 rounded-[22px] border border-black/10 bg-zinc-50 px-2 py-2">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 text-primary">
|
||||
<Sparkles className="h-4 w-4" />
|
||||
</div>
|
||||
<Input
|
||||
value={prompt}
|
||||
onChange={(e) => setPrompt(e.target.value)}
|
||||
onBlur={() => scheduleFlush(true, false)}
|
||||
placeholder="输入指令,基于当前画布实时生成"
|
||||
className="h-10 min-w-[180px] flex-1 border-0 bg-transparent px-2 text-sm shadow-none focus-visible:ring-0"
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-10 rounded-xl px-4 font-medium"
|
||||
onClick={triggerGenerate}
|
||||
disabled={status === "connecting"}
|
||||
>
|
||||
{status === "connecting" ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
"生成"
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="h-10 rounded-xl px-4 font-medium"
|
||||
onClick={handleApplyToCanvas}
|
||||
disabled={!previewUrl}
|
||||
>
|
||||
<Send className="mr-1.5 h-4 w-4" />
|
||||
发送到画布
|
||||
</Button>
|
||||
<span className="ml-auto text-xs text-zinc-500">
|
||||
{statusMessage}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{projectPanelOpen ? (
|
||||
<div className="absolute left-4 top-28 z-40 w-[22rem] max-w-[calc(100vw-2rem)] rounded-[24px] border border-black/10 bg-white/95 p-4 shadow-2xl backdrop-blur-xl">
|
||||
<div className="absolute left-4 top-20 z-40 w-[22rem] max-w-[calc(100vw-2rem)] rounded-[24px] border border-black/10 bg-white/95 p-4 shadow-2xl backdrop-blur-xl">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<div className="text-sm font-semibold text-zinc-900">项目列表</div>
|
||||
@ -2900,6 +2863,45 @@ export function RealtimeCanvasDialog({
|
||||
className="hidden"
|
||||
onChange={handleSketchUpload}
|
||||
/>
|
||||
<div className="absolute bottom-5 left-1/2 z-20 w-[min(760px,calc(100vw-2rem))] -translate-x-1/2 pointer-events-none">
|
||||
<div className="pointer-events-auto flex items-center gap-2 rounded-[22px] border border-black/10 bg-white/96 px-2 py-2 shadow-[0_12px_40px_rgba(15,23,42,0.12)]">
|
||||
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary">
|
||||
<Sparkles className="h-4 w-4" />
|
||||
</div>
|
||||
<Input
|
||||
value={prompt}
|
||||
onChange={(e) => setPrompt(e.target.value)}
|
||||
onBlur={() => scheduleFlush(true, false)}
|
||||
placeholder="输入指令,基于当前画布实时生成"
|
||||
className="h-9 flex-1 border-0 bg-transparent px-1 text-sm shadow-none focus-visible:ring-0"
|
||||
/>
|
||||
<span className="hidden whitespace-nowrap text-xs text-zinc-500 xl:inline">
|
||||
{statusMessage}
|
||||
</span>
|
||||
<Button
|
||||
size="sm"
|
||||
className="h-9 shrink-0 rounded-full px-4 font-medium"
|
||||
onClick={triggerGenerate}
|
||||
disabled={status === "connecting"}
|
||||
>
|
||||
{status === "connecting" ? (
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
) : (
|
||||
"生成"
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="h-9 shrink-0 rounded-full px-4 font-medium"
|
||||
onClick={handleApplyToCanvas}
|
||||
disabled={!previewUrl}
|
||||
>
|
||||
<Send className="mr-1.5 h-4 w-4" />
|
||||
发送到画布
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user