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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/template.ts +45 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-realreview",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Multi-model code review for pi: independent reviewers, an adjudicating synthesizer that removes false positives, and an HTML report",
5
5
  "keywords": [
6
6
  "pi-package",
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 (with notes) and copies them as Markdown. Data comes from #rr-data. */
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 (note) lines.push(" Note: " + indent(note));
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
- "</span></td><td>" +
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");