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
@@ -1,18 +1,13 @@
1
1
  import * as React from 'react';
2
2
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
3
+ import type { TranslationBundle } from '@jupyterlab/translation';
3
4
  import { type FileDiffMetadata } from '@pierre/diffs';
4
- /**
5
- * The CSS class added to the root of the notebook diff. Selectors that
6
- * style cell sections, headers, and the per-cell diff blocks hang off this
7
- * class.
8
- */
9
- export declare const NOTEBOOK_DIFF_CSS_CLASS = "jp-xtralab-NotebookDiff";
10
5
  /**
11
6
  * Minimal slice of nbformat 4.x that the diff inspects. Fields we don't
12
7
  * read (attachments, e.g.) are intentionally not narrowed — we still pass
13
8
  * them around as part of the cell, but we never project them into the diff.
14
9
  */
15
- export interface INotebookCell {
10
+ interface INotebookCell {
16
11
  cell_type: string;
17
12
  id?: string;
18
13
  source: string | string[];
@@ -21,7 +16,7 @@ export interface INotebookCell {
21
16
  execution_count?: number | null;
22
17
  attachments?: Record<string, unknown>;
23
18
  }
24
- export interface INotebookOutput {
19
+ interface INotebookOutput {
25
20
  output_type: string;
26
21
  data?: Record<string, unknown>;
27
22
  text?: string | string[];
@@ -32,7 +27,7 @@ export interface INotebookOutput {
32
27
  execution_count?: number | null;
33
28
  metadata?: Record<string, unknown>;
34
29
  }
35
- export interface INotebook {
30
+ interface INotebook {
36
31
  cells: INotebookCell[];
37
32
  metadata: Record<string, unknown>;
38
33
  nbformat: number;
@@ -45,7 +40,7 @@ export interface INotebook {
45
40
  * carry the same payload but live on separate union members so `Extract`
46
41
  * narrowing works on `kind` literals downstream.
47
42
  */
48
- export type NotebookCellDiff = {
43
+ type NotebookCellDiff = {
49
44
  kind: 'modified';
50
45
  oldCell: INotebookCell;
51
46
  newCell: INotebookCell;
@@ -87,63 +82,7 @@ export interface INotebookDiffResult {
87
82
  */
88
83
  notebookMetadataDiff: FileDiffMetadata | null;
89
84
  }
90
- /**
91
- * Parse notebook JSON. Returns `null` if the text doesn't parse or doesn't
92
- * look like a notebook (missing `cells` array). The caller falls back to a
93
- * raw text diff so a malformed file is still inspectable.
94
- */
95
- export declare function parseNotebook(text: string): INotebook | null;
96
- /**
97
- * nbformat allows multiline string fields (`source`, stream `text`,
98
- * `text/plain` outputs, …) to be either a single string or an array of
99
- * strings. Normalize to one string so equality checks and diffs see the
100
- * same shape regardless of how the writer chose to serialize it.
101
- */
102
- export declare function joinMultiline(value: string | string[] | undefined): string;
103
- export declare function cellSource(cell: INotebookCell): string;
104
- /**
105
- * Canonical text representation of a cell's outputs, used both for equality
106
- * checks and for the per-cell output diff. Stream and `text/plain` data
107
- * round-trip verbatim; richer mime types (image/png, text/html, …) collapse
108
- * to a `<mime-type>` placeholder so diffing image bytes doesn't drown the
109
- * panel in base64 noise.
110
- */
111
- export declare function canonicalOutputs(cell: INotebookCell): string;
112
- /**
113
- * Stable JSON of the cell's `metadata` object. Keys are sorted recursively
114
- * so a writer that re-orders the metadata object doesn't surface as a diff.
115
- * An empty metadata object returns the empty string so freshly-added cells
116
- * that carry the default `{}` don't produce a `+{}` placeholder section.
117
- */
118
- export declare function canonicalMetadata(cell: INotebookCell): string;
119
- /**
120
- * Stable JSON of the notebook-level metadata bundle (kernelspec, language
121
- * info, nbformat). We diff the bundle as a single block so the user sees
122
- * all environment-level changes in one place.
123
- */
124
- export declare function canonicalNotebookMetadata(notebook: INotebook): string;
125
- /**
126
- * Equality check used to classify matched cells as `unchanged` vs
127
- * `modified`. Compares the three things we actually render — type, source,
128
- * outputs, metadata — using the same canonical representations the diff
129
- * itself sees, so an "equal" verdict here implies all per-cell diff blocks
130
- * would be empty.
131
- */
132
- export declare function cellsAreEqual(a: INotebookCell, b: INotebookCell): boolean;
133
- /**
134
- * Align cells between two notebook revisions. Cells that carry a stable id
135
- * (nbformat ≥ 4.5) match on their id; remaining cells fall back to
136
- * positional matching against the next un-consumed unidentified old cell so
137
- * older notebooks still produce a sensible diff.
138
- *
139
- * Output ordering: entries follow new-file order, with cells that exist
140
- * only in the old revision appended at the end as `removed`. We don't try
141
- * to interleave removals at their original position — for typical notebook
142
- * edits this is more readable than guessing alignment between unrelated
143
- * insertions and deletions.
144
- */
145
- export declare function alignNotebookCells(oldCells: INotebookCell[], newCells: INotebookCell[]): NotebookCellDiff[];
146
- export interface IBuildNotebookDiffOptions {
85
+ interface IBuildNotebookDiffOptions {
147
86
  oldText: string;
148
87
  newText: string;
149
88
  }
@@ -166,6 +105,10 @@ interface INotebookDiffViewProps {
166
105
  */
167
106
  pierreTheme: boolean;
168
107
  rendermime: IRenderMimeRegistry | null;
108
+ /**
109
+ * Translation bundle for user-facing strings.
110
+ */
111
+ trans: TranslationBundle;
169
112
  }
170
113
  /**
171
114
  * The notebook-level diff view. Lays out cells in a 2-column grid where
@@ -11,13 +11,13 @@ import { resolveDiffTheme } from './diffTheme';
11
11
  * style cell sections, headers, and the per-cell diff blocks hang off this
12
12
  * class.
13
13
  */
14
- export const NOTEBOOK_DIFF_CSS_CLASS = 'jp-xtralab-NotebookDiff';
14
+ const NOTEBOOK_DIFF_CSS_CLASS = 'jp-xtralab-NotebookDiff';
15
15
  /**
16
16
  * Parse notebook JSON. Returns `null` if the text doesn't parse or doesn't
17
17
  * look like a notebook (missing `cells` array). The caller falls back to a
18
18
  * raw text diff so a malformed file is still inspectable.
19
19
  */
20
- export function parseNotebook(text) {
20
+ function parseNotebook(text) {
21
21
  if (text.length === 0) {
22
22
  return emptyNotebook();
23
23
  }
@@ -53,13 +53,13 @@ function emptyNotebook() {
53
53
  * strings. Normalize to one string so equality checks and diffs see the
54
54
  * same shape regardless of how the writer chose to serialize it.
55
55
  */
56
- export function joinMultiline(value) {
56
+ function joinMultiline(value) {
57
57
  if (value === undefined) {
58
58
  return '';
59
59
  }
60
60
  return Array.isArray(value) ? value.join('') : value;
61
61
  }
62
- export function cellSource(cell) {
62
+ function cellSource(cell) {
63
63
  return joinMultiline(cell.source);
64
64
  }
65
65
  /**
@@ -69,7 +69,7 @@ export function cellSource(cell) {
69
69
  * to a `<mime-type>` placeholder so diffing image bytes doesn't drown the
70
70
  * panel in base64 noise.
71
71
  */
72
- export function canonicalOutputs(cell) {
72
+ function canonicalOutputs(cell) {
73
73
  var _a;
74
74
  const outputs = (_a = cell.outputs) !== null && _a !== void 0 ? _a : [];
75
75
  if (outputs.length === 0) {
@@ -132,7 +132,7 @@ function stripAnsi(value) {
132
132
  * An empty metadata object returns the empty string so freshly-added cells
133
133
  * that carry the default `{}` don't produce a `+{}` placeholder section.
134
134
  */
135
- export function canonicalMetadata(cell) {
135
+ function canonicalMetadata(cell) {
136
136
  var _a;
137
137
  const md = (_a = cell.metadata) !== null && _a !== void 0 ? _a : {};
138
138
  if (Object.keys(md).length === 0) {
@@ -145,7 +145,7 @@ export function canonicalMetadata(cell) {
145
145
  * info, nbformat). We diff the bundle as a single block so the user sees
146
146
  * all environment-level changes in one place.
147
147
  */
148
- export function canonicalNotebookMetadata(notebook) {
148
+ function canonicalNotebookMetadata(notebook) {
149
149
  return stableStringify({
150
150
  metadata: notebook.metadata,
151
151
  nbformat: notebook.nbformat,
@@ -155,11 +155,11 @@ export function canonicalNotebookMetadata(notebook) {
155
155
  function stableStringify(value) {
156
156
  return JSON.stringify(value, sortKeysReplacer, 2);
157
157
  }
158
- function sortKeysReplacer(_key, value) {
158
+ function sortKeysReplacer(key, value) {
159
159
  if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
160
160
  const sorted = {};
161
- for (const key of Object.keys(value).sort()) {
162
- sorted[key] = value[key];
161
+ for (const name of Object.keys(value).sort()) {
162
+ sorted[name] = value[name];
163
163
  }
164
164
  return sorted;
165
165
  }
@@ -172,7 +172,7 @@ function sortKeysReplacer(_key, value) {
172
172
  * itself sees, so an "equal" verdict here implies all per-cell diff blocks
173
173
  * would be empty.
174
174
  */
175
- export function cellsAreEqual(a, b) {
175
+ function cellsAreEqual(a, b) {
176
176
  return (a.cell_type === b.cell_type &&
177
177
  cellSource(a) === cellSource(b) &&
178
178
  canonicalOutputs(a) === canonicalOutputs(b) &&
@@ -190,7 +190,7 @@ export function cellsAreEqual(a, b) {
190
190
  * edits this is more readable than guessing alignment between unrelated
191
191
  * insertions and deletions.
192
192
  */
193
- export function alignNotebookCells(oldCells, newCells) {
193
+ function alignNotebookCells(oldCells, newCells) {
194
194
  const oldById = new Map();
195
195
  oldCells.forEach((cell, index) => {
196
196
  if (cell.id !== undefined && cell.id.length > 0) {
@@ -544,7 +544,7 @@ function SideBySidePanes(props) {
544
544
  */
545
545
  function OutputsSection(props) {
546
546
  var _a, _b;
547
- const { entry, rendermime, placement } = props;
547
+ const { entry, rendermime, placement, trans } = props;
548
548
  const oldCell = 'oldCell' in entry ? entry.oldCell : null;
549
549
  const newCell = 'newCell' in entry ? entry.newCell : null;
550
550
  const oldOutputs = (_a = oldCell === null || oldCell === void 0 ? void 0 : oldCell.outputs) !== null && _a !== void 0 ? _a : [];
@@ -562,9 +562,9 @@ function OutputsSection(props) {
562
562
  const showOld = placement !== 'right' && hasOld;
563
563
  const showNew = placement !== 'left' && hasNew;
564
564
  if (rendermime === null) {
565
- return (React.createElement(SideBySidePanes, { label: "Outputs", hasOld: showOld, hasNew: showNew, oldNode: React.createElement("pre", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-fallbackText` }, oldCell !== null ? canonicalOutputs(oldCell) : ''), newNode: React.createElement("pre", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-fallbackText` }, newCell !== null ? canonicalOutputs(newCell) : '') }));
565
+ return (React.createElement(SideBySidePanes, { label: trans.__('Outputs'), hasOld: showOld, hasNew: showNew, oldNode: React.createElement("pre", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-fallbackText` }, oldCell !== null ? canonicalOutputs(oldCell) : ''), newNode: React.createElement("pre", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-fallbackText` }, newCell !== null ? canonicalOutputs(newCell) : '') }));
566
566
  }
567
- return (React.createElement(SideBySidePanes, { label: "Outputs", hasOld: showOld, hasNew: showNew, oldNode: React.createElement(OutputsPreview, { outputs: oldOutputs, rendermime: rendermime }), newNode: React.createElement(OutputsPreview, { outputs: newOutputs, rendermime: rendermime }) }));
567
+ return (React.createElement(SideBySidePanes, { label: trans.__('Outputs'), hasOld: showOld, hasNew: showNew, oldNode: React.createElement(OutputsPreview, { outputs: oldOutputs, rendermime: rendermime }), newNode: React.createElement(OutputsPreview, { outputs: newOutputs, rendermime: rendermime }) }));
568
568
  }
569
569
  /**
570
570
  * Rendered markdown preview section. Sits next to (and below) the markdown
@@ -572,7 +572,7 @@ function OutputsSection(props) {
572
572
  * rendered cell looks. Skipped for non-markdown cells.
573
573
  */
574
574
  function MarkdownPreviewSection(props) {
575
- const { entry, rendermime, placement } = props;
575
+ const { entry, rendermime, placement, trans } = props;
576
576
  const isMarkdown = referenceCellType(entry) === 'markdown';
577
577
  if (!isMarkdown || rendermime === null) {
578
578
  return null;
@@ -584,7 +584,7 @@ function MarkdownPreviewSection(props) {
584
584
  if (!showOld && !showNew) {
585
585
  return null;
586
586
  }
587
- return (React.createElement(SideBySidePanes, { label: "Rendered", hasOld: showOld, hasNew: showNew, oldNode: React.createElement(MarkdownPreview, { source: oldSource, rendermime: rendermime }), newNode: React.createElement(MarkdownPreview, { source: newSource, rendermime: rendermime }) }));
587
+ return (React.createElement(SideBySidePanes, { label: trans.__('Rendered'), hasOld: showOld, hasNew: showNew, oldNode: React.createElement(MarkdownPreview, { source: oldSource, rendermime: rendermime }), newNode: React.createElement(MarkdownPreview, { source: newSource, rendermime: rendermime }) }));
588
588
  }
589
589
  /**
590
590
  * The notebook-level diff view. Lays out cells in a 2-column grid where
@@ -597,13 +597,13 @@ function MarkdownPreviewSection(props) {
597
597
  * the orientation clear without a separate column header.
598
598
  */
599
599
  export function NotebookDiffView(props) {
600
- const { diff, dark, pierreTheme, rendermime } = props;
600
+ const { diff, dark, pierreTheme, rendermime, trans } = props;
601
601
  const theme = resolveDiffTheme(dark, pierreTheme);
602
602
  const summary = React.useMemo(() => summarize(diff.cells), [diff.cells]);
603
603
  return (React.createElement("div", { className: NOTEBOOK_DIFF_CSS_CLASS },
604
- React.createElement(NotebookDiffHeader, { summary: summary }),
605
- React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-grid` }, diff.cells.map(entry => (React.createElement(CellEntryRow, { key: cellEntryKey(entry), entry: entry, language: diff.language, theme: theme, dark: dark, rendermime: rendermime })))),
606
- diff.notebookMetadataDiff !== null ? (React.createElement(NotebookMetadataBlock, { metadata: diff.notebookMetadataDiff, theme: theme, dark: dark })) : null));
604
+ React.createElement(NotebookDiffHeader, { summary: summary, trans: trans }),
605
+ React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-grid` }, diff.cells.map(entry => (React.createElement(CellEntryRow, { key: cellEntryKey(entry), entry: entry, language: diff.language, theme: theme, dark: dark, rendermime: rendermime, trans: trans })))),
606
+ diff.notebookMetadataDiff !== null ? (React.createElement(NotebookMetadataBlock, { metadata: diff.notebookMetadataDiff, theme: theme, dark: dark, trans: trans })) : null));
607
607
  }
608
608
  /**
609
609
  * Place a single cell entry into the outer 2-column grid. Modified /
@@ -614,24 +614,28 @@ export function NotebookDiffView(props) {
614
614
  * cell was inserted or removed relative to the other side.
615
615
  */
616
616
  function CellEntryRow(props) {
617
- const { entry } = props;
617
+ const { entry, trans } = props;
618
618
  if (entry.kind === 'added') {
619
619
  return (React.createElement(React.Fragment, null,
620
- React.createElement(EmptyCellPlaceholder, { side: "old", reason: "added" }),
620
+ React.createElement(EmptyCellPlaceholder, { side: "old", reason: "added", trans: trans }),
621
621
  React.createElement(CellDiffBlock, { ...props, placement: "right" })));
622
622
  }
623
623
  if (entry.kind === 'removed') {
624
624
  return (React.createElement(React.Fragment, null,
625
625
  React.createElement(CellDiffBlock, { ...props, placement: "left" }),
626
- React.createElement(EmptyCellPlaceholder, { side: "new", reason: "removed" })));
626
+ React.createElement(EmptyCellPlaceholder, { side: "new", reason: "removed", trans: trans })));
627
627
  }
628
628
  return React.createElement(CellDiffBlock, { ...props, placement: "full" });
629
629
  }
630
630
  function EmptyCellPlaceholder(props) {
631
- const { side, reason } = props;
632
- const label = reason === 'added' ? 'cell added on the right' : 'cell removed';
631
+ const { side, reason, trans } = props;
632
+ const label = reason === 'added'
633
+ ? trans.__('cell added on the right')
634
+ : trans.__('cell removed');
633
635
  return (React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-empty`, "data-side": side, "aria-label": label },
634
- React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-emptyLabel` }, reason === 'added' ? '— no cell —' : '— cell removed —')));
636
+ React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-emptyLabel` }, reason === 'added'
637
+ ? trans.__('— no cell —')
638
+ : trans.__('— cell removed —'))));
635
639
  }
636
640
  function summarize(cells) {
637
641
  const summary = {
@@ -646,25 +650,14 @@ function summarize(cells) {
646
650
  return summary;
647
651
  }
648
652
  function NotebookDiffHeader(props) {
649
- const { summary } = props;
653
+ const { summary, trans } = props;
650
654
  const total = summary.added + summary.removed + summary.modified + summary.unchanged;
651
655
  return (React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-header` },
652
- React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-headerCount` },
653
- total,
654
- " cell",
655
- total === 1 ? '' : 's'),
656
- summary.modified > 0 ? (React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-stat`, "data-kind": "modified" },
657
- summary.modified,
658
- " modified")) : null,
659
- summary.added > 0 ? (React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-stat`, "data-kind": "added" },
660
- summary.added,
661
- " added")) : null,
662
- summary.removed > 0 ? (React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-stat`, "data-kind": "removed" },
663
- summary.removed,
664
- " removed")) : null,
665
- summary.unchanged > 0 ? (React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-stat`, "data-kind": "unchanged" },
666
- summary.unchanged,
667
- " unchanged")) : null));
656
+ React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-headerCount` }, trans._n('%1 cell', '%1 cells', total)),
657
+ summary.modified > 0 ? (React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-stat`, "data-kind": "modified" }, trans.__('%1 modified', summary.modified))) : null,
658
+ summary.added > 0 ? (React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-stat`, "data-kind": "added" }, trans.__('%1 added', summary.added))) : null,
659
+ summary.removed > 0 ? (React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-stat`, "data-kind": "removed" }, trans.__('%1 removed', summary.removed))) : null,
660
+ summary.unchanged > 0 ? (React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-stat`, "data-kind": "unchanged" }, trans.__('%1 unchanged', summary.unchanged))) : null));
668
661
  }
669
662
  function cellEntryKey(entry) {
670
663
  var _a, _b, _c, _d;
@@ -678,7 +671,7 @@ function cellEntryKey(entry) {
678
671
  }
679
672
  function CellDiffBlock(props) {
680
673
  var _a, _b;
681
- const { entry, language, theme, dark, rendermime, placement } = props;
674
+ const { entry, language, theme, dark, rendermime, placement, trans } = props;
682
675
  // Unchanged cells start collapsed — the user opted out of seeing those
683
676
  // sections by virtue of them being unchanged. A toggle lets them peek if
684
677
  // they want.
@@ -698,23 +691,23 @@ function CellDiffBlock(props) {
698
691
  }
699
692
  } },
700
693
  React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellChevron` }, collapsed ? '▸' : '▾'),
701
- React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellBadge`, "data-kind": entry.kind }, kindBadge(entry.kind)),
694
+ React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellBadge`, "data-kind": entry.kind }, kindBadge(entry.kind, trans)),
702
695
  React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellLabel` },
703
696
  indexLabel,
704
697
  " \u00B7 ",
705
698
  cellType),
706
- entry.kind === 'unchanged' ? (React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellHint` }, "unchanged")) : null),
707
- !collapsed ? (React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellBody` }, entry.kind === 'unchanged' ? (React.createElement(UnchangedCellBody, { entry: entry, rendermime: rendermime })) : (React.createElement(React.Fragment, null,
708
- subdiffs.map(sub => (React.createElement(CellSubDiff, { key: sub.kind, kind: sub.kind, metadata: sub.metadata, theme: theme, dark: dark, placement: placement }))),
709
- React.createElement(MarkdownPreviewSection, { entry: entry, rendermime: rendermime, placement: placement }),
710
- showOutputs ? (React.createElement(OutputsSection, { entry: entry, rendermime: rendermime, placement: placement })) : null,
699
+ entry.kind === 'unchanged' ? (React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellHint` }, trans.__('unchanged'))) : null),
700
+ !collapsed ? (React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellBody` }, entry.kind === 'unchanged' ? (React.createElement(UnchangedCellBody, { entry: entry, rendermime: rendermime, trans: trans })) : (React.createElement(React.Fragment, null,
701
+ subdiffs.map(sub => (React.createElement(CellSubDiff, { key: sub.kind, kind: sub.kind, metadata: sub.metadata, theme: theme, dark: dark, placement: placement, trans: trans }))),
702
+ React.createElement(MarkdownPreviewSection, { entry: entry, rendermime: rendermime, placement: placement, trans: trans }),
703
+ showOutputs ? (React.createElement(OutputsSection, { entry: entry, rendermime: rendermime, placement: placement, trans: trans })) : null,
711
704
  subdiffs.length === 0 &&
712
705
  !(showOutputs &&
713
706
  (('oldCell' in entry && ((_a = entry.oldCell.outputs) === null || _a === void 0 ? void 0 : _a.length)) ||
714
- ('newCell' in entry && ((_b = entry.newCell.outputs) === null || _b === void 0 ? void 0 : _b.length)))) ? (React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellEmpty` }, "No content differences detected.")) : null)))) : null));
707
+ ('newCell' in entry && ((_b = entry.newCell.outputs) === null || _b === void 0 ? void 0 : _b.length)))) ? (React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellEmpty` }, trans.__('No content differences detected.'))) : null)))) : null));
715
708
  }
716
709
  function CellSubDiff(props) {
717
- const { kind, metadata, theme, dark, placement } = props;
710
+ const { kind, metadata, theme, dark, placement, trans } = props;
718
711
  // For modified / unchanged cells the diff spans both outer columns, and
719
712
  // split mode lets it visually align with the outer old | new lanes. For
720
713
  // added / removed cells the diff sits inside a single outer column, so
@@ -722,7 +715,7 @@ function CellSubDiff(props) {
722
715
  // column empty.
723
716
  const diffStyle = placement === 'full' ? 'split' : 'unified';
724
717
  return (React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-subdiff`, "data-section": kind },
725
- React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-subdiffLabel` }, subdiffLabel(kind)),
718
+ React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-subdiffLabel` }, subdiffLabel(kind, trans)),
726
719
  React.createElement(FileDiff, { fileDiff: metadata,
727
720
  // See diffWidget.tsx — the worker bootstrap can't resolve through
728
721
  // JupyterLab's federation pipeline, so every diff in this extension
@@ -731,14 +724,14 @@ function CellSubDiff(props) {
731
724
  disableWorkerPool: true, options: { ...diffLibraryOptions(theme, dark), diffStyle } })));
732
725
  }
733
726
  function NotebookMetadataBlock(props) {
734
- const { metadata, theme, dark } = props;
727
+ const { metadata, theme, dark, trans } = props;
735
728
  return (React.createElement("section", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cell`, "data-kind": "modified", "data-cell-type": "metadata" },
736
- React.createElement("header", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellHeader`, "aria-label": "Notebook metadata" },
729
+ React.createElement("header", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellHeader`, "aria-label": trans.__('Notebook metadata') },
737
730
  React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellChevron` }, "\u25BE"),
738
- React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellBadge`, "data-kind": "modified" }, "M"),
739
- React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellLabel` }, "Notebook metadata")),
731
+ React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellBadge`, "data-kind": "modified" }, trans.__('M')),
732
+ React.createElement("span", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellLabel` }, trans.__('Notebook metadata'))),
740
733
  React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-cellBody` },
741
- React.createElement(CellSubDiff, { kind: "metadata", metadata: metadata, theme: theme, dark: dark, placement: "full" }))));
734
+ React.createElement(CellSubDiff, { kind: "metadata", metadata: metadata, theme: theme, dark: dark, placement: "full", trans: trans }))));
742
735
  }
743
736
  /**
744
737
  * Body for an unchanged cell when the user has expanded it. The cell's
@@ -750,7 +743,7 @@ function NotebookMetadataBlock(props) {
750
743
  */
751
744
  function UnchangedCellBody(props) {
752
745
  var _a;
753
- const { entry, rendermime } = props;
746
+ const { entry, rendermime, trans } = props;
754
747
  const cell = entry.newCell;
755
748
  const source = cellSource(cell);
756
749
  const isMarkdown = cell.cell_type === 'markdown';
@@ -758,9 +751,9 @@ function UnchangedCellBody(props) {
758
751
  const hasOutputs = outputs.length > 0;
759
752
  return (React.createElement(React.Fragment, null,
760
753
  isMarkdown && rendermime !== null && source.length > 0 ? (React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-unchangedRendered` },
761
- React.createElement(MarkdownPreview, { source: source, rendermime: rendermime }))) : (React.createElement("pre", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-unchangedSource` }, source.length > 0 ? source : '(empty cell)')),
754
+ React.createElement(MarkdownPreview, { source: source, rendermime: rendermime }))) : (React.createElement("pre", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-unchangedSource` }, source.length > 0 ? source : trans.__('(empty cell)'))),
762
755
  hasOutputs && rendermime !== null ? (React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-unchangedOutputs` },
763
- React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-subdiffLabel` }, "Outputs"),
756
+ React.createElement("div", { className: `${NOTEBOOK_DIFF_CSS_CLASS}-subdiffLabel` }, trans.__('Outputs')),
764
757
  React.createElement(OutputsPreview, { outputs: outputs, rendermime: rendermime }))) : null));
765
758
  }
766
759
  function referenceCellType(entry) {
@@ -784,23 +777,23 @@ function cellIndexLabel(entry) {
784
777
  }
785
778
  return `Cell ${entry.oldIndex + 1} → ${entry.newIndex + 1}`;
786
779
  }
787
- function kindBadge(kind) {
780
+ function kindBadge(kind, trans) {
788
781
  switch (kind) {
789
782
  case 'added':
790
- return 'A';
783
+ return trans.__('A');
791
784
  case 'removed':
792
- return 'D';
785
+ return trans.__('D');
793
786
  case 'modified':
794
- return 'M';
787
+ return trans.__('M');
795
788
  case 'unchanged':
796
789
  return '·';
797
790
  }
798
791
  }
799
- function subdiffLabel(kind) {
792
+ function subdiffLabel(kind, trans) {
800
793
  switch (kind) {
801
794
  case 'source':
802
- return 'Source';
795
+ return trans.__('Source');
803
796
  case 'metadata':
804
- return 'Metadata';
797
+ return trans.__('Metadata');
805
798
  }
806
799
  }
@@ -17,7 +17,9 @@ export interface IGitStatusFile {
17
17
  from: string;
18
18
  is_binary: boolean | null;
19
19
  }
20
- /** Response shape of `POST /git/<path>/status`. */
20
+ /**
21
+ * Response shape of `POST /git/<path>/status`.
22
+ */
21
23
  export interface IGitStatusResult {
22
24
  code: number;
23
25
  branch: string | null;
@@ -58,7 +60,7 @@ export interface IGitContentResult {
58
60
  * changes on top of an already-staged version. The panel models that as two
59
61
  * separate entries, one per group.
60
62
  */
61
- export type FileChangeGroup = 'staged' | 'unstaged';
63
+ type FileChangeGroup = 'staged' | 'unstaged';
62
64
  /**
63
65
  * The user-facing status of a file change. Drives the single-letter badge
64
66
  * (M/A/D/R/U/?) shown next to each entry in the panel.
@@ -76,3 +78,4 @@ export interface IFileChange {
76
78
  status: FileChangeStatus;
77
79
  isBinary: boolean | null;
78
80
  }
81
+ export {};
@@ -1,6 +1,7 @@
1
1
  import { ICommandPalette } from '@jupyterlab/apputils';
2
2
  import { CodeMirrorEditor } from '@jupyterlab/codemirror';
3
3
  import { IDocumentManager } from '@jupyterlab/docmanager';
4
+ import { FileEditor } from '@jupyterlab/fileeditor';
4
5
  import { ITranslator, nullTranslator } from '@jupyterlab/translation';
5
6
  import { StateEffect, StateField } from '@codemirror/state';
6
7
  import { Decoration, EditorView } from '@codemirror/view';
@@ -12,7 +13,9 @@ const PLUGIN_ID = 'xtralab:highlight';
12
13
  */
13
14
  const HIGHLIGHT_LINES_COMMAND = 'xtralab:highlight-lines';
14
15
  const CLEAR_HIGHLIGHTS_COMMAND = 'xtralab:clear-highlights';
15
- /** CodeMirror line-decoration class; styled in style/highlight.css. */
16
+ /**
17
+ * CodeMirror line-decoration class; styled in style/highlight.css.
18
+ */
16
19
  const HIGHLIGHT_LINE_CLASS = 'jp-xtralab-highlightLine';
17
20
  /**
18
21
  * A CodeMirror effect carrying the 1-indexed, inclusive line range to
@@ -52,15 +55,16 @@ const highlightField = StateField.define({
52
55
  provide: field => EditorView.decorations.from(field)
53
56
  });
54
57
  /**
55
- * Return the CodeMirror view backing a widget's editor, or `null` when the
56
- * widget is not a CodeMirror-based text editor (a notebook, a terminal, the
57
- * settings editor, …). Duck-typed on `content.editor` so the plugin does not
58
- * depend on `@jupyterlab/fileeditor`.
58
+ * Return the CodeMirror view backing a widget's file editor, or `null` when
59
+ * the widget is not a CodeMirror-based text editor (a notebook, a terminal,
60
+ * the settings editor, …).
59
61
  */
60
62
  function viewForWidget(widget) {
61
- var _a;
62
- const editor = (_a = widget === null || widget === void 0 ? void 0 : widget.content) === null || _a === void 0 ? void 0 : _a.editor;
63
- return editor instanceof CodeMirrorEditor ? editor.editor : null;
63
+ const content = widget === null || widget === void 0 ? void 0 : widget.content;
64
+ return content instanceof FileEditor &&
65
+ content.editor instanceof CodeMirrorEditor
66
+ ? content.editor.editor
67
+ : null;
64
68
  }
65
69
  /**
66
70
  * Coerce a JSON command argument to a positive integer line number, falling
package/lib/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import agentSessionsPlugin from './agentSessions';
2
+ import askAgentPlugin from './askAgent';
2
3
  import commandBarPlugin from './commandBar';
3
4
  import fileBrowserPlugin from './fileBrowser';
4
5
  import fileTypeIconsPlugin from './fileTypeIcons';
@@ -27,6 +28,7 @@ import walkthroughPlugin from './walkthrough';
27
28
  */
28
29
  const plugins = [
29
30
  agentSessionsPlugin,
31
+ askAgentPlugin,
30
32
  commandBarPlugin,
31
33
  editorBreadcrumbsPlugin,
32
34
  editorIndentPlugin,
@@ -6,10 +6,9 @@ import { BUILTIN_AGENT_ICONS } from './icons';
6
6
  * in `jupyter-ai-contrib/jupyter-ai-acp-client` (which is also where their
7
7
  * icons come from), but use the bare CLI names a user would type in a
8
8
  * terminal rather than the ACP wrapper binaries that project spawns.
9
- * Antigravity (Google's agent-first IDE/CLI, `agy`) takes the Google slot —
10
- * Gemini CLI was dropped because Google is sunsetting it for consumer
11
- * accounts on 2026-06-18. Anything not on the user's `$PATH` is filtered
12
- * out at activation time, so the wider list is harmless.
9
+ * Antigravity (Google's agent-first IDE/CLI, `agy`) is the Google agent.
10
+ * Anything not on the user's `$PATH` is filtered out at activation time, so
11
+ * the wider list is harmless.
13
12
  */
14
13
  const DEFAULTS = [
15
14
  {
@@ -40,9 +40,13 @@ export declare function registerAgentCommands(app: JupyterFrontEnd, agents: IAge
40
40
  */
41
41
  export declare function launchInTerminal(commands: CommandRegistry, options: {
42
42
  cwd?: string;
43
- /** The literal command line typed into the fresh terminal. */
43
+ /**
44
+ * The literal command line typed into the fresh terminal.
45
+ */
44
46
  invocation: string;
45
- /** Tab/title label applied once the command is sent. */
47
+ /**
48
+ * Tab/title label applied once the command is sent.
49
+ */
46
50
  label: string;
47
51
  onSession?: (sessionName: string) => void;
48
52
  }): Promise<MainAreaWidget<ITerminal.ITerminal>>;
@@ -1,5 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import type { CommandRegistry } from '@lumino/commands';
3
+ import type { TranslationBundle } from '@jupyterlab/translation';
3
4
  import { ReactWidget } from '@jupyterlab/ui-components';
4
5
  import type { Widget } from '@lumino/widgets';
5
6
  import type { IAgentSessions } from '../agentSessions';
@@ -12,7 +13,7 @@ import type { IEditor } from './editors';
12
13
  * tab for the freshly created terminal). Keeping it as an opaque
13
14
  * callback means the widget itself does not import `app.shell`.
14
15
  */
15
- export interface ILauncherDashboardOptions {
16
+ interface ILauncherDashboardOptions {
16
17
  commands: CommandRegistry;
17
18
  /**
18
19
  * The agent list to render. Already filtered by availability + sorted by
@@ -48,6 +49,11 @@ export interface ILauncherDashboardOptions {
48
49
  * Empty string lets the terminal extension default to the server root.
49
50
  */
50
51
  cwd: string;
52
+ /**
53
+ * The translation bundle used to localize the dashboard's user-facing
54
+ * strings.
55
+ */
56
+ trans: TranslationBundle;
51
57
  }
52
58
  /**
53
59
  * Lumino host for the React-based launcher dashboard. Mirrors the
@@ -59,3 +65,4 @@ export declare class LauncherDashboard extends ReactWidget {
59
65
  protected render(): React.ReactElement;
60
66
  private _options;
61
67
  }
68
+ export {};