Skip to content

Commit 2d194a0

Browse files
committed
fix(ui): prevent long clone paths from breaking report cards
1 parent 96e80a0 commit 2d194a0

2 files changed

Lines changed: 253 additions & 14 deletions

File tree

codeclone/html_report.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,19 +542,75 @@ def _meta_row_class(label: str) -> str:
542542
return "meta-row meta-row-wide"
543543
return "meta-row"
544544

545+
def _is_path_field(label: str) -> bool:
546+
"""Check if field contains a file path."""
547+
return label in {"Baseline", "Cache path"}
548+
549+
def _is_bool_field(label: str) -> bool:
550+
"""Check if field contains a boolean value."""
551+
return label in {"Baseline loaded", "Cache used"}
552+
553+
def _format_meta_value(label: str, value: Any) -> str:
554+
"""Format meta value with appropriate styling."""
555+
display_val = _meta_display(value)
556+
557+
# Boolean fields with badge styling
558+
if _is_bool_field(label):
559+
if isinstance(value, bool):
560+
badge_class = "meta-bool-true" if value else "meta-bool-false"
561+
badge_text = "true" if value else "false"
562+
return f'<span class="meta-bool {badge_class}">{badge_text}</span>'
563+
else:
564+
return '<span class="meta-bool meta-bool-na">n/a</span>'
565+
566+
# Path fields with tooltip on hover
567+
if _is_path_field(label) and display_val != "n/a":
568+
escaped_path = _escape(display_val)
569+
return (
570+
f'<span class="meta-path">'
571+
f"{escaped_path}"
572+
f'<span class="meta-path-tooltip">{escaped_path}</span>'
573+
f"</span>"
574+
)
575+
576+
# Regular fields
577+
return _escape(display_val)
578+
545579
meta_rows_html = "".join(
546580
(
547581
f'<div class="{_meta_row_class(label)}">'
548582
f"<dt>{_escape(label)}</dt>"
549-
f"<dd>{_escape(_meta_display(value))}</dd>"
583+
f"<dd>{_format_meta_value(label, value)}</dd>"
550584
"</div>"
551585
)
552586
for label, value in meta_rows
553587
)
588+
589+
# Chevron icon for toggle
590+
chevron_icon = (
591+
'<svg class="icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">'
592+
'<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" '
593+
'd="M19 9l-7 7-7-7"/>'
594+
"</svg>"
595+
)
596+
597+
# Count non-n/a fields for badge
598+
non_na_count = sum(1 for _, val in meta_rows if _meta_display(val) != "n/a")
599+
554600
report_meta_html = (
555601
f'<section class="meta-panel" id="report-meta" {meta_attrs}>'
556-
'<div class="meta-title">Report Provenance</div>'
602+
'<div class="meta-header">'
603+
'<div class="meta-header-left">'
604+
f'<div class="meta-toggle">{chevron_icon}</div>'
605+
'<h3 class="meta-title">Report Provenance</h3>'
606+
f'<span class="meta-badge">{non_na_count} fields</span>'
607+
"</div>"
608+
"</div>"
609+
'<div class="meta-content">'
610+
'<div class="meta-body">'
557611
f'<dl class="meta-grid">{meta_rows_html}</dl>'
612+
"</div>"
613+
"</div>"
558614
"</section>"
559615
)
560616

codeclone/templates.py

Lines changed: 195 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -316,56 +316,210 @@
316316
margin-top: 40px;
317317
}
318318
319+
/* Meta Panel - Collapsible Pro Design 2025 */
319320
.meta-panel {
320321
margin-top: 22px;
321322
margin-bottom: 20px;
322-
padding: 14px 16px;
323323
background: var(--surface-1);
324324
border: 1px solid var(--border-subtle);
325325
border-radius: var(--radius-lg);
326+
overflow: hidden;
327+
transition: all var(--transition-base);
328+
}
329+
330+
.meta-panel:hover {
331+
border-color: var(--border-default);
332+
}
333+
334+
.meta-header {
335+
display: flex;
336+
align-items: center;
337+
justify-content: space-between;
338+
padding: 14px 16px;
339+
cursor: pointer;
340+
user-select: none;
341+
transition: background var(--transition-fast);
342+
}
343+
344+
.meta-header:hover {
345+
background: var(--surface-2);
346+
}
347+
348+
.meta-header-left {
349+
display: flex;
350+
align-items: center;
351+
gap: 12px;
352+
}
353+
354+
.meta-toggle {
355+
width: 18px;
356+
height: 18px;
357+
display: flex;
358+
align-items: center;
359+
justify-content: center;
360+
transition: transform var(--transition-base);
361+
color: var(--text-tertiary);
362+
flex-shrink: 0;
363+
}
364+
365+
.meta-toggle.collapsed {
366+
transform: rotate(-90deg);
326367
}
327368
328369
.meta-title {
329370
font-size: var(--text-sm);
330371
font-weight: 700;
331372
color: var(--text-primary);
332-
margin-bottom: 10px;
373+
margin: 0;
374+
}
375+
376+
.meta-badge {
377+
font-size: 11px;
378+
font-weight: 500;
379+
color: var(--text-tertiary);
380+
background: var(--surface-2);
381+
padding: 2px 8px;
382+
border-radius: var(--radius-sm);
383+
border: 1px solid var(--border-subtle);
384+
}
385+
386+
.meta-content {
387+
overflow: hidden;
388+
transition: max-height var(--transition-slow), opacity var(--transition-base);
389+
max-height: 2000px;
390+
opacity: 1;
391+
}
392+
393+
.meta-content.collapsed {
394+
max-height: 0;
395+
opacity: 0;
396+
}
397+
398+
.meta-body {
399+
padding: 14px 16px 16px;
400+
border-top: 1px solid var(--border-subtle);
333401
}
334402
335403
.meta-grid {
336404
margin: 0;
337405
display: grid;
338-
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
339-
gap: 8px 16px;
406+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
407+
gap: 12px;
408+
row-gap: 10px;
340409
}
341410
342411
.meta-row {
343-
display: grid;
344-
grid-template-columns: 160px 1fr;
345-
gap: 8px;
412+
display: flex;
413+
flex-direction: column;
414+
gap: 6px;
415+
padding: 10px 12px;
416+
background: var(--surface-0);
417+
border: 1px solid var(--border-subtle);
418+
border-radius: var(--radius);
419+
transition: all var(--transition-fast);
346420
margin: 0;
347421
min-width: 0;
348422
}
349423
424+
.meta-row:hover {
425+
background: var(--surface-2);
426+
border-color: var(--border-default);
427+
}
428+
429+
.meta-row-wide {
430+
grid-column: 1 / -1;
431+
}
432+
350433
.meta-row dt {
351434
color: var(--text-muted);
352435
font-size: var(--text-xs);
353436
font-weight: 600;
354437
text-transform: uppercase;
355438
letter-spacing: 0.02em;
439+
display: flex;
440+
align-items: center;
441+
gap: 6px;
356442
}
357443
358444
.meta-row dd {
359445
margin: 0;
360-
color: var(--text-secondary);
446+
color: var(--text-primary);
361447
font-family: var(--font-mono);
362448
font-size: var(--text-xs);
363-
word-break: normal;
364-
overflow-wrap: anywhere;
449+
line-height: 1.4;
450+
position: relative;
451+
overflow: hidden;
365452
}
366453
367-
.meta-row-wide {
368-
grid-column: 1 / -1;
454+
/* Path truncation with hover tooltip */
455+
.meta-path {
456+
display: block;
457+
white-space: nowrap;
458+
overflow: hidden;
459+
text-overflow: ellipsis;
460+
max-width: 100%;
461+
cursor: help;
462+
position: relative;
463+
padding: 2px 0;
464+
}
465+
466+
.meta-path:hover {
467+
color: var(--accent-primary);
468+
}
469+
470+
.meta-path-tooltip {
471+
position: absolute;
472+
left: 0;
473+
bottom: calc(100% + 8px);
474+
background: var(--surface-3);
475+
color: var(--text-primary);
476+
padding: 8px 12px;
477+
border-radius: var(--radius);
478+
font-size: var(--text-xs);
479+
white-space: normal;
480+
word-break: break-all;
481+
border: 1px solid var(--border-default);
482+
box-shadow: var(--elevation-3);
483+
opacity: 0;
484+
pointer-events: none;
485+
transition: opacity var(--transition-fast);
486+
z-index: 1000;
487+
max-width: 600px;
488+
min-width: 200px;
489+
}
490+
491+
.meta-path:hover .meta-path-tooltip {
492+
opacity: 1;
493+
}
494+
495+
/* Boolean value badges */
496+
.meta-bool {
497+
display: inline-flex;
498+
align-items: center;
499+
padding: 3px 8px;
500+
border-radius: var(--radius-sm);
501+
font-size: 11px;
502+
font-weight: 500;
503+
font-family: var(--font-sans);
504+
}
505+
506+
.meta-bool-true {
507+
background: var(--success-subtle);
508+
color: var(--success);
509+
border: 1px solid rgba(16, 185, 129, 0.3);
510+
}
511+
512+
.meta-bool-false {
513+
background: var(--surface-2);
514+
color: var(--text-muted);
515+
border: 1px solid var(--border-default);
516+
}
517+
518+
.meta-bool-na {
519+
background: var(--surface-2);
520+
color: var(--text-tertiary);
521+
border: 1px solid var(--border-subtle);
522+
font-style: italic;
369523
}
370524
371525
.section-head {
@@ -2251,9 +2405,38 @@
22512405
if (state.chartVisible) renderComplexityChart();
22522406
});
22532407
2408+
// ========== Meta Panel Toggle ==========
2409+
function initMetaPanel() {
2410+
const header = $$('.meta-header');
2411+
const toggle = $$('.meta-toggle');
2412+
const content = $$('.meta-content');
2413+
2414+
if (!header || !toggle || !content) return;
2415+
2416+
// Start collapsed by default to save space
2417+
const startCollapsed = true;
2418+
if (startCollapsed) {
2419+
toggle.classList.add('collapsed');
2420+
content.classList.add('collapsed');
2421+
}
2422+
2423+
header.addEventListener('click', (e) => {
2424+
const isCollapsed = toggle.classList.contains('collapsed');
2425+
2426+
if (isCollapsed) {
2427+
toggle.classList.remove('collapsed');
2428+
content.classList.remove('collapsed');
2429+
} else {
2430+
toggle.classList.add('collapsed');
2431+
content.classList.add('collapsed');
2432+
}
2433+
});
2434+
}
2435+
22542436
// ========== Initialize ==========
22552437
initTheme();
22562438
initCommandPalette();
2439+
initMetaPanel();
22572440
initSection('functions');
22582441
initSection('blocks');
22592442
initSection('segments');

0 commit comments

Comments
 (0)