output-app.js (2870B)
1 // One output surface. The saved payload carries its own provenance/format; 2 // changing controls cannot relabel an earlier result or change its download. 3 export function createOutput() { 4 const text = document.getElementById('result'); 5 const provenance = document.getElementById('output-provenance'); 6 const copy = document.getElementById('copy-output'); 7 const download = document.getElementById('download-output'); 8 let saved, busy = false; 9 const controls = (working) => { busy = working; copy.disabled = download.disabled = busy || !saved; }; 10 function clear() { saved = undefined; text.textContent = ''; text.className = ''; provenance.textContent = 'Evaluate an expression, or show a stored definition. Nothing runs automatically.'; controls(busy); } 11 function show(value, filename, description) { 12 saved = { text: value, filename }; 13 text.textContent = value.slice(0, 16_384); 14 text.className = ''; 15 provenance.textContent = description + (value.length > 16_384 ? ' · Preview: first 16,384 characters. Copy and Download include the complete output.' : ' · Complete output.'); 16 controls(busy); 17 } 18 function file(value, name, description) { 19 show(value.text, `${name.replace(/[^a-zA-Z0-9_-]/g, '-').slice(0, 64)}.${value.extension}`, `${description} · ${value.bytes.toLocaleString()} UTF-8 bytes`); 20 } 21 function result(value, description) { 22 if ('file' in value) { 23 file(value.file, 'result', `${description} · ${value.type}`); 24 return; 25 } 26 if (value.type === 'tree' || value.type === 'list') 27 throw new Error('Select tricu source or DAG to inspect tree results'); 28 const content = typeof value.value === 'string' ? value.value : JSON.stringify(value.value, null, 2); 29 show(content, value.type === 'record' || value.type === 'bytes' ? 'result.json' : 'result.txt', `${description} · ${value.type}`); 30 } 31 copy.onclick = async () => { 32 const value = saved; 33 if (!value) 34 return; 35 try { 36 await navigator.clipboard.writeText(value.text); 37 if (saved === value) 38 provenance.textContent += ' · Copied.'; 39 } 40 catch { 41 if (saved === value) 42 provenance.textContent += ' · Clipboard unavailable; use Download.'; 43 } 44 }; 45 download.onclick = () => { 46 if (!saved) 47 return; 48 const url = URL.createObjectURL(new Blob([saved.text], { type: 'text/plain;charset=utf-8' })); 49 const a = document.createElement('a'); 50 a.href = url; 51 a.download = saved.filename; 52 document.body.append(a); 53 a.click(); 54 a.remove(); 55 setTimeout(() => URL.revokeObjectURL(url), 1000); 56 }; 57 clear(); 58 return { clear, controls, file, result }; 59 }