Skip to content

Commit 5ae2a72

Browse files
quic-boyucclaude
andcommitted
observatory: n-column compare-mode sidebar (RFC commit 2/3)
When the report aggregates multiple archives (compare mode), the left sidebar is now an N-column grid with one column per archive. - main.css: new .index-list.compare-mode rule that switches the list to CSS grid with grid-template-columns: repeat(var(--archive-cols), 1fr). Each archive becomes an .archive-column li with a sticky header (.archive-column-header) showing the archive label, and an internal ul.archive-sessions holding that archive's session-dashboard links and grouped records (region tree or flat). Tightened item styling inside columns (no shadows, smaller font) so two columns stay readable on standard sidebar widths. - 02_layout.js: renderIndex() now branches on state.data.archives.length: - len > 1: compare-mode N-column grid; sessions filtered into their column via _renderArchiveColumn. - len == 1 (single archive): flat session list, identical to today's rendering. - sessions empty: defensive fallback (very old archives). --archive-cols CSS var is set at render time so the grid size adjusts to the archive count automatically. Single-archive reports are unchanged visually; only the structure of state.data.archives drives the layout. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent 370d69e commit 5ae2a72

2 files changed

Lines changed: 95 additions & 0 deletions

File tree

devtools/observatory/templates/css/main.css

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,3 +481,62 @@
481481
border-left: 2px solid var(--border-color);
482482
margin-left: 0.5rem;
483483
}
484+
485+
/* Compare-mode n-column layout: when the report aggregates
486+
multiple archives, the sidebar becomes an N-column grid with
487+
one column per archive. Each column carries its archive label
488+
as a sticky header and lists that archive's sessions, dashboard
489+
links, and (region-tree or flat) records.
490+
491+
In single-archive reports `.index-list` keeps its default
492+
single-column flow -- the grid styles below only apply when
493+
the renderer adds the `compare-mode` class. */
494+
.index-list.compare-mode {
495+
display: grid;
496+
grid-template-columns: repeat(var(--archive-cols, 1), minmax(0, 1fr));
497+
gap: 0.5rem;
498+
padding: 0.5rem;
499+
align-items: start;
500+
}
501+
.archive-column {
502+
list-style: none;
503+
display: flex;
504+
flex-direction: column;
505+
min-width: 0;
506+
background: var(--bg-primary);
507+
border: 1px solid var(--border-color);
508+
border-radius: 6px;
509+
overflow: hidden;
510+
}
511+
.archive-column-header {
512+
padding: 0.45rem 0.75rem;
513+
font-size: 0.85rem;
514+
font-weight: 700;
515+
background: var(--bg-tertiary);
516+
border-bottom: 1px solid var(--border-color);
517+
color: var(--text-primary);
518+
position: sticky;
519+
top: 0;
520+
z-index: 1;
521+
text-overflow: ellipsis;
522+
overflow: hidden;
523+
white-space: nowrap;
524+
}
525+
ul.archive-sessions {
526+
list-style: none;
527+
margin: 0;
528+
padding: 0.4rem;
529+
min-width: 0;
530+
}
531+
/* Tightened items inside columns -- the column itself provides
532+
the visual separation so per-item shadows/margins look heavy. */
533+
.archive-column .index-item {
534+
margin-bottom: 0.2rem;
535+
font-size: 0.82rem;
536+
box-shadow: none;
537+
background: var(--bg-tertiary);
538+
}
539+
.archive-column .session-dashboard-link {
540+
margin-bottom: 0.3rem;
541+
font-weight: 600;
542+
}

devtools/observatory/templates/js/02_layout.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,22 +314,58 @@
314314
return html;
315315
}
316316

317+
function _renderArchiveColumn(archive, sessions, records, useTree) {
318+
const sessionsInArchive = sessions.filter((s) => s.archive === archive.label);
319+
let inner = '';
320+
sessionsInArchive.forEach((session) => {
321+
inner += _renderSessionDashboardLink(session);
322+
inner += useTree
323+
? renderIndexTree(records, session.id)
324+
: renderIndexFlat(records, session.id);
325+
});
326+
return `
327+
<li class="archive-column">
328+
<div class="archive-column-header" title="${escapeHtml(archive.label)}">${escapeHtml(archive.label)}</div>
329+
<ul class="archive-sessions">${inner}</ul>
330+
</li>
331+
`;
332+
}
333+
317334
function renderIndex() {
318335
const list = document.getElementById('index-list');
319336
if (!list) return;
320337

321338
const records = state.data.records || [];
322339
const sessions = state.data.sessions || [];
340+
const archives = state.data.archives || [];
323341
const useTree = treeView && records.some((r) => Array.isArray(r.region_stack) && r.region_stack.length > 0);
324342

325343
if (sessions.length === 0) {
326344
// Defensive fallback: payloads without a sessions list (very old
327345
// archives) render as a single ungrouped list.
346+
list.classList.remove('compare-mode');
347+
list.style.removeProperty('--archive-cols');
328348
list.innerHTML = useTree ? renderIndexTree(records) : renderIndexFlat(records);
329349
updateIndexHeader();
330350
return;
331351
}
332352

353+
// Compare mode: more than one archive => N-column grid, one per archive.
354+
if (archives.length > 1) {
355+
list.classList.add('compare-mode');
356+
list.style.setProperty('--archive-cols', String(archives.length));
357+
let html = '';
358+
archives.forEach((archive) => {
359+
html += _renderArchiveColumn(archive, sessions, records, useTree);
360+
});
361+
list.innerHTML = html;
362+
updateIndexHeader();
363+
return;
364+
}
365+
366+
// Single-archive: flat list of session dashboards + records.
367+
list.classList.remove('compare-mode');
368+
list.style.removeProperty('--archive-cols');
333369
let html = '';
334370
sessions.forEach((session) => {
335371
html += _renderSessionDashboardLink(session);

0 commit comments

Comments
 (0)