tech-debt-visualizer 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- /* Technical Debt Report — Grafana-style dashboard */
1
+ /* Technical Debt Report — Colorful dashboard, clean fit, scroll in sections */
2
2
  :root {
3
3
  --bg: #0b0c0e;
4
4
  --bg-elevated: #111214;
@@ -9,8 +9,13 @@
9
9
  --text: #e8e9ea;
10
10
  --text-muted: #8b9199;
11
11
  --link: #5794f2;
12
- --radius: 4px;
13
- --shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
12
+ --radius: 6px;
13
+ --shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
14
+ --tier-1: #e53935;
15
+ --tier-2: #f57c00;
16
+ --tier-3: #f9a825;
17
+ --tier-4: #29b6f6;
18
+ --tier-5: #43a047;
14
19
  }
15
20
 
16
21
  [data-theme="light"] {
@@ -24,6 +29,11 @@
24
29
  --text-muted: #6b7077;
25
30
  --link: #3274d9;
26
31
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
32
+ --tier-1: #c62828;
33
+ --tier-2: #ef6c00;
34
+ --tier-3: #f9a825;
35
+ --tier-4: #0288d1;
36
+ --tier-5: #2e7d32;
27
37
  }
28
38
 
29
39
  * { box-sizing: border-box; }
@@ -40,7 +50,11 @@ body {
40
50
  }
41
51
 
42
52
  body.dashboard-page {
43
- padding-bottom: 2rem;
53
+ padding-bottom: 0;
54
+ height: 100vh;
55
+ overflow: hidden;
56
+ display: flex;
57
+ flex-direction: column;
44
58
  }
45
59
 
46
60
  /* ——— Dashboard header (Grafana top bar: title left, score right) ——— */
@@ -104,70 +118,187 @@ body.dashboard-page {
104
118
  .dashboard-score-of { font-size: 0.85rem; font-weight: 500; color: var(--text-muted); }
105
119
  .dashboard-score-label { font-size: 11px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.04em; margin-left: 0.35rem; opacity: 0.95; }
106
120
 
107
- .dashboard-score.tier-1 { background: rgba(204, 0, 0, 0.2); color: #f66; }
108
- .dashboard-score.tier-2 { background: rgba(232, 93, 0, 0.2); color: #f90; }
109
- .dashboard-score.tier-3 { background: rgba(184, 134, 11, 0.2); color: #db9; }
110
- .dashboard-score.tier-4 { background: rgba(0, 102, 153, 0.2); color: #6cf; }
111
- .dashboard-score.tier-5 { background: rgba(10, 107, 10, 0.2); color: #6c6; }
121
+ .dashboard-score.tier-1 { background: rgba(229, 57, 53, 0.25); color: #ff8a80; font-weight: 700; }
122
+ .dashboard-score.tier-2 { background: rgba(245, 124, 0, 0.25); color: #ffb74d; font-weight: 700; }
123
+ .dashboard-score.tier-3 { background: rgba(249, 168, 37, 0.25); color: #ffd54f; font-weight: 700; }
124
+ .dashboard-score.tier-4 { background: rgba(41, 182, 246, 0.25); color: #82b1ff; font-weight: 700; }
125
+ .dashboard-score.tier-5 { background: rgba(67, 160, 71, 0.25); color: #81c784; font-weight: 700; }
112
126
 
113
127
  .dashboard-date {
114
128
  font-size: 12px;
115
129
  color: var(--text-muted);
116
130
  }
117
131
 
118
- /* ——— Main content: 2×2 dashboard grid ——— */
132
+ /* ——— Main content: 2×2 grid, fills viewport, scroll inside cells ——— */
119
133
  .dashboard-main {
120
- max-width: 1400px;
134
+ flex: 1;
135
+ min-height: 0;
136
+ max-width: 1600px;
121
137
  margin: 0 auto;
122
- padding: 1rem 1.5rem;
123
- min-height: calc(100vh - 52px);
138
+ padding: 0.75rem 1rem;
139
+ width: 100%;
140
+ overflow: hidden;
141
+ display: flex;
142
+ flex-direction: column;
124
143
  }
125
144
 
126
145
  .dashboard-grid-2x2 {
146
+ flex: 1;
147
+ min-height: 0;
127
148
  display: grid;
128
149
  grid-template-columns: 1fr 1fr;
129
- grid-template-rows: auto minmax(320px, 1fr);
130
- gap: 1rem;
131
- align-items: start;
150
+ grid-template-rows: auto 1fr;
151
+ gap: 0.75rem;
152
+ align-items: stretch;
132
153
  }
133
154
 
134
155
  .dashboard-cell {
135
156
  min-height: 0;
136
157
  min-width: 0;
158
+ overflow: hidden;
137
159
  }
138
160
 
139
161
  .dashboard-cell .panel {
140
162
  height: 100%;
141
163
  display: flex;
142
164
  flex-direction: column;
165
+ min-height: 0;
143
166
  }
144
167
 
145
168
  .dashboard-cell .panel-body {
146
169
  flex: 1;
147
170
  min-height: 0;
148
171
  overflow: auto;
172
+ -webkit-overflow-scrolling: touch;
149
173
  }
150
174
 
151
- .dashboard-cell-score .panel-body-score { flex: 1 1 auto; overflow: visible; }
175
+ .dashboard-cell-score .panel-body-score { flex: 1 1 auto; min-height: 0; }
152
176
 
153
177
  .dashboard-cell-heatmap .panel-body-heatmap,
154
178
  .dashboard-cell-list .panel-body {
155
- min-height: 280px;
179
+ min-height: 0;
156
180
  }
157
181
 
158
182
  @media (max-width: 900px) {
183
+ body.dashboard-page { height: auto; overflow: auto; }
159
184
  .dashboard-grid-2x2 {
160
185
  grid-template-columns: 1fr;
161
186
  grid-template-rows: auto auto auto auto;
187
+ height: auto;
162
188
  }
189
+ .dashboard-cell-heatmap .panel-body-heatmap { min-height: 240px; }
190
+ .dashboard-cell-list .panel-body { min-height: 240px; }
163
191
  }
164
192
 
165
193
  @media (max-width: 560px) {
166
- .dashboard-header { padding: 0.6rem 1rem; }
167
- .dashboard-main { padding: 0.75rem 1rem; }
194
+ .dashboard-header { padding: 0.5rem 0.75rem; }
195
+ .dashboard-main { padding: 0.5rem 0.75rem; }
196
+ }
197
+
198
+ /* ——— Footer: AI prompts button ——— */
199
+ .dashboard-footer {
200
+ flex-shrink: 0;
201
+ padding: 0.5rem 0;
202
+ border-top: 1px solid var(--border-subtle);
203
+ margin-top: 0.5rem;
204
+ }
205
+
206
+ .btn-ai-prompts {
207
+ font: inherit;
208
+ font-size: 13px;
209
+ font-weight: 600;
210
+ padding: 0.5rem 1rem;
211
+ background: var(--surface);
212
+ color: var(--text);
213
+ border: 1px solid var(--border);
214
+ border-radius: var(--radius);
215
+ cursor: pointer;
216
+ }
217
+
218
+ .btn-ai-prompts:hover {
219
+ background: var(--surface-hover);
220
+ border-color: var(--text-muted);
221
+ }
222
+
223
+ /* ——— AI prompts overlay (blank screen) ——— */
224
+ .overlay {
225
+ position: fixed;
226
+ inset: 0;
227
+ z-index: 200;
228
+ display: none;
229
+ align-items: center;
230
+ justify-content: center;
231
+ padding: 1rem;
232
+ }
233
+
234
+ .overlay.show {
235
+ display: flex;
236
+ }
237
+
238
+ .overlay-backdrop {
239
+ position: absolute;
240
+ inset: 0;
241
+ background: rgba(0, 0, 0, 0.5);
242
+ }
243
+
244
+ .overlay-panel {
245
+ position: relative;
246
+ background: var(--surface);
247
+ border: 1px solid var(--border);
248
+ border-radius: var(--radius);
249
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
250
+ max-width: 560px;
251
+ width: 100%;
252
+ max-height: 85vh;
253
+ display: flex;
254
+ flex-direction: column;
255
+ overflow: hidden;
256
+ }
257
+
258
+ .overlay-header {
259
+ display: flex;
260
+ align-items: center;
261
+ justify-content: space-between;
262
+ padding: 0.75rem 1rem;
263
+ border-bottom: 1px solid var(--border-subtle);
264
+ flex-shrink: 0;
168
265
  }
169
266
 
170
- /* ——— Panels (Grafana-style) ——— */
267
+ .overlay-title {
268
+ margin: 0;
269
+ font-size: 1rem;
270
+ font-weight: 600;
271
+ color: var(--text);
272
+ }
273
+
274
+ .overlay-close {
275
+ font-size: 1.5rem;
276
+ line-height: 1;
277
+ padding: 0.25rem;
278
+ background: none;
279
+ border: none;
280
+ color: var(--text-muted);
281
+ cursor: pointer;
282
+ }
283
+
284
+ .overlay-close:hover {
285
+ color: var(--text);
286
+ }
287
+
288
+ .overlay-body {
289
+ padding: 1rem;
290
+ overflow: auto;
291
+ flex: 1;
292
+ min-height: 0;
293
+ }
294
+
295
+ .overlay-placeholder {
296
+ margin: 0;
297
+ font-size: 13px;
298
+ color: var(--text-muted);
299
+ }
300
+
301
+ /* ——— Panels: borders, colored accents ——— */
171
302
  .panel {
172
303
  background: var(--surface);
173
304
  border: 1px solid var(--border);
@@ -177,9 +308,10 @@ body.dashboard-page {
177
308
  }
178
309
 
179
310
  .panel-header {
180
- padding: 0.75rem 1rem;
311
+ padding: 0.6rem 0.85rem;
181
312
  border-bottom: 1px solid var(--border-subtle);
182
- background: rgba(0, 0, 0, 0.03);
313
+ background: rgba(0, 0, 0, 0.04);
314
+ flex-shrink: 0;
183
315
  }
184
316
 
185
317
  [data-theme="light"] .panel-header {
@@ -188,83 +320,80 @@ body.dashboard-page {
188
320
 
189
321
  .panel-title {
190
322
  margin: 0;
191
- font-size: 13px;
192
- font-weight: 600;
323
+ font-size: 12px;
324
+ font-weight: 700;
193
325
  color: var(--text);
194
- letter-spacing: -0.01em;
326
+ letter-spacing: 0.02em;
327
+ text-transform: uppercase;
195
328
  }
196
329
 
197
330
  .panel-desc {
198
- margin: 0.25rem 0 0;
199
- font-size: 12px;
331
+ margin: 0.2rem 0 0;
332
+ font-size: 11px;
200
333
  color: var(--text-muted);
201
- line-height: 1.4;
334
+ line-height: 1.35;
202
335
  }
203
336
 
204
337
  .panel-body {
205
- padding: 1rem;
338
+ padding: 0.85rem 1rem;
206
339
  }
207
340
 
208
341
  .panel-body-center {
209
342
  text-align: center;
210
343
  }
211
344
 
212
- /* ——— Score panel (top-left: shield + caption + stats) ——— */
345
+ /* ——— Score panel (top-left): numeric score only, scrollable ——— */
213
346
  .panel-score .panel-body-score {
214
347
  text-align: center;
215
- padding: 1.25rem 1rem;
348
+ padding: 1rem 0.85rem;
216
349
  }
217
350
 
218
- .panel-score .score-badge {
219
- display: inline-block;
220
- margin-bottom: 0.75rem;
221
- line-height: 0;
222
- }
223
-
224
- .panel-score .score-badge-svg {
225
- display: block;
226
- width: 100px;
227
- height: auto;
228
- margin: 0 auto;
351
+ .score-numeric {
352
+ display: flex;
353
+ align-items: baseline;
354
+ justify-content: center;
355
+ gap: 0.25rem;
356
+ margin-bottom: 0.5rem;
229
357
  }
230
358
 
231
- .panel-score .score-badge-svg .score-badge-num {
232
- font-size: 36px;
359
+ .score-num {
360
+ font-size: 2rem;
233
361
  font-weight: 800;
234
- letter-spacing: -0.02em;
362
+ line-height: 1;
363
+ letter-spacing: -0.04em;
364
+ color: var(--text);
235
365
  }
236
366
 
237
- .panel-score .score-badge-svg .score-badge-of {
238
- font-size: 11px;
239
- font-weight: 700;
240
- letter-spacing: 0.08em;
241
- text-transform: uppercase;
242
- opacity: 0.95;
367
+ .score-of {
368
+ font-size: 0.9rem;
369
+ font-weight: 600;
370
+ color: var(--text-muted);
243
371
  }
244
372
 
245
373
  .panel-score .score-label {
246
- margin: 0 0 0.25rem;
247
- font-size: 1rem;
248
- font-weight: 600;
374
+ margin: 0 0 0.2rem;
375
+ font-size: 0.95rem;
376
+ font-weight: 700;
249
377
  color: var(--text);
250
378
  }
251
379
 
252
380
  .panel-score .score-desc {
253
- margin: 0 0 0.5rem;
254
- font-size: 13px;
381
+ margin: 0 0 0.4rem;
382
+ font-size: 12px;
255
383
  color: var(--text-muted);
256
- line-height: 1.45;
257
- max-width: 280px;
384
+ line-height: 1.4;
385
+ max-width: 260px;
258
386
  margin-left: auto;
259
387
  margin-right: auto;
260
388
  }
261
389
 
262
390
  .panel-score .score-stats {
263
391
  margin: 0;
264
- font-size: 11px;
392
+ font-size: 10px;
265
393
  color: var(--text-muted);
266
394
  text-transform: uppercase;
267
- letter-spacing: 0.03em;
395
+ letter-spacing: 0.04em;
396
+ font-weight: 600;
268
397
  }
269
398
 
270
399
  /* ——— Description of problems panel (top-right) ——— */
@@ -278,18 +407,18 @@ body.dashboard-page {
278
407
  .priority-inline {
279
408
  display: grid;
280
409
  grid-template-columns: 1fr 1fr;
281
- gap: 1rem;
282
- margin-top: 1rem;
283
- padding-top: 1rem;
410
+ gap: 0.75rem;
411
+ margin-top: 0.75rem;
412
+ padding-top: 0.75rem;
284
413
  border-top: 1px solid var(--border-subtle);
285
414
  }
286
415
 
287
416
  .priority-inline h4 {
288
- margin: 0 0 0.5rem;
289
- font-size: 11px;
290
- font-weight: 600;
417
+ margin: 0 0 0.35rem;
418
+ font-size: 10px;
419
+ font-weight: 700;
291
420
  text-transform: uppercase;
292
- letter-spacing: 0.04em;
421
+ letter-spacing: 0.05em;
293
422
  color: var(--text-muted);
294
423
  }
295
424
 
@@ -324,10 +453,6 @@ body.dashboard-page {
324
453
  border-radius: var(--radius);
325
454
  }
326
455
 
327
- /* LLM panel accent */
328
- .panel-llm {
329
- border-left: 3px solid var(--link);
330
- }
331
456
 
332
457
  /* ——— Banner ——— */
333
458
  .no-llm-banner {
@@ -345,27 +470,33 @@ body.dashboard-page {
345
470
  margin: 0;
346
471
  }
347
472
 
348
- /* ——— Treemap ——— */
473
+ /* ——— Treemap: square boxes per file ——— */
349
474
  #treemap {
350
475
  display: flex;
351
476
  flex-wrap: wrap;
352
- gap: 4px;
353
- min-height: 160px;
477
+ gap: 6px;
478
+ min-height: 120px;
354
479
  padding: 0.25rem 0;
355
480
  }
356
481
 
357
482
  .treemap-cell {
483
+ width: 44px;
484
+ height: 44px;
485
+ min-width: 44px;
486
+ min-height: 44px;
487
+ flex: none;
358
488
  display: flex;
359
- align-items: flex-end;
360
- min-width: 64px;
361
- padding: 6px 8px;
362
- font-size: 11px;
489
+ align-items: center;
490
+ justify-content: center;
491
+ padding: 4px;
492
+ font-size: 10px;
363
493
  cursor: pointer;
364
494
  overflow: hidden;
365
495
  text-overflow: ellipsis;
366
496
  white-space: nowrap;
367
497
  border: 1px solid rgba(0, 0, 0, 0.15);
368
- border-radius: 2px;
498
+ border-radius: 4px;
499
+ text-align: center;
369
500
  }
370
501
 
371
502
  .treemap-cell:hover {
@@ -373,11 +504,11 @@ body.dashboard-page {
373
504
  outline-offset: 1px;
374
505
  }
375
506
 
376
- .treemap-cell[data-severity="critical"] { background: #c00; color: #fff; border-color: #900; }
377
- .treemap-cell[data-severity="high"] { background: #e85d00; color: #fff; border-color: #b84a00; }
378
- .treemap-cell[data-severity="medium"] { background: #b8860b; color: #fff; border-color: #8b6914; }
379
- .treemap-cell[data-severity="low"] { background: #0a6b0a; color: #fff; border-color: #064906; }
380
- .treemap-cell[data-severity="none"] { background: var(--border); color: var(--text-muted); border-color: var(--border); }
507
+ .treemap-cell[data-severity="critical"] { background: var(--tier-1); color: #fff; border-color: rgba(0,0,0,0.2); }
508
+ .treemap-cell[data-severity="high"] { background: var(--tier-2); color: #fff; border-color: rgba(0,0,0,0.15); }
509
+ .treemap-cell[data-severity="medium"] { background: var(--tier-3); color: #1a1a1a; border-color: rgba(0,0,0,0.1); }
510
+ .treemap-cell[data-severity="low"] { background: var(--tier-5); color: #fff; border-color: rgba(0,0,0,0.1); }
511
+ .treemap-cell[data-severity="none"] { background: var(--border); color: var(--text-muted); border-color: var(--border-subtle); }
381
512
 
382
513
  .legend {
383
514
  display: flex;
@@ -389,12 +520,12 @@ body.dashboard-page {
389
520
  color: var(--text-muted);
390
521
  }
391
522
 
392
- .legend span { display: inline-flex; align-items: center; gap: 0.35rem; }
393
- .legend .swatch { width: 10px; height: 10px; border-radius: 2px; }
394
- .legend .swatch-crit { background: #c00; }
395
- .legend .swatch-high { background: #e85d00; }
396
- .legend .swatch-med { background: #b8860b; }
397
- .legend .swatch-low { background: #0a6b0a; }
523
+ .legend span { display: inline-flex; align-items: center; gap: 0.4rem; font-size: 11px; font-weight: 600; }
524
+ .legend .swatch { width: 12px; height: 12px; border-radius: 3px; }
525
+ .legend .swatch-crit { background: var(--tier-1); }
526
+ .legend .swatch-high { background: var(--tier-2); }
527
+ .legend .swatch-med { background: var(--tier-3); }
528
+ .legend .swatch-low { background: var(--tier-5); }
398
529
  .legend .swatch-none { background: var(--border); }
399
530
 
400
531
  /* ——— Priority lists ——— */
@@ -460,12 +591,12 @@ body.dashboard-page {
460
591
  border-radius: 3px;
461
592
  }
462
593
 
463
- .badge-critical { background: #c00; color: #fff; }
464
- .badge-high { background: #e85d00; color: #fff; }
465
- .badge-medium { background: #b8860b; color: #fff; }
466
- .badge-low { background: #0a6b0a; color: #fff; }
594
+ .badge-critical { background: var(--tier-1); color: #fff; font-weight: 700; }
595
+ .badge-high { background: var(--tier-2); color: #fff; font-weight: 700; }
596
+ .badge-medium { background: var(--tier-3); color: #1a1a1a; font-weight: 700; }
597
+ .badge-low { background: var(--tier-5); color: #fff; font-weight: 700; }
467
598
  .badge-none { background: var(--border); color: var(--text-muted); }
468
- .badge-llm { background: var(--link); color: #fff; }
599
+ .badge-llm { background: var(--link); color: #fff; font-weight: 700; }
469
600
 
470
601
  /* ——— Detail modal ——— */
471
602
  #detail {
@@ -145,8 +145,6 @@ fileScores.forEach(function (_ref) {
145
145
  var cell = document.createElement("div");
146
146
  cell.className = "treemap-cell";
147
147
  cell.dataset.severity = severity;
148
- cell.style.flex = String((score / maxScore) * 100) + " 1 80px";
149
- cell.style.minWidth = "72px";
150
148
  cell.title = file + llmScore;
151
149
  cell.textContent = file.split("/").pop() || file;
152
150
  cell.addEventListener("click", function () { showDetail(file, items); });
@@ -319,3 +317,22 @@ function showDetail(file, items) {
319
317
  if (e.target === panel) panel.classList.remove("show");
320
318
  };
321
319
  }
320
+
321
+ (function () {
322
+ var btn = document.getElementById("btnAiPrompts");
323
+ var overlay = document.getElementById("aiPromptsOverlay");
324
+ var closeBtn = document.getElementById("closeAiPrompts");
325
+ var backdrop = overlay && overlay.querySelector(".overlay-backdrop");
326
+ if (!btn || !overlay) return;
327
+ function open() {
328
+ overlay.classList.add("show");
329
+ overlay.setAttribute("aria-hidden", "false");
330
+ }
331
+ function close() {
332
+ overlay.classList.remove("show");
333
+ overlay.setAttribute("aria-hidden", "true");
334
+ }
335
+ btn.addEventListener("click", open);
336
+ if (closeBtn) closeBtn.addEventListener("click", close);
337
+ if (backdrop) backdrop.addEventListener("click", close);
338
+ })();
@@ -14,25 +14,25 @@ export async function generateHtmlReport(run, options) {
14
14
  const html = buildHtml(run, title, darkMode, css, script);
15
15
  await writeFile(outputPath, html, "utf-8");
16
16
  }
17
- /** Inline SVG badge: shield shape with tier number and "of 5". */
17
+ /** Inline SVG badge: shield shape with tier number and "of 5" — clean, bold, tier-colored. */
18
18
  function buildScoreBadgeSvg(tier, fillColor) {
19
- const lighter = adjustHexBrightness(fillColor, 1.3);
19
+ const lighter = adjustHexBrightness(fillColor, 1.25);
20
20
  const shadowId = "badge-shadow-" + tier;
21
21
  const gradientId = "badge-shine-" + tier;
22
22
  return `<svg class="score-badge-svg" viewBox="0 0 140 168" xmlns="http://www.w3.org/2000/svg" role="img">
23
23
  <defs>
24
24
  <linearGradient id="${gradientId}" x1="0%" y1="0%" x2="0%" y2="100%">
25
- <stop offset="0%" style="stop-color:${lighter};stop-opacity:0.5" />
25
+ <stop offset="0%" style="stop-color:${lighter};stop-opacity:0.6" />
26
26
  <stop offset="100%" style="stop-color:${fillColor};stop-opacity:1" />
27
27
  </linearGradient>
28
- <filter id="${shadowId}" x="-30%" y="-20%" width="160%" height="150%">
29
- <feDropShadow dx="0" dy="6" stdDeviation="8" flood-opacity="0.35"/>
28
+ <filter id="${shadowId}" x="-40%" y="-30%" width="180%" height="180%">
29
+ <feDropShadow dx="0" dy="4" stdDeviation="6" flood-color="${fillColor}" flood-opacity="0.4"/>
30
30
  </filter>
31
31
  </defs>
32
32
  <path fill="url(#${gradientId})" filter="url(#${shadowId})" d="M70 12 C108 12 128 38 128 72 C128 106 70 156 70 156 C70 156 12 106 12 72 C12 38 32 12 70 12 Z"/>
33
- <path fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="2.5" stroke-linejoin="round" d="M70 12 C108 12 128 38 128 72 C128 106 70 156 70 156 C70 156 12 106 12 72 C12 38 32 12 70 12 Z"/>
34
- <text x="70" y="78" text-anchor="middle" class="score-badge-num" fill="white">${tier}</text>
35
- <text x="70" y="100" text-anchor="middle" class="score-badge-of" fill="white">of 5</text>
33
+ <path fill="none" stroke="rgba(255,255,255,0.5)" stroke-width="2" stroke-linejoin="round" d="M70 12 C108 12 128 38 128 72 C128 106 70 156 70 156 C70 156 12 106 12 72 C12 38 32 12 70 12 Z"/>
34
+ <text x="70" y="80" text-anchor="middle" class="score-badge-num" fill="white">${tier}</text>
35
+ <text x="70" y="102" text-anchor="middle" class="score-badge-of" fill="rgba(255,255,255,0.95)">of 5</text>
36
36
  </svg>`;
37
37
  }
38
38
  function adjustHexBrightness(hex, factor) {
@@ -69,15 +69,6 @@ function buildHtml(run, title, darkMode, css, script) {
69
69
  });
70
70
  const highCriticalCount = run.debtItems.filter((d) => d.severity === "high" || d.severity === "critical").length;
71
71
  const hotspotCount = run.fileMetrics.filter((m) => (m.hotspotScore ?? 0) > 0.3).length;
72
- const tierColors = {
73
- 1: "#c00",
74
- 2: "#e85d00",
75
- 3: "#b8860b",
76
- 4: "#069",
77
- 5: "#0a6b0a",
78
- };
79
- const tierColor = tierColors[cleanliness.tier] ?? "#666";
80
- const scoreBadgeSvg = buildScoreBadgeSvg(cleanliness.tier, tierColor);
81
72
  const statsLine = `${run.fileMetrics.length} files · ${run.debtItems.length} items · ${highCriticalCount} high/crit · ${hotspotCount} hotspots`;
82
73
  return `<!DOCTYPE html>
83
74
  <html lang="en" data-theme="${theme}">
@@ -113,12 +104,15 @@ function buildHtml(run, title, darkMode, css, script) {
113
104
  <main class="dashboard-main">
114
105
  <div class="dashboard-grid-2x2">
115
106
  <div class="dashboard-cell dashboard-cell-score">
116
- <div class="panel panel-score tier-${cleanliness.tier}">
107
+ <div class="panel panel-score">
117
108
  <div class="panel-header">
118
109
  <h2 class="panel-title">Technical Debt Cleanliness Score</h2>
119
110
  </div>
120
111
  <div class="panel-body panel-body-score">
121
- <div class="score-badge" aria-hidden="true">${scoreBadgeSvg}</div>
112
+ <div class="score-numeric">
113
+ <span class="score-num">${cleanliness.tier}</span>
114
+ <span class="score-of">of 5</span>
115
+ </div>
122
116
  <p class="score-label">${escapeHtml(cleanliness.label)}</p>
123
117
  <p class="score-desc">${escapeHtml(cleanliness.description)}</p>
124
118
  <p class="score-stats">${statsLine}</p>
@@ -126,8 +120,27 @@ function buildHtml(run, title, darkMode, css, script) {
126
120
  </div>
127
121
  </div>
128
122
 
123
+ <div class="dashboard-cell dashboard-cell-heatmap">
124
+ <div class="panel panel-heatmap">
125
+ <div class="panel-header panel-header-heatmap">
126
+ <h2 class="panel-title">Files by debt</h2>
127
+ <p class="panel-desc">Size = complexity + churn. Color = severity. Click for details.</p>
128
+ <div class="legend legend-inline">
129
+ <span><span class="swatch swatch-crit"></span> Critical</span>
130
+ <span><span class="swatch swatch-high"></span> High</span>
131
+ <span><span class="swatch swatch-med"></span> Medium</span>
132
+ <span><span class="swatch swatch-low"></span> Low</span>
133
+ <span><span class="swatch swatch-none"></span> None</span>
134
+ </div>
135
+ </div>
136
+ <div class="panel-body panel-body-heatmap">
137
+ <div id="treemap"></div>
138
+ </div>
139
+ </div>
140
+ </div>
141
+
129
142
  <div class="dashboard-cell dashboard-cell-problems">
130
- <div class="panel panel-llm">
143
+ <div class="panel">
131
144
  <div class="panel-header">
132
145
  <h2 class="panel-title">Description of problems</h2>
133
146
  </div>
@@ -153,25 +166,6 @@ function buildHtml(run, title, darkMode, css, script) {
153
166
  </div>
154
167
  </div>
155
168
 
156
- <div class="dashboard-cell dashboard-cell-heatmap">
157
- <div class="panel panel-heatmap">
158
- <div class="panel-header panel-header-heatmap">
159
- <h2 class="panel-title">Files by debt</h2>
160
- <p class="panel-desc">Size = complexity + churn. Color = severity. Click for details.</p>
161
- <div class="legend legend-inline">
162
- <span><span class="swatch swatch-crit"></span> Critical</span>
163
- <span><span class="swatch swatch-high"></span> High</span>
164
- <span><span class="swatch swatch-med"></span> Medium</span>
165
- <span><span class="swatch swatch-low"></span> Low</span>
166
- <span><span class="swatch swatch-none"></span> None</span>
167
- </div>
168
- </div>
169
- <div class="panel-body panel-body-heatmap">
170
- <div id="treemap"></div>
171
- </div>
172
- </div>
173
- </div>
174
-
175
169
  <div class="dashboard-cell dashboard-cell-list">
176
170
  <div class="panel">
177
171
  <div class="panel-header">
@@ -184,8 +178,25 @@ function buildHtml(run, title, darkMode, css, script) {
184
178
  </div>
185
179
  </div>
186
180
  </div>
181
+
182
+ <div class="dashboard-footer">
183
+ <button type="button" class="btn-ai-prompts" id="btnAiPrompts">AI cleanup prompts</button>
184
+ </div>
187
185
  </main>
188
186
 
187
+ <div id="aiPromptsOverlay" class="overlay" aria-hidden="true">
188
+ <div class="overlay-backdrop"></div>
189
+ <div class="overlay-panel">
190
+ <div class="overlay-header">
191
+ <h2 class="overlay-title">AI cleanup prompts</h2>
192
+ <button type="button" class="overlay-close" id="closeAiPrompts" aria-label="Close">&times;</button>
193
+ </div>
194
+ <div class="overlay-body">
195
+ <p class="overlay-placeholder">Prompts to pass into AI for cleaning up the codebase will appear here.</p>
196
+ </div>
197
+ </div>
198
+ </div>
199
+
189
200
  <div id="detail">
190
201
  <div class="panel">
191
202
  <h3 id="detailTitle"></h3>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tech-debt-visualizer",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Language-agnostic CLI that analyzes repos and generates interactive technical debt visualizations with AI-powered insights",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",