xtralab 0.15.0 → 0.15.2

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 (237) hide show
  1. package/README.md +65 -30
  2. package/lib/about/icons.d.ts +0 -5
  3. package/lib/about/icons.js +0 -5
  4. package/lib/about/index.d.ts +4 -8
  5. package/lib/about/index.js +6 -17
  6. package/lib/agentSessions.d.ts +9 -29
  7. package/lib/agentSessions.js +2 -7
  8. package/lib/askAgent/editorSelection.d.ts +20 -26
  9. package/lib/askAgent/editorSelection.js +11 -26
  10. package/lib/askAgent/icons.d.ts +2 -3
  11. package/lib/askAgent/icons.js +2 -3
  12. package/lib/askAgent/index.d.ts +4 -26
  13. package/lib/askAgent/index.js +54 -158
  14. package/lib/askAgent/popup.d.ts +12 -16
  15. package/lib/askAgent/popup.js +18 -37
  16. package/lib/askAgent/prompt.d.ts +3 -5
  17. package/lib/askAgent/prompt.js +6 -19
  18. package/lib/askAgent/queue.d.ts +11 -21
  19. package/lib/askAgent/queue.js +9 -19
  20. package/lib/askAgent/queuePanel.d.ts +13 -18
  21. package/lib/askAgent/queuePanel.js +15 -37
  22. package/lib/askAgent/targetPicker.d.ts +2 -9
  23. package/lib/askAgent/targetPicker.js +6 -11
  24. package/lib/askAgent/tokens.d.ts +32 -46
  25. package/lib/askAgent/tokens.js +4 -6
  26. package/lib/commandBar/index.d.ts +4 -13
  27. package/lib/commandBar/index.js +21 -39
  28. package/lib/customPanel/index.js +15 -0
  29. package/lib/editorBreadcrumbs/index.d.ts +3 -6
  30. package/lib/editorBreadcrumbs/index.js +10 -6
  31. package/lib/editorBreadcrumbs/widget.d.ts +17 -4
  32. package/lib/editorBreadcrumbs/widget.js +11 -17
  33. package/lib/editorIndent/index.d.ts +4 -12
  34. package/lib/editorIndent/index.js +8 -23
  35. package/lib/fileBrowser/commands.d.ts +29 -19
  36. package/lib/fileBrowser/commands.js +83 -97
  37. package/lib/fileBrowser/contents.d.ts +3 -0
  38. package/lib/fileBrowser/dragAndDrop.d.ts +17 -4
  39. package/lib/fileBrowser/dragAndDrop.js +2 -7
  40. package/lib/fileBrowser/fileBrowser.d.ts +19 -9
  41. package/lib/fileBrowser/fileBrowser.js +70 -222
  42. package/lib/fileBrowser/gitStatus.d.ts +16 -10
  43. package/lib/fileBrowser/gitStatus.js +31 -30
  44. package/lib/fileBrowser/gitignore.d.ts +4 -17
  45. package/lib/fileBrowser/gitignore.js +4 -25
  46. package/lib/fileBrowser/icons.d.ts +7 -17
  47. package/lib/fileBrowser/icons.js +25 -80
  48. package/lib/fileBrowser/index.js +2 -4
  49. package/lib/fileBrowser/widget.d.ts +141 -48
  50. package/lib/fileBrowser/widget.js +102 -4
  51. package/lib/fileTypeIcons/index.js +0 -10
  52. package/lib/git/api.d.ts +5 -13
  53. package/lib/git/api.js +10 -36
  54. package/lib/git/askRequest.d.ts +4 -8
  55. package/lib/git/askRequest.js +6 -20
  56. package/lib/git/commands.d.ts +45 -5
  57. package/lib/git/commands.js +20 -13
  58. package/lib/git/diffModel.js +4 -0
  59. package/lib/git/diffPreferences.d.ts +90 -0
  60. package/lib/git/diffPreferences.js +107 -0
  61. package/lib/git/diffProvider.js +10 -2
  62. package/lib/git/diffSurface.d.ts +64 -63
  63. package/lib/git/diffSurface.js +72 -213
  64. package/lib/git/diffTheme.d.ts +7 -0
  65. package/lib/git/diffTheme.js +23 -0
  66. package/lib/git/diffWidget.d.ts +70 -14
  67. package/lib/git/diffWidget.js +100 -94
  68. package/lib/git/imageDiff.d.ts +14 -6
  69. package/lib/git/imageDiff.js +19 -75
  70. package/lib/git/index.d.ts +0 -3
  71. package/lib/git/index.js +44 -10
  72. package/lib/git/notebookDiff.d.ts +110 -33
  73. package/lib/git/notebookDiff.js +55 -197
  74. package/lib/git/tokens.d.ts +73 -31
  75. package/lib/git/tokens.js +2 -3
  76. package/lib/highlight/index.d.ts +4 -10
  77. package/lib/highlight/index.js +10 -41
  78. package/lib/index.d.ts +0 -9
  79. package/lib/index.js +0 -9
  80. package/lib/launcher/agents.d.ts +55 -51
  81. package/lib/launcher/agents.js +15 -57
  82. package/lib/launcher/availability.d.ts +3 -9
  83. package/lib/launcher/availability.js +3 -9
  84. package/lib/launcher/commands.d.ts +7 -32
  85. package/lib/launcher/commands.js +14 -45
  86. package/lib/launcher/dashboard.d.ts +19 -24
  87. package/lib/launcher/dashboard.js +21 -67
  88. package/lib/launcher/editorRegistry.d.ts +10 -22
  89. package/lib/launcher/editorRegistry.js +14 -18
  90. package/lib/launcher/editors.d.ts +39 -45
  91. package/lib/launcher/editors.js +8 -34
  92. package/lib/launcher/icons.d.ts +0 -12
  93. package/lib/launcher/icons.js +5 -39
  94. package/lib/launcher/index.js +12 -55
  95. package/lib/launcher/invocation.d.ts +4 -10
  96. package/lib/launcher/invocation.js +7 -22
  97. package/lib/launcher/registry.d.ts +9 -7
  98. package/lib/launcher/registry.js +9 -7
  99. package/lib/launcher/tokens.d.ts +10 -22
  100. package/lib/launcher/tokens.js +5 -8
  101. package/lib/menuBar/index.d.ts +3 -10
  102. package/lib/menuBar/index.js +11 -28
  103. package/lib/menus/index.d.ts +3 -9
  104. package/lib/menus/index.js +14 -30
  105. package/lib/omnibox/files.d.ts +2 -4
  106. package/lib/omnibox/files.js +5 -11
  107. package/lib/omnibox/index.d.ts +4 -14
  108. package/lib/omnibox/index.js +9 -27
  109. package/lib/omnibox/model.d.ts +38 -10
  110. package/lib/omnibox/model.js +15 -33
  111. package/lib/omnibox/recents.d.ts +15 -17
  112. package/lib/omnibox/recents.js +12 -17
  113. package/lib/omnibox/tokens.d.ts +5 -10
  114. package/lib/omnibox/tokens.js +2 -6
  115. package/lib/omnibox/widget.d.ts +11 -7
  116. package/lib/omnibox/widget.js +8 -16
  117. package/lib/searchReplace/index.d.ts +4 -20
  118. package/lib/searchReplace/index.js +7 -27
  119. package/lib/showOutput/index.d.ts +3 -12
  120. package/lib/showOutput/index.js +6 -22
  121. package/lib/sidebar/index.d.ts +4 -7
  122. package/lib/sidebar/index.js +5 -12
  123. package/lib/terminalNotifications/index.d.ts +4 -10
  124. package/lib/terminalNotifications/index.js +6 -20
  125. package/lib/terminals/agentTerminals.d.ts +12 -5
  126. package/lib/terminals/agentTerminals.js +25 -44
  127. package/lib/terminals/detection.d.ts +4 -8
  128. package/lib/terminals/detection.js +4 -8
  129. package/lib/terminals/index.d.ts +0 -35
  130. package/lib/terminals/index.js +12 -101
  131. package/lib/terminals/model.d.ts +64 -139
  132. package/lib/terminals/model.js +80 -220
  133. package/lib/terminals/tokens.d.ts +11 -22
  134. package/lib/terminals/tokens.js +2 -2
  135. package/lib/terminals/widget.d.ts +43 -13
  136. package/lib/terminals/widget.js +33 -18
  137. package/lib/topBar/icons.d.ts +3 -8
  138. package/lib/topBar/icons.js +3 -8
  139. package/lib/topBar/index.d.ts +4 -11
  140. package/lib/topBar/index.js +10 -38
  141. package/lib/walkthrough/index.d.ts +4 -9
  142. package/lib/walkthrough/index.js +6 -20
  143. package/lib/walkthrough/panel.d.ts +40 -9
  144. package/lib/walkthrough/panel.js +4 -10
  145. package/package.json +5 -3
  146. package/schema/git-diff-preferences.json +42 -0
  147. package/src/about/icons.ts +0 -5
  148. package/src/about/index.tsx +6 -17
  149. package/src/agentSessions.ts +9 -29
  150. package/src/askAgent/editorSelection.ts +22 -29
  151. package/src/askAgent/icons.ts +2 -3
  152. package/src/askAgent/index.ts +54 -158
  153. package/src/askAgent/popup.tsx +23 -45
  154. package/src/askAgent/prompt.ts +6 -19
  155. package/src/askAgent/queue.ts +13 -24
  156. package/src/askAgent/queuePanel.tsx +21 -46
  157. package/src/askAgent/targetPicker.tsx +5 -15
  158. package/src/askAgent/tokens.ts +32 -46
  159. package/src/commandBar/index.ts +24 -39
  160. package/src/customPanel/index.ts +15 -0
  161. package/src/editorBreadcrumbs/index.ts +10 -6
  162. package/src/editorBreadcrumbs/widget.ts +23 -17
  163. package/src/editorIndent/index.ts +17 -23
  164. package/src/fileBrowser/commands.ts +119 -96
  165. package/src/fileBrowser/contents.ts +3 -0
  166. package/src/fileBrowser/dragAndDrop.ts +17 -7
  167. package/src/fileBrowser/fileBrowser.tsx +86 -230
  168. package/src/fileBrowser/gitStatus.ts +36 -33
  169. package/src/fileBrowser/gitignore.ts +4 -25
  170. package/src/fileBrowser/icons.ts +25 -80
  171. package/src/fileBrowser/index.ts +2 -4
  172. package/src/fileBrowser/widget.tsx +171 -50
  173. package/src/fileTypeIcons/index.ts +0 -10
  174. package/src/git/api.ts +10 -36
  175. package/src/git/askRequest.ts +6 -20
  176. package/src/git/commands.ts +83 -11
  177. package/src/git/diffModel.ts +25 -0
  178. package/src/git/diffPreferences.ts +192 -0
  179. package/src/git/diffProvider.tsx +11 -2
  180. package/src/git/diffSurface.tsx +165 -286
  181. package/src/git/diffTheme.ts +24 -0
  182. package/src/git/diffWidget.tsx +194 -134
  183. package/src/git/imageDiff.tsx +56 -84
  184. package/src/git/index.ts +54 -10
  185. package/src/git/notebookDiff.tsx +214 -240
  186. package/src/git/tokens.ts +73 -31
  187. package/src/highlight/index.ts +10 -41
  188. package/src/index.ts +0 -9
  189. package/src/launcher/agents.ts +64 -95
  190. package/src/launcher/availability.ts +3 -9
  191. package/src/launcher/commands.ts +14 -51
  192. package/src/launcher/dashboard.tsx +49 -88
  193. package/src/launcher/editorRegistry.ts +21 -29
  194. package/src/launcher/editors.ts +39 -58
  195. package/src/launcher/icons.ts +5 -39
  196. package/src/launcher/index.ts +12 -55
  197. package/src/launcher/invocation.ts +7 -22
  198. package/src/launcher/registry.ts +9 -7
  199. package/src/launcher/tokens.ts +10 -22
  200. package/src/menuBar/index.ts +11 -28
  201. package/src/menus/index.ts +14 -30
  202. package/src/omnibox/files.ts +10 -14
  203. package/src/omnibox/index.ts +9 -27
  204. package/src/omnibox/model.ts +50 -38
  205. package/src/omnibox/recents.ts +17 -22
  206. package/src/omnibox/tokens.ts +5 -10
  207. package/src/omnibox/widget.tsx +14 -19
  208. package/src/searchReplace/index.ts +7 -27
  209. package/src/showOutput/index.ts +6 -22
  210. package/src/sidebar/index.ts +20 -12
  211. package/src/terminalNotifications/index.ts +42 -25
  212. package/src/terminals/agentTerminals.ts +25 -49
  213. package/src/terminals/detection.ts +4 -8
  214. package/src/terminals/index.ts +12 -101
  215. package/src/terminals/model.ts +130 -246
  216. package/src/terminals/tokens.ts +11 -22
  217. package/src/terminals/widget.tsx +48 -31
  218. package/src/topBar/icons.ts +3 -8
  219. package/src/topBar/index.ts +21 -44
  220. package/src/walkthrough/index.ts +6 -20
  221. package/src/walkthrough/panel.ts +40 -10
  222. package/style/about.css +2 -7
  223. package/style/askAgent.css +8 -40
  224. package/style/base.css +9 -45
  225. package/style/chrome.css +108 -308
  226. package/style/commandBar.css +4 -20
  227. package/style/customPanel.css +1 -4
  228. package/style/git.css +26 -186
  229. package/style/highlight.css +1 -3
  230. package/style/launcher.css +6 -70
  231. package/style/omnibox.css +2 -14
  232. package/style/showOutput.css +0 -6
  233. package/style/sidebar.css +29 -9
  234. package/style/tabs.css +5 -13
  235. package/style/terminals.css +6 -53
  236. package/style/topBar.css +2 -22
  237. package/style/walkthrough.css +0 -8
@@ -29,6 +29,7 @@ export class XtralabFileBrowser extends Widget {
29
29
  super();
30
30
  this._selectedPaths = [];
31
31
  this._selectionChanged = new Signal(this);
32
+ this._gitChanges = [];
32
33
  this._refreshRequested = new Signal(this);
33
34
  this._pathAdded = new Signal(this);
34
35
  this._revealRequested = new Signal(this);
@@ -68,65 +69,128 @@ export class XtralabFileBrowser extends Widget {
68
69
  this._section.addWidget(this._content);
69
70
  layout.addWidget(this._section);
70
71
  }
72
+ /**
73
+ * The contents manager backing the tree.
74
+ */
71
75
  get contentsManager() {
72
76
  return this._contentsManager;
73
77
  }
78
+ /**
79
+ * The toolbar above the tree.
80
+ */
74
81
  get toolbar() {
75
82
  return this._toolbar;
76
83
  }
84
+ /**
85
+ * Canonical selected paths; folder paths carry a trailing slash.
86
+ */
77
87
  get selectedPaths() {
78
88
  return this._selectedPaths;
79
89
  }
90
+ /**
91
+ * A signal emitted when the tree's selection changes.
92
+ */
80
93
  get selectionChanged() {
81
94
  return this._selectionChanged;
82
95
  }
96
+ /**
97
+ * Git changes from the latest status poll.
98
+ */
99
+ get gitChanges() {
100
+ return this._gitChanges;
101
+ }
102
+ /**
103
+ * A signal emitted when {@link refresh} is called.
104
+ */
83
105
  get refreshRequested() {
84
106
  return this._refreshRequested;
85
107
  }
108
+ /**
109
+ * A signal emitting a newly-created canonical path for the tree to insert.
110
+ */
86
111
  get pathAdded() {
87
112
  return this._pathAdded;
88
113
  }
114
+ /**
115
+ * A signal emitting a canonical path to load, expand, select, and scroll to.
116
+ */
89
117
  get revealRequested() {
90
118
  return this._revealRequested;
91
119
  }
120
+ /**
121
+ * A signal emitted when the tree should return to the workspace root.
122
+ */
92
123
  get rootRequested() {
93
124
  return this._rootRequested;
94
125
  }
126
+ /**
127
+ * A signal emitted when every expanded folder should collapse.
128
+ */
95
129
  get collapseAllRequested() {
96
130
  return this._collapseAllRequested;
97
131
  }
132
+ /**
133
+ * Whether the filter box above the tree is shown.
134
+ */
98
135
  get fileFilterVisible() {
99
136
  return this._fileFilterVisible;
100
137
  }
138
+ /**
139
+ * A signal emitted when {@link fileFilterVisible} changes.
140
+ */
101
141
  get fileFilterVisibleChanged() {
102
142
  return this._fileFilterVisibleChanged;
103
143
  }
104
144
  /**
105
- * Update the cached selection. Called from the React tree when the
106
- * underlying `@pierre/trees` model emits a selection change.
145
+ * Update the cached selection from the React tree.
107
146
  */
108
147
  updateSelection(paths) {
109
148
  this._selectedPaths = paths;
110
149
  this._selectionChanged.emit(paths);
111
150
  }
151
+ /**
152
+ * Update the cached git changes from the React tree's status poll.
153
+ */
154
+ updateGitChanges(changes) {
155
+ this._gitChanges = changes;
156
+ }
157
+ /**
158
+ * Trigger a refresh of every loaded directory in the tree.
159
+ */
112
160
  refresh() {
113
161
  this._refreshRequested.emit();
114
162
  }
163
+ /**
164
+ * Notify the tree that a new canonical path was created.
165
+ */
115
166
  notifyPathAdded(canonicalPath) {
116
167
  this._pathAdded.emit(canonicalPath);
117
168
  }
169
+ /**
170
+ * Reveal a non-empty canonical path in the tree; use {@link scrollToRoot}
171
+ * for the workspace root, which has no tree row of its own.
172
+ */
118
173
  reveal(canonicalPath) {
119
174
  if (canonicalPath.length === 0) {
120
175
  throw new Error('XtralabFileBrowser.reveal requires a non-empty canonical path; use scrollToRoot for the workspace root.');
121
176
  }
122
177
  this._revealRequested.emit(canonicalPath);
123
178
  }
179
+ /**
180
+ * Return to the workspace root: clear the selection and scroll to the top.
181
+ */
124
182
  scrollToRoot() {
125
183
  this._rootRequested.emit();
126
184
  }
185
+ /**
186
+ * Ask the tree to collapse every currently expanded folder.
187
+ */
127
188
  collapseAll() {
128
189
  this._collapseAllRequested.emit();
129
190
  }
191
+ /**
192
+ * Show or hide the filter box above the tree.
193
+ */
130
194
  setFileFilterVisible(visible) {
131
195
  if (this._fileFilterVisible === visible) {
132
196
  return;
@@ -134,24 +198,45 @@ export class XtralabFileBrowser extends Widget {
134
198
  this._fileFilterVisible = visible;
135
199
  this._fileFilterVisibleChanged.emit(visible);
136
200
  }
201
+ /**
202
+ * Toggle the filter box above the tree.
203
+ */
137
204
  toggleFileFilter() {
138
205
  this.setFileFilterVisible(!this._fileFilterVisible);
139
206
  }
207
+ /**
208
+ * The DOM node of the movable "Files" section.
209
+ */
140
210
  get sectionNode() {
141
211
  return this._section.node;
142
212
  }
213
+ /**
214
+ * Whether the panel currently holds no sections, its own tree included.
215
+ */
143
216
  get isEmpty() {
144
217
  return this._panelLayout.widgets.length === 0;
145
218
  }
219
+ /**
220
+ * A signal emitted when a section is added to or removed from the panel.
221
+ */
146
222
  get contentChanged() {
147
223
  return this._contentChanged;
148
224
  }
225
+ /**
226
+ * A signal emitting each section entry announced to the move plugin.
227
+ */
149
228
  get sectionAdded() {
150
229
  return this._sectionAdded;
151
230
  }
231
+ /**
232
+ * The accordion hosting the sections, or `null` while the tree sits alone.
233
+ */
152
234
  get accordionPanel() {
153
235
  return this._accordion;
154
236
  }
237
+ /**
238
+ * Section widgets hosted from other panels; the tree section is excluded.
239
+ */
155
240
  get sections() {
156
241
  if (!this._accordion) {
157
242
  return [];
@@ -173,6 +258,10 @@ export class XtralabFileBrowser extends Widget {
173
258
  ]
174
259
  : [];
175
260
  }
261
+ /**
262
+ * Detach and return the tree section so the move plugin can re-host it;
263
+ * `null` for any other id or when the section is already away from home.
264
+ */
176
265
  removeSectionById(sectionId) {
177
266
  if (sectionId !== TREE_SECTION_ID || !this._isSectionHome()) {
178
267
  return null;
@@ -188,6 +277,9 @@ export class XtralabFileBrowser extends Widget {
188
277
  this._contentChanged.emit();
189
278
  return this._section;
190
279
  }
280
+ /**
281
+ * Re-attach the tree section after a move back to this panel.
282
+ */
191
283
  reinsertSection(widget) {
192
284
  if (this._accordion) {
193
285
  this._accordion.insertWidget(0, widget);
@@ -228,6 +320,10 @@ export class XtralabFileBrowser extends Widget {
228
320
  }
229
321
  this._contentChanged.emit();
230
322
  }
323
+ /**
324
+ * Remove a hosted section; the accordion is torn down once only the
325
+ * panel's own tree section (or nothing) remains.
326
+ */
231
327
  removeSectionWidget(widget) {
232
328
  if (!this._accordion || widget.parent !== this._accordion) {
233
329
  return;
@@ -262,14 +358,16 @@ export class XtralabFileBrowser extends Widget {
262
358
  }
263
359
  }
264
360
  /**
265
- * The React-rendered region of the file browser. Lives inside the host
266
- * widget below the toolbar and renders the actual tree.
361
+ * The React-rendered region below the toolbar that hosts the actual tree.
267
362
  */
268
363
  class XtralabFileTreeContent extends ReactWidget {
269
364
  constructor(options) {
270
365
  super();
271
366
  this._options = options;
272
367
  }
368
+ /**
369
+ * Render the file browser React component.
370
+ */
273
371
  render() {
274
372
  return (React.createElement(FileBrowserComponent, { contentsManager: this._options.contentsManager, docManager: this._options.docManager, onOpenFile: this._options.onOpenFile, translator: this._options.translator, widget: this._options.browser }));
275
373
  }
@@ -11,12 +11,8 @@ function representativePath(fileType) {
11
11
  if (extension === undefined || extension === '') {
12
12
  return null;
13
13
  }
14
- // Extensions are stored with a leading dot (e.g. `.py`, `.tar.gz`).
15
14
  return `file${extension.startsWith('.') ? extension : `.${extension}`}`;
16
15
  }
17
- /**
18
- * Point a file type's icon at the tree glyph for its extension.
19
- */
20
16
  function applyTreeIcon(fileType) {
21
17
  const path = representativePath(fileType);
22
18
  if (path === null) {
@@ -62,9 +58,6 @@ const EXTRA_FILE_TYPE_EXTENSIONS = [
62
58
  '.graphql',
63
59
  '.wasm'
64
60
  ];
65
- /**
66
- * Register icon-only file types for unclaimed source extensions.
67
- */
68
61
  function registerExtraFileTypes(docRegistry) {
69
62
  for (const extension of EXTRA_FILE_TYPE_EXTENSIONS) {
70
63
  const icon = getSpecificTreeIcon(`file${extension}`);
@@ -100,7 +93,6 @@ const plugin = {
100
93
  applyTreeIcon(fileType);
101
94
  }
102
95
  registerExtraFileTypes(docRegistry);
103
- // Pick up file types registered after this plugin activates.
104
96
  docRegistry.changed.connect((_, args) => {
105
97
  if (args.type === 'fileType' &&
106
98
  args.change === 'added' &&
@@ -120,8 +112,6 @@ const plugin = {
120
112
  }
121
113
  };
122
114
  setIcon();
123
- // Keep the tab icon right after a rename, and release the listener
124
- // when the document closes.
125
115
  widget.context.pathChanged.connect(setIcon);
126
116
  widget.disposed.connect(() => {
127
117
  widget.context.pathChanged.disconnect(setIcon);
package/lib/git/api.d.ts CHANGED
@@ -1,24 +1,16 @@
1
1
  import { GitReference, IFileChange, IGitContentResult, IGitStatusFile, IGitStatusResult } from './tokens';
2
2
  /**
3
- * Fetch `git status --porcelain` for the repository the server resolves at
4
- * `repoPath`. When `repoPath` is the empty string, git resolves the repo
5
- * from the JupyterLab server's root directory.
3
+ * Fetch `git status --porcelain` for the repo at `repoPath` (empty string
4
+ * resolves from the server's root directory).
6
5
  */
7
6
  export declare function status(repoPath: string): Promise<IGitStatusResult>;
8
7
  /**
9
- * Fetch a file's contents at a particular git reference. `filename` is the
10
- * file's path relative to the git repository root (matching the `to` field
11
- * of {@link status}'s response).
8
+ * Fetch a file's contents at a git reference; `filename` is relative to the
9
+ * git repository root.
12
10
  */
13
11
  export declare function content(repoPath: string, filename: string, reference: GitReference): Promise<IGitContentResult>;
14
12
  /**
15
13
  * Expand the porcelain `files` array into one entry per logical change.
16
- * A single porcelain row may map to two `IFileChange` rows (a file that's
17
- * both staged and modified again in the worktree); callers that just want
18
- * a flat list of all changes — without any staged-vs-unstaged grouping —
19
- * can use this helper.
20
- *
21
- * Provided as a manual concat rather than `Array.prototype.flatMap` so we
22
- * don't pin our `tsconfig.lib` to ES2019.
14
+ * Manual concat rather than `flatMap` so `tsconfig.lib` need not be ES2019.
23
15
  */
24
16
  export declare function expandStatusFiles(files: IGitStatusFile[]): IFileChange[];
package/lib/git/api.js CHANGED
@@ -1,24 +1,10 @@
1
1
  import { URLExt } from '@jupyterlab/coreutils';
2
2
  import { ServerConnection } from '@jupyterlab/services';
3
- /**
4
- * Root URL segment used by the `jupyterlab_git` server extension. Every
5
- * endpoint hangs off this prefix; the `<repoPath>` between it and the
6
- * endpoint suffix is the JupyterLab server-relative path of the git
7
- * repository (URL-encoded). For an empty `repoPath` the URL collapses to
8
- * `/git/<endpoint>` — a shape git's URL routing also accepts.
9
- */
10
3
  const GIT_NAMESPACE = 'git';
11
- /**
12
- * Issue a `POST` against a `jupyterlab_git` endpoint that takes a `<path>`
13
- * prefix (e.g. `/git/<path>/status`). The response body is decoded as JSON;
14
- * non-2xx responses are surfaced via the JupyterLab `ServerConnection`
15
- * machinery so callers can show their `message` field as-is.
16
- */
17
4
  async function postWithPath(endpoint, repoPath, body) {
18
5
  var _a, _b;
19
6
  const settings = ServerConnection.makeSettings();
20
- // `URLExt.encodeParts` encodes each path segment but preserves slashes
21
- // between them — the server expects the path to look like a posix path.
7
+ // encodeParts keeps the slashes the server's posix-path routing expects.
22
8
  const encodedPath = repoPath.length > 0 ? URLExt.encodeParts(repoPath) : '';
23
9
  const url = URLExt.join(settings.baseUrl, GIT_NAMESPACE, encodedPath, endpoint);
24
10
  const init = {
@@ -33,7 +19,7 @@ async function postWithPath(endpoint, repoPath, body) {
33
19
  data = JSON.parse(text);
34
20
  }
35
21
  catch (_c) {
36
- // Fall through into the error handling below.
22
+ // Non-JSON bodies still surface via the ResponseError below.
37
23
  }
38
24
  }
39
25
  if (!response.ok) {
@@ -43,17 +29,15 @@ async function postWithPath(endpoint, repoPath, body) {
43
29
  return (data !== null && data !== void 0 ? data : {});
44
30
  }
45
31
  /**
46
- * Fetch `git status --porcelain` for the repository the server resolves at
47
- * `repoPath`. When `repoPath` is the empty string, git resolves the repo
48
- * from the JupyterLab server's root directory.
32
+ * Fetch `git status --porcelain` for the repo at `repoPath` (empty string
33
+ * resolves from the server's root directory).
49
34
  */
50
35
  export async function status(repoPath) {
51
36
  return postWithPath('status', repoPath, {});
52
37
  }
53
38
  /**
54
- * Fetch a file's contents at a particular git reference. `filename` is the
55
- * file's path relative to the git repository root (matching the `to` field
56
- * of {@link status}'s response).
39
+ * Fetch a file's contents at a git reference; `filename` is relative to the
40
+ * git repository root.
57
41
  */
58
42
  export async function content(repoPath, filename, reference) {
59
43
  return postWithPath('content', repoPath, {
@@ -63,13 +47,7 @@ export async function content(repoPath, filename, reference) {
63
47
  }
64
48
  /**
65
49
  * Expand the porcelain `files` array into one entry per logical change.
66
- * A single porcelain row may map to two `IFileChange` rows (a file that's
67
- * both staged and modified again in the worktree); callers that just want
68
- * a flat list of all changes — without any staged-vs-unstaged grouping —
69
- * can use this helper.
70
- *
71
- * Provided as a manual concat rather than `Array.prototype.flatMap` so we
72
- * don't pin our `tsconfig.lib` to ES2019.
50
+ * Manual concat rather than `flatMap` so `tsconfig.lib` need not be ES2019.
73
51
  */
74
52
  export function expandStatusFiles(files) {
75
53
  const result = [];
@@ -81,16 +59,12 @@ export function expandStatusFiles(files) {
81
59
  return result;
82
60
  }
83
61
  /**
84
- * Translate one entry in the porcelain `files` array into the panel's
85
- * preferred shape. A single porcelain entry may represent up to two changes
86
- * (one in the index, one in the worktree); we emit one {@link IFileChange}
87
- * per non-empty side, so a "modified-staged-and-then-modified-again" file
88
- * shows up twice in the panel — once under "Staged Changes" and once under
89
- * "Changes" — matching the layout the user sees in VS Code.
62
+ * Map one porcelain entry to up to two {@link IFileChange} rows — one per
63
+ * non-empty index/worktree side — so a staged-then-modified-again file
64
+ * appears under both groups, as in VS Code.
90
65
  */
91
66
  function porcelainToFileChanges(file) {
92
67
  const result = [];
93
- // Untracked files are reported with `??` and only appear under "Changes".
94
68
  if (file.x === '?' && file.y === '?') {
95
69
  return [
96
70
  {
@@ -4,14 +4,10 @@ import type { IAskAgentRequest } from '../askAgent/tokens';
4
4
  import type { IXtralabDiffModel } from './diffWidget';
5
5
  /**
6
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.
7
+ * `deletions` ranges index the reference text; other ranges — including
8
+ * sideless unified context rows, whose gutter shows new-side numbers —
9
+ * index the challenger. A drag across the two split columns mixes
10
+ * line-number spaces, so it is described textually instead of numerically.
15
11
  */
16
12
  export declare function buildDiffAskRequest(options: {
17
13
  model: IXtralabDiffModel;
@@ -1,9 +1,7 @@
1
1
  import { Git } from '@jupyterlab/git';
2
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.
3
+ * Agent-facing name of a diff content source. English on purpose — this
4
+ * ends up in the agent prompt, not in the UI.
7
5
  */
8
6
  function describeSource(source, fallback) {
9
7
  if (source === Git.Diff.SpecialRef.WORKING) {
@@ -23,9 +21,6 @@ function describeSource(source, fallback) {
23
21
  function sideName(side) {
24
22
  return side === 'deletions' ? 'old side' : 'new side';
25
23
  }
26
- /**
27
- * Extract lines `startLine`–`endLine` (1-indexed, inclusive) from `text`.
28
- */
29
24
  function sliceLines(text, startLine, endLine) {
30
25
  return text
31
26
  .split('\n')
@@ -34,14 +29,10 @@ function sliceLines(text, startLine, endLine) {
34
29
  }
35
30
  /**
36
31
  * 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.
32
+ * `deletions` ranges index the reference text; other ranges — including
33
+ * sideless unified context rows, whose gutter shows new-side numbers —
34
+ * index the challenger. A drag across the two split columns mixes
35
+ * line-number spaces, so it is described textually instead of numerically.
45
36
  */
46
37
  export function buildDiffAskRequest(options) {
47
38
  var _a, _b, _c;
@@ -75,14 +66,9 @@ export function buildDiffAskRequest(options) {
75
66
  ...base,
76
67
  startLine,
77
68
  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
69
  linesInWorkingFile: !isOld && model.challenger.source === Git.Diff.SpecialRef.WORKING,
82
70
  text: sliceLines(isOld ? oldText : newText, startLine, endLine),
83
71
  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
72
  ...(isOld ? { note: trans.__('old version') } : {})
87
73
  },
88
74
  anchor
@@ -5,6 +5,7 @@ import { Contents } from '@jupyterlab/services';
5
5
  import type { TranslationBundle } from '@jupyterlab/translation';
6
6
  import { ISignal } from '@lumino/signaling';
7
7
  import type { IAskAgent } from '../askAgent/tokens';
8
+ import type { IDiffPreferences } from './diffPreferences';
8
9
  import { XtralabDiffWidget } from './diffWidget';
9
10
  import { IFileChange } from './tokens';
10
11
  /**
@@ -17,15 +18,24 @@ export declare namespace CommandIDs {
17
18
  * Argument shapes used by {@link CommandIDs.openDiff}.
18
19
  */
19
20
  export declare namespace CommandArguments {
21
+ /**
22
+ * The arguments for the open-diff command.
23
+ */
20
24
  interface IOpenDiff {
25
+ /**
26
+ * The server-relative path of the repository containing the change.
27
+ */
21
28
  repoPath: string;
29
+ /**
30
+ * The file change to show.
31
+ */
22
32
  change: IFileChange;
33
+ /**
34
+ * Whether to open the diff as a pinned tab instead of the shared preview.
35
+ */
23
36
  pin?: boolean;
24
37
  }
25
38
  }
26
- /**
27
- * Widget id of the single, reused preview diff tab.
28
- */
29
39
  export declare const PREVIEW_DIFF_WIDGET_ID = "xtralab:diff:preview";
30
40
  /**
31
41
  * Deterministic widget id for a pinned file/group pair.
@@ -36,18 +46,42 @@ export declare function pinnedDiffWidgetId(change: IFileChange): string;
36
46
  */
37
47
  export declare class DiffMainAreaWidget extends MainAreaWidget<XtralabDiffWidget> {
38
48
  constructor(options: MainAreaWidget.IOptions<XtralabDiffWidget>, change: IFileChange, pinned: boolean, trans: TranslationBundle);
49
+ /**
50
+ * The file change currently displayed.
51
+ */
39
52
  get change(): IFileChange;
53
+ /**
54
+ * Whether the widget is pinned rather than the shared preview.
55
+ */
40
56
  get pinned(): boolean;
57
+ /**
58
+ * Pin the widget; a no-op when already pinned.
59
+ */
41
60
  pin(): void;
61
+ /**
62
+ * A signal emitted when the widget becomes pinned.
63
+ */
42
64
  get pinnedChanged(): ISignal<this, boolean>;
65
+ /**
66
+ * Display a new file change and update the tab title, caption, and icon.
67
+ */
43
68
  setChange(repoPath: string, change: IFileChange): void;
44
69
  private _change;
45
70
  private _pinned;
46
71
  private _trans;
47
72
  private _pinnedChanged;
48
73
  }
74
+ /**
75
+ * The options for {@link registerGitCommands}.
76
+ */
49
77
  interface IRegisterGitCommandsOptions {
78
+ /**
79
+ * The JupyterLab application, providing the commands registry and shell.
80
+ */
50
81
  app: JupyterFrontEnd;
82
+ /**
83
+ * Theme manager the diff views follow; may be `null`.
84
+ */
51
85
  themeManager: IThemeManager | null;
52
86
  /**
53
87
  * Contents manager used to write hunk-discard results.
@@ -61,6 +95,10 @@ interface IRegisterGitCommandsOptions {
61
95
  * Ask-agent popup for prompting an agent about diff lines; may be `null`.
62
96
  */
63
97
  askAgent: IAskAgent | null;
98
+ /**
99
+ * The display choices shared by all diff views.
100
+ */
101
+ preferences: IDiffPreferences;
64
102
  /**
65
103
  * Translation bundle for user-facing strings.
66
104
  */
@@ -73,11 +111,13 @@ interface IRegisterGitCommandsOptions {
73
111
  * Look up an already-open diff widget for a file change.
74
112
  */
75
113
  findDiff(change: IFileChange, pin?: boolean): DiffMainAreaWidget | undefined;
114
+ /**
115
+ * Callback invoked when a preview diff widget becomes pinned.
116
+ */
76
117
  onPinned(widget: DiffMainAreaWidget): void;
77
118
  }
78
119
  /**
79
- * Register the launcher's git diff command on the application command
80
- * registry.
120
+ * Register the launcher's git diff command.
81
121
  */
82
122
  export declare function registerGitCommands(options: IRegisterGitCommandsOptions): void;
83
123
  export {};
@@ -11,9 +11,6 @@ export var CommandIDs;
11
11
  (function (CommandIDs) {
12
12
  CommandIDs.openDiff = 'xtralab:git:open-diff';
13
13
  })(CommandIDs || (CommandIDs = {}));
14
- /**
15
- * Widget id of the single, reused preview diff tab.
16
- */
17
14
  export const PREVIEW_DIFF_WIDGET_ID = 'xtralab:diff:preview';
18
15
  /**
19
16
  * Deterministic widget id for a pinned file/group pair.
@@ -38,12 +35,21 @@ export class DiffMainAreaWidget extends MainAreaWidget {
38
35
  this._pinned = pinned;
39
36
  this._trans = trans;
40
37
  }
38
+ /**
39
+ * The file change currently displayed.
40
+ */
41
41
  get change() {
42
42
  return this._change;
43
43
  }
44
+ /**
45
+ * Whether the widget is pinned rather than the shared preview.
46
+ */
44
47
  get pinned() {
45
48
  return this._pinned;
46
49
  }
50
+ /**
51
+ * Pin the widget; a no-op when already pinned.
52
+ */
47
53
  pin() {
48
54
  if (this._pinned) {
49
55
  return;
@@ -51,9 +57,15 @@ export class DiffMainAreaWidget extends MainAreaWidget {
51
57
  this._pinned = true;
52
58
  this._pinnedChanged.emit(true);
53
59
  }
60
+ /**
61
+ * A signal emitted when the widget becomes pinned.
62
+ */
54
63
  get pinnedChanged() {
55
64
  return this._pinnedChanged;
56
65
  }
66
+ /**
67
+ * Display a new file change and update the tab title, caption, and icon.
68
+ */
57
69
  setChange(repoPath, change) {
58
70
  this._change = change;
59
71
  this.content.setModel(fileChangeToDiffModel(repoPath, change));
@@ -62,14 +74,11 @@ export class DiffMainAreaWidget extends MainAreaWidget {
62
74
  this.title.icon = getTreeIcon(change.path);
63
75
  }
64
76
  }
65
- /**
66
- * Build the launcher's diff tab around the shared diff widget.
67
- */
68
77
  function createDiffWidget(options) {
69
78
  var _a;
70
- const { repoPath, change, themeManager, contentsManager, rendermime, askAgent, trans } = options;
79
+ const { repoPath, change, themeManager, contentsManager, rendermime, askAgent, preferences, trans } = options;
71
80
  const pinned = options.pinned === true;
72
- const content = new XtralabDiffWidget(fileChangeToDiffModel(repoPath, change), { contentsManager, rendermime, themeManager, askAgent, trans });
81
+ const content = new XtralabDiffWidget(fileChangeToDiffModel(repoPath, change), { contentsManager, rendermime, themeManager, askAgent, preferences, trans });
73
82
  const widget = new DiffMainAreaWidget({ content }, change, pinned, trans);
74
83
  widget.id = pinned ? pinnedDiffWidgetId(change) : PREVIEW_DIFF_WIDGET_ID;
75
84
  widget.title.label = formatTitle(change, trans);
@@ -89,7 +98,6 @@ function createDiffWidget(options) {
89
98
  }
90
99
  widget.toolbar.addItem('pin', pinButton);
91
100
  addDiffToolbarItems(widget.toolbar, content);
92
- // Keep chrome visible only while there is a relevant toolbar control.
93
101
  const syncToolbarVisibility = () => {
94
102
  if (widget.isDisposed) {
95
103
  return;
@@ -115,7 +123,6 @@ function createDiffWidget(options) {
115
123
  }
116
124
  syncToolbarVisibility();
117
125
  };
118
- // Auto-close once a discard leaves nothing to show.
119
126
  const onEmptied = () => {
120
127
  if (!widget.isDisposed) {
121
128
  widget.close();
@@ -135,11 +142,10 @@ function createDiffWidget(options) {
135
142
  return widget;
136
143
  }
137
144
  /**
138
- * Register the launcher's git diff command on the application command
139
- * registry.
145
+ * Register the launcher's git diff command.
140
146
  */
141
147
  export function registerGitCommands(options) {
142
- const { app, themeManager, contentsManager, rendermime, askAgent, trans, trackDiff, findDiff, onPinned } = options;
148
+ const { app, themeManager, contentsManager, rendermime, askAgent, preferences, trans, trackDiff, findDiff, onPinned } = options;
143
149
  const { commands } = app;
144
150
  commands.addCommand(CommandIDs.openDiff, {
145
151
  label: trans.__('Open Git Diff'),
@@ -165,6 +171,7 @@ export function registerGitCommands(options) {
165
171
  contentsManager,
166
172
  rendermime,
167
173
  askAgent,
174
+ preferences,
168
175
  trans,
169
176
  pinned: pin,
170
177
  onPinned