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.
Files changed (236) hide show
  1. package/README.md +10 -0
  2. package/lib/about/icons.d.ts +0 -5
  3. package/lib/about/icons.js +0 -5
  4. package/lib/about/index.d.ts +4 -8
  5. package/lib/about/index.js +6 -17
  6. package/lib/agentSessions.d.ts +9 -29
  7. package/lib/agentSessions.js +2 -7
  8. package/lib/askAgent/editorSelection.d.ts +20 -26
  9. package/lib/askAgent/editorSelection.js +11 -26
  10. package/lib/askAgent/icons.d.ts +2 -3
  11. package/lib/askAgent/icons.js +2 -3
  12. package/lib/askAgent/index.d.ts +4 -26
  13. package/lib/askAgent/index.js +54 -158
  14. package/lib/askAgent/popup.d.ts +12 -16
  15. package/lib/askAgent/popup.js +18 -37
  16. package/lib/askAgent/prompt.d.ts +3 -5
  17. package/lib/askAgent/prompt.js +6 -19
  18. package/lib/askAgent/queue.d.ts +11 -21
  19. package/lib/askAgent/queue.js +9 -19
  20. package/lib/askAgent/queuePanel.d.ts +13 -18
  21. package/lib/askAgent/queuePanel.js +15 -37
  22. package/lib/askAgent/targetPicker.d.ts +2 -9
  23. package/lib/askAgent/targetPicker.js +6 -11
  24. package/lib/askAgent/tokens.d.ts +32 -46
  25. package/lib/askAgent/tokens.js +4 -6
  26. package/lib/commandBar/index.d.ts +4 -13
  27. package/lib/commandBar/index.js +21 -39
  28. package/lib/customPanel/index.d.ts +7 -0
  29. package/lib/customPanel/index.js +89 -0
  30. package/lib/editorBreadcrumbs/index.d.ts +3 -6
  31. package/lib/editorBreadcrumbs/index.js +10 -6
  32. package/lib/editorBreadcrumbs/widget.d.ts +17 -4
  33. package/lib/editorBreadcrumbs/widget.js +11 -17
  34. package/lib/editorIndent/index.d.ts +4 -12
  35. package/lib/editorIndent/index.js +8 -23
  36. package/lib/fileBrowser/commands.d.ts +28 -19
  37. package/lib/fileBrowser/commands.js +51 -98
  38. package/lib/fileBrowser/contents.d.ts +14 -0
  39. package/lib/fileBrowser/contents.js +28 -0
  40. package/lib/fileBrowser/dragAndDrop.d.ts +79 -0
  41. package/lib/fileBrowser/dragAndDrop.js +110 -0
  42. package/lib/fileBrowser/fileBrowser.d.ts +21 -9
  43. package/lib/fileBrowser/fileBrowser.js +211 -405
  44. package/lib/fileBrowser/gitStatus.d.ts +4 -9
  45. package/lib/fileBrowser/gitStatus.js +7 -17
  46. package/lib/fileBrowser/gitignore.d.ts +4 -17
  47. package/lib/fileBrowser/gitignore.js +4 -25
  48. package/lib/fileBrowser/icons.d.ts +7 -17
  49. package/lib/fileBrowser/icons.js +25 -80
  50. package/lib/fileBrowser/index.js +41 -10
  51. package/lib/fileBrowser/widget.d.ts +170 -53
  52. package/lib/fileBrowser/widget.js +242 -18
  53. package/lib/fileTypeIcons/index.js +0 -10
  54. package/lib/git/api.d.ts +5 -13
  55. package/lib/git/api.js +10 -36
  56. package/lib/git/askRequest.d.ts +4 -8
  57. package/lib/git/askRequest.js +6 -20
  58. package/lib/git/commands.d.ts +40 -5
  59. package/lib/git/commands.js +16 -10
  60. package/lib/git/diffModel.js +4 -0
  61. package/lib/git/diffSurface.d.ts +41 -55
  62. package/lib/git/diffSurface.js +42 -117
  63. package/lib/git/diffWidget.d.ts +81 -2
  64. package/lib/git/diffWidget.js +62 -12
  65. package/lib/git/imageDiff.d.ts +5 -6
  66. package/lib/git/imageDiff.js +18 -48
  67. package/lib/git/index.d.ts +0 -3
  68. package/lib/git/index.js +3 -8
  69. package/lib/git/notebookDiff.d.ts +110 -33
  70. package/lib/git/notebookDiff.js +52 -195
  71. package/lib/git/tokens.d.ts +73 -31
  72. package/lib/git/tokens.js +2 -3
  73. package/lib/highlight/index.d.ts +4 -10
  74. package/lib/highlight/index.js +10 -41
  75. package/lib/index.d.ts +0 -9
  76. package/lib/index.js +2 -9
  77. package/lib/launcher/agents.d.ts +55 -51
  78. package/lib/launcher/agents.js +25 -54
  79. package/lib/launcher/availability.d.ts +3 -9
  80. package/lib/launcher/availability.js +3 -9
  81. package/lib/launcher/commands.d.ts +7 -32
  82. package/lib/launcher/commands.js +14 -45
  83. package/lib/launcher/dashboard.d.ts +19 -24
  84. package/lib/launcher/dashboard.js +21 -67
  85. package/lib/launcher/editorRegistry.d.ts +10 -22
  86. package/lib/launcher/editorRegistry.js +14 -18
  87. package/lib/launcher/editors.d.ts +39 -45
  88. package/lib/launcher/editors.js +8 -34
  89. package/lib/launcher/icons.d.ts +0 -12
  90. package/lib/launcher/icons.js +14 -37
  91. package/lib/launcher/index.js +12 -55
  92. package/lib/launcher/invocation.d.ts +4 -10
  93. package/lib/launcher/invocation.js +7 -22
  94. package/lib/launcher/registry.d.ts +9 -7
  95. package/lib/launcher/registry.js +9 -7
  96. package/lib/launcher/schemaDefaults.d.ts +2 -9
  97. package/lib/launcher/schemaDefaults.js +37 -13
  98. package/lib/launcher/tokens.d.ts +10 -22
  99. package/lib/launcher/tokens.js +5 -8
  100. package/lib/menuBar/index.d.ts +3 -10
  101. package/lib/menuBar/index.js +11 -28
  102. package/lib/menus/index.d.ts +3 -9
  103. package/lib/menus/index.js +14 -30
  104. package/lib/omnibox/files.d.ts +2 -4
  105. package/lib/omnibox/files.js +5 -11
  106. package/lib/omnibox/index.d.ts +4 -14
  107. package/lib/omnibox/index.js +9 -27
  108. package/lib/omnibox/model.d.ts +38 -10
  109. package/lib/omnibox/model.js +15 -33
  110. package/lib/omnibox/recents.d.ts +15 -17
  111. package/lib/omnibox/recents.js +12 -17
  112. package/lib/omnibox/tokens.d.ts +5 -10
  113. package/lib/omnibox/tokens.js +2 -6
  114. package/lib/omnibox/widget.d.ts +11 -7
  115. package/lib/omnibox/widget.js +8 -16
  116. package/lib/searchReplace/index.d.ts +4 -20
  117. package/lib/searchReplace/index.js +7 -27
  118. package/lib/showOutput/index.d.ts +3 -12
  119. package/lib/showOutput/index.js +6 -22
  120. package/lib/sidebar/index.d.ts +4 -13
  121. package/lib/sidebar/index.js +73 -46
  122. package/lib/terminalNotifications/index.d.ts +4 -10
  123. package/lib/terminalNotifications/index.js +6 -20
  124. package/lib/terminals/agentTerminals.d.ts +12 -5
  125. package/lib/terminals/agentTerminals.js +25 -44
  126. package/lib/terminals/detection.d.ts +4 -8
  127. package/lib/terminals/detection.js +4 -8
  128. package/lib/terminals/index.d.ts +0 -33
  129. package/lib/terminals/index.js +24 -102
  130. package/lib/terminals/model.d.ts +64 -139
  131. package/lib/terminals/model.js +80 -220
  132. package/lib/terminals/tokens.d.ts +11 -22
  133. package/lib/terminals/tokens.js +2 -2
  134. package/lib/terminals/widget.d.ts +72 -38
  135. package/lib/terminals/widget.js +173 -73
  136. package/lib/topBar/icons.d.ts +3 -8
  137. package/lib/topBar/icons.js +3 -8
  138. package/lib/topBar/index.d.ts +4 -11
  139. package/lib/topBar/index.js +10 -38
  140. package/lib/walkthrough/index.d.ts +4 -9
  141. package/lib/walkthrough/index.js +6 -20
  142. package/lib/walkthrough/panel.d.ts +40 -9
  143. package/lib/walkthrough/panel.js +4 -10
  144. package/package.json +2 -2
  145. package/schema/launcher.json +2 -2
  146. package/schema/sidebar.json +47 -7
  147. package/src/about/icons.ts +0 -5
  148. package/src/about/index.tsx +6 -17
  149. package/src/agentSessions.ts +9 -29
  150. package/src/askAgent/editorSelection.ts +22 -29
  151. package/src/askAgent/icons.ts +2 -3
  152. package/src/askAgent/index.ts +54 -158
  153. package/src/askAgent/popup.tsx +23 -45
  154. package/src/askAgent/prompt.ts +6 -19
  155. package/src/askAgent/queue.ts +13 -24
  156. package/src/askAgent/queuePanel.tsx +21 -46
  157. package/src/askAgent/targetPicker.tsx +5 -15
  158. package/src/askAgent/tokens.ts +32 -46
  159. package/src/commandBar/index.ts +24 -39
  160. package/src/customPanel/index.ts +119 -0
  161. package/src/editorBreadcrumbs/index.ts +10 -6
  162. package/src/editorBreadcrumbs/widget.ts +23 -17
  163. package/src/editorIndent/index.ts +17 -23
  164. package/src/fileBrowser/commands.ts +70 -98
  165. package/src/fileBrowser/contents.ts +33 -0
  166. package/src/fileBrowser/dragAndDrop.ts +188 -0
  167. package/src/fileBrowser/fileBrowser.tsx +259 -440
  168. package/src/fileBrowser/gitStatus.ts +7 -17
  169. package/src/fileBrowser/gitignore.ts +4 -25
  170. package/src/fileBrowser/icons.ts +25 -80
  171. package/src/fileBrowser/index.ts +43 -10
  172. package/src/fileBrowser/widget.tsx +344 -64
  173. package/src/fileTypeIcons/index.ts +0 -10
  174. package/src/git/api.ts +10 -36
  175. package/src/git/askRequest.ts +6 -20
  176. package/src/git/commands.ts +70 -10
  177. package/src/git/diffModel.ts +25 -0
  178. package/src/git/diffSurface.tsx +71 -159
  179. package/src/git/diffWidget.tsx +104 -14
  180. package/src/git/imageDiff.tsx +45 -51
  181. package/src/git/index.ts +3 -8
  182. package/src/git/notebookDiff.tsx +211 -238
  183. package/src/git/tokens.ts +73 -31
  184. package/src/highlight/index.ts +10 -41
  185. package/src/index.ts +2 -9
  186. package/src/launcher/agents.ts +74 -92
  187. package/src/launcher/availability.ts +3 -9
  188. package/src/launcher/commands.ts +14 -51
  189. package/src/launcher/dashboard.tsx +49 -88
  190. package/src/launcher/editorRegistry.ts +21 -29
  191. package/src/launcher/editors.ts +39 -58
  192. package/src/launcher/icons.ts +15 -37
  193. package/src/launcher/index.ts +12 -55
  194. package/src/launcher/invocation.ts +7 -22
  195. package/src/launcher/registry.ts +9 -7
  196. package/src/launcher/schemaDefaults.ts +43 -13
  197. package/src/launcher/tokens.ts +10 -22
  198. package/src/menuBar/index.ts +11 -28
  199. package/src/menus/index.ts +14 -30
  200. package/src/omnibox/files.ts +10 -14
  201. package/src/omnibox/index.ts +9 -27
  202. package/src/omnibox/model.ts +50 -38
  203. package/src/omnibox/recents.ts +17 -22
  204. package/src/omnibox/tokens.ts +5 -10
  205. package/src/omnibox/widget.tsx +14 -19
  206. package/src/searchReplace/index.ts +7 -27
  207. package/src/showOutput/index.ts +6 -22
  208. package/src/sidebar/index.ts +103 -52
  209. package/src/terminalNotifications/index.ts +42 -25
  210. package/src/terminals/agentTerminals.ts +25 -49
  211. package/src/terminals/detection.ts +4 -8
  212. package/src/terminals/index.ts +31 -102
  213. package/src/terminals/model.ts +130 -246
  214. package/src/terminals/tokens.ts +11 -22
  215. package/src/terminals/widget.tsx +229 -113
  216. package/src/topBar/icons.ts +3 -8
  217. package/src/topBar/index.ts +21 -44
  218. package/src/walkthrough/index.ts +6 -20
  219. package/src/walkthrough/panel.ts +40 -10
  220. package/style/about.css +2 -7
  221. package/style/askAgent.css +8 -40
  222. package/style/base.css +25 -73
  223. package/style/chrome.css +108 -308
  224. package/style/commandBar.css +4 -20
  225. package/style/customPanel.css +6 -0
  226. package/style/git.css +22 -184
  227. package/style/highlight.css +1 -3
  228. package/style/index.js +1 -0
  229. package/style/launcher.css +6 -70
  230. package/style/omnibox.css +2 -14
  231. package/style/showOutput.css +0 -6
  232. package/style/sidebar.css +29 -9
  233. package/style/tabs.css +5 -13
  234. package/style/terminals.css +18 -123
  235. package/style/topBar.css +2 -22
  236. package/style/walkthrough.css +0 -8
@@ -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
- * The ask-agent popup; when available, diff line selections get an
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
- // Release any in-flight refresh awaiter so a host that awaits refresh()
215
- // (jupyterlab-git re-shows its diff button only once it resolves) does not
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
 
@@ -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
- * The git server base64-encodes binary content, so both diff hosts receive
9
- * the two sides as base64 strings through the same `content()` getters used
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 image extensions xtralab renders as an image diff, mapped to the
17
- * MIME subtype used in the `data:image/<subtype>;base64,…` URL.
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 — 2-up is the
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. Python's `base64.encodebytes`
81
- * (used by the git server) inserts a newline every 76 characters; most
82
- * browsers tolerate that inside a `data:` URL but some are stricter, and
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 segmented mode selector (reusing the
156
- * diff segmented-control styling so it matches the Notebook/JSON toggle)
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 a `naturalWidth × naturalHeight` image inside an `areaWidth ×
333
- * areaHeight` box without upscaling past its intrinsic size — the same
334
- * "contain, never enlarge" rule the 2-up and onion views get for free from
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
- // Display both revisions in a single box — the union of their intrinsic
400
- // sizes, scaled down to fit but never enlarged — and size the frame to
401
- // exactly that box. The divider's `left: N%`, the pointer-to-percent
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
- * The upstream git panel stays enabled; this plugin only gives the launcher
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