dsh-diff-approval 0.4.0 → 0.6.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 +655 -231
- package/lib/index.js +138 -1
- package/lib/types/client/PendingPanel.d.ts +1 -1
- package/lib/types/client/highlight.d.ts +11 -1
- package/lib/types/client/locales.d.ts +4 -0
- package/lib/types/client/port.d.ts +7 -3
- package/lib/types/client/reference.d.ts +13 -11
- package/lib/types/client/slots.d.ts +7 -1
- package/lib/types/client/store.d.ts +5 -1
- package/lib/types/index.d.ts +1 -1
- package/lib/types/pending.d.ts +14 -0
- package/lib/types/types.d.ts +23 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -108,6 +108,28 @@ var PendingDiffStore = class {
|
|
|
108
108
|
for (const [key, indexedId] of this.pathIndex) if (indexedId === id) this.pathIndex.delete(key);
|
|
109
109
|
return true;
|
|
110
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Advance one entry's tracked content after a block-level keep/revert. The
|
|
113
|
+
* entry keeps its id and path; only the given side's text and capture time
|
|
114
|
+
* move. When the caller decides the sides now match, it removes the entry
|
|
115
|
+
* instead of updating it.
|
|
116
|
+
* @param sessionId - the owning session.
|
|
117
|
+
* @param id - the entry id.
|
|
118
|
+
* @param patch - the side to advance (`oldText` for keep, `newText` for revert).
|
|
119
|
+
* @returns whether the entry changed.
|
|
120
|
+
*/
|
|
121
|
+
update(sessionId, id, patch) {
|
|
122
|
+
const key = entryKey(sessionId, id);
|
|
123
|
+
const entry = this.entries.get(key);
|
|
124
|
+
if (entry === void 0) return false;
|
|
125
|
+
const next = {
|
|
126
|
+
...entry,
|
|
127
|
+
...patch,
|
|
128
|
+
updatedAt: Date.now()
|
|
129
|
+
};
|
|
130
|
+
this.entries.set(key, next);
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
111
133
|
/** Total entry count across all sessions. */
|
|
112
134
|
get size() {
|
|
113
135
|
return this.entries.size;
|
|
@@ -760,7 +782,10 @@ function apply(ctx, config) {
|
|
|
760
782
|
if (sessionId === void 0) return rpcError("sessionId must be a non-empty string");
|
|
761
783
|
return {
|
|
762
784
|
ok: true,
|
|
763
|
-
value: {
|
|
785
|
+
value: {
|
|
786
|
+
files: await listWithState(await workspaceEntries(sessionId)),
|
|
787
|
+
workspacePath: workspaceOf(sessionId)?.path
|
|
788
|
+
}
|
|
764
789
|
};
|
|
765
790
|
}
|
|
766
791
|
case "keep": {
|
|
@@ -797,6 +822,51 @@ function apply(ctx, config) {
|
|
|
797
822
|
value: { outcome: "reverted" }
|
|
798
823
|
};
|
|
799
824
|
}
|
|
825
|
+
case "block-keep": {
|
|
826
|
+
const blockTarget = blockTargetOf(payload);
|
|
827
|
+
if (blockTarget === void 0) return rpcError("sessionId, id, and block must be valid");
|
|
828
|
+
await ensureLoaded(blockTarget.sessionId);
|
|
829
|
+
const entry = store.get(blockTarget.sessionId, blockTarget.id);
|
|
830
|
+
if (entry === void 0) return {
|
|
831
|
+
ok: true,
|
|
832
|
+
value: { outcome: "missing" }
|
|
833
|
+
};
|
|
834
|
+
const accepted = contentRangeOf(entry.newText, blockTarget.block.newStart, blockTarget.block.newEnd);
|
|
835
|
+
const updatedOld = replaceContentLines(entry.oldText, blockTarget.block.oldStart, blockTarget.block.oldEnd, accepted);
|
|
836
|
+
if (updatedOld === entry.newText) store.remove(blockTarget.sessionId, blockTarget.id);
|
|
837
|
+
else store.update(blockTarget.sessionId, blockTarget.id, { oldText: updatedOld });
|
|
838
|
+
await persistSession(blockTarget.sessionId);
|
|
839
|
+
return {
|
|
840
|
+
ok: true,
|
|
841
|
+
value: { outcome: "kept" }
|
|
842
|
+
};
|
|
843
|
+
}
|
|
844
|
+
case "block-revert": {
|
|
845
|
+
const blockTarget = blockTargetOf(payload);
|
|
846
|
+
if (blockTarget === void 0) return rpcError("sessionId, id, and block must be valid");
|
|
847
|
+
await ensureLoaded(blockTarget.sessionId);
|
|
848
|
+
const entry = store.get(blockTarget.sessionId, blockTarget.id);
|
|
849
|
+
if (entry === void 0) return {
|
|
850
|
+
ok: true,
|
|
851
|
+
value: { outcome: "missing" }
|
|
852
|
+
};
|
|
853
|
+
const restored = contentRangeOf(entry.oldText, blockTarget.block.oldStart, blockTarget.block.oldEnd);
|
|
854
|
+
const updatedNew = replaceContentLines(entry.newText, blockTarget.block.newStart, blockTarget.block.newEnd, restored);
|
|
855
|
+
try {
|
|
856
|
+
const resolved = await ctx.fs.resolve(entry.path, { signal });
|
|
857
|
+
if (entry.kind === "create" && updatedNew === "") await rm(ctx.fs.processPath(resolved), { force: true });
|
|
858
|
+
else await ctx.fs.writeText(resolved, updatedNew, void 0, signal);
|
|
859
|
+
} catch (error) {
|
|
860
|
+
return rpcError(`block revert failed: ${errorMessage(error)}`);
|
|
861
|
+
}
|
|
862
|
+
if (updatedNew === entry.oldText) store.remove(blockTarget.sessionId, blockTarget.id);
|
|
863
|
+
else store.update(blockTarget.sessionId, blockTarget.id, { newText: updatedNew });
|
|
864
|
+
await persistSession(blockTarget.sessionId);
|
|
865
|
+
return {
|
|
866
|
+
ok: true,
|
|
867
|
+
value: { outcome: "reverted" }
|
|
868
|
+
};
|
|
869
|
+
}
|
|
800
870
|
case "open": {
|
|
801
871
|
const target = openTargetOf(payload);
|
|
802
872
|
if (target === void 0) return rpcError("sessionId, id, and action must be valid");
|
|
@@ -836,6 +906,73 @@ function sessionOf(payload) {
|
|
|
836
906
|
const value = payload.sessionId;
|
|
837
907
|
return typeof value === "string" && value.length > 0 ? SessionId(value) : void 0;
|
|
838
908
|
}
|
|
909
|
+
/** The content lines of `text`, matching the diff's line numbering (a single
|
|
910
|
+
trailing newline is a terminator, not an extra empty line). */
|
|
911
|
+
function contentLinesOf(text) {
|
|
912
|
+
if (text === "") return [];
|
|
913
|
+
const lines = text.split("\n");
|
|
914
|
+
if (lines[lines.length - 1] === "") lines.pop();
|
|
915
|
+
return lines;
|
|
916
|
+
}
|
|
917
|
+
/** Rebuild text from content lines, keeping `original`'s trailing-newline convention. */
|
|
918
|
+
function fromContentLines(original, lines) {
|
|
919
|
+
if (lines.length === 0) return "";
|
|
920
|
+
return lines.join("\n") + (original.endsWith("\n") ? "\n" : "");
|
|
921
|
+
}
|
|
922
|
+
/** The content lines [start..end] (1-based inclusive) of `text`; empty when start > end. */
|
|
923
|
+
function contentRangeOf(text, start, end) {
|
|
924
|
+
if (start > end) return [];
|
|
925
|
+
return contentLinesOf(text).slice(start - 1, end);
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Replace the content lines [start..end] (1-based) of `text` with `replacement`
|
|
929
|
+
* lines. An empty range (`start > end`) inserts before line `start`. Out-of-range
|
|
930
|
+
* bounds clamp; the trailing-newline convention of `text` is preserved.
|
|
931
|
+
*/
|
|
932
|
+
function replaceContentLines(text, start, end, replacement) {
|
|
933
|
+
const lines = contentLinesOf(text);
|
|
934
|
+
const count = lines.length;
|
|
935
|
+
if (start > end) {
|
|
936
|
+
const at = Math.min(Math.max(start, 1), count + 1);
|
|
937
|
+
return fromContentLines(text, [
|
|
938
|
+
...lines.slice(0, at - 1),
|
|
939
|
+
...replacement,
|
|
940
|
+
...lines.slice(at - 1)
|
|
941
|
+
]);
|
|
942
|
+
}
|
|
943
|
+
const s = Math.min(Math.max(start, 1), count + 1);
|
|
944
|
+
const e = Math.min(Math.max(end, 1), count);
|
|
945
|
+
if (s > e) return text;
|
|
946
|
+
return fromContentLines(text, [
|
|
947
|
+
...lines.slice(0, s - 1),
|
|
948
|
+
...replacement,
|
|
949
|
+
...lines.slice(e)
|
|
950
|
+
]);
|
|
951
|
+
}
|
|
952
|
+
/** Narrow a wire payload to one block keep/revert target. */
|
|
953
|
+
function blockTargetOf(payload) {
|
|
954
|
+
const target = targetOf(payload);
|
|
955
|
+
if (target === void 0) return void 0;
|
|
956
|
+
if (typeof payload !== "object" || payload === null || Array.isArray(payload)) return void 0;
|
|
957
|
+
const block = payload.block;
|
|
958
|
+
if (typeof block !== "object" || block === null || Array.isArray(block)) return void 0;
|
|
959
|
+
const { oldStart, oldEnd, newStart, newEnd } = block;
|
|
960
|
+
if (![
|
|
961
|
+
oldStart,
|
|
962
|
+
oldEnd,
|
|
963
|
+
newStart,
|
|
964
|
+
newEnd
|
|
965
|
+
].every((value) => typeof value === "number" && Number.isFinite(value))) return void 0;
|
|
966
|
+
return {
|
|
967
|
+
...target,
|
|
968
|
+
block: {
|
|
969
|
+
oldStart,
|
|
970
|
+
oldEnd,
|
|
971
|
+
newStart,
|
|
972
|
+
newEnd
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
}
|
|
839
976
|
/** Narrow a wire payload to one keep/revert target. */
|
|
840
977
|
function targetOf(payload) {
|
|
841
978
|
const sessionId = sessionOf(payload);
|
|
@@ -4,4 +4,4 @@ import type { PendingPanelFace } from './slots.ts';
|
|
|
4
4
|
/** Full panel props composed by the sidebar footer-action slot. */
|
|
5
5
|
export type PendingPanelProps = PropsRuntime<'sidebar.footer.action'> & InjectFace<PendingPanelFace> & PropsLocale<'diff-approval'>;
|
|
6
6
|
/** Render the pending-edit review panel and its unified footer action. */
|
|
7
|
-
export declare function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onOpen, t, }: PendingPanelProps): import("react").JSX.Element;
|
|
7
|
+
export declare function PendingPanel({ wide, useSessions, usePending, onRefresh, onKeep, onRevert, onBlockKeep, onBlockRevert, onOpen, t, }: PendingPanelProps): import("react").JSX.Element;
|
|
@@ -15,8 +15,18 @@ export interface HighlightSpan {
|
|
|
15
15
|
text: string;
|
|
16
16
|
style: CSSProperties;
|
|
17
17
|
}
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Primary grammar ids offered in the viewer's language selector, in picker
|
|
20
|
+
* order (alphabetical). Kept explicit instead of deriving from `LANGS`: some
|
|
21
|
+
* `@shikijs/langs` modules are composite grammars whose `.name` arrays expose
|
|
22
|
+
* embedded sub-grammars (e.g. C++ → `[regexp, glsl, cpp-macro, cpp]`,
|
|
23
|
+
* TypeScript → `[typescript, jsx, tsx, graphql]`), which would otherwise leak
|
|
24
|
+
* ids like `regexp`, `glsl`, `haml` into the menu. `LANGS` still registers the
|
|
25
|
+
* full grammars (including their embedded languages) for highlighting.
|
|
26
|
+
*/
|
|
19
27
|
export declare const HIGHLIGHT_LANGS: string[];
|
|
28
|
+
/** Conventional display name for a grammar id, falling back to the id itself. */
|
|
29
|
+
export declare function languageDisplayName(id: string): string;
|
|
20
30
|
/**
|
|
21
31
|
* Tokenize `code` into per-line highlighted runs when `lang` names a
|
|
22
32
|
* registered grammar; `undefined` means the caller renders its plain fallback.
|
|
@@ -29,6 +29,8 @@ export declare const zh: {
|
|
|
29
29
|
'action.langAuto': string;
|
|
30
30
|
'action.langAutoDetected': string;
|
|
31
31
|
'action.langSelect': string;
|
|
32
|
+
'action.expand': string;
|
|
33
|
+
'action.exitFullscreen': string;
|
|
32
34
|
'action.close': string;
|
|
33
35
|
'status.kept': string;
|
|
34
36
|
'status.reverted': string;
|
|
@@ -71,6 +73,8 @@ export declare const en: {
|
|
|
71
73
|
'action.langAuto': string;
|
|
72
74
|
'action.langAutoDetected': string;
|
|
73
75
|
'action.langSelect': string;
|
|
76
|
+
'action.expand': string;
|
|
77
|
+
'action.exitFullscreen': string;
|
|
74
78
|
'action.close': string;
|
|
75
79
|
'status.kept': string;
|
|
76
80
|
'status.reverted': string;
|
|
@@ -5,17 +5,21 @@
|
|
|
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, DiffApprovalOpenAction, DiffApprovalOpenValue
|
|
8
|
+
import type { DiffApprovalActionValue, DiffApprovalBlockRange, DiffApprovalListValue, DiffApprovalOpenAction, DiffApprovalOpenValue } 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
|
-
/** Read one session's pending entries, oldest capture first. */
|
|
14
|
-
list(sessionId: SessionId): Promise<
|
|
13
|
+
/** Read one session's pending entries (plus its workspace root), oldest capture first. */
|
|
14
|
+
list(sessionId: SessionId): Promise<DiffApprovalListValue>;
|
|
15
15
|
/** Keep one operation. */
|
|
16
16
|
keep(sessionId: SessionId, id: string): Promise<DiffApprovalActionValue>;
|
|
17
17
|
/** Revert one operation. */
|
|
18
18
|
revert(sessionId: SessionId, id: string): Promise<DiffApprovalActionValue>;
|
|
19
|
+
/** Keep one diff block (accept its change into the tracked baseline). */
|
|
20
|
+
blockKeep(sessionId: SessionId, id: string, block: DiffApprovalBlockRange): Promise<DiffApprovalActionValue>;
|
|
21
|
+
/** Revert one diff block (restore its old lines in the file). */
|
|
22
|
+
blockRevert(sessionId: SessionId, id: string, block: DiffApprovalBlockRange): Promise<DiffApprovalActionValue>;
|
|
19
23
|
/** Open one file with its default application or reveal it in the folder. */
|
|
20
24
|
open(sessionId: SessionId, id: string, action: DiffApprovalOpenAction): Promise<DiffApprovalOpenValue>;
|
|
21
25
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Reference labels for the selection toolbar: a
|
|
3
|
-
*
|
|
4
|
-
* Pure derivation so the display rule is unit-testable without
|
|
2
|
+
* Reference labels for the selection toolbar: a workspace-relative path (or
|
|
3
|
+
* the absolute path when the file is outside the workspace) plus a 1-based
|
|
4
|
+
* line range. Pure derivation so the display rule is unit-testable without
|
|
5
|
+
* the panel.
|
|
5
6
|
* @module dsh-diff-approval/client/reference
|
|
6
7
|
*/
|
|
7
|
-
import type { PendingFileDiff } from '../types.ts';
|
|
8
8
|
/**
|
|
9
9
|
* Last path segment of a file path, any separator style.
|
|
10
10
|
* @param path - the path to shorten.
|
|
@@ -12,13 +12,15 @@ import type { PendingFileDiff } from '../types.ts';
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function basenameOf(path: string): string;
|
|
14
14
|
/**
|
|
15
|
-
* The path
|
|
16
|
-
* file
|
|
15
|
+
* The path embedded in a copied reference: workspace-relative (forward
|
|
16
|
+
* slashes) when the file lives inside the current workspace, the absolute
|
|
17
|
+
* path otherwise. A bare file name is never enough — a reference must resolve
|
|
18
|
+
* to exactly one file.
|
|
17
19
|
* @param path - the selected file's path.
|
|
18
|
-
* @param
|
|
19
|
-
* @returns the
|
|
20
|
+
* @param workspacePath - the current workspace root, or `undefined`.
|
|
21
|
+
* @returns the reference path.
|
|
20
22
|
*/
|
|
21
|
-
export declare function
|
|
23
|
+
export declare function referencePathOf(path: string, workspacePath: string | undefined): string;
|
|
22
24
|
/**
|
|
23
25
|
* Format one line range as a reference suffix: a single line number, or the
|
|
24
26
|
* inclusive range when the selection spans more than one line.
|
|
@@ -30,9 +32,9 @@ export declare function lineRangeLabel(start: number, end: number): string;
|
|
|
30
32
|
/**
|
|
31
33
|
* Build the clipboard text for a selected line range.
|
|
32
34
|
* @param path - the selected file's path.
|
|
33
|
-
* @param
|
|
35
|
+
* @param workspacePath - the current workspace root, or `undefined`.
|
|
34
36
|
* @param start - first selected line number.
|
|
35
37
|
* @param end - last selected line number.
|
|
36
38
|
* @returns the `path:range` reference text.
|
|
37
39
|
*/
|
|
38
|
-
export declare function referenceOf(path: string,
|
|
40
|
+
export declare function referenceOf(path: string, workspacePath: string | undefined, start: number, end: number): string;
|
|
@@ -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 { DiffApprovalOpenAction, PendingFileDiff } from '../types.ts';
|
|
4
|
+
import type { DiffApprovalBlockRange, DiffApprovalOpenAction, PendingFileDiff } 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. */
|
|
@@ -12,6 +12,8 @@ export interface PendingDiffSnapshot {
|
|
|
12
12
|
error?: string;
|
|
13
13
|
/** Entry ids whose keep/revert is in flight; their controls are disabled. */
|
|
14
14
|
busy: ReadonlySet<string>;
|
|
15
|
+
/** The viewing session's workspace root (when it has one); enables workspace-relative references. */
|
|
16
|
+
workspacePath?: string | undefined;
|
|
15
17
|
}
|
|
16
18
|
/** The injected face the panel component receives from the plugin body. */
|
|
17
19
|
export interface PendingPanelFace {
|
|
@@ -25,6 +27,10 @@ export interface PendingPanelFace {
|
|
|
25
27
|
onKeep: (sessionId: SessionId, id: string) => Promise<void>;
|
|
26
28
|
/** Revert one operation (restore its prior content, or remove a created file). */
|
|
27
29
|
onRevert: (sessionId: SessionId, id: string) => Promise<void>;
|
|
30
|
+
/** Keep one diff block (accept its change into the tracked baseline). */
|
|
31
|
+
onBlockKeep: (sessionId: SessionId, id: string, block: DiffApprovalBlockRange) => Promise<void>;
|
|
32
|
+
/** Revert one diff block (restore its old lines in the file). */
|
|
33
|
+
onBlockRevert: (sessionId: SessionId, id: string, block: DiffApprovalBlockRange) => Promise<void>;
|
|
28
34
|
/** Open one file with its default application or reveal it in the folder. */
|
|
29
35
|
onOpen: (sessionId: SessionId, id: string, action: DiffApprovalOpenAction) => Promise<void>;
|
|
30
36
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
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 { DiffApprovalOpenAction } from '../types.ts';
|
|
10
|
+
import type { DiffApprovalBlockRange, DiffApprovalOpenAction } 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. */
|
|
@@ -18,6 +18,10 @@ export interface PendingDiffStore extends HostObservable<PendingDiffSnapshot> {
|
|
|
18
18
|
keep: (sessionId: SessionId, id: string) => Promise<void>;
|
|
19
19
|
/** Revert one operation. */
|
|
20
20
|
revert: (sessionId: SessionId, id: string) => Promise<void>;
|
|
21
|
+
/** Keep one diff block, then refresh so the entry's diff reflects the accept. */
|
|
22
|
+
blockKeep: (sessionId: SessionId, id: string, block: DiffApprovalBlockRange) => Promise<void>;
|
|
23
|
+
/** Revert one diff block, then refresh so the entry's diff reflects the undo. */
|
|
24
|
+
blockRevert: (sessionId: SessionId, id: string, block: DiffApprovalBlockRange) => Promise<void>;
|
|
21
25
|
/** Open one file with its default application or reveal it in the folder. */
|
|
22
26
|
open: (sessionId: SessionId, id: string, action: DiffApprovalOpenAction) => Promise<void>;
|
|
23
27
|
/** Drop every local fact (used on connection reset). */
|
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, DiffApprovalListValue, DiffApprovalOpenAction, DiffApprovalOpenValue, PendingEntry, PendingEntryKind, PendingFileDiff, } from './types.ts';
|
|
38
|
+
export type { DiffApprovalActionOutcome, DiffApprovalActionValue, DiffApprovalBlockRange, DiffApprovalBlockTarget, DiffApprovalListValue, DiffApprovalOpenAction, DiffApprovalOpenValue, 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/pending.d.ts
CHANGED
|
@@ -55,6 +55,20 @@ export declare class PendingDiffStore {
|
|
|
55
55
|
* @returns whether an entry was removed.
|
|
56
56
|
*/
|
|
57
57
|
remove(sessionId: SessionId, id: string): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Advance one entry's tracked content after a block-level keep/revert. The
|
|
60
|
+
* entry keeps its id and path; only the given side's text and capture time
|
|
61
|
+
* move. When the caller decides the sides now match, it removes the entry
|
|
62
|
+
* instead of updating it.
|
|
63
|
+
* @param sessionId - the owning session.
|
|
64
|
+
* @param id - the entry id.
|
|
65
|
+
* @param patch - the side to advance (`oldText` for keep, `newText` for revert).
|
|
66
|
+
* @returns whether the entry changed.
|
|
67
|
+
*/
|
|
68
|
+
update(sessionId: SessionId, id: string, patch: {
|
|
69
|
+
oldText?: string;
|
|
70
|
+
newText?: string;
|
|
71
|
+
}): boolean;
|
|
58
72
|
/** Total entry count across all sessions. */
|
|
59
73
|
get size(): number;
|
|
60
74
|
}
|
package/lib/types/types.d.ts
CHANGED
|
@@ -51,6 +51,8 @@ export interface PendingFileDiff extends PendingEntry {
|
|
|
51
51
|
export interface DiffApprovalListValue {
|
|
52
52
|
/** Pending entries for the requested session, oldest capture first. */
|
|
53
53
|
files: PendingFileDiff[];
|
|
54
|
+
/** The viewing session's workspace root (when it has one), for workspace-relative references. */
|
|
55
|
+
workspacePath?: string | undefined;
|
|
54
56
|
}
|
|
55
57
|
/** What the open endpoint asks the OS to do with a file. */
|
|
56
58
|
export type DiffApprovalOpenAction = 'open' | 'reveal';
|
|
@@ -59,6 +61,27 @@ export interface DiffApprovalOpenValue {
|
|
|
59
61
|
/** What the request did; `missing` means no pending entry existed. */
|
|
60
62
|
outcome: 'opened' | 'missing';
|
|
61
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* One diff block's line ranges on the old and new sides, 1-based inclusive.
|
|
66
|
+
* A side is empty when its start exceeds its end; an empty side's start is
|
|
67
|
+
* the insertion point (the line before which content inserts) on that side.
|
|
68
|
+
*/
|
|
69
|
+
export interface DiffApprovalBlockRange {
|
|
70
|
+
/** First old-file line this block spans; the insertion point for pure additions. */
|
|
71
|
+
oldStart: number;
|
|
72
|
+
/** Last old-file line; `oldStart - 1` when the block has no old side. */
|
|
73
|
+
oldEnd: number;
|
|
74
|
+
/** First new-file line this block spans; the insertion point for pure deletions. */
|
|
75
|
+
newStart: number;
|
|
76
|
+
/** Last new-file line; `newStart - 1` when the block has no new side. */
|
|
77
|
+
newEnd: number;
|
|
78
|
+
}
|
|
79
|
+
/** Target of one block-level keep/revert: the entry plus the block range. */
|
|
80
|
+
export interface DiffApprovalBlockTarget {
|
|
81
|
+
sessionId: SessionId;
|
|
82
|
+
id: string;
|
|
83
|
+
block: DiffApprovalBlockRange;
|
|
84
|
+
}
|
|
62
85
|
/** Outcome of one keep/revert request. */
|
|
63
86
|
export type DiffApprovalActionOutcome = 'kept' | 'reverted' | 'missing';
|
|
64
87
|
/** Value returned by the channel's keep and revert endpoints. */
|
package/package.json
CHANGED