Files
audio-ui/lib/hooks/useEffectChain.ts
Sebastian Krüger 0986896756 feat: add Phase 6.6 Effect Chain Management system
Effect Chain System:
- Create comprehensive effect chain types and state management
- Implement EffectRack component with drag-and-drop reordering
- Add enable/disable toggle for individual effects
- Build PresetManager for save/load/import/export functionality
- Create useEffectChain hook with localStorage persistence

UI Integration:
- Add Chain tab to SidePanel with effect rack
- Integrate preset manager dialog
- Add chain management controls (clear, presets)
- Update SidePanel with chain props and handlers

Features:
- Drag-and-drop effect reordering with visual feedback
- Effect bypass/enable toggle with power icons
- Save effect chains as presets with descriptions
- Import/export presets as JSON files
- localStorage persistence for chains and presets
- Visual status indicators for enabled/disabled effects
- Preset timestamp and effect count display

Components Created:
- /lib/audio/effects/chain.ts - Effect chain types and utilities
- /components/effects/EffectRack.tsx - Visual effect chain component
- /components/effects/PresetManager.tsx - Preset management dialog
- /lib/hooks/useEffectChain.ts - Effect chain state hook

Updated PLAN.md:
- Mark Phase 6.6 as complete
- Update current status to Phase 6.6 Complete
- Add effect chain features to working features list
- Update Next Steps to show Phase 6 complete

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 20:27:08 +01:00

123 lines
3.2 KiB
TypeScript

import { useState, useCallback, useEffect } from 'react';
import type {
EffectChain,
EffectPreset,
ChainEffect,
EffectParameters,
} from '@/lib/audio/effects/chain';
import {
createEffectChain,
addEffectToChain,
removeEffectFromChain,
toggleEffect,
updateEffectParameters,
reorderEffects,
loadPreset,
} from '@/lib/audio/effects/chain';
const STORAGE_KEY_CHAIN = 'audio-ui-effect-chain';
const STORAGE_KEY_PRESETS = 'audio-ui-effect-presets';
export function useEffectChain() {
const [chain, setChain] = useState<EffectChain>(() => {
if (typeof window === 'undefined') return createEffectChain('Main Chain');
try {
const saved = localStorage.getItem(STORAGE_KEY_CHAIN);
return saved ? JSON.parse(saved) : createEffectChain('Main Chain');
} catch {
return createEffectChain('Main Chain');
}
});
const [presets, setPresets] = useState<EffectPreset[]>(() => {
if (typeof window === 'undefined') return [];
try {
const saved = localStorage.getItem(STORAGE_KEY_PRESETS);
return saved ? JSON.parse(saved) : [];
} catch {
return [];
}
});
// Save chain to localStorage whenever it changes
useEffect(() => {
if (typeof window === 'undefined') return;
try {
localStorage.setItem(STORAGE_KEY_CHAIN, JSON.stringify(chain));
} catch (error) {
console.error('Failed to save effect chain:', error);
}
}, [chain]);
// Save presets to localStorage whenever they change
useEffect(() => {
if (typeof window === 'undefined') return;
try {
localStorage.setItem(STORAGE_KEY_PRESETS, JSON.stringify(presets));
} catch (error) {
console.error('Failed to save presets:', error);
}
}, [presets]);
const addEffect = useCallback((effect: ChainEffect) => {
setChain((prev) => addEffectToChain(prev, effect));
}, []);
const removeEffect = useCallback((effectId: string) => {
setChain((prev) => removeEffectFromChain(prev, effectId));
}, []);
const toggleEffectEnabled = useCallback((effectId: string) => {
setChain((prev) => toggleEffect(prev, effectId));
}, []);
const updateEffect = useCallback(
(effectId: string, parameters: EffectParameters) => {
setChain((prev) => updateEffectParameters(prev, effectId, parameters));
},
[]
);
const reorder = useCallback((fromIndex: number, toIndex: number) => {
setChain((prev) => reorderEffects(prev, fromIndex, toIndex));
}, []);
const clearChain = useCallback(() => {
setChain((prev) => ({ ...prev, effects: [] }));
}, []);
const savePreset = useCallback((preset: EffectPreset) => {
setPresets((prev) => [...prev, preset]);
}, []);
const loadPresetToChain = useCallback((preset: EffectPreset) => {
const loadedChain = loadPreset(preset);
setChain(loadedChain);
}, []);
const deletePreset = useCallback((presetId: string) => {
setPresets((prev) => prev.filter((p) => p.id !== presetId));
}, []);
const importPreset = useCallback((preset: EffectPreset) => {
setPresets((prev) => [...prev, preset]);
}, []);
return {
chain,
presets,
addEffect,
removeEffect,
toggleEffectEnabled,
updateEffect,
reorder,
clearChain,
savePreset,
loadPresetToChain,
deletePreset,
importPreset,
};
}