style: unify realtime canvas control colors
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-04-05 23:00:08 +08:00
parent ed827b629a
commit df1b1a017e
2 changed files with 160 additions and 83 deletions

View File

@ -88,13 +88,19 @@ const FALLBACK_CONFIG: EnhanceProClientConfig = {
};
const PANEL_SHELL_CLASS =
"pointer-events-auto rounded-[28px] border border-[#e7e1d4] bg-white/82 shadow-[0_20px_50px_rgba(15,23,42,0.08)] backdrop-blur-xl";
"pointer-events-auto rounded-[28px] border border-[#e7e1d4] bg-white/88 shadow-[0_20px_50px_rgba(15,23,42,0.08)] backdrop-blur-xl";
const SOLID_PANEL_FIELD_CLASS =
"border-[#e8e2d8] bg-[#fffdfa] text-foreground shadow-[0_8px_20px_rgba(15,23,42,0.05)]";
"border-[#e7e1d4] bg-[#fffdfa] text-foreground shadow-[0_8px_20px_rgba(15,23,42,0.05)]";
const SOLID_PANEL_CARD_CLASS =
"rounded-[22px] border border-[#e8e2d8] bg-[#fffdfa] shadow-[0_8px_20px_rgba(15,23,42,0.05)]";
"rounded-[22px] border border-[#e7e1d4] bg-[#fffdfa] shadow-[0_8px_20px_rgba(15,23,42,0.05)]";
const PANEL_SECTION_TITLE_CLASS = "font-semibold text-[13px] text-slate-700";
const PANEL_SECTION_NOTE_CLASS = "text-[11px] leading-5 text-slate-500";
const SOLID_PANEL_SELECT_CONTENT_CLASS =
"border-[#e7e1d4] bg-[#fffdfa] shadow-[0_16px_34px_rgba(15,23,42,0.08)]";
const PRIMARY_ACTION_BUTTON_CLASS =
"border border-[#2563eb] bg-[#2563eb] text-white shadow-[0_10px_22px_rgba(37,99,235,0.18)] transition hover:border-[#1d4ed8] hover:bg-[#1d4ed8] disabled:border-[#e7e1d4] disabled:bg-slate-200 disabled:text-slate-500 disabled:shadow-none";
const SECONDARY_ACTION_BUTTON_CLASS =
"border border-[#d8e4ff] bg-[#eef4ff] text-[#1d4ed8] shadow-[0_8px_20px_rgba(59,130,246,0.10)] transition hover:bg-[#e4eeff] hover:text-[#1e40af] disabled:border-[#e7e1d4] disabled:bg-slate-100 disabled:text-slate-400 disabled:shadow-none";
function clampNumber(value: number, min: number, max: number) {
return Math.min(max, Math.max(min, value));
@ -498,7 +504,7 @@ export function EnhanceProWorkspace({
className="max-h-full max-w-full object-contain"
src={getDisplayImageUrl(activePreviewUrl, 1800)}
/>
<div className="pointer-events-none absolute top-6 left-6 flex items-center gap-2 rounded-full border border-border/60 bg-background/95 px-3 py-1.5 text-muted-foreground text-xs shadow-sm backdrop-blur-sm">
<div className="pointer-events-none absolute top-6 left-6 flex items-center gap-2 rounded-full border border-[#e7e1d4] bg-[#fffdfa]/95 px-3 py-1.5 text-muted-foreground text-xs shadow-sm backdrop-blur-sm">
<ImageIcon className="h-3.5 w-3.5" />
<span>{activePreviewLabel}</span>
</div>
@ -518,7 +524,7 @@ export function EnhanceProWorkspace({
{sidePreviewItems.length > 0 ? (
<div className="absolute right-6 bottom-28 left-6 flex items-center justify-center">
<div className="flex max-w-[min(720px,calc(100vw-5rem))] items-center gap-2 rounded-[24px] border border-border/60 bg-background/95 px-3 py-3 shadow-xl backdrop-blur-sm">
<div className="flex max-w-[min(720px,calc(100vw-5rem))] items-center gap-2 rounded-[24px] border border-[#e7e1d4] bg-[#fffdfa]/95 px-3 py-3 shadow-xl backdrop-blur-sm">
{sidePreviewItems.map((item, index) => {
const isActive = item.isSource
? selectedResultIndex < 0
@ -529,7 +535,7 @@ export function EnhanceProWorkspace({
"group flex h-16 w-16 shrink-0 overflow-hidden rounded-2xl border transition",
isActive
? "border-sky-500 shadow-[0_0_0_1px_rgba(14,165,233,0.18)]"
: "border-border/60 hover:border-foreground/20"
: "border-[#e7e1d4] hover:shadow-sm"
)}
key={item.key}
onClick={() =>
@ -587,7 +593,7 @@ export function EnhanceProWorkspace({
>
<SelectValue placeholder="选择一个增强预设" />
</SelectTrigger>
<SelectContent>
<SelectContent className={SOLID_PANEL_SELECT_CONTENT_CLASS}>
{presets.map((preset) => (
<SelectItem key={preset.id} value={preset.id}>
{preset.name}
@ -702,7 +708,7 @@ export function EnhanceProWorkspace({
>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectContent className={SOLID_PANEL_SELECT_CONTENT_CLASS}>
{Array.from(
{ length: config.maxBatchSize },
(_, index) => index + 1
@ -752,7 +758,7 @@ export function EnhanceProWorkspace({
>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectContent className={SOLID_PANEL_SELECT_CONTENT_CLASS}>
<SelectItem value="Redefine">Redefine</SelectItem>
<SelectItem value="Recovery">Recovery</SelectItem>
<SelectItem value="Recovery V2">Recovery V2</SelectItem>
@ -776,7 +782,7 @@ export function EnhanceProWorkspace({
>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectContent className={SOLID_PANEL_SELECT_CONTENT_CLASS}>
<SelectItem value="jpg">JPG</SelectItem>
<SelectItem value="png">PNG</SelectItem>
<SelectItem value="webp">WEBP</SelectItem>
@ -799,7 +805,7 @@ export function EnhanceProWorkspace({
>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectContent className={SOLID_PANEL_SELECT_CONTENT_CLASS}>
<SelectItem value="All">全部</SelectItem>
<SelectItem value="Foreground">前景</SelectItem>
<SelectItem value="Background">背景</SelectItem>
@ -1070,8 +1076,8 @@ export function EnhanceProWorkspace({
</div>
</div>
<div className="pointer-events-none absolute bottom-6 left-1/2 z-30 flex w-[min(560px,calc(100vw-2rem))] -translate-x-1/2 flex-col items-center gap-2">
<div className="pointer-events-auto rounded-full border border-border/60 bg-background/92 px-4 py-1.5 text-muted-foreground text-xs shadow-lg backdrop-blur-xl">
<div className="pointer-events-none absolute bottom-6 left-1/2 z-30 flex w-fit max-w-[calc(100vw-2rem)] -translate-x-1/2 flex-col items-center gap-2">
<div className="pointer-events-auto rounded-full border border-[#e7e1d4] bg-[#fffdfa]/95 px-4 py-1.5 text-muted-foreground text-xs shadow-lg backdrop-blur-xl">
<span className="inline-flex items-center gap-1.5">
{selectedGeneratedImage ? (
<CheckCircle2 className="h-3.5 w-3.5 text-emerald-600" />
@ -1079,9 +1085,12 @@ export function EnhanceProWorkspace({
{actionHint}
</span>
</div>
<div className="pointer-events-auto flex w-full items-center justify-center gap-3 rounded-full border border-border/60 bg-background/92 p-2 shadow-2xl backdrop-blur-xl">
<div className="pointer-events-auto inline-flex items-center justify-center gap-3 rounded-full border border-[#e7e1d4] bg-[#fffdfa]/95 p-2 shadow-2xl backdrop-blur-xl">
<Button
className="h-11 min-w-[180px] flex-1 rounded-full bg-[#2563eb] text-white shadow-sm hover:bg-[#1d4ed8] disabled:bg-slate-200 disabled:text-slate-500"
className={cn(
"h-11 min-w-[220px] rounded-full px-6",
PRIMARY_ACTION_BUTTON_CLASS
)}
disabled={isSubmitting || !sourceImageUrl}
onClick={handleSubmit}
type="button"
@ -1094,7 +1103,10 @@ export function EnhanceProWorkspace({
开始增强
</Button>
<Button
className="h-11 min-w-[180px] flex-1 rounded-full border-[#bfdbfe] bg-[#eff6ff] px-4 text-[#1d4ed8] shadow-sm hover:bg-[#dbeafe] hover:text-[#1e40af] disabled:border-slate-200 disabled:bg-slate-100 disabled:text-slate-400"
className={cn(
"h-11 min-w-[220px] rounded-full px-6",
SECONDARY_ACTION_BUTTON_CLASS
)}
disabled={!selectedGeneratedImage}
onClick={handleApply}
type="button"

View File

@ -2917,10 +2917,18 @@ export function RealtimeCanvasDialog({
setStatusMessage("新建空白项目");
}, [clearPreviewCanvas, closeBrushSettingPopovers, resetCanvas]);
const floatingSurfaceClass =
"border border-[#e7e1d4] bg-white/88 shadow-[0_18px_40px_rgba(15,23,42,0.08)] backdrop-blur-xl";
const controlSurfaceClass =
"border border-[#e7e1d4] bg-[#fffdfa] text-foreground shadow-[0_8px_20px_rgba(15,23,42,0.05)]";
const softActionButtonClass =
"border border-[#d8e4ff] bg-[#eef4ff] text-[#1d4ed8] shadow-[0_8px_20px_rgba(59,130,246,0.10)] transition hover:bg-[#e4eeff] hover:text-[#1e40af]";
const primaryActionButtonClass =
"border border-[#2563eb] bg-[#2563eb] text-white shadow-[0_10px_22px_rgba(37,99,235,0.18)] transition hover:border-[#1d4ed8] hover:bg-[#1d4ed8]";
const compactToolButtonClass =
"h-9 w-9 rounded-[16px] text-muted-foreground transition hover:bg-accent hover:text-foreground";
"h-9 w-9 rounded-[16px] border border-transparent text-slate-600 transition hover:border-[#d8e4ff] hover:bg-[#eef4ff] hover:text-[#1d4ed8]";
const compactToolButtonActiveClass =
"bg-foreground text-background hover:bg-foreground hover:text-background";
"border-[#d8e4ff] bg-[#eef4ff] text-[#1d4ed8] shadow-[0_6px_16px_rgba(59,130,246,0.12)] hover:bg-[#e4eeff] hover:text-[#1d4ed8]";
const currentModeLabel =
brushMode === "select"
? "鼠标"
@ -2959,9 +2967,12 @@ export function RealtimeCanvasDialog({
<DialogDescription>左侧绘制草图,右侧实时预览。</DialogDescription>
</DialogHeader>
)}
<div className="pointer-events-none absolute top-6 left-6 right-24 z-50 flex flex-wrap items-start gap-3">
<div className="pointer-events-none absolute top-6 left-6 right-6 z-50 flex flex-wrap items-start gap-3">
<button
className="pointer-events-auto flex items-center gap-3 rounded-full border border-border/60 bg-background/95 px-4 py-2 text-foreground shadow-xl backdrop-blur-sm transition hover:bg-background"
className={cn(
"pointer-events-auto flex h-16 items-center gap-3 rounded-full px-5 text-foreground transition hover:bg-white",
floatingSurfaceClass
)}
onClick={handleGoHome}
title="返回首页"
type="button"
@ -2977,7 +2988,7 @@ export function RealtimeCanvasDialog({
}
/>
) : (
<div className="flex h-6 w-6 items-center justify-center rounded bg-primary text-primary-foreground">
<div className="flex h-6 w-6 items-center justify-center rounded bg-[#eef4ff] text-[#1d4ed8]">
<Sparkles className="h-3.5 w-3.5" />
</div>
)}
@ -2986,13 +2997,18 @@ export function RealtimeCanvasDialog({
</span>
</button>
<div className="pointer-events-auto inline-flex w-fit max-w-full items-center gap-1 rounded-full border border-border/60 bg-background/95 p-1.5 shadow-xl backdrop-blur-sm">
<div
className={cn(
"pointer-events-auto inline-flex h-16 w-fit max-w-full items-center gap-1 rounded-full p-1.5",
floatingSurfaceClass
)}
>
<button
className={cn(
"inline-flex items-center gap-2 rounded-full px-3 py-2 text-sm transition",
"inline-flex h-full items-center gap-2 rounded-full border px-4 text-sm transition",
workspaceMode === "realtime"
? "bg-foreground text-background shadow-sm"
: "bg-transparent text-muted-foreground hover:bg-muted hover:text-foreground"
? softActionButtonClass
: "border-transparent bg-transparent text-slate-600 hover:bg-[#f7f3eb] hover:text-foreground"
)}
onClick={() => setWorkspaceMode("realtime")}
type="button"
@ -3002,10 +3018,10 @@ export function RealtimeCanvasDialog({
</button>
<button
className={cn(
"inline-flex items-center gap-2 rounded-full px-3 py-2 text-sm transition",
"inline-flex h-full items-center gap-2 rounded-full border px-4 text-sm transition",
workspaceMode === "enhance-pro"
? "bg-foreground text-background shadow-sm"
: "bg-transparent text-muted-foreground hover:bg-muted hover:text-foreground"
? softActionButtonClass
: "border-transparent bg-transparent text-slate-600 hover:bg-[#f7f3eb] hover:text-foreground"
)}
onClick={() => setWorkspaceMode("enhance-pro")}
type="button"
@ -3015,23 +3031,13 @@ export function RealtimeCanvasDialog({
</button>
</div>
</div>
<div className="pointer-events-none absolute top-6 right-6 z-50">
<div className="pointer-events-auto rounded-full border border-border/60 bg-background/95 p-1 shadow-xl backdrop-blur-sm">
<Button
className="h-7 w-7 rounded-full bg-background text-foreground transition hover:bg-muted hover:text-foreground"
onClick={handleCloseRealtime}
size="icon"
title="关闭"
type="button"
variant="ghost"
>
<X className="h-4 w-4" />
</Button>
</div>
</div>
{saveProjectDialogOpen ? (
<div className="-translate-x-1/2 absolute top-24 left-1/2 z-40 w-[22rem] max-w-[calc(100vw-2rem)] rounded-[24px] border border-border/40 bg-background/95 p-4 shadow-2xl backdrop-blur-xl">
<div
className={cn(
"-translate-x-1/2 absolute top-24 left-1/2 z-40 w-[22rem] max-w-[calc(100vw-2rem)] rounded-[24px] p-4",
floatingSurfaceClass
)}
>
<div className="flex items-center justify-between">
<div>
<div className="font-semibold text-foreground text-sm">
@ -3053,7 +3059,7 @@ export function RealtimeCanvasDialog({
</div>
<Input
autoFocus
className="mt-4 h-11 rounded-2xl border-border/40 bg-background/50"
className={cn("mt-4 h-11 rounded-2xl", controlSurfaceClass)}
onChange={(event) => setPendingProjectName(event.target.value)}
onKeyDown={(event) => {
if (event.key === "Enter") {
@ -3066,7 +3072,7 @@ export function RealtimeCanvasDialog({
/>
<div className="mt-4 flex gap-2">
<Button
className="h-10 flex-1 rounded-2xl"
className={cn("h-10 flex-1 rounded-2xl", controlSurfaceClass)}
onClick={() => setSaveProjectDialogOpen(false)}
type="button"
variant="outline"
@ -3074,7 +3080,7 @@ export function RealtimeCanvasDialog({
取消
</Button>
<Button
className="h-10 flex-1 rounded-2xl bg-primary text-primary-foreground hover:bg-primary/90"
className={cn("h-10 flex-1 rounded-2xl", primaryActionButtonClass)}
onClick={() => void confirmSaveCurrentProject()}
type="button"
>
@ -3084,7 +3090,12 @@ export function RealtimeCanvasDialog({
</div>
) : null}
{projectPanelOpen ? (
<div className="absolute top-20 left-4 z-40 w-[22rem] max-w-[calc(100vw-2rem)] rounded-[24px] border border-border/40 bg-background/95 p-4 shadow-2xl backdrop-blur-xl">
<div
className={cn(
"absolute top-20 left-4 z-40 w-[22rem] max-w-[calc(100vw-2rem)] rounded-[24px] p-4",
floatingSurfaceClass
)}
>
<div className="flex items-center justify-between">
<div>
<div className="font-semibold text-foreground text-sm">
@ -3106,17 +3117,17 @@ export function RealtimeCanvasDialog({
</div>
<div className="mt-4 max-h-[55vh] min-h-0 space-y-2 overflow-y-auto overscroll-contain pr-1">
{savedProjects.length === 0 ? (
<div className="rounded-2xl border border-border/40 border-dashed px-4 py-8 text-center text-muted-foreground text-sm">
<div className="rounded-2xl border border-[#e7e1d4] border-dashed px-4 py-8 text-center text-muted-foreground text-sm">
暂无保存的项目
</div>
) : (
savedProjects.map((project) => (
<div
className="rounded-2xl border border-border bg-card p-3 shadow-sm"
className="rounded-2xl border border-[#e7e1d4] bg-[#fffdfa] p-3 shadow-sm"
key={project.id}
>
<div className="flex items-center gap-3">
<div className="h-14 w-14 shrink-0 overflow-hidden rounded-xl border border-border/10 bg-muted">
<div className="h-14 w-14 shrink-0 overflow-hidden rounded-xl border border-[#e7e1d4] bg-muted">
{project.coverImageUrl || project.previewUrl ? (
<img
alt={project.name}
@ -3147,7 +3158,10 @@ export function RealtimeCanvasDialog({
</div>
<div className="mt-2 flex items-center justify-end gap-2">
<Button
className="h-8 rounded-xl bg-primary px-4 text-primary-foreground hover:bg-primary/90"
className={cn(
"h-8 rounded-xl px-4",
softActionButtonClass
)}
onClick={() => void openSavedProject(project.id)}
size="sm"
type="button"
@ -3155,7 +3169,7 @@ export function RealtimeCanvasDialog({
打开
</Button>
<Button
className="h-8 rounded-xl px-4"
className={cn("h-8 rounded-xl px-4", controlSurfaceClass)}
onClick={() => deleteSavedProject(project.id)}
size="sm"
type="button"
@ -3183,7 +3197,7 @@ export function RealtimeCanvasDialog({
{/* Main Canvas Area - Split View */}
<div className="relative flex h-full w-full bg-muted/30">
{/* Left: Input Canvas */}
<div className="relative flex flex-1 flex-col items-center justify-center overflow-hidden border-border/10 border-r">
<div className="relative flex flex-1 flex-col items-center justify-center overflow-hidden border-[#e7e1d4] border-r">
<div
className="relative flex h-full w-full items-center justify-center bg-background"
onDragEnter={handleDragEnter}
@ -3268,7 +3282,7 @@ export function RealtimeCanvasDialog({
] as const
).map((handle) => (
<div
className="-translate-x-1/2 -translate-y-1/2 pointer-events-none absolute z-20 h-3.5 w-3.5 rounded-full border border-border/70 bg-background shadow-[0_2px_8px_rgba(15,23,42,0.18)]"
className="-translate-x-1/2 -translate-y-1/2 pointer-events-none absolute z-20 h-3.5 w-3.5 rounded-full border border-[#e7e1d4] bg-background shadow-[0_2px_8px_rgba(15,23,42,0.18)]"
key={handle.key}
style={{
left: handle.left,
@ -3290,15 +3304,20 @@ export function RealtimeCanvasDialog({
/>
</div>
</div>
<div className="absolute top-28 bottom-4 left-4 z-10 flex w-16 max-w-[18vw] flex-col rounded-[24px] border border-border/50 bg-background/96 p-2 shadow-xl backdrop-blur-sm">
<div className="flex items-center justify-center border-border/10 border-b pb-3">
<div
className={cn(
"absolute top-28 bottom-4 left-4 z-10 flex w-16 max-w-[18vw] flex-col rounded-[24px] p-2",
floatingSurfaceClass
)}
>
<div className="flex items-center justify-center border-[#e7e1d4] border-b pb-3">
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-muted text-foreground">
<Layers3 className="h-4 w-4" />
</div>
</div>
<div className="mt-2 flex flex-1 flex-col items-center space-y-2 overflow-y-auto py-1">
{imageLayers.length === 0 ? (
<div className="rounded-2xl border border-black/10 border-dashed px-1 py-4 text-center text-[10px] text-muted-foreground leading-4">
<div className="rounded-2xl border border-[#e7e1d4] border-dashed px-1 py-4 text-center text-[10px] text-muted-foreground leading-4">
暂无图层
</div>
) : (
@ -3349,7 +3368,10 @@ export function RealtimeCanvasDialog({
</div>
<div className="mt-2 flex flex-col gap-2">
<Button
className="h-10 rounded-xl bg-background px-0 hover:bg-muted"
className={cn(
"h-10 rounded-xl px-0 hover:bg-[#f7f3eb]",
controlSurfaceClass
)}
disabled={isLayerSplitImporting}
onClick={() => splitUploadInputRef.current?.click()}
title="图层拆分导入"
@ -3363,7 +3385,10 @@ export function RealtimeCanvasDialog({
)}
</Button>
<Button
className="h-10 rounded-xl bg-background px-0 hover:bg-muted"
className={cn(
"h-10 rounded-xl px-0 hover:bg-[#f7f3eb]",
controlSurfaceClass
)}
disabled={isLayerSplitImporting}
onClick={() => uploadInputRef.current?.click()}
title="添加图片图层"
@ -3402,7 +3427,10 @@ export function RealtimeCanvasDialog({
{previewUrl ? (
<div className="absolute right-5 bottom-5">
<Button
className="h-10 rounded-full bg-background px-4 font-medium text-foreground text-sm shadow-lg hover:bg-muted"
className={cn(
"h-10 rounded-full px-4 font-medium text-sm",
softActionButtonClass
)}
onClick={handleDownloadPreview}
size="sm"
type="button"
@ -3433,7 +3461,12 @@ export function RealtimeCanvasDialog({
/>
<div className="-translate-x-1/2 pointer-events-none absolute bottom-4 left-1/2 z-30 flex w-[min(600px,calc(100vw-2rem))] flex-col items-center gap-2.5">
<TooltipProvider delayDuration={120}>
<div className="pointer-events-auto flex flex-wrap items-center justify-center gap-1 rounded-[24px] border border-border/50 bg-background/96 p-1.5 shadow-xl backdrop-blur-sm">
<div
className={cn(
"pointer-events-auto flex flex-wrap items-center justify-center gap-1 rounded-[24px] p-1.5",
floatingSurfaceClass
)}
>
<Tooltip>
<TooltipTrigger asChild>
<Button
@ -3478,20 +3511,28 @@ export function RealtimeCanvasDialog({
</Button>
</PopoverTrigger>
</TooltipTrigger>
<PopoverContent className="w-[250px] rounded-[18px] border-border/40 bg-background/95 p-4 shadow-xl backdrop-blur-md">
<PopoverContent
className={cn(
"w-[250px] rounded-[18px] p-4 backdrop-blur-md",
floatingSurfaceClass
)}
>
<div className="space-y-4">
<div className="space-y-2">
<div className="font-medium text-muted-foreground text-xs">
颜色
</div>
<button
className="flex h-10 w-full items-center justify-between rounded-xl border border-border bg-background px-3 font-medium text-foreground text-sm"
className={cn(
"flex h-10 w-full items-center justify-between rounded-xl px-3 font-medium text-sm",
controlSurfaceClass
)}
onClick={() => colorInputRef.current?.click()}
type="button"
>
<span>选择颜色</span>
<span
className="h-5 w-5 rounded-full border border-border/20 shadow-inner"
className="h-5 w-5 rounded-full border border-[#e7e1d4] shadow-inner"
style={{ backgroundColor: brushColor }}
/>
</button>
@ -3518,9 +3559,9 @@ export function RealtimeCanvasDialog({
max={50}
min={2}
onValueChange={([val]) => setBrushSize(val)}
rangeClassName="bg-primary"
rangeClassName="bg-[#7da2ff]"
step={1}
thumbClassName="h-5 w-5 border border-border bg-background shadow-sm"
thumbClassName="h-5 w-5 border border-[#d8e4ff] bg-[#eef4ff] shadow-sm"
trackClassName="h-2.5 bg-muted shadow-inner"
value={[brushSize]}
/>
@ -3557,7 +3598,12 @@ export function RealtimeCanvasDialog({
</Button>
</PopoverTrigger>
</TooltipTrigger>
<PopoverContent className="w-[220px] rounded-[18px] border-border/40 bg-background/95 p-4 shadow-xl backdrop-blur-md">
<PopoverContent
className={cn(
"w-[220px] rounded-[18px] p-4 backdrop-blur-md",
floatingSurfaceClass
)}
>
<div className="space-y-2">
<div className="flex items-center justify-between font-medium text-muted-foreground text-xs">
<span>橡皮粗细</span>
@ -3570,9 +3616,9 @@ export function RealtimeCanvasDialog({
max={50}
min={2}
onValueChange={([val]) => setBrushSize(val)}
rangeClassName="bg-primary"
rangeClassName="bg-[#7da2ff]"
step={1}
thumbClassName="h-5 w-5 border border-border bg-background shadow-sm"
thumbClassName="h-5 w-5 border border-[#d8e4ff] bg-[#eef4ff] shadow-sm"
trackClassName="h-2.5 bg-muted shadow-inner"
value={[brushSize]}
/>
@ -3604,16 +3650,22 @@ export function RealtimeCanvasDialog({
</Button>
</PopoverTrigger>
</TooltipTrigger>
<PopoverContent className="w-[220px] rounded-[18px] border-border/40 bg-background/95 p-3 shadow-xl backdrop-blur-md">
<PopoverContent
className={cn(
"w-[220px] rounded-[18px] p-3 backdrop-blur-md",
floatingSurfaceClass
)}
>
<div className="mb-2 font-medium text-muted-foreground text-xs">
形状工具
</div>
<div className="grid grid-cols-3 gap-2">
<Button
className={cn(
"h-10 rounded-xl",
brushMode === "rect" &&
"bg-foreground text-background hover:bg-foreground"
"h-10 rounded-xl border",
brushMode === "rect"
? softActionButtonClass
: "border-transparent text-slate-600 hover:bg-[#f7f3eb] hover:text-foreground"
)}
onClick={() => activateShapeMode("rect")}
type="button"
@ -3623,9 +3675,10 @@ export function RealtimeCanvasDialog({
</Button>
<Button
className={cn(
"h-10 rounded-xl",
brushMode === "circle" &&
"bg-foreground text-background hover:bg-foreground"
"h-10 rounded-xl border",
brushMode === "circle"
? softActionButtonClass
: "border-transparent text-slate-600 hover:bg-[#f7f3eb] hover:text-foreground"
)}
onClick={() => activateShapeMode("circle")}
type="button"
@ -3635,9 +3688,10 @@ export function RealtimeCanvasDialog({
</Button>
<Button
className={cn(
"h-10 rounded-xl",
brushMode === "line" &&
"bg-foreground text-background hover:bg-foreground"
"h-10 rounded-xl border",
brushMode === "line"
? softActionButtonClass
: "border-transparent text-slate-600 hover:bg-[#f7f3eb] hover:text-foreground"
)}
onClick={() => activateShapeMode("line")}
type="button"
@ -3760,7 +3814,12 @@ export function RealtimeCanvasDialog({
</div>
</TooltipProvider>
<div className="pointer-events-auto w-full rounded-[24px] border border-border/50 bg-background/98 p-4 shadow-2xl backdrop-blur-sm">
<div
className={cn(
"pointer-events-auto w-full rounded-[24px] p-4",
floatingSurfaceClass
)}
>
<Textarea
className="min-h-[56px] resize-none border-0 bg-transparent px-1 py-0.5 text-[15px] leading-7 shadow-none focus-visible:ring-0 focus-visible:ring-offset-0"
onBlur={() => scheduleFlush(true, false)}
@ -3801,7 +3860,10 @@ export function RealtimeCanvasDialog({
</div>
<div className="flex items-center gap-2">
<Button
className="h-9 shrink-0 rounded-full bg-primary px-4 font-medium text-primary-foreground hover:bg-primary/90"
className={cn(
"h-9 shrink-0 rounded-full px-4 font-medium",
primaryActionButtonClass
)}
disabled={status === "connecting"}
onClick={triggerGenerate}
size="sm"
@ -3813,7 +3875,10 @@ export function RealtimeCanvasDialog({
)}
</Button>
<Button
className="h-9 shrink-0 rounded-full px-4 font-medium"
className={cn(
"h-9 shrink-0 rounded-full px-4 font-medium",
softActionButtonClass
)}
disabled={!previewUrl}
onClick={handleApplyToCanvas}
size="sm"