dsh-diff-approval 0.19.2 → 0.19.3
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 +823 -212
- package/lib/index.js +141 -6
- package/lib/types/client/PendingPanel.d.ts +5 -1
- package/lib/types/client/locales.d.ts +28 -0
- package/lib/types/client/port.d.ts +7 -5
- package/lib/types/client/search.d.ts +23 -0
- package/lib/types/client/settings.d.ts +25 -0
- package/lib/types/client/slots.d.ts +9 -5
- package/lib/types/client/split-diff.d.ts +7 -5
- package/lib/types/client/store.d.ts +8 -5
- package/lib/types/index.d.ts +1 -1
- package/lib/types/types.d.ts +16 -0
- package/lib/types/vcs.d.ts +6 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -440,6 +440,18 @@ function isPathInside(absolutePath, root) {
|
|
|
440
440
|
if (path === base) return true;
|
|
441
441
|
return path.startsWith(base + sep);
|
|
442
442
|
}
|
|
443
|
+
/**
|
|
444
|
+
* Whether one changed path belongs to the scan: inside the workspace, and inside
|
|
445
|
+
* the narrowed `scope` when the caller set one.
|
|
446
|
+
* @param absolutePath - the changed file's absolute path.
|
|
447
|
+
* @param workspaceRoot - the session's workspace root.
|
|
448
|
+
* @param scope - one path to restrict the scan to, or undefined for all of it.
|
|
449
|
+
* @returns whether the change is in scope.
|
|
450
|
+
*/
|
|
451
|
+
function inScanScope(absolutePath, workspaceRoot, scope) {
|
|
452
|
+
if (!isPathInside(absolutePath, workspaceRoot)) return false;
|
|
453
|
+
return scope === void 0 || isPathInside(absolutePath, scope);
|
|
454
|
+
}
|
|
443
455
|
/** The VCS marker of one directory, or undefined when it holds none. */
|
|
444
456
|
function markerOf(directory) {
|
|
445
457
|
if (existsSync(resolve(directory, ".git"))) return "git";
|
|
@@ -511,12 +523,12 @@ function parseGitPorcelainZ(output) {
|
|
|
511
523
|
}
|
|
512
524
|
/** Enumerate the workspace's local changes in a git checkout. */
|
|
513
525
|
async function gitChanges(input) {
|
|
514
|
-
const { root, workspaceRoot, includeUntracked, shell, readText, signal } = input;
|
|
526
|
+
const { root, workspaceRoot, includeUntracked, scope, shell, readText, signal } = input;
|
|
515
527
|
const stdout = await runShell(shell, "git -c status.renames=false status --porcelain=v1 -z --untracked-files=all", root, signal);
|
|
516
528
|
const changes = [];
|
|
517
529
|
for (const { xy, rel } of parseGitPorcelainZ(stdout)) {
|
|
518
530
|
const absolute = resolve(root, rel);
|
|
519
|
-
if (!
|
|
531
|
+
if (!inScanScope(absolute, workspaceRoot, scope)) continue;
|
|
520
532
|
if (xy === "??") {
|
|
521
533
|
if (!includeUntracked) continue;
|
|
522
534
|
const newText = await readText(absolute) ?? "";
|
|
@@ -559,7 +571,7 @@ function xmlUnescape(value) {
|
|
|
559
571
|
}
|
|
560
572
|
/** Enumerate the workspace's local changes in an svn working copy. */
|
|
561
573
|
async function svnChanges(input) {
|
|
562
|
-
const { root, workspaceRoot, includeUntracked, shell, readText, signal } = input;
|
|
574
|
+
const { root, workspaceRoot, includeUntracked, scope, shell, readText, signal } = input;
|
|
563
575
|
const stdout = await runShell(shell, "svn status --xml", root, signal);
|
|
564
576
|
const changes = [];
|
|
565
577
|
const entryPattern = /<entry[^>]*path="([^"]*)"[^>]*>\s*<wc-status[^>]*item="([^"]*)"/g;
|
|
@@ -568,7 +580,7 @@ async function svnChanges(input) {
|
|
|
568
580
|
const rel = xmlUnescape(match[1]);
|
|
569
581
|
const item = match[2];
|
|
570
582
|
const absolute = resolve(root, rel);
|
|
571
|
-
if (!
|
|
583
|
+
if (!inScanScope(absolute, workspaceRoot, scope)) continue;
|
|
572
584
|
if (item === "modified" || item === "deleted") {
|
|
573
585
|
let oldText = "";
|
|
574
586
|
try {
|
|
@@ -623,7 +635,7 @@ function p4ChangeOf(line) {
|
|
|
623
635
|
* not yet opened for add; off keeps to already-opened files (`p4 opened`) so
|
|
624
636
|
* the scan — which can be slow — is skipped. */
|
|
625
637
|
async function p4Changes(input) {
|
|
626
|
-
const { root, workspaceRoot, includeUntracked, shell, readText, signal } = input;
|
|
638
|
+
const { root, workspaceRoot, includeUntracked, scope, shell, readText, signal } = input;
|
|
627
639
|
const stdout = await runShell(shell, includeUntracked ? "p4 status" : "p4 opened", root, signal);
|
|
628
640
|
const changes = [];
|
|
629
641
|
for (const line of stdout.split("\n")) {
|
|
@@ -632,7 +644,7 @@ async function p4Changes(input) {
|
|
|
632
644
|
const local = (await runShell(shell, `p4 where ${shq(opened.depot)}`, root, signal)).trim().split(/\s+/).pop();
|
|
633
645
|
if (local === void 0 || local.length === 0) continue;
|
|
634
646
|
const absolute = resolve(local);
|
|
635
|
-
if (!
|
|
647
|
+
if (!inScanScope(absolute, workspaceRoot, scope)) continue;
|
|
636
648
|
const deleted = opened.action === "delete" || opened.action === "move/delete";
|
|
637
649
|
const created = opened.action === "add" || opened.action === "move/add";
|
|
638
650
|
const newText = deleted ? "" : await readText(absolute) ?? "";
|
|
@@ -1298,6 +1310,33 @@ function apply(ctx, config) {
|
|
|
1298
1310
|
value: { outcome: "missing" }
|
|
1299
1311
|
};
|
|
1300
1312
|
}
|
|
1313
|
+
if (payload.keepListed === true) {
|
|
1314
|
+
store.update(target.id, { oldText: entry.newText });
|
|
1315
|
+
const afterEntry = {
|
|
1316
|
+
...entry,
|
|
1317
|
+
oldText: entry.newText,
|
|
1318
|
+
updatedAt: Date.now()
|
|
1319
|
+
};
|
|
1320
|
+
pushUndo(target.sessionId, {
|
|
1321
|
+
id: entry.path,
|
|
1322
|
+
path: entry.path,
|
|
1323
|
+
entry,
|
|
1324
|
+
fileText: void 0
|
|
1325
|
+
}, {
|
|
1326
|
+
id: entry.path,
|
|
1327
|
+
path: entry.path,
|
|
1328
|
+
entry: afterEntry,
|
|
1329
|
+
fileText: void 0
|
|
1330
|
+
});
|
|
1331
|
+
persistSession(true);
|
|
1332
|
+
return {
|
|
1333
|
+
ok: true,
|
|
1334
|
+
value: {
|
|
1335
|
+
outcome: "kept",
|
|
1336
|
+
resolved: true
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1301
1340
|
store.remove(target.id);
|
|
1302
1341
|
pushUndo(target.sessionId, {
|
|
1303
1342
|
id: entry.path,
|
|
@@ -1334,6 +1373,34 @@ function apply(ctx, config) {
|
|
|
1334
1373
|
} catch (error) {
|
|
1335
1374
|
return rpcError(`revert failed: ${errorMessage(error)}`);
|
|
1336
1375
|
}
|
|
1376
|
+
if (payload.keepListed === true) {
|
|
1377
|
+
const content = undo?.after.fileText ?? "";
|
|
1378
|
+
store.update(target.id, { newText: content });
|
|
1379
|
+
const afterEntry = {
|
|
1380
|
+
...entry,
|
|
1381
|
+
newText: content,
|
|
1382
|
+
updatedAt: Date.now()
|
|
1383
|
+
};
|
|
1384
|
+
if (undo !== void 0) pushUndo(target.sessionId, {
|
|
1385
|
+
id: entry.path,
|
|
1386
|
+
path: entry.path,
|
|
1387
|
+
entry,
|
|
1388
|
+
fileText: undo.before.fileText
|
|
1389
|
+
}, {
|
|
1390
|
+
id: entry.path,
|
|
1391
|
+
path: entry.path,
|
|
1392
|
+
entry: afterEntry,
|
|
1393
|
+
fileText: content
|
|
1394
|
+
});
|
|
1395
|
+
persistSession(true);
|
|
1396
|
+
return {
|
|
1397
|
+
ok: true,
|
|
1398
|
+
value: {
|
|
1399
|
+
outcome: "reverted",
|
|
1400
|
+
resolved: true
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1337
1404
|
store.remove(target.id);
|
|
1338
1405
|
if (undo !== void 0) pushUndo(target.sessionId, undo.before, undo.after);
|
|
1339
1406
|
persistSession(true);
|
|
@@ -1687,6 +1754,74 @@ function apply(ctx, config) {
|
|
|
1687
1754
|
}
|
|
1688
1755
|
};
|
|
1689
1756
|
}
|
|
1757
|
+
case "vcs-refresh": {
|
|
1758
|
+
const target = targetOf(payload);
|
|
1759
|
+
if (target === void 0) return rpcError("sessionId and id must be non-empty strings");
|
|
1760
|
+
await ensureLoaded();
|
|
1761
|
+
const entry = store.get(target.id);
|
|
1762
|
+
if (entry === void 0) return {
|
|
1763
|
+
ok: true,
|
|
1764
|
+
value: { outcome: "missing" }
|
|
1765
|
+
};
|
|
1766
|
+
const workspace = workspaceOf(target.sessionId);
|
|
1767
|
+
if (workspace === void 0) return rpcError("refresh unavailable: the session has no workspace");
|
|
1768
|
+
const root = detectVcsRoot(workspace.path);
|
|
1769
|
+
if (root === void 0) return {
|
|
1770
|
+
ok: true,
|
|
1771
|
+
value: { outcome: "no-vcs" }
|
|
1772
|
+
};
|
|
1773
|
+
const shell = ctx.get("shell");
|
|
1774
|
+
if (shell === void 0) return rpcError("refresh unavailable: the deployment has no shell executor");
|
|
1775
|
+
let changes;
|
|
1776
|
+
try {
|
|
1777
|
+
changes = await listVcsChanges({
|
|
1778
|
+
kind: root.kind,
|
|
1779
|
+
root: root.root,
|
|
1780
|
+
workspaceRoot: workspace.path,
|
|
1781
|
+
includeUntracked: payload.includeUntracked === true,
|
|
1782
|
+
scope: entry.path,
|
|
1783
|
+
shell,
|
|
1784
|
+
readText: (path) => readFile(path, "utf8").catch(() => void 0),
|
|
1785
|
+
signal
|
|
1786
|
+
});
|
|
1787
|
+
} catch (error) {
|
|
1788
|
+
return rpcError(`refresh failed: ${errorMessage(error)}`);
|
|
1789
|
+
}
|
|
1790
|
+
const folded = (value) => process.platform === "win32" ? resolve(value).toLowerCase() : resolve(value);
|
|
1791
|
+
const change = changes.find((candidate) => folded(candidate.path) === folded(entry.path));
|
|
1792
|
+
if (change === void 0) return {
|
|
1793
|
+
ok: true,
|
|
1794
|
+
value: { outcome: "no-change" }
|
|
1795
|
+
};
|
|
1796
|
+
if (change.kind === entry.kind && change.oldText === entry.oldText && change.newText === entry.newText) return {
|
|
1797
|
+
ok: true,
|
|
1798
|
+
value: { outcome: "unchanged" }
|
|
1799
|
+
};
|
|
1800
|
+
const refreshed = {
|
|
1801
|
+
...entry,
|
|
1802
|
+
kind: change.kind,
|
|
1803
|
+
oldText: change.oldText,
|
|
1804
|
+
newText: change.newText,
|
|
1805
|
+
updatedAt: Date.now()
|
|
1806
|
+
};
|
|
1807
|
+
store.restore(refreshed);
|
|
1808
|
+
pushUndo(target.sessionId, {
|
|
1809
|
+
id: entry.path,
|
|
1810
|
+
path: entry.path,
|
|
1811
|
+
entry,
|
|
1812
|
+
fileText: void 0
|
|
1813
|
+
}, {
|
|
1814
|
+
id: entry.path,
|
|
1815
|
+
path: entry.path,
|
|
1816
|
+
entry: refreshed,
|
|
1817
|
+
fileText: void 0
|
|
1818
|
+
});
|
|
1819
|
+
persistSession(true);
|
|
1820
|
+
return {
|
|
1821
|
+
ok: true,
|
|
1822
|
+
value: { outcome: "refreshed" }
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1690
1825
|
case "open": {
|
|
1691
1826
|
const target = openTargetOf(payload);
|
|
1692
1827
|
if (target === void 0) return rpcError("sessionId, id, and action must be valid");
|
|
@@ -66,7 +66,11 @@ interface RowRange {
|
|
|
66
66
|
export interface SplitDiffHandle {
|
|
67
67
|
jump: (direction: -1 | 1, wrapGuard?: boolean, singleToast?: boolean) => void;
|
|
68
68
|
openSearch: () => void;
|
|
69
|
+
toggleSearch: () => void;
|
|
70
|
+
closeSearch: () => boolean;
|
|
69
71
|
searchNext: (direction: -1 | 1) => boolean;
|
|
72
|
+
toggleMatchCase: () => boolean;
|
|
73
|
+
toggleMatchWholeWord: () => boolean;
|
|
70
74
|
}
|
|
71
75
|
/** The two-column (side-by-side) whole-file diff view. */
|
|
72
76
|
export declare const SplitDiff: import("react").ForwardRefExoticComponent<{
|
|
@@ -93,5 +97,5 @@ export declare const SplitDiff: import("react").ForwardRefExoticComponent<{
|
|
|
93
97
|
*/
|
|
94
98
|
export declare function selectedPlainText(): string | undefined;
|
|
95
99
|
/** Render the pending-edit review panel and its unified footer action. */
|
|
96
|
-
export declare function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, onPreviewImage, onPasteReference, onUndo, onRedo, onImportVcs, onKeepAll, onRevertAll, onAckRedoCleared, collapseSidebar, t, }: PendingPanelProps): import("react").JSX.Element;
|
|
100
|
+
export declare function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, onPreviewImage, onPasteReference, onUndo, onRedo, onImportVcs, onRefreshVcs, onKeepAll, onRevertAll, onAckRedoCleared, collapseSidebar, t, }: PendingPanelProps): import("react").JSX.Element;
|
|
97
101
|
export {};
|
|
@@ -59,6 +59,8 @@ export declare const zh: {
|
|
|
59
59
|
'panel.key.openSearch': string;
|
|
60
60
|
'panel.key.searchNext': string;
|
|
61
61
|
'panel.key.searchPrev': string;
|
|
62
|
+
'panel.key.matchCase': string;
|
|
63
|
+
'panel.key.matchWholeWord': string;
|
|
62
64
|
'panel.key.undo': string;
|
|
63
65
|
'panel.key.redo': string;
|
|
64
66
|
'panel.key.cycleNext': string;
|
|
@@ -116,6 +118,18 @@ export declare const zh: {
|
|
|
116
118
|
'status.missing': string;
|
|
117
119
|
'panel.resolvedAsk': string;
|
|
118
120
|
'panel.keepInList': string;
|
|
121
|
+
'panel.fileKeptAsk': string;
|
|
122
|
+
'panel.fileRevertedAsk': string;
|
|
123
|
+
'panel.confirmFileRemove': string;
|
|
124
|
+
'panel.confirmFileRemoveDesc': string;
|
|
125
|
+
'action.refreshVcs': string;
|
|
126
|
+
'action.matchCase': string;
|
|
127
|
+
'action.matchWholeWord': string;
|
|
128
|
+
'panel.refreshDone': string;
|
|
129
|
+
'panel.refreshUnchanged': string;
|
|
130
|
+
'panel.refreshNone': string;
|
|
131
|
+
'panel.refreshUntrackedHint': string;
|
|
132
|
+
'panel.refreshFailed': string;
|
|
119
133
|
};
|
|
120
134
|
/** Translation keys owned by the pending-edit review namespace. */
|
|
121
135
|
export type DiffApprovalKey = keyof typeof zh;
|
|
@@ -184,6 +198,8 @@ export declare const en: {
|
|
|
184
198
|
'panel.key.openSearch': string;
|
|
185
199
|
'panel.key.searchNext': string;
|
|
186
200
|
'panel.key.searchPrev': string;
|
|
201
|
+
'panel.key.matchCase': string;
|
|
202
|
+
'panel.key.matchWholeWord': string;
|
|
187
203
|
'panel.key.undo': string;
|
|
188
204
|
'panel.key.redo': string;
|
|
189
205
|
'panel.key.cycleNext': string;
|
|
@@ -241,4 +257,16 @@ export declare const en: {
|
|
|
241
257
|
'status.missing': string;
|
|
242
258
|
'panel.resolvedAsk': string;
|
|
243
259
|
'panel.keepInList': string;
|
|
260
|
+
'panel.fileKeptAsk': string;
|
|
261
|
+
'panel.fileRevertedAsk': string;
|
|
262
|
+
'panel.confirmFileRemove': string;
|
|
263
|
+
'panel.confirmFileRemoveDesc': string;
|
|
264
|
+
'action.refreshVcs': string;
|
|
265
|
+
'action.matchCase': string;
|
|
266
|
+
'action.matchWholeWord': string;
|
|
267
|
+
'panel.refreshDone': string;
|
|
268
|
+
'panel.refreshUnchanged': string;
|
|
269
|
+
'panel.refreshNone': string;
|
|
270
|
+
'panel.refreshUntrackedHint': string;
|
|
271
|
+
'panel.refreshFailed': string;
|
|
244
272
|
};
|
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
* @module dsh-diff-approval/client/port
|
|
6
6
|
*/
|
|
7
7
|
import type { ClientConnectionRpc, SessionId } from '@deepseek-ai/dsh-client-connection/client';
|
|
8
|
-
import type { DiffApprovalActionValue, DiffApprovalBlockRange, DiffApprovalBulkValue, DiffApprovalListValue, DiffApprovalOpenAction, DiffApprovalOpenValue, DiffApprovalPreviewImageValue, VcsImportValue } from '../types.ts';
|
|
8
|
+
import type { DiffApprovalActionValue, DiffApprovalBlockRange, DiffApprovalBulkValue, DiffApprovalListValue, DiffApprovalOpenAction, DiffApprovalOpenValue, DiffApprovalPreviewImageValue, DiffApprovalRefreshValue, VcsImportValue } from '../types.ts';
|
|
9
9
|
/** The channel the host half registers and this port calls. */
|
|
10
10
|
export declare const DIFF_APPROVAL_CHANNEL = "/diff-approval";
|
|
11
11
|
/** This package's business verbs over the review channel. */
|
|
12
12
|
export interface DiffApprovalPort {
|
|
13
13
|
/** Read one session's pending entries (plus its workspace root), oldest capture first. */
|
|
14
14
|
list(sessionId: SessionId): Promise<DiffApprovalListValue>;
|
|
15
|
-
/** Keep one operation. */
|
|
16
|
-
keep(sessionId: SessionId, id: string): Promise<DiffApprovalActionValue>;
|
|
17
|
-
/** Revert one operation. */
|
|
18
|
-
revert(sessionId: SessionId, id: string): Promise<DiffApprovalActionValue>;
|
|
15
|
+
/** Keep one operation. `keepListed` leaves the resolved entry in the list. */
|
|
16
|
+
keep(sessionId: SessionId, id: string, keepListed?: boolean): Promise<DiffApprovalActionValue>;
|
|
17
|
+
/** Revert one operation. `keepListed` leaves the resolved entry in the list. */
|
|
18
|
+
revert(sessionId: SessionId, id: string, keepListed?: boolean): Promise<DiffApprovalActionValue>;
|
|
19
19
|
/** Keep one diff block (accept its change into the tracked baseline). */
|
|
20
20
|
blockKeep(sessionId: SessionId, id: string, block: DiffApprovalBlockRange, removeWhenResolved?: boolean): Promise<DiffApprovalActionValue>;
|
|
21
21
|
/** Revert one diff block (restore its old lines in the file). */
|
|
@@ -26,6 +26,8 @@ export interface DiffApprovalPort {
|
|
|
26
26
|
redo(sessionId: SessionId): Promise<DiffApprovalActionValue>;
|
|
27
27
|
/** Import the workspace's local VCS changes as pending entries. */
|
|
28
28
|
importVcs(sessionId: SessionId, includeUntracked: boolean): Promise<VcsImportValue>;
|
|
29
|
+
/** Replace one entry's diff with the file's current local VCS change. */
|
|
30
|
+
refreshVcs(sessionId: SessionId, id: string, includeUntracked: boolean): Promise<DiffApprovalRefreshValue>;
|
|
29
31
|
/** Open one file with its default application or reveal it in the folder. */
|
|
30
32
|
open(sessionId: SessionId, id: string, action: DiffApprovalOpenAction): Promise<DiffApprovalOpenValue>;
|
|
31
33
|
/** Keep every pending entry of one session in a single host call (one batch). */
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The in-file search matcher, shared by the unified diff and the split view so
|
|
3
|
+
* both highlight and count the same occurrences.
|
|
4
|
+
* @module dsh-diff-approval/client/search
|
|
5
|
+
*/
|
|
6
|
+
/** Which occurrences an in-file search accepts. */
|
|
7
|
+
export interface SearchOptions {
|
|
8
|
+
/** Match the query's letter case exactly. */
|
|
9
|
+
caseSensitive: boolean;
|
|
10
|
+
/** Accept only whole words (no identifier character touching either end). */
|
|
11
|
+
wholeWord: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** The plain search: case-insensitive, substring. */
|
|
14
|
+
export declare const DEFAULT_SEARCH_OPTIONS: SearchOptions;
|
|
15
|
+
/**
|
|
16
|
+
* Character ranges of every occurrence of `query` in `text` that the options
|
|
17
|
+
* accept. Case-insensitive and substring-based unless narrowed.
|
|
18
|
+
* @param text - the line to search.
|
|
19
|
+
* @param query - the search text; empty matches nothing.
|
|
20
|
+
* @param options - case and whole-word narrowing.
|
|
21
|
+
* @returns `[start, end)` ranges in ascending order.
|
|
22
|
+
*/
|
|
23
|
+
export declare function matchRangesOf(text: string, query: string, options?: SearchOptions): [number, number][];
|
|
@@ -36,6 +36,31 @@ export declare function setPasteOnCopyEnabled(value: boolean): void;
|
|
|
36
36
|
export declare function includeUntrackedEnabled(): boolean;
|
|
37
37
|
/** Persist the import-untracked preference. */
|
|
38
38
|
export declare function setIncludeUntrackedEnabled(value: boolean): void;
|
|
39
|
+
/**
|
|
40
|
+
* Whether a whole-file keep/revert asks before dropping the resolved file from
|
|
41
|
+
* the list. Defaults to on; only an explicit `'0'` disables it, which then
|
|
42
|
+
* removes the file straight away.
|
|
43
|
+
* @returns whether the remove prompt is enabled.
|
|
44
|
+
*/
|
|
45
|
+
export declare function confirmFileRemoveEnabled(): boolean;
|
|
46
|
+
/** Persist the whole-file remove-prompt preference. */
|
|
47
|
+
export declare function setConfirmFileRemoveEnabled(value: boolean): void;
|
|
48
|
+
/**
|
|
49
|
+
* Whether the in-file search matches letter case exactly. Defaults to off, so a
|
|
50
|
+
* plain query keeps matching either case; only an explicit `'1'` turns it on.
|
|
51
|
+
* @returns whether the search is case-sensitive.
|
|
52
|
+
*/
|
|
53
|
+
export declare function searchCaseSensitive(): boolean;
|
|
54
|
+
/** Persist the search case-sensitivity preference. */
|
|
55
|
+
export declare function setSearchCaseSensitive(value: boolean): void;
|
|
56
|
+
/**
|
|
57
|
+
* Whether the in-file search only matches whole words. Defaults to off; only an
|
|
58
|
+
* explicit `'1'` enables it.
|
|
59
|
+
* @returns whether the search matches whole words only.
|
|
60
|
+
*/
|
|
61
|
+
export declare function searchWholeWord(): boolean;
|
|
62
|
+
/** Persist the search whole-word preference. */
|
|
63
|
+
export declare function setSearchWholeWord(value: boolean): void;
|
|
39
64
|
/**
|
|
40
65
|
* Whether lines wrap (auto-wrap) in the diff for one highlight language.
|
|
41
66
|
* Defaults to off; only an explicit `'1'` enables it. Stored per language, so
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** The panel's injected business face and its observable snapshot. */
|
|
2
2
|
import type { HostObservable } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
3
|
import type { SessionId } from '@deepseek-ai/dsh-client-connection/client';
|
|
4
|
-
import type { DiffApprovalBlockRange, DiffApprovalOpenAction, PendingFileDiff, VcsImportValue } from '../types.ts';
|
|
4
|
+
import type { DiffApprovalBlockRange, DiffApprovalOpenAction, DiffApprovalRefreshValue, PendingFileDiff, VcsImportValue } from '../types.ts';
|
|
5
5
|
/** What the panel reads and drives: the pending list plus in-flight entries. */
|
|
6
6
|
export interface PendingDiffSnapshot {
|
|
7
7
|
/** Whether a list read has completed at least once. */
|
|
@@ -29,10 +29,11 @@ export interface PendingPanelFace {
|
|
|
29
29
|
};
|
|
30
30
|
/** Read the pending list for the current session into the snapshot. */
|
|
31
31
|
onRefresh: (sessionId: SessionId | undefined) => void;
|
|
32
|
-
/** Keep one operation
|
|
33
|
-
onKeep: (sessionId: SessionId, id: string) => Promise<void>;
|
|
34
|
-
/** Revert one operation (restore its prior content, or remove a created file).
|
|
35
|
-
|
|
32
|
+
/** Keep one operation. `keepListed` leaves the resolved entry in the list. */
|
|
33
|
+
onKeep: (sessionId: SessionId, id: string, keepListed?: boolean) => Promise<void>;
|
|
34
|
+
/** Revert one operation (restore its prior content, or remove a created file).
|
|
35
|
+
* `keepListed` leaves the resolved entry in the list. */
|
|
36
|
+
onRevert: (sessionId: SessionId, id: string, keepListed?: boolean) => Promise<void>;
|
|
36
37
|
/** Keep one diff block (accept its change into the tracked baseline). */
|
|
37
38
|
onBlockKeep: (sessionId: SessionId, id: string, block: DiffApprovalBlockRange, removeWhenResolved?: boolean) => Promise<void>;
|
|
38
39
|
/** Revert one diff block (restore its old lines in the file). */
|
|
@@ -50,6 +51,9 @@ export interface PendingPanelFace {
|
|
|
50
51
|
onRedo: (sessionId: SessionId) => Promise<string | undefined>;
|
|
51
52
|
/** Import the workspace's local VCS changes as pending entries (detection included). */
|
|
52
53
|
onImportVcs: (sessionId: SessionId, includeUntracked: boolean) => Promise<VcsImportValue>;
|
|
54
|
+
/** Replace one entry's diff with the file's current local VCS change, then
|
|
55
|
+
* refresh the list; resolves to what the scan found. */
|
|
56
|
+
onRefreshVcs: (sessionId: SessionId, id: string, includeUntracked: boolean) => Promise<DiffApprovalRefreshValue>;
|
|
53
57
|
/** Keep every pending entry of one session in a single host call (bulk). */
|
|
54
58
|
onKeepAll: (sessionId: SessionId) => Promise<void>;
|
|
55
59
|
/** Revert every pending entry of one session in a single host call (bulk). */
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @module dsh-diff-approval/client/split-diff
|
|
9
9
|
*/
|
|
10
10
|
import type { WholeFileDiffRow } from './whole-file-diff.ts';
|
|
11
|
+
import type { SearchOptions } from './search.ts';
|
|
11
12
|
/** One side of a split pair: text plus its 1-based line number (absent on a pure add). */
|
|
12
13
|
export interface SplitSide {
|
|
13
14
|
/** The side's source line content, without the terminating newline. */
|
|
@@ -43,12 +44,13 @@ export interface SplitDiff {
|
|
|
43
44
|
*/
|
|
44
45
|
export declare function computeSideBySideDiff(rows: readonly WholeFileDiffRow[], alignBySimilarity?: boolean): SplitDiff;
|
|
45
46
|
/**
|
|
46
|
-
* Pair indices whose left or right text
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
47
|
+
* Pair indices whose left or right text matches the query. A pair counts once
|
|
48
|
+
* however many times the query appears, so split search highlights the whole pair
|
|
49
|
+
* on both columns and a single "current" pair is stepped through — not each
|
|
50
|
+
* individual left/right occurrence.
|
|
50
51
|
* @param pairs - the split pairs.
|
|
51
52
|
* @param query - the query text; an empty query matches nothing.
|
|
53
|
+
* @param options - case and whole-word narrowing.
|
|
52
54
|
* @returns matching pair indices, in file order.
|
|
53
55
|
*/
|
|
54
|
-
export declare function searchPairs(pairs: readonly SplitPair[], query: string): number[];
|
|
56
|
+
export declare function searchPairs(pairs: readonly SplitPair[], query: string, options?: SearchOptions): number[];
|
|
@@ -7,17 +7,17 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { SessionId } from '@deepseek-ai/dsh-client-connection/client';
|
|
9
9
|
import type { HostObservable } from '@deepseek-ai/dsh-client-ui-slots';
|
|
10
|
-
import type { DiffApprovalBlockRange, DiffApprovalOpenAction, VcsImportValue } from '../types.ts';
|
|
10
|
+
import type { DiffApprovalBlockRange, DiffApprovalOpenAction, DiffApprovalRefreshValue, VcsImportValue } from '../types.ts';
|
|
11
11
|
import type { PendingDiffSnapshot } from './slots.ts';
|
|
12
12
|
import type { DiffApprovalPort } from './port.ts';
|
|
13
13
|
/** The observable the panel reads and the plugin body drives. */
|
|
14
14
|
export interface PendingDiffStore extends HostObservable<PendingDiffSnapshot> {
|
|
15
15
|
/** Re-read one session's pending list (an absent session empties the view). */
|
|
16
16
|
refresh: (sessionId: SessionId | undefined) => Promise<void>;
|
|
17
|
-
/** Keep one operation. */
|
|
18
|
-
keep: (sessionId: SessionId, id: string) => Promise<void>;
|
|
19
|
-
/** Revert one operation. */
|
|
20
|
-
revert: (sessionId: SessionId, id: string) => Promise<void>;
|
|
17
|
+
/** Keep one operation. `keepListed` leaves the resolved entry in the list. */
|
|
18
|
+
keep: (sessionId: SessionId, id: string, keepListed?: boolean) => Promise<void>;
|
|
19
|
+
/** Revert one operation. `keepListed` leaves the resolved entry in the list. */
|
|
20
|
+
revert: (sessionId: SessionId, id: string, keepListed?: boolean) => Promise<void>;
|
|
21
21
|
/** Keep one diff block, then refresh so the entry's diff reflects the accept. */
|
|
22
22
|
blockKeep: (sessionId: SessionId, id: string, block: DiffApprovalBlockRange, removeWhenResolved?: boolean) => Promise<void>;
|
|
23
23
|
/** Revert one diff block, then refresh so the entry's diff reflects the undo. */
|
|
@@ -28,6 +28,9 @@ export interface PendingDiffStore extends HostObservable<PendingDiffSnapshot> {
|
|
|
28
28
|
redo: (sessionId: SessionId) => Promise<string | undefined>;
|
|
29
29
|
/** Import the workspace's local VCS changes as pending entries, then refresh. */
|
|
30
30
|
importVcs: (sessionId: SessionId, includeUntracked: boolean) => Promise<VcsImportValue>;
|
|
31
|
+
/** Replace one entry's diff with the file's current local VCS change, then
|
|
32
|
+
* refresh; resolves to what the scan found. */
|
|
33
|
+
refreshVcs: (sessionId: SessionId, id: string, includeUntracked: boolean) => Promise<DiffApprovalRefreshValue>;
|
|
31
34
|
/** Open one file with its default application or reveal it in the folder. */
|
|
32
35
|
open: (sessionId: SessionId, id: string, action: DiffApprovalOpenAction) => Promise<void>;
|
|
33
36
|
/** Keep every pending entry of one session in a single host call, then refresh. */
|
package/lib/types/index.d.ts
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
*/
|
|
36
36
|
import { Context } from '@deepseek-ai/cordis';
|
|
37
37
|
import type { DiffApprovalOpenAction } from './types.ts';
|
|
38
|
-
export type { DiffApprovalActionOutcome, DiffApprovalActionValue, DiffApprovalBlockRange, DiffApprovalBlockTarget, DiffApprovalListValue, DiffApprovalOpenAction, DiffApprovalOpenValue, PendingEntry, PendingEntryKind, PendingFileDiff, } from './types.ts';
|
|
38
|
+
export type { DiffApprovalActionOutcome, DiffApprovalActionValue, DiffApprovalBlockRange, DiffApprovalBlockTarget, DiffApprovalListValue, DiffApprovalOpenAction, DiffApprovalOpenValue, DiffApprovalRefreshOutcome, DiffApprovalRefreshValue, PendingEntry, PendingEntryKind, PendingFileDiff, } from './types.ts';
|
|
39
39
|
export { PendingDiffStore } from './pending.ts';
|
|
40
40
|
export { PendingPersistence, defaultStorageDir } from './persist.ts';
|
|
41
41
|
export { defaultOpenPath } from './open.ts';
|
package/lib/types/types.d.ts
CHANGED
|
@@ -123,6 +123,22 @@ export interface DiffApprovalBulkValue {
|
|
|
123
123
|
/** How many pending entries were kept/reverted (0 when the session had none). */
|
|
124
124
|
affected: number;
|
|
125
125
|
}
|
|
126
|
+
/** What one file's VCS refresh found. */
|
|
127
|
+
export type DiffApprovalRefreshOutcome =
|
|
128
|
+
/** The tracked diff was replaced with the file's current VCS change. */
|
|
129
|
+
'refreshed'
|
|
130
|
+
/** A VCS change exists but matches what the entry already tracks. */
|
|
131
|
+
| 'unchanged'
|
|
132
|
+
/** The file has no local VCS change (untracked-and-excluded, or already clean). */
|
|
133
|
+
| 'no-change'
|
|
134
|
+
/** No pending entry existed for the id. */
|
|
135
|
+
| 'missing'
|
|
136
|
+
/** The workspace is not inside a git/svn/p4 checkout. */
|
|
137
|
+
| 'no-vcs';
|
|
138
|
+
/** Value returned by the channel's vcs-refresh endpoint. */
|
|
139
|
+
export interface DiffApprovalRefreshValue {
|
|
140
|
+
outcome: DiffApprovalRefreshOutcome;
|
|
141
|
+
}
|
|
126
142
|
/** Value returned by the channel's keep and revert endpoints. */
|
|
127
143
|
export interface DiffApprovalActionValue {
|
|
128
144
|
/** What the request did; `missing` means no pending entry existed. */
|
package/lib/types/vcs.d.ts
CHANGED
|
@@ -68,6 +68,12 @@ export interface VcsImportInput {
|
|
|
68
68
|
workspaceRoot: string;
|
|
69
69
|
/** Whether new/untracked files are imported (git `??`, svn `?`). */
|
|
70
70
|
includeUntracked: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Restrict the scan to one absolute path (a file, or a directory's subtree).
|
|
73
|
+
* Absent scans the whole workspace, which is what an import does; the review
|
|
74
|
+
* panel's per-file refresh passes the file so a large tree is not rescanned.
|
|
75
|
+
*/
|
|
76
|
+
scope?: string | undefined;
|
|
71
77
|
shell: ShellExecutorLike;
|
|
72
78
|
/** Reads working-file content (the host passes a node fs reader). */
|
|
73
79
|
readText: VcsFileReader;
|
package/package.json
CHANGED