diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-18 11:05:37 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-18 11:05:37 +0100 |
| commit | 890f4fdf96945832d5da078cb795266127cf122d (patch) | |
| tree | ecfc93c2ad41df0787aa471d8f0be4991160f683 /tools/mq_editor/editor.js | |
| parent | f8f664964594a341884b2e9947f64feea4b925a6 (diff) | |
feat(mq_editor): jog sliders for synth params, reset partials on WAV load, panel refresh after extract
- Clear extractedPartials and editor state when loading a new WAV
- After extract, refresh right panels (re-select if index still valid)
- Synth fields (decay, jitter, spread) get jog sliders: drag to nudge, spring-back on release
- Spread extension limit dashed line: alpha 0.4→0.75, lineWidth 1→1.5, dash [3,4]→[4,3]
handoff(Gemini): mq_editor UX polish — jog sliders, WAV reset, panel refresh, spread line visibility
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'tools/mq_editor/editor.js')
| -rw-r--r-- | tools/mq_editor/editor.js | 55 |
1 files changed, 54 insertions, 1 deletions
diff --git a/tools/mq_editor/editor.js b/tools/mq_editor/editor.js index 9cfcdf1..b767534 100644 --- a/tools/mq_editor/editor.js +++ b/tools/mq_editor/editor.js @@ -146,8 +146,15 @@ class PartialEditor { if (this.viewer) this.viewer.render(); }); inputs[p.key] = inp; + + const jog = this._makeJogSlider(inp, partial, index, p, defaults); + const wrap = document.createElement('div'); + wrap.className = 'synth-field-wrap'; + wrap.appendChild(inp); + wrap.appendChild(jog); + grid.appendChild(lbl); - grid.appendChild(inp); + grid.appendChild(wrap); } // Auto-detect spread button @@ -173,6 +180,52 @@ class PartialEditor { grid.appendChild(autoBtn); } + _makeJogSlider(inp, partial, index, p, defaults) { + const slider = document.createElement('div'); + slider.className = 'jog-slider'; + const thumb = document.createElement('div'); + thumb.className = 'jog-thumb'; + slider.appendChild(thumb); + + const sensitivity = parseFloat(p.step) * 5; + let startX = 0, startVal = 0, dragging = false; + + const onMove = (e) => { + if (!dragging) return; + const dx = e.clientX - startX; + const half = slider.offsetWidth / 2; + const clamped = Math.max(-half, Math.min(half, dx)); + thumb.style.transition = 'none'; + thumb.style.left = `calc(50% - 3px + ${clamped}px)`; + const newVal = Math.max(0, startVal + dx * sensitivity); + inp.value = newVal.toFixed(3); + if (!this.partials || !this.partials[index]) return; + if (!this.partials[index].replicas) this.partials[index].replicas = { ...defaults }; + this.partials[index].replicas[p.key] = newVal; + if (this.viewer) this.viewer.render(); + }; + + const onUp = () => { + if (!dragging) return; + dragging = false; + thumb.style.transition = ''; + thumb.style.left = 'calc(50% - 3px)'; + document.removeEventListener('mousemove', onMove); + document.removeEventListener('mouseup', onUp); + }; + + slider.addEventListener('mousedown', (e) => { + dragging = true; + startX = e.clientX; + startVal = Math.max(0, parseFloat(inp.value) || 0); + document.addEventListener('mousemove', onMove); + document.addEventListener('mouseup', onUp); + e.preventDefault(); + }); + + return slider; + } + _makeCurveUpdater(partialIndex, curveKey, field, pointIndex) { return (e) => { if (!this.partials) return; |
