SOURCE / PINNED RELEASE
Made of little things.
Body Journal
- Release
- e8049f8da4e2…
- Author-recorded commit
- 61c04bf0e8aa…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-6c4554844c7
Archive hash verified: 4369d778da046a8b…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import { escape as esc } from './case.js';
import type { Diagnostic } from './ai-diagnostics.js';
const labels: Record<Diagnostic['stage'],string> = {checking:'Checking bridge',uploading:'Preparing image transfer',queued:'Request accepted by bridge',requesting:'Waiting for provider',receiving:'Receiving provider output',validating:'Validating report',complete:'Report accepted',failed:'Request failed',stopped:'Stopped waiting'};
export function diagnosticMarkup(d: Diagnostic): string {
const detail = (label: string, value: string, key: string) => `<details data-diagnostic-detail="${key}"><summary>${label}</summary><pre data-napplet-select="text">${esc(value)}</pre></details>`;
return `<details class="ai-diagnostics" data-diagnostic-detail="root" ${d.stage==='failed'?'open':''}><summary>Request diagnostics · ${labels[d.stage]}</summary><p role="status">${labels[d.stage]} · ${(d.elapsedMs/1000).toFixed(1)} s at last update</p><p class="field-hint">Unvalidated provider output, not medical advice. Live updates arrive through the bridge. This panel is kept only for this session and may contain health information.</p><dl>${[['Request',d.requestId],['Model',d.model],['HTTP',d.httpStatus],['Provider response',d.responseId],['Finish reason',d.finishReason]].filter(([,v])=>v!==undefined).map(([k,v])=>`<dt>${k}</dt><dd>${esc(String(v))}</dd>`).join('')}</dl>${d.formatRecovery?`<p class="field-hint" data-format-recovery><strong>JSON formatting recovered.</strong> ${esc(d.formatRecovery)}</p>`:''}${d.evidenceNotice?`<p class="field-hint" data-evidence-notice><strong>Photo evidence note.</strong> ${esc(d.evidenceNotice)}</p>`:''}${d.usage?`<p class="small">Tokens: input ${d.usage.input??'—'} · output ${d.usage.output??'—'} · total ${d.usage.total??'—'} · reasoning ${d.usage.reasoning??'—'}</p>`:''}${d.issue?`<p class="inline-error"><strong>${esc(d.issue.path)}</strong>: ${esc(d.issue.message)}</p>`:''}<ol class="diagnostic-events">${d.events.map(e=>`<li>${(e.elapsedMs/1000).toFixed(1)} s · ${labels[e.stage]}</li>`).join('')}</ol>${detail('Raw response · assistant content',d.output||'No assistant content received yet.','output')}${detail('Provider reasoning / thinking',d.reasoning||'No reasoning text was exposed by this provider. This app does not infer or fabricate it.','reasoning')}${d.truncated?'<p class="field-hint">Diagnostic text reached its display limit; only the retained portion is shown.</p>':''}<button class="text-button" data-ai="clear-diagnostics">Clear diagnostics</button></details>`;
}
export function updateDiagnosticView(d?: Diagnostic): void {
const root=document.querySelector('#aiDiagnostics');if(!root)return;
const open=new Map([...root.querySelectorAll<HTMLDetailsElement>('details')].map(e=>[e.dataset.diagnosticDetail,e.open]));
const scroll=new Map([...root.querySelectorAll<HTMLElement>('pre')].map(e=>[e.parentElement?.dataset.diagnosticDetail,[e.scrollTop,e.scrollLeft]]));
root.innerHTML=d?diagnosticMarkup(d):'';
root.querySelectorAll<HTMLDetailsElement>('details').forEach(e=>{if(open.has(e.dataset.diagnosticDetail))e.open=open.get(e.dataset.diagnosticDetail)!;});
root.querySelectorAll<HTMLElement>('pre').forEach(e=>{const pos=scroll.get(e.parentElement?.dataset.diagnosticDetail);if(pos){e.scrollTop=pos[0];e.scrollLeft=pos[1];}});
}
