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
@@ -2,7 +2,7 @@ import * as React from 'react';
2
2
  import { IDocumentManager } from '@jupyterlab/docmanager';
3
3
  import { Contents } from '@jupyterlab/services';
4
4
  import type { XtralabFileBrowser } from './widget';
5
- export interface IFileBrowserProps {
5
+ interface IFileBrowserProps {
6
6
  contentsManager: Contents.IManager;
7
7
  docManager: IDocumentManager;
8
8
  onOpenFile?: (serverPath: string) => void;
@@ -22,3 +22,4 @@ export interface IFileBrowserProps {
22
22
  * state map.
23
23
  */
24
24
  export declare function FileBrowserComponent(props: IFileBrowserProps): React.ReactElement;
25
+ export {};
@@ -4,14 +4,7 @@ import { Toolbar } from '@jupyterlab/ui-components';
4
4
  import { ISignal } from '@lumino/signaling';
5
5
  import { Widget } from '@lumino/widgets';
6
6
  export declare const FILE_BROWSER_ID = "xtralab:file-browser";
7
- /**
8
- * The CSS class added to the xtralab file browser widget. The selectors that
9
- * bind application context menu items hang off this class, so it must remain
10
- * specific enough not to clash with other browsers (such as the default
11
- * `jp-FileBrowser`).
12
- */
13
- export declare const FILE_BROWSER_CSS_CLASS = "jp-xtralab-FileBrowser";
14
- export interface IXtralabFileBrowserOptions {
7
+ interface IXtralabFileBrowserOptions {
15
8
  contentsManager: Contents.IManager;
16
9
  docManager: IDocumentManager;
17
10
  onOpenFile?: (serverPath: string) => void;
@@ -78,7 +71,9 @@ export interface IXtralabFileBrowser {
78
71
  * directories, calling `.collapse()` on each expanded one.
79
72
  */
80
73
  readonly collapseAllRequested: ISignal<IXtralabFileBrowser, void>;
81
- /** Trigger a refresh of every loaded directory in the tree. */
74
+ /**
75
+ * Trigger a refresh of every loaded directory in the tree.
76
+ */
82
77
  refresh(): void;
83
78
  /**
84
79
  * Notify the React component that a new path was created and should
@@ -143,3 +138,4 @@ export declare class XtralabFileBrowser extends Widget implements IXtralabFileBr
143
138
  private _toolbar;
144
139
  private _content;
145
140
  }
141
+ export {};
@@ -11,7 +11,7 @@ export const FILE_BROWSER_ID = 'xtralab:file-browser';
11
11
  * specific enough not to clash with other browsers (such as the default
12
12
  * `jp-FileBrowser`).
13
13
  */
14
- export const FILE_BROWSER_CSS_CLASS = 'jp-xtralab-FileBrowser';
14
+ const FILE_BROWSER_CSS_CLASS = 'jp-xtralab-FileBrowser';
15
15
  const TOOLBAR_CSS_CLASS = 'jp-xtralab-FileBrowser-toolbar';
16
16
  const CONTENT_CSS_CLASS = 'jp-xtralab-FileBrowser-content';
17
17
  /**
@@ -120,8 +120,12 @@ const plugin = {
120
120
  }
121
121
  };
122
122
  setIcon();
123
- // Keep the tab icon right after a rename.
123
+ // Keep the tab icon right after a rename, and release the listener
124
+ // when the document closes.
124
125
  widget.context.pathChanged.connect(setIcon);
126
+ widget.disposed.connect(() => {
127
+ widget.context.pathChanged.disconnect(setIcon);
128
+ });
125
129
  });
126
130
  }
127
131
  if (gitExtension !== null) {
@@ -134,7 +138,6 @@ const plugin = {
134
138
  if (icon === null) {
135
139
  continue;
136
140
  }
137
- // Cross jupyterlab-git's nested `LabIcon` package boundary.
138
141
  file.type = { ...file.type, icon };
139
142
  }
140
143
  });
package/lib/git/api.d.ts CHANGED
@@ -1,11 +1,4 @@
1
1
  import { GitReference, IFileChange, IGitContentResult, IGitStatusFile, IGitStatusResult } from './tokens';
2
- /**
3
- * Fetch the absolute filesystem path of the git top-level for the given
4
- * server-relative path. Returns `null` when the path is not in a git
5
- * repository — the server returns a non-zero `code` (or `path: null`) in
6
- * that case, which is expected and not a fatal error.
7
- */
8
- export declare function showTopLevel(repoPath: string): Promise<string | null>;
9
2
  /**
10
3
  * Fetch `git status --porcelain` for the repository the server resolves at
11
4
  * `repoPath`. When `repoPath` is the empty string, git resolves the repo
@@ -18,16 +11,6 @@ export declare function status(repoPath: string): Promise<IGitStatusResult>;
18
11
  * of {@link status}'s response).
19
12
  */
20
13
  export declare function content(repoPath: string, filename: string, reference: GitReference): Promise<IGitContentResult>;
21
- /** Stage a single file. */
22
- export declare function add(repoPath: string, filename: string): Promise<void>;
23
- /** Stage every changed file (including untracked). */
24
- export declare function addAll(repoPath: string): Promise<void>;
25
- /** Unstage a single file. */
26
- export declare function reset(repoPath: string, filename: string): Promise<void>;
27
- /** Unstage every file. */
28
- export declare function resetAll(repoPath: string): Promise<void>;
29
- /** Restore a single file to the version in the index (i.e. discard worktree changes). */
30
- export declare function checkout(repoPath: string, filename: string): Promise<void>;
31
14
  /**
32
15
  * Expand the porcelain `files` array into one entry per logical change.
33
16
  * A single porcelain row may map to two `IFileChange` rows (a file that's
@@ -39,12 +22,3 @@ export declare function checkout(repoPath: string, filename: string): Promise<vo
39
22
  * don't pin our `tsconfig.lib` to ES2019.
40
23
  */
41
24
  export declare function expandStatusFiles(files: IGitStatusFile[]): IFileChange[];
42
- /**
43
- * Translate one entry in the porcelain `files` array into the panel's
44
- * preferred shape. A single porcelain entry may represent up to two changes
45
- * (one in the index, one in the worktree); we emit one {@link IFileChange}
46
- * per non-empty side, so a "modified-staged-and-then-modified-again" file
47
- * shows up twice in the panel — once under "Staged Changes" and once under
48
- * "Changes" — matching the layout the user sees in VS Code.
49
- */
50
- export declare function porcelainToFileChanges(file: IGitStatusFile): IFileChange[];
package/lib/git/api.js CHANGED
@@ -32,7 +32,7 @@ async function postWithPath(endpoint, repoPath, body) {
32
32
  try {
33
33
  data = JSON.parse(text);
34
34
  }
35
- catch (_err) {
35
+ catch (_c) {
36
36
  // Fall through into the error handling below.
37
37
  }
38
38
  }
@@ -42,25 +42,6 @@ async function postWithPath(endpoint, repoPath, body) {
42
42
  }
43
43
  return (data !== null && data !== void 0 ? data : {});
44
44
  }
45
- /**
46
- * Fetch the absolute filesystem path of the git top-level for the given
47
- * server-relative path. Returns `null` when the path is not in a git
48
- * repository — the server returns a non-zero `code` (or `path: null`) in
49
- * that case, which is expected and not a fatal error.
50
- */
51
- export async function showTopLevel(repoPath) {
52
- let result;
53
- try {
54
- result = await postWithPath('show_top_level', repoPath, {});
55
- }
56
- catch (err) {
57
- return null;
58
- }
59
- if (result.code !== 0 || !result.path) {
60
- return null;
61
- }
62
- return result.path;
63
- }
64
45
  /**
65
46
  * Fetch `git status --porcelain` for the repository the server resolves at
66
47
  * `repoPath`. When `repoPath` is the empty string, git resolves the repo
@@ -80,29 +61,6 @@ export async function content(repoPath, filename, reference) {
80
61
  reference
81
62
  });
82
63
  }
83
- /** Stage a single file. */
84
- export async function add(repoPath, filename) {
85
- await postWithPath('add', repoPath, { add_all: false, filename });
86
- }
87
- /** Stage every changed file (including untracked). */
88
- export async function addAll(repoPath) {
89
- await postWithPath('add', repoPath, { add_all: true });
90
- }
91
- /** Unstage a single file. */
92
- export async function reset(repoPath, filename) {
93
- await postWithPath('reset', repoPath, { reset_all: false, filename });
94
- }
95
- /** Unstage every file. */
96
- export async function resetAll(repoPath) {
97
- await postWithPath('reset', repoPath, { reset_all: true });
98
- }
99
- /** Restore a single file to the version in the index (i.e. discard worktree changes). */
100
- export async function checkout(repoPath, filename) {
101
- await postWithPath('checkout', repoPath, {
102
- checkout_branch: false,
103
- filename
104
- });
105
- }
106
64
  /**
107
65
  * Expand the porcelain `files` array into one entry per logical change.
108
66
  * A single porcelain row may map to two `IFileChange` rows (a file that's
@@ -130,7 +88,7 @@ export function expandStatusFiles(files) {
130
88
  * shows up twice in the panel — once under "Staged Changes" and once under
131
89
  * "Changes" — matching the layout the user sees in VS Code.
132
90
  */
133
- export function porcelainToFileChanges(file) {
91
+ function porcelainToFileChanges(file) {
134
92
  const result = [];
135
93
  // Untracked files are reported with `??` and only appear under "Changes".
136
94
  if (file.x === '?' && file.y === '?') {
@@ -0,0 +1,23 @@
1
+ import type { TranslationBundle } from '@jupyterlab/translation';
2
+ import type { SelectedLineRange } from '@pierre/diffs';
3
+ import type { IAskAgentRequest } from '../askAgent/tokens';
4
+ import type { IXtralabDiffModel } from './diffWidget';
5
+ /**
6
+ * Build the ask-agent request for a line range selected in a git diff.
7
+ *
8
+ * The range's `side` decides which file version the line numbers (and the
9
+ * embedded snippet) refer to: `deletions` reads from the reference text,
10
+ * anything else — including the sideless context rows of a unified diff,
11
+ * whose gutter shows new-side numbers — reads from the challenger text. A
12
+ * gutter drag that crosses the two split columns has endpoints in different
13
+ * line-number spaces, so it is described textually instead of as a numeric
14
+ * range.
15
+ */
16
+ export declare function buildDiffAskRequest(options: {
17
+ model: IXtralabDiffModel;
18
+ oldText: string;
19
+ newText: string;
20
+ range: SelectedLineRange;
21
+ anchor: DOMRect | null;
22
+ trans: TranslationBundle;
23
+ }): IAskAgentRequest;
@@ -0,0 +1,90 @@
1
+ import { Git } from '@jupyterlab/git';
2
+ /**
3
+ * Agent-facing name of a diff content source: the special refs get plain
4
+ * words, a git ref string is used verbatim, anything else falls back to the
5
+ * side name. English on purpose — this ends up in the agent prompt, not in
6
+ * the UI.
7
+ */
8
+ function describeSource(source, fallback) {
9
+ if (source === Git.Diff.SpecialRef.WORKING) {
10
+ return 'working tree';
11
+ }
12
+ if (source === Git.Diff.SpecialRef.INDEX) {
13
+ return 'staged';
14
+ }
15
+ if (source === Git.Diff.SpecialRef.BASE) {
16
+ return 'merge base';
17
+ }
18
+ if (typeof source === 'string' && source.length > 0) {
19
+ return source;
20
+ }
21
+ return fallback;
22
+ }
23
+ function sideName(side) {
24
+ return side === 'deletions' ? 'old side' : 'new side';
25
+ }
26
+ /**
27
+ * Extract lines `startLine`–`endLine` (1-indexed, inclusive) from `text`.
28
+ */
29
+ function sliceLines(text, startLine, endLine) {
30
+ return text
31
+ .split('\n')
32
+ .slice(startLine - 1, endLine)
33
+ .join('\n');
34
+ }
35
+ /**
36
+ * Build the ask-agent request for a line range selected in a git diff.
37
+ *
38
+ * The range's `side` decides which file version the line numbers (and the
39
+ * embedded snippet) refer to: `deletions` reads from the reference text,
40
+ * anything else — including the sideless context rows of a unified diff,
41
+ * whose gutter shows new-side numbers — reads from the challenger text. A
42
+ * gutter drag that crosses the two split columns has endpoints in different
43
+ * line-number spaces, so it is described textually instead of as a numeric
44
+ * range.
45
+ */
46
+ export function buildDiffAskRequest(options) {
47
+ var _a, _b, _c;
48
+ const { model, oldText, newText, range, anchor, trans } = options;
49
+ const repositoryPath = (_a = model.repositoryPath) !== null && _a !== void 0 ? _a : '';
50
+ const base = {
51
+ path: model.filename,
52
+ ...(repositoryPath.length > 0 ? { cwd: repositoryPath } : {})
53
+ };
54
+ const startSide = (_b = range.side) !== null && _b !== void 0 ? _b : 'additions';
55
+ const endSide = (_c = range.endSide) !== null && _c !== void 0 ? _c : startSide;
56
+ if (startSide !== endSide) {
57
+ return {
58
+ context: {
59
+ ...base,
60
+ text: '',
61
+ location: `lines ${range.start} (${sideName(startSide)}) through ` +
62
+ `${range.end} (${sideName(endSide)}) of the current git diff`,
63
+ note: trans.__('diff selection')
64
+ },
65
+ anchor
66
+ };
67
+ }
68
+ const startLine = Math.min(range.start, range.end);
69
+ const endLine = Math.max(range.start, range.end);
70
+ const isOld = startSide === 'deletions';
71
+ const source = isOld ? model.reference.source : model.challenger.source;
72
+ const ref = describeSource(source, isOld ? 'old' : 'new');
73
+ return {
74
+ context: {
75
+ ...base,
76
+ startLine,
77
+ endLine,
78
+ // Only new-side lines of a working-tree diff index the file on disk;
79
+ // old-side and historical line numbers must not be used to highlight
80
+ // the working file.
81
+ linesInWorkingFile: !isOld && model.challenger.source === Git.Diff.SpecialRef.WORKING,
82
+ text: sliceLines(isOld ? oldText : newText, startLine, endLine),
83
+ location: `the ${sideName(startSide)} (${ref}) of the current git diff`,
84
+ // New-side lines read as the file's current content, so only old-side
85
+ // ranges need a clarifying tag in the popup header.
86
+ ...(isOld ? { note: trans.__('old version') } : {})
87
+ },
88
+ anchor
89
+ };
90
+ }
@@ -2,7 +2,9 @@ import { JupyterFrontEnd } from '@jupyterlab/application';
2
2
  import { IThemeManager, MainAreaWidget } from '@jupyterlab/apputils';
3
3
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
4
4
  import { Contents } from '@jupyterlab/services';
5
+ import type { TranslationBundle } from '@jupyterlab/translation';
5
6
  import { ISignal } from '@lumino/signaling';
7
+ import type { IAskAgent } from '../askAgent/tokens';
6
8
  import { XtralabDiffWidget } from './diffWidget';
7
9
  import { IFileChange } from './tokens';
8
10
  /**
@@ -33,7 +35,7 @@ export declare function pinnedDiffWidgetId(change: IFileChange): string;
33
35
  * Launcher-owned host for a shared diff widget.
34
36
  */
35
37
  export declare class DiffMainAreaWidget extends MainAreaWidget<XtralabDiffWidget> {
36
- constructor(options: MainAreaWidget.IOptions<XtralabDiffWidget>, change: IFileChange, pinned: boolean);
38
+ constructor(options: MainAreaWidget.IOptions<XtralabDiffWidget>, change: IFileChange, pinned: boolean, trans: TranslationBundle);
37
39
  get change(): IFileChange;
38
40
  get pinned(): boolean;
39
41
  pin(): void;
@@ -41,9 +43,10 @@ export declare class DiffMainAreaWidget extends MainAreaWidget<XtralabDiffWidget
41
43
  setChange(repoPath: string, change: IFileChange): void;
42
44
  private _change;
43
45
  private _pinned;
46
+ private _trans;
44
47
  private _pinnedChanged;
45
48
  }
46
- export interface IRegisterGitCommandsOptions {
49
+ interface IRegisterGitCommandsOptions {
47
50
  app: JupyterFrontEnd;
48
51
  themeManager: IThemeManager | null;
49
52
  /**
@@ -54,6 +57,14 @@ export interface IRegisterGitCommandsOptions {
54
57
  * Rendermime registry used by notebook diffs; may be `null`.
55
58
  */
56
59
  rendermime: IRenderMimeRegistry | null;
60
+ /**
61
+ * Ask-agent popup for prompting an agent about diff lines; may be `null`.
62
+ */
63
+ askAgent: IAskAgent | null;
64
+ /**
65
+ * Translation bundle for user-facing strings.
66
+ */
67
+ trans: TranslationBundle;
57
68
  /**
58
69
  * Track newly created diff widgets before they are added to the shell.
59
70
  */
@@ -69,3 +80,4 @@ export interface IRegisterGitCommandsOptions {
69
80
  * registry.
70
81
  */
71
82
  export declare function registerGitCommands(options: IRegisterGitCommandsOptions): void;
83
+ export {};
@@ -21,21 +21,22 @@ export const PREVIEW_DIFF_WIDGET_ID = 'xtralab:diff:preview';
21
21
  export function pinnedDiffWidgetId(change) {
22
22
  return `xtralab:diff:pinned:${change.group}:${change.path}`;
23
23
  }
24
- function formatTitle(change) {
24
+ function formatTitle(change, trans) {
25
25
  var _a;
26
26
  const name = (_a = change.path.split('/').pop()) !== null && _a !== void 0 ? _a : change.path;
27
- const groupLabel = change.group === 'staged' ? 'Staged' : 'Working';
27
+ const groupLabel = change.group === 'staged' ? trans.__('Staged') : trans.__('Working');
28
28
  return `${name} (${groupLabel})`;
29
29
  }
30
30
  /**
31
31
  * Launcher-owned host for a shared diff widget.
32
32
  */
33
33
  export class DiffMainAreaWidget extends MainAreaWidget {
34
- constructor(options, change, pinned) {
34
+ constructor(options, change, pinned, trans) {
35
35
  super(options);
36
36
  this._pinnedChanged = new Signal(this);
37
37
  this._change = change;
38
38
  this._pinned = pinned;
39
+ this._trans = trans;
39
40
  }
40
41
  get change() {
41
42
  return this._change;
@@ -56,7 +57,7 @@ export class DiffMainAreaWidget extends MainAreaWidget {
56
57
  setChange(repoPath, change) {
57
58
  this._change = change;
58
59
  this.content.setModel(fileChangeToDiffModel(repoPath, change));
59
- this.title.label = formatTitle(change);
60
+ this.title.label = formatTitle(change, this._trans);
60
61
  this.title.caption = change.path;
61
62
  this.title.icon = getTreeIcon(change.path);
62
63
  }
@@ -66,12 +67,12 @@ export class DiffMainAreaWidget extends MainAreaWidget {
66
67
  */
67
68
  function createDiffWidget(options) {
68
69
  var _a;
69
- const { repoPath, change, themeManager, contentsManager, rendermime } = options;
70
+ const { repoPath, change, themeManager, contentsManager, rendermime, askAgent, trans } = options;
70
71
  const pinned = options.pinned === true;
71
- const content = new XtralabDiffWidget(fileChangeToDiffModel(repoPath, change), { contentsManager, rendermime, themeManager });
72
- const widget = new DiffMainAreaWidget({ content }, change, pinned);
72
+ const content = new XtralabDiffWidget(fileChangeToDiffModel(repoPath, change), { contentsManager, rendermime, themeManager, askAgent, trans });
73
+ const widget = new DiffMainAreaWidget({ content }, change, pinned, trans);
73
74
  widget.id = pinned ? pinnedDiffWidgetId(change) : PREVIEW_DIFF_WIDGET_ID;
74
- widget.title.label = formatTitle(change);
75
+ widget.title.label = formatTitle(change, trans);
75
76
  widget.title.caption = change.path;
76
77
  widget.title.closable = true;
77
78
  widget.title.icon = getTreeIcon(change.path);
@@ -80,7 +81,7 @@ function createDiffWidget(options) {
80
81
  const onPinned = (_a = options.onPinned) !== null && _a !== void 0 ? _a : (() => undefined);
81
82
  const pinButton = new ToolbarButton({
82
83
  icon: launchIcon,
83
- tooltip: 'Pin tab',
84
+ tooltip: trans.__('Pin tab'),
84
85
  onClick: () => widget.pin()
85
86
  });
86
87
  if (widget.pinned) {
@@ -100,7 +101,7 @@ function createDiffWidget(options) {
100
101
  widget.toolbar.hide();
101
102
  }
102
103
  };
103
- const onPinnedChanged = (_sender, value) => {
104
+ const onPinnedChanged = (sender, value) => {
104
105
  if (widget.isDisposed) {
105
106
  return;
106
107
  }
@@ -138,11 +139,11 @@ function createDiffWidget(options) {
138
139
  * registry.
139
140
  */
140
141
  export function registerGitCommands(options) {
141
- const { app, themeManager, contentsManager, rendermime, trackDiff, findDiff, onPinned } = options;
142
+ const { app, themeManager, contentsManager, rendermime, askAgent, trans, trackDiff, findDiff, onPinned } = options;
142
143
  const { commands } = app;
143
144
  commands.addCommand(CommandIDs.openDiff, {
144
- label: 'Open Git Diff',
145
- caption: 'Open a side-by-side diff for a changed file',
145
+ label: trans.__('Open Git Diff'),
146
+ caption: trans.__('Open a side-by-side diff for a changed file'),
146
147
  execute: async (args) => {
147
148
  const typed = args;
148
149
  if ((typed === null || typed === void 0 ? void 0 : typed.change) === undefined) {
@@ -163,6 +164,8 @@ export function registerGitCommands(options) {
163
164
  themeManager,
164
165
  contentsManager,
165
166
  rendermime,
167
+ askAgent,
168
+ trans,
166
169
  pinned: pin,
167
170
  onPinned
168
171
  });
@@ -1,13 +1,5 @@
1
1
  import type { IXtralabDiffModel } from './diffWidget';
2
- import type { GitReference, IFileChange } from './tokens';
3
- /**
4
- * Resolve the old/new git refs for a launcher file change. Staged diffs are
5
- * INDEX vs HEAD; unstaged diffs are WORKING vs INDEX.
6
- */
7
- export declare function resolveReferences(change: IFileChange): {
8
- oldRef: GitReference | null;
9
- newRef: GitReference | null;
10
- };
2
+ import type { IFileChange } from './tokens';
11
3
  /**
12
4
  * Build a diff model for a single {@link IFileChange}.
13
5
  */
@@ -5,7 +5,7 @@ import { content } from './api';
5
5
  * Resolve the old/new git refs for a launcher file change. Staged diffs are
6
6
  * INDEX vs HEAD; unstaged diffs are WORKING vs INDEX.
7
7
  */
8
- export function resolveReferences(change) {
8
+ function resolveReferences(change) {
9
9
  if (change.status === 'untracked') {
10
10
  return { oldRef: null, newRef: { special: 'WORKING' } };
11
11
  }
@@ -1,6 +1,8 @@
1
1
  import { IThemeManager } from '@jupyterlab/apputils';
2
2
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
3
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
3
4
  import { IGitExtension } from '@jupyterlab/git';
5
+ import { IAskAgent } from '../askAgent/tokens';
4
6
  import { XtralabDiffWidget, addDiffToolbarItems } from './diffWidget';
5
7
  import { IMAGE_DIFF_EXTENSIONS } from './imageDiff';
6
8
  /**
@@ -20,12 +22,15 @@ const diffProviderPlugin = {
20
22
  description: "Replaces jupyterlab-git's notebook, text and image diff plugins with xtralab's renderer.",
21
23
  autoStart: true,
22
24
  requires: [IGitExtension],
23
- optional: [IRenderMimeRegistry, IThemeManager],
24
- activate: (app, gitExtension, rendermime, themeManager) => {
25
+ optional: [IRenderMimeRegistry, IThemeManager, ITranslator, IAskAgent],
26
+ activate: (app, gitExtension, rendermime, themeManager, translator, askAgent) => {
27
+ const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
25
28
  const factory = makeXtralabDiffFactory({
26
29
  contentsManager: app.serviceManager.contents,
27
30
  rendermime,
28
- themeManager
31
+ themeManager,
32
+ askAgent,
33
+ trans
29
34
  });
30
35
  // Use one shared factory; the surface detects notebooks/images by filename.
31
36
  gitExtension.registerDiffProvider('XtralabNotebook', ['.ipynb'], factory);
@@ -1,6 +1,8 @@
1
1
  import * as React from 'react';
2
2
  import { IThemeManager } from '@jupyterlab/apputils';
3
3
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
4
+ import type { TranslationBundle } from '@jupyterlab/translation';
5
+ import { type SelectedLineRange } from '@pierre/diffs';
4
6
  /**
5
7
  * The CSS class added to the diff main-area widget. The selectors that
6
8
  * style the embedded `@pierre/diffs` viewer hang off this class.
@@ -42,12 +44,12 @@ export declare function isPierreTheme(themeManager: IThemeManager | null): boole
42
44
  * the on-disk path) and triggers any follow-up refresh in
43
45
  * {@link IHunkDiscard.onAfterSave}.
44
46
  */
45
- export interface IHunkDiscard {
47
+ interface IHunkDiscard {
46
48
  enabled: boolean;
47
49
  save: (fullText: string) => Promise<void>;
48
50
  onAfterSave: () => void;
49
51
  }
50
- export interface IDiffSurfaceProps {
52
+ interface IDiffSurfaceProps {
51
53
  /**
52
54
  * Whether the host is still resolving the file contents.
53
55
  */
@@ -124,6 +126,18 @@ export interface IDiffSurfaceProps {
124
126
  * Optional per-hunk discard wiring; omit for a read-only diff.
125
127
  */
126
128
  hunkDiscard?: IHunkDiscard;
129
+ /**
130
+ * When provided, the textual/code file diff gets line selection: clicking
131
+ * or dragging over the line numbers selects a range, and a gutter "+"
132
+ * button appears on the hovered/selected lines. Clicking that button
133
+ * invokes this callback with the selected range and the button's viewport
134
+ * rectangle (to anchor a popup to), `null` when it cannot be measured.
135
+ */
136
+ onLineAsk?: (range: SelectedLineRange, anchor: DOMRect | null) => void;
137
+ /**
138
+ * Translation bundle for user-facing strings.
139
+ */
140
+ trans: TranslationBundle;
127
141
  }
128
142
  /**
129
143
  * Shared renderer for text, notebook and image diffs.
@@ -139,6 +153,7 @@ export declare function NotebookViewModeControl(props: {
139
153
  mode: NotebookDiffViewMode;
140
154
  available: boolean;
141
155
  onChange: (mode: NotebookDiffViewMode) => void;
156
+ trans: TranslationBundle;
142
157
  }): React.ReactElement;
143
158
  /**
144
159
  * Segmented Split/Unified selector. Mirrors {@link NotebookViewModeControl}:
@@ -151,4 +166,6 @@ export declare function DiffStyleControl(props: {
151
166
  diffStyle: DiffStyle;
152
167
  available: boolean;
153
168
  onChange: (style: DiffStyle) => void;
169
+ trans: TranslationBundle;
154
170
  }): React.ReactElement;
171
+ export {};