pi-diff-review 0.1.16 → 0.1.17
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/README.md +1 -0
- package/package.json +1 -1
- package/src/review/component.ts +163 -9
package/README.md
CHANGED
|
@@ -62,6 +62,7 @@ Review a branch or commit range by passing any `git diff` arguments after `/diff
|
|
|
62
62
|
- `/diff --cached` reviews staged changes
|
|
63
63
|
- `/diff main...HEAD` reviews changes on the current branch relative to `main`
|
|
64
64
|
- `/diff <git-diff-args>` passes arguments through to `git diff`
|
|
65
|
+
- `h` toggles the command help modal
|
|
65
66
|
- `j/k` or arrow keys to move
|
|
66
67
|
- `g/G` to jump to the top or bottom of the diff
|
|
67
68
|
- `ctrl-u` / `ctrl-d` to move up/down by half a page
|
package/package.json
CHANGED
package/src/review/component.ts
CHANGED
|
@@ -51,11 +51,32 @@ type AnnotatedDiffRow =
|
|
|
51
51
|
| { kind: "split"; splitRowIndex: number }
|
|
52
52
|
| InlineBoxRow;
|
|
53
53
|
|
|
54
|
+
const HELP_COMMANDS = [
|
|
55
|
+
["h", "show or hide this help"],
|
|
56
|
+
["j/k or arrows", "move selection"],
|
|
57
|
+
["ctrl-u / ctrl-d", "move up or down half a page"],
|
|
58
|
+
["g / G", "jump to top or bottom"],
|
|
59
|
+
["n / p", "jump to next or previous hunk"],
|
|
60
|
+
["/", "search diff lines"],
|
|
61
|
+
["n / N", "jump between search matches"],
|
|
62
|
+
["J / K", "extend highlighted selection"],
|
|
63
|
+
["c", "add or edit a line or range comment"],
|
|
64
|
+
["C", "add or edit an overall diff comment"],
|
|
65
|
+
["x", "delete the current line or range comment"],
|
|
66
|
+
["t", "toggle inline comments and explanations"],
|
|
67
|
+
["v", "toggle unified or split rendering"],
|
|
68
|
+
["?", "toggle AI explanation for current hunk"],
|
|
69
|
+
["Enter", "submit comments, save edits, or jump to search result"],
|
|
70
|
+
["Esc", "close help, cancel search/edit, clear selection, or exit"],
|
|
71
|
+
["q", "exit review"],
|
|
72
|
+
] as const;
|
|
73
|
+
|
|
54
74
|
export class ReviewComponent {
|
|
55
75
|
private navigation: ReviewNavigationState;
|
|
56
76
|
private editMode = false;
|
|
57
77
|
private editingCommentKey?: string;
|
|
58
78
|
private search: ReviewSearchState;
|
|
79
|
+
private helpVisible = false;
|
|
59
80
|
|
|
60
81
|
private inlineAnnotationsVisible = true;
|
|
61
82
|
private visibleExplanationKeys = new Set<string>();
|
|
@@ -171,6 +192,13 @@ export class ReviewComponent {
|
|
|
171
192
|
}
|
|
172
193
|
|
|
173
194
|
handleInput(data: string): void {
|
|
195
|
+
if (this.helpVisible) {
|
|
196
|
+
if (data === "h" || matchesKey(data, "escape")) {
|
|
197
|
+
this.toggleHelp();
|
|
198
|
+
}
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
174
202
|
if (this.editMode) {
|
|
175
203
|
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
176
204
|
this.exitEditMode();
|
|
@@ -201,6 +229,10 @@ export class ReviewComponent {
|
|
|
201
229
|
this.done({ action: "cancel" });
|
|
202
230
|
return;
|
|
203
231
|
}
|
|
232
|
+
if (data === "h") {
|
|
233
|
+
this.toggleHelp();
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
204
236
|
if (data === "t") {
|
|
205
237
|
this.toggleInlineAnnotations();
|
|
206
238
|
return;
|
|
@@ -292,15 +324,9 @@ export class ReviewComponent {
|
|
|
292
324
|
const output: string[] = [];
|
|
293
325
|
|
|
294
326
|
output.push(
|
|
295
|
-
|
|
296
|
-
this.
|
|
297
|
-
|
|
298
|
-
this.editMode
|
|
299
|
-
? `${this.title} • ${this.lines.length} lines • ${this.comments.size} comments • editing inline comment • Enter save • Esc/Ctrl+C cancel`
|
|
300
|
-
: this.hasSelection()
|
|
301
|
-
? `${this.title} • ${this.lines.length} lines • ${this.comments.size} comments • J/K extend • Esc clear selection • c comment range • C overall comment • Enter submit`
|
|
302
|
-
: `${this.title} • ${this.lines.length} lines • ${this.comments.size} comments • ${this.getPositionText(selectedLine)} • inline ${this.inlineAnnotationsVisible ? "shown" : "hidden"} • j/k move • g/G top/bottom • ctrl-u/d page • / search • t annotations • v unified/split • ? explain • J/K extend • c comment • C overall • x delete • ${this.search.query ? "n/N search" : "n/p hunk"} • Enter submit • q quit`,
|
|
303
|
-
),
|
|
327
|
+
this.renderStatusLine(
|
|
328
|
+
this.getHeaderText(selectedLine),
|
|
329
|
+
"Press h for help",
|
|
304
330
|
width,
|
|
305
331
|
),
|
|
306
332
|
);
|
|
@@ -314,9 +340,132 @@ export class ReviewComponent {
|
|
|
314
340
|
width,
|
|
315
341
|
),
|
|
316
342
|
);
|
|
343
|
+
return this.helpVisible ? this.renderHelpModal(output, width) : output;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private getHeaderText(selectedLine?: ReviewLine): string {
|
|
347
|
+
const base = `${this.title} • ${this.lines.length} lines • ${this.comments.size} comments`;
|
|
348
|
+
|
|
349
|
+
if (this.editMode) {
|
|
350
|
+
return `${base} • editing ${this.editingCommentKey === GLOBAL_COMMENT_KEY ? "overall comment" : "inline comment"}`;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (this.hasSelection()) {
|
|
354
|
+
return `${base} • selection active`;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return `${base} • ${this.getPositionText(selectedLine)} • inline ${this.inlineAnnotationsVisible ? "shown" : "hidden"} • ${this.diffRenderMode}`;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
private renderStatusLine(left: string, right: string, width: number): string {
|
|
361
|
+
const styledLeft = this.theme.fg("dim", left);
|
|
362
|
+
const styledRight = this.theme.fg("muted", right);
|
|
363
|
+
const rightWidth = visibleWidth(styledRight);
|
|
364
|
+
const leftWidth = Math.max(0, width - rightWidth - 1);
|
|
365
|
+
const truncatedLeft = truncateToWidth(styledLeft, leftWidth);
|
|
366
|
+
const spacer = Math.max(
|
|
367
|
+
1,
|
|
368
|
+
width - visibleWidth(truncatedLeft) - rightWidth,
|
|
369
|
+
);
|
|
370
|
+
return truncateToWidth(
|
|
371
|
+
`${truncatedLeft}${" ".repeat(spacer)}${styledRight}`,
|
|
372
|
+
width,
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
private renderHelpModal(rows: string[], width: number): string[] {
|
|
377
|
+
if (rows.length === 0 || width < 8) return rows;
|
|
378
|
+
|
|
379
|
+
const modalWidth = Math.max(8, Math.min(72, width - 2));
|
|
380
|
+
const contentWidth = Math.max(4, modalWidth - 4);
|
|
381
|
+
const keyWidth = Math.min(16, Math.max(8, Math.floor(contentWidth * 0.35)));
|
|
382
|
+
const modalRows = this.buildHelpModalRows(
|
|
383
|
+
modalWidth,
|
|
384
|
+
contentWidth,
|
|
385
|
+
keyWidth,
|
|
386
|
+
);
|
|
387
|
+
const maxRows = Math.max(1, rows.length - 2);
|
|
388
|
+
const visibleModalRows =
|
|
389
|
+
modalRows.length > maxRows
|
|
390
|
+
? [
|
|
391
|
+
...modalRows.slice(0, maxRows - 1),
|
|
392
|
+
this.renderModalRow(
|
|
393
|
+
this.theme.fg("dim", "More commands available on taller screens"),
|
|
394
|
+
contentWidth,
|
|
395
|
+
),
|
|
396
|
+
]
|
|
397
|
+
: modalRows;
|
|
398
|
+
const top = Math.max(
|
|
399
|
+
1,
|
|
400
|
+
Math.floor((rows.length - visibleModalRows.length) / 2),
|
|
401
|
+
);
|
|
402
|
+
const left = Math.max(0, Math.floor((width - modalWidth) / 2));
|
|
403
|
+
|
|
404
|
+
const output = [...rows];
|
|
405
|
+
for (let index = 0; index < visibleModalRows.length; index++) {
|
|
406
|
+
const row = visibleModalRows[index]!;
|
|
407
|
+
output[top + index] = padToWidth(
|
|
408
|
+
truncateToWidth(`${" ".repeat(left)}${row}`, width),
|
|
409
|
+
width,
|
|
410
|
+
);
|
|
411
|
+
}
|
|
317
412
|
return output;
|
|
318
413
|
}
|
|
319
414
|
|
|
415
|
+
private buildHelpModalRows(
|
|
416
|
+
modalWidth: number,
|
|
417
|
+
contentWidth: number,
|
|
418
|
+
keyWidth: number,
|
|
419
|
+
): string[] {
|
|
420
|
+
const rows = [
|
|
421
|
+
this.renderModalHorizontal(" Help ", modalWidth, "top"),
|
|
422
|
+
this.renderModalRow(
|
|
423
|
+
this.theme.fg("dim", "Press h or Esc to close."),
|
|
424
|
+
contentWidth,
|
|
425
|
+
),
|
|
426
|
+
this.renderModalRow("", contentWidth),
|
|
427
|
+
];
|
|
428
|
+
|
|
429
|
+
for (const [keys, description] of HELP_COMMANDS) {
|
|
430
|
+
const keyCell = padToWidth(
|
|
431
|
+
truncateToWidth(this.theme.fg("accent", keys), keyWidth),
|
|
432
|
+
keyWidth,
|
|
433
|
+
);
|
|
434
|
+
rows.push(
|
|
435
|
+
this.renderModalRow(
|
|
436
|
+
`${keyCell} ${this.theme.fg("text", description)}`,
|
|
437
|
+
contentWidth,
|
|
438
|
+
),
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
rows.push(this.renderModalHorizontal("", modalWidth, "bottom"));
|
|
443
|
+
return rows;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
private renderModalRow(text: string, contentWidth: number): string {
|
|
447
|
+
return `${this.theme.fg("borderMuted", "│")} ${padToWidth(
|
|
448
|
+
truncateToWidth(text, contentWidth),
|
|
449
|
+
contentWidth,
|
|
450
|
+
)} ${this.theme.fg("borderMuted", "│")}`;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private renderModalHorizontal(
|
|
454
|
+
title: string,
|
|
455
|
+
width: number,
|
|
456
|
+
part: "top" | "bottom",
|
|
457
|
+
): string {
|
|
458
|
+
const contentWidth = Math.max(0, width - 2);
|
|
459
|
+
const visibleTitle = truncateToWidth(title, contentWidth);
|
|
460
|
+
const remaining = Math.max(0, contentWidth - visibleWidth(visibleTitle));
|
|
461
|
+
const left = part === "top" ? "╭" : "╰";
|
|
462
|
+
const right = part === "top" ? "╮" : "╯";
|
|
463
|
+
return `${this.theme.fg("borderMuted", left)}${visibleTitle}${this.theme.fg(
|
|
464
|
+
"borderMuted",
|
|
465
|
+
"─".repeat(remaining),
|
|
466
|
+
)}${this.theme.fg("borderMuted", right)}`;
|
|
467
|
+
}
|
|
468
|
+
|
|
320
469
|
private renderAnnotatedDiffRows(width: number, height: number): string[] {
|
|
321
470
|
const rows = this.getAnnotatedRows(width);
|
|
322
471
|
const output: string[] = [];
|
|
@@ -766,6 +915,11 @@ export class ReviewComponent {
|
|
|
766
915
|
this.tui.requestRender(true);
|
|
767
916
|
}
|
|
768
917
|
|
|
918
|
+
private toggleHelp(): void {
|
|
919
|
+
this.helpVisible = !this.helpVisible;
|
|
920
|
+
this.tui.requestRender(true);
|
|
921
|
+
}
|
|
922
|
+
|
|
769
923
|
private hideInlineExplanation(): void {
|
|
770
924
|
this.visibleExplanationKeys.clear();
|
|
771
925
|
}
|