<!-- SS · part 33 -->
('#es-kt-chs').innerHTML = D.samples.map(function (s) { return '<option value="' + s.id + '">' + esc(s.name) + '</option>'; }).join('');
    $('#es-kt-stem').innerHTML = '<option value="">From a take/stem…</option>' + (window.ESREC ? ESREC.list() : []).map(function (s) { return '<option value="' + s.id + '">' + esc(s.name) + '</option>'; }).join('');
  }
  $('#es-kt-lib').addEventListener('change', function (e) { if (!e.target.hasAttribute('data-sn')) return; var s = sample(e.target.closest('[data-s]').getAttribute('data-s')); s.name = e.target.value.slice(0, 60) || s.name; persist({ samples: [{ id: s.id, name: s.name }] }); renderPads(); });
  $('#es-kt-lib').addEventListener('click', function (e) {
    var a = e.target.getAttribute('data-sa'); if (!a) return; var id = e.target.closest('[data-s]').getAttribute('data-s');
    if (a === 'play') ES.unlock().then(function () { return loadBuf(id); }).then(function (b) { var c = Tone.getContext().rawContext, s = c.createBufferSource(); s.buffer = b; s.connect(c.destination); s.start(); });
    if (a === 'del') { if (!confirm('Delete this sample from the server? Kits using it lose that pad.')) return; ES.api('POST', '/api/ss/kits/sample/' + id + '/delete', {}).then(function () { return refresh(); }).then(function () { say('Sample deleted.'); }).catch(function (x) { say(x.message); }); }
  });
  function upload(blob, name, source, kind) { var fd = new FormData(); fd.append('name', name); fd.append('source', source); fd.append('kind', kind || 'oneshot'); fd.append('audio', blob, name.replace(/[^\w\-]+/g, '_') + (/wav/.test(blob.type) ? '.wav' : /mp4|m4a/.test(blob.type) ? '.m4a' : /mpeg|mp3/.test(blob.type) ? '.mp3' : '.webm')); return ES.api('POST', '/api/ss/kits/sample', fd); }
  $('#es-kt-file').addEventListener('change', function () {
    var files = [].slice.call(this.files || []); this.value = ''; if (!files.length) return;
    var bad = files.filter(function (f) { return f.size > 25 * 1048576; }); if (bad.length) return say(bad[0].name + ' is over 25 MB.');
    say('Uploading ' + files.length + ' file(s)…');
    files.reduce(function (p, f) { return p.then(function () { return upload(f, f.name.replace(/\.[^.]+$/, '').slice(0, 60), 'upload'); }); }, Promise.resolve()).then(refresh).then(function () { say('Uploaded. Assign samples to pads above.'); }).catch(function (x) { say(x.message); });
  });
  $('#es-kt-stem').addEventListener('change', function () { var id = this.value; this.value = ''; var s = (ESREC.list() || []).filter(function (x) { return x.id === id; })[0]; if (!s) return; say('Uploading "' + s.name + '"…'); upload(s.blob, s.name, s.kind === 'mic' ? 'recording' : 'upload', 'loop').then(refresh).then(function () { say('Added "' + s.name + '" to your samples.'); }).catch(function (x) { say(x.message); }); });
  var mr = null, chunks = [];
  $('#es-kt-rec').addEventListener('click', function () {
    var b = this;
    if (mr && mr.state === 'recording') { mr.stop(); return; }
    if (!navigator.mediaDevices || typeof MediaRecorder === 'undefined') return say('This browser cannot record.');
    navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: false, noiseSuppression: false, autoGainControl: false } }).then(function (st) {
      chunks = []; mr = new MediaRecorder(st); mr.ondataavailable = function (e) { if (e.data.size) chunks.push(e.data); };
      mr.onstop = function () { st.getTracks().forEach(function (t) { t.stop(); }); b.textContent = '● Record sample'; b.classList.remove('is-on'); var blob = new Blob(chunks, { type: mr.mimeType || 'audio/webm' }); if (blob.size < 1000) return say('Nothing recorded.'); var n = prompt('Name this sample', 'My sound ' + (D.samples.length + 1)); if (n === null) return say('Discarded.'); say('Uploading…'); upload(blob, (n || 'My sound').slice(0, 60), 'recording').then(refresh).then(function () { say('Recorded sample saved. Trim it with Start/End on a pad.'); }).catch(function (x) { say(x.message); }); };
      mr.start(); b.textContent = '■ Stop'; b.classList.add('is-on'); say('Recording… press Stop.');
    }).catch(function () { say('Mic permission was denied.'); });
  });
  // kits
  function renderKits() { $('#es-kt-kit').innerHTML = D.kits.length ? D.kits.map(function (k) { return '<option value="' + esc(k.id) + '"' + (k.id === K.kit ? ' selected' : '') + '>' + esc(k.name) + '</option>'; }).join('') : '<option value="">No kits yet</option>'; if (!kit() && D.kits[0]) { K.kit = D.kits[0].id; keep(); } }
  $('#es-kt-kit').addEventListener('change', function () { K.kit = this.value; keep(); renderPads(); loadKitBufs().then(rewire); renderProv(); });
  $('#es-kt-new').addEventListener('click', function () { var n = prompt('Kit name', 'My Kit ' + (D.kits.length + 1)); if (!n) return; var k = { id: Date.now().toString(36), name: n.slice(0, 60), pads: {} }; D.kits.push(k); K.kit = k.id; keep(); renderKits(); renderPads(); persist({ kits: D.kits }); });
  $('#es-kt-ren').addEventL