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