xtralab 0.11.1 → 0.12.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 (159) hide show
  1. package/README.md +38 -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
@@ -3,6 +3,7 @@ import * as React from 'react';
3
3
  import type * as nbformat from '@jupyterlab/nbformat';
4
4
  import { OutputAreaModel, SimplifiedOutputArea } from '@jupyterlab/outputarea';
5
5
  import { IRenderMimeRegistry, MimeModel } from '@jupyterlab/rendermime';
6
+ import type { TranslationBundle } from '@jupyterlab/translation';
6
7
  import { MessageLoop } from '@lumino/messaging';
7
8
  import { Widget } from '@lumino/widgets';
8
9
  import { FileDiff } from '@pierre/diffs/react';
@@ -20,14 +21,14 @@ import { resolveDiffTheme } from './diffTheme';
20
21
  * style cell sections, headers, and the per-cell diff blocks hang off this
21
22
  * class.
22
23
  */
23
- export const NOTEBOOK_DIFF_CSS_CLASS = 'jp-xtralab-NotebookDiff';
24
+ const NOTEBOOK_DIFF_CSS_CLASS = 'jp-xtralab-NotebookDiff';
24
25
 
25
26
  /**
26
27
  * Minimal slice of nbformat 4.x that the diff inspects. Fields we don't
27
28
  * read (attachments, e.g.) are intentionally not narrowed — we still pass
28
29
  * them around as part of the cell, but we never project them into the diff.
29
30
  */
30
- export interface INotebookCell {
31
+ interface INotebookCell {
31
32
  cell_type: string;
32
33
  id?: string;
33
34
  source: string | string[];
@@ -37,7 +38,7 @@ export interface INotebookCell {
37
38
  attachments?: Record<string, unknown>;
38
39
  }
39
40
 
40
- export interface INotebookOutput {
41
+ interface INotebookOutput {
41
42
  output_type: string;
42
43
  data?: Record<string, unknown>;
43
44
  text?: string | string[];
@@ -49,7 +50,7 @@ export interface INotebookOutput {
49
50
  metadata?: Record<string, unknown>;
50
51
  }
51
52
 
52
- export interface INotebook {
53
+ interface INotebook {
53
54
  cells: INotebookCell[];
54
55
  metadata: Record<string, unknown>;
55
56
  nbformat: number;
@@ -63,7 +64,7 @@ export interface INotebook {
63
64
  * carry the same payload but live on separate union members so `Extract`
64
65
  * narrowing works on `kind` literals downstream.
65
66
  */
66
- export type NotebookCellDiff =
67
+ type NotebookCellDiff =
67
68
  | {
68
69
  kind: 'modified';
69
70
  oldCell: INotebookCell;
@@ -116,7 +117,7 @@ export interface INotebookDiffResult {
116
117
  * look like a notebook (missing `cells` array). The caller falls back to a
117
118
  * raw text diff so a malformed file is still inspectable.
118
119
  */
119
- export function parseNotebook(text: string): INotebook | null {
120
+ function parseNotebook(text: string): INotebook | null {
120
121
  if (text.length === 0) {
121
122
  return emptyNotebook();
122
123
  }
@@ -155,14 +156,14 @@ function emptyNotebook(): INotebook {
155
156
  * strings. Normalize to one string so equality checks and diffs see the
156
157
  * same shape regardless of how the writer chose to serialize it.
157
158
  */
158
- export function joinMultiline(value: string | string[] | undefined): string {
159
+ function joinMultiline(value: string | string[] | undefined): string {
159
160
  if (value === undefined) {
160
161
  return '';
161
162
  }
162
163
  return Array.isArray(value) ? value.join('') : value;
163
164
  }
164
165
 
165
- export function cellSource(cell: INotebookCell): string {
166
+ function cellSource(cell: INotebookCell): string {
166
167
  return joinMultiline(cell.source);
167
168
  }
168
169
 
@@ -173,7 +174,7 @@ export function cellSource(cell: INotebookCell): string {
173
174
  * to a `<mime-type>` placeholder so diffing image bytes doesn't drown the
174
175
  * panel in base64 noise.
175
176
  */
176
- export function canonicalOutputs(cell: INotebookCell): string {
177
+ function canonicalOutputs(cell: INotebookCell): string {
177
178
  const outputs = cell.outputs ?? [];
178
179
  if (outputs.length === 0) {
179
180
  return '';
@@ -241,7 +242,7 @@ function stripAnsi(value: string): string {
241
242
  * An empty metadata object returns the empty string so freshly-added cells
242
243
  * that carry the default `{}` don't produce a `+{}` placeholder section.
243
244
  */
244
- export function canonicalMetadata(cell: INotebookCell): string {
245
+ function canonicalMetadata(cell: INotebookCell): string {
245
246
  const md = cell.metadata ?? {};
246
247
  if (Object.keys(md).length === 0) {
247
248
  return '';
@@ -254,7 +255,7 @@ export function canonicalMetadata(cell: INotebookCell): string {
254
255
  * info, nbformat). We diff the bundle as a single block so the user sees
255
256
  * all environment-level changes in one place.
256
257
  */
257
- export function canonicalNotebookMetadata(notebook: INotebook): string {
258
+ function canonicalNotebookMetadata(notebook: INotebook): string {
258
259
  return stableStringify({
259
260
  metadata: notebook.metadata,
260
261
  nbformat: notebook.nbformat,
@@ -266,11 +267,11 @@ function stableStringify(value: unknown): string {
266
267
  return JSON.stringify(value, sortKeysReplacer, 2);
267
268
  }
268
269
 
269
- function sortKeysReplacer(_key: string, value: unknown): unknown {
270
+ function sortKeysReplacer(key: string, value: unknown): unknown {
270
271
  if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
271
272
  const sorted: Record<string, unknown> = {};
272
- for (const key of Object.keys(value).sort()) {
273
- sorted[key] = (value as Record<string, unknown>)[key];
273
+ for (const name of Object.keys(value).sort()) {
274
+ sorted[name] = (value as Record<string, unknown>)[name];
274
275
  }
275
276
  return sorted;
276
277
  }
@@ -284,7 +285,7 @@ function sortKeysReplacer(_key: string, value: unknown): unknown {
284
285
  * itself sees, so an "equal" verdict here implies all per-cell diff blocks
285
286
  * would be empty.
286
287
  */
287
- export function cellsAreEqual(a: INotebookCell, b: INotebookCell): boolean {
288
+ function cellsAreEqual(a: INotebookCell, b: INotebookCell): boolean {
288
289
  return (
289
290
  a.cell_type === b.cell_type &&
290
291
  cellSource(a) === cellSource(b) &&
@@ -305,7 +306,7 @@ export function cellsAreEqual(a: INotebookCell, b: INotebookCell): boolean {
305
306
  * edits this is more readable than guessing alignment between unrelated
306
307
  * insertions and deletions.
307
308
  */
308
- export function alignNotebookCells(
309
+ function alignNotebookCells(
309
310
  oldCells: INotebookCell[],
310
311
  newCells: INotebookCell[]
311
312
  ): NotebookCellDiff[] {
@@ -373,7 +374,7 @@ export function alignNotebookCells(
373
374
  return entries;
374
375
  }
375
376
 
376
- export interface IBuildNotebookDiffOptions {
377
+ interface IBuildNotebookDiffOptions {
377
378
  oldText: string;
378
379
  newText: string;
379
380
  }
@@ -754,8 +755,9 @@ function OutputsSection(props: {
754
755
  entry: NotebookCellDiff;
755
756
  rendermime: IRenderMimeRegistry | null;
756
757
  placement: CellPlacement;
758
+ trans: TranslationBundle;
757
759
  }): React.ReactElement | null {
758
- const { entry, rendermime, placement } = props;
760
+ const { entry, rendermime, placement, trans } = props;
759
761
  const oldCell = 'oldCell' in entry ? entry.oldCell : null;
760
762
  const newCell = 'newCell' in entry ? entry.newCell : null;
761
763
  const oldOutputs = oldCell?.outputs ?? [];
@@ -775,7 +777,7 @@ function OutputsSection(props: {
775
777
  if (rendermime === null) {
776
778
  return (
777
779
  <SideBySidePanes
778
- label="Outputs"
780
+ label={trans.__('Outputs')}
779
781
  hasOld={showOld}
780
782
  hasNew={showNew}
781
783
  oldNode={
@@ -793,7 +795,7 @@ function OutputsSection(props: {
793
795
  }
794
796
  return (
795
797
  <SideBySidePanes
796
- label="Outputs"
798
+ label={trans.__('Outputs')}
797
799
  hasOld={showOld}
798
800
  hasNew={showNew}
799
801
  oldNode={<OutputsPreview outputs={oldOutputs} rendermime={rendermime} />}
@@ -811,8 +813,9 @@ function MarkdownPreviewSection(props: {
811
813
  entry: NotebookCellDiff;
812
814
  rendermime: IRenderMimeRegistry | null;
813
815
  placement: CellPlacement;
816
+ trans: TranslationBundle;
814
817
  }): React.ReactElement | null {
815
- const { entry, rendermime, placement } = props;
818
+ const { entry, rendermime, placement, trans } = props;
816
819
  const isMarkdown = referenceCellType(entry) === 'markdown';
817
820
  if (!isMarkdown || rendermime === null) {
818
821
  return null;
@@ -826,7 +829,7 @@ function MarkdownPreviewSection(props: {
826
829
  }
827
830
  return (
828
831
  <SideBySidePanes
829
- label="Rendered"
832
+ label={trans.__('Rendered')}
830
833
  hasOld={showOld}
831
834
  hasNew={showNew}
832
835
  oldNode={<MarkdownPreview source={oldSource} rendermime={rendermime} />}
@@ -844,6 +847,10 @@ interface INotebookDiffViewProps {
844
847
  */
845
848
  pierreTheme: boolean;
846
849
  rendermime: IRenderMimeRegistry | null;
850
+ /**
851
+ * Translation bundle for user-facing strings.
852
+ */
853
+ trans: TranslationBundle;
847
854
  }
848
855
 
849
856
  /**
@@ -859,12 +866,12 @@ interface INotebookDiffViewProps {
859
866
  export function NotebookDiffView(
860
867
  props: INotebookDiffViewProps
861
868
  ): React.ReactElement {
862
- const { diff, dark, pierreTheme, rendermime } = props;
869
+ const { diff, dark, pierreTheme, rendermime, trans } = props;
863
870
  const theme: DiffsThemeNames = resolveDiffTheme(dark, pierreTheme);
864
871
  const summary = React.useMemo(() => summarize(diff.cells), [diff.cells]);
865
872
  return (
866
873
  <div className={NOTEBOOK_DIFF_CSS_CLASS}>
867
- <NotebookDiffHeader summary={summary} />
874
+ <NotebookDiffHeader summary={summary} trans={trans} />
868
875
  <div className={`${NOTEBOOK_DIFF_CSS_CLASS}-grid`}>
869
876
  {diff.cells.map(entry => (
870
877
  <CellEntryRow
@@ -874,6 +881,7 @@ export function NotebookDiffView(
874
881
  theme={theme}
875
882
  dark={dark}
876
883
  rendermime={rendermime}
884
+ trans={trans}
877
885
  />
878
886
  ))}
879
887
  </div>
@@ -882,6 +890,7 @@ export function NotebookDiffView(
882
890
  metadata={diff.notebookMetadataDiff}
883
891
  theme={theme}
884
892
  dark={dark}
893
+ trans={trans}
885
894
  />
886
895
  ) : null}
887
896
  </div>
@@ -897,11 +906,11 @@ export function NotebookDiffView(
897
906
  * cell was inserted or removed relative to the other side.
898
907
  */
899
908
  function CellEntryRow(props: ICellDiffBlockProps): React.ReactElement {
900
- const { entry } = props;
909
+ const { entry, trans } = props;
901
910
  if (entry.kind === 'added') {
902
911
  return (
903
912
  <>
904
- <EmptyCellPlaceholder side="old" reason="added" />
913
+ <EmptyCellPlaceholder side="old" reason="added" trans={trans} />
905
914
  <CellDiffBlock {...props} placement="right" />
906
915
  </>
907
916
  );
@@ -910,7 +919,7 @@ function CellEntryRow(props: ICellDiffBlockProps): React.ReactElement {
910
919
  return (
911
920
  <>
912
921
  <CellDiffBlock {...props} placement="left" />
913
- <EmptyCellPlaceholder side="new" reason="removed" />
922
+ <EmptyCellPlaceholder side="new" reason="removed" trans={trans} />
914
923
  </>
915
924
  );
916
925
  }
@@ -920,9 +929,13 @@ function CellEntryRow(props: ICellDiffBlockProps): React.ReactElement {
920
929
  function EmptyCellPlaceholder(props: {
921
930
  side: 'old' | 'new';
922
931
  reason: 'added' | 'removed';
932
+ trans: TranslationBundle;
923
933
  }): React.ReactElement {
924
- const { side, reason } = props;
925
- const label = reason === 'added' ? 'cell added on the right' : 'cell removed';
934
+ const { side, reason, trans } = props;
935
+ const label =
936
+ reason === 'added'
937
+ ? trans.__('cell added on the right')
938
+ : trans.__('cell removed');
926
939
  return (
927
940
  <div
928
941
  className={`${NOTEBOOK_DIFF_CSS_CLASS}-empty`}
@@ -930,7 +943,9 @@ function EmptyCellPlaceholder(props: {
930
943
  aria-label={label}
931
944
  >
932
945
  <span className={`${NOTEBOOK_DIFF_CSS_CLASS}-emptyLabel`}>
933
- {reason === 'added' ? '— no cell —' : '— cell removed —'}
946
+ {reason === 'added'
947
+ ? trans.__('— no cell —')
948
+ : trans.__('— cell removed —')}
934
949
  </span>
935
950
  </div>
936
951
  );
@@ -958,31 +973,32 @@ function summarize(cells: NotebookCellDiff[]): INotebookDiffSummary {
958
973
 
959
974
  function NotebookDiffHeader(props: {
960
975
  summary: INotebookDiffSummary;
976
+ trans: TranslationBundle;
961
977
  }): React.ReactElement {
962
- const { summary } = props;
978
+ const { summary, trans } = props;
963
979
  const total =
964
980
  summary.added + summary.removed + summary.modified + summary.unchanged;
965
981
  return (
966
982
  <div className={`${NOTEBOOK_DIFF_CSS_CLASS}-header`}>
967
983
  <span className={`${NOTEBOOK_DIFF_CSS_CLASS}-headerCount`}>
968
- {total} cell{total === 1 ? '' : 's'}
984
+ {trans._n('%1 cell', '%1 cells', total)}
969
985
  </span>
970
986
  {summary.modified > 0 ? (
971
987
  <span
972
988
  className={`${NOTEBOOK_DIFF_CSS_CLASS}-stat`}
973
989
  data-kind="modified"
974
990
  >
975
- {summary.modified} modified
991
+ {trans.__('%1 modified', summary.modified)}
976
992
  </span>
977
993
  ) : null}
978
994
  {summary.added > 0 ? (
979
995
  <span className={`${NOTEBOOK_DIFF_CSS_CLASS}-stat`} data-kind="added">
980
- {summary.added} added
996
+ {trans.__('%1 added', summary.added)}
981
997
  </span>
982
998
  ) : null}
983
999
  {summary.removed > 0 ? (
984
1000
  <span className={`${NOTEBOOK_DIFF_CSS_CLASS}-stat`} data-kind="removed">
985
- {summary.removed} removed
1001
+ {trans.__('%1 removed', summary.removed)}
986
1002
  </span>
987
1003
  ) : null}
988
1004
  {summary.unchanged > 0 ? (
@@ -990,7 +1006,7 @@ function NotebookDiffHeader(props: {
990
1006
  className={`${NOTEBOOK_DIFF_CSS_CLASS}-stat`}
991
1007
  data-kind="unchanged"
992
1008
  >
993
- {summary.unchanged} unchanged
1009
+ {trans.__('%1 unchanged', summary.unchanged)}
994
1010
  </span>
995
1011
  ) : null}
996
1012
  </div>
@@ -1013,6 +1029,7 @@ interface ICellDiffBlockProps {
1013
1029
  theme: DiffsThemeNames;
1014
1030
  dark: boolean;
1015
1031
  rendermime: IRenderMimeRegistry | null;
1032
+ trans: TranslationBundle;
1016
1033
  }
1017
1034
 
1018
1035
  /**
@@ -1034,7 +1051,7 @@ interface IPlacedCellDiffBlockProps extends ICellDiffBlockProps {
1034
1051
  }
1035
1052
 
1036
1053
  function CellDiffBlock(props: IPlacedCellDiffBlockProps): React.ReactElement {
1037
- const { entry, language, theme, dark, rendermime, placement } = props;
1054
+ const { entry, language, theme, dark, rendermime, placement, trans } = props;
1038
1055
  // Unchanged cells start collapsed — the user opted out of seeing those
1039
1056
  // sections by virtue of them being unchanged. A toggle lets them peek if
1040
1057
  // they want.
@@ -1081,21 +1098,25 @@ function CellDiffBlock(props: IPlacedCellDiffBlockProps): React.ReactElement {
1081
1098
  className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellBadge`}
1082
1099
  data-kind={entry.kind}
1083
1100
  >
1084
- {kindBadge(entry.kind)}
1101
+ {kindBadge(entry.kind, trans)}
1085
1102
  </span>
1086
1103
  <span className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellLabel`}>
1087
1104
  {indexLabel} · {cellType}
1088
1105
  </span>
1089
1106
  {entry.kind === 'unchanged' ? (
1090
1107
  <span className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellHint`}>
1091
- unchanged
1108
+ {trans.__('unchanged')}
1092
1109
  </span>
1093
1110
  ) : null}
1094
1111
  </header>
1095
1112
  {!collapsed ? (
1096
1113
  <div className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellBody`}>
1097
1114
  {entry.kind === 'unchanged' ? (
1098
- <UnchangedCellBody entry={entry} rendermime={rendermime} />
1115
+ <UnchangedCellBody
1116
+ entry={entry}
1117
+ rendermime={rendermime}
1118
+ trans={trans}
1119
+ />
1099
1120
  ) : (
1100
1121
  <>
1101
1122
  {subdiffs.map(sub => (
@@ -1106,18 +1127,21 @@ function CellDiffBlock(props: IPlacedCellDiffBlockProps): React.ReactElement {
1106
1127
  theme={theme}
1107
1128
  dark={dark}
1108
1129
  placement={placement}
1130
+ trans={trans}
1109
1131
  />
1110
1132
  ))}
1111
1133
  <MarkdownPreviewSection
1112
1134
  entry={entry}
1113
1135
  rendermime={rendermime}
1114
1136
  placement={placement}
1137
+ trans={trans}
1115
1138
  />
1116
1139
  {showOutputs ? (
1117
1140
  <OutputsSection
1118
1141
  entry={entry}
1119
1142
  rendermime={rendermime}
1120
1143
  placement={placement}
1144
+ trans={trans}
1121
1145
  />
1122
1146
  ) : null}
1123
1147
  {subdiffs.length === 0 &&
@@ -1127,7 +1151,7 @@ function CellDiffBlock(props: IPlacedCellDiffBlockProps): React.ReactElement {
1127
1151
  ('newCell' in entry && entry.newCell.outputs?.length))
1128
1152
  ) ? (
1129
1153
  <div className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellEmpty`}>
1130
- No content differences detected.
1154
+ {trans.__('No content differences detected.')}
1131
1155
  </div>
1132
1156
  ) : null}
1133
1157
  </>
@@ -1144,8 +1168,9 @@ function CellSubDiff(props: {
1144
1168
  theme: DiffsThemeNames;
1145
1169
  dark: boolean;
1146
1170
  placement: CellPlacement;
1171
+ trans: TranslationBundle;
1147
1172
  }): React.ReactElement {
1148
- const { kind, metadata, theme, dark, placement } = props;
1173
+ const { kind, metadata, theme, dark, placement, trans } = props;
1149
1174
  // For modified / unchanged cells the diff spans both outer columns, and
1150
1175
  // split mode lets it visually align with the outer old | new lanes. For
1151
1176
  // added / removed cells the diff sits inside a single outer column, so
@@ -1156,7 +1181,7 @@ function CellSubDiff(props: {
1156
1181
  return (
1157
1182
  <div className={`${NOTEBOOK_DIFF_CSS_CLASS}-subdiff`} data-section={kind}>
1158
1183
  <div className={`${NOTEBOOK_DIFF_CSS_CLASS}-subdiffLabel`}>
1159
- {subdiffLabel(kind)}
1184
+ {subdiffLabel(kind, trans)}
1160
1185
  </div>
1161
1186
  <FileDiff
1162
1187
  fileDiff={metadata}
@@ -1175,8 +1200,9 @@ function NotebookMetadataBlock(props: {
1175
1200
  metadata: FileDiffMetadata;
1176
1201
  theme: DiffsThemeNames;
1177
1202
  dark: boolean;
1203
+ trans: TranslationBundle;
1178
1204
  }): React.ReactElement {
1179
- const { metadata, theme, dark } = props;
1205
+ const { metadata, theme, dark, trans } = props;
1180
1206
  return (
1181
1207
  <section
1182
1208
  className={`${NOTEBOOK_DIFF_CSS_CLASS}-cell`}
@@ -1185,17 +1211,17 @@ function NotebookMetadataBlock(props: {
1185
1211
  >
1186
1212
  <header
1187
1213
  className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellHeader`}
1188
- aria-label="Notebook metadata"
1214
+ aria-label={trans.__('Notebook metadata')}
1189
1215
  >
1190
1216
  <span className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellChevron`}>▾</span>
1191
1217
  <span
1192
1218
  className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellBadge`}
1193
1219
  data-kind="modified"
1194
1220
  >
1195
- M
1221
+ {trans.__('M')}
1196
1222
  </span>
1197
1223
  <span className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellLabel`}>
1198
- Notebook metadata
1224
+ {trans.__('Notebook metadata')}
1199
1225
  </span>
1200
1226
  </header>
1201
1227
  <div className={`${NOTEBOOK_DIFF_CSS_CLASS}-cellBody`}>
@@ -1205,6 +1231,7 @@ function NotebookMetadataBlock(props: {
1205
1231
  theme={theme}
1206
1232
  dark={dark}
1207
1233
  placement="full"
1234
+ trans={trans}
1208
1235
  />
1209
1236
  </div>
1210
1237
  </section>
@@ -1222,8 +1249,9 @@ function NotebookMetadataBlock(props: {
1222
1249
  function UnchangedCellBody(props: {
1223
1250
  entry: Extract<NotebookCellDiff, { kind: 'unchanged' }>;
1224
1251
  rendermime: IRenderMimeRegistry | null;
1252
+ trans: TranslationBundle;
1225
1253
  }): React.ReactElement {
1226
- const { entry, rendermime } = props;
1254
+ const { entry, rendermime, trans } = props;
1227
1255
  const cell = entry.newCell;
1228
1256
  const source = cellSource(cell);
1229
1257
  const isMarkdown = cell.cell_type === 'markdown';
@@ -1237,13 +1265,13 @@ function UnchangedCellBody(props: {
1237
1265
  </div>
1238
1266
  ) : (
1239
1267
  <pre className={`${NOTEBOOK_DIFF_CSS_CLASS}-unchangedSource`}>
1240
- {source.length > 0 ? source : '(empty cell)'}
1268
+ {source.length > 0 ? source : trans.__('(empty cell)')}
1241
1269
  </pre>
1242
1270
  )}
1243
1271
  {hasOutputs && rendermime !== null ? (
1244
1272
  <div className={`${NOTEBOOK_DIFF_CSS_CLASS}-unchangedOutputs`}>
1245
1273
  <div className={`${NOTEBOOK_DIFF_CSS_CLASS}-subdiffLabel`}>
1246
- Outputs
1274
+ {trans.__('Outputs')}
1247
1275
  </div>
1248
1276
  <OutputsPreview outputs={outputs} rendermime={rendermime} />
1249
1277
  </div>
@@ -1275,24 +1303,30 @@ function cellIndexLabel(entry: NotebookCellDiff): string {
1275
1303
  return `Cell ${entry.oldIndex + 1} → ${entry.newIndex + 1}`;
1276
1304
  }
1277
1305
 
1278
- function kindBadge(kind: NotebookCellDiff['kind']): string {
1306
+ function kindBadge(
1307
+ kind: NotebookCellDiff['kind'],
1308
+ trans: TranslationBundle
1309
+ ): string {
1279
1310
  switch (kind) {
1280
1311
  case 'added':
1281
- return 'A';
1312
+ return trans.__('A');
1282
1313
  case 'removed':
1283
- return 'D';
1314
+ return trans.__('D');
1284
1315
  case 'modified':
1285
- return 'M';
1316
+ return trans.__('M');
1286
1317
  case 'unchanged':
1287
1318
  return '·';
1288
1319
  }
1289
1320
  }
1290
1321
 
1291
- function subdiffLabel(kind: 'source' | 'metadata'): string {
1322
+ function subdiffLabel(
1323
+ kind: 'source' | 'metadata',
1324
+ trans: TranslationBundle
1325
+ ): string {
1292
1326
  switch (kind) {
1293
1327
  case 'source':
1294
- return 'Source';
1328
+ return trans.__('Source');
1295
1329
  case 'metadata':
1296
- return 'Metadata';
1330
+ return trans.__('Metadata');
1297
1331
  }
1298
1332
  }
package/src/git/tokens.ts CHANGED
@@ -19,7 +19,9 @@ export interface IGitStatusFile {
19
19
  is_binary: boolean | null;
20
20
  }
21
21
 
22
- /** Response shape of `POST /git/<path>/status`. */
22
+ /**
23
+ * Response shape of `POST /git/<path>/status`.
24
+ */
23
25
  export interface IGitStatusResult {
24
26
  code: number;
25
27
  branch: string | null;
@@ -61,7 +63,7 @@ export interface IGitContentResult {
61
63
  * changes on top of an already-staged version. The panel models that as two
62
64
  * separate entries, one per group.
63
65
  */
64
- export type FileChangeGroup = 'staged' | 'unstaged';
66
+ type FileChangeGroup = 'staged' | 'unstaged';
65
67
 
66
68
  /**
67
69
  * The user-facing status of a file change. Drives the single-letter badge
@@ -5,6 +5,7 @@ import {
5
5
  import { ICommandPalette } from '@jupyterlab/apputils';
6
6
  import { CodeMirrorEditor } from '@jupyterlab/codemirror';
7
7
  import { IDocumentManager } from '@jupyterlab/docmanager';
8
+ import { FileEditor } from '@jupyterlab/fileeditor';
8
9
  import { ITranslator, nullTranslator } from '@jupyterlab/translation';
9
10
  import { StateEffect, StateField } from '@codemirror/state';
10
11
  import { Decoration, DecorationSet, EditorView } from '@codemirror/view';
@@ -20,7 +21,9 @@ const PLUGIN_ID = 'xtralab:highlight';
20
21
  const HIGHLIGHT_LINES_COMMAND = 'xtralab:highlight-lines';
21
22
  const CLEAR_HIGHLIGHTS_COMMAND = 'xtralab:clear-highlights';
22
23
 
23
- /** CodeMirror line-decoration class; styled in style/highlight.css. */
24
+ /**
25
+ * CodeMirror line-decoration class; styled in style/highlight.css.
26
+ */
24
27
  const HIGHLIGHT_LINE_CLASS = 'jp-xtralab-highlightLine';
25
28
 
26
29
  /**
@@ -66,15 +69,16 @@ const highlightField = StateField.define<DecorationSet>({
66
69
  });
67
70
 
68
71
  /**
69
- * Return the CodeMirror view backing a widget's editor, or `null` when the
70
- * widget is not a CodeMirror-based text editor (a notebook, a terminal, the
71
- * settings editor, …). Duck-typed on `content.editor` so the plugin does not
72
- * depend on `@jupyterlab/fileeditor`.
72
+ * Return the CodeMirror view backing a widget's file editor, or `null` when
73
+ * the widget is not a CodeMirror-based text editor (a notebook, a terminal,
74
+ * the settings editor, …).
73
75
  */
74
76
  function viewForWidget(widget: Widget | null): EditorView | null {
75
- const editor = (widget as { content?: { editor?: unknown } } | null)?.content
76
- ?.editor;
77
- return editor instanceof CodeMirrorEditor ? editor.editor : null;
77
+ const content = (widget as { content?: unknown } | null)?.content;
78
+ return content instanceof FileEditor &&
79
+ content.editor instanceof CodeMirrorEditor
80
+ ? content.editor.editor
81
+ : null;
78
82
  }
79
83
 
80
84
  /**
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
2
 
3
3
  import agentSessionsPlugin from './agentSessions';
4
+ import askAgentPlugin from './askAgent';
4
5
  import commandBarPlugin from './commandBar';
5
6
  import fileBrowserPlugin from './fileBrowser';
6
7
  import fileTypeIconsPlugin from './fileTypeIcons';
@@ -30,6 +31,7 @@ import walkthroughPlugin from './walkthrough';
30
31
  */
31
32
  const plugins: JupyterFrontEndPlugin<unknown>[] = [
32
33
  agentSessionsPlugin,
34
+ askAgentPlugin,
33
35
  commandBarPlugin,
34
36
  editorBreadcrumbsPlugin,
35
37
  editorIndentPlugin,
@@ -85,10 +85,9 @@ export interface IAgentSettings {
85
85
  * in `jupyter-ai-contrib/jupyter-ai-acp-client` (which is also where their
86
86
  * icons come from), but use the bare CLI names a user would type in a
87
87
  * terminal rather than the ACP wrapper binaries that project spawns.
88
- * Antigravity (Google's agent-first IDE/CLI, `agy`) takes the Google slot —
89
- * Gemini CLI was dropped because Google is sunsetting it for consumer
90
- * accounts on 2026-06-18. Anything not on the user's `$PATH` is filtered
91
- * out at activation time, so the wider list is harmless.
88
+ * Antigravity (Google's agent-first IDE/CLI, `agy`) is the Google agent.
89
+ * Anything not on the user's `$PATH` is filtered out at activation time, so
90
+ * the wider list is harmless.
92
91
  */
93
92
  const DEFAULTS: IAgent[] = [
94
93
  {
@@ -78,9 +78,13 @@ export async function launchInTerminal(
78
78
  commands: CommandRegistry,
79
79
  options: {
80
80
  cwd?: string;
81
- /** The literal command line typed into the fresh terminal. */
81
+ /**
82
+ * The literal command line typed into the fresh terminal.
83
+ */
82
84
  invocation: string;
83
- /** Tab/title label applied once the command is sent. */
85
+ /**
86
+ * Tab/title label applied once the command is sent.
87
+ */
84
88
  label: string;
85
89
  onSession?: (sessionName: string) => void;
86
90
  }