tricu

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

limits-ui.ts (4286B)


      1 import { defaultLimits, executionSettings, type ExecutionSettings, type Runner } from './execution.js';
      2 const committed = new Map<Runner, ExecutionSettings>();
      3 export function readSettings(runner: Runner): ExecutionSettings {
      4   const settings = committed.get(runner);
      5   if (!settings) throw new Error('Execution settings are not ready');
      6   return { limits: { ...settings.limits }, uncapped: settings.uncapped };
      7 }
      8 for (const runner of ['package', 'compiler'] as const) {
      9   const checkbox = document.getElementById(`${runner === 'package' ? '' : 'compiler-'}uncapped`) as HTMLInputElement;
     10   const summary = document.getElementById(`${runner}-limits-summary`)!;
     11   const container = document.getElementById(`${runner}-limits-defaults`)!;
     12   const defaults = defaultLimits(runner), fields = new Map<string, HTMLInputElement>();
     13   const form = document.createElement('form'); form.noValidate = true; form.className = 'limits-editor';
     14   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)']] as const) {
     15     const wrapper = document.createElement('div'), caption = document.createElement('label'), input = document.createElement('input');
     16     const hint = document.createElement('span'); hint.className = 'hint';
     17     input.id = `${runner}-limit-${key}`; input.type = 'number'; input.min = key === 'seconds' || key === 'fuel' ? '0' : '1'; input.step = '1';
     18     input.inputMode = 'numeric'; input.required = true; input.value = String(defaults[key]);
     19     caption.htmlFor = input.id; caption.textContent = label;
     20     hint.textContent = `Default: ${defaults[key].toLocaleString()}`;
     21     wrapper.append(caption, input, hint); form.append(wrapper); fields.set(key, input);
     22   }
     23   const actions = document.createElement('div'); actions.className = 'controls';
     24   const apply = document.createElement('button'), restore = document.createElement('button');
     25   apply.type = 'submit'; apply.textContent = 'Apply limits'; restore.type = 'button'; restore.textContent = 'Restore defaults';
     26   const error = document.createElement('p'); error.className = 'limits-error'; error.setAttribute('role', 'status');
     27   actions.append(apply, restore); form.append(actions, error); container.replaceWith(form);
     28   committed.set(runner, executionSettings(defaults, runner, true));
     29   function update(): void {
     30     const settings = readSettings(runner);
     31     const customized = Object.entries(defaults).some(([key, value]) => settings.limits[key as keyof typeof defaults] !== value);
     32     summary.textContent = `Optional limits${settings.uncapped ? ' (off)' : customized ? ' (custom)' : ' (on)'}`;
     33     for (const input of fields.values()) input.disabled = settings.uncapped;
     34     apply.disabled = settings.uncapped;
     35     document.getElementById(`${runner}-resource-mode`)!.textContent = settings.uncapped ? 'Off' : 'On';
     36   }
     37   function publish(settings: ExecutionSettings): void {
     38     committed.set(runner, settings);
     39     for (const [key, input] of fields) input.value = String(settings.limits[key as keyof typeof settings.limits]);
     40     error.textContent = ''; update();
     41     window.dispatchEvent(new CustomEvent('limits-change', { detail: runner }));
     42   }
     43   form.oninput = () => { error.textContent = ''; summary.textContent = 'Advanced: unapplied resource settings'; };
     44   form.onsubmit = event => {
     45     event.preventDefault();
     46     try {
     47       const values: Record<string, number> = {};
     48       for (const [key, field] of fields) {
     49         if (!field.value.trim()) throw new Error(`${key}: enter a whole number`);
     50         values[key] = Number(field.value);
     51       }
     52       publish(executionSettings(values, runner, checkbox.checked));
     53     } catch (cause) { error.textContent = cause instanceof Error ? cause.message : String(cause); }
     54   };
     55   checkbox.onchange = () => {
     56     const settings = readSettings(runner); settings.uncapped = checkbox.checked;
     57     // Bypass uses the last applied finite values, not a partial/invalid draft.
     58     publish(settings);
     59   };
     60   restore.onclick = () => {
     61     for (const [key, input] of fields) input.value = String(defaults[key as keyof typeof defaults]);
     62     checkbox.checked = true; publish(executionSettings(defaults, runner, true));
     63   };
     64   update();
     65 }