// panes/lang.js — language pipelines with cards, examples, run history. import { registerPane } from '../app.js'; import { jget, jpost, escapeHtml } from '../lib/api.js'; import { fmtJSON, err, ok, copyToClipboard, historyStore } from '../lib/ui.js'; const runHist = historyStore('cx_lang_runs', 20); const TPL = `
Lang
Run language pipelines via the sidecar.
loading…

Select a pipeline

Pick a pipeline on the left.

`; let pipelines = []; let current = null; function renderList(host) { const q = host.querySelector('#lang-search').value.trim().toLowerCase(); const items = pipelines.filter(p => !q || (p.name + ' ' + (p.description || '')).toLowerCase().includes(q)); host.querySelector('#lang-pipelines').innerHTML = items.length ? items.map(p => ``).join('') : '
no pipelines
'; host.querySelectorAll('[data-p]').forEach(b => b.addEventListener('click', () => select(host, b.dataset.p))); } function renderHistory(host) { const list = runHist.list().filter(r => !current || r.name === current.name); host.querySelector('#lang-history').innerHTML = list.length ? list.map((r, i) => ``).join('') : '
no runs
'; host.querySelectorAll('#lang-history [data-hi]').forEach(b => b.addEventListener('click', () => { const r = list[+b.dataset.hi]; if (r) { host.querySelector('#lang-input').value = fmtJSON(r.input); host.querySelector('#lang-result').textContent = fmtJSON(r.output); } })); } function select(host, name) { current = pipelines.find(p => p.name === name); if (!current) return; host.querySelector('#lang-name').textContent = current.name; host.querySelector('#lang-desc').textContent = current.description || '—'; host.querySelector('#lang-body').hidden = false; const ex = current.examples || current.sample_inputs || []; host.querySelector('#lang-examples').innerHTML = ex.length ? ex.map((e, i) => ``).join('') : 'no examples'; host.querySelectorAll('#lang-examples .chip').forEach(b => b.addEventListener('click', () => { const e = ex[+b.dataset.ex]; host.querySelector('#lang-input').value = fmtJSON(e.input ?? e.value ?? e); })); host.querySelector('#lang-input').value = fmtJSON(current.default_input || {}); renderHistory(host); } async function refresh(host) { try { const r = await jget('/api/lang/pipelines'); pipelines = r.pipelines || []; renderList(host); host.querySelector('#lang-pill').classList.remove('err', 'warn'); host.querySelector('#lang-pill').classList.add('ok'); host.querySelector('#lang-pill-text').textContent = `${pipelines.length} pipelines`; } catch (e) { host.querySelector('#lang-pipelines').innerHTML = `
${escapeHtml(e.message)}
`; host.querySelector('#lang-pill').classList.add('err'); host.querySelector('#lang-pill-text').textContent = 'down'; } } registerPane('lang', { label: 'Lang', init(host) { host.innerHTML = TPL; host.querySelector('#lang-refresh').addEventListener('click', () => refresh(host)); host.querySelector('#lang-search').addEventListener('input', () => renderList(host)); host.querySelector('#lang-run').addEventListener('click', async () => { if (!current) return err('select a pipeline'); let input; try { input = JSON.parse(host.querySelector('#lang-input').value || '{}'); } catch (e) { return err(`bad JSON: ${e.message}`); } host.querySelector('#lang-status').textContent = 'running…'; const t0 = performance.now(); try { const r = await jpost(`/api/lang/pipelines/${encodeURIComponent(current.name)}`, { input }); const ms = +r.duration_ms || (performance.now() - t0).toFixed(0); host.querySelector('#lang-result').textContent = fmtJSON(r); host.querySelector('#lang-status').textContent = `ok · ${ms}ms`; host.querySelector('#lang-meta').textContent = `${ms}ms`; runHist.push({ name: current.name, input, output: r, ms, ok: true }); renderHistory(host); ok('done'); } catch (e) { host.querySelector('#lang-result').textContent = fmtJSON(e.body ?? { error: e.message }); host.querySelector('#lang-status').textContent = `error ${e.status ?? ''}`; runHist.push({ name: current.name, input, error: e.message, ok: false }); renderHistory(host); err(e.message); } }); host.querySelector('#lang-format').addEventListener('click', () => { const ta = host.querySelector('#lang-input'); try { ta.value = fmtJSON(JSON.parse(ta.value || '{}')); } catch (e) { err('bad JSON: ' + e.message); } }); host.querySelector('#lang-copy-input').addEventListener('click', () => copyToClipboard(host.querySelector('#lang-input').value)); host.querySelector('#lang-copy-out').addEventListener('click', () => copyToClipboard(host.querySelector('#lang-result').textContent)); host.querySelector('#lang-health').addEventListener('click', async () => { try { host.querySelector('#lang-result').textContent = fmtJSON(await jget('/api/lang/healthz')); } catch (e) { host.querySelector('#lang-result').textContent = e.message; } }); refresh(host); }, refresh, });