const TELEMETRY_ENDPOINT = "/telemetry"; function send(event) { try { navigator.sendBeacon(TELEMETRY_ENDPOINT, JSON.stringify(event)); } catch {} } /** Fire-and-forget client events (same channel as initTelemetry). */ export function sendTelemetry(event) { send(event); } export function initTelemetry() { import("web-vitals/attribution").then(({ onCLS, onLCP, onINP }) => { onCLS((metric) => { if (metric.value > 0.05) { send({ type: "cls", value: metric.value, target: metric.attribution?.largestShiftTarget || null, sources: metric.attribution?.largestShiftSource || null, }); } }); onLCP((metric) => { send({ type: "lcp", value: metric.value, element: metric.attribution?.element || null, url: metric.attribution?.url || null, }); }); onINP((metric) => { if (metric.value > 200) { send({ type: "inp", value: metric.value, target: metric.attribution?.interactionTarget || null, }); } }); }).catch(() => {}); const watchIds = ["tab-bar", "action-bar"]; const observer = new ResizeObserver((entries) => { for (const entry of entries) { const id = entry.target.dataset?.watchId || entry.target.className; const rect = entry.target.getBoundingClientRect(); send({ type: "resize", id, width: Math.round(rect.width), height: Math.round(rect.height) }); } }); requestAnimationFrame(() => { for (const id of watchIds) { const el = document.querySelector(`[data-watch-id="${id}"]`) || document.querySelector(`.${id}`); if (el) observer.observe(el); } }); try { const perfObs = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.duration > 200) { send({ type: "slow_interaction", name: entry.name, duration: Math.round(entry.duration) }); } } }); perfObs.observe({ type: "event", buffered: true, durationThreshold: 100 }); } catch {} }