dsh-diff-approval 0.25.0 → 0.26.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 +707 -343
- package/lib/index.js +45 -17
- package/lib/types/client/PendingPanel.d.ts +24 -0
- package/lib/types/client/discussion.d.ts +16 -0
- package/lib/types/client/locales.d.ts +14 -4
- package/lib/types/client/panel-memory.d.ts +7 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -3029,6 +3029,50 @@ function apply(ctx, config) {
|
|
|
3029
3029
|
duplicates: 0
|
|
3030
3030
|
}
|
|
3031
3031
|
};
|
|
3032
|
+
/**
|
|
3033
|
+
* Admit what the add found and answer the wire value. Shared by the two ways in — the
|
|
3034
|
+
* one file a reader named, and the scan of a path — so a named file is listed, counted
|
|
3035
|
+
* and identified exactly like a scanned one.
|
|
3036
|
+
*
|
|
3037
|
+
* @param candidates - the entries to admit (already built, in order).
|
|
3038
|
+
* @param directory - whether the named path was a directory (its own outcome wording).
|
|
3039
|
+
* @param cut - whether the walk hit its cap.
|
|
3040
|
+
* @returns the channel's answer for this add.
|
|
3041
|
+
*/
|
|
3042
|
+
const admitNamed = async (candidates, directory, cut) => {
|
|
3043
|
+
const listed = new Set(store.list(target.sessionId).map((entry) => pathIdentity(entry.path)));
|
|
3044
|
+
const fresh = candidates.filter((entry) => !listed.has(pathIdentity(entry.path)));
|
|
3045
|
+
const duplicates = candidates.length - fresh.length;
|
|
3046
|
+
const added = await foldBatch(target.sessionId, fresh, true);
|
|
3047
|
+
const outcome = added > 0 ? "added" : duplicates > 0 ? "duplicate" : directory ? "empty" : "unchanged";
|
|
3048
|
+
const single = directory ? void 0 : fresh[0] ?? store.list(target.sessionId).find((entry) => pathIdentity(entry.path) === pathIdentity(absolute));
|
|
3049
|
+
return {
|
|
3050
|
+
ok: true,
|
|
3051
|
+
value: {
|
|
3052
|
+
outcome,
|
|
3053
|
+
added,
|
|
3054
|
+
duplicates,
|
|
3055
|
+
...single === void 0 ? {} : { id: single.id },
|
|
3056
|
+
...cut ? { truncated: true } : {}
|
|
3057
|
+
}
|
|
3058
|
+
};
|
|
3059
|
+
};
|
|
3060
|
+
const now = Date.now();
|
|
3061
|
+
if (target.exact) {
|
|
3062
|
+
const existing = store.list(target.sessionId).find((entry) => pathIdentity(entry.path) === pathIdentity(absolute));
|
|
3063
|
+
if (existing !== void 0) return await admitNamed([existing], false, false);
|
|
3064
|
+
const content = await readTextOrNone(absolute, signal);
|
|
3065
|
+
return await admitNamed(content === void 0 ? [] : [{
|
|
3066
|
+
id: absolute,
|
|
3067
|
+
sessionId: target.sessionId,
|
|
3068
|
+
path: absolute,
|
|
3069
|
+
kind: "edit",
|
|
3070
|
+
oldText: content,
|
|
3071
|
+
newText: content,
|
|
3072
|
+
updatedAt: now,
|
|
3073
|
+
sessionIds: [target.sessionId]
|
|
3074
|
+
}], false, false);
|
|
3075
|
+
}
|
|
3032
3076
|
const root = detectVcsRoot(workspace.path);
|
|
3033
3077
|
if (root === void 0) return {
|
|
3034
3078
|
ok: true,
|
|
@@ -3063,7 +3107,6 @@ function apply(ctx, config) {
|
|
|
3063
3107
|
}
|
|
3064
3108
|
};
|
|
3065
3109
|
}
|
|
3066
|
-
const now = Date.now();
|
|
3067
3110
|
const candidates = changes.map((change) => ({
|
|
3068
3111
|
id: change.path,
|
|
3069
3112
|
sessionId: target.sessionId,
|
|
@@ -3101,22 +3144,7 @@ function apply(ctx, config) {
|
|
|
3101
3144
|
});
|
|
3102
3145
|
}
|
|
3103
3146
|
}
|
|
3104
|
-
|
|
3105
|
-
const fresh = candidates.filter((entry) => !listed.has(pathIdentity(entry.path)));
|
|
3106
|
-
const duplicates = candidates.length - fresh.length;
|
|
3107
|
-
const added = await foldBatch(target.sessionId, fresh, true);
|
|
3108
|
-
const outcome = added > 0 ? "added" : duplicates > 0 ? "duplicate" : isDirectory ? "empty" : "unchanged";
|
|
3109
|
-
const single = isDirectory ? void 0 : fresh[0] ?? store.list(target.sessionId).find((entry) => pathIdentity(entry.path) === pathIdentity(absolute));
|
|
3110
|
-
return {
|
|
3111
|
-
ok: true,
|
|
3112
|
-
value: {
|
|
3113
|
-
outcome,
|
|
3114
|
-
added,
|
|
3115
|
-
duplicates,
|
|
3116
|
-
...single === void 0 ? {} : { id: single.id },
|
|
3117
|
-
...truncated ? { truncated: true } : {}
|
|
3118
|
-
}
|
|
3119
|
-
};
|
|
3147
|
+
return await admitNamed(candidates, isDirectory, truncated);
|
|
3120
3148
|
}
|
|
3121
3149
|
case "open": {
|
|
3122
3150
|
const target = openTargetOf(payload);
|
|
@@ -22,6 +22,30 @@ export declare function diffPathsMatch(chipPath: string, filePath: string, works
|
|
|
22
22
|
* breakpoint so the two stay consistent. */
|
|
23
23
|
export declare const SIDEBAR_AUTO_COLLAPSE_PX = 1024;
|
|
24
24
|
export declare function wrapInto(text: string, widthPx: number, measure: (t: string) => number, tabPx: number): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Wrap one line of a turn the way the browser lays it out: the prose by character, each inline-code
|
|
27
|
+
* chip as one box that cannot be broken.
|
|
28
|
+
*
|
|
29
|
+
* `wrapInto` walks characters and nothing else, which is right for the code view and wrong here:
|
|
30
|
+
* a turn's chip is an inline-flex box (the chat's own inline code, see `.discussionCode`), so half
|
|
31
|
+
* of a chip never lands on the next line — it moves over whole, and the text after it starts after
|
|
32
|
+
* the box rather than inside it. Measuring the chip as ordinary text therefore reserves the wrong
|
|
33
|
+
* number of rows on exactly the lines that carry one, and a row too few is a clipped last line.
|
|
34
|
+
*
|
|
35
|
+
* The prose segments are wrapped by `wrapInto` itself, so a line with a chip in it breaks its words
|
|
36
|
+
* and its spaces exactly where a line without one does; only the chips are placed as boxes.
|
|
37
|
+
*
|
|
38
|
+
* @param line - the line's text, markers and all: the runs are read the way the render reads them.
|
|
39
|
+
* @param room - the width the line wraps in.
|
|
40
|
+
* @param measure - the prose measurer.
|
|
41
|
+
* @param chipWidth - the width of one chip's BOX (its text in the chip's own font, plus its padding
|
|
42
|
+
* and its hairline).
|
|
43
|
+
* @param tabPx - one tab stop, in px.
|
|
44
|
+
* @returns the row count for the line.
|
|
45
|
+
*/
|
|
46
|
+
export declare function wrapChipRows(line: string, room: number, measure: (text: string) => number, chipWidth: (text: string) => number, tabPx: number): number;
|
|
47
|
+
/** Create a measurer bound to `font`; falls back to a rough char estimate. */
|
|
48
|
+
export declare function makeMeasurer(font: string | undefined): ((text: string) => number) | undefined;
|
|
25
49
|
/** Full panel props composed by the sidebar footer-action slot. */
|
|
26
50
|
export type PendingPanelProps = PropsRuntime<'sidebar.footer.action'> & InjectFace<PendingPanelFace> & PropsLocale<'diff-approval'> & PendingPanelDockProps;
|
|
27
51
|
/** How the panel is hosted when it is not the footer's floating overlay: the
|
|
@@ -264,6 +264,22 @@ export declare function discussionOverlapping(discussions: readonly Discussion[]
|
|
|
264
264
|
* @returns the re-anchored discussion (the same object when nothing moved).
|
|
265
265
|
*/
|
|
266
266
|
export declare function remapDiscussion(discussion: Discussion, lineOf: (row: number) => number | undefined, textOf: (row: number) => string, rowCount: number): Discussion;
|
|
267
|
+
/**
|
|
268
|
+
* Re-anchor a whole file's blocks in one pass.
|
|
269
|
+
*
|
|
270
|
+
* Each block is re-anchored exactly as `remapDiscussion` does it — same rules, same identities,
|
|
271
|
+
* same "nothing moved, same object back" contract — but the row windows every quote is looked up
|
|
272
|
+
* in are joined ONCE for the whole list. A rebuild (a keep, an edit, a switched file) runs this
|
|
273
|
+
* over every block a file has, and joining the rows per block is what made the pass cost grow with
|
|
274
|
+
* the number of comments on a long file.
|
|
275
|
+
*
|
|
276
|
+
* @param discussions - the blocks to re-anchor.
|
|
277
|
+
* @param lineOf - the new-file line number of a row, or `undefined` for a row that has none.
|
|
278
|
+
* @param textOf - the text of a row, for the quote comparison.
|
|
279
|
+
* @param rowCount - how many rows the current model has.
|
|
280
|
+
* @returns the re-anchored blocks, in the order they came in.
|
|
281
|
+
*/
|
|
282
|
+
export declare function remapDiscussions(discussions: readonly Discussion[], lineOf: (row: number) => number | undefined, textOf: (row: number) => string, rowCount: number): readonly Discussion[];
|
|
267
283
|
/** What the selection frame shows for the current selection. */
|
|
268
284
|
export interface SelectionFrameActions {
|
|
269
285
|
/** Whether keep/revert apply (the selection fully covers change blocks). */
|
|
@@ -11,6 +11,12 @@ export declare const zh: {
|
|
|
11
11
|
'panel.readFailed': string;
|
|
12
12
|
'panel.group.current': string;
|
|
13
13
|
'panel.group.others': string;
|
|
14
|
+
'panel.tabs': string;
|
|
15
|
+
'panel.tab.pending': string;
|
|
16
|
+
'panel.tab.comments': string;
|
|
17
|
+
'panel.commentsEmpty': string;
|
|
18
|
+
'panel.commentOutdated': string;
|
|
19
|
+
'panel.commentEmptyTitle': string;
|
|
14
20
|
'panel.aria': string;
|
|
15
21
|
'panel.stats': string;
|
|
16
22
|
'panel.blockPosition': string;
|
|
@@ -98,8 +104,7 @@ export declare const zh: {
|
|
|
98
104
|
'discussion.promptRuleSkill': string;
|
|
99
105
|
'discussion.thinking': string;
|
|
100
106
|
'discussion.queued': string;
|
|
101
|
-
'discussion.
|
|
102
|
-
'discussion.quote': string;
|
|
107
|
+
'discussion.outdatedQuote': string;
|
|
103
108
|
'discussion.hidden': string;
|
|
104
109
|
'discussion.failed': string;
|
|
105
110
|
'discussion.stopped': string;
|
|
@@ -230,6 +235,12 @@ export declare const en: {
|
|
|
230
235
|
'panel.readFailed': string;
|
|
231
236
|
'panel.group.current': string;
|
|
232
237
|
'panel.group.others': string;
|
|
238
|
+
'panel.tabs': string;
|
|
239
|
+
'panel.tab.pending': string;
|
|
240
|
+
'panel.tab.comments': string;
|
|
241
|
+
'panel.commentsEmpty': string;
|
|
242
|
+
'panel.commentOutdated': string;
|
|
243
|
+
'panel.commentEmptyTitle': string;
|
|
233
244
|
'panel.aria': string;
|
|
234
245
|
'panel.stats': string;
|
|
235
246
|
'panel.blockPosition': string;
|
|
@@ -317,8 +328,7 @@ export declare const en: {
|
|
|
317
328
|
'discussion.promptRuleSkill': string;
|
|
318
329
|
'discussion.thinking': string;
|
|
319
330
|
'discussion.queued': string;
|
|
320
|
-
'discussion.
|
|
321
|
-
'discussion.quote': string;
|
|
331
|
+
'discussion.outdatedQuote': string;
|
|
322
332
|
'discussion.hidden': string;
|
|
323
333
|
'discussion.failed': string;
|
|
324
334
|
'discussion.stopped': string;
|
|
@@ -14,6 +14,13 @@
|
|
|
14
14
|
* @module dsh-diff-approval/client/panel-memory
|
|
15
15
|
*/
|
|
16
16
|
import type { Discussion } from './discussion.ts';
|
|
17
|
+
/**
|
|
18
|
+
* Fired on `window` whenever a session's threads change. The threads are written by the file
|
|
19
|
+
* detail (which owns them) and read by the list pane (whose comments tab shows them all), so the
|
|
20
|
+
* event is what keeps the two in step: a reader who posts a comment sees it in the list at once
|
|
21
|
+
* instead of on the next poll.
|
|
22
|
+
*/
|
|
23
|
+
export declare const COMMENTS_CHANGED_EVENT = "dsh-diff-approval:comments-changed";
|
|
17
24
|
/**
|
|
18
25
|
* The comment threads a session's panel is holding.
|
|
19
26
|
* @param sessionId - the session, if any.
|
package/package.json
CHANGED