tech-debt-visualizer 0.2.2 → 0.2.4
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.
- package/dist/reports/assets/report.css +130 -66
- package/dist/reports/assets/report.js +85 -39
- package/dist/reports/html.js +62 -91
- package/package.json +1 -1
|
@@ -43,7 +43,7 @@ body.dashboard-page {
|
|
|
43
43
|
padding-bottom: 2rem;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
/* ——— Dashboard header (Grafana
|
|
46
|
+
/* ——— Dashboard header (Grafana top bar: title left, score right) ——— */
|
|
47
47
|
.dashboard-header {
|
|
48
48
|
display: flex;
|
|
49
49
|
align-items: center;
|
|
@@ -100,25 +100,9 @@ body.dashboard-page {
|
|
|
100
100
|
font-weight: 600;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
.dashboard-score-value {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.dashboard-score-of {
|
|
109
|
-
font-size: 0.85rem;
|
|
110
|
-
font-weight: 500;
|
|
111
|
-
color: var(--text-muted);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.dashboard-score-label {
|
|
115
|
-
font-size: 11px;
|
|
116
|
-
font-weight: 500;
|
|
117
|
-
text-transform: uppercase;
|
|
118
|
-
letter-spacing: 0.04em;
|
|
119
|
-
margin-left: 0.35rem;
|
|
120
|
-
opacity: 0.95;
|
|
121
|
-
}
|
|
103
|
+
.dashboard-score-value { font-size: 1.25rem; line-height: 1; }
|
|
104
|
+
.dashboard-score-of { font-size: 0.85rem; font-weight: 500; color: var(--text-muted); }
|
|
105
|
+
.dashboard-score-label { font-size: 11px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.04em; margin-left: 0.35rem; opacity: 0.95; }
|
|
122
106
|
|
|
123
107
|
.dashboard-score.tier-1 { background: rgba(204, 0, 0, 0.2); color: #f66; }
|
|
124
108
|
.dashboard-score.tier-2 { background: rgba(232, 93, 0, 0.2); color: #f90; }
|
|
@@ -131,36 +115,56 @@ body.dashboard-page {
|
|
|
131
115
|
color: var(--text-muted);
|
|
132
116
|
}
|
|
133
117
|
|
|
134
|
-
/* ——— Main content
|
|
118
|
+
/* ——— Main content: 2×2 dashboard grid ——— */
|
|
135
119
|
.dashboard-main {
|
|
136
|
-
max-width:
|
|
120
|
+
max-width: 1400px;
|
|
137
121
|
margin: 0 auto;
|
|
138
|
-
padding:
|
|
122
|
+
padding: 1rem 1.5rem;
|
|
123
|
+
min-height: calc(100vh - 52px);
|
|
139
124
|
}
|
|
140
125
|
|
|
141
|
-
.dashboard-grid {
|
|
126
|
+
.dashboard-grid-2x2 {
|
|
142
127
|
display: grid;
|
|
128
|
+
grid-template-columns: 1fr 1fr;
|
|
129
|
+
grid-template-rows: auto minmax(320px, 1fr);
|
|
143
130
|
gap: 1rem;
|
|
144
|
-
|
|
131
|
+
align-items: start;
|
|
145
132
|
}
|
|
146
133
|
|
|
147
|
-
.dashboard-
|
|
148
|
-
|
|
134
|
+
.dashboard-cell {
|
|
135
|
+
min-height: 0;
|
|
136
|
+
min-width: 0;
|
|
149
137
|
}
|
|
150
138
|
|
|
151
|
-
.dashboard-
|
|
152
|
-
|
|
139
|
+
.dashboard-cell .panel {
|
|
140
|
+
height: 100%;
|
|
141
|
+
display: flex;
|
|
142
|
+
flex-direction: column;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.dashboard-cell .panel-body {
|
|
146
|
+
flex: 1;
|
|
147
|
+
min-height: 0;
|
|
148
|
+
overflow: auto;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.dashboard-cell-score .panel-body-score { flex: 1 1 auto; overflow: visible; }
|
|
152
|
+
|
|
153
|
+
.dashboard-cell-heatmap .panel-body-heatmap,
|
|
154
|
+
.dashboard-cell-list .panel-body {
|
|
155
|
+
min-height: 280px;
|
|
153
156
|
}
|
|
154
157
|
|
|
155
158
|
@media (max-width: 900px) {
|
|
156
|
-
.dashboard-grid-
|
|
157
|
-
|
|
159
|
+
.dashboard-grid-2x2 {
|
|
160
|
+
grid-template-columns: 1fr;
|
|
161
|
+
grid-template-rows: auto auto auto auto;
|
|
162
|
+
}
|
|
158
163
|
}
|
|
159
164
|
|
|
160
165
|
@media (max-width: 560px) {
|
|
161
166
|
.dashboard-header { padding: 0.6rem 1rem; }
|
|
162
|
-
.dashboard-main { padding: 1rem; }
|
|
163
|
-
.dashboard-grid-stats { grid-template-columns: 1fr; }
|
|
167
|
+
.dashboard-main { padding: 0.75rem 1rem; }
|
|
164
168
|
}
|
|
165
169
|
|
|
166
170
|
/* ——— Panels (Grafana-style) ——— */
|
|
@@ -205,32 +209,9 @@ body.dashboard-page {
|
|
|
205
209
|
text-align: center;
|
|
206
210
|
}
|
|
207
211
|
|
|
208
|
-
/*
|
|
209
|
-
.
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
.stat-value {
|
|
214
|
-
font-size: 1.75rem;
|
|
215
|
-
font-weight: 700;
|
|
216
|
-
line-height: 1.2;
|
|
217
|
-
color: var(--text);
|
|
218
|
-
letter-spacing: -0.02em;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
.stat-label {
|
|
222
|
-
font-size: 11px;
|
|
223
|
-
font-weight: 500;
|
|
224
|
-
text-transform: uppercase;
|
|
225
|
-
letter-spacing: 0.04em;
|
|
226
|
-
color: var(--text-muted);
|
|
227
|
-
margin-top: 0.25rem;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
.stat-panel-warn .stat-value { color: #f44; }
|
|
231
|
-
|
|
232
|
-
/* Score panel (cleanliness gauge) */
|
|
233
|
-
.panel-score .panel-body-center {
|
|
212
|
+
/* ——— Score panel (top-left: shield + caption + stats) ——— */
|
|
213
|
+
.panel-score .panel-body-score {
|
|
214
|
+
text-align: center;
|
|
234
215
|
padding: 1.25rem 1rem;
|
|
235
216
|
}
|
|
236
217
|
|
|
@@ -242,32 +223,105 @@ body.dashboard-page {
|
|
|
242
223
|
|
|
243
224
|
.panel-score .score-badge-svg {
|
|
244
225
|
display: block;
|
|
245
|
-
width:
|
|
226
|
+
width: 100px;
|
|
246
227
|
height: auto;
|
|
228
|
+
margin: 0 auto;
|
|
247
229
|
}
|
|
248
230
|
|
|
249
231
|
.panel-score .score-badge-svg .score-badge-num {
|
|
250
|
-
font-size:
|
|
232
|
+
font-size: 36px;
|
|
251
233
|
font-weight: 800;
|
|
252
234
|
letter-spacing: -0.02em;
|
|
253
235
|
}
|
|
254
236
|
|
|
255
237
|
.panel-score .score-badge-svg .score-badge-of {
|
|
256
|
-
font-size:
|
|
238
|
+
font-size: 11px;
|
|
257
239
|
font-weight: 700;
|
|
258
240
|
letter-spacing: 0.08em;
|
|
259
241
|
text-transform: uppercase;
|
|
260
242
|
opacity: 0.95;
|
|
261
243
|
}
|
|
262
244
|
|
|
245
|
+
.panel-score .score-label {
|
|
246
|
+
margin: 0 0 0.25rem;
|
|
247
|
+
font-size: 1rem;
|
|
248
|
+
font-weight: 600;
|
|
249
|
+
color: var(--text);
|
|
250
|
+
}
|
|
251
|
+
|
|
263
252
|
.panel-score .score-desc {
|
|
253
|
+
margin: 0 0 0.5rem;
|
|
264
254
|
font-size: 13px;
|
|
265
255
|
color: var(--text-muted);
|
|
266
|
-
|
|
267
|
-
max-width:
|
|
256
|
+
line-height: 1.45;
|
|
257
|
+
max-width: 280px;
|
|
268
258
|
margin-left: auto;
|
|
269
259
|
margin-right: auto;
|
|
270
|
-
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.panel-score .score-stats {
|
|
263
|
+
margin: 0;
|
|
264
|
+
font-size: 11px;
|
|
265
|
+
color: var(--text-muted);
|
|
266
|
+
text-transform: uppercase;
|
|
267
|
+
letter-spacing: 0.03em;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/* ——— Description of problems panel (top-right) ——— */
|
|
271
|
+
.panel-empty {
|
|
272
|
+
margin: 0;
|
|
273
|
+
font-size: 13px;
|
|
274
|
+
color: var(--text-muted);
|
|
275
|
+
font-style: italic;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.priority-inline {
|
|
279
|
+
display: grid;
|
|
280
|
+
grid-template-columns: 1fr 1fr;
|
|
281
|
+
gap: 1rem;
|
|
282
|
+
margin-top: 1rem;
|
|
283
|
+
padding-top: 1rem;
|
|
284
|
+
border-top: 1px solid var(--border-subtle);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.priority-inline h4 {
|
|
288
|
+
margin: 0 0 0.5rem;
|
|
289
|
+
font-size: 11px;
|
|
290
|
+
font-weight: 600;
|
|
291
|
+
text-transform: uppercase;
|
|
292
|
+
letter-spacing: 0.04em;
|
|
293
|
+
color: var(--text-muted);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.priority-inline .priority-list { margin: 0; }
|
|
297
|
+
|
|
298
|
+
@media (max-width: 600px) {
|
|
299
|
+
.priority-inline { grid-template-columns: 1fr; }
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/* ——— Heatmap panel (bottom-left) ——— */
|
|
303
|
+
.panel-heatmap .panel-header-heatmap {
|
|
304
|
+
display: flex;
|
|
305
|
+
flex-wrap: wrap;
|
|
306
|
+
align-items: baseline;
|
|
307
|
+
gap: 0.75rem 1.25rem;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.panel-heatmap .panel-title { margin-right: 0.5rem; }
|
|
311
|
+
.panel-heatmap .panel-desc { margin: 0.15rem 0 0; flex-basis: 100%; }
|
|
312
|
+
|
|
313
|
+
.legend-inline {
|
|
314
|
+
margin: 0;
|
|
315
|
+
margin-left: auto;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.panel-body-heatmap {
|
|
319
|
+
padding: 1rem;
|
|
320
|
+
padding-top: 0.5rem;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.panel-body-heatmap #treemap {
|
|
324
|
+
border-radius: var(--radius);
|
|
271
325
|
}
|
|
272
326
|
|
|
273
327
|
/* LLM panel accent */
|
|
@@ -379,8 +433,18 @@ body.dashboard-page {
|
|
|
379
433
|
|
|
380
434
|
.debt-list .title { font-weight: 600; margin-bottom: 0.2rem; }
|
|
381
435
|
.debt-list .meta { font-size: 12px; color: var(--text-muted); display: block; margin-top: 0.25rem; font-family: ui-monospace, monospace; }
|
|
436
|
+
.debt-list-explanation {
|
|
437
|
+
font-size: 12px;
|
|
438
|
+
color: var(--text-muted);
|
|
439
|
+
margin: 0.35rem 0 0;
|
|
440
|
+
line-height: 1.4;
|
|
441
|
+
display: -webkit-box;
|
|
442
|
+
-webkit-line-clamp: 2;
|
|
443
|
+
-webkit-box-orient: vertical;
|
|
444
|
+
overflow: hidden;
|
|
445
|
+
}
|
|
382
446
|
.debt-list .insight { font-size: 13px; color: var(--text-muted); margin-top: 0.25rem; line-height: 1.4; white-space: pre-wrap; word-break: break-word; }
|
|
383
|
-
.debt-list-ratings { display: flex; align-items: center; gap: 1rem; margin: 0.25rem 0; flex-wrap: wrap; }
|
|
447
|
+
.debt-list-ratings { display: flex; align-items: center; gap: 1rem; margin: 0.25rem 0 0; flex-wrap: wrap; }
|
|
384
448
|
.debt-list-rating { display: inline-flex; align-items: center; gap: 0.35rem; }
|
|
385
449
|
.debt-list-rating-label { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-muted); }
|
|
386
450
|
.debt-list-llm-none { color: var(--text-muted); font-size: 12px; }
|
|
@@ -166,44 +166,81 @@ document.getElementById("q2").innerHTML = highImpact
|
|
|
166
166
|
.map(function (d) { return '<li style="font-size:0.8rem">' + escapeHtml(d.file) + "</li>"; })
|
|
167
167
|
.join("");
|
|
168
168
|
|
|
169
|
-
// Debt list:
|
|
170
|
-
var sev = { critical: 4, high: 3, medium: 2, low: 1 };
|
|
169
|
+
// Debt list: every file rated above none by static OR LLM; show both ratings and short explanation
|
|
170
|
+
var sev = { critical: 4, high: 3, medium: 2, low: 1, none: 0 };
|
|
171
171
|
var list = document.getElementById("debtList");
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
172
|
+
|
|
173
|
+
function severityNum(s) { return sev[s] || 0; }
|
|
174
|
+
function fileWorstStatic(items) {
|
|
175
|
+
if (!items || !items.length) return 0;
|
|
176
|
+
return Math.max.apply(null, items.map(function (d) { return severityNum(d.severity); }));
|
|
177
|
+
}
|
|
178
|
+
function fileWorstLlm(metric) {
|
|
179
|
+
if (!metric || !metric.llmSeverity || metric.llmSeverity === "none") return 0;
|
|
180
|
+
return severityNum(metric.llmSeverity);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
var filesFromStatic = new Set(debtByFile.keys());
|
|
184
|
+
var filesFromLlm = new Set();
|
|
185
|
+
DATA.fileMetrics.forEach(function (m) {
|
|
186
|
+
if (m.llmSeverity && m.llmSeverity !== "none") filesFromLlm.add(m.file);
|
|
187
|
+
});
|
|
188
|
+
var fileSet = new Set([].concat(Array.from(filesFromStatic), Array.from(filesFromLlm)));
|
|
189
|
+
|
|
190
|
+
var filesWithDebt = Array.from(fileSet).sort(function (fa, fb) {
|
|
191
|
+
var itemsA = debtByFile.get(fa) || [];
|
|
192
|
+
var itemsB = debtByFile.get(fb) || [];
|
|
193
|
+
var metricA = DATA.fileMetrics.find(function (m) { return m.file === fa; });
|
|
194
|
+
var metricB = DATA.fileMetrics.find(function (m) { return m.file === fb; });
|
|
195
|
+
var worstA = Math.max(fileWorstStatic(itemsA), fileWorstLlm(metricA));
|
|
196
|
+
var worstB = Math.max(fileWorstStatic(itemsB), fileWorstLlm(metricB));
|
|
177
197
|
if (worstB !== worstA) return worstB - worstA;
|
|
178
198
|
return fa.localeCompare(fb);
|
|
179
199
|
});
|
|
200
|
+
|
|
201
|
+
function firstLine(text) {
|
|
202
|
+
if (!text || !String(text).trim()) return "";
|
|
203
|
+
return String(text).trim().split(/\n/)[0].trim().slice(0, 120);
|
|
204
|
+
}
|
|
205
|
+
|
|
180
206
|
filesWithDebt.forEach(function (file) {
|
|
181
|
-
var items = debtByFile.get(file);
|
|
207
|
+
var items = debtByFile.get(file) || [];
|
|
208
|
+
var fileM = DATA.fileMetrics.find(function (m) { return m.file === file; });
|
|
182
209
|
var worstSeverityVal = items.length ? items.reduce(function (best, d) {
|
|
183
|
-
return (
|
|
210
|
+
return severityNum(d.severity) > severityNum(best.severity) ? d : best;
|
|
184
211
|
}, items[0]) : null;
|
|
185
|
-
var staticSeverity = worstSeverityVal ? worstSeverityVal.severity :
|
|
186
|
-
var
|
|
187
|
-
|
|
188
|
-
var staticBadge =
|
|
189
|
-
|
|
190
|
-
? '<span class="badge badge-' + fileLlmSeverity + '" title="LLM rating for this file">' + fileLlmSeverity + "</span>"
|
|
212
|
+
var staticSeverity = worstSeverityVal ? worstSeverityVal.severity : null;
|
|
213
|
+
var fileLlmSeverity = fileM && fileM.llmSeverity && fileM.llmSeverity !== "none" ? fileM.llmSeverity : null;
|
|
214
|
+
|
|
215
|
+
var staticBadge = staticSeverity
|
|
216
|
+
? '<span class="badge badge-' + staticSeverity + '" title="Static (worst of ' + items.length + ')">' + staticSeverity + "</span>"
|
|
191
217
|
: '<span class="debt-list-llm-none">—</span>';
|
|
192
|
-
var
|
|
193
|
-
?
|
|
194
|
-
:
|
|
218
|
+
var llmBadge = fileLlmSeverity
|
|
219
|
+
? '<span class="badge badge-' + fileLlmSeverity + '" title="LLM">' + fileLlmSeverity + "</span>"
|
|
220
|
+
: '<span class="debt-list-llm-none">—</span>';
|
|
221
|
+
|
|
222
|
+
var explanation = "";
|
|
223
|
+
if (items.length && worstSeverityVal) {
|
|
224
|
+
explanation = escapeHtml(firstLine(worstSeverityVal.title || worstSeverityVal.description || ""));
|
|
225
|
+
if (items.length > 1) explanation += " (+" + (items.length - 1) + " more)";
|
|
226
|
+
}
|
|
227
|
+
if (fileM && (fileM.llmAssessment || fileM.llmRawAssessment)) {
|
|
228
|
+
var llmBlurb = firstLine(stripTrailingSeverityAndScore(fileM.llmRawAssessment || fileM.llmAssessment || ""));
|
|
229
|
+
if (llmBlurb) explanation = explanation ? explanation + " · " + escapeHtml(llmBlurb) : escapeHtml(llmBlurb);
|
|
230
|
+
}
|
|
231
|
+
if (!explanation) explanation = "Rated by static or LLM.";
|
|
232
|
+
|
|
195
233
|
var ratingsRow =
|
|
196
234
|
'<div class="debt-list-ratings">' +
|
|
197
235
|
'<span class="debt-list-rating"><span class="debt-list-rating-label">Static</span> ' + staticBadge + "</span>" +
|
|
198
|
-
'<span class="debt-list-rating"><span class="debt-list-rating-label">LLM</span> ' +
|
|
236
|
+
'<span class="debt-list-rating"><span class="debt-list-rating-label">LLM</span> ' + llmBadge + "</span>" +
|
|
199
237
|
"</div>";
|
|
200
238
|
var li = document.createElement("li");
|
|
201
239
|
li.innerHTML =
|
|
202
|
-
'<span class="title">' +
|
|
203
|
-
escapeHtml(titleText) +
|
|
204
|
-
"</span> " +
|
|
240
|
+
'<span class="title">' + escapeHtml(file.split("/").pop() || file) + "</span> " +
|
|
205
241
|
ratingsRow +
|
|
206
|
-
'<span class="meta">' + escapeHtml(file) + "</span>"
|
|
242
|
+
'<span class="meta">' + escapeHtml(file) + "</span>" +
|
|
243
|
+
'<p class="debt-list-explanation">' + explanation + "</p>";
|
|
207
244
|
li.addEventListener("click", function () { showDetail(file, items); });
|
|
208
245
|
list.appendChild(li);
|
|
209
246
|
});
|
|
@@ -212,35 +249,44 @@ function showDetail(file, items) {
|
|
|
212
249
|
var panel = document.getElementById("detail");
|
|
213
250
|
var fileMetric = DATA.fileMetrics.find(function (m) { return m.file === file; });
|
|
214
251
|
var worstItem = items.length ? items.reduce(function (best, d) {
|
|
215
|
-
return (
|
|
252
|
+
return severityNum(d.severity) > severityNum(best.severity) ? d : best;
|
|
216
253
|
}, items[0]) : null;
|
|
217
|
-
var staticSeverity = worstItem ? worstItem.severity :
|
|
254
|
+
var staticSeverity = worstItem ? worstItem.severity : null;
|
|
218
255
|
|
|
219
|
-
|
|
256
|
+
var titleText = items.length === 1
|
|
220
257
|
? (items[0].title || "Debt item")
|
|
221
|
-
: items.length
|
|
258
|
+
: items.length > 1
|
|
259
|
+
? items.length + " static issues"
|
|
260
|
+
: (fileMetric && fileMetric.llmSeverity ? "LLM assessment" : "File details");
|
|
261
|
+
document.getElementById("detailTitle").textContent = titleText;
|
|
222
262
|
document.getElementById("detailFile").textContent = file;
|
|
223
263
|
|
|
224
264
|
var explanationEl = document.getElementById("detailExplanation");
|
|
225
265
|
var parts = [];
|
|
226
266
|
parts.push(
|
|
227
267
|
'<div class="detail-severities">' +
|
|
228
|
-
'<span class="detail-sev"><strong>Static</strong>
|
|
268
|
+
'<span class="detail-sev"><strong>Static</strong> ' +
|
|
269
|
+
(staticSeverity ? '<span class="badge badge-' + staticSeverity + '">' + staticSeverity + "</span> (worst of " + items.length + ")" : "<span class=\"debt-list-llm-none\">\u2014</span>") +
|
|
270
|
+
"</span> " +
|
|
229
271
|
'<span class="detail-sev"><strong>LLM</strong> ' +
|
|
230
|
-
(fileMetric && fileMetric.llmSeverity ? '<span class="badge badge-' + fileMetric.llmSeverity + '">' + fileMetric.llmSeverity + "</span>" : "<span class=\"debt-list-llm-none\"
|
|
272
|
+
(fileMetric && fileMetric.llmSeverity && fileMetric.llmSeverity !== "none" ? '<span class="badge badge-' + fileMetric.llmSeverity + '">' + fileMetric.llmSeverity + "</span>" : "<span class=\"debt-list-llm-none\">\u2014</span>") +
|
|
231
273
|
"</span></div>"
|
|
232
274
|
);
|
|
233
275
|
parts.push('<div class="detail-static-desc"><strong>Static issues</strong><ul class="detail-issues-list">');
|
|
234
|
-
items.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
276
|
+
if (items.length) {
|
|
277
|
+
items.forEach(function (item) {
|
|
278
|
+
parts.push(
|
|
279
|
+
'<li><span class="badge badge-' + item.severity + '">' + item.severity + "</span> " +
|
|
280
|
+
escapeHtml(item.title || "Issue") +
|
|
281
|
+
(item.line ? " <span class=\"detail-line\">line " + item.line + "</span>" : "")
|
|
282
|
+
);
|
|
283
|
+
if (item.description)
|
|
284
|
+
parts.push('<div class="detail-issue-desc">' + escapeHtml(item.description).replace(/\n/g, "<br>") + "</div>");
|
|
285
|
+
parts.push("</li>");
|
|
286
|
+
});
|
|
287
|
+
} else {
|
|
288
|
+
parts.push("<li class=\"detail-no-llm\">No static issues for this file.</li>");
|
|
289
|
+
}
|
|
244
290
|
parts.push("</ul></div>");
|
|
245
291
|
parts.push('<div class="detail-llm-label"><strong>LLM assessment</strong></div>');
|
|
246
292
|
if (fileMetric && (fileMetric.llmRawAssessment || fileMetric.llmAssessment)) {
|
package/dist/reports/html.js
CHANGED
|
@@ -78,21 +78,7 @@ function buildHtml(run, title, darkMode, css, script) {
|
|
|
78
78
|
};
|
|
79
79
|
const tierColor = tierColors[cleanliness.tier] ?? "#666";
|
|
80
80
|
const scoreBadgeSvg = buildScoreBadgeSvg(cleanliness.tier, tierColor);
|
|
81
|
-
const
|
|
82
|
-
? `
|
|
83
|
-
<div class="panel panel-llm">
|
|
84
|
-
<div class="panel-header">
|
|
85
|
-
<h2 class="panel-title">LLM overall assessment</h2>
|
|
86
|
-
</div>
|
|
87
|
-
<div class="panel-body">
|
|
88
|
-
<div class="llm-output">${run.llmOverallAssessment
|
|
89
|
-
? renderLlmOutputToHtml(run.llmOverallAssessment)
|
|
90
|
-
: '<div class="llm-prose">' +
|
|
91
|
-
escapeHtml(stripTrailingSeverityAndScore(run.llmOverallRaw ?? "")).replace(/\n/g, "<br>") +
|
|
92
|
-
"</div>"}</div>
|
|
93
|
-
</div>
|
|
94
|
-
</div>`
|
|
95
|
-
: "";
|
|
81
|
+
const statsLine = `${run.fileMetrics.length} files · ${run.debtItems.length} items · ${highCriticalCount} high/crit · ${hotspotCount} hotspots`;
|
|
96
82
|
return `<!DOCTYPE html>
|
|
97
83
|
<html lang="en" data-theme="${theme}">
|
|
98
84
|
<head>
|
|
@@ -125,92 +111,77 @@ function buildHtml(run, title, darkMode, css, script) {
|
|
|
125
111
|
</header>
|
|
126
112
|
|
|
127
113
|
<main class="dashboard-main">
|
|
128
|
-
<div class="dashboard-grid
|
|
129
|
-
<div class="
|
|
130
|
-
<div class="panel-
|
|
131
|
-
<div class="
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
114
|
+
<div class="dashboard-grid-2x2">
|
|
115
|
+
<div class="dashboard-cell dashboard-cell-score">
|
|
116
|
+
<div class="panel panel-score tier-${cleanliness.tier}">
|
|
117
|
+
<div class="panel-header">
|
|
118
|
+
<h2 class="panel-title">Technical Debt Cleanliness Score</h2>
|
|
119
|
+
</div>
|
|
120
|
+
<div class="panel-body panel-body-score">
|
|
121
|
+
<div class="score-badge" aria-hidden="true">${scoreBadgeSvg}</div>
|
|
122
|
+
<p class="score-label">${escapeHtml(cleanliness.label)}</p>
|
|
123
|
+
<p class="score-desc">${escapeHtml(cleanliness.description)}</p>
|
|
124
|
+
<p class="score-stats">${statsLine}</p>
|
|
125
|
+
</div>
|
|
139
126
|
</div>
|
|
140
127
|
</div>
|
|
141
|
-
<div class="panel stat-panel stat-panel-warn">
|
|
142
|
-
<div class="panel-body">
|
|
143
|
-
<div class="stat-value">${highCriticalCount}</div>
|
|
144
|
-
<div class="stat-label">High / Critical</div>
|
|
145
|
-
</div>
|
|
146
|
-
</div>
|
|
147
|
-
<div class="panel stat-panel">
|
|
148
|
-
<div class="panel-body">
|
|
149
|
-
<div class="stat-value">${hotspotCount}</div>
|
|
150
|
-
<div class="stat-label">Hotspots</div>
|
|
151
|
-
</div>
|
|
152
|
-
</div>
|
|
153
|
-
</div>
|
|
154
128
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
129
|
+
<div class="dashboard-cell dashboard-cell-problems">
|
|
130
|
+
<div class="panel panel-llm">
|
|
131
|
+
<div class="panel-header">
|
|
132
|
+
<h2 class="panel-title">Description of problems</h2>
|
|
133
|
+
</div>
|
|
134
|
+
<div class="panel-body">
|
|
135
|
+
${run.llmOverallAssessment || run.llmOverallRaw
|
|
136
|
+
? `<div class="llm-output">${run.llmOverallAssessment
|
|
137
|
+
? renderLlmOutputToHtml(run.llmOverallAssessment)
|
|
138
|
+
: '<div class="llm-prose">' +
|
|
139
|
+
escapeHtml(stripTrailingSeverityAndScore(run.llmOverallRaw ?? "")).replace(/\n/g, "<br>") +
|
|
140
|
+
"</div>"}</div>`
|
|
141
|
+
: '<p class="panel-empty">Run with LLM for an overall assessment of problems.</p>'}
|
|
142
|
+
<div class="priority-inline">
|
|
143
|
+
<div class="priority-inline-col">
|
|
144
|
+
<h4>High impact, easier</h4>
|
|
145
|
+
<ul id="q1" class="priority-list"></ul>
|
|
146
|
+
</div>
|
|
147
|
+
<div class="priority-inline-col">
|
|
148
|
+
<h4>High impact, harder</h4>
|
|
149
|
+
<ul id="q2" class="priority-list"></ul>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
163
153
|
</div>
|
|
164
154
|
</div>
|
|
165
|
-
</div>
|
|
166
155
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
<
|
|
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>
|
|
181
172
|
</div>
|
|
182
|
-
<div id="treemap"></div>
|
|
183
173
|
</div>
|
|
184
|
-
</div>
|
|
185
174
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
175
|
+
<div class="dashboard-cell dashboard-cell-list">
|
|
176
|
+
<div class="panel">
|
|
177
|
+
<div class="panel-header">
|
|
178
|
+
<h2 class="panel-title">Ratings & files</h2>
|
|
179
|
+
<p class="panel-desc">Files rated above none by static or LLM. Click for full details.</p>
|
|
180
|
+
</div>
|
|
181
|
+
<div class="panel-body">
|
|
182
|
+
<ul class="debt-list" id="debtList"></ul>
|
|
183
|
+
</div>
|
|
191
184
|
</div>
|
|
192
|
-
<div class="panel-body">
|
|
193
|
-
<ul id="q1" class="priority-list"></ul>
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
196
|
-
<div class="panel">
|
|
197
|
-
<div class="panel-header">
|
|
198
|
-
<h2 class="panel-title">High impact, harder</h2>
|
|
199
|
-
<p class="panel-desc">Critical or hotspot files.</p>
|
|
200
|
-
</div>
|
|
201
|
-
<div class="panel-body">
|
|
202
|
-
<ul id="q2" class="priority-list"></ul>
|
|
203
|
-
</div>
|
|
204
|
-
</div>
|
|
205
|
-
</div>
|
|
206
|
-
|
|
207
|
-
<div class="panel">
|
|
208
|
-
<div class="panel-header">
|
|
209
|
-
<h2 class="panel-title">All debt items</h2>
|
|
210
|
-
<p class="panel-desc">Static and LLM ratings. Click a row for details.</p>
|
|
211
|
-
</div>
|
|
212
|
-
<div class="panel-body">
|
|
213
|
-
<ul class="debt-list" id="debtList"></ul>
|
|
214
185
|
</div>
|
|
215
186
|
</div>
|
|
216
187
|
</main>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tech-debt-visualizer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
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",
|