feat: implement UI state persistence and theme toggle
Major improvements to UI state management and user preferences: - Add theme toggle with dark/light mode support - Implement Zustand persist middleware for UI state - Add ui-store for panel layout preferences (dock width, heights, tabs) - Persist tool settings (active tool, size, opacity, hardness, etc.) - Persist canvas view preferences (grid, rulers, snap-to-grid) - Persist shape tool settings - Persist collapsible section states - Fix canvas coordinate transformation for centered rendering - Constrain checkerboard and grid to canvas bounds - Add icons to all tab buttons and collapsible sections - Restructure panel-dock to use persisted state Storage impact: ~3.5KB total across all preferences Storage keys: tool-storage, canvas-view-storage, shape-storage, ui-storage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@ import { useColorStore } from '@/store/color-store';
|
||||
import { useToolStore } from '@/store';
|
||||
import { ColorPicker } from './color-picker';
|
||||
import { ColorSwatches } from './color-swatches';
|
||||
import { ArrowLeftRight, Palette, Clock } from 'lucide-react';
|
||||
import { ArrowLeftRight, Palette, Clock, Grid3x3 } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
type Tab = 'picker' | 'swatches' | 'recent';
|
||||
@@ -32,15 +32,10 @@ export function ColorPanel() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full bg-card border-r border-border w-64">
|
||||
{/* Header */}
|
||||
<div className="border-b border-border p-3">
|
||||
<h2 className="text-sm font-semibold text-card-foreground">Colors</h2>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col h-full bg-card w-full">
|
||||
{/* Current colors display */}
|
||||
<div className="p-3 border-b border-border">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="border-b border-border">
|
||||
<div className="flex items-center gap-2 p-3">
|
||||
{/* Primary/Secondary color squares */}
|
||||
<div className="relative">
|
||||
<button
|
||||
@@ -75,42 +70,45 @@ export function ColorPanel() {
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex border-b border-border">
|
||||
<button
|
||||
onClick={() => setActiveTab('picker')}
|
||||
className={cn(
|
||||
'flex-1 flex items-center justify-center gap-2 py-2 text-sm transition-colors',
|
||||
activeTab === 'picker'
|
||||
? 'bg-background text-foreground border-b-2 border-primary'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-accent'
|
||||
)}
|
||||
>
|
||||
<Palette className="h-4 w-4" />
|
||||
Picker
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('swatches')}
|
||||
className={cn(
|
||||
'flex-1 flex items-center justify-center gap-2 py-2 text-sm transition-colors',
|
||||
activeTab === 'swatches'
|
||||
? 'bg-background text-foreground border-b-2 border-primary'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-accent'
|
||||
)}
|
||||
>
|
||||
Swatches
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('recent')}
|
||||
className={cn(
|
||||
'flex-1 flex items-center justify-center gap-2 py-2 text-sm transition-colors',
|
||||
activeTab === 'recent'
|
||||
? 'bg-background text-foreground border-b-2 border-primary'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-accent'
|
||||
)}
|
||||
>
|
||||
<Clock className="h-4 w-4" />
|
||||
Recent
|
||||
</button>
|
||||
<div className="border-b border-border">
|
||||
<div className="flex">
|
||||
<button
|
||||
onClick={() => setActiveTab('picker')}
|
||||
className={cn(
|
||||
'flex-1 flex items-center justify-center gap-2 py-2 text-sm transition-colors',
|
||||
activeTab === 'picker'
|
||||
? 'bg-background text-foreground border-b-2 border-primary'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-accent'
|
||||
)}
|
||||
>
|
||||
<Palette className="h-4 w-4" />
|
||||
Picker
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('swatches')}
|
||||
className={cn(
|
||||
'flex-1 flex items-center justify-center gap-2 py-2 text-sm transition-colors',
|
||||
activeTab === 'swatches'
|
||||
? 'bg-background text-foreground border-b-2 border-primary'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-accent'
|
||||
)}
|
||||
>
|
||||
<Grid3x3 className="h-4 w-4" />
|
||||
Swatches
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('recent')}
|
||||
className={cn(
|
||||
'flex-1 flex items-center justify-center gap-2 py-2 text-sm transition-colors',
|
||||
activeTab === 'recent'
|
||||
? 'bg-background text-foreground border-b-2 border-primary'
|
||||
: 'text-muted-foreground hover:text-foreground hover:bg-accent'
|
||||
)}
|
||||
>
|
||||
<Clock className="h-4 w-4" />
|
||||
Recent
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
|
||||
Reference in New Issue
Block a user