feat(phase-13): implement brush presets system
Add comprehensive brush presets system for saving and loading brush configurations. Features: - Brush presets store with persistence - 5 default presets included: * Hard Brush - Solid, precise edges * Soft Brush - Smooth, gradual falloff * Airbrush - Low opacity, soft flow * Pencil - Tiny, hard, precise * Ink - Medium, slightly soft - Preset management functions: * addPreset() - Save current brush settings * removePreset() - Delete preset * updatePreset() - Modify preset * setActivePreset() - Select active preset * getPreset() - Get preset by ID * clearPresets() - Reset to defaults - Each preset stores: * size, opacity, hardness, flow, spacing * Unique ID and creation timestamp - localStorage persistence - Active preset tracking Changes: - Created store/brush-presets-store.ts - BrushPreset interface with settings - Exported from store/index.ts - Ready for UI integration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
152
store/brush-presets-store.ts
Normal file
152
store/brush-presets-store.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import type { ToolSettings } from '@/types';
|
||||
|
||||
export interface BrushPreset {
|
||||
id: string;
|
||||
name: string;
|
||||
settings: Partial<ToolSettings>;
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
interface BrushPresetsStore {
|
||||
/** All brush presets */
|
||||
presets: BrushPreset[];
|
||||
/** Currently selected preset ID */
|
||||
activePresetId: string | null;
|
||||
|
||||
/** Add a new preset */
|
||||
addPreset: (name: string, settings: Partial<ToolSettings>) => BrushPreset;
|
||||
/** Remove a preset */
|
||||
removePreset: (id: string) => void;
|
||||
/** Update a preset */
|
||||
updatePreset: (id: string, name: string, settings: Partial<ToolSettings>) => void;
|
||||
/** Set active preset */
|
||||
setActivePreset: (id: string | null) => void;
|
||||
/** Get preset by ID */
|
||||
getPreset: (id: string) => BrushPreset | undefined;
|
||||
/** Clear all presets */
|
||||
clearPresets: () => void;
|
||||
}
|
||||
|
||||
// Default presets
|
||||
const DEFAULT_PRESETS: Omit<BrushPreset, 'id' | 'createdAt'>[] = [
|
||||
{
|
||||
name: 'Hard Brush',
|
||||
settings: {
|
||||
size: 10,
|
||||
opacity: 1,
|
||||
hardness: 1,
|
||||
flow: 1,
|
||||
spacing: 0.25,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Soft Brush',
|
||||
settings: {
|
||||
size: 30,
|
||||
opacity: 1,
|
||||
hardness: 0,
|
||||
flow: 1,
|
||||
spacing: 0.25,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Airbrush',
|
||||
settings: {
|
||||
size: 50,
|
||||
opacity: 0.3,
|
||||
hardness: 0,
|
||||
flow: 0.5,
|
||||
spacing: 0.1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Pencil',
|
||||
settings: {
|
||||
size: 2,
|
||||
opacity: 1,
|
||||
hardness: 1,
|
||||
flow: 1,
|
||||
spacing: 0.1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Ink',
|
||||
settings: {
|
||||
size: 5,
|
||||
opacity: 1,
|
||||
hardness: 0.8,
|
||||
flow: 1,
|
||||
spacing: 0.15,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const useBrushPresetsStore = create<BrushPresetsStore>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
presets: DEFAULT_PRESETS.map((preset, index) => ({
|
||||
...preset,
|
||||
id: `default-${index}`,
|
||||
createdAt: Date.now() - (DEFAULT_PRESETS.length - index) * 1000,
|
||||
})),
|
||||
activePresetId: null,
|
||||
|
||||
addPreset: (name, settings) => {
|
||||
const now = Date.now();
|
||||
const preset: BrushPreset = {
|
||||
id: `preset-${now}`,
|
||||
name,
|
||||
settings,
|
||||
createdAt: now,
|
||||
};
|
||||
|
||||
set((state) => ({
|
||||
presets: [...state.presets, preset],
|
||||
}));
|
||||
|
||||
return preset;
|
||||
},
|
||||
|
||||
removePreset: (id) => {
|
||||
set((state) => ({
|
||||
presets: state.presets.filter((p) => p.id !== id),
|
||||
activePresetId: state.activePresetId === id ? null : state.activePresetId,
|
||||
}));
|
||||
},
|
||||
|
||||
updatePreset: (id, name, settings) => {
|
||||
set((state) => ({
|
||||
presets: state.presets.map((p) =>
|
||||
p.id === id
|
||||
? { ...p, name, settings }
|
||||
: p
|
||||
),
|
||||
}));
|
||||
},
|
||||
|
||||
setActivePreset: (id) => {
|
||||
set({ activePresetId: id });
|
||||
},
|
||||
|
||||
getPreset: (id) => {
|
||||
return get().presets.find((p) => p.id === id);
|
||||
},
|
||||
|
||||
clearPresets: () => {
|
||||
set({
|
||||
presets: DEFAULT_PRESETS.map((preset, index) => ({
|
||||
...preset,
|
||||
id: `default-${index}`,
|
||||
createdAt: Date.now() - (DEFAULT_PRESETS.length - index) * 1000,
|
||||
})),
|
||||
activePresetId: null,
|
||||
});
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'brush-presets-storage',
|
||||
}
|
||||
)
|
||||
);
|
||||
@@ -13,3 +13,4 @@ export * from './toast-store';
|
||||
export * from './context-menu-store';
|
||||
export * from './guides-store';
|
||||
export * from './recent-files-store';
|
||||
export * from './brush-presets-store';
|
||||
|
||||
Reference in New Issue
Block a user