feat: canvas history (undo/redo), copy/paste nodes, node search, shortcuts modal
- Undo/Redo (Ctrl+Z / Ctrl+Y): 50-entry snapshot stack in canvasStore; snapshot before all mutations and on node drag stop - Copy/Paste (Ctrl+C / Ctrl+V): copy selected nodes to clipboard, paste with +50px offset and new IDs - Node search (Ctrl+K): spotlight overlay — fuzzy search by label/IP/hostname, jumps + focuses matched node - Shortcuts modal (?): lists all keyboard shortcuts, accessible via ? key or toolbar ? button - Toolbar: undo/redo buttons (disabled when stack empty), ? help button
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Save, LayoutDashboard, Download, Palette } from 'lucide-react'
|
||||
import { Save, LayoutDashboard, Download, Palette, Undo2, Redo2, HelpCircle } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Logo } from '@/components/ui/Logo'
|
||||
import { useCanvasStore } from '@/stores/canvasStore'
|
||||
@@ -8,15 +8,37 @@ interface ToolbarProps {
|
||||
onAutoLayout: () => void
|
||||
onExport: () => void
|
||||
onChangeStyle: () => void
|
||||
onUndo: () => void
|
||||
onRedo: () => void
|
||||
onShortcuts: () => void
|
||||
}
|
||||
|
||||
export function Toolbar({ onSave, onAutoLayout, onExport, onChangeStyle }: ToolbarProps) {
|
||||
const { hasUnsavedChanges } = useCanvasStore()
|
||||
export function Toolbar({ onSave, onAutoLayout, onExport, onChangeStyle, onUndo, onRedo, onShortcuts }: ToolbarProps) {
|
||||
const { hasUnsavedChanges, past, future } = useCanvasStore()
|
||||
|
||||
return (
|
||||
<header className="flex items-center gap-2 px-4 py-2 border-b border-border bg-[#161b22] shrink-0">
|
||||
<Logo size={28} showText={true} />
|
||||
<div className="flex-1" />
|
||||
<Button
|
||||
size="sm" variant="ghost"
|
||||
className="gap-1.5 text-muted-foreground hover:text-foreground disabled:opacity-30"
|
||||
onClick={onUndo}
|
||||
disabled={past.length === 0}
|
||||
title="Undo (Ctrl+Z)"
|
||||
>
|
||||
<Undo2 size={14} />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm" variant="ghost"
|
||||
className="gap-1.5 text-muted-foreground hover:text-foreground disabled:opacity-30"
|
||||
onClick={onRedo}
|
||||
disabled={future.length === 0}
|
||||
title="Redo (Ctrl+Y)"
|
||||
>
|
||||
<Redo2 size={14} />
|
||||
</Button>
|
||||
<div className="w-px h-4 bg-border mx-1" />
|
||||
<Button size="sm" variant="ghost" className="gap-1.5 text-muted-foreground hover:text-foreground" onClick={onAutoLayout}>
|
||||
<LayoutDashboard size={14} /> Auto Layout
|
||||
</Button>
|
||||
@@ -26,6 +48,9 @@ export function Toolbar({ onSave, onAutoLayout, onExport, onChangeStyle }: Toolb
|
||||
<Button size="sm" variant="ghost" className="gap-1.5 text-muted-foreground hover:text-foreground" onClick={onExport}>
|
||||
<Download size={14} /> Export
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost" className="gap-1.5 text-muted-foreground hover:text-foreground" onClick={onShortcuts} title="Keyboard shortcuts (?)">
|
||||
<HelpCircle size={14} />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
className="gap-1.5 relative"
|
||||
|
||||
Reference in New Issue
Block a user