added last log preview

This commit is contained in:
Pierre committed 2026-10-04 12:54:31 +02:00
1 parent cfb5334666
commit 6c345ccf81
4 files changed
+36 -7

No files matched your search

+4
View File
@@ -8,3 +8,7 @@
.block-title{font-weight:600;margin-bottom:6px;color:var(--fg);overflow-wrap:anywhere} .block-title{font-weight:600;margin-bottom:6px;color:var(--fg);overflow-wrap:anywhere}
#error-count{font-weight:700;font-size:14px}#error-count.has-errors{color:var(--vscode-editorError-foreground,#ffacac);border-color:currentColor;background:var(--vscode-inputValidation-errorBackground,#5b3039)} #error-count{font-weight:700;font-size:14px}#error-count.has-errors{color:var(--vscode-editorError-foreground,#ffacac);border-color:currentColor;background:var(--vscode-inputValidation-errorBackground,#5b3039)}
.last-log{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
summary{display:grid;grid-template-columns:auto minmax(0,1fr) repeat(3,max-content);align-items:center;column-gap:10px;row-gap:5px}
summary .last-log{grid-column:1/-1;min-width:0}
+24 -5
View File
@@ -56,6 +56,19 @@
const years = Math.floor(days / 365); const years = Math.floor(days / 365);
return `${years} year${years === 1 ? '' : 's'} ago`; return `${years} year${years === 1 ? '' : 's'} ago`;
} }
function shortTimeSince(time) {
const seconds = Math.max(0, Math.floor((Date.now() - Number(time)) / 1000));
if (seconds < 60) return `${seconds} sec ago`;
const minutes = Math.floor(seconds / 60);
if (minutes < 60) return `${minutes} min ago`;
const hours = Math.floor(minutes / 60);
if (hours < 24) return `${hours} hr ago`;
const days = Math.floor(hours / 24);
if (days < 30) return `${days} d ago`;
const months = Math.floor(days / 30);
if (days < 365) return `${months} mo ago`;
return `${Math.floor(days / 365)} yr ago`;
}
function copy(text) { if (text) vscode.postMessage({ type: 'copy', text }); } function copy(text) { if (text) vscode.postMessage({ type: 'copy', text }); }
function logMessages(items, cls) { function logMessages(items, cls) {
const container = node('div', undefined, cls); const container = node('div', undefined, cls);
@@ -133,7 +146,7 @@
if (!groups.size) container.append(node('div', 'No logs match these filters. Try a wider time range or clear the search.', 'empty')); if (!groups.size) container.append(node('div', 'No logs match these filters. Try a wider time range or clear the search.', 'empty'));
const lastLogs = new Map(); const lastLogs = new Map();
entries.filter(e => e.kind === 'log' || e.kind.startsWith('initialization_')).forEach(e => { entries.filter(e => e.kind === 'log' || e.kind.startsWith('initialization_')).forEach(e => {
if (!lastLogs.has(e.app) || e.time > lastLogs.get(e.app).time) lastLogs.set(e.app, e); if (!lastLogs.has(e.app) || e.time >= lastLogs.get(e.app).time) lastLogs.set(e.app, e);
}); });
const timeOf = (app, logs) => (lastLogs.get(app) || logs[logs.length - 1]).time; const timeOf = (app, logs) => (lastLogs.get(app) || logs[logs.length - 1]).time;
$('error-count').setAttribute('aria-pressed', String($('level').value === 'ERROR')); $('error-count').setAttribute('aria-pressed', String($('level').value === 'ERROR'));
@@ -146,15 +159,21 @@
if (recentApps.has(app)) detail.classList.add('new-log-app'); if (recentApps.has(app)) detail.classList.add('new-log-app');
const summary = node('summary'), label = node('div', undefined, 'app-label'); const summary = node('summary'), label = node('div', undefined, 'app-label');
const last = lastLogs.get(app); const last = lastLogs.get(app);
let preview;
label.append(node('span', app, 'name')); label.append(node('span', app, 'name'));
if (last) { if (last) {
const age = node('span', `Last log: ${timeSince(last.time)}`, 'last-log last-age'); const line = node('span', undefined, `last-log log-message level-${normalLevel(last)}`);
const age = node('span', shortTimeSince(last.time), 'last-age');
age.dataset.time = String(last.time); age.dataset.time = String(last.time);
age.title = `${formatTime(last, true)} · Exact: ${last.timestamp}`; age.title = `${formatTime(last, true)} · Exact: ${last.timestamp}`;
label.append(age); const message = cleanMessage(last);
} else label.append(node('span', 'No log in loaded history', 'last-log')); line.title = message;
line.append(age, document.createTextNode(' : '), node('span', message.split(/\r?\n/, 1)[0].trim() || '—', 'last-message'));
preview = line;
} else preview = node('span', 'No log in loaded history', 'last-log');
summary.append(label); summary.append(label);
['INFO', 'WARNING', 'ERROR'].forEach(level => summary.append(node('span', `${level} ${logs.filter(e => normalLevel(e) === level).length}`, `badge ${level}`))); ['INFO', 'WARNING', 'ERROR'].forEach(level => summary.append(node('span', `${level} ${logs.filter(e => normalLevel(e) === level).length}`, `badge ${level}`)));
summary.append(preview);
detail.append(summary); detail.append(summary);
function fill() { function fill() {
detail.querySelector('.rows')?.remove(); detail.querySelector('.rows')?.remove();
@@ -332,7 +351,7 @@
$('warnings').textContent = (message.warnings || []).join(' · '); renderApps(); renderDetail(); $('warnings').textContent = (message.warnings || []).join(' · '); renderApps(); renderDetail();
} }
function refreshLastLogAges() { function refreshLastLogAges() {
document.querySelectorAll('.last-age[data-time]').forEach(label => { label.textContent = `Last log: ${timeSince(label.dataset.time)}`; }); document.querySelectorAll('.last-age[data-time]').forEach(label => { label.textContent = shortTimeSince(label.dataset.time); });
document.querySelectorAll('.relative-time[data-time]').forEach(label => { label.textContent = timeSince(label.dataset.time); }); document.querySelectorAll('.relative-time[data-time]').forEach(label => { label.textContent = timeSince(label.dataset.time); });
} }
setInterval(refreshLastLogAges, 1000); setInterval(refreshLastLogAges, 1000);
+8 -2
View File
@@ -23,6 +23,10 @@ test('English interface, latest timestamps, sorting and separate initialization
assert.equal(d.getElementById('copy').textContent,'Copy selected log'); assert.equal(d.getElementById('copy').textContent,'Copy selected log');
assert.deepEqual([...d.querySelectorAll('[data-app]')].map(e=>e.dataset.app),['beta','alpha']); assert.deepEqual([...d.querySelectorAll('[data-app]')].map(e=>e.dataset.app),['beta','alpha']);
assert.match(d.querySelector('[data-app="alpha"] .last-age').title,/10:00:10/); assert.match(d.querySelector('[data-app="alpha"] .last-age').title,/10:00:10/);
assert.match(d.querySelector('[data-app="alpha"] summary .last-log').textContent,/ : Température déjà disponible$/);
const summary=d.querySelector('[data-app="alpha"] summary');
assert.deepEqual([...summary.children].map(child=>child.className),['app-label','badge INFO','badge WARNING','badge ERROR','last-log']);
assert.equal(summary.querySelector('.app-label .last-log'),null);
d.getElementById('sort').value='oldest';d.getElementById('sort').dispatchEvent(new dom.window.Event('input')); d.getElementById('sort').value='oldest';d.getElementById('sort').dispatchEvent(new dom.window.Event('input'));
assert.equal(d.querySelector('[data-app]').dataset.app,'alpha'); assert.equal(d.querySelector('[data-app]').dataset.app,'alpha');
const alpha=d.querySelector('[data-app="alpha"]');alpha.open=true;alpha.dispatchEvent(new dom.window.Event('toggle')); const alpha=d.querySelector('[data-app="alpha"]');alpha.open=true;alpha.dispatchEvent(new dom.window.Event('toggle'));
@@ -109,10 +113,12 @@ test('pulses an app when a new log arrives and refreshes its last-log age',async
assert.ok(app.classList.contains('new-log-app')); assert.ok(app.classList.contains('new-log-app'));
app.open=true;app.dispatchEvent(new dom.window.Event('toggle')); app.open=true;app.dispatchEvent(new dom.window.Event('toggle'));
assert.ok(app.querySelector('.recent-log')); assert.ok(app.querySelector('.recent-log'));
assert.equal(app.querySelector('.last-age').textContent,'Last log: 1 second ago'); assert.equal(app.querySelector('.last-age').textContent,'1 sec ago');
assert.equal(app.querySelector('summary .last-log').textContent,'1 sec ago : Fresh log');
dom.window.Date.now=()=>nextTime+65000; dom.window.Date.now=()=>nextTime+65000;
await new Promise(resolve=>setTimeout(resolve,1100)); await new Promise(resolve=>setTimeout(resolve,1100));
assert.equal(app.querySelector('.last-age').textContent,'Last log: 1 minute ago'); assert.equal(app.querySelector('.last-age').textContent,'1 min ago');
assert.equal(app.querySelector('summary .last-log').textContent,'1 min ago : Fresh log');
dom.window.close(); dom.window.close();
}); });
Binary file not shown.