2025-11-21 01:55:28 +01:00
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import { useState } from 'react';
|
|
|
|
|
import { useColorStore } from '@/store/color-store';
|
|
|
|
|
import { useToolStore } from '@/store';
|
|
|
|
|
import { ColorPicker } from './color-picker';
|
|
|
|
|
import { ColorSwatches } from './color-swatches';
|
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>
2025-11-21 09:03:14 +01:00
|
|
|
import { ArrowLeftRight, Palette, Clock, Grid3x3 } from 'lucide-react';
|
2025-11-21 01:55:28 +01:00
|
|
|
import { cn } from '@/lib/utils';
|
|
|
|
|
|
|
|
|
|
type Tab = 'picker' | 'swatches' | 'recent';
|
|
|
|
|
|
|
|
|
|
export function ColorPanel() {
|
|
|
|
|
const {
|
|
|
|
|
primaryColor,
|
|
|
|
|
secondaryColor,
|
|
|
|
|
recentColors,
|
|
|
|
|
customSwatches,
|
|
|
|
|
setPrimaryColor,
|
|
|
|
|
setSecondaryColor,
|
|
|
|
|
swapColors,
|
|
|
|
|
addCustomSwatch,
|
|
|
|
|
removeCustomSwatch,
|
|
|
|
|
} = useColorStore();
|
|
|
|
|
|
|
|
|
|
const { setColor } = useToolStore();
|
|
|
|
|
const [activeTab, setActiveTab] = useState<Tab>('picker');
|
|
|
|
|
|
|
|
|
|
const handleColorChange = (color: string) => {
|
|
|
|
|
setPrimaryColor(color);
|
|
|
|
|
setColor(color);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
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>
2025-11-21 09:03:14 +01:00
|
|
|
<div className="flex flex-col h-full bg-card w-full">
|
2025-11-21 01:55:28 +01:00
|
|
|
{/* Current colors display */}
|
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>
2025-11-21 09:03:14 +01:00
|
|
|
<div className="border-b border-border">
|
|
|
|
|
<div className="flex items-center gap-2 p-3">
|
2025-11-21 01:55:28 +01:00
|
|
|
{/* Primary/Secondary color squares */}
|
|
|
|
|
<div className="relative">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleColorChange(primaryColor)}
|
|
|
|
|
className="w-12 h-12 rounded-md border-2 border-border relative z-10"
|
|
|
|
|
style={{ backgroundColor: primaryColor }}
|
|
|
|
|
title={`Primary: ${primaryColor}`}
|
|
|
|
|
/>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setSecondaryColor(secondaryColor)}
|
|
|
|
|
className="absolute w-8 h-8 rounded-md border-2 border-border -bottom-1 -right-1"
|
|
|
|
|
style={{ backgroundColor: secondaryColor }}
|
|
|
|
|
title={`Secondary: ${secondaryColor}`}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Swap button */}
|
|
|
|
|
<button
|
|
|
|
|
onClick={swapColors}
|
|
|
|
|
className="p-2 hover:bg-accent rounded-md transition-colors"
|
|
|
|
|
title="Swap colors (X)"
|
|
|
|
|
>
|
|
|
|
|
<ArrowLeftRight className="h-4 w-4" />
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
{/* Color values */}
|
|
|
|
|
<div className="flex-1 text-xs space-y-1">
|
|
|
|
|
<div className="font-mono">{primaryColor}</div>
|
|
|
|
|
<div className="font-mono text-muted-foreground">{secondaryColor}</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Tabs */}
|
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>
2025-11-21 09:03:14 +01:00
|
|
|
<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>
|
2025-11-21 01:55:28 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Content */}
|
|
|
|
|
<div className="flex-1 overflow-y-auto p-3">
|
|
|
|
|
{activeTab === 'picker' && (
|
|
|
|
|
<ColorPicker color={primaryColor} onChange={handleColorChange} />
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{activeTab === 'swatches' && (
|
|
|
|
|
<ColorSwatches
|
|
|
|
|
currentColor={primaryColor}
|
|
|
|
|
onColorSelect={handleColorChange}
|
|
|
|
|
customSwatches={customSwatches}
|
|
|
|
|
onAddSwatch={addCustomSwatch}
|
|
|
|
|
onRemoveSwatch={removeCustomSwatch}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{activeTab === 'recent' && (
|
|
|
|
|
<div className="space-y-3">
|
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
|
|
|
Recently used colors
|
|
|
|
|
</p>
|
|
|
|
|
{recentColors.length > 0 ? (
|
|
|
|
|
<div className="grid grid-cols-10 gap-1">
|
|
|
|
|
{recentColors.map((color, index) => (
|
|
|
|
|
<button
|
|
|
|
|
key={`${color}-${index}`}
|
|
|
|
|
onClick={() => handleColorChange(color)}
|
|
|
|
|
className={cn(
|
|
|
|
|
'w-6 h-6 rounded border-2 transition-all hover:scale-110',
|
|
|
|
|
primaryColor.toUpperCase() === color.toUpperCase()
|
|
|
|
|
? 'border-primary ring-2 ring-primary/50'
|
|
|
|
|
: 'border-border hover:border-primary/50'
|
|
|
|
|
)}
|
|
|
|
|
style={{ backgroundColor: color }}
|
|
|
|
|
title={color}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<p className="text-sm text-muted-foreground italic">
|
|
|
|
|
No recent colors yet. Start drawing!
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|