dsh-diff-approval 0.30.1 → 0.31.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/README.md +3 -0
- package/README.zh.md +3 -0
- package/assets/fonts/README.md +10 -8
- package/assets/fonts/manifest.json +1 -1
- package/lib/client.js +2414 -848
- package/lib/index.js +1672 -265
- package/lib/types/annotate-skill.d.ts +1 -1
- package/lib/types/annotate-tool.d.ts +11 -0
- package/lib/types/annotate.d.ts +28 -0
- package/lib/types/client/PendingPanel.d.ts +27 -21
- package/lib/types/client/category-color.d.ts +62 -0
- package/lib/types/client/chords.d.ts +70 -17
- package/lib/types/client/discussion.d.ts +7 -0
- package/lib/types/client/dock.d.ts +8 -3
- package/lib/types/client/dsh-icons.d.ts +39 -1
- package/lib/types/client/header-entry.d.ts +8 -7
- package/lib/types/client/locales.d.ts +42 -12
- package/lib/types/client/panel-memory.d.ts +24 -6
- package/lib/types/client/path-match.d.ts +50 -0
- package/lib/types/client/port.d.ts +29 -2
- package/lib/types/client/produced-diff.d.ts +83 -6
- package/lib/types/client/reference.d.ts +0 -2
- package/lib/types/client/remap-sync.d.ts +18 -1
- package/lib/types/client/session-seat.d.ts +47 -7
- package/lib/types/client/settings.d.ts +40 -14
- package/lib/types/client/slots.d.ts +115 -3
- package/lib/types/client/store.d.ts +50 -0
- package/lib/types/comment-ask.d.ts +47 -20
- package/lib/types/comments.d.ts +141 -33
- package/lib/types/index.d.ts +1 -1
- package/lib/types/pending.d.ts +30 -4
- package/lib/types/types.d.ts +180 -4
- package/lib/types/vcs.d.ts +2 -1
- package/lib/types/version.d.ts +162 -0
- package/package.json +4 -2
|
@@ -125,18 +125,36 @@ export declare function rememberPlacedThreads(sessionId: string | undefined, thr
|
|
|
125
125
|
*/
|
|
126
126
|
export declare function forgetPlacedThreadsNotIn(sessionId: string | undefined, listed: readonly string[]): void;
|
|
127
127
|
/**
|
|
128
|
-
* Whether the reader asked not to be asked again about
|
|
128
|
+
* Whether the reader asked not to be asked again about rows leaving THIS session's list.
|
|
129
129
|
* @param sessionId - the session the panel is reviewing; nothing is quiet without one.
|
|
130
|
-
* @param fileId - the pending entry the question was about.
|
|
131
130
|
* @returns true when the action should run without the question.
|
|
132
131
|
*/
|
|
133
|
-
export declare function removalAskQuiet(sessionId: string | undefined
|
|
132
|
+
export declare function removalAskQuiet(sessionId: string | undefined): boolean;
|
|
134
133
|
/**
|
|
135
|
-
* Stop asking whether
|
|
134
|
+
* Stop asking whether rows should leave the list, for the rest of this page: the whole session, every
|
|
135
|
+
* file in it.
|
|
136
136
|
* @param sessionId - the session the panel is reviewing; nothing is recorded without one.
|
|
137
|
-
* @param fileId - the pending entry the question was about.
|
|
138
137
|
*/
|
|
139
|
-
export declare function quietenRemovalAsk(sessionId: string | undefined
|
|
138
|
+
export declare function quietenRemovalAsk(sessionId: string | undefined): void;
|
|
139
|
+
/**
|
|
140
|
+
* Window event a dismissal dispatches, so every mounted seat hears it.
|
|
141
|
+
*
|
|
142
|
+
* The notice's chip lives in the open file's status bar, and the panel that draws it can be mounted more than
|
|
143
|
+
* once (the footer seat and the right sidebar's docked tab are separate mounts): the state above is the fact,
|
|
144
|
+
* and this event is how the OTHER mount learns about it NOW rather than at its next mount. One state, one
|
|
145
|
+
* event — so whichever seat is on screen cannot disagree about whether the reader has said "got it".
|
|
146
|
+
*/
|
|
147
|
+
export declare const UPDATE_DISMISSED_EVENT = "diff-approval:update-dismissed";
|
|
148
|
+
/**
|
|
149
|
+
* The release whose notice the reader dismissed on this page.
|
|
150
|
+
* @returns the dismissed version, or `''` when none has been dismissed here.
|
|
151
|
+
*/
|
|
152
|
+
export declare function dismissedUpdateVersion(): string;
|
|
153
|
+
/**
|
|
154
|
+
* Remember that the reader dismissed the notice for one version on this page, and tell the other mounts.
|
|
155
|
+
* @param version - the version that notice was about.
|
|
156
|
+
*/
|
|
157
|
+
export declare function dismissUpdate(version: string): void;
|
|
140
158
|
/**
|
|
141
159
|
* Remember that a session's panel was left showing one file, and where in it.
|
|
142
160
|
* @param sessionId - the session the panel was reviewing; nothing is recorded
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path comparison for the review's own references: is the path a press named the same file as one the
|
|
3
|
+
* pending list holds?
|
|
4
|
+
*
|
|
5
|
+
* Its own module because three places ask it and they must not disagree — the panel's jump to a file, the
|
|
6
|
+
* plugin's produced-file press bridge, and the store's "does the page hold this path" reader — while the
|
|
7
|
+
* panel itself is a React component module that a data-layer reader should not have to import.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-diff-approval/client/path-match
|
|
10
|
+
*/
|
|
11
|
+
/** Normalize a path for comparison: forward slashes, no trailing slash. */
|
|
12
|
+
export declare function normalizeDiffPath(p: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* The directory a file path lives in, as the path was spelled.
|
|
15
|
+
*
|
|
16
|
+
* @param p - the file's own path.
|
|
17
|
+
* @returns the directory, or undefined when the path names no directory (empty, or a bare file name).
|
|
18
|
+
*/
|
|
19
|
+
export declare function directoryOfPath(p: string | undefined): string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* The absolute path a Markdown link names, resolved the way Markdown means it: against the directory of
|
|
22
|
+
* the file the preview is showing.
|
|
23
|
+
*
|
|
24
|
+
* A link in a document is relative to THAT DOCUMENT — `../src/foo.ts` in `docs/a.md` is `src/foo.ts` at the
|
|
25
|
+
* workspace root, not `../src/foo.ts` of the page's own URL — which is why the base is the previewed file's
|
|
26
|
+
* own directory and not the workspace root. The href is percent-decoded, its `#line`/`?query` suffix is
|
|
27
|
+
* dropped, `.`/`..` are resolved, and both separators are accepted. An href that is already absolute
|
|
28
|
+
* (drive letter or a leading slash) needs no base.
|
|
29
|
+
*
|
|
30
|
+
* Exported so the panel's preview click and its tests share one implementation and cannot disagree about
|
|
31
|
+
* what a link points at.
|
|
32
|
+
*
|
|
33
|
+
* @param baseDirectory - the directory of the file being previewed, or the workspace root when that file's
|
|
34
|
+
* own path is unknown.
|
|
35
|
+
* @param href - the anchor's href, as written in the Markdown.
|
|
36
|
+
* @returns the absolute path, or undefined when it cannot be named (an empty href, or a relative href with
|
|
37
|
+
* no base to resolve against).
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveMarkdownHref(baseDirectory: string | undefined, href: string): string | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Whether a produced-file chip path and a pending file path refer to the same file, tolerant of separator
|
|
42
|
+
* style (\\ vs /) and of a workspace-relative vs absolute form. `chipPath` is typically the harness's
|
|
43
|
+
* workspace-relative forward-slash path; `filePath` is the host's absolute native-separator path. Matching
|
|
44
|
+
* is case-insensitive so a Windows drive/segment case difference does not miss the file the user clicked.
|
|
45
|
+
* @param chipPath - the path a press (or a reference) named.
|
|
46
|
+
* @param filePath - the path the pending list holds.
|
|
47
|
+
* @param workspacePath - the session's workspace root, when it is known.
|
|
48
|
+
* @returns whether the two name one file.
|
|
49
|
+
*/
|
|
50
|
+
export declare function diffPathsMatch(chipPath: string, filePath: string, workspacePath: string | undefined): boolean;
|
|
@@ -5,7 +5,7 @@
|
|
|
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 { CommentAnchor, CommentQuoteLine, DiffApprovalActionValue, DiffApprovalAddValue, DiffApprovalBlockRange, DiffApprovalBrowseValue, DiffApprovalBulkValue, DiffApprovalCommentAddValue, DiffApprovalCommentAskValue, DiffApprovalCommentRemoveManyValue, DiffApprovalCommentRemoveValue, DiffApprovalListValue, DiffApprovalOpenAction, DiffApprovalOpenValue, DiffApprovalPreviewImageValue, DiffApprovalRefreshValue, VcsImportValue } from '../types.ts';
|
|
8
|
+
import type { CommentAnchor, CommentQuoteLine, DiffApprovalActionValue, DiffApprovalAddValue, DiffApprovalBlockRange, DiffApprovalBrowseValue, DiffApprovalBulkValue, DiffApprovalCommentAddValue, DiffApprovalCommentAskValue, DiffApprovalCommentRemoveManyValue, DiffApprovalCommentRemoveValue, DiffApprovalListCountValue, DiffApprovalListValue, DiffApprovalOpenAction, DiffApprovalOpenValue, DiffApprovalPreviewImageValue, DiffApprovalRefreshValue, DiffApprovalUpdateValue, 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
|
/**
|
|
@@ -33,6 +33,18 @@ export interface CommentDraft {
|
|
|
33
33
|
export interface DiffApprovalPort {
|
|
34
34
|
/** Read one session's pending entries (plus its workspace root), oldest capture first. */
|
|
35
35
|
list(sessionId: SessionId): Promise<DiffApprovalListValue>;
|
|
36
|
+
/**
|
|
37
|
+
* Ask HOW MANY rows a full `list` read would carry for this session, reading no file content.
|
|
38
|
+
*
|
|
39
|
+
* This is the badge's own question: the full read ships every visible entry's whole old/new text
|
|
40
|
+
* (megabytes on a long-lived session), while the count costs one stat per visible row on the host and
|
|
41
|
+
* puts a single number on the wire. It is the SAME visibility rule as `list` on the host's side.
|
|
42
|
+
*
|
|
43
|
+
* A host that predates the endpoint throws instead — its handler answers `unknown endpoint
|
|
44
|
+
* "list-count"` — and the caller is expected to fall back to the full read (see
|
|
45
|
+
* `PendingDiffStore.refreshCount`), which is exactly the behaviour a page had before this verb existed.
|
|
46
|
+
*/
|
|
47
|
+
listCount(sessionId: SessionId): Promise<DiffApprovalListCountValue>;
|
|
36
48
|
/** Keep one operation. `keepListed` leaves the resolved entry in the list. */
|
|
37
49
|
keep(sessionId: SessionId, id: string, keepListed?: boolean): Promise<DiffApprovalActionValue>;
|
|
38
50
|
/** Revert one operation. `keepListed` leaves the resolved entry in the list. */
|
|
@@ -56,6 +68,8 @@ export interface DiffApprovalPort {
|
|
|
56
68
|
* path must be one regular file: a directory is refused instead of being scanned.
|
|
57
69
|
*/
|
|
58
70
|
addPath(sessionId: SessionId, path: string, includeUnchanged: boolean, exact?: boolean): Promise<DiffApprovalAddValue>;
|
|
71
|
+
/** Tell the host the reader is looking at a file: the row's unseen dot goes out. */
|
|
72
|
+
markSeen(sessionId: SessionId, id: string): Promise<void>;
|
|
59
73
|
/** Open one file with its default application or reveal it in the folder. */
|
|
60
74
|
open(sessionId: SessionId, id: string, action: DiffApprovalOpenAction): Promise<DiffApprovalOpenValue>;
|
|
61
75
|
/** Keep every pending entry of one session in a single host call (one batch). */
|
|
@@ -90,8 +104,21 @@ export interface DiffApprovalPort {
|
|
|
90
104
|
* calls is a different thing — N requests, N writes — which is what this endpoint exists to avoid.
|
|
91
105
|
*/
|
|
92
106
|
commentRemoveMany(sessionId: SessionId, ids: readonly string[]): Promise<DiffApprovalCommentRemoveManyValue>;
|
|
93
|
-
/**
|
|
107
|
+
/** Asks one stored comment as its own turn of the session. */
|
|
94
108
|
commentAsk(sessionId: SessionId, id: string, prompt: string, text: string): Promise<DiffApprovalCommentAskValue>;
|
|
109
|
+
/** Tell the host the reader is looking at a comment: the card's unseen dot goes out. */
|
|
110
|
+
commentSeen(sessionId: SessionId, id: string): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Ask whether a newer release of this plugin is published, and what changed since the
|
|
113
|
+
* installed one.
|
|
114
|
+
*
|
|
115
|
+
* No session is named: the answer is about the PACKAGE, not about a review. The host owns
|
|
116
|
+
* the whole question — it reads its own `package.json`, reaches the registry (a browser
|
|
117
|
+
* may not: no CORS header there) and decides `newer` — so a caller only has to render the
|
|
118
|
+
* answer. A host that predates the endpoint answers `unknown endpoint "update-check"`, and
|
|
119
|
+
* the caller is expected to ignore that failure exactly as it ignores a network one.
|
|
120
|
+
*/
|
|
121
|
+
checkUpdate(): Promise<DiffApprovalUpdateValue>;
|
|
95
122
|
}
|
|
96
123
|
/** Build the port over one generic RPC caller.
|
|
97
124
|
* @param rpc - the connection's channel caller.
|
|
@@ -13,35 +13,89 @@
|
|
|
13
13
|
* `a[href]` any other link naming a file `href` = the path
|
|
14
14
|
*
|
|
15
15
|
* Everything else is left entirely alone: a link to a URL, an in-page anchor, a chip naming something
|
|
16
|
-
* that is not a file, a file this panel does not hold, a modified press (Ctrl/Cmd/Shift/
|
|
17
|
-
* middle click — gestures the harness may give its own meaning later), and any press inside this
|
|
16
|
+
* that is not a file, a file LINK whose file this panel does not hold, a modified press (Ctrl/Cmd/Shift/
|
|
17
|
+
* Alt, or a middle click — gestures the harness may give its own meaning later), and any press inside this
|
|
18
18
|
* plugin's own surface, whose buttons carry paths too and belong to the panel that is already showing
|
|
19
|
-
* the file.
|
|
19
|
+
* the file. A file-list ROW is the one press taken over whether or not the panel holds its file (see
|
|
20
|
+
* `ROW_SELECTOR`); "we do not hold it" then decides what the menu offers, not whether it appears.
|
|
20
21
|
*
|
|
21
22
|
* The name of the module (and of the event it raises) dates from when the produced-file card was the
|
|
22
23
|
* only press of this kind; `data-presented-files-row` is that same list under 0.1.7's name for it.
|
|
23
24
|
*/
|
|
25
|
+
import type { PendingDiffSnapshot } from './slots.ts';
|
|
24
26
|
/** Window event dispatched when a pending file's press is taken over; the panel listens for it. */
|
|
25
27
|
export declare const CHIP_MENU_EVENT = "diff-approval:chip-menu";
|
|
28
|
+
/**
|
|
29
|
+
* Whether the page HOLDS this path — the `held` label a press carries into the menu.
|
|
30
|
+
*
|
|
31
|
+
* It is a LABEL now, not a gate: every file-list row's press becomes the menu regardless, and `held` only
|
|
32
|
+
* decides whether "在审批面板中查看" opens the row directly or adds it first (see `PendingPanel`). It is
|
|
33
|
+
* answered from the views the page is holding, i.e. from the last FULL read of each session: while every
|
|
34
|
+
* surface is shut nothing re-reads the list, so a row edited since then is labelled not-held. That is
|
|
35
|
+
* acceptable rather than hidden: the add answers the truth (a path already listed comes back `duplicate` and
|
|
36
|
+
* selects the same entry), so the item still opens the file — one request later. What the label must not do
|
|
37
|
+
* is claim a row exists that does not: `false` errs towards adding, never towards acting on nothing.
|
|
38
|
+
* @param views - the views this page is holding, one per session it has read (see `PendingDiffStore.views`).
|
|
39
|
+
* @param path - the path a press named.
|
|
40
|
+
* @returns whether any held view holds it.
|
|
41
|
+
*/
|
|
42
|
+
export declare function panelHolds(views: readonly PendingDiffSnapshot[], path: string): boolean;
|
|
26
43
|
/** Window event dispatched by the panel to open a file in the panel; PendingPanel listens for it. */
|
|
27
44
|
export declare const OPEN_FILE_EVENT = "diff-approval:open-file";
|
|
28
|
-
/** What a press tells the panel: which file,
|
|
45
|
+
/** What a press tells the panel: which file, where the press was (the menu hangs under it), and whether
|
|
46
|
+
* the review list already holds that file. */
|
|
29
47
|
export interface ProducedChipMenuDetail {
|
|
30
48
|
/** The path the pressed element names. */
|
|
31
49
|
path: string;
|
|
32
50
|
/** The pressed element's own box, in viewport coordinates. */
|
|
33
51
|
x: number;
|
|
34
52
|
y: number;
|
|
53
|
+
/**
|
|
54
|
+
* Whether the review list holds this path right now.
|
|
55
|
+
*
|
|
56
|
+
* It decides what the menu's items DO, never whether the menu appears: a row press always becomes the
|
|
57
|
+
* menu, and "not held" is what makes "在审批面板中查看" add the path first (see `PendingPanel`). It is
|
|
58
|
+
* also what a future item that NEEDS a listed entry has to gate on — pressing keep or revert for a file
|
|
59
|
+
* the host is not holding would simply fail.
|
|
60
|
+
*/
|
|
61
|
+
held: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* The session the press belongs to, when its own surface says so.
|
|
64
|
+
*
|
|
65
|
+
* WITHOUT THIS the panel can only ask the session IT is showing to add the path, and the host resolves that
|
|
66
|
+
* path against that session's workspace: a press in session A's transcript while the panel is still bound
|
|
67
|
+
* to session B answers `outside` ("路径不在当前工作区内") — measured, and it was the reader's bug: the first
|
|
68
|
+
* press refused and the second worked, because by then the panel had followed the shell's selection.
|
|
69
|
+
*
|
|
70
|
+
* Read from the ANCESTORS of the pressed element only (see `sessionOfPress`), and `undefined` when none of
|
|
71
|
+
* them names a session. That is the honest half: the panel then asks its own session and, if the host
|
|
72
|
+
* refuses, says so with the wording that names what was checked rather than blaming the path.
|
|
73
|
+
*/
|
|
74
|
+
sessionId?: string | undefined;
|
|
35
75
|
}
|
|
36
76
|
/** What the bridge needs from the host to decide, and to report. */
|
|
37
77
|
export interface ProducedChipBridge {
|
|
38
78
|
/**
|
|
39
|
-
* Whether the
|
|
40
|
-
*
|
|
79
|
+
* Whether the review list holds this path right now.
|
|
80
|
+
*
|
|
81
|
+
* A LABEL, not a gate: it rides every press this bridge takes over (a file-list row and a message's file
|
|
82
|
+
* link alike) and decides only what the menu's items DO — see `held` above. It is called synchronously on
|
|
83
|
+
* every press because that is when the menu is raised, and there is no second chance at a press.
|
|
41
84
|
*/
|
|
42
85
|
isPending: (path: string) => boolean;
|
|
43
86
|
/** A pending file's press was taken over: open the menu the press asked for. */
|
|
44
87
|
onMenu: (detail: ProducedChipMenuDetail) => void;
|
|
88
|
+
/**
|
|
89
|
+
* Whether this plugin's file menu is MOUNTED at all — the reader's setting, read at press time.
|
|
90
|
+
*
|
|
91
|
+
* `false` leaves every press exactly as DSH has it: no menu, no `preventDefault`, not even a path lookup,
|
|
92
|
+
* so the plugin's presence is indistinguishable from it never having loaded. It is read per press rather
|
|
93
|
+
* than latched, because the setting can be switched while the page is open.
|
|
94
|
+
*
|
|
95
|
+
* ABSENT means enabled: a host, or a test, that offers no setting keeps the behaviour this bridge has
|
|
96
|
+
* always had.
|
|
97
|
+
*/
|
|
98
|
+
enabled?: (() => boolean) | undefined;
|
|
45
99
|
}
|
|
46
100
|
/**
|
|
47
101
|
* Press a file's own press again — the shell's open, run the only way this plugin can run it
|
|
@@ -55,6 +109,29 @@ export interface ProducedChipBridge {
|
|
|
55
109
|
* @returns whether an element naming it was found to press.
|
|
56
110
|
*/
|
|
57
111
|
export declare function replayFilePress(path: string): boolean;
|
|
112
|
+
/**
|
|
113
|
+
* The session a press belongs to, read from the pressed element's OWN surface.
|
|
114
|
+
*
|
|
115
|
+
* Ancestors only, and the NEAREST ancestor that names a session wins — whichever spelling it uses:
|
|
116
|
+
*
|
|
117
|
+
* `data-row-key="session:<id>"` a session-LIST item, measured by this repo's e2e harness. Right for a
|
|
118
|
+
* press raised inside the sidebar, and NOT an ancestor of a transcript
|
|
119
|
+
* chip (measured on the live shell: exactly one such element in the whole
|
|
120
|
+
* document, and it does not contain the chip).
|
|
121
|
+
* `data-conversation-session` the conversation BODY, measured on the live shell — this is the one that
|
|
122
|
+
* covers the reader's press, a chip inside a message.
|
|
123
|
+
* `data-session-id` unmeasured alternative spelling, kept as a last resort.
|
|
124
|
+
*
|
|
125
|
+
* Walking the chain rather than asking three separate `closest` queries is what makes "nearest wins" true
|
|
126
|
+
* across spellings: a chip inside a session row that ALSO nests a conversation body takes the nearer
|
|
127
|
+
* body's session. A document-wide search is still deliberately NOT done — an identity hit may be a SIBLING,
|
|
128
|
+
* an `href` or an `aria-controls`, and guessing which session a press belongs to would be worse than not
|
|
129
|
+
* knowing: a wrong guess would ask a THIRD session to add the file, which this can never do.
|
|
130
|
+
*
|
|
131
|
+
* @param press - the element the press landed on.
|
|
132
|
+
* @returns the session id, or undefined when no ancestor names one.
|
|
133
|
+
*/
|
|
134
|
+
export declare function sessionOfPress(press: Element): string | undefined;
|
|
58
135
|
/**
|
|
59
136
|
* Start routing the presses that would open a file in the shell's own viewer.
|
|
60
137
|
*
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
* the panel.
|
|
6
6
|
* @module dsh-diff-approval/client/reference
|
|
7
7
|
*/
|
|
8
|
-
/** The marker that replaces a reference's line number when its lines are gone. */
|
|
9
|
-
export declare const LINE_MISSING_LABEL = "LINE_MISSING";
|
|
10
8
|
/**
|
|
11
9
|
* Last path segment of a file path, any separator style.
|
|
12
10
|
* @param path - the path to shorten.
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* `reference.ts`.
|
|
7
7
|
* @module dsh-diff-approval/client/remap-sync
|
|
8
8
|
*/
|
|
9
|
+
import type { SessionId } from '@deepseek-ai/dsh-client-connection/client';
|
|
9
10
|
import type { PendingDiffStore } from './store.ts';
|
|
10
11
|
/** One queued message's minimal content block; text blocks carry `text`, and every other kind passes through verbatim. */
|
|
11
12
|
interface QueueBlock {
|
|
@@ -28,6 +29,22 @@ export interface ReferenceRemapOpts {
|
|
|
28
29
|
readQueue: () => readonly QueueMessage[];
|
|
29
30
|
/** Apply an edit mutation to one queued message's full content. */
|
|
30
31
|
writeQueue: (itemId: string, content: readonly QueueBlock[]) => void;
|
|
32
|
+
/**
|
|
33
|
+
* The session whose list this sync follows and whose view supplies the workspace path, read per
|
|
34
|
+
* observation.
|
|
35
|
+
*
|
|
36
|
+
* The pending list is per session now, and the draft and the queue this sync rewrites belong to the
|
|
37
|
+
* session the page is SHOWING (see `shownSessionId`): observing another session's files would remap one
|
|
38
|
+
* session's references inside another session's composer. It must not be a pointer to whichever mount
|
|
39
|
+
* refreshed last either — with a dock tab on one session and the header on another, alternating polls
|
|
40
|
+
* moved that pointer about once a second and dropped these baselines with it, so a file that really
|
|
41
|
+
* changed was only ever re-seeded and never remapped. The baselines are still dropped when this answers
|
|
42
|
+
* a DIFFERENT session, so a session switch seeds fresh baselines instead of reading as "file changed".
|
|
43
|
+
*
|
|
44
|
+
* Absent, the page-wide view is observed instead — the same fallback a reader with no session gets
|
|
45
|
+
* (`viewFor(undefined)`, which answers the newest read).
|
|
46
|
+
*/
|
|
47
|
+
sessionId?: (() => SessionId | undefined) | undefined;
|
|
31
48
|
}
|
|
32
49
|
/**
|
|
33
50
|
* Subscribe to the pending store and remap references as each file's content
|
|
@@ -39,6 +56,6 @@ export interface ReferenceRemapOpts {
|
|
|
39
56
|
*/
|
|
40
57
|
export declare function attachReferenceRemap(opts: ReferenceRemapOpts): {
|
|
41
58
|
unsubscribe: () => void;
|
|
42
|
-
remapFile: (path: string, oldText: string, newText: string) => void;
|
|
59
|
+
remapFile: (sessionId: SessionId, path: string, oldText: string, newText: string) => void;
|
|
43
60
|
};
|
|
44
61
|
export {};
|
|
@@ -18,12 +18,27 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import type { SessionId } from '@deepseek-ai/dsh-client-connection/client';
|
|
20
20
|
/**
|
|
21
|
-
* Publish the session the header entry is showing.
|
|
21
|
+
* Publish the session the header entry is showing. Call it from an EFFECT, never during render.
|
|
22
|
+
*
|
|
23
|
+
* Writing this during the header's render was the cross-talk's other half: the value a render read
|
|
24
|
+
* depended on whether another component had already rendered, so two mounts could disagree about which
|
|
25
|
+
* session was showing — and, in concurrent rendering, a discarded render could publish a session that
|
|
26
|
+
* was never committed. From an effect the value only ever describes a committed render.
|
|
22
27
|
* @param id - the id, or `undefined` when the shell named none.
|
|
23
28
|
*/
|
|
24
29
|
export declare function publishSessionId(id: SessionId | undefined): void;
|
|
25
30
|
/** The session the header entry last showed, or `undefined` when it never named one. */
|
|
26
31
|
export declare function publishedSessionId(): SessionId | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* The published session as a React value: the root-scoped mounts (the sidebar footer's badge) have no
|
|
34
|
+
* session prop, so this is how they follow the one the Session header is showing.
|
|
35
|
+
*
|
|
36
|
+
* It subscribes rather than reading the module global at render time, because the write now happens in
|
|
37
|
+
* an effect: a reader that only read the global would keep whatever the value was on its first render
|
|
38
|
+
* and never notice the reader switching sessions.
|
|
39
|
+
* @returns the session the header entry is showing, or `undefined`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function usePublishedSessionId(): SessionId | undefined;
|
|
27
42
|
/**
|
|
28
43
|
* The selected session a shell's session-list state names, by every field the shells have used.
|
|
29
44
|
* @param state - the store state handed to the selector.
|
|
@@ -44,10 +59,35 @@ export declare function selectedSessionOf(state: unknown): SessionId | undefined
|
|
|
44
59
|
*/
|
|
45
60
|
export declare function sessionIsBlank(state: unknown, id: SessionId | undefined): boolean;
|
|
46
61
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
62
|
+
* Whether a seat has NO reviewable session: it cannot name one at all, or the shell says the one it names
|
|
63
|
+
* is blank (a brand-new session with nothing said in it yet).
|
|
64
|
+
*
|
|
65
|
+
* The two halves are ONE question and must be asked of the SAME id — the id the seat would read its view
|
|
66
|
+
* for. Splitting them is what this helper exists to stop: a seat that drew its view for the published
|
|
67
|
+
* session while testing `sessionIsBlank` against a different (absent) id stayed enabled on a blank session
|
|
68
|
+
* and drew the previous session's list — from another workspace as readily as from its own, because nothing
|
|
69
|
+
* in this client compares workspaces.
|
|
70
|
+
*
|
|
71
|
+
* @param state - the session-list state handed to the selector.
|
|
72
|
+
* @param id - the session this seat would read its view for.
|
|
73
|
+
* @returns true when there is nothing to review.
|
|
74
|
+
*/
|
|
75
|
+
export declare function unreviewableSession(state: unknown, id: SessionId | undefined): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* The session the PAGE is showing, resolved the way a whole-page reader has to resolve it: the
|
|
78
|
+
* session-list store's own selection first, else what the Session header entry published.
|
|
79
|
+
*
|
|
80
|
+
* `readSessionsSelection` is supplied by the plugin body (it owns the `sessions` service), and may
|
|
81
|
+
* return `undefined` for a shell whose state names none — 0.1.7's store carries no selected session and
|
|
82
|
+
* hands the id to its own session-scoped seats instead, which is why the published id is read here too
|
|
83
|
+
* (`publishSessionId` runs from the header entry's effect). This is a plain read for non-React code: the
|
|
84
|
+
* component that publishes it reads the SUBSCRIBED value ({@link usePublishedSessionId}).
|
|
85
|
+
*
|
|
86
|
+
* It is deliberately NOT every session anyone has read: the page-wide readers here (the reference remap,
|
|
87
|
+
* which rewrites the visible composer's draft) mean the session the reader is looking at, and taking the
|
|
88
|
+
* newest session some other mount polled would address a composer that is not on screen.
|
|
89
|
+
*
|
|
90
|
+
* @param readSessionsSelection - the store's own selected session, when its shape names one.
|
|
91
|
+
* @returns the id, or `undefined` when nothing on this page names a session.
|
|
52
92
|
*/
|
|
53
|
-
export declare function
|
|
93
|
+
export declare function shownSessionId(readSessionsSelection: () => SessionId | undefined): SessionId | undefined;
|
|
@@ -204,32 +204,58 @@ export declare function setSplitMode(value: boolean): void;
|
|
|
204
204
|
export declare function mdPreviewEnabled(): boolean;
|
|
205
205
|
/** Persist the Markdown-preview default preference. */
|
|
206
206
|
export declare function setMdPreviewEnabled(value: boolean): void;
|
|
207
|
+
/**
|
|
208
|
+
* Whether this plugin MOUNTS its own file menu at all — the press bridge that takes over a file press in
|
|
209
|
+
* the shell's lists, a file link in a message, and a relative link in the Markdown preview.
|
|
210
|
+
*
|
|
211
|
+
* Default ON, and the default is the interesting half: only an explicit `'0'` turns it off, so a reader who
|
|
212
|
+
* never touches the setting keeps the menu they have always had (and a storage that cannot be read — a
|
|
213
|
+
* privacy mode, a cleared profile — leaves the plugin working rather than silently disabled).
|
|
214
|
+
*
|
|
215
|
+
* Off means the bridge does not take the press over AT ALL: no menu, and the shell's own behaviour runs
|
|
216
|
+
* exactly as if this plugin were not installed. That includes this plugin's own preview, where a relative
|
|
217
|
+
* link therefore goes back to the browser's own navigation — which is the honest consequence of asking this
|
|
218
|
+
* plugin to keep its hands off, not a bug to special-case.
|
|
219
|
+
*
|
|
220
|
+
* @returns whether the file menu is enabled.
|
|
221
|
+
*/
|
|
222
|
+
export declare function chipMenuEnabled(): boolean;
|
|
223
|
+
/** Persist the file-menu preference (see {@link chipMenuEnabled}). */
|
|
224
|
+
export declare function setChipMenuEnabled(value: boolean): void;
|
|
207
225
|
/**
|
|
208
226
|
* Whether commenting on a diff range is offered: the selection frame's comment button,
|
|
209
|
-
* and the chord that stands for it. Defaults to
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
227
|
+
* and the chord that stands for it. **Defaults to ON** — comment mode is the mode now,
|
|
228
|
+
* not a preview — so only an explicit `'0'` turns it off (the `chipMenuEnabled` idiom a
|
|
229
|
+
* few lines up), and an absent field means on for every profile that predates the flip.
|
|
230
|
+
* This gates STARTING a thread, not the threads themselves — one already written keeps
|
|
231
|
+
* rendering, so turning the mode off never hides work or interrupts an answer on its way
|
|
232
|
+
* back.
|
|
213
233
|
* @returns whether comment mode is on.
|
|
214
234
|
*/
|
|
215
235
|
export declare function commentModeEnabled(): boolean;
|
|
216
|
-
/** Window event: comment mode was toggled (see {@link setCommentModeEnabled}).
|
|
217
|
-
|
|
218
|
-
|
|
236
|
+
/** Window event: comment mode was toggled (see {@link setCommentModeEnabled}). A DIFFERENT string from
|
|
237
|
+
* {@link COMMENT_MODE_KEY} on purpose: a key literal and an event literal that read the same make every
|
|
238
|
+
* grep a guess (the hazard this file already names for the file-menu key at the top). */
|
|
239
|
+
export declare const COMMENT_MODE_CHANGED_EVENT = "diff-approval:comment-mode-changed";
|
|
240
|
+
/** Persist the comment-mode preference (`'1'` on, `'0'` off — see {@link commentModeEnabled}). */
|
|
219
241
|
export declare function setCommentModeEnabled(value: boolean): void;
|
|
220
242
|
/**
|
|
221
243
|
* Whether the diff's code uses the font this plugin bundles.
|
|
222
244
|
*
|
|
223
|
-
* Defaults to
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
* is
|
|
227
|
-
*
|
|
245
|
+
* **Defaults to ON**, like comment mode: absent means on, and only an explicit `'0'` turns
|
|
246
|
+
* it off. The old field's answer is deliberately NOT carried over (see the constant): a
|
|
247
|
+
* reader who had turned the bundled face off before this change comes back with it on, which
|
|
248
|
+
* is the one behaviour the reader asked for. The cost is real and worth naming where the
|
|
249
|
+
* switch is: the face is a JetBrains Maple Mono subset sliced into 138 woff2 files, and a
|
|
250
|
+
* page pulls only the slices its characters land in — tens of kilobytes for a comment, up to
|
|
251
|
+
* ~120 KB for a rare character — downloaded on demand rather than shipped.
|
|
228
252
|
* @returns whether the bundled code font is enabled.
|
|
229
253
|
*/
|
|
230
254
|
export declare function codeFontEnabled(): boolean;
|
|
231
|
-
/** Window event: the bundled code font was turned on or off (see {@link setCodeFontEnabled}).
|
|
232
|
-
|
|
255
|
+
/** Window event: the bundled code font was turned on or off (see {@link setCodeFontEnabled}). A DIFFERENT
|
|
256
|
+
* string from {@link CODE_FONT_KEY} on purpose — they used to be the same literal, which made a grep for
|
|
257
|
+
* either one a guess (the hazard this file names for the file-menu key at the top). */
|
|
258
|
+
export declare const CODE_FONT_CHANGED_EVENT = "diff-approval:code-font-changed";
|
|
233
259
|
/** Persist the bundled-code-font preference. */
|
|
234
260
|
export declare function setCodeFontEnabled(value: boolean): void;
|
|
235
261
|
/**
|