xtralab 0.11.1 → 0.12.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.
Files changed (159) hide show
  1. package/README.md +37 -231
  2. package/lib/agentSessions.d.ts +6 -2
  3. package/lib/askAgent/editorSelection.d.ts +53 -0
  4. package/lib/askAgent/editorSelection.js +95 -0
  5. package/lib/askAgent/icons.d.ts +7 -0
  6. package/lib/askAgent/icons.js +15 -0
  7. package/lib/askAgent/index.d.ts +32 -0
  8. package/lib/askAgent/index.js +741 -0
  9. package/lib/askAgent/popup.d.ts +83 -0
  10. package/lib/askAgent/popup.js +221 -0
  11. package/lib/askAgent/prompt.d.ts +24 -0
  12. package/lib/askAgent/prompt.js +116 -0
  13. package/lib/askAgent/queue.d.ts +93 -0
  14. package/lib/askAgent/queue.js +207 -0
  15. package/lib/askAgent/queuePanel.d.ts +76 -0
  16. package/lib/askAgent/queuePanel.js +188 -0
  17. package/lib/askAgent/targetPicker.d.ts +58 -0
  18. package/lib/askAgent/targetPicker.js +74 -0
  19. package/lib/askAgent/tokens.d.ts +124 -0
  20. package/lib/askAgent/tokens.js +13 -0
  21. package/lib/commandBar/index.d.ts +12 -8
  22. package/lib/commandBar/index.js +58 -41
  23. package/lib/editorBreadcrumbs/index.js +9 -4
  24. package/lib/editorBreadcrumbs/widget.d.ts +7 -7
  25. package/lib/editorBreadcrumbs/widget.js +6 -8
  26. package/lib/fileBrowser/commands.d.ts +2 -8
  27. package/lib/fileBrowser/commands.js +39 -30
  28. package/lib/fileBrowser/contents.d.ts +8 -3
  29. package/lib/fileBrowser/fileBrowser.d.ts +2 -1
  30. package/lib/fileBrowser/widget.d.ts +5 -9
  31. package/lib/fileBrowser/widget.js +1 -1
  32. package/lib/fileTypeIcons/index.js +5 -2
  33. package/lib/git/api.d.ts +0 -26
  34. package/lib/git/api.js +2 -44
  35. package/lib/git/askRequest.d.ts +23 -0
  36. package/lib/git/askRequest.js +90 -0
  37. package/lib/git/commands.d.ts +14 -2
  38. package/lib/git/commands.js +16 -13
  39. package/lib/git/diffModel.d.ts +1 -9
  40. package/lib/git/diffModel.js +1 -1
  41. package/lib/git/diffProvider.js +8 -3
  42. package/lib/git/diffSurface.d.ts +19 -2
  43. package/lib/git/diffSurface.js +64 -18
  44. package/lib/git/diffTheme.d.ts +0 -4
  45. package/lib/git/diffTheme.js +1 -1
  46. package/lib/git/diffWidget.d.ts +9 -0
  47. package/lib/git/diffWidget.js +38 -10
  48. package/lib/git/imageDiff.d.ts +17 -4
  49. package/lib/git/imageDiff.js +90 -30
  50. package/lib/git/index.js +7 -2
  51. package/lib/git/notebookDiff.d.ts +10 -67
  52. package/lib/git/notebookDiff.js +62 -69
  53. package/lib/git/tokens.d.ts +5 -2
  54. package/lib/highlight/index.js +12 -8
  55. package/lib/index.js +2 -0
  56. package/lib/launcher/agents.js +3 -4
  57. package/lib/launcher/commands.d.ts +6 -2
  58. package/lib/launcher/dashboard.d.ts +8 -1
  59. package/lib/launcher/dashboard.js +39 -33
  60. package/lib/launcher/editorRegistry.js +5 -3
  61. package/lib/launcher/editors.d.ts +15 -5
  62. package/lib/launcher/icons.d.ts +0 -37
  63. package/lib/launcher/icons.js +11 -12
  64. package/lib/launcher/index.js +2 -1
  65. package/lib/launcher/invocation.d.ts +0 -15
  66. package/lib/launcher/invocation.js +1 -1
  67. package/lib/launcher/schemaDefaults.d.ts +3 -1
  68. package/lib/launcher/schemaDefaults.js +3 -1
  69. package/lib/mimeData.d.ts +7 -0
  70. package/lib/mimeData.js +16 -0
  71. package/lib/omnibox/index.d.ts +4 -0
  72. package/lib/omnibox/index.js +43 -2
  73. package/lib/omnibox/model.d.ts +43 -14
  74. package/lib/omnibox/model.js +117 -14
  75. package/lib/omnibox/recents.d.ts +65 -0
  76. package/lib/omnibox/recents.js +109 -0
  77. package/lib/omnibox/tokens.d.ts +6 -2
  78. package/lib/omnibox/widget.d.ts +23 -6
  79. package/lib/omnibox/widget.js +26 -7
  80. package/lib/showOutput/index.js +15 -18
  81. package/lib/terminals/agentTerminals.d.ts +63 -0
  82. package/lib/terminals/agentTerminals.js +157 -0
  83. package/lib/terminals/index.d.ts +7 -1
  84. package/lib/terminals/index.js +50 -30
  85. package/lib/terminals/model.d.ts +11 -2
  86. package/lib/terminals/model.js +27 -8
  87. package/lib/terminals/tokens.d.ts +66 -0
  88. package/lib/terminals/tokens.js +7 -0
  89. package/lib/terminals/widget.d.ts +3 -6
  90. package/lib/terminals/widget.js +1 -1
  91. package/lib/topBar/index.js +3 -3
  92. package/lib/walkthrough/index.js +14 -3
  93. package/lib/walkthrough/panel.d.ts +12 -4
  94. package/lib/walkthrough/panel.js +9 -3
  95. package/package.json +49 -51
  96. package/schema/command-bar.json +10 -0
  97. package/schema/omnibox.json +15 -0
  98. package/src/agentSessions.ts +6 -2
  99. package/src/askAgent/editorSelection.ts +138 -0
  100. package/src/askAgent/icons.ts +16 -0
  101. package/src/askAgent/index.ts +915 -0
  102. package/src/askAgent/popup.tsx +407 -0
  103. package/src/askAgent/prompt.ts +144 -0
  104. package/src/askAgent/queue.ts +269 -0
  105. package/src/askAgent/queuePanel.tsx +418 -0
  106. package/src/askAgent/targetPicker.tsx +209 -0
  107. package/src/askAgent/tokens.ts +139 -0
  108. package/src/commandBar/index.ts +75 -49
  109. package/src/editorBreadcrumbs/index.ts +14 -4
  110. package/src/editorBreadcrumbs/widget.ts +11 -9
  111. package/src/fileBrowser/commands.ts +45 -31
  112. package/src/fileBrowser/contents.ts +7 -3
  113. package/src/fileBrowser/fileBrowser.tsx +1 -1
  114. package/src/fileBrowser/widget.tsx +5 -3
  115. package/src/fileTypeIcons/index.ts +6 -3
  116. package/src/git/api.ts +2 -61
  117. package/src/git/askRequest.ts +113 -0
  118. package/src/git/commands.ts +41 -14
  119. package/src/git/diffModel.ts +1 -1
  120. package/src/git/diffProvider.tsx +11 -3
  121. package/src/git/diffSurface.tsx +114 -22
  122. package/src/git/diffTheme.ts +1 -1
  123. package/src/git/diffWidget.tsx +54 -7
  124. package/src/git/imageDiff.tsx +169 -58
  125. package/src/git/index.ts +10 -2
  126. package/src/git/notebookDiff.tsx +91 -57
  127. package/src/git/tokens.ts +4 -2
  128. package/src/highlight/index.ts +12 -8
  129. package/src/index.ts +2 -0
  130. package/src/launcher/agents.ts +3 -4
  131. package/src/launcher/commands.ts +6 -2
  132. package/src/launcher/dashboard.tsx +86 -38
  133. package/src/launcher/editorRegistry.ts +10 -4
  134. package/src/launcher/editors.ts +15 -5
  135. package/src/launcher/icons.ts +11 -12
  136. package/src/launcher/index.ts +2 -1
  137. package/src/launcher/invocation.ts +1 -1
  138. package/src/launcher/schemaDefaults.ts +3 -1
  139. package/src/mimeData.ts +20 -0
  140. package/src/omnibox/index.ts +48 -1
  141. package/src/omnibox/model.ts +173 -33
  142. package/src/omnibox/recents.ts +155 -0
  143. package/src/omnibox/tokens.ts +6 -2
  144. package/src/omnibox/widget.tsx +54 -13
  145. package/src/showOutput/index.ts +20 -23
  146. package/src/terminals/agentTerminals.ts +238 -0
  147. package/src/terminals/index.ts +54 -30
  148. package/src/terminals/model.ts +28 -7
  149. package/src/terminals/tokens.ts +77 -0
  150. package/src/terminals/widget.tsx +4 -2
  151. package/src/topBar/index.ts +12 -6
  152. package/src/walkthrough/index.ts +19 -4
  153. package/src/walkthrough/panel.ts +15 -5
  154. package/style/askAgent.css +544 -0
  155. package/style/commandBar.css +12 -33
  156. package/style/git.css +72 -31
  157. package/style/index.css +1 -0
  158. package/style/index.js +2 -0
  159. package/style/topBar.css +4 -4
@@ -1,5 +1,7 @@
1
1
  import * as React from 'react';
2
2
 
3
+ import type { TranslationBundle } from '@jupyterlab/translation';
4
+
3
5
  /**
4
6
  * xtralab's image diff, registered in place of `@jupyterlab/git:image-diff`.
5
7
  *
@@ -28,7 +30,9 @@ const IMAGE_DATA_TYPES: Record<string, string> = {
28
30
  '.ico': 'x-icon'
29
31
  };
30
32
 
31
- /** The file extensions registered as the xtralab image diff provider. */
33
+ /**
34
+ * The file extensions registered as the xtralab image diff provider.
35
+ */
32
36
  export const IMAGE_DIFF_EXTENSIONS = Object.keys(IMAGE_DATA_TYPES);
33
37
 
34
38
  /**
@@ -44,7 +48,9 @@ export function imageDataType(path: string): string | null {
44
48
  return IMAGE_DATA_TYPES[lower.slice(dot)] ?? null;
45
49
  }
46
50
 
47
- /** Comparison layouts, matching jupyterlab-git's image diff modes. */
51
+ /**
52
+ * Comparison layouts, matching jupyterlab-git's image diff modes.
53
+ */
48
54
  type ImageDiffViewMode = '2-up' | 'swipe' | 'onion';
49
55
 
50
56
  const IMAGE_DIFF_VIEW_MODE_STORAGE_KEY = 'xtralab:image-diff-view-mode';
@@ -80,7 +86,9 @@ function cleanBase64(value: string): string {
80
86
  return value.replace(/\s+/g, '');
81
87
  }
82
88
 
83
- /** Decoded byte length of a (whitespace-free) base64 string. */
89
+ /**
90
+ * Decoded byte length of a (whitespace-free) base64 string.
91
+ */
84
92
  function base64ByteLength(clean: string): number {
85
93
  if (clean.length === 0) {
86
94
  return 0;
@@ -104,7 +112,9 @@ function formatBytes(bytes: number): string {
104
112
  }
105
113
 
106
114
  interface IImageSide {
107
- /** Cleaned base64 payload; empty string means the side does not exist. */
115
+ /**
116
+ * Cleaned base64 payload; empty string means the side does not exist.
117
+ */
108
118
  data: string;
109
119
  present: boolean;
110
120
  uri: string | null;
@@ -123,12 +133,22 @@ function toSide(raw: string, fileType: string): IImageSide {
123
133
  }
124
134
 
125
135
  interface IImageDiffViewProps {
126
- /** Base64 of the reference (old) revision; empty when added. */
136
+ /**
137
+ * Base64 of the reference (old) revision; empty when added.
138
+ */
127
139
  reference: string;
128
- /** Base64 of the challenger (new) revision; empty when deleted. */
140
+ /**
141
+ * Base64 of the challenger (new) revision; empty when deleted.
142
+ */
129
143
  challenger: string;
130
- /** MIME subtype from {@link imageDataType}. */
144
+ /**
145
+ * MIME subtype from {@link imageDataType}.
146
+ */
131
147
  fileType: string;
148
+ /**
149
+ * Translation bundle for user-facing strings.
150
+ */
151
+ trans: TranslationBundle;
132
152
  }
133
153
 
134
154
  /**
@@ -137,7 +157,7 @@ interface IImageDiffViewProps {
137
157
  * plus the selected 2-up / swipe / onion-skin view.
138
158
  */
139
159
  export function ImageDiffView(props: IImageDiffViewProps): React.ReactElement {
140
- const { reference, challenger, fileType } = props;
160
+ const { reference, challenger, fileType, trans } = props;
141
161
  const ref = React.useMemo(
142
162
  () => toSide(reference, fileType),
143
163
  [reference, fileType]
@@ -161,13 +181,13 @@ export function ImageDiffView(props: IImageDiffViewProps): React.ReactElement {
161
181
  <div
162
182
  className="jp-xtralab-DiffWidget-segmented"
163
183
  role="tablist"
164
- aria-label="Image diff view mode"
184
+ aria-label={trans.__('Image diff view mode')}
165
185
  >
166
186
  {(
167
187
  [
168
- ['2-up', '2-up'],
169
- ['swipe', 'Swipe'],
170
- ['onion', 'Onion Skin']
188
+ ['2-up', trans.__('2-up')],
189
+ ['swipe', trans.__('Swipe')],
190
+ ['onion', trans.__('Onion Skin')]
171
191
  ] as [ImageDiffViewMode, string][]
172
192
  ).map(([value, label]) => (
173
193
  <button
@@ -186,11 +206,11 @@ export function ImageDiffView(props: IImageDiffViewProps): React.ReactElement {
186
206
  </div>
187
207
  <div className="jp-xtralab-ImageDiff-body">
188
208
  {mode === '2-up' ? (
189
- <TwoUp reference={ref} challenger={chall} />
209
+ <TwoUp reference={ref} challenger={chall} trans={trans} />
190
210
  ) : mode === 'swipe' ? (
191
- <Swipe reference={ref} challenger={chall} />
211
+ <Swipe reference={ref} challenger={chall} trans={trans} />
192
212
  ) : (
193
- <OnionSkin reference={ref} challenger={chall} />
213
+ <OnionSkin reference={ref} challenger={chall} trans={trans} />
194
214
  )}
195
215
  </div>
196
216
  </div>
@@ -200,6 +220,7 @@ export function ImageDiffView(props: IImageDiffViewProps): React.ReactElement {
200
220
  interface ISideViewProps {
201
221
  reference: IImageSide;
202
222
  challenger: IImageSide;
223
+ trans: TranslationBundle;
203
224
  }
204
225
 
205
226
  function useNaturalSize(): [
@@ -231,7 +252,11 @@ function caption(
231
252
  return `${dims}${formatBytes(side.bytes)}`;
232
253
  }
233
254
 
234
- function TwoUp({ reference, challenger }: ISideViewProps): React.ReactElement {
255
+ function TwoUp({
256
+ reference,
257
+ challenger,
258
+ trans
259
+ }: ISideViewProps): React.ReactElement {
235
260
  const [refSize, onRefLoad] = useNaturalSize();
236
261
  const [challSize, onChallLoad] = useNaturalSize();
237
262
  return (
@@ -242,15 +267,17 @@ function TwoUp({ reference, challenger }: ISideViewProps): React.ReactElement {
242
267
  <img
243
268
  className="jp-xtralab-ImageDiff-img"
244
269
  src={reference.uri}
245
- alt="Reference revision"
270
+ alt={trans.__('Reference revision')}
246
271
  onLoad={onRefLoad}
247
272
  />
248
273
  ) : (
249
- <div className="jp-xtralab-ImageDiff-empty">No previous image</div>
274
+ <div className="jp-xtralab-ImageDiff-empty">
275
+ {trans.__('No previous image')}
276
+ </div>
250
277
  )}
251
278
  </div>
252
279
  <figcaption className="jp-xtralab-ImageDiff-caption">
253
- {caption(reference, refSize, 'Added')}
280
+ {caption(reference, refSize, trans.__('Added'))}
254
281
  </figcaption>
255
282
  </figure>
256
283
  <figure className="jp-xtralab-ImageDiff-col">
@@ -259,15 +286,17 @@ function TwoUp({ reference, challenger }: ISideViewProps): React.ReactElement {
259
286
  <img
260
287
  className="jp-xtralab-ImageDiff-img"
261
288
  src={challenger.uri}
262
- alt="Challenger revision"
289
+ alt={trans.__('Challenger revision')}
263
290
  onLoad={onChallLoad}
264
291
  />
265
292
  ) : (
266
- <div className="jp-xtralab-ImageDiff-empty">Deleted</div>
293
+ <div className="jp-xtralab-ImageDiff-empty">
294
+ {trans.__('Deleted')}
295
+ </div>
267
296
  )}
268
297
  </div>
269
298
  <figcaption className="jp-xtralab-ImageDiff-caption">
270
- {caption(challenger, challSize, 'Deleted')}
299
+ {caption(challenger, challSize, trans.__('Deleted'))}
271
300
  </figcaption>
272
301
  </figure>
273
302
  </div>
@@ -299,11 +328,85 @@ function RangeSlider(props: {
299
328
  );
300
329
  }
301
330
 
302
- function Swipe({ reference, challenger }: ISideViewProps): React.ReactElement {
331
+ /**
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.
337
+ */
338
+ function containBox(
339
+ naturalWidth: number,
340
+ naturalHeight: number,
341
+ areaWidth: number,
342
+ areaHeight: number
343
+ ): { width: number; height: number } | null {
344
+ if (
345
+ naturalWidth <= 0 ||
346
+ naturalHeight <= 0 ||
347
+ areaWidth <= 0 ||
348
+ areaHeight <= 0
349
+ ) {
350
+ return null;
351
+ }
352
+ const scale = Math.min(
353
+ areaWidth / naturalWidth,
354
+ areaHeight / naturalHeight,
355
+ 1
356
+ );
357
+ return { width: naturalWidth * scale, height: naturalHeight * scale };
358
+ }
359
+
360
+ /**
361
+ * Track an element's content-box size, kept current across layout changes.
362
+ */
363
+ function useElementSize(): readonly [
364
+ React.RefObject<HTMLDivElement>,
365
+ { width: number; height: number }
366
+ ] {
367
+ const ref = React.useRef<HTMLDivElement>(null);
368
+ const [size, setSize] = React.useState({ width: 0, height: 0 });
369
+ React.useEffect(() => {
370
+ const el = ref.current;
371
+ if (el === null) {
372
+ return;
373
+ }
374
+ const measure = () => {
375
+ setSize({ width: el.clientWidth, height: el.clientHeight });
376
+ };
377
+ measure();
378
+ const observer = new ResizeObserver(measure);
379
+ observer.observe(el);
380
+ return () => {
381
+ observer.disconnect();
382
+ };
383
+ }, []);
384
+ return [ref, size] as const;
385
+ }
386
+
387
+ function Swipe({
388
+ reference,
389
+ challenger,
390
+ trans
391
+ }: ISideViewProps): React.ReactElement {
303
392
  const [position, setPosition] = React.useState(50);
304
393
  const [dragging, setDragging] = React.useState(false);
394
+ const [refSize, onRefLoad] = useNaturalSize();
395
+ const [challSize, onChallLoad] = useNaturalSize();
396
+ const [areaRef, area] = useElementSize();
305
397
  const frameRef = React.useRef<HTMLDivElement>(null);
306
398
 
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.
406
+ const naturalWidth = Math.max(refSize?.[0] ?? 0, challSize?.[0] ?? 0);
407
+ const naturalHeight = Math.max(refSize?.[1] ?? 0, challSize?.[1] ?? 0);
408
+ const box = containBox(naturalWidth, naturalHeight, area.width, area.height);
409
+
307
410
  const setFromClientX = React.useCallback((clientX: number) => {
308
411
  const frame = frameRef.current;
309
412
  if (frame === null) {
@@ -379,19 +482,25 @@ function Swipe({ reference, challenger }: ISideViewProps): React.ReactElement {
379
482
 
380
483
  return (
381
484
  <div className="jp-xtralab-ImageDiff-interactive">
382
- <div
383
- className="jp-xtralab-ImageDiff-swipeFrame"
384
- ref={frameRef}
385
- onPointerDown={onPointerDown}
386
- data-dragging={dragging ? 'true' : undefined}
387
- >
388
- <div className="jp-xtralab-ImageDiff-stack">
485
+ <div className="jp-xtralab-ImageDiff-swipeArea" ref={areaRef}>
486
+ <div
487
+ className="jp-xtralab-ImageDiff-swipeFrame"
488
+ ref={frameRef}
489
+ style={
490
+ box !== null
491
+ ? { width: `${box.width}px`, height: `${box.height}px` }
492
+ : { width: 0, height: 0, visibility: 'hidden' }
493
+ }
494
+ onPointerDown={onPointerDown}
495
+ data-dragging={dragging ? 'true' : undefined}
496
+ >
389
497
  {reference.uri !== null ? (
390
498
  <img
391
- className="jp-xtralab-ImageDiff-stackImg"
499
+ className="jp-xtralab-ImageDiff-swipeImg"
392
500
  src={reference.uri}
393
- alt="Reference revision"
501
+ alt={trans.__('Reference revision')}
394
502
  draggable={false}
503
+ onLoad={onRefLoad}
395
504
  style={{
396
505
  clipPath: `inset(0 ${100 - position}% 0 0)`
397
506
  }}
@@ -399,35 +508,36 @@ function Swipe({ reference, challenger }: ISideViewProps): React.ReactElement {
399
508
  ) : null}
400
509
  {challenger.uri !== null ? (
401
510
  <img
402
- className="jp-xtralab-ImageDiff-stackImg"
511
+ className="jp-xtralab-ImageDiff-swipeImg"
403
512
  src={challenger.uri}
404
- alt="Challenger revision"
513
+ alt={trans.__('Challenger revision')}
405
514
  draggable={false}
515
+ onLoad={onChallLoad}
406
516
  style={{
407
517
  clipPath: `inset(0 0 0 ${position}%)`
408
518
  }}
409
519
  />
410
520
  ) : null}
411
- </div>
412
- <div
413
- className="jp-xtralab-ImageDiff-swipeDivider"
414
- style={{ left: `${position}%` }}
415
- role="slider"
416
- aria-label="Swipe between reference and challenger"
417
- aria-valuemin={0}
418
- aria-valuemax={100}
419
- aria-valuenow={Math.round(position)}
420
- aria-orientation="vertical"
421
- tabIndex={0}
422
- onKeyDown={onKeyDown}
423
- >
424
- <div className="jp-xtralab-ImageDiff-swipeDividerHandle jp-mod-top">
425
- <div className="jp-xtralab-ImageDiff-swipeDividerArrow jp-mod-left" />
426
- <div className="jp-xtralab-ImageDiff-swipeDividerArrow jp-mod-right" />
427
- </div>
428
- <div className="jp-xtralab-ImageDiff-swipeDividerHandle jp-mod-bottom">
429
- <div className="jp-xtralab-ImageDiff-swipeDividerArrow jp-mod-left" />
430
- <div className="jp-xtralab-ImageDiff-swipeDividerArrow jp-mod-right" />
521
+ <div
522
+ className="jp-xtralab-ImageDiff-swipeDivider"
523
+ style={{ left: `${position}%` }}
524
+ role="slider"
525
+ aria-label={trans.__('Swipe between reference and challenger')}
526
+ aria-valuemin={0}
527
+ aria-valuemax={100}
528
+ aria-valuenow={Math.round(position)}
529
+ aria-orientation="vertical"
530
+ tabIndex={0}
531
+ onKeyDown={onKeyDown}
532
+ >
533
+ <div className="jp-xtralab-ImageDiff-swipeDividerHandle jp-mod-top">
534
+ <div className="jp-xtralab-ImageDiff-swipeDividerArrow jp-mod-left" />
535
+ <div className="jp-xtralab-ImageDiff-swipeDividerArrow jp-mod-right" />
536
+ </div>
537
+ <div className="jp-xtralab-ImageDiff-swipeDividerHandle jp-mod-bottom">
538
+ <div className="jp-xtralab-ImageDiff-swipeDividerArrow jp-mod-left" />
539
+ <div className="jp-xtralab-ImageDiff-swipeDividerArrow jp-mod-right" />
540
+ </div>
431
541
  </div>
432
542
  </div>
433
543
  </div>
@@ -437,7 +547,8 @@ function Swipe({ reference, challenger }: ISideViewProps): React.ReactElement {
437
547
 
438
548
  function OnionSkin({
439
549
  reference,
440
- challenger
550
+ challenger,
551
+ trans
441
552
  }: ISideViewProps): React.ReactElement {
442
553
  const [opacity, setOpacity] = React.useState(50);
443
554
  return (
@@ -447,14 +558,14 @@ function OnionSkin({
447
558
  <img
448
559
  className="jp-xtralab-ImageDiff-stackImg"
449
560
  src={reference.uri}
450
- alt="Reference revision"
561
+ alt={trans.__('Reference revision')}
451
562
  />
452
563
  ) : null}
453
564
  {challenger.uri !== null ? (
454
565
  <img
455
566
  className="jp-xtralab-ImageDiff-stackImg"
456
567
  src={challenger.uri}
457
- alt="Challenger revision"
568
+ alt={trans.__('Challenger revision')}
458
569
  style={{ opacity: opacity / 100 }}
459
570
  />
460
571
  ) : null}
@@ -462,7 +573,7 @@ function OnionSkin({
462
573
  <RangeSlider
463
574
  value={opacity}
464
575
  onChange={setOpacity}
465
- ariaLabel="Fade between reference and challenger"
576
+ ariaLabel={trans.__('Fade between reference and challenger')}
466
577
  />
467
578
  </div>
468
579
  );
package/src/git/index.ts CHANGED
@@ -4,6 +4,9 @@ import {
4
4
  } from '@jupyterlab/application';
5
5
  import { IThemeManager, WidgetTracker } from '@jupyterlab/apputils';
6
6
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
7
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
8
+
9
+ import { IAskAgent } from '../askAgent/tokens';
7
10
 
8
11
  import {
9
12
  CommandArguments,
@@ -30,12 +33,15 @@ const diffCommandPlugin: JupyterFrontEndPlugin<void> = {
30
33
  description:
31
34
  "The launcher dashboard's side-by-side git diff command, powered by @pierre/diffs.",
32
35
  autoStart: true,
33
- optional: [IThemeManager, IRenderMimeRegistry],
36
+ optional: [IThemeManager, IRenderMimeRegistry, ITranslator, IAskAgent],
34
37
  activate: (
35
38
  app: JupyterFrontEnd,
36
39
  themeManager: IThemeManager | null,
37
- rendermime: IRenderMimeRegistry | null
40
+ rendermime: IRenderMimeRegistry | null,
41
+ translator: ITranslator | null,
42
+ askAgent: IAskAgent | null
38
43
  ): void => {
44
+ const trans = (translator ?? nullTranslator).load('jupyterlab');
39
45
  // Track open diffs so repeated opens reveal the existing tab.
40
46
  const tracker = new WidgetTracker<DiffMainAreaWidget>({
41
47
  namespace: GIT_DIFF_TRACKER_NAMESPACE
@@ -57,6 +63,8 @@ const diffCommandPlugin: JupyterFrontEndPlugin<void> = {
57
63
  themeManager,
58
64
  contentsManager: app.serviceManager.contents,
59
65
  rendermime,
66
+ askAgent,
67
+ trans,
60
68
  trackDiff: widget => tracker.add(widget),
61
69
  onPinned: current => {
62
70
  const existing = findDiff(current.change, true);