refactor: restructure to monorepo with npm workspaces (Phase 0)

Move frontend to packages/client/, server to packages/server/.
Root package.json uses npm workspaces to orchestrate both.

Structure:
  reaktor/
    packages/client/  (React + Vite + Tone.js frontend)
    packages/server/  (static file server, future API)
    dist/             (built output, shared)
    docker-compose.yml (app + PostgreSQL for future backend)

- npm run dev → runs Vite dev server from client workspace
- npm run build → builds client, outputs to root dist/
- npm run start → runs server.js serving dist/
- Dockerfile updated for multi-stage monorepo build
- docker-compose.yml added with PostgreSQL service (ready for Phase 1)
- All imports and paths preserved, zero functionality change

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jose Luis
2026-03-21 19:52:57 +01:00
parent 4baa86eed0
commit b058997889
59 changed files with 96 additions and 33 deletions

View File

@@ -0,0 +1,328 @@
/**
* moduleRegistry.js — Defines all available module types
* Each module type specifies: ports, params, icon, category, and audio factory
*/
export const PORT_TYPE = {
AUDIO: 'audio',
CONTROL: 'control',
TRIGGER: 'trigger',
};
// Module type definitions
const registry = {};
export function defineModule(type, def) {
registry[type] = { type, ...def };
}
export function getModuleDef(type) {
return registry[type] || null;
}
export function getAllModuleDefs() {
return Object.values(registry);
}
export function getModulesByCategory() {
const cats = {};
for (const def of Object.values(registry)) {
if (!cats[def.category]) cats[def.category] = [];
cats[def.category].push(def);
}
return cats;
}
// ==================== SOURCE ====================
defineModule('oscillator', {
name: 'Oscillator',
icon: '~',
category: 'Source',
inputs: [
{ name: 'freq', type: PORT_TYPE.AUDIO, label: 'Freq' },
{ name: 'detune', type: PORT_TYPE.CONTROL, label: 'Detune' },
],
outputs: [
{ name: 'out', type: PORT_TYPE.AUDIO, label: 'Out' },
],
params: {
waveform: { type: 'select', options: ['sine', 'square', 'sawtooth', 'triangle'], default: 'sawtooth', label: 'Wave' },
frequency: { type: 'knob', min: 20, max: 8000, default: 440, unit: 'Hz', label: 'Freq' },
detune: { type: 'knob', min: -1200, max: 1200, default: 0, unit: 'ct', label: 'Detune' },
},
});
defineModule('lfo', {
name: 'LFO',
icon: '∿',
category: 'Source',
inputs: [],
outputs: [
{ name: 'out', type: PORT_TYPE.CONTROL, label: 'Out' },
],
params: {
waveform: { type: 'select', options: ['sine', 'square', 'sawtooth', 'triangle'], default: 'sine', label: 'Wave' },
frequency: { type: 'knob', min: 0.01, max: 50, default: 2, unit: 'Hz', label: 'Rate' },
amplitude: { type: 'knob', min: 0, max: 1, default: 0.5, unit: '', label: 'Depth' },
},
});
defineModule('noise', {
name: 'Noise',
icon: '⣿',
category: 'Source',
inputs: [],
outputs: [
{ name: 'out', type: PORT_TYPE.AUDIO, label: 'Out' },
],
params: {
type: { type: 'select', options: ['white', 'pink', 'brown'], default: 'white', label: 'Type' },
},
});
// ==================== FILTER ====================
defineModule('filter', {
name: 'Filter',
icon: '▽',
category: 'Filter',
inputs: [
{ name: 'in', type: PORT_TYPE.AUDIO, label: 'In' },
{ name: 'cutoff', type: PORT_TYPE.CONTROL, label: 'Cutoff' },
],
outputs: [
{ name: 'out', type: PORT_TYPE.AUDIO, label: 'Out' },
],
params: {
type: { type: 'select', options: ['lowpass', 'highpass', 'bandpass', 'notch'], default: 'lowpass', label: 'Type' },
frequency: { type: 'knob', min: 20, max: 20000, default: 1000, unit: 'Hz', label: 'Cutoff' },
Q: { type: 'knob', min: 0.1, max: 20, default: 1, unit: '', label: 'Reso' },
},
});
// ==================== ENVELOPE ====================
defineModule('envelope', {
name: 'Envelope',
icon: '⏤╲',
category: 'Modulation',
inputs: [
{ name: 'gate', type: PORT_TYPE.TRIGGER, label: 'Gate' },
],
outputs: [
{ name: 'out', type: PORT_TYPE.CONTROL, label: 'Out' },
],
params: {
attack: { type: 'knob', min: 0.001, max: 4, default: 0.01, unit: 's', label: 'Attack' },
decay: { type: 'knob', min: 0.001, max: 4, default: 0.2, unit: 's', label: 'Decay' },
sustain: { type: 'knob', min: 0, max: 1, default: 0.5, unit: '', label: 'Sustain' },
release: { type: 'knob', min: 0.001, max: 8, default: 0.5, unit: 's', label: 'Release' },
},
});
// ==================== AMPLIFIER ====================
defineModule('vca', {
name: 'VCA',
icon: '△',
category: 'Utility',
inputs: [
{ name: 'in', type: PORT_TYPE.AUDIO, label: 'In' },
{ name: 'cv', type: PORT_TYPE.CONTROL, label: 'CV' },
],
outputs: [
{ name: 'out', type: PORT_TYPE.AUDIO, label: 'Out' },
],
params: {
gain: { type: 'knob', min: 0, max: 1, default: 0.8, unit: '', label: 'Gain' },
},
});
// ==================== EFFECTS ====================
defineModule('delay', {
name: 'Delay',
icon: '⟫',
category: 'Effect',
inputs: [
{ name: 'in', type: PORT_TYPE.AUDIO, label: 'In' },
],
outputs: [
{ name: 'out', type: PORT_TYPE.AUDIO, label: 'Out' },
],
params: {
delayTime: { type: 'knob', min: 0.01, max: 2, default: 0.3, unit: 's', label: 'Time' },
feedback: { type: 'knob', min: 0, max: 0.95, default: 0.4, unit: '', label: 'Feedbk' },
wet: { type: 'knob', min: 0, max: 1, default: 0.5, unit: '', label: 'Mix' },
},
});
defineModule('reverb', {
name: 'Reverb',
icon: '◌',
category: 'Effect',
inputs: [
{ name: 'in', type: PORT_TYPE.AUDIO, label: 'In' },
],
outputs: [
{ name: 'out', type: PORT_TYPE.AUDIO, label: 'Out' },
],
params: {
decay: { type: 'knob', min: 0.1, max: 15, default: 3, unit: 's', label: 'Decay' },
wet: { type: 'knob', min: 0, max: 1, default: 0.4, unit: '', label: 'Mix' },
},
});
defineModule('distortion', {
name: 'Distortion',
icon: '⚡',
category: 'Effect',
inputs: [
{ name: 'in', type: PORT_TYPE.AUDIO, label: 'In' },
],
outputs: [
{ name: 'out', type: PORT_TYPE.AUDIO, label: 'Out' },
],
params: {
distortion: { type: 'knob', min: 0, max: 1, default: 0.4, unit: '', label: 'Drive' },
wet: { type: 'knob', min: 0, max: 1, default: 0.5, unit: '', label: 'Mix' },
},
});
// ==================== MIXER ====================
defineModule('mixer', {
name: 'Mixer',
icon: '≡',
category: 'Utility',
inputs: [
{ name: 'in1', type: PORT_TYPE.AUDIO, label: 'In 1' },
{ name: 'in2', type: PORT_TYPE.AUDIO, label: 'In 2' },
{ name: 'in3', type: PORT_TYPE.AUDIO, label: 'In 3' },
{ name: 'in4', type: PORT_TYPE.AUDIO, label: 'In 4' },
],
outputs: [
{ name: 'out', type: PORT_TYPE.AUDIO, label: 'Out' },
],
params: {
gain1: { type: 'knob', min: 0, max: 1, default: 0.8, unit: '', label: 'Ch 1' },
gain2: { type: 'knob', min: 0, max: 1, default: 0.8, unit: '', label: 'Ch 2' },
gain3: { type: 'knob', min: 0, max: 1, default: 0.8, unit: '', label: 'Ch 3' },
gain4: { type: 'knob', min: 0, max: 1, default: 0.8, unit: '', label: 'Ch 4' },
},
});
// ==================== SCOPE ====================
defineModule('scope', {
name: 'Scope',
icon: '📊',
category: 'Utility',
inputs: [
{ name: 'in', type: PORT_TYPE.AUDIO, label: 'In' },
],
outputs: [],
params: {},
});
// ==================== CV TO GATE ====================
defineModule('cv2gate', {
name: 'CV→Gate',
icon: '⚡',
category: 'Utility',
inputs: [
{ name: 'in', type: PORT_TYPE.CONTROL, label: 'CV In' },
],
outputs: [
{ name: 'gate', type: PORT_TYPE.TRIGGER, label: 'Gate' },
],
params: {
threshold: { type: 'knob', min: 0, max: 1, default: 0.5, unit: '', label: 'Thresh' },
},
});
// ==================== OUTPUT ====================
defineModule('output', {
name: 'Output',
icon: '🔊',
category: 'Output',
inputs: [
{ name: 'left', type: PORT_TYPE.AUDIO, label: 'Left' },
{ name: 'right', type: PORT_TYPE.AUDIO, label: 'Right' },
],
outputs: [],
params: {
volume: { type: 'knob', min: -60, max: 6, default: -6, unit: 'dB', label: 'Volume' },
},
});
// ==================== KEYBOARD ====================
defineModule('keyboard', {
name: 'Keyboard',
icon: '🎹',
category: 'Source',
inputs: [],
outputs: [
{ name: 'freq', type: PORT_TYPE.AUDIO, label: 'Freq' },
{ name: 'gate', type: PORT_TYPE.TRIGGER, label: 'Gate' },
],
params: {
octave: { type: 'knob', min: 1, max: 8, default: 4, unit: '', label: 'Octave' },
},
});
// ==================== DRUM PAD ====================
defineModule('drumpad', {
name: 'Drum Pad',
icon: '🥁',
category: 'Source',
inputs: [],
outputs: [
{ name: 'freq', type: PORT_TYPE.AUDIO, label: 'Freq' },
{ name: 'gate', type: PORT_TYPE.TRIGGER, label: 'Gate' },
],
params: {},
});
// ==================== SEQUENCER ====================
defineModule('sequencer', {
name: 'Sequencer',
icon: '▦',
category: 'Source',
inputs: [],
outputs: [
{ name: 'freq', type: PORT_TYPE.AUDIO, label: 'Freq' },
{ name: 'gate', type: PORT_TYPE.TRIGGER, label: 'Gate' },
],
params: {
bpm: { type: 'knob', min: 40, max: 300, default: 140, unit: 'bpm', label: 'BPM' },
steps: { type: 'select', options: ['8', '16', '32'], default: '16', label: 'Steps' },
swing: { type: 'knob', min: 0, max: 0.5, default: 0, unit: '', label: 'Swing' },
},
// Custom data: step notes/gates stored in module.params._steps
});
// ==================== PIANO ROLL ====================
defineModule('pianoroll', {
name: 'Piano Roll',
icon: '🎼',
category: 'Source',
inputs: [],
outputs: [
{ name: 'freq', type: PORT_TYPE.AUDIO, label: 'Freq' },
{ name: 'gate', type: PORT_TYPE.TRIGGER, label: 'Gate' },
],
params: {
bpm: { type: 'knob', min: 40, max: 300, default: 140, unit: 'bpm', label: 'BPM' },
loop: { type: 'select', options: ['on', 'off'], default: 'on', label: 'Loop' },
bars: { type: 'select', options: ['1', '2', '4', '8'], default: '4', label: 'Bars' },
},
// Custom data: notes stored in module.params._notes = [{note, start, duration}, ...]
});