@@ -10110,6 +10110,34 @@ def get_local_ip():
1011010110</div><!-- end page-nemoclaw (theme 2) -->
1011110111
1011210112
10113+ <div class="page" id="page-logs">
10114+ <div class="refresh-bar" style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
10115+ <button class="refresh-btn" onclick="loadLogs()">↻ Refresh</button>
10116+ <label style="font-size:12px;color:var(--text-secondary);">Lines:
10117+ <input id="log-lines" type="number" value="200" min="10" max="2000" step="10"
10118+ style="width:60px;margin-left:4px;padding:3px 6px;border:1px solid var(--border-primary);border-radius:6px;background:var(--bg-secondary);color:var(--text-primary);font-size:12px;">
10119+ </label>
10120+ <input id="log-filter" type="text" placeholder="Filter logs…" oninput="filterLogLines()"
10121+ style="padding:5px 10px;border-radius:7px;border:1px solid var(--border-primary);background:var(--bg-secondary);color:var(--text-primary);font-size:12px;width:180px;">
10122+ <div style="display:flex;gap:4px;flex-wrap:wrap;">
10123+ <button class="time-btn active" id="log-filter-all" onclick="setLogLevel('all',this)">All</button>
10124+ <button class="time-btn" id="log-filter-info" onclick="setLogLevel('info',this)">Info</button>
10125+ <button class="time-btn" id="log-filter-warn" onclick="setLogLevel('warn',this)">Warn</button>
10126+ <button class="time-btn" id="log-filter-error" onclick="setLogLevel('error',this)">Error</button>
10127+ </div>
10128+ <label style="display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-secondary);cursor:pointer;margin-left:auto;">
10129+ <input type="checkbox" id="log-autoscroll" checked> Auto-scroll
10130+ </label>
10131+ <span id="log-stream-status" style="font-size:11px;color:var(--text-muted);">● Connecting…</span>
10132+ </div>
10133+ <div class="card" style="padding:0;overflow:hidden;margin-top:8px;">
10134+ <div id="logs-full"
10135+ style="font-family:monospace;font-size:12px;padding:12px 16px;max-height:calc(100vh - 180px);overflow-y:auto;background:var(--bg-secondary);border-radius:8px;">
10136+ <div style="color:var(--text-muted);text-align:center;padding:24px;">Loading logs…</div>
10137+ </div>
10138+ </div>
10139+ </div>
10140+
1011310141<script>
1011410142// === Budget & Alert Functions ===
1011510143function openBudgetModal() {
@@ -14574,17 +14602,25 @@ def get_local_ip():
1457414602 if (window.CLOUD_MODE) return;
1457514603 if (logStream) logStream.close();
1457614604 streamBuffer = [];
14605+ var statusEl = document.getElementById('log-stream-status');
14606+ if (statusEl) statusEl.textContent = '\u25cf Connecting\u2026';
1457714607 logStream = new EventSource('/api/logs-stream' + (localStorage.getItem('clawmetry-token') ? '?token=' + encodeURIComponent(localStorage.getItem('clawmetry-token')) : ''));
14608+ logStream.onopen = function() {
14609+ var s = document.getElementById('log-stream-status');
14610+ if (s) { s.textContent = '\u25cf Live'; s.style.color = '#22c55e'; }
14611+ };
1457814612 logStream.onmessage = function(e) {
1457914613 var data = JSON.parse(e.data);
1458014614 streamBuffer.push(data.line);
1458114615 if (streamBuffer.length > MAX_STREAM_LINES) streamBuffer.shift();
1458214616 appendLogLine('ov-logs', data.line);
1458314617 appendLogLine('logs-full', data.line);
1458414618 processFlowEvent(data.line);
14585- document.getElementById('refresh-time').textContent = 'Live • ' + new Date().toLocaleTimeString();
14619+ document.getElementById('refresh-time').textContent = 'Live \u2022 ' + new Date().toLocaleTimeString();
1458614620 };
1458714621 logStream.onerror = function() {
14622+ var s = document.getElementById('log-stream-status');
14623+ if (s) { s.textContent = '\u25cf Reconnecting\u2026'; s.style.color = '#f59e0b'; }
1458814624 setTimeout(startLogStream, 5000);
1458914625 };
1459014626}
@@ -14632,11 +14668,54 @@ def get_local_ip():
1463214668 div.innerHTML = '<span class="' + parsed.cls + '">' + parsed.html + '</span>';
1463314669 el.appendChild(div);
1463414670 while (el.children.length > MAX_STREAM_LINES) el.removeChild(el.firstChild);
14635- if (el.scrollHeight - el.scrollTop - el.clientHeight < 150) {
14636- el.scrollTop = el.scrollHeight;
14671+ // Apply live filter for logs-full
14672+ if (elId === 'logs-full') {
14673+ var span = el.lastElementChild ? el.lastElementChild.querySelector('span') : null;
14674+ var cls = span ? span.className : '';
14675+ var text = el.lastElementChild ? el.lastElementChild.textContent.toLowerCase() : '';
14676+ var query = (document.getElementById('log-filter') ? document.getElementById('log-filter').value : '').toLowerCase();
14677+ var levelOk = _logLevelFilter === 'all'
14678+ || (_logLevelFilter === 'error' && cls === 'err')
14679+ || (_logLevelFilter === 'warn' && (cls === 'warn' || cls === 'err'))
14680+ || (_logLevelFilter === 'info' && (cls === 'info' || cls === 'warn' || cls === 'err'));
14681+ if (!levelOk || (query && !text.includes(query))) el.lastElementChild.style.display = 'none';
14682+ }
14683+ var autoScroll = document.getElementById('log-autoscroll');
14684+ if (!autoScroll || autoScroll.checked) {
14685+ if (el.scrollHeight - el.scrollTop - el.clientHeight < 150) {
14686+ el.scrollTop = el.scrollHeight;
14687+ }
1463714688 }
1463814689}
1463914690
14691+
14692+ // ===== Log Tab Helpers =====
14693+ var _logLevelFilter = 'all';
14694+
14695+ function setLogLevel(level, btn) {
14696+ _logLevelFilter = level;
14697+ document.querySelectorAll('#page-logs .time-btn').forEach(function(b) { b.classList.remove('active'); });
14698+ if (btn) btn.classList.add('active');
14699+ filterLogLines();
14700+ }
14701+
14702+ function filterLogLines() {
14703+ var el = document.getElementById('logs-full');
14704+ if (!el) return;
14705+ var query = (document.getElementById('log-filter') ? document.getElementById('log-filter').value : '').toLowerCase();
14706+ Array.from(el.children).forEach(function(div) {
14707+ var span = div.querySelector('span');
14708+ var cls = span ? span.className : '';
14709+ var text = div.textContent.toLowerCase();
14710+ var levelOk = _logLevelFilter === 'all'
14711+ || (_logLevelFilter === 'error' && cls === 'err')
14712+ || (_logLevelFilter === 'warn' && (cls === 'warn' || cls === 'err'))
14713+ || (_logLevelFilter === 'info' && (cls === 'info' || cls === 'warn' || cls === 'err'));
14714+ var queryOk = !query || text.includes(query);
14715+ div.style.display = levelOk && queryOk ? '' : 'none';
14716+ });
14717+ }
14718+
1464014719// ===== Flow Visualization Engine =====
1464114720var flowStats = { messages: 0, events: 0, activeTools: {}, msgTimestamps: [] };
1464214721var flowInitDone = false;
0 commit comments