Skip to content

Commit b5059ab

Browse files
authored
fix(html): resolve responsive report overflows and polish mobile UX (#17)
* chore(deps): upgrade requests to 2.33.0 and refresh locked security updates and bump codeclone version * fix(html): resolve responsive report overflows and polish provenance badges * fix(html): polish mobile responsive layout — sticky tabs, scroll shadows, compact topbar
1 parent a472e97 commit b5059ab

File tree

14 files changed

+352
-202
lines changed

14 files changed

+352
-202
lines changed

.github/workflows/benchmark.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ run-name: benchmark • ${{ github.event_name }} • ${{ github.ref_name }}
33

44
on:
55
push:
6-
branches: [ "feat/2.0.0" ]
6+
branches: [ "**" ]
77
pull_request:
8-
branches: [ "feat/2.0.0" ]
98
workflow_dispatch:
109
inputs:
1110
profile:

CHANGELOG.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
# Changelog
22

3-
## [2.0.0b1]
3+
## [2.0.0b2]
4+
5+
### Dependencies
6+
7+
- Upgrade requests (dev dep) to 2.33.0 for extract_zipped_paths security fix (CVE-2026-25645)
8+
9+
### HTML
10+
11+
- Fix page-level horizontal scrolling in wide table tabs by constraining overflow to local table wrappers (#14).
12+
- Fix mobile header brand block layout on narrow viewports (#15).
13+
- Make mobile navigation tabs sticky and horizontally scrollable with scroll-shadow affordance.
14+
- Keep Overview KPI micro-badges inside cards at extreme browser/mobile widths.
15+
- Restyle Report Provenance summary badges to match the card-style badge language used across the report.
16+
17+
## [2.0.0b1] - 20260325
418

519
Major upgrade: CodeClone evolves from a structural clone detector into a
620
**baseline-aware code-health and CI governance tool** for Python.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ Dynamic/runtime false positives are resolved via explicit inline suppressions, n
197197
{
198198
"report_schema_version": "2.1",
199199
"meta": {
200-
"codeclone_version": "2.0.0b1",
200+
"codeclone_version": "2.0.0b2",
201201
"project_name": "...",
202202
"scan_root": ".",
203203
"report_mode": "full",

benchmarks/run_docker_benchmark.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
set -euo pipefail
33

44
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5-
IMAGE_TAG="${IMAGE_TAG:-codeclone-benchmark:2.0.0b1}"
5+
IMAGE_TAG="${IMAGE_TAG:-codeclone-benchmark:2.0.0b2}"
66
OUT_DIR="${OUT_DIR:-$ROOT_DIR/.cache/benchmarks}"
77
OUTPUT_BASENAME="${OUTPUT_BASENAME:-codeclone-benchmark.json}"
88
CPUSET="${CPUSET:-0}"

codeclone.baseline.json

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
"meta": {
33
"generator": {
44
"name": "codeclone",
5-
"version": "2.0.0b1"
5+
"version": "2.0.0b2"
66
},
77
"schema_version": "2.0",
88
"fingerprint_version": "1",
99
"python_tag": "cp313",
10-
"created_at": "2026-03-24T15:14:34Z",
10+
"created_at": "2026-03-26T16:36:17Z",
1111
"payload_sha256": "691c6cedd10e2a51d6038780f3ae9dffe763356dd2aba742b3980f131b79f217",
12-
"metrics_payload_sha256": "3310d3a0f64d5fa0373546c5c4c82675dc5a441344f876092005665e81234e94"
12+
"metrics_payload_sha256": "f18db9aa4573517b0babb31e4e995208209895ea6b8a1957087c0f3b6f1f5434"
1313
},
1414
"clones": {
1515
"functions": [
@@ -32,16 +32,14 @@
3232
"high_coupling_classes": [],
3333
"max_cohesion": 5,
3434
"low_cohesion_classes": [
35-
"codeclone._cli_reports:_OutputPaths",
36-
"codeclone._cli_reports:_ReportArtifacts",
3735
"codeclone.baseline:Baseline",
3836
"codeclone.metrics_baseline:MetricsBaseline",
3937
"tests.test_golden_v2:_DummyExecutor"
4038
],
4139
"dependency_cycles": [],
42-
"dependency_max_depth": 9,
40+
"dependency_max_depth": 10,
4341
"dead_code_items": [],
44-
"health_score": 78,
42+
"health_score": 81,
4543
"health_grade": "B"
4644
}
4745
}

codeclone/_html_css.py

Lines changed: 71 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,18 @@
139139
box-shadow:var(--shadow-sm)}
140140
.topbar-inner{display:flex;align-items:center;justify-content:space-between;
141141
height:72px;padding:0 var(--sp-6);max-width:var(--container-max);margin:0 auto}
142-
.brand{display:flex;align-items:center;gap:var(--sp-3)}
142+
.brand{display:flex;align-items:center;gap:var(--sp-3);min-width:0;flex:1}
143143
.brand-logo{flex-shrink:0}
144-
.brand-text{display:flex;flex-direction:column;gap:2px}
145-
.brand h1{font-size:1.15rem;font-weight:700;color:var(--text-primary);line-height:1.3}
146-
.brand-meta{font-size:.78rem;color:var(--text-muted)}
147-
.brand-project{font-weight:500;color:var(--text-secondary)}
144+
.brand-text{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1}
145+
.brand h1{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--sp-1);font-size:1.15rem;
146+
font-weight:700;color:var(--text-primary);line-height:1.3;min-width:0}
147+
.brand-meta{font-size:.78rem;color:var(--text-muted);overflow-wrap:anywhere}
148+
.brand-project{display:inline-flex;flex-wrap:wrap;align-items:baseline;gap:4px;
149+
font-weight:500;color:var(--text-secondary);min-width:0}
148150
.brand-project-name{font-family:var(--font-mono);font-size:.85em;font-weight:500;padding:1px 5px;
149-
border-radius:var(--radius-sm);background:var(--bg-overlay);color:var(--accent-primary)}
150-
.topbar-actions{display:flex;align-items:center;gap:var(--sp-2)}
151+
border-radius:var(--radius-sm);background:var(--bg-overlay);color:var(--accent-primary);
152+
max-width:100%;overflow-wrap:anywhere}
153+
.topbar-actions{display:flex;align-items:center;gap:var(--sp-2);flex-shrink:0;flex-wrap:wrap}
151154
152155
/* Theme toggle */
153156
.theme-toggle{display:inline-flex;align-items:center;gap:var(--sp-1);
@@ -284,9 +287,15 @@
284287
# ---------------------------------------------------------------------------
285288

286289
_TABLES = """\
287-
.table-wrap{overflow-x:auto;border:1px solid var(--border);border-radius:var(--radius-lg);
288-
margin-bottom:var(--sp-4)}
289-
.table{width:100%;border-collapse:collapse;font-size:.82rem;font-family:var(--font-mono)}
290+
.table-wrap{display:block;inline-size:100%;max-inline-size:100%;min-inline-size:0;overflow-x:auto;
291+
overflow-y:hidden;border:1px solid var(--border);border-radius:var(--radius-lg);margin-bottom:var(--sp-4);
292+
background:
293+
linear-gradient(to right,var(--bg-surface) 30%,transparent) left center / 40px 100% no-repeat local,
294+
linear-gradient(to left,var(--bg-surface) 30%,transparent) right center / 40px 100% no-repeat local,
295+
linear-gradient(to right,rgba(0,0,0,.15),transparent) left center / 14px 100% no-repeat scroll,
296+
linear-gradient(to left,rgba(0,0,0,.15),transparent) right center / 14px 100% no-repeat scroll}
297+
.table{inline-size:max-content;min-inline-size:100%;border-collapse:collapse;font-size:.82rem;
298+
font-family:var(--font-mono)}
290299
.table th{position:sticky;top:0;z-index:2;padding:var(--sp-2) var(--sp-3);text-align:left;font-family:var(--font-sans);
291300
font-weight:600;font-size:.75rem;text-transform:uppercase;letter-spacing:.05em;
292301
color:var(--text-muted);background:var(--bg-overlay);border-bottom:1px solid var(--border);
@@ -300,10 +309,11 @@
300309
.table tr:last-child td{border-bottom:none}
301310
.table tr:hover td{background:var(--bg-raised)}
302311
.table .col-name{font-weight:500;color:var(--text-primary)}
303-
.table .col-file{color:var(--text-muted);max-width:240px;overflow:hidden;
312+
.table .col-file,.table .col-path{color:var(--text-muted);max-width:240px;overflow:hidden;
304313
text-overflow:ellipsis;white-space:nowrap}
305-
.table .col-number{font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
306-
.table .col-risk{white-space:nowrap}
314+
.table .col-number,.table .col-num{font-variant-numeric:tabular-nums;text-align:right;white-space:nowrap}
315+
.table .col-risk,.table .col-badge,.table .col-cat{white-space:nowrap}
316+
.table .col-steps{max-width:120px;word-break:break-word}
307317
.table .col-wide{max-width:320px;word-break:break-all}
308318
.table-empty{padding:var(--sp-8);text-align:center;color:var(--text-muted);font-size:.9rem}
309319
"""
@@ -478,6 +488,12 @@
478488
}
479489
@media(max-width:520px){
480490
.overview-kpi-cards{grid-template-columns:1fr}
491+
.overview-kpi-cards .meta-item{grid-template-rows:auto auto auto;align-content:start;
492+
min-height:0}
493+
.overview-kpi-cards .meta-item .meta-label{min-height:0}
494+
.overview-kpi-cards .meta-item .meta-value{padding-top:0}
495+
.overview-kpi-cards .kpi-detail{align-self:start}
496+
.overview-kpi-cards .kpi-micro{max-width:100%;white-space:normal;overflow-wrap:anywhere}
481497
}
482498
483499
/* Health gauge */
@@ -510,7 +526,7 @@
510526
background:var(--bg-alt)}
511527
512528
/* Badge modal */
513-
.badge-modal{max-width:540px;width:90vw;max-height:85vh}
529+
.badge-modal{max-width:680px;width:92vw;max-height:85vh}
514530
.badge-modal .modal-head{display:flex;align-items:center;justify-content:space-between;
515531
padding:var(--sp-3) var(--sp-4);border-bottom:1px solid var(--border)}
516532
.badge-modal .modal-head h2{font-size:1rem;font-weight:700;margin:0}
@@ -916,12 +932,19 @@
916932
/* Provenance summary badges */
917933
.prov-summary{display:flex;flex-wrap:wrap;align-items:center;gap:6px;
918934
padding:var(--sp-2) var(--sp-4);border-top:1px solid var(--border)}
919-
.prov-badge{font-size:.65rem;font-weight:600;padding:2px 8px;
920-
border-radius:10px;white-space:nowrap;display:inline-flex;align-items:center;gap:3px}
921-
.prov-badge.green{background:var(--success-muted);color:var(--success)}
922-
.prov-badge.red{background:var(--error-muted);color:var(--error)}
923-
.prov-badge.amber{background:var(--warning-muted);color:var(--warning)}
924-
.prov-badge.neutral{background:var(--bg-overlay);color:var(--text-muted)}
935+
.prov-badge{display:inline-flex;align-items:center;gap:4px;font-size:.66rem;
936+
padding:2px 8px;border-radius:var(--radius-sm);background:var(--bg-raised);
937+
white-space:nowrap;line-height:1.3;border:1px solid color-mix(in srgb,var(--border) 55%,transparent)}
938+
.prov-badge-val{font-weight:600;font-variant-numeric:tabular-nums}
939+
.prov-badge-lbl{font-weight:400;color:var(--text-muted);text-transform:lowercase}
940+
.prov-badge--green{background:var(--success-muted);border-color:color-mix(in srgb,var(--success) 20%,transparent)}
941+
.prov-badge--green .prov-badge-val{color:var(--success)}
942+
.prov-badge--red{background:var(--error-muted);border-color:color-mix(in srgb,var(--error) 20%,transparent)}
943+
.prov-badge--red .prov-badge-val{color:var(--error)}
944+
.prov-badge--amber{background:var(--warning-muted);border-color:color-mix(in srgb,var(--warning) 20%,transparent)}
945+
.prov-badge--amber .prov-badge-val{color:var(--warning)}
946+
.prov-badge--neutral{background:var(--bg-overlay);border-color:color-mix(in srgb,var(--border) 75%,transparent)}
947+
.prov-badge--neutral .prov-badge-val{color:var(--text-secondary)}
925948
.prov-explain{font-size:.62rem;color:var(--text-muted);margin-left:auto;font-style:italic}
926949
"""
927950

@@ -1033,12 +1056,39 @@
10331056
.overview-row-spread{margin-left:0;width:100%}
10341057
.suggestion-head{flex-direction:column;align-items:flex-start}
10351058
.suggestion-facts{grid-template-columns:1fr}
1059+
.sf-head{flex-direction:column;align-items:flex-start}
1060+
.sf-meta{width:100%}
10361061
.container{padding:0 var(--sp-3)}
1037-
.main-tabs{padding:0 var(--sp-3)}
1062+
.topbar{position:static}
1063+
.topbar-inner{height:auto;padding:var(--sp-2) var(--sp-3);flex-direction:row;
1064+
align-items:center;gap:var(--sp-2)}
1065+
.brand{flex:1;min-width:0;align-items:center;gap:var(--sp-2)}
1066+
.brand-logo{width:24px;height:24px}
1067+
.brand-text{gap:0}
1068+
.brand h1{font-size:.85rem;line-height:1.25;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
1069+
.brand-project-name{font-size:.78em;padding:0 3px}
1070+
.brand-meta{display:none}
1071+
.topbar-actions{flex-shrink:0;gap:var(--sp-1)}
1072+
.topbar-actions .btn-prov{font-size:0;gap:0;width:32px;height:32px;
1073+
padding:0;align-items:center;justify-content:center}
1074+
.topbar-actions .btn-prov .prov-dot{width:10px;height:10px}
1075+
.theme-toggle{font-size:0;gap:0;width:32px;height:32px;
1076+
padding:0;align-items:center;justify-content:center}
1077+
.theme-toggle svg{width:16px;height:16px}
1078+
.main-tabs-wrap{position:sticky;top:0;z-index:90;padding:var(--sp-2) 0 0}
1079+
.main-tabs{padding:var(--sp-1);gap:2px;
1080+
background:
1081+
linear-gradient(to right,var(--bg-surface) 30%,transparent) left center / 28px 100% no-repeat local,
1082+
linear-gradient(to left,var(--bg-surface) 30%,transparent) right center / 28px 100% no-repeat local,
1083+
linear-gradient(to right,rgba(0,0,0,.12),transparent) left center / 10px 100% no-repeat scroll,
1084+
linear-gradient(to left,rgba(0,0,0,.12),transparent) right center / 10px 100% no-repeat scroll,
1085+
var(--bg-surface)}
1086+
.main-tab{flex:none;padding:var(--sp-1) var(--sp-2);font-size:.78rem}
10381087
}
10391088
@media(max-width:480px){
10401089
.overview-kpi-grid{grid-template-columns:1fr}
10411090
.search-box input[type="text"]{width:140px}
1091+
.brand-logo{width:28px;height:28px}
10421092
}
10431093
10441094
/* Print */

codeclone/_html_report/_sections/_meta.py

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -333,36 +333,41 @@ def _section_html(title: str, rows: list[tuple[str, object]]) -> str:
333333
_section_html(st, rows) for st, rows in meta_sections if rows
334334
)
335335

336-
def _prov_badge(label: str, color: str) -> str:
337-
return f'<span class="prov-badge {color}">{_escape_html(label)}</span>'
336+
def _prov_badge(label: str, value: str, color: str) -> str:
337+
return (
338+
f'<span class="prov-badge prov-badge--{color}">'
339+
f'<span class="prov-badge-val">{_escape_html(value)}</span>'
340+
f'<span class="prov-badge-lbl">{_escape_html(label)}</span>'
341+
"</span>"
342+
)
338343

339344
badges: list[str] = []
340345
if _bl_verified is True:
341-
badges.append(_prov_badge("Baseline verified", "green"))
346+
badges.append(_prov_badge("Baseline", "verified", "green"))
342347
elif _bl_loaded is True and _bl_verified is not True:
343-
badges.append(_prov_badge("Baseline untrusted", "red"))
348+
badges.append(_prov_badge("Baseline", "untrusted", "red"))
344349
elif _bl_loaded is False or _bl_loaded is None:
345-
badges.append(_prov_badge("Baseline missing", "amber"))
350+
badges.append(_prov_badge("Baseline", "missing", "amber"))
346351
if ctx.report_schema_version:
347-
badges.append(_prov_badge(f"Schema {ctx.report_schema_version}", "neutral"))
352+
badges.append(_prov_badge("Schema", str(ctx.report_schema_version), "neutral"))
348353
if _bl_fp_ver is not None:
349-
badges.append(_prov_badge(f"Fingerprint {_bl_fp_ver}", "neutral"))
354+
badges.append(_prov_badge("Fingerprint", str(_bl_fp_ver), "neutral"))
350355
gen_name = str(_bl_gen_name or "")
351356
if gen_name and gen_name != "codeclone":
352-
badges.append(_prov_badge(f"Generator mismatch: {gen_name}", "red"))
357+
badges.append(_prov_badge("Generator mismatch", gen_name, "red"))
353358
if _cache_used is True:
354-
badges.append(_prov_badge("Cache hit", "green"))
359+
badges.append(_prov_badge("Cache", "hit", "green"))
355360
elif _cache_used is False:
356-
badges.append(_prov_badge("Cache miss", "amber"))
361+
badges.append(_prov_badge("Cache", "miss", "amber"))
357362
else:
358-
badges.append(_prov_badge("Cache N/A", "neutral"))
363+
badges.append(_prov_badge("Cache", "N/A", "neutral"))
359364
analysis_mode = str(_meta_pick(meta.get("analysis_mode")) or "")
360365
if analysis_mode:
361-
badges.append(_prov_badge(f"Mode: {analysis_mode}", "neutral"))
366+
badges.append(_prov_badge("Mode", analysis_mode, "neutral"))
362367
if _mbl_verified is True:
363-
badges.append(_prov_badge("Metrics baseline verified", "green"))
368+
badges.append(_prov_badge("Metrics baseline", "verified", "green"))
364369
elif _mbl_loaded is True and _mbl_verified is not True:
365-
badges.append(_prov_badge("Metrics baseline untrusted", "red"))
370+
badges.append(_prov_badge("Metrics baseline", "untrusted", "red"))
366371

367372
prov_summary = (
368373
f'<div class="prov-summary">{"".join(badges)}'

docs/book/08-report.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Purpose
44

5-
Define report contracts in `2.0.0b1`: canonical JSON (`report_schema_version=2.1`)
5+
Define report contracts in `2.0.0b2`: canonical JSON (`report_schema_version=2.1`)
66
plus deterministic TXT/Markdown/SARIF projections.
77

88
## Public surface

docs/book/appendix/b-schema-layouts.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
## Purpose
44

5-
Compact structural layouts for baseline/cache/report contracts in `2.0.0b1`.
5+
Compact structural layouts for baseline/cache/report contracts in `2.0.0b2`.
66

77
## Baseline schema (`2.0`)
88

99
```json
1010
{
1111
"meta": {
12-
"generator": { "name": "codeclone", "version": "2.0.0b1" },
12+
"generator": { "name": "codeclone", "version": "2.0.0b2" },
1313
"schema_version": "2.0",
1414
"fingerprint_version": "1",
1515
"python_tag": "cp313",
@@ -83,7 +83,7 @@ Notes:
8383
{
8484
"report_schema_version": "2.1",
8585
"meta": {
86-
"codeclone_version": "2.0.0b1",
86+
"codeclone_version": "2.0.0b2",
8787
"project_name": "codeclone",
8888
"scan_root": ".",
8989
"analysis_mode": "full",
@@ -264,7 +264,7 @@ Notes:
264264
"tool": {
265265
"driver": {
266266
"name": "codeclone",
267-
"version": "2.0.0b1",
267+
"version": "2.0.0b2",
268268
"rules": [
269269
{
270270
"id": "CCLONE001",

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "codeclone"
7-
version = "2.0.0b1"
7+
version = "2.0.0b2"
88
description = "Structural code quality analysis for Python"
99
readme = { file = "README.md", content-type = "text/markdown" }
1010
license = "MIT"
@@ -67,7 +67,7 @@ dev = [
6767
"build>=1.4.1",
6868
"twine>=5.0.0",
6969
"mypy>=1.19.1",
70-
"ruff>=0.15.7",
70+
"ruff>=0.15.8",
7171
"pre-commit>=4.5.1",
7272
]
7373

0 commit comments

Comments
 (0)