tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

limits-ui.js (4415B)


      1 import { defaultLimits, executionSettings } from './execution.js';
      2 const committed = new Map();
      3 export function readSettings(runner) {
      4     const settings = committed.get(runner);
      5     if (!settings)
      6         throw new Error('Execution settings are not ready');
      7     return { limits: { ...settings.limits }, uncapped: settings.uncapped };
      8 }
      9 for (const runner of ['package', 'compiler']) {
     10     const checkbox = document.getElementById(`${runner === 'package' ? '' : 'compiler-'}uncapped`);
     11     const summary = document.getElementById(`${runner}-limits-summary`);
     12     const container = document.getElementById(`${runner}-limits-defaults`);
     13     const defaults = defaultLimits(runner), fields = new Map();
     14     const form = document.createElement('form');
     15     form.noValidate = true;
     16     form.className = 'limits-editor';
     17     for (const [key, label] of [['nodes', 'Memory budget (live nodes)'], ['fuel', 'Work units (0 = no work limit)'], ['stack', 'Evaluation depth (frames)'], ['seconds', 'Active seconds (0 = unlimited)']]) {
     18         const wrapper = document.createElement('div'), caption = document.createElement('label'), input = document.createElement('input');
     19         const hint = document.createElement('span');
     20         hint.className = 'hint';
     21         input.id = `${runner}-limit-${key}`;
     22         input.type = 'number';
     23         input.min = key === 'seconds' || key === 'fuel' ? '0' : '1';
     24         input.step = '1';
     25         input.inputMode = 'numeric';
     26         input.required = true;
     27         input.value = String(defaults[key]);
     28         caption.htmlFor = input.id;
     29         caption.textContent = label;
     30         hint.textContent = `Default: ${defaults[key].toLocaleString()}`;
     31         wrapper.append(caption, input, hint);
     32         form.append(wrapper);
     33         fields.set(key, input);
     34     }
     35     const actions = document.createElement('div');
     36     actions.className = 'controls';
     37     const apply = document.createElement('button'), restore = document.createElement('button');
     38     apply.type = 'submit';
     39     apply.textContent = 'Apply limits';
     40     restore.type = 'button';
     41     restore.textContent = 'Restore defaults';
     42     const error = document.createElement('p');
     43     error.className = 'limits-error';
     44     error.setAttribute('role', 'status');
     45     actions.append(apply, restore);
     46     form.append(actions, error);
     47     container.replaceWith(form);
     48     committed.set(runner, executionSettings(defaults, runner, true));
     49     function update() {
     50         const settings = readSettings(runner);
     51         const customized = Object.entries(defaults).some(([key, value]) => settings.limits[key] !== value);
     52         summary.textContent = `Optional limits${settings.uncapped ? ' (off)' : customized ? ' (custom)' : ' (on)'}`;
     53         for (const input of fields.values())
     54             input.disabled = settings.uncapped;
     55         apply.disabled = settings.uncapped;
     56         document.getElementById(`${runner}-resource-mode`).textContent = settings.uncapped ? 'Off' : 'On';
     57     }
     58     function publish(settings) {
     59         committed.set(runner, settings);
     60         for (const [key, input] of fields)
     61             input.value = String(settings.limits[key]);
     62         error.textContent = '';
     63         update();
     64         window.dispatchEvent(new CustomEvent('limits-change', { detail: runner }));
     65     }
     66     form.oninput = () => { error.textContent = ''; summary.textContent = 'Advanced: unapplied resource settings'; };
     67     form.onsubmit = event => {
     68         event.preventDefault();
     69         try {
     70             const values = {};
     71             for (const [key, field] of fields) {
     72                 if (!field.value.trim())
     73                     throw new Error(`${key}: enter a whole number`);
     74                 values[key] = Number(field.value);
     75             }
     76             publish(executionSettings(values, runner, checkbox.checked));
     77         }
     78         catch (cause) {
     79             error.textContent = cause instanceof Error ? cause.message : String(cause);
     80         }
     81     };
     82     checkbox.onchange = () => {
     83         const settings = readSettings(runner);
     84         settings.uncapped = checkbox.checked;
     85         // Bypass uses the last applied finite values, not a partial/invalid draft.
     86         publish(settings);
     87     };
     88     restore.onclick = () => {
     89         for (const [key, input] of fields)
     90             input.value = String(defaults[key]);
     91         checkbox.checked = true;
     92         publish(executionSettings(defaults, runner, true));
     93     };
     94     update();
     95 }