dsh-diff-approval 0.14.1 → 0.16.0
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/lib/client.js +828 -284
- package/lib/index.js +13 -3
- package/lib/types/client/PendingPanel.d.ts +12 -0
- package/lib/types/client/locales.d.ts +14 -2
- package/lib/types/client/settings.d.ts +13 -0
- package/lib/types/client/split-diff.d.ts +7 -4
- package/lib/types/client/whole-file-diff.d.ts +58 -0
- package/lib/types/vcs.d.ts +2 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -424,6 +424,9 @@ function defaultOpenPath(path, action) {
|
|
|
424
424
|
/** Cap on one VCS command's runtime; scans (git status, svn status) can be slow
|
|
425
425
|
* on large trees but must not hang the import. */
|
|
426
426
|
const VCS_COMMAND_TIMEOUT_MS = 6e4;
|
|
427
|
+
/** Slack added to a blob's size when raising the per-command stdout budget
|
|
428
|
+
* (`stdoutMaxBytes`), so a baseline blob is never truncated at the boundary. */
|
|
429
|
+
const GIT_BLOB_STDOUT_SLACK = 1024;
|
|
427
430
|
/** Quote one argument for a POSIX-ish shell, so paths with spaces or special
|
|
428
431
|
* characters survive interpolation into VCS command lines. */
|
|
429
432
|
function shq(value) {
|
|
@@ -464,12 +467,13 @@ function detectVcsRoot(start) {
|
|
|
464
467
|
}
|
|
465
468
|
}
|
|
466
469
|
/** Run one command through the shell executor; a non-zero exit throws. */
|
|
467
|
-
async function runShell(shell, command, workdir, signal) {
|
|
470
|
+
async function runShell(shell, command, workdir, signal, stdoutMaxBytes) {
|
|
468
471
|
const spec = shell.resolve({
|
|
469
472
|
command,
|
|
470
473
|
workdir,
|
|
471
474
|
timeoutMs: VCS_COMMAND_TIMEOUT_MS,
|
|
472
|
-
signal
|
|
475
|
+
signal,
|
|
476
|
+
stdoutMaxBytes
|
|
473
477
|
});
|
|
474
478
|
const result = await shell.run(spec);
|
|
475
479
|
if (result.exitCode !== 0) {
|
|
@@ -528,7 +532,13 @@ async function gitChanges(input) {
|
|
|
528
532
|
if (worktree !== "M" && worktree !== "D") continue;
|
|
529
533
|
let oldText = "";
|
|
530
534
|
try {
|
|
531
|
-
|
|
535
|
+
let size = 0;
|
|
536
|
+
try {
|
|
537
|
+
size = Number.parseInt((await runShell(shell, `git cat-file -s :0:${shq(rel)}`, root, signal)).trim(), 10);
|
|
538
|
+
} catch {
|
|
539
|
+
size = 0;
|
|
540
|
+
}
|
|
541
|
+
if (Number.isFinite(size) && size > 0) oldText = await runShell(shell, `git show :0:${shq(rel)}`, root, signal, size + GIT_BLOB_STDOUT_SLACK);
|
|
532
542
|
} catch {
|
|
533
543
|
oldText = "";
|
|
534
544
|
}
|
|
@@ -5,6 +5,7 @@ import type { DiffApprovalBlockRange, PendingFileDiff } from '../types.ts';
|
|
|
5
5
|
import type { PendingPanelFace } from './slots.ts';
|
|
6
6
|
import type { DiffApprovalKey } from './locales.ts';
|
|
7
7
|
import { computeWholeFileDiff } from './whole-file-diff.ts';
|
|
8
|
+
import type { IntraRun } from './whole-file-diff.ts';
|
|
8
9
|
import { highlightLines } from './highlight.ts';
|
|
9
10
|
/** The dsh shell's sidebar auto-collapse breakpoint (ui-layout columns.ts):
|
|
10
11
|
* below it the sidebar auto-collapses, and the file list floats on the same
|
|
@@ -30,6 +31,8 @@ interface RowModel {
|
|
|
30
31
|
diff: ReturnType<typeof computeWholeFileDiff>;
|
|
31
32
|
/** Maximal runs of changed rows (inclusive row indices); one per modification. */
|
|
32
33
|
blocks: ChangeBlock[];
|
|
34
|
+
/** Intra-line runs keyed by row index, present only for annotated del/add rows. */
|
|
35
|
+
intra: Map<number, IntraRun[]>;
|
|
33
36
|
}
|
|
34
37
|
/** One file's deferred syntax-highlight runs, one entry per side. */
|
|
35
38
|
interface HighlightRuns {
|
|
@@ -65,9 +68,18 @@ export declare const SplitDiff: import("react").ForwardRefExoticComponent<{
|
|
|
65
68
|
busy: boolean;
|
|
66
69
|
t: Translator;
|
|
67
70
|
selection: RowRange | undefined;
|
|
71
|
+
leadRows: number;
|
|
68
72
|
onBlockKeep: (sessionId: SessionId, id: string, block: DiffApprovalBlockRange) => Promise<void>;
|
|
69
73
|
onBlockRevert: (sessionId: SessionId, id: string, block: DiffApprovalBlockRange) => Promise<void>;
|
|
70
74
|
} & import("react").RefAttributes<SplitDiffHandle>>;
|
|
75
|
+
/**
|
|
76
|
+
* Reconstruct the plain text of the current selection so auto-wrap's visual
|
|
77
|
+
* line breaks never leak into the clipboard. A wrapped row renders its code as
|
|
78
|
+
* several `.subline` block elements, and the browser's default copy inserts a
|
|
79
|
+
* newline between them; those segments form one logical line, so they are joined
|
|
80
|
+
* without a newline while the real newline between diff rows is kept.
|
|
81
|
+
*/
|
|
82
|
+
export declare function selectedPlainText(): string | undefined;
|
|
71
83
|
/** Render the pending-edit review panel and its unified footer action. */
|
|
72
84
|
export declare function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, onPasteReference, onUndo, onRedo, onImportVcs, onAckRedoCleared, onAckJustResolved, collapseSidebar, t, }: PendingPanelProps): import("react").JSX.Element;
|
|
73
85
|
export {};
|
|
@@ -17,7 +17,6 @@ export declare const zh: {
|
|
|
17
17
|
'panel.missingHint': string;
|
|
18
18
|
'panel.externalChanged': string;
|
|
19
19
|
'panel.dismiss': string;
|
|
20
|
-
'panel.createHint': string;
|
|
21
20
|
'panel.pasteOnCopy': string;
|
|
22
21
|
'panel.pasteOnCopyDesc': string;
|
|
23
22
|
'panel.importUntracked': string;
|
|
@@ -26,6 +25,8 @@ export declare const zh: {
|
|
|
26
25
|
'panel.tabWidthDesc': string;
|
|
27
26
|
'panel.splitMode': string;
|
|
28
27
|
'panel.splitModeDesc': string;
|
|
28
|
+
'panel.navLeadRows': string;
|
|
29
|
+
'panel.navLeadRowsDesc': string;
|
|
29
30
|
'panel.quickSummon': string;
|
|
30
31
|
'panel.quickSummonDesc': string;
|
|
31
32
|
'panel.recordShortcut': string;
|
|
@@ -37,6 +38,7 @@ export declare const zh: {
|
|
|
37
38
|
'row.removed': string;
|
|
38
39
|
'action.keep': string;
|
|
39
40
|
'action.revert': string;
|
|
41
|
+
'action.delete': string;
|
|
40
42
|
'action.keepAll': string;
|
|
41
43
|
'action.revertAll': string;
|
|
42
44
|
'action.openFile': string;
|
|
@@ -46,6 +48,8 @@ export declare const zh: {
|
|
|
46
48
|
'action.nextDiff': string;
|
|
47
49
|
'action.showFileList': string;
|
|
48
50
|
'action.hideFileList': string;
|
|
51
|
+
'action.viewSplit': string;
|
|
52
|
+
'action.viewUnified': string;
|
|
49
53
|
'action.copyHint': string;
|
|
50
54
|
'action.copied': string;
|
|
51
55
|
'action.langAuto': string;
|
|
@@ -54,6 +58,8 @@ export declare const zh: {
|
|
|
54
58
|
'action.wrap': string;
|
|
55
59
|
'action.toggleOn': string;
|
|
56
60
|
'action.toggleOff': string;
|
|
61
|
+
'action.decrease': string;
|
|
62
|
+
'action.increase': string;
|
|
57
63
|
'action.importVcs': string;
|
|
58
64
|
'action.importVcsBusy': string;
|
|
59
65
|
'panel.importNone': string;
|
|
@@ -96,7 +102,6 @@ export declare const en: {
|
|
|
96
102
|
'panel.missingHint': string;
|
|
97
103
|
'panel.externalChanged': string;
|
|
98
104
|
'panel.dismiss': string;
|
|
99
|
-
'panel.createHint': string;
|
|
100
105
|
'panel.pasteOnCopy': string;
|
|
101
106
|
'panel.pasteOnCopyDesc': string;
|
|
102
107
|
'panel.importUntracked': string;
|
|
@@ -105,6 +110,8 @@ export declare const en: {
|
|
|
105
110
|
'panel.tabWidthDesc': string;
|
|
106
111
|
'panel.splitMode': string;
|
|
107
112
|
'panel.splitModeDesc': string;
|
|
113
|
+
'panel.navLeadRows': string;
|
|
114
|
+
'panel.navLeadRowsDesc': string;
|
|
108
115
|
'panel.quickSummon': string;
|
|
109
116
|
'panel.quickSummonDesc': string;
|
|
110
117
|
'panel.recordShortcut': string;
|
|
@@ -116,6 +123,7 @@ export declare const en: {
|
|
|
116
123
|
'row.removed': string;
|
|
117
124
|
'action.keep': string;
|
|
118
125
|
'action.revert': string;
|
|
126
|
+
'action.delete': string;
|
|
119
127
|
'action.keepAll': string;
|
|
120
128
|
'action.revertAll': string;
|
|
121
129
|
'action.openFile': string;
|
|
@@ -125,6 +133,8 @@ export declare const en: {
|
|
|
125
133
|
'action.nextDiff': string;
|
|
126
134
|
'action.showFileList': string;
|
|
127
135
|
'action.hideFileList': string;
|
|
136
|
+
'action.viewSplit': string;
|
|
137
|
+
'action.viewUnified': string;
|
|
128
138
|
'action.copyHint': string;
|
|
129
139
|
'action.copied': string;
|
|
130
140
|
'action.langAuto': string;
|
|
@@ -133,6 +143,8 @@ export declare const en: {
|
|
|
133
143
|
'action.wrap': string;
|
|
134
144
|
'action.toggleOn': string;
|
|
135
145
|
'action.toggleOff': string;
|
|
146
|
+
'action.decrease': string;
|
|
147
|
+
'action.increase': string;
|
|
136
148
|
'action.importVcs': string;
|
|
137
149
|
'action.importVcsBusy': string;
|
|
138
150
|
'panel.importNone': string;
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/** Client preferences for the review panel, persisted in localStorage. */
|
|
2
|
+
/** Default lead rows above a jumped-to diff block (kept small and bounded). */
|
|
3
|
+
export declare const NAV_LEAD_ROWS_DEFAULT = 2;
|
|
4
|
+
export declare const NAV_LEAD_ROWS_MIN = 0;
|
|
5
|
+
export declare const NAV_LEAD_ROWS_MAX = 10;
|
|
2
6
|
/**
|
|
3
7
|
* Whether copying a reference should also paste it into the chat input and
|
|
4
8
|
* focus it. Defaults to on; only an explicit `'0'` disables it.
|
|
@@ -45,6 +49,15 @@ export declare function setTabWidth(value: number): void;
|
|
|
45
49
|
export declare function splitMode(): boolean;
|
|
46
50
|
/** Persist the split-view preference. */
|
|
47
51
|
export declare function setSplitMode(value: boolean): void;
|
|
52
|
+
/**
|
|
53
|
+
* How many rows of lead the diff block jump leaves above the jumped-to block,
|
|
54
|
+
* and how far the anchored navigation scans. Defaults to 2; an out-of-range or
|
|
55
|
+
* non-integer value falls back to the default.
|
|
56
|
+
* @returns the lead row count.
|
|
57
|
+
*/
|
|
58
|
+
export declare function navLeadRows(): number;
|
|
59
|
+
/** Persist the block-jump lead row count. */
|
|
60
|
+
export declare function setNavLeadRows(value: number): void;
|
|
48
61
|
/** Default quick-summon chord (toggle the review panel open/closed). */
|
|
49
62
|
export declare const DEFAULT_QUICK_SUMMON = "Ctrl+D";
|
|
50
63
|
/**
|
|
@@ -32,13 +32,16 @@ export interface SplitDiff {
|
|
|
32
32
|
pairOfRow: ReadonlyMap<number, number>;
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
-
* Regroup the whole-file rows into aligned split pairs,
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* Regroup the whole-file rows into aligned split pairs. By order, a deletion
|
|
36
|
+
* run pairs line-by-line with a following addition run; with similarity
|
|
37
|
+
* alignment, each deletion pairs with its most-similar addition (order
|
|
38
|
+
* preserved, threshold-bounded) so a mixed insert/delete block does not force a
|
|
39
|
+
* wrong line together. A stray deletion or addition stays a one-sided pair.
|
|
38
40
|
* @param rows - the whole-file diff rows.
|
|
41
|
+
* @param alignBySimilarity - align a change block by similarity instead of order.
|
|
39
42
|
* @returns the split pairs plus the row→pair index map.
|
|
40
43
|
*/
|
|
41
|
-
export declare function computeSideBySideDiff(rows: readonly WholeFileDiffRow[]): SplitDiff;
|
|
44
|
+
export declare function computeSideBySideDiff(rows: readonly WholeFileDiffRow[], alignBySimilarity?: boolean): SplitDiff;
|
|
42
45
|
/**
|
|
43
46
|
* Pair indices whose left or right text contains the query (case-insensitive).
|
|
44
47
|
* A pair counts once however many times the query appears, so split search
|
|
@@ -47,3 +47,61 @@ export interface WholeFileDiff {
|
|
|
47
47
|
*/
|
|
48
48
|
export declare function contentKey(text: string): string;
|
|
49
49
|
export declare function computeWholeFileDiff(oldText: string, newText: string): WholeFileDiff;
|
|
50
|
+
/**
|
|
51
|
+
* One intra-line segment of a changed line, used to highlight which characters
|
|
52
|
+
* within a paired del/add line differ. A run is either unmarked context
|
|
53
|
+
* (`same`) or the characters that were removed (`del`) /
|
|
54
|
+
* added (`add`). Reassembling the runs of a del row in order yields the old
|
|
55
|
+
* line's text; the runs of the paired add row yield the new line's text.
|
|
56
|
+
*/
|
|
57
|
+
export interface IntraRun {
|
|
58
|
+
text: string;
|
|
59
|
+
kind: 'same' | 'del' | 'add';
|
|
60
|
+
}
|
|
61
|
+
/** One matched del→add row pair within a change block. */
|
|
62
|
+
interface BlockPair {
|
|
63
|
+
delIndex: number;
|
|
64
|
+
addIndex: number;
|
|
65
|
+
}
|
|
66
|
+
/** The alignment of one del/add change block: matched pairs and excess sides. */
|
|
67
|
+
export interface BlockAlignment {
|
|
68
|
+
/** Matched del→add pairs, in file order. */
|
|
69
|
+
pairs: BlockPair[];
|
|
70
|
+
/** Row indices of unmatched deletions (shown as del-only in the split view). */
|
|
71
|
+
delOnly: number[];
|
|
72
|
+
/** Row indices of unmatched additions (shown as add-only in the split view). */
|
|
73
|
+
addOnly: number[];
|
|
74
|
+
/** True when similarity matching actually ran. False for a by-order block or a
|
|
75
|
+
* block too large to match — the caller then skips the intra-line highlight so
|
|
76
|
+
* alignment and highlighting are all-or-nothing (either both or neither). */
|
|
77
|
+
usedSimilarity: boolean;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Align a del run with the following add run. By order (the baseline) pairs
|
|
81
|
+
* `del[i]`↔`add[i]`. With similarity alignment, an order-preserving best match
|
|
82
|
+
* pairs each del with the add that maximises total similarity, keeping a pair
|
|
83
|
+
* only when it clears the similarity threshold — so a lone insert or delete in
|
|
84
|
+
* a mixed block stays unaligned instead of being forced onto a wrong line.
|
|
85
|
+
* @param delRows - the run's del rows (index + text).
|
|
86
|
+
* @param addRows - the run's add rows (index + text).
|
|
87
|
+
* @param alignBySimilarity - whether to use the similarity-based matching.
|
|
88
|
+
* @returns the aligned pairs and the unmatched sides.
|
|
89
|
+
*/
|
|
90
|
+
export declare function alignChangedBlock(delRows: readonly {
|
|
91
|
+
index: number;
|
|
92
|
+
text: string;
|
|
93
|
+
}[], addRows: readonly {
|
|
94
|
+
index: number;
|
|
95
|
+
text: string;
|
|
96
|
+
}[], alignBySimilarity: boolean): BlockAlignment;
|
|
97
|
+
/**
|
|
98
|
+
* Derive intra-line runs for a whole-file row list. Each del/add block is
|
|
99
|
+
* aligned (by order, or by similarity when `alignBySimilarity`), and each
|
|
100
|
+
* matched pair is compared by `intraRunsOf`. Returns a map keyed by the row's
|
|
101
|
+
* index in `rows`, present only for rows that carry a highlight.
|
|
102
|
+
* @param rows - the unified `WholeFileDiff` row list.
|
|
103
|
+
* @param alignBySimilarity - align blocks by similarity instead of by order.
|
|
104
|
+
* @returns per-row-index intra-line runs for annotated del/add rows.
|
|
105
|
+
*/
|
|
106
|
+
export declare function computeIntraLineDiff(rows: readonly WholeFileDiffRow[], alignBySimilarity?: boolean): Map<number, IntraRun[]>;
|
|
107
|
+
export {};
|
package/lib/types/vcs.d.ts
CHANGED
|
@@ -44,6 +44,8 @@ export interface ShellExecutorLike {
|
|
|
44
44
|
workdir?: string | undefined;
|
|
45
45
|
timeoutMs?: number | undefined;
|
|
46
46
|
signal?: AbortSignal | undefined;
|
|
47
|
+
/** Foreground stdout capture budget in bytes; absent uses the executor's cap. */
|
|
48
|
+
stdoutMaxBytes?: number | undefined;
|
|
47
49
|
}): unknown;
|
|
48
50
|
run(spec: unknown): Promise<{
|
|
49
51
|
exitCode: number | null;
|
package/package.json
CHANGED