minutes-core 0.27.0

Core library for minutes — audio capture, transcription, and meeting memory
(() => {
  const instance = crypto.randomUUID();
  const ids = new WeakMap();
  let next = 0, revision = 0, signature = '', undo = [];
  const text = value => String(value || '').trim().slice(0, 180);
  function elements() {
    return [...document.querySelectorAll('input[type="range"],input[type="number"],input[type="checkbox"],select')]
      .filter(el => !el.disabled && !el.readOnly && el.getClientRects().length && getComputedStyle(el).visibility !== 'hidden')
      .slice(0, 64);
  }
  function value(el) { return el.type === 'checkbox' ? String(el.checked) : el.value; }
  function controls() {
    return elements().map(el => {
      if (!ids.has(el)) ids.set(el, `control-${++next}`);
      const labelled = (el.getAttribute('aria-labelledby') || '').split(/\s+/).map(id => document.getElementById(id)?.textContent || '').join(' ');
      return { control_id: ids.get(el), label: text(el.getAttribute('aria-label') || labelled || [...el.labels || []].map(l => l.textContent).join(' ') || el.name || el.id),
        type: el.tagName === 'SELECT' ? 'select' : el.type, value: value(el), min: el.min || '', max: el.max || '', step: el.step || '',
        options: el.tagName === 'SELECT' ? [...el.options].slice(0, 64).map(o => ({ value: o.value, label: text(o.label), disabled: o.disabled })) : undefined };
    });
  }
  function state() {
    const list = controls();
    const output = [...document.querySelectorAll('output,[role="status"],[aria-live="polite"]')].slice(0, 12).map(el => text(el.textContent)).join('\n');
    const sig = JSON.stringify([list, output]);
    if (sig !== signature) { signature = sig; revision++; }
    return { snapshot_id: `${instance}:${revision}`, controls: list, output, undo_id: undo.at(-1)?.id || null };
  }
  function page(result, command) {
    if (!result.controls) return result;
    const offset = key => Number.isSafeInteger(command[key]) && command[key] >= 0 ? command[key] : 0;
    const focused = command.operation === 'inspect' ? command.control_id : null;
    const all = focused ? result.controls.filter(c => c.control_id === focused) : result.controls;
    const changedIndex = Math.max(0, all.findIndex(c => c.control_id === result.changed?.control_id));
    const start = focused ? 0 : command.operation === 'inspect' ? offset('control_offset') : Math.floor(changedIndex / 4) * 4;
    result.total_controls = all.length;
    result.control_offset = start;
    result.controls = all.slice(start, start + 4).map(control => {
      if (!control.options) return control;
      const optionStart = offset('option_offset');
      return { ...control, total_options: control.options.length, option_offset: optionStart,
        options: control.options.slice(optionStart, optionStart + 4) };
    });
    return result;
  }
  function validate(el, raw) {
    if (typeof raw !== 'string' || raw.length > 256) throw Error('Use the exact string value for this control');
    if (el.tagName === 'SELECT') {
      if (el.multiple || ![...el.options].some(o => !o.disabled && o.value === raw)) throw Error('Unsupported or unavailable option');
      return raw;
    }
    if (el.type === 'checkbox') {
      if (!['true', 'false'].includes(raw)) throw Error('Checkbox value must be true or false');
      return raw;
    }
    if (!raw.trim() || !Number.isFinite(Number(raw))) throw Error('Value must be a finite number');
    const n = Number(raw), min = el.min === '' ? (el.type === 'range' ? 0 : -Infinity) : Number(el.min);
    const max = el.max === '' ? (el.type === 'range' ? 100 : Infinity) : Number(el.max);
    if (n < min || n > max) throw Error('Value is outside this control range');
    const step = el.step === 'any' ? 0 : Number(el.step || 1);
    const base = el.min !== '' ? Number(el.min) : Number(el.getAttribute('value') || 0);
    if (step && Math.abs((n - base) / step - Math.round((n - base) / step)) > 1e-7) throw Error('Value does not match this control step');
    return String(n);
  }
  async function write(el, raw) {
    const property = el.type === 'checkbox' ? 'checked' : 'value';
    const prototype = el.tagName === 'SELECT' ? HTMLSelectElement.prototype : HTMLInputElement.prototype;
    Object.getOwnPropertyDescriptor(prototype, property).set.call(el, property === 'checked' ? raw === 'true' : raw);
    el.dispatchEvent(new Event('input', { bubbles: true }));
    el.dispatchEvent(new Event('change', { bubbles: true }));
    await new Promise(resolve => setTimeout(resolve, 80));
    if (!el.isConnected || value(el) !== raw) throw Error('Control did not retain the requested value; inspect before retrying');
  }
  let busy = false;
  addEventListener('message', async event => {
    if (event.source !== parent || event.data?.kind !== 'minutes-control' || busy) return;
    busy = true;
    const { id, command } = event.data;
    let result;
    try {
      const before = state();
      if (command.operation === 'inspect') result = before;
      else {
        if (command.snapshot_id !== before.snapshot_id) throw Error('Preview changed; inspect again before editing');
        let controlId = command.control_id, raw = command.value, change;
        if (command.operation === 'undo') {
          change = undo.at(-1);
          if (!change || command.undo_id !== change.id || before.snapshot_id !== change.after) throw Error('Undo conflicts with a later edit');
          controlId = change.control; raw = change.before;
        } else if (command.operation !== 'set') throw Error('Unsupported command');
        const el = elements().find(el => ids.get(el) === controlId);
        if (!el) throw Error('Control is no longer available');
        const old = value(el), desired = validate(el, raw);
        await write(el, desired);
        result = state();
        if (change) undo.pop();
        else {
          undo.push({ id: crypto.randomUUID(), control: controlId, before: old, after: result.snapshot_id });
          if (undo.length > 32) undo.shift();
        }
        result.undo_id = undo.at(-1)?.id || null;
        result.changed = { control_id: controlId, before: old, after: value(el) };
      }
    } catch (error) { result = { error: String(error.message || error).slice(0, 240) }; }
    parent.postMessage({ kind: 'minutes-control-result', id, result: page(result, command) }, '*');
    busy = false;
  });
})();