feat: comprehensive UI redesign for infinite canvas (LOVART style)
This commit is contained in:
parent
f289eca90c
commit
79e5a870e1
@ -3,6 +3,7 @@
|
||||
import { useEffect, useState, useCallback, use } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import {
|
||||
ArrowLeft,
|
||||
Save,
|
||||
Undo2,
|
||||
Redo2,
|
||||
@ -360,12 +361,27 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<div className="h-full flex flex-col bg-background">
|
||||
{/* 顶部工具栏 */}
|
||||
<div className="h-10 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 flex items-center justify-between px-4 shrink-0">
|
||||
{/* 左侧:标题编辑 */}
|
||||
<div className="flex items-center gap-2">
|
||||
{isEditingTitle ? (
|
||||
<div className="relative h-dvh w-full overflow-hidden bg-background">
|
||||
|
||||
{/* 1. Canvas Layer (Full Screen) */}
|
||||
<div className="absolute inset-0 z-0">
|
||||
<CanvasWorkspace />
|
||||
</div>
|
||||
|
||||
{/* 2. Floating Header (Top Left) */}
|
||||
<div className="absolute top-4 left-4 z-20 flex items-center gap-2 bg-background/80 backdrop-blur-md border rounded-xl p-1.5 shadow-sm">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" onClick={() => router.push('/')} className="rounded-lg h-8 w-8">
|
||||
<ArrowLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>返回首页</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Separator orientation="vertical" className="h-5" />
|
||||
|
||||
{isEditingTitle ? (
|
||||
<Input
|
||||
value={editTitle}
|
||||
onChange={(e) => setEditTitle(e.target.value)}
|
||||
@ -374,12 +390,12 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
||||
if (e.key === 'Enter') handleUpdateTitle();
|
||||
if (e.key === 'Escape') setIsEditingTitle(false);
|
||||
}}
|
||||
className="h-7 w-48 text-sm"
|
||||
className="h-8 w-48 text-sm bg-transparent border-none focus-visible:ring-0 px-2"
|
||||
autoFocus
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
className="text-sm font-medium hover:text-primary transition-colors"
|
||||
className="text-sm font-medium px-3 h-8 hover:bg-muted rounded-lg transition-colors"
|
||||
onClick={() => {
|
||||
setEditTitle(projectTitle);
|
||||
setIsEditingTitle(true);
|
||||
@ -388,208 +404,81 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
||||
{projectTitle}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 中间:工具栏 */}
|
||||
<div className="flex items-center gap-1">
|
||||
{/* 选择/平移工具 */}
|
||||
<div className="flex items-center bg-muted rounded-md p-0.5">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant={tool === 'select' ? 'secondary' : 'ghost'}
|
||||
size="icon"
|
||||
className="h-7 w-7"
|
||||
onClick={() => setTool('select')}
|
||||
>
|
||||
<MousePointer className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>选择工具 (V)</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant={tool === 'pan' ? 'secondary' : 'ghost'}
|
||||
size="icon"
|
||||
className="h-7 w-7"
|
||||
onClick={() => setTool('pan')}
|
||||
>
|
||||
<Hand className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>平移工具 (H)</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<Separator orientation="vertical" className="h-6 mx-2" />
|
||||
|
||||
{/* 撤销/重做 */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={undo}
|
||||
disabled={!canUndo}
|
||||
>
|
||||
<Undo2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>撤销 (Ctrl+Z)</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={redo}
|
||||
disabled={!canRedo}
|
||||
>
|
||||
<Redo2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>重做 (Ctrl+Shift+Z)</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Separator orientation="vertical" className="h-6 mx-2" />
|
||||
|
||||
{/* 缩放 */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={zoomOut}
|
||||
>
|
||||
<ZoomOut className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>缩小</TooltipContent>
|
||||
</Tooltip>
|
||||
<span className="text-xs w-12 text-center tabular-nums">
|
||||
{Math.round(viewport.zoom * 100)}%
|
||||
</span>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={zoomIn}
|
||||
>
|
||||
<ZoomIn className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>放大</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Separator orientation="vertical" className="h-6 mx-2" />
|
||||
|
||||
{/* 添加节点 */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="sm" className="h-8">
|
||||
<Plus className="h-4 w-4 mr-1" />
|
||||
添加
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="center">
|
||||
<DropdownMenuItem onClick={handleAddTextNode}>
|
||||
<Type className="h-4 w-4 mr-2" />
|
||||
文本节点
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={handleAddImageNode}>
|
||||
<ImagePlus className="h-4 w-4 mr-2" />
|
||||
图片节点
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={handleAddVideoNode}>
|
||||
<Video className="h-4 w-4 mr-2" />
|
||||
视频节点
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
{/* 右侧:面板切换 + 保存 */}
|
||||
<div className="flex items-center gap-2">
|
||||
{isGenerating && (
|
||||
<div className="flex items-center gap-1 text-xs text-muted-foreground">
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
生成中...
|
||||
</div>
|
||||
)}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={() => setIsPanelOpen(!isPanelOpen)}
|
||||
>
|
||||
{isPanelOpen ? (
|
||||
<PanelLeftClose className="h-4 w-4" />
|
||||
) : (
|
||||
<PanelLeft className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{isPanelOpen ? "隐藏面板" : "显示面板"}</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
className="h-8"
|
||||
onClick={handleSave}
|
||||
disabled={isSaving}
|
||||
>
|
||||
{isSaving ? (
|
||||
<Loader2 className="h-4 w-4 mr-1 animate-spin" />
|
||||
) : (
|
||||
<Save className="h-4 w-4 mr-1" />
|
||||
)}
|
||||
保存
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>保存 (Ctrl+S)</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 主体区域 */}
|
||||
<div className="flex-1 flex overflow-hidden">
|
||||
{/* 左侧面板 - 标签页切换 AI对话/快捷工具 */}
|
||||
<div
|
||||
className={cn(
|
||||
"border-r bg-card transition-all duration-300 ease-in-out overflow-hidden flex flex-col",
|
||||
isPanelOpen ? "w-96" : "w-0"
|
||||
)}
|
||||
>
|
||||
{isPanelOpen && (
|
||||
{/* 3. Floating Actions (Top Right) */}
|
||||
<div className="absolute top-4 right-4 z-20 flex items-center gap-2">
|
||||
<div className="bg-background/80 backdrop-blur-md border rounded-xl p-1.5 shadow-sm flex items-center gap-1">
|
||||
{isGenerating && (
|
||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground px-2 border-r mr-1">
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
<span className="hidden sm:inline">生成中...</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Save Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleSave}
|
||||
disabled={isSaving}
|
||||
className="rounded-lg h-8"
|
||||
>
|
||||
{isSaving ? <Loader2 className="h-4 w-4 animate-spin" /> : <Save className="h-4 w-4" />}
|
||||
<span className="ml-2 hidden sm:inline">保存</span>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>保存 (Ctrl+S)</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Separator orientation="vertical" className="h-5" />
|
||||
|
||||
{/* Panel Toggle */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setIsPanelOpen(!isPanelOpen)}
|
||||
className={cn("rounded-lg h-8 w-8", isPanelOpen && "bg-muted")}
|
||||
>
|
||||
{isPanelOpen ? <PanelLeftClose className="h-4 w-4" /> : <PanelLeft className="h-4 w-4" />}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{isPanelOpen ? "隐藏面板" : "显示面板"}</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 4. Floating Sidebar (Left) */}
|
||||
<div className={cn(
|
||||
"absolute top-20 left-4 bottom-24 z-10 w-[400px] transition-all duration-300 ease-in-out",
|
||||
isPanelOpen ? "translate-x-0 opacity-100" : "-translate-x-[120%] opacity-0"
|
||||
)}>
|
||||
<div className="h-full w-full rounded-2xl border bg-background/95 backdrop-blur-md shadow-2xl overflow-hidden flex flex-col">
|
||||
<Tabs defaultValue="chat" className="flex flex-col h-full">
|
||||
<TabsList className="grid w-full grid-cols-2 shrink-0 m-2 mb-0" style={{ width: 'calc(100% - 16px)' }}>
|
||||
<TabsTrigger value="chat" className="text-xs">
|
||||
<MessageSquare className="h-3.5 w-3.5 mr-1.5" />
|
||||
AI 对话
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="tools" className="text-xs">
|
||||
<Wand2 className="h-3.5 w-3.5 mr-1.5" />
|
||||
快捷工具
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<div className="px-4 pt-4 pb-2 bg-muted/30 border-b">
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsTrigger value="chat" className="text-xs">
|
||||
<MessageSquare className="h-3.5 w-3.5 mr-1.5" />
|
||||
AI 对话
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="tools" className="text-xs">
|
||||
<Wand2 className="h-3.5 w-3.5 mr-1.5" />
|
||||
快捷工具
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
|
||||
{/* AI 对话标签页 */}
|
||||
<TabsContent value="chat" className="flex-1 overflow-hidden m-0 mt-2">
|
||||
<TabsContent value="chat" className="flex-1 overflow-hidden m-0">
|
||||
<CanvasChatPanel chatId={`canvas-${id}`} />
|
||||
</TabsContent>
|
||||
|
||||
{/* 快捷工具标签页 */}
|
||||
<TabsContent value="tools" className="flex-1 overflow-hidden m-0 mt-2">
|
||||
<TabsContent value="tools" className="flex-1 overflow-hidden m-0">
|
||||
<ScrollArea className="h-full">
|
||||
<div className="p-4 space-y-6">
|
||||
{/* AI 图片生成 */}
|
||||
@ -606,7 +495,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
||||
placeholder="描述你想要生成的图片..."
|
||||
value={prompt}
|
||||
onChange={(e) => setPrompt(e.target.value)}
|
||||
className="min-h-[100px] text-sm resize-none"
|
||||
className="min-h-[100px] text-sm resize-none bg-muted/30"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -616,14 +505,14 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
||||
placeholder="描述你不想出现的内容..."
|
||||
value={negativePrompt}
|
||||
onChange={(e) => setNegativePrompt(e.target.value)}
|
||||
className="min-h-[60px] text-sm resize-none"
|
||||
className="min-h-[60px] text-sm resize-none bg-muted/30"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs">尺寸比例</Label>
|
||||
<Select value={aspectRatio} onValueChange={setAspectRatio}>
|
||||
<SelectTrigger className="h-9">
|
||||
<SelectTrigger className="h-9 bg-muted/30">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@ -681,33 +570,33 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
||||
|
||||
{selectedImageNode ? (
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start">
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start bg-muted/30">
|
||||
<Scissors className="h-4 w-4 mr-2" />
|
||||
抠图
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start">
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start bg-muted/30">
|
||||
<Sparkles className="h-4 w-4 mr-2" />
|
||||
变清晰
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start">
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start bg-muted/30">
|
||||
<Expand className="h-4 w-4 mr-2" />
|
||||
扩展
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start">
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start bg-muted/30">
|
||||
<Maximize2 className="h-4 w-4 mr-2" />
|
||||
调整
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start">
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start bg-muted/30">
|
||||
<Move3D className="h-4 w-4 mr-2" />
|
||||
角度
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start">
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start bg-muted/30">
|
||||
<Layers className="h-4 w-4 mr-2" />
|
||||
场景
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-xs text-muted-foreground text-center py-4">
|
||||
<p className="text-xs text-muted-foreground text-center py-4 bg-muted/20 rounded-lg border border-dashed">
|
||||
选中图片节点以使用编辑工具
|
||||
</p>
|
||||
)}
|
||||
@ -723,19 +612,19 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start" onClick={handleAddImageNode}>
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start bg-muted/30" onClick={handleAddImageNode}>
|
||||
<ImagePlus className="h-4 w-4 mr-2" />
|
||||
添加图片
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start" onClick={handleAddTextNode}>
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start bg-muted/30" onClick={handleAddTextNode}>
|
||||
<Type className="h-4 w-4 mr-2" />
|
||||
添加文本
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start">
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start bg-muted/30">
|
||||
<Upload className="h-4 w-4 mr-2" />
|
||||
上传图片
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start">
|
||||
<Button variant="outline" size="sm" className="h-9 justify-start bg-muted/30">
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
导出
|
||||
</Button>
|
||||
@ -745,14 +634,107 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
||||
</ScrollArea>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 画布工作区 */}
|
||||
<div className="flex-1 overflow-hidden bg-muted/30">
|
||||
<CanvasWorkspace />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 5. Floating Bottom Toolbar (Center) */}
|
||||
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 z-20">
|
||||
<div className="flex items-center gap-1.5 p-2 rounded-2xl border bg-background/80 backdrop-blur-md shadow-2xl">
|
||||
{/* Tools: Select, Pan, Add Text, Add Image, Add Video */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant={tool === 'select' ? 'secondary' : 'ghost'}
|
||||
size="icon"
|
||||
onClick={() => setTool('select')}
|
||||
className={cn("rounded-xl h-10 w-10", tool === 'select' && "bg-primary text-primary-foreground hover:bg-primary/90")}
|
||||
>
|
||||
<MousePointer className="h-5 w-5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>选择 (V)</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant={tool === 'pan' ? 'secondary' : 'ghost'}
|
||||
size="icon"
|
||||
onClick={() => setTool('pan')}
|
||||
className={cn("rounded-xl h-10 w-10", tool === 'pan' && "bg-primary text-primary-foreground hover:bg-primary/90")}
|
||||
>
|
||||
<Hand className="h-5 w-5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>平移 (H)</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Separator orientation="vertical" className="h-6 mx-1" />
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" onClick={handleAddTextNode} className="rounded-xl h-10 w-10">
|
||||
<Type className="h-5 w-5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>添加文本</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" onClick={handleAddImageNode} className="rounded-xl h-10 w-10">
|
||||
<ImagePlus className="h-5 w-5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>添加图片</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" onClick={handleAddVideoNode} className="rounded-xl h-10 w-10">
|
||||
<Video className="h-5 w-5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>添加视频</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Separator orientation="vertical" className="h-6 mx-1" />
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" onClick={undo} disabled={!canUndo} className="rounded-xl h-10 w-10">
|
||||
<Undo2 className="h-5 w-5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>撤销</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="ghost" size="icon" onClick={redo} disabled={!canRedo} className="rounded-xl h-10 w-10">
|
||||
<Redo2 className="h-5 w-5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>重做</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Separator orientation="vertical" className="h-6 mx-1" />
|
||||
|
||||
{/* Zoom Controls */}
|
||||
<div className="flex items-center gap-1">
|
||||
<Button variant="ghost" size="icon" onClick={zoomOut} className="h-8 w-8 rounded-lg">
|
||||
<ZoomOut className="h-4 w-4" />
|
||||
</Button>
|
||||
<span className="text-xs w-10 text-center tabular-nums font-medium">
|
||||
{Math.round(viewport.zoom * 100)}%
|
||||
</span>
|
||||
<Button variant="ghost" size="icon" onClick={zoomIn} className="h-8 w-8 rounded-lg">
|
||||
<ZoomIn className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
);
|
||||
|
||||
@ -217,7 +217,7 @@ export function CanvasWorkspace() {
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={cn(
|
||||
"relative w-full h-full overflow-hidden bg-[#f5f5f5] dark:bg-[#1a1a2e]",
|
||||
"relative w-full h-full overflow-hidden bg-background",
|
||||
tool === 'pan' && "cursor-grab",
|
||||
isPanning && "cursor-grabbing"
|
||||
)}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user