refine realtime canvas controls
This commit is contained in:
parent
e3f6bf5457
commit
f0340d3d96
@ -34,7 +34,6 @@ import {
|
|||||||
Upload,
|
Upload,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronUp,
|
ChevronUp,
|
||||||
RefreshCw,
|
|
||||||
Sparkles,
|
Sparkles,
|
||||||
Save,
|
Save,
|
||||||
Trash2,
|
Trash2,
|
||||||
@ -115,7 +114,6 @@ const FALLBACK_PROMPT =
|
|||||||
const CANVAS_BACKGROUND = "#ffffff";
|
const CANVAS_BACKGROUND = "#ffffff";
|
||||||
const DEFAULT_BRUSH_COLOR = "#18181b";
|
const DEFAULT_BRUSH_COLOR = "#18181b";
|
||||||
const DRAW_LAYER_ID = "__draw_layer__";
|
const DRAW_LAYER_ID = "__draw_layer__";
|
||||||
const CANVAS_BACKGROUND_PRESETS = ["#ffffff", "#f4f4f5", "#fdf2f8", "#ecfeff", "#f0fdf4"] as const;
|
|
||||||
const REALTIME_PROJECTS_STORAGE_KEY = "ai-chatbot:realtime-canvas-projects";
|
const REALTIME_PROJECTS_STORAGE_KEY = "ai-chatbot:realtime-canvas-projects";
|
||||||
|
|
||||||
function cloneImageLayers(layers: ImageLayer[]) {
|
function cloneImageLayers(layers: ImageLayer[]) {
|
||||||
@ -2208,17 +2206,6 @@ export function RealtimeCanvasDialog({
|
|||||||
[importFilesAsLayers]
|
[importFilesAsLayers]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCanvasBackgroundChange = useCallback(
|
|
||||||
(nextColor: string) => {
|
|
||||||
canvasBackgroundRef.current = nextColor;
|
|
||||||
setCanvasBackground(nextColor);
|
|
||||||
saveHistory(undefined, nextColor);
|
|
||||||
markCanvasDirty(null);
|
|
||||||
scheduleFlush(true);
|
|
||||||
},
|
|
||||||
[markCanvasDirty, saveHistory, scheduleFlush]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleDownloadCanvas = useCallback(async () => {
|
const handleDownloadCanvas = useCallback(async () => {
|
||||||
const compositeCanvas = await buildCompositeCanvas();
|
const compositeCanvas = await buildCompositeCanvas();
|
||||||
if (!compositeCanvas) return;
|
if (!compositeCanvas) return;
|
||||||
@ -2357,18 +2344,6 @@ export function RealtimeCanvasDialog({
|
|||||||
>
|
>
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
{standalone ? (
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
size="icon"
|
|
||||||
variant="ghost"
|
|
||||||
className="h-8 w-8 rounded-full"
|
|
||||||
onClick={() => onOpenChange(false)}
|
|
||||||
title="返回画布"
|
|
||||||
>
|
|
||||||
<X className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex h-10 shrink-0 items-center gap-0.5 rounded-full border border-black/10 bg-white px-1">
|
<div className="flex h-10 shrink-0 items-center gap-0.5 rounded-full border border-black/10 bg-white px-1">
|
||||||
@ -2379,7 +2354,10 @@ export function RealtimeCanvasDialog({
|
|||||||
"h-8 w-8 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"
|
brushMode === "brush" && "bg-black text-white hover:bg-black"
|
||||||
)}
|
)}
|
||||||
onClick={() => setBrushMode("brush")}
|
onClick={() => {
|
||||||
|
setSelectedLayerId(DRAW_LAYER_ID);
|
||||||
|
setBrushMode("brush");
|
||||||
|
}}
|
||||||
title="画笔"
|
title="画笔"
|
||||||
>
|
>
|
||||||
<Paintbrush className="h-5 w-5" />
|
<Paintbrush className="h-5 w-5" />
|
||||||
@ -2391,7 +2369,10 @@ export function RealtimeCanvasDialog({
|
|||||||
"h-8 w-8 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"
|
brushMode === "eraser" && "bg-black text-white hover:bg-black"
|
||||||
)}
|
)}
|
||||||
onClick={() => setBrushMode("eraser")}
|
onClick={() => {
|
||||||
|
setSelectedLayerId(DRAW_LAYER_ID);
|
||||||
|
setBrushMode("eraser");
|
||||||
|
}}
|
||||||
title="橡皮"
|
title="橡皮"
|
||||||
>
|
>
|
||||||
<Eraser className="h-5 w-5" />
|
<Eraser className="h-5 w-5" />
|
||||||
@ -2403,7 +2384,10 @@ export function RealtimeCanvasDialog({
|
|||||||
"h-8 w-8 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"
|
brushMode === "rect" && "bg-black text-white hover:bg-black"
|
||||||
)}
|
)}
|
||||||
onClick={() => setBrushMode("rect")}
|
onClick={() => {
|
||||||
|
setSelectedLayerId(DRAW_LAYER_ID);
|
||||||
|
setBrushMode("rect");
|
||||||
|
}}
|
||||||
title="矩形"
|
title="矩形"
|
||||||
>
|
>
|
||||||
<Square className="h-5 w-5" />
|
<Square className="h-5 w-5" />
|
||||||
@ -2415,7 +2399,10 @@ export function RealtimeCanvasDialog({
|
|||||||
"h-8 w-8 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"
|
brushMode === "circle" && "bg-black text-white hover:bg-black"
|
||||||
)}
|
)}
|
||||||
onClick={() => setBrushMode("circle")}
|
onClick={() => {
|
||||||
|
setSelectedLayerId(DRAW_LAYER_ID);
|
||||||
|
setBrushMode("circle");
|
||||||
|
}}
|
||||||
title="圆形"
|
title="圆形"
|
||||||
>
|
>
|
||||||
<CircleIcon className="h-5 w-5" />
|
<CircleIcon className="h-5 w-5" />
|
||||||
@ -2427,7 +2414,10 @@ export function RealtimeCanvasDialog({
|
|||||||
"h-8 w-8 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"
|
brushMode === "line" && "bg-black text-white hover:bg-black"
|
||||||
)}
|
)}
|
||||||
onClick={() => setBrushMode("line")}
|
onClick={() => {
|
||||||
|
setSelectedLayerId(DRAW_LAYER_ID);
|
||||||
|
setBrushMode("line");
|
||||||
|
}}
|
||||||
title="直线"
|
title="直线"
|
||||||
>
|
>
|
||||||
<Slash className="h-5 w-5" />
|
<Slash className="h-5 w-5" />
|
||||||
@ -2471,21 +2461,6 @@ export function RealtimeCanvasDialog({
|
|||||||
thumbClassName="h-5 w-5 border border-zinc-300 bg-white shadow-sm"
|
thumbClassName="h-5 w-5 border border-zinc-300 bg-white shadow-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<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}
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"h-5 w-5 rounded-full border border-black/15 transition-all",
|
|
||||||
canvasBackground === color && "scale-110 ring-2 ring-black/20"
|
|
||||||
)}
|
|
||||||
style={{ backgroundColor: color }}
|
|
||||||
onClick={() => handleCanvasBackgroundChange(color)}
|
|
||||||
title="画布底色"
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex h-10 shrink-0 items-center gap-0.5 rounded-full border border-black/10 bg-white px-1">
|
<div className="flex h-10 shrink-0 items-center gap-0.5 rounded-full border border-black/10 bg-white px-1">
|
||||||
@ -2498,15 +2473,6 @@ export function RealtimeCanvasDialog({
|
|||||||
>
|
>
|
||||||
<Upload className="h-5 w-5" />
|
<Upload className="h-5 w-5" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
variant="ghost"
|
|
||||||
className="h-8 w-8 rounded-full text-zinc-600 hover:bg-zinc-100"
|
|
||||||
onClick={clearSketch}
|
|
||||||
title="清空"
|
|
||||||
>
|
|
||||||
<Trash2 className="h-5 w-5" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
size="icon"
|
size="icon"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@ -2536,17 +2502,23 @@ export function RealtimeCanvasDialog({
|
|||||||
>
|
>
|
||||||
<Download className="h-5 w-5" />
|
<Download className="h-5 w-5" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
size="icon"
|
|
||||||
variant="ghost"
|
|
||||||
className="h-8 w-8 rounded-full text-zinc-600 hover:bg-zinc-100"
|
|
||||||
onClick={forceReconnect}
|
|
||||||
title="重连"
|
|
||||||
>
|
|
||||||
<RefreshCw className="h-5 w-5" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{standalone ? (
|
||||||
|
<div className="flex h-10 shrink-0 items-center rounded-full border border-black/10 bg-white px-1">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
className="h-8 w-8 rounded-full"
|
||||||
|
onClick={() => onOpenChange(false)}
|
||||||
|
title="关闭页面"
|
||||||
|
>
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -2690,7 +2662,7 @@ export function RealtimeCanvasDialog({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute left-3 top-28 bottom-4 z-10 flex w-[4.9rem] max-w-[20vw] flex-col rounded-[20px] border border-black/10 bg-white/96 p-2 shadow-xl">
|
<div className="absolute left-3 top-28 bottom-4 z-10 flex w-[4.4rem] max-w-[18vw] flex-col rounded-[18px] border border-black/10 bg-white/96 p-1.5 shadow-xl">
|
||||||
<div className="flex items-center justify-between border-b border-black/10 pb-2">
|
<div className="flex items-center justify-between border-b border-black/10 pb-2">
|
||||||
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-zinc-50 text-zinc-900">
|
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-zinc-50 text-zinc-900">
|
||||||
<Layers3 className="h-4 w-4" />
|
<Layers3 className="h-4 w-4" />
|
||||||
@ -2699,22 +2671,7 @@ export function RealtimeCanvasDialog({
|
|||||||
{imageLayers.length} 张
|
{imageLayers.length} 张
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 flex-1 space-y-2 overflow-y-auto pr-0.5">
|
<div className="mt-2 flex-1 space-y-1.5 overflow-y-auto pr-0.5">
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={cn(
|
|
||||||
"flex w-full flex-col items-center gap-1 rounded-2xl border px-1.5 py-2 text-center transition",
|
|
||||||
selectedLayerId === DRAW_LAYER_ID
|
|
||||||
? "border-black/20 bg-zinc-900 text-white shadow-sm"
|
|
||||||
: "border-dashed border-black/10 bg-zinc-50 text-zinc-900 hover:bg-zinc-100"
|
|
||||||
)}
|
|
||||||
onClick={() => setSelectedLayerId(DRAW_LAYER_ID)}
|
|
||||||
>
|
|
||||||
<Paintbrush className={cn("h-4 w-4", selectedLayerId === DRAW_LAYER_ID ? "text-white" : "text-zinc-700")} />
|
|
||||||
<div className={cn("text-[10px] font-medium", selectedLayerId === DRAW_LAYER_ID ? "text-white" : "text-zinc-600")}>
|
|
||||||
画笔
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
{imageLayers.length === 0 ? (
|
{imageLayers.length === 0 ? (
|
||||||
<div className="rounded-2xl border border-dashed border-black/10 px-1 py-4 text-center text-[10px] leading-4 text-zinc-400">
|
<div className="rounded-2xl border border-dashed border-black/10 px-1 py-4 text-center text-[10px] leading-4 text-zinc-400">
|
||||||
暂无图层
|
暂无图层
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user