fuzzyworld

nuts / files / src / debug.ts

debug.ts

2.7 KB · history · blame

1/**
2 * Tiny on-screen debug log + remote terminal forwarder.
3 * Call dbg('some message') from anywhere to show it in the overlay
4 * and forward it to the Vite dev server terminal as [web] prefix.
5 * Tap the overlay to dismiss.
6 *
7 * In production (import.meta.env.PROD) all calls are no-ops.
8 */
9
10const ENABLED = false // flip to true to re-enable on-screen log + remote forwarding
11
12let panel: HTMLDivElement | null = null
13const MAX_LINES = 20
14const lines: string[] = []
15
16function getPanel(): HTMLDivElement {
17 if (panel) return panel
18 panel = document.createElement('div')
19 Object.assign(panel.style, {
20 position: 'fixed',
21 bottom: '0',
22 left: '0',
23 right: '0',
24 maxHeight: '40vh',
25 overflowY: 'auto',
26 background: 'rgba(0,0,0,0.78)',
27 color: '#0f0',
28 fontFamily: 'monospace',
29 fontSize: '11px',
30 padding: '8px',
31 zIndex: '9999',
32 whiteSpace: 'pre-wrap',
33 wordBreak: 'break-all',
34 pointerEvents: 'auto',
35 })
36 panel.addEventListener('click', () => {
37 panel?.remove()
38 panel = null
39 lines.length = 0
40 })
41 document.body.appendChild(panel)
42 return panel
43}
44
45function ts(): string {
46 const d = new Date()
47 return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}:${d.getSeconds().toString().padStart(2, '0')}.${d.getMilliseconds().toString().padStart(3, '0')}`
48}
49
50function sendToServer(level: 'log' | 'error', msg: string): void {
51 // Best-effort: fire-and-forget, never throws
52 fetch('/__log', {
53 method: 'POST',
54 headers: { 'content-type': 'application/json' },
55 body: JSON.stringify({ level, msg }),
56 }).catch(() => { /* dev server may not be listening */ })
57}
58
59function display(msg: string, isError = false): void {
60 lines.push(msg)
61 if (lines.length > MAX_LINES) lines.shift()
62 const p = getPanel()
63 p.style.color = isError ? '#f55' : '#0f0'
64 p.textContent = lines.join('\n')
65 p.scrollTop = p.scrollHeight
66}
67
68export function dbg(...args: unknown[]): void {
69 if (import.meta.env.PROD || !ENABLED) return
70 const msg = `[${ts()}] ${args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ')}`
71 console.log(msg)
72 display(msg)
73 sendToServer('log', msg)
74}
75
76export function dbgError(...args: unknown[]): void {
77 if (import.meta.env.PROD || !ENABLED) return
78 const msg = `[${ts()}] ❌ ${args.map(a => a instanceof Error ? a.message : typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ')}`
79 console.error(msg)
80 display(msg, true)
81 sendToServer('error', msg)
82}
83
84export function remoteLog(msg: string): void {
85 if (import.meta.env.PROD) return
86 sendToServer('log', msg)
87}