1// ===== LOGGER CLIENT ===== 2// Logs debug désactivés par défaut en production. 3// Activation possible en localStorage avec la clé `webnearby_debug_logs=true`. 4const DEBUG_STORAGE_KEY = 'webnearby_debug_logs'; 5const DEBUG_PANEL_STORAGE_KEY = 'webnearby_debug_panel'; 6const DEBUG_PANEL_ID = 'webnearbyDebugPanel'; 7const DEBUG_PANEL_CONTENT_ID = 'webnearbyDebugPanelContent'; 8const DEBUG_BUFFER_MAX_LINES = 300; 9const DEBUG_BUFFER_VISIBLE_LINES = 120; 10const debugBuffer = []; 11let debugPanelInitRequested = false; 12function isBrowser() { 13 return typeof window !== 'undefined' && typeof localStorage !== 'undefined'; 14} 15function isLocalDevHost() { 16 if (!isBrowser()) 17 return false; 18 const host = window.location.hostname; 19 return host === 'localhost' || host === '127.0.0.1' || host.endsWith('.local'); 20} 21function isDebugEnabled() { 22 if (!isBrowser()) 23 return false; 24 const url = new URL(window.location.href); 25 const fromQuery = url.searchParams.get('debugLogs'); 26 if (fromQuery === '1' || fromQuery === 'true') 27 return true; 28 if (fromQuery === '0' || fromQuery === 'false') 29 return false; 30 const fromStorage = localStorage.getItem(DEBUG_STORAGE_KEY); 31 if (fromStorage === 'true') 32 return true; 33 if (fromStorage === 'false') 34 return false; 35 return isLocalDevHost(); 36} 37function isDebugPanelEnabled() { 38 if (!isBrowser()) 39 return false; 40 const url = new URL(window.location.href); 41 const fromQuery = url.searchParams.get('debugPanel'); 42 if (fromQuery === '1' || fromQuery === 'true') 43 return true; 44 if (fromQuery === '0' || fromQuery === 'false') 45 return false; 46 return localStorage.getItem(DEBUG_PANEL_STORAGE_KEY) === 'true'; 47} 48function asText(arg) { 49 if (typeof arg === 'string') 50 return arg; 51 if (arg === undefined) 52 return 'undefined'; 53 if (arg === null) 54 return 'null'; 55 try { 56 return JSON.stringify(arg); 57 } 58 catch { 59 return String(arg); 60 } 61} 62function addLineToBuffer(level, args) { 63 if (!isBrowser()) 64 return; 65 if (!isDebugEnabled()) 66 return; 67 const stamp = new Date().toISOString().split('T')[1]?.replace('Z', '') ?? ''; 68 const line = `[${stamp}] [${level}] ${args.map(asText).join(' ')}`; 69 debugBuffer.push(line); 70 if (debugBuffer.length > DEBUG_BUFFER_MAX_LINES) { 71 debugBuffer.splice(0, debugBuffer.length - DEBUG_BUFFER_MAX_LINES); 72 } 73 try { 74 localStorage.setItem('webnearby_debug_buffer', debugBuffer.join('\n')); 75 } 76 catch { 77 // Ignore quota errors 78 } 79 const panelContent = document.getElementById(DEBUG_PANEL_CONTENT_ID); 80 if (panelContent) { 81 panelContent.textContent = debugBuffer.slice(-DEBUG_BUFFER_VISIBLE_LINES).join('\n'); 82 panelContent.scrollTop = panelContent.scrollHeight; 83 } 84} 85function ensureDebugPanel() { 86 if (!isBrowser()) 87 return; 88 if (!isDebugPanelEnabled()) 89 return; 90 if (document.getElementById(DEBUG_PANEL_ID)) 91 return; 92 const create = () => { 93 if (document.getElementById(DEBUG_PANEL_ID)) 94 return; 95 if (!document.body) 96 return; 97 const panel = document.createElement('div'); 98 panel.id = DEBUG_PANEL_ID; 99 panel.style.position = 'fixed'; 100 panel.style.left = '0'; 101 panel.style.right = '0'; 102 panel.style.bottom = '0'; 103 panel.style.height = '38vh'; 104 panel.style.zIndex = '999999'; 105 panel.style.background = 'rgba(17,24,39,0.97)'; 106 panel.style.color = '#e5e7eb'; 107 panel.style.borderTop = '1px solid #374151'; 108 panel.style.display = 'flex'; 109 panel.style.flexDirection = 'column'; 110 panel.style.fontFamily = 'monospace'; 111 const header = document.createElement('div'); 112 header.style.display = 'flex'; 113 header.style.alignItems = 'center'; 114 header.style.justifyContent = 'space-between'; 115 header.style.padding = '6px 10px'; 116 header.style.fontSize = '12px'; 117 header.style.gap = '8px'; 118 header.style.borderBottom = '1px solid #374151'; 119 header.textContent = 'Refect Debug Logs'; 120 const actions = document.createElement('div'); 121 actions.style.display = 'flex'; 122 actions.style.gap = '8px'; 123 const copyBtn = document.createElement('button'); 124 copyBtn.type = 'button'; 125 copyBtn.textContent = 'Copier'; 126 copyBtn.style.background = '#111827'; 127 copyBtn.style.border = '1px solid #6b7280'; 128 copyBtn.style.color = '#f9fafb'; 129 copyBtn.style.padding = '4px 8px'; 130 copyBtn.style.borderRadius = '6px'; 131 copyBtn.addEventListener('click', async () => { 132 const text = debugBuffer.join('\n'); 133 try { 134 await navigator.clipboard.writeText(text); 135 } 136 catch { 137 // Ignore clipboard errors 138 } 139 }); 140 const clearBtn = document.createElement('button'); 141 clearBtn.type = 'button'; 142 clearBtn.textContent = 'Vider'; 143 clearBtn.style.background = '#111827'; 144 clearBtn.style.border = '1px solid #6b7280'; 145 clearBtn.style.color = '#f9fafb'; 146 clearBtn.style.padding = '4px 8px'; 147 clearBtn.style.borderRadius = '6px'; 148 clearBtn.addEventListener('click', () => { 149 debugBuffer.length = 0; 150 const panelContent = document.getElementById(DEBUG_PANEL_CONTENT_ID); 151 if (panelContent) 152 panelContent.textContent = ''; 153 try { 154 localStorage.removeItem('webnearby_debug_buffer'); 155 } 156 catch { 157 // Ignore storage errors 158 } 159 }); 160 const hideBtn = document.createElement('button'); 161 hideBtn.type = 'button'; 162 hideBtn.textContent = 'Masquer'; 163 hideBtn.style.background = '#111827'; 164 hideBtn.style.border = '1px solid #6b7280'; 165 hideBtn.style.color = '#f9fafb'; 166 hideBtn.style.padding = '4px 8px'; 167 hideBtn.style.borderRadius = '6px'; 168 hideBtn.addEventListener('click', () => { 169 panel.remove(); 170 }); 171 actions.appendChild(copyBtn); 172 actions.appendChild(clearBtn); 173 actions.appendChild(hideBtn); 174 header.appendChild(actions); 175 const content = document.createElement('pre'); 176 content.id = DEBUG_PANEL_CONTENT_ID; 177 content.style.margin = '0'; 178 content.style.padding = '8px 10px'; 179 content.style.fontSize = '11px'; 180 content.style.lineHeight = '1.3'; 181 content.style.overflow = 'auto'; 182 content.style.whiteSpace = 'pre-wrap'; 183 content.style.wordBreak = 'break-word'; 184 content.textContent = debugBuffer.slice(-DEBUG_BUFFER_VISIBLE_LINES).join('\n'); 185 panel.appendChild(header); 186 panel.appendChild(content); 187 document.body.appendChild(panel); 188 }; 189 if (document.readyState === 'loading') { 190 if (debugPanelInitRequested) 191 return; 192 debugPanelInitRequested = true; 193 document.addEventListener('DOMContentLoaded', create, { once: true }); 194 } 195 else { 196 create(); 197 } 198} 199export function setDebugLogsEnabled(enabled) { 200 if (!isBrowser()) 201 return; 202 localStorage.setItem(DEBUG_STORAGE_KEY, enabled ? 'true' : 'false'); 203} 204export function logDebug(...args) { 205 if (!isDebugEnabled()) 206 return; 207 ensureDebugPanel(); 208 addLineToBuffer('DEBUG', args); 209} 210export function logInfo(...args) { 211 ensureDebugPanel(); 212 addLineToBuffer('INFO', args); 213 if (typeof console !== 'undefined' && console.info) { 214 console.info(...args); 215 } 216} 217export function logWarn(...args) { 218 ensureDebugPanel();
219 addLineToBuffer('WARN', args); 220 if (typeof console !== 'undefined' && console.warn) { 221 console.warn(...args); 222 } 223} 224export function logError(...args) { 225 ensureDebugPanel(); 226 addLineToBuffer('ERROR', args); 227 if (typeof console !== 'undefined' && console.error) { 228 console.error(...args); 229 } 230}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.