pi-realreview 0.1.0 → 0.1.1
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/package.json +1 -1
- package/src/template.ts +45 -6
package/package.json
CHANGED
package/src/template.ts
CHANGED
|
@@ -63,6 +63,8 @@ summary { cursor:pointer; font-weight:600; }
|
|
|
63
63
|
.banner { padding:12px 16px; margin:0 0 18px; border:1px solid #efdfbf; background:#fbf2e2; color:var(--warn); border-radius:8px; font-size:13px; }
|
|
64
64
|
footer { border-top:1px solid var(--line); margin-top:22px; padding-top:18px; font-size:12px; color:var(--muted); }
|
|
65
65
|
th.pick,td.pick { width:34px; padding-right:0; }
|
|
66
|
+
th.id,td.id { width:1%; white-space:nowrap; font-weight:700; }
|
|
67
|
+
td.id .muted { font-weight:500; }
|
|
66
68
|
td.pick input { width:16px; height:16px; margin:3px 0 0; cursor:pointer; accent-color:var(--accent); }
|
|
67
69
|
tr.picked td { background:#f6f8fc; }
|
|
68
70
|
textarea.note { display:block; width:100%; min-height:54px; margin-top:8px; padding:6px 8px; font:inherit; font-size:12.5px; color:var(--ink); background:var(--paper); border:1px solid var(--line); border-radius:6px; resize:vertical; }
|
|
@@ -82,7 +84,7 @@ body.has-tray .shell { padding-bottom:110px; }
|
|
|
82
84
|
@media print { body{background:#fff} nav,.tray,.pick,textarea.note:disabled{display:none} .layout{display:block} .section{border:0;padding:10px 0} .finding{break-inside:avoid} }
|
|
83
85
|
`;
|
|
84
86
|
|
|
85
|
-
/** Collects checked reconciliation rows
|
|
87
|
+
/** Collects checked reconciliation rows and copies them as Markdown: the candidate, its finding when one exists, and any note. Data comes from #rr-data. */
|
|
86
88
|
const SELECTION_SCRIPT = `(function () {
|
|
87
89
|
var data = JSON.parse(document.getElementById("rr-data").textContent);
|
|
88
90
|
var tray = document.getElementById("rr-tray");
|
|
@@ -115,16 +117,36 @@ const SELECTION_SCRIPT = `(function () {
|
|
|
115
117
|
return text.trim().split("\\n").join("\\n ");
|
|
116
118
|
}
|
|
117
119
|
|
|
120
|
+
function pushBlock(lines, text) {
|
|
121
|
+
text.trim().split("\\n").forEach(function (line) {
|
|
122
|
+
lines.push(line ? " " + line : "");
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
118
126
|
function toMarkdown() {
|
|
119
127
|
var lines = ["## Selected review items (" + data.repo + " · " + data.scope + ")", ""];
|
|
120
128
|
picked().forEach(function (row, index) {
|
|
121
129
|
var item = data.rows[Number(row.dataset.rec)];
|
|
122
130
|
var note = row.querySelector("textarea").value.trim();
|
|
131
|
+
var finding = item.finding;
|
|
123
132
|
lines.push((index + 1) + ". **" + item.candidate + "** — " + item.decision + " (raised by " + item.raisedBy + ")");
|
|
124
133
|
lines.push(" Reasoning: " + indent(item.reasoning));
|
|
125
|
-
if (
|
|
134
|
+
if (finding) {
|
|
135
|
+
lines.push("");
|
|
136
|
+
lines.push(" **" + finding.id + " [" + finding.severity + "]** " + finding.title);
|
|
137
|
+
if (finding.location) lines.push(" **Where:** " + indent(finding.location));
|
|
138
|
+
if (finding.body && finding.body.trim()) {
|
|
139
|
+
lines.push("");
|
|
140
|
+
pushBlock(lines, finding.body);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (note) {
|
|
144
|
+
lines.push("");
|
|
145
|
+
lines.push(" **Note:** " + indent(note));
|
|
146
|
+
}
|
|
147
|
+
lines.push("");
|
|
126
148
|
});
|
|
127
|
-
return lines.join("\\n") + "\\n";
|
|
149
|
+
return lines.join("\\n").replace(/\\n+$/, "") + "\\n";
|
|
128
150
|
}
|
|
129
151
|
|
|
130
152
|
function fallbackCopy(text) {
|
|
@@ -356,6 +378,9 @@ function reconciliationSection(report: ReportData, reviewers: ReportMeta["review
|
|
|
356
378
|
const raisedByHtml = raisedBy
|
|
357
379
|
.map(part => ("reviewer" in part ? '<a href="#' + notesId(part.reviewer) + '">' + escapeHtml(part.text) + "</a>" : renderInline(part.text)))
|
|
358
380
|
.join("");
|
|
381
|
+
const findingId = finding
|
|
382
|
+
? '<a href="#' + escapeHtml(finding) + '" title="Jump to ' + escapeHtml(finding) + '">' + escapeHtml(finding) + "</a>"
|
|
383
|
+
: '<span class="muted">—</span>';
|
|
359
384
|
return (
|
|
360
385
|
'<tr id="' +
|
|
361
386
|
recId(index) +
|
|
@@ -371,7 +396,9 @@ function reconciliationSection(report: ReportData, reviewers: ReportMeta["review
|
|
|
371
396
|
decisionClass(row.decision) +
|
|
372
397
|
'">' +
|
|
373
398
|
escapeHtml(row.decision) +
|
|
374
|
-
|
|
399
|
+
'</span></td><td class="id">' +
|
|
400
|
+
findingId +
|
|
401
|
+
"</td><td>" +
|
|
375
402
|
renderInline(row.reasoning) +
|
|
376
403
|
'<textarea class="note" disabled placeholder="Check the row to add a note" aria-label="Note for ' +
|
|
377
404
|
escapeHtml(row.candidate) +
|
|
@@ -380,7 +407,7 @@ function reconciliationSection(report: ReportData, reviewers: ReportMeta["review
|
|
|
380
407
|
})
|
|
381
408
|
.join("");
|
|
382
409
|
return (
|
|
383
|
-
'<div class="table-wrap"><table><thead><tr><th class="pick"></th><th>Candidate</th><th>Raised by</th><th>Decision</th><th>Reasoning</th></tr></thead><tbody>' +
|
|
410
|
+
'<div class="table-wrap"><table><thead><tr><th class="pick"></th><th>Candidate</th><th>Raised by</th><th>Decision</th><th class="id">ID</th><th>Reasoning</th></tr></thead><tbody>' +
|
|
384
411
|
rows +
|
|
385
412
|
"</tbody></table></div>"
|
|
386
413
|
);
|
|
@@ -389,10 +416,22 @@ function reconciliationSection(report: ReportData, reviewers: ReportMeta["review
|
|
|
389
416
|
/** The fixed tray, the row data it copies, and the script that wires them up. Empty when nothing can be selected. */
|
|
390
417
|
function selectionTray(meta: ReportMeta, report: ReportData | undefined) {
|
|
391
418
|
if (!report || report.reconciliation.length === 0) return "";
|
|
419
|
+
const findingIds = new Set(report.findings.map(finding => finding.id));
|
|
420
|
+
const findingsById = new Map(report.findings.map(finding => [finding.id, finding]));
|
|
392
421
|
const data = {
|
|
393
422
|
repo: meta.repo,
|
|
394
423
|
scope: meta.scopeLabel,
|
|
395
|
-
rows: report.reconciliation
|
|
424
|
+
rows: report.reconciliation.map(row => {
|
|
425
|
+
const id = rowFinding(row, findingIds);
|
|
426
|
+
const finding = id ? findingsById.get(id) : undefined;
|
|
427
|
+
return {
|
|
428
|
+
candidate: row.candidate,
|
|
429
|
+
raisedBy: row.raisedBy,
|
|
430
|
+
decision: row.decision,
|
|
431
|
+
reasoning: row.reasoning,
|
|
432
|
+
finding: finding ? { id: finding.id, severity: finding.severity, title: finding.title, location: finding.location, body: finding.body } : null,
|
|
433
|
+
};
|
|
434
|
+
}),
|
|
396
435
|
};
|
|
397
436
|
// Escaping "<" keeps "</script>" inside a string from closing the tag.
|
|
398
437
|
const json = JSON.stringify(data).replace(/</g, "\\u003c");
|