xtralab 0.14.1 → 0.15.1
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 +10 -0
- package/lib/about/icons.d.ts +0 -5
- package/lib/about/icons.js +0 -5
- package/lib/about/index.d.ts +4 -8
- package/lib/about/index.js +6 -17
- package/lib/agentSessions.d.ts +9 -29
- package/lib/agentSessions.js +2 -7
- package/lib/askAgent/editorSelection.d.ts +20 -26
- package/lib/askAgent/editorSelection.js +11 -26
- package/lib/askAgent/icons.d.ts +2 -3
- package/lib/askAgent/icons.js +2 -3
- package/lib/askAgent/index.d.ts +4 -26
- package/lib/askAgent/index.js +54 -158
- package/lib/askAgent/popup.d.ts +12 -16
- package/lib/askAgent/popup.js +18 -37
- package/lib/askAgent/prompt.d.ts +3 -5
- package/lib/askAgent/prompt.js +6 -19
- package/lib/askAgent/queue.d.ts +11 -21
- package/lib/askAgent/queue.js +9 -19
- package/lib/askAgent/queuePanel.d.ts +13 -18
- package/lib/askAgent/queuePanel.js +15 -37
- package/lib/askAgent/targetPicker.d.ts +2 -9
- package/lib/askAgent/targetPicker.js +6 -11
- package/lib/askAgent/tokens.d.ts +32 -46
- package/lib/askAgent/tokens.js +4 -6
- package/lib/commandBar/index.d.ts +4 -13
- package/lib/commandBar/index.js +21 -39
- package/lib/customPanel/index.d.ts +7 -0
- package/lib/customPanel/index.js +89 -0
- package/lib/editorBreadcrumbs/index.d.ts +3 -6
- package/lib/editorBreadcrumbs/index.js +10 -6
- package/lib/editorBreadcrumbs/widget.d.ts +17 -4
- package/lib/editorBreadcrumbs/widget.js +11 -17
- package/lib/editorIndent/index.d.ts +4 -12
- package/lib/editorIndent/index.js +8 -23
- package/lib/fileBrowser/commands.d.ts +28 -19
- package/lib/fileBrowser/commands.js +51 -98
- package/lib/fileBrowser/contents.d.ts +14 -0
- package/lib/fileBrowser/contents.js +28 -0
- package/lib/fileBrowser/dragAndDrop.d.ts +79 -0
- package/lib/fileBrowser/dragAndDrop.js +110 -0
- package/lib/fileBrowser/fileBrowser.d.ts +21 -9
- package/lib/fileBrowser/fileBrowser.js +211 -405
- package/lib/fileBrowser/gitStatus.d.ts +4 -9
- package/lib/fileBrowser/gitStatus.js +7 -17
- package/lib/fileBrowser/gitignore.d.ts +4 -17
- package/lib/fileBrowser/gitignore.js +4 -25
- package/lib/fileBrowser/icons.d.ts +7 -17
- package/lib/fileBrowser/icons.js +25 -80
- package/lib/fileBrowser/index.js +41 -10
- package/lib/fileBrowser/widget.d.ts +170 -53
- package/lib/fileBrowser/widget.js +242 -18
- package/lib/fileTypeIcons/index.js +0 -10
- package/lib/git/api.d.ts +5 -13
- package/lib/git/api.js +10 -36
- package/lib/git/askRequest.d.ts +4 -8
- package/lib/git/askRequest.js +6 -20
- package/lib/git/commands.d.ts +40 -5
- package/lib/git/commands.js +16 -10
- package/lib/git/diffModel.js +4 -0
- package/lib/git/diffSurface.d.ts +41 -55
- package/lib/git/diffSurface.js +42 -117
- package/lib/git/diffWidget.d.ts +81 -2
- package/lib/git/diffWidget.js +62 -12
- package/lib/git/imageDiff.d.ts +5 -6
- package/lib/git/imageDiff.js +18 -48
- package/lib/git/index.d.ts +0 -3
- package/lib/git/index.js +3 -8
- package/lib/git/notebookDiff.d.ts +110 -33
- package/lib/git/notebookDiff.js +52 -195
- package/lib/git/tokens.d.ts +73 -31
- package/lib/git/tokens.js +2 -3
- package/lib/highlight/index.d.ts +4 -10
- package/lib/highlight/index.js +10 -41
- package/lib/index.d.ts +0 -9
- package/lib/index.js +2 -9
- package/lib/launcher/agents.d.ts +55 -51
- package/lib/launcher/agents.js +25 -54
- package/lib/launcher/availability.d.ts +3 -9
- package/lib/launcher/availability.js +3 -9
- package/lib/launcher/commands.d.ts +7 -32
- package/lib/launcher/commands.js +14 -45
- package/lib/launcher/dashboard.d.ts +19 -24
- package/lib/launcher/dashboard.js +21 -67
- package/lib/launcher/editorRegistry.d.ts +10 -22
- package/lib/launcher/editorRegistry.js +14 -18
- package/lib/launcher/editors.d.ts +39 -45
- package/lib/launcher/editors.js +8 -34
- package/lib/launcher/icons.d.ts +0 -12
- package/lib/launcher/icons.js +14 -37
- package/lib/launcher/index.js +12 -55
- package/lib/launcher/invocation.d.ts +4 -10
- package/lib/launcher/invocation.js +7 -22
- package/lib/launcher/registry.d.ts +9 -7
- package/lib/launcher/registry.js +9 -7
- package/lib/launcher/schemaDefaults.d.ts +2 -9
- package/lib/launcher/schemaDefaults.js +37 -13
- package/lib/launcher/tokens.d.ts +10 -22
- package/lib/launcher/tokens.js +5 -8
- package/lib/menuBar/index.d.ts +3 -10
- package/lib/menuBar/index.js +11 -28
- package/lib/menus/index.d.ts +3 -9
- package/lib/menus/index.js +14 -30
- package/lib/omnibox/files.d.ts +2 -4
- package/lib/omnibox/files.js +5 -11
- package/lib/omnibox/index.d.ts +4 -14
- package/lib/omnibox/index.js +9 -27
- package/lib/omnibox/model.d.ts +38 -10
- package/lib/omnibox/model.js +15 -33
- package/lib/omnibox/recents.d.ts +15 -17
- package/lib/omnibox/recents.js +12 -17
- package/lib/omnibox/tokens.d.ts +5 -10
- package/lib/omnibox/tokens.js +2 -6
- package/lib/omnibox/widget.d.ts +11 -7
- package/lib/omnibox/widget.js +8 -16
- package/lib/searchReplace/index.d.ts +4 -20
- package/lib/searchReplace/index.js +7 -27
- package/lib/showOutput/index.d.ts +3 -12
- package/lib/showOutput/index.js +6 -22
- package/lib/sidebar/index.d.ts +4 -13
- package/lib/sidebar/index.js +73 -46
- package/lib/terminalNotifications/index.d.ts +4 -10
- package/lib/terminalNotifications/index.js +6 -20
- package/lib/terminals/agentTerminals.d.ts +12 -5
- package/lib/terminals/agentTerminals.js +25 -44
- package/lib/terminals/detection.d.ts +4 -8
- package/lib/terminals/detection.js +4 -8
- package/lib/terminals/index.d.ts +0 -33
- package/lib/terminals/index.js +24 -102
- package/lib/terminals/model.d.ts +64 -139
- package/lib/terminals/model.js +80 -220
- package/lib/terminals/tokens.d.ts +11 -22
- package/lib/terminals/tokens.js +2 -2
- package/lib/terminals/widget.d.ts +72 -38
- package/lib/terminals/widget.js +173 -73
- package/lib/topBar/icons.d.ts +3 -8
- package/lib/topBar/icons.js +3 -8
- package/lib/topBar/index.d.ts +4 -11
- package/lib/topBar/index.js +10 -38
- package/lib/walkthrough/index.d.ts +4 -9
- package/lib/walkthrough/index.js +6 -20
- package/lib/walkthrough/panel.d.ts +40 -9
- package/lib/walkthrough/panel.js +4 -10
- package/package.json +2 -2
- package/schema/launcher.json +2 -2
- package/schema/sidebar.json +47 -7
- package/src/about/icons.ts +0 -5
- package/src/about/index.tsx +6 -17
- package/src/agentSessions.ts +9 -29
- package/src/askAgent/editorSelection.ts +22 -29
- package/src/askAgent/icons.ts +2 -3
- package/src/askAgent/index.ts +54 -158
- package/src/askAgent/popup.tsx +23 -45
- package/src/askAgent/prompt.ts +6 -19
- package/src/askAgent/queue.ts +13 -24
- package/src/askAgent/queuePanel.tsx +21 -46
- package/src/askAgent/targetPicker.tsx +5 -15
- package/src/askAgent/tokens.ts +32 -46
- package/src/commandBar/index.ts +24 -39
- package/src/customPanel/index.ts +119 -0
- package/src/editorBreadcrumbs/index.ts +10 -6
- package/src/editorBreadcrumbs/widget.ts +23 -17
- package/src/editorIndent/index.ts +17 -23
- package/src/fileBrowser/commands.ts +70 -98
- package/src/fileBrowser/contents.ts +33 -0
- package/src/fileBrowser/dragAndDrop.ts +188 -0
- package/src/fileBrowser/fileBrowser.tsx +259 -440
- package/src/fileBrowser/gitStatus.ts +7 -17
- package/src/fileBrowser/gitignore.ts +4 -25
- package/src/fileBrowser/icons.ts +25 -80
- package/src/fileBrowser/index.ts +43 -10
- package/src/fileBrowser/widget.tsx +344 -64
- package/src/fileTypeIcons/index.ts +0 -10
- package/src/git/api.ts +10 -36
- package/src/git/askRequest.ts +6 -20
- package/src/git/commands.ts +70 -10
- package/src/git/diffModel.ts +25 -0
- package/src/git/diffSurface.tsx +71 -159
- package/src/git/diffWidget.tsx +104 -14
- package/src/git/imageDiff.tsx +45 -51
- package/src/git/index.ts +3 -8
- package/src/git/notebookDiff.tsx +211 -238
- package/src/git/tokens.ts +73 -31
- package/src/highlight/index.ts +10 -41
- package/src/index.ts +2 -9
- package/src/launcher/agents.ts +74 -92
- package/src/launcher/availability.ts +3 -9
- package/src/launcher/commands.ts +14 -51
- package/src/launcher/dashboard.tsx +49 -88
- package/src/launcher/editorRegistry.ts +21 -29
- package/src/launcher/editors.ts +39 -58
- package/src/launcher/icons.ts +15 -37
- package/src/launcher/index.ts +12 -55
- package/src/launcher/invocation.ts +7 -22
- package/src/launcher/registry.ts +9 -7
- package/src/launcher/schemaDefaults.ts +43 -13
- package/src/launcher/tokens.ts +10 -22
- package/src/menuBar/index.ts +11 -28
- package/src/menus/index.ts +14 -30
- package/src/omnibox/files.ts +10 -14
- package/src/omnibox/index.ts +9 -27
- package/src/omnibox/model.ts +50 -38
- package/src/omnibox/recents.ts +17 -22
- package/src/omnibox/tokens.ts +5 -10
- package/src/omnibox/widget.tsx +14 -19
- package/src/searchReplace/index.ts +7 -27
- package/src/showOutput/index.ts +6 -22
- package/src/sidebar/index.ts +103 -52
- package/src/terminalNotifications/index.ts +42 -25
- package/src/terminals/agentTerminals.ts +25 -49
- package/src/terminals/detection.ts +4 -8
- package/src/terminals/index.ts +31 -102
- package/src/terminals/model.ts +130 -246
- package/src/terminals/tokens.ts +11 -22
- package/src/terminals/widget.tsx +229 -113
- package/src/topBar/icons.ts +3 -8
- package/src/topBar/index.ts +21 -44
- package/src/walkthrough/index.ts +6 -20
- package/src/walkthrough/panel.ts +40 -10
- package/style/about.css +2 -7
- package/style/askAgent.css +8 -40
- package/style/base.css +25 -73
- package/style/chrome.css +108 -308
- package/style/commandBar.css +4 -20
- package/style/customPanel.css +6 -0
- package/style/git.css +22 -184
- package/style/highlight.css +1 -3
- package/style/index.js +1 -0
- package/style/launcher.css +6 -70
- package/style/omnibox.css +2 -14
- package/style/showOutput.css +0 -6
- package/style/sidebar.css +29 -9
- package/style/tabs.css +5 -13
- package/style/terminals.css +18 -123
- package/style/topBar.css +2 -22
- package/style/walkthrough.css +0 -8
package/src/git/diffWidget.tsx
CHANGED
|
@@ -37,8 +37,17 @@ export { DIFF_WIDGET_CSS_CLASS };
|
|
|
37
37
|
* `Git.Diff.IModel` plus optional xtralab-only metadata.
|
|
38
38
|
*/
|
|
39
39
|
export interface IXtralabDiffModel extends Git.Diff.IModel {
|
|
40
|
+
/**
|
|
41
|
+
* Whether the file is binary, so no text contents are fetched.
|
|
42
|
+
*/
|
|
40
43
|
isBinary?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Whether hunks may be discarded; when absent, a working-tree heuristic decides.
|
|
46
|
+
*/
|
|
41
47
|
canDiscard?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* The old-side path when the diff represents a rename.
|
|
50
|
+
*/
|
|
42
51
|
oldFilename?: string;
|
|
43
52
|
}
|
|
44
53
|
|
|
@@ -46,14 +55,27 @@ export interface IXtralabDiffModel extends Git.Diff.IModel {
|
|
|
46
55
|
* Application-level services the diff model does not carry.
|
|
47
56
|
*/
|
|
48
57
|
export interface IXtralabDiffContext {
|
|
58
|
+
/**
|
|
59
|
+
* The contents manager used to save hunk-discard results.
|
|
60
|
+
*/
|
|
49
61
|
contentsManager: Contents.IManager;
|
|
62
|
+
/**
|
|
63
|
+
* The rendermime registry for rendered notebook diffs, or `null` to
|
|
64
|
+
* force the textual fallback.
|
|
65
|
+
*/
|
|
50
66
|
rendermime: IRenderMimeRegistry | null;
|
|
67
|
+
/**
|
|
68
|
+
* The theme manager used to track theme changes, or `null` to watch
|
|
69
|
+
* the body attribute instead.
|
|
70
|
+
*/
|
|
51
71
|
themeManager: IThemeManager | null;
|
|
52
72
|
/**
|
|
53
|
-
*
|
|
54
|
-
* "ask an agent about these lines" gutter button.
|
|
73
|
+
* When available, diff line selections get an "ask an agent" gutter button.
|
|
55
74
|
*/
|
|
56
75
|
askAgent: IAskAgent | null;
|
|
76
|
+
/**
|
|
77
|
+
* The application translation bundle.
|
|
78
|
+
*/
|
|
57
79
|
trans: TranslationBundle;
|
|
58
80
|
}
|
|
59
81
|
|
|
@@ -75,6 +97,9 @@ export class XtralabDiffWidget
|
|
|
75
97
|
this.addClass(DIFF_WIDGET_CSS_CLASS);
|
|
76
98
|
}
|
|
77
99
|
|
|
100
|
+
/**
|
|
101
|
+
* The git diff model being rendered.
|
|
102
|
+
*/
|
|
78
103
|
get model(): Git.Diff.IModel {
|
|
79
104
|
return this._model;
|
|
80
105
|
}
|
|
@@ -124,10 +149,16 @@ export class XtralabDiffWidget
|
|
|
124
149
|
await done.promise;
|
|
125
150
|
}
|
|
126
151
|
|
|
152
|
+
/**
|
|
153
|
+
* The current rendered-vs-JSON choice for notebook diffs.
|
|
154
|
+
*/
|
|
127
155
|
get notebookViewMode(): NotebookDiffViewMode {
|
|
128
156
|
return this._notebookViewMode;
|
|
129
157
|
}
|
|
130
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Set the notebook view mode and persist it.
|
|
161
|
+
*/
|
|
131
162
|
setNotebookViewMode(mode: NotebookDiffViewMode): void {
|
|
132
163
|
if (mode === this._notebookViewMode) {
|
|
133
164
|
return;
|
|
@@ -137,14 +168,23 @@ export class XtralabDiffWidget
|
|
|
137
168
|
this._notebookViewModeChanged.emit(mode);
|
|
138
169
|
}
|
|
139
170
|
|
|
171
|
+
/**
|
|
172
|
+
* A signal emitted when the notebook view mode changes.
|
|
173
|
+
*/
|
|
140
174
|
get notebookViewModeChanged(): ISignal<this, NotebookDiffViewMode> {
|
|
141
175
|
return this._notebookViewModeChanged;
|
|
142
176
|
}
|
|
143
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Whether a rendered notebook view is currently available.
|
|
180
|
+
*/
|
|
144
181
|
get hasNotebookView(): boolean {
|
|
145
182
|
return this._hasNotebookView;
|
|
146
183
|
}
|
|
147
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Set whether a rendered notebook view is available.
|
|
187
|
+
*/
|
|
148
188
|
setHasNotebookView(value: boolean): void {
|
|
149
189
|
if (value === this._hasNotebookView) {
|
|
150
190
|
return;
|
|
@@ -153,14 +193,23 @@ export class XtralabDiffWidget
|
|
|
153
193
|
this._hasNotebookViewChanged.emit(value);
|
|
154
194
|
}
|
|
155
195
|
|
|
196
|
+
/**
|
|
197
|
+
* A signal emitted when rendered-notebook availability changes.
|
|
198
|
+
*/
|
|
156
199
|
get hasNotebookViewChanged(): ISignal<this, boolean> {
|
|
157
200
|
return this._hasNotebookViewChanged;
|
|
158
201
|
}
|
|
159
202
|
|
|
203
|
+
/**
|
|
204
|
+
* The current split/unified layout for textual file diffs.
|
|
205
|
+
*/
|
|
160
206
|
get diffStyle(): DiffStyle {
|
|
161
207
|
return this._diffStyle;
|
|
162
208
|
}
|
|
163
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Set the diff style and persist it.
|
|
212
|
+
*/
|
|
164
213
|
setDiffStyle(style: DiffStyle): void {
|
|
165
214
|
if (style === this._diffStyle) {
|
|
166
215
|
return;
|
|
@@ -170,6 +219,9 @@ export class XtralabDiffWidget
|
|
|
170
219
|
this._diffStyleChanged.emit(style);
|
|
171
220
|
}
|
|
172
221
|
|
|
222
|
+
/**
|
|
223
|
+
* A signal emitted when the diff style changes.
|
|
224
|
+
*/
|
|
173
225
|
get diffStyleChanged(): ISignal<this, DiffStyle> {
|
|
174
226
|
return this._diffStyleChanged;
|
|
175
227
|
}
|
|
@@ -181,6 +233,9 @@ export class XtralabDiffWidget
|
|
|
181
233
|
return this._fileDiffActive;
|
|
182
234
|
}
|
|
183
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Set whether the textual file diff is the active view.
|
|
238
|
+
*/
|
|
184
239
|
setFileDiffActive(value: boolean): void {
|
|
185
240
|
if (value === this._fileDiffActive) {
|
|
186
241
|
return;
|
|
@@ -189,6 +244,9 @@ export class XtralabDiffWidget
|
|
|
189
244
|
this._fileDiffActiveChanged.emit(value);
|
|
190
245
|
}
|
|
191
246
|
|
|
247
|
+
/**
|
|
248
|
+
* A signal emitted when {@link fileDiffActive} changes.
|
|
249
|
+
*/
|
|
192
250
|
get fileDiffActiveChanged(): ISignal<this, boolean> {
|
|
193
251
|
return this._fileDiffActiveChanged;
|
|
194
252
|
}
|
|
@@ -200,32 +258,49 @@ export class XtralabDiffWidget
|
|
|
200
258
|
return this._emptied;
|
|
201
259
|
}
|
|
202
260
|
|
|
261
|
+
/**
|
|
262
|
+
* Emit the {@link emptied} signal.
|
|
263
|
+
*/
|
|
203
264
|
notifyEmptied(): void {
|
|
204
265
|
this._emptied.emit();
|
|
205
266
|
}
|
|
206
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Resolve and clear the pending `refresh()` promise, if any.
|
|
270
|
+
*/
|
|
207
271
|
settleRefresh(): void {
|
|
208
272
|
const pending = this._pendingRefresh;
|
|
209
273
|
this._pendingRefresh = null;
|
|
210
274
|
pending?.resolve();
|
|
211
275
|
}
|
|
212
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Dispose of the resources held by the widget.
|
|
279
|
+
*/
|
|
213
280
|
dispose(): void {
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
// hang when the widget is torn down mid-fetch.
|
|
281
|
+
// jupyterlab-git awaits refresh() to re-show its diff button; release any
|
|
282
|
+
// in-flight awaiter so it does not hang on mid-fetch teardown.
|
|
217
283
|
this.settleRefresh();
|
|
218
284
|
super.dispose();
|
|
219
285
|
}
|
|
220
286
|
|
|
287
|
+
/**
|
|
288
|
+
* A counter incremented on each `refresh()` to trigger a content re-fetch.
|
|
289
|
+
*/
|
|
221
290
|
get reloadNonce(): number {
|
|
222
291
|
return this._reloadNonce;
|
|
223
292
|
}
|
|
224
293
|
|
|
294
|
+
/**
|
|
295
|
+
* The application-level services used by the rendered diff.
|
|
296
|
+
*/
|
|
225
297
|
get context(): IXtralabDiffContext {
|
|
226
298
|
return this._context;
|
|
227
299
|
}
|
|
228
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Render the diff view for the current model.
|
|
303
|
+
*/
|
|
229
304
|
protected render(): React.ReactElement {
|
|
230
305
|
return <ModelDiffView widget={this} />;
|
|
231
306
|
}
|
|
@@ -247,10 +322,25 @@ export class XtralabDiffWidget
|
|
|
247
322
|
private _emptied = new Signal<this, void>(this);
|
|
248
323
|
}
|
|
249
324
|
|
|
325
|
+
/**
|
|
326
|
+
* Resolved file-contents state for {@link ModelDiffView}.
|
|
327
|
+
*/
|
|
250
328
|
interface IModelDiffState {
|
|
329
|
+
/**
|
|
330
|
+
* Whether the file contents are still being fetched.
|
|
331
|
+
*/
|
|
251
332
|
loading: boolean;
|
|
333
|
+
/**
|
|
334
|
+
* The resolved reference-side text.
|
|
335
|
+
*/
|
|
252
336
|
oldText: string;
|
|
337
|
+
/**
|
|
338
|
+
* The resolved challenger-side text.
|
|
339
|
+
*/
|
|
253
340
|
newText: string;
|
|
341
|
+
/**
|
|
342
|
+
* The fetch error message, or `null` if none.
|
|
343
|
+
*/
|
|
254
344
|
error: string | null;
|
|
255
345
|
}
|
|
256
346
|
|
|
@@ -270,7 +360,6 @@ function ModelDiffView(props: {
|
|
|
270
360
|
error: null
|
|
271
361
|
});
|
|
272
362
|
|
|
273
|
-
// Mirror toolbar-driven notebook view changes into React state.
|
|
274
363
|
const [notebookViewMode, setNotebookViewMode] =
|
|
275
364
|
React.useState<NotebookDiffViewMode>(() => widget.notebookViewMode);
|
|
276
365
|
React.useEffect(() => {
|
|
@@ -294,7 +383,6 @@ function ModelDiffView(props: {
|
|
|
294
383
|
[widget]
|
|
295
384
|
);
|
|
296
385
|
|
|
297
|
-
// Mirror toolbar-driven diff-style changes into React state.
|
|
298
386
|
const [diffStyle, setDiffStyle] = React.useState<DiffStyle>(
|
|
299
387
|
() => widget.diffStyle
|
|
300
388
|
);
|
|
@@ -343,14 +431,12 @@ function ModelDiffView(props: {
|
|
|
343
431
|
};
|
|
344
432
|
}, [themeManager]);
|
|
345
433
|
|
|
346
|
-
// Image diffs use the server's base64 payloads; other binaries show a placeholder.
|
|
347
434
|
const isImage = React.useMemo(
|
|
348
435
|
() => imageDataType(model.filename) !== null,
|
|
349
436
|
[model.filename]
|
|
350
437
|
);
|
|
351
438
|
const isBinary = model.isBinary === true && !isImage;
|
|
352
439
|
|
|
353
|
-
// Used by the launcher to auto-close an emptied diff after discard.
|
|
354
440
|
const [hunkCount, setHunkCount] = React.useState<number | null>(null);
|
|
355
441
|
const handleMetadataChange = React.useCallback(
|
|
356
442
|
(info: { hunkCount: number | null }) => {
|
|
@@ -359,10 +445,8 @@ function ModelDiffView(props: {
|
|
|
359
445
|
[]
|
|
360
446
|
);
|
|
361
447
|
|
|
362
|
-
// Fetch both sides whenever the model or reload nonce changes.
|
|
363
448
|
React.useEffect(() => {
|
|
364
449
|
let cancelled = false;
|
|
365
|
-
// Non-image binaries do not need content fetches.
|
|
366
450
|
if (isBinary) {
|
|
367
451
|
setState({ loading: false, oldText: '', newText: '', error: null });
|
|
368
452
|
// An in-flight `refresh()` still has to resolve.
|
|
@@ -399,7 +483,6 @@ function ModelDiffView(props: {
|
|
|
399
483
|
});
|
|
400
484
|
} finally {
|
|
401
485
|
if (!cancelled) {
|
|
402
|
-
// Let `refresh()` settle once content has loaded.
|
|
403
486
|
widget.settleRefresh();
|
|
404
487
|
}
|
|
405
488
|
}
|
|
@@ -422,7 +505,6 @@ function ModelDiffView(props: {
|
|
|
422
505
|
}
|
|
423
506
|
}, [state.loading, state.error, isBinary, hunkCount, nonce, widget]);
|
|
424
507
|
|
|
425
|
-
// The launcher sets `canDiscard`; jupyterlab-git models fall back to source.
|
|
426
508
|
const canDiscardHunk =
|
|
427
509
|
model.canDiscard ??
|
|
428
510
|
(model.challenger.source === Git.Diff.SpecialRef.WORKING &&
|
|
@@ -444,7 +526,6 @@ function ModelDiffView(props: {
|
|
|
444
526
|
});
|
|
445
527
|
},
|
|
446
528
|
onAfterSave: () => {
|
|
447
|
-
// Re-pull so the diff reflects the reverted hunk.
|
|
448
529
|
void widget.refresh();
|
|
449
530
|
}
|
|
450
531
|
}),
|
|
@@ -503,6 +584,9 @@ class NotebookViewModeToolbarItem extends ReactWidget {
|
|
|
503
584
|
this.addClass('jp-xtralab-DiffWidget-viewModeToolbarItem');
|
|
504
585
|
}
|
|
505
586
|
|
|
587
|
+
/**
|
|
588
|
+
* Render the Notebook/JSON toggle.
|
|
589
|
+
*/
|
|
506
590
|
protected render(): React.ReactElement {
|
|
507
591
|
return <NotebookViewModeToolbarControl widget={this._widget} />;
|
|
508
592
|
}
|
|
@@ -561,6 +645,9 @@ class DiffStyleToolbarItem extends ReactWidget {
|
|
|
561
645
|
this.addClass('jp-xtralab-DiffWidget-diffStyleToolbarItem');
|
|
562
646
|
}
|
|
563
647
|
|
|
648
|
+
/**
|
|
649
|
+
* Render the Split/Unified toggle.
|
|
650
|
+
*/
|
|
564
651
|
protected render(): React.ReactElement {
|
|
565
652
|
return <DiffStyleToolbarControl widget={this._widget} />;
|
|
566
653
|
}
|
|
@@ -608,6 +695,9 @@ function DiffStyleToolbarControl(props: {
|
|
|
608
695
|
* Structural toolbar type shared across host package boundaries.
|
|
609
696
|
*/
|
|
610
697
|
interface IDiffToolbar {
|
|
698
|
+
/**
|
|
699
|
+
* Add an item to the toolbar under the given name.
|
|
700
|
+
*/
|
|
611
701
|
addItem(name: string, widget: Widget): boolean;
|
|
612
702
|
}
|
|
613
703
|
|
package/src/git/imageDiff.tsx
CHANGED
|
@@ -4,21 +4,15 @@ import type { TranslationBundle } from '@jupyterlab/translation';
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* xtralab's image diff, registered in place of `@jupyterlab/git:image-diff`.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* for text. This module turns those strings into `<img>` elements and
|
|
11
|
-
* offers the 2-up / swipe / onion-skin comparison modes; no diffing library
|
|
12
|
-
* is involved (`@pierre/diffs` is text-only).
|
|
7
|
+
* Both sides arrive base64-encoded through the same `content()` getters used
|
|
8
|
+
* for text; the 2-up/swipe/onion views are hand-rolled (`@pierre/diffs` is
|
|
9
|
+
* text-only).
|
|
13
10
|
*/
|
|
14
11
|
|
|
15
12
|
/**
|
|
16
|
-
* Raster
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* `.svg` is intentionally absent: it is XML text, so the `@pierre/diffs`
|
|
20
|
-
* text diff (the fallback provider) is more useful for it than an image
|
|
21
|
-
* comparison.
|
|
13
|
+
* Raster extensions mapped to the `data:image/<subtype>` MIME subtype.
|
|
14
|
+
* `.svg` is intentionally absent: it is XML text, so the fallback text diff
|
|
15
|
+
* is more useful than an image comparison.
|
|
22
16
|
*/
|
|
23
17
|
const IMAGE_DATA_TYPES: Record<string, string> = {
|
|
24
18
|
'.png': 'png',
|
|
@@ -30,9 +24,6 @@ const IMAGE_DATA_TYPES: Record<string, string> = {
|
|
|
30
24
|
'.ico': 'x-icon'
|
|
31
25
|
};
|
|
32
26
|
|
|
33
|
-
/**
|
|
34
|
-
* The file extensions registered as the xtralab image diff provider.
|
|
35
|
-
*/
|
|
36
27
|
export const IMAGE_DIFF_EXTENSIONS = Object.keys(IMAGE_DATA_TYPES);
|
|
37
28
|
|
|
38
29
|
/**
|
|
@@ -48,9 +39,6 @@ export function imageDataType(path: string): string | null {
|
|
|
48
39
|
return IMAGE_DATA_TYPES[lower.slice(dot)] ?? null;
|
|
49
40
|
}
|
|
50
41
|
|
|
51
|
-
/**
|
|
52
|
-
* Comparison layouts, matching jupyterlab-git's image diff modes.
|
|
53
|
-
*/
|
|
54
42
|
type ImageDiffViewMode = '2-up' | 'swipe' | 'onion';
|
|
55
43
|
|
|
56
44
|
const IMAGE_DIFF_VIEW_MODE_STORAGE_KEY = 'xtralab:image-diff-view-mode';
|
|
@@ -62,8 +50,7 @@ function readStoredImageViewMode(): ImageDiffViewMode {
|
|
|
62
50
|
return raw;
|
|
63
51
|
}
|
|
64
52
|
} catch {
|
|
65
|
-
// localStorage can throw in privacy/sandboxed contexts
|
|
66
|
-
// sensible default anyway.
|
|
53
|
+
// localStorage can throw in privacy/sandboxed contexts.
|
|
67
54
|
}
|
|
68
55
|
return '2-up';
|
|
69
56
|
}
|
|
@@ -77,18 +64,14 @@ function writeStoredImageViewMode(mode: ImageDiffViewMode): void {
|
|
|
77
64
|
}
|
|
78
65
|
|
|
79
66
|
/**
|
|
80
|
-
* Strip whitespace from a base64 payload
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* the cleaned length also lets us size the file accurately.
|
|
67
|
+
* Strip whitespace from a base64 payload: the server's `base64.encodebytes`
|
|
68
|
+
* inserts newlines some browsers reject in `data:` URLs, and the cleaned
|
|
69
|
+
* length sizes the file accurately.
|
|
84
70
|
*/
|
|
85
71
|
function cleanBase64(value: string): string {
|
|
86
72
|
return value.replace(/\s+/g, '');
|
|
87
73
|
}
|
|
88
74
|
|
|
89
|
-
/**
|
|
90
|
-
* Decoded byte length of a (whitespace-free) base64 string.
|
|
91
|
-
*/
|
|
92
75
|
function base64ByteLength(clean: string): number {
|
|
93
76
|
if (clean.length === 0) {
|
|
94
77
|
return 0;
|
|
@@ -111,13 +94,25 @@ function formatBytes(bytes: number): string {
|
|
|
111
94
|
return `${value.toFixed(value < 10 ? 1 : 0)} ${units[unit]}`;
|
|
112
95
|
}
|
|
113
96
|
|
|
97
|
+
/**
|
|
98
|
+
* One side of the image comparison, decoded from its base64 payload.
|
|
99
|
+
*/
|
|
114
100
|
interface IImageSide {
|
|
115
101
|
/**
|
|
116
102
|
* Cleaned base64 payload; empty string means the side does not exist.
|
|
117
103
|
*/
|
|
118
104
|
data: string;
|
|
105
|
+
/**
|
|
106
|
+
* Whether this side exists; added and deleted files lack one side.
|
|
107
|
+
*/
|
|
119
108
|
present: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* The `data:` URI for the image, or `null` when the side is absent.
|
|
111
|
+
*/
|
|
120
112
|
uri: string | null;
|
|
113
|
+
/**
|
|
114
|
+
* Decoded size of the payload in bytes.
|
|
115
|
+
*/
|
|
121
116
|
bytes: number;
|
|
122
117
|
}
|
|
123
118
|
|
|
@@ -132,6 +127,9 @@ function toSide(raw: string, fileType: string): IImageSide {
|
|
|
132
127
|
};
|
|
133
128
|
}
|
|
134
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Props for {@link ImageDiffView}.
|
|
132
|
+
*/
|
|
135
133
|
interface IImageDiffViewProps {
|
|
136
134
|
/**
|
|
137
135
|
* Base64 of the reference (old) revision; empty when added.
|
|
@@ -152,9 +150,8 @@ interface IImageDiffViewProps {
|
|
|
152
150
|
}
|
|
153
151
|
|
|
154
152
|
/**
|
|
155
|
-
* The image comparison surface: a
|
|
156
|
-
*
|
|
157
|
-
* plus the selected 2-up / swipe / onion-skin view.
|
|
153
|
+
* The image comparison surface: a mode selector (reusing the diff widget's
|
|
154
|
+
* segmented-control styling) plus the selected 2-up / swipe / onion view.
|
|
158
155
|
*/
|
|
159
156
|
export function ImageDiffView(props: IImageDiffViewProps): React.ReactElement {
|
|
160
157
|
const { reference, challenger, fileType, trans } = props;
|
|
@@ -217,9 +214,21 @@ export function ImageDiffView(props: IImageDiffViewProps): React.ReactElement {
|
|
|
217
214
|
);
|
|
218
215
|
}
|
|
219
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Props shared by the 2-up, swipe, and onion-skin views.
|
|
219
|
+
*/
|
|
220
220
|
interface ISideViewProps {
|
|
221
|
+
/**
|
|
222
|
+
* The reference (old) side.
|
|
223
|
+
*/
|
|
221
224
|
reference: IImageSide;
|
|
225
|
+
/**
|
|
226
|
+
* The challenger (new) side.
|
|
227
|
+
*/
|
|
222
228
|
challenger: IImageSide;
|
|
229
|
+
/**
|
|
230
|
+
* Translation bundle for user-facing strings.
|
|
231
|
+
*/
|
|
223
232
|
trans: TranslationBundle;
|
|
224
233
|
}
|
|
225
234
|
|
|
@@ -303,11 +312,6 @@ function TwoUp({
|
|
|
303
312
|
);
|
|
304
313
|
}
|
|
305
314
|
|
|
306
|
-
/**
|
|
307
|
-
* Range slider shared by the swipe and onion-skin views. A native
|
|
308
|
-
* `<input type="range">` (styled via CSS) keeps the bundle free of a UI
|
|
309
|
-
* component dependency.
|
|
310
|
-
*/
|
|
311
315
|
function RangeSlider(props: {
|
|
312
316
|
value: number;
|
|
313
317
|
onChange: (value: number) => void;
|
|
@@ -329,11 +333,9 @@ function RangeSlider(props: {
|
|
|
329
333
|
}
|
|
330
334
|
|
|
331
335
|
/**
|
|
332
|
-
* Fit
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
* `max-width/height: 100%; width/height: auto`. Returns the displayed pixel
|
|
336
|
-
* box, or `null` until both the image and the area have been measured.
|
|
336
|
+
* Fit an image inside an area without upscaling past its intrinsic size —
|
|
337
|
+
* the "contain, never enlarge" rule the other views get from CSS. Returns
|
|
338
|
+
* the displayed pixel box, or `null` until both sizes are measured.
|
|
337
339
|
*/
|
|
338
340
|
function containBox(
|
|
339
341
|
naturalWidth: number,
|
|
@@ -357,9 +359,6 @@ function containBox(
|
|
|
357
359
|
return { width: naturalWidth * scale, height: naturalHeight * scale };
|
|
358
360
|
}
|
|
359
361
|
|
|
360
|
-
/**
|
|
361
|
-
* Track an element's content-box size, kept current across layout changes.
|
|
362
|
-
*/
|
|
363
362
|
function useElementSize(): readonly [
|
|
364
363
|
React.RefObject<HTMLDivElement>,
|
|
365
364
|
{ width: number; height: number }
|
|
@@ -396,13 +395,9 @@ function Swipe({
|
|
|
396
395
|
const [areaRef, area] = useElementSize();
|
|
397
396
|
const frameRef = React.useRef<HTMLDivElement>(null);
|
|
398
397
|
|
|
399
|
-
//
|
|
400
|
-
//
|
|
401
|
-
//
|
|
402
|
-
// mapping (below) and each image's `clip-path` inset then all resolve
|
|
403
|
-
// against the same width, so the white line sits precisely on the seam
|
|
404
|
-
// between the two revisions at every position. A full-width frame instead
|
|
405
|
-
// let the line drift off the seam by as much as half the letterbox.
|
|
398
|
+
// Size the frame to the union box of both revisions so the divider %, the
|
|
399
|
+
// pointer mapping and the clip-paths all resolve against the same width;
|
|
400
|
+
// a full-width frame lets the line drift off the seam.
|
|
406
401
|
const naturalWidth = Math.max(refSize?.[0] ?? 0, challSize?.[0] ?? 0);
|
|
407
402
|
const naturalHeight = Math.max(refSize?.[1] ?? 0, challSize?.[1] ?? 0);
|
|
408
403
|
const box = containBox(naturalWidth, naturalHeight, area.width, area.height);
|
|
@@ -422,7 +417,6 @@ function Swipe({
|
|
|
422
417
|
|
|
423
418
|
const onPointerDown = React.useCallback(
|
|
424
419
|
(event: React.PointerEvent<HTMLDivElement>) => {
|
|
425
|
-
// Only react to the primary pointer (left click / touch / pen tip).
|
|
426
420
|
if (event.button !== 0) {
|
|
427
421
|
return;
|
|
428
422
|
}
|
package/src/git/index.ts
CHANGED
|
@@ -23,10 +23,9 @@ const GIT_DIFF_COMMAND_PLUGIN_ID = 'xtralab:git-diff-command';
|
|
|
23
23
|
const GIT_DIFF_TRACKER_NAMESPACE = 'xtralab-git-diff';
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* The launcher's git diff command plugin.
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* its own `xtralab:git:open-diff` command and preview/pinned-tab tracking.
|
|
26
|
+
* The launcher's git diff command plugin. The upstream git panel stays
|
|
27
|
+
* enabled; this only adds `xtralab:git:open-diff` and preview/pinned-tab
|
|
28
|
+
* tracking.
|
|
30
29
|
*/
|
|
31
30
|
const diffCommandPlugin: JupyterFrontEndPlugin<void> = {
|
|
32
31
|
id: GIT_DIFF_COMMAND_PLUGIN_ID,
|
|
@@ -42,7 +41,6 @@ const diffCommandPlugin: JupyterFrontEndPlugin<void> = {
|
|
|
42
41
|
askAgent: IAskAgent | null
|
|
43
42
|
): void => {
|
|
44
43
|
const trans = (translator ?? nullTranslator).load('jupyterlab');
|
|
45
|
-
// Track open diffs so repeated opens reveal the existing tab.
|
|
46
44
|
const tracker = new WidgetTracker<DiffMainAreaWidget>({
|
|
47
45
|
namespace: GIT_DIFF_TRACKER_NAMESPACE
|
|
48
46
|
});
|
|
@@ -86,9 +84,6 @@ const diffCommandPlugin: JupyterFrontEndPlugin<void> = {
|
|
|
86
84
|
}
|
|
87
85
|
};
|
|
88
86
|
|
|
89
|
-
/**
|
|
90
|
-
* Plugins contributed by xtralab's git integration.
|
|
91
|
-
*/
|
|
92
87
|
const plugins: JupyterFrontEndPlugin<unknown>[] = [
|
|
93
88
|
diffCommandPlugin,
|
|
94
89
|
diffProviderPlugin
|