summaryrefslogtreecommitdiff
path: root/tools/mq_editor/editor.js
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-18 11:05:37 +0100
committerskal <pascal.massimino@gmail.com>2026-02-18 11:05:37 +0100
commit890f4fdf96945832d5da078cb795266127cf122d (patch)
treeecfc93c2ad41df0787aa471d8f0be4991160f683 /tools/mq_editor/editor.js
parentf8f664964594a341884b2e9947f64feea4b925a6 (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.js55
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;