Files
paint-ui/store/tool-store.ts
Sebastian Krüger 8f595ac6c4 feat(phase-13): implement gradient tool with linear, radial, and angular modes
Add comprehensive gradient tool with three gradient types and full UI integration.

Features:
- Gradient tool with drag-to-create interaction
- Three gradient types: Linear, Radial, and Angular (conic)
- Live preview during drag with 70% opacity overlay
- Primary and secondary color selection
- Gradient type selector in tool options
- Undo/redo support through command system
- Fallback to radial gradient for browsers without conic gradient support

Changes:
- Created tools/gradient-tool.ts with GradientTool class
- Added 'gradient' to ToolType in types/tool.ts
- Extended ToolSettings with secondaryColor and gradientType
- Updated store/tool-store.ts with setSecondaryColor and setGradientType methods
- Added gradient tool loading in lib/tool-loader.ts
- Added gradient button to tool palette with 'G' shortcut
- Added gradient tool options UI in components/editor/tool-options.tsx

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 19:48:00 +01:00

142 lines
3.6 KiB
TypeScript

import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import type { ToolType, ToolSettings, ToolState } from '@/types';
interface ToolStore extends ToolState {
/** Set active tool */
setActiveTool: (tool: ToolType) => void;
/** Update tool settings */
updateSettings: (settings: Partial<ToolSettings>) => void;
/** Set brush size */
setSize: (size: number) => void;
/** Set opacity */
setOpacity: (opacity: number) => void;
/** Set hardness */
setHardness: (hardness: number) => void;
/** Set color */
setColor: (color: string) => void;
/** Set secondary color */
setSecondaryColor: (color: string) => void;
/** Set gradient type */
setGradientType: (type: 'linear' | 'radial' | 'angular') => void;
/** Set flow */
setFlow: (flow: number) => void;
/** Set spacing */
setSpacing: (spacing: number) => void;
/** Reset settings to defaults */
resetSettings: () => void;
}
const DEFAULT_SETTINGS: ToolSettings = {
size: 10,
opacity: 1,
hardness: 1,
color: '#000000',
secondaryColor: '#ffffff',
gradientType: 'linear',
flow: 1,
spacing: 0.25,
};
export const useToolStore = create<ToolStore>()(
persist(
(set) => ({
activeTool: 'brush',
settings: { ...DEFAULT_SETTINGS },
cursor: 'crosshair',
setActiveTool: (tool) => {
const cursors: Record<ToolType, string> = {
select: 'crosshair',
move: 'move',
pencil: 'crosshair',
brush: 'crosshair',
eraser: 'crosshair',
fill: 'crosshair',
gradient: 'crosshair',
eyedropper: 'crosshair',
text: 'text',
shape: 'crosshair',
crop: 'crosshair',
clone: 'crosshair',
smudge: 'crosshair',
dodge: 'crosshair',
blur: 'crosshair',
sharpen: 'crosshair',
};
set({
activeTool: tool,
cursor: cursors[tool],
});
},
updateSettings: (settings) => {
set((state) => ({
settings: { ...state.settings, ...settings },
}));
},
setSize: (size) => {
set((state) => ({
settings: { ...state.settings, size: Math.max(1, Math.min(1000, size)) },
}));
},
setOpacity: (opacity) => {
set((state) => ({
settings: { ...state.settings, opacity: Math.max(0, Math.min(1, opacity)) },
}));
},
setHardness: (hardness) => {
set((state) => ({
settings: { ...state.settings, hardness: Math.max(0, Math.min(1, hardness)) },
}));
},
setColor: (color) => {
set((state) => ({
settings: { ...state.settings, color },
}));
},
setSecondaryColor: (color) => {
set((state) => ({
settings: { ...state.settings, secondaryColor: color },
}));
},
setGradientType: (type) => {
set((state) => ({
settings: { ...state.settings, gradientType: type },
}));
},
setFlow: (flow) => {
set((state) => ({
settings: { ...state.settings, flow: Math.max(0, Math.min(1, flow)) },
}));
},
setSpacing: (spacing) => {
set((state) => ({
settings: { ...state.settings, spacing: Math.max(0.01, Math.min(10, spacing)) },
}));
},
resetSettings: () => {
set({ settings: { ...DEFAULT_SETTINGS } });
},
}),
{
name: 'tool-storage',
partialize: (state) => ({
activeTool: state.activeTool,
settings: state.settings,
// Exclude cursor - it's derived from activeTool
}),
}
)
);