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
@@ -17,6 +17,7 @@ import {
17
17
  import { Signal, ISignal } from '@lumino/signaling';
18
18
  import { AccordionPanel, PanelLayout, Widget } from '@lumino/widgets';
19
19
 
20
+ import type { IFileChange } from '../git/tokens';
20
21
  import { FileBrowserComponent } from './fileBrowser';
21
22
  import { xtralabFileBrowserIcon } from './icons';
22
23
 
@@ -42,10 +43,26 @@ const ACCORDION_CSS_CLASS = 'jp-xtralab-FileBrowser-accordion';
42
43
 
43
44
  const MOVABLE_SECTION_CLASS = 'jp-movable-section';
44
45
 
46
+ /**
47
+ * The options used to create an {@link XtralabFileBrowser}.
48
+ */
45
49
  interface IXtralabFileBrowserOptions {
50
+ /**
51
+ * The contents manager backing the tree.
52
+ */
46
53
  contentsManager: Contents.IManager;
54
+ /**
55
+ * The document manager used to open files from the tree.
56
+ */
47
57
  docManager: IDocumentManager;
58
+ /**
59
+ * Callback invoked with a file's server path when its row is
60
+ * double-clicked. No double-click handler is installed when absent.
61
+ */
48
62
  onOpenFile?: (serverPath: string) => void;
63
+ /**
64
+ * The application language translator.
65
+ */
49
66
  translator?: ITranslator;
50
67
  }
51
68
 
@@ -56,82 +73,68 @@ interface IXtralabFileBrowserOptions {
56
73
  */
57
74
  export interface IXtralabFileBrowser {
58
75
  /**
59
- * The contents manager backing the tree. Exposed so command handlers can
60
- * issue contents operations (rename, delete, copy, …) against the same
61
- * drive the tree was loaded from.
76
+ * The contents manager backing the tree; command handlers issue rename,
77
+ * delete, copy, … against the same drive.
62
78
  */
63
79
  readonly contentsManager: Contents.IManager;
64
80
 
65
81
  /**
66
- * The toolbar shown above the tree. Plugin code populates it with command
67
- * buttons after the widget is constructed.
82
+ * The toolbar above the tree, populated by plugin code after construction.
68
83
  */
69
84
  readonly toolbar: Toolbar;
70
85
 
71
86
  /**
72
- * Canonical paths of the items currently selected in the tree. Folder
73
- * paths carry a trailing slash; file paths do not.
87
+ * Canonical selected paths; folder paths carry a trailing slash.
74
88
  */
75
89
  readonly selectedPaths: readonly string[];
76
90
 
77
91
  /**
78
- * Emits when the tree's selection changes. Commands that have an
79
- * `isVisible` / `isEnabled` predicate should listen so they re-evaluate.
92
+ * Emits when the tree's selection changes.
80
93
  */
81
94
  readonly selectionChanged: ISignal<IXtralabFileBrowser, readonly string[]>;
82
95
 
83
96
  /**
84
- * Emits when {@link refresh} is called. The React component listens and
85
- * re-fetches the directories that were previously loaded so the tree
86
- * matches the contents on disk again.
97
+ * Git changes from the latest status poll, one per staged or unstaged
98
+ * change; paths are repo-relative.
99
+ */
100
+ readonly gitChanges: readonly IFileChange[];
101
+
102
+ /**
103
+ * Emits when {@link refresh} is called.
87
104
  */
88
105
  readonly refreshRequested: ISignal<IXtralabFileBrowser, void>;
89
106
 
90
107
  /**
91
- * Emits when the widget is asked to surface a newly-created path
92
- * (for example, after the toolbar's "new folder" command). The React
93
- * component listens and inserts the path into the model so the user
94
- * sees the new item without a full refresh.
108
+ * Emits a newly-created canonical path so the tree inserts it without a
109
+ * full refresh.
95
110
  */
96
111
  readonly pathAdded: ISignal<IXtralabFileBrowser, string>;
97
112
 
98
113
  /**
99
- * Emits when an external caller asks the tree to scroll to and select
100
- * the given canonical path. The React component listens, lazily loads
101
- * any unloaded ancestor directories, expands them, and selects the
102
- * target. Used by the editor breadcrumbs to jump back to a file or
103
- * folder shown in the breadcrumb trail.
114
+ * Emits a canonical path to load, expand, select, and scroll to. Used by
115
+ * the editor breadcrumbs.
104
116
  */
105
117
  readonly revealRequested: ISignal<IXtralabFileBrowser, string>;
106
118
 
107
119
  /**
108
- * Emits when an external caller asks the tree to return to the
109
- * workspace root: clear any current selection and scroll back to the
110
- * first row. Distinct from {@link revealRequested} because the
111
- * workspace root has no tree row of its own — it cannot be reached
112
- * by passing a path.
120
+ * Emits when the tree should return to the workspace root — the root has
121
+ * no tree row of its own, so it cannot be revealed by path.
113
122
  */
114
123
  readonly rootRequested: ISignal<IXtralabFileBrowser, void>;
115
124
 
116
125
  /**
117
- * Emits when an external caller asks the tree to collapse every
118
- * expanded folder. The React component listens and walks the loaded
119
- * directories, calling `.collapse()` on each expanded one.
126
+ * Emits when every expanded folder should collapse.
120
127
  */
121
128
  readonly collapseAllRequested: ISignal<IXtralabFileBrowser, void>;
122
129
 
123
130
  /**
124
- * Whether the filter box above the tree is shown. Mirrors the default
125
- * file browser's `showFileFilter`: hidden on startup and flipped by the
126
- * toolbar toggle. Also forced to `true` when the tree opens a search
127
- * session on its own — typing a letter while the tree has focus.
131
+ * Whether the filter box above the tree is shown; also forced `true` when
132
+ * the tree opens a search session itself (typing while it has focus).
128
133
  */
129
134
  readonly fileFilterVisible: boolean;
130
135
 
131
136
  /**
132
- * Emits when {@link fileFilterVisible} changes. The React component
133
- * listens to show or hide the filter box; the toggle command listens
134
- * to refresh its toggled state.
137
+ * Emits when {@link fileFilterVisible} changes.
135
138
  */
136
139
  readonly fileFilterVisibleChanged: ISignal<IXtralabFileBrowser, boolean>;
137
140
 
@@ -141,24 +144,19 @@ export interface IXtralabFileBrowser {
141
144
  refresh(): void;
142
145
 
143
146
  /**
144
- * Notify the React component that a new path was created and should
145
- * appear in the tree. `canonicalPath` follows the `@pierre/trees`
146
- * convention: directories carry a trailing slash, files do not.
147
+ * Notify the React component that a new canonical path was created and
148
+ * should appear in the tree.
147
149
  */
148
150
  notifyPathAdded(canonicalPath: string): void;
149
151
 
150
152
  /**
151
- * Ask the tree to reveal {@link canonicalPath}: load and expand any
152
- * unloaded ancestor directories, then select and scroll the target
153
- * into view. `canonicalPath` follows the `@pierre/trees` convention
154
- * (directories carry a trailing slash, files do not). Must be a
155
- * non-empty path — use {@link scrollToRoot} for the root gesture.
153
+ * Reveal a non-empty canonical path: load and expand ancestors, then
154
+ * select and scroll to it. Use {@link scrollToRoot} for the root gesture.
156
155
  */
157
156
  reveal(canonicalPath: string): void;
158
157
 
159
158
  /**
160
- * Ask the tree to return to the workspace root: clear any current
161
- * selection and scroll back to the top of the tree.
159
+ * Return to the workspace root: clear the selection and scroll to the top.
162
160
  */
163
161
  scrollToRoot(): void;
164
162
 
@@ -177,6 +175,10 @@ export interface IXtralabFileBrowser {
177
175
  */
178
176
  toggleFileFilter(): void;
179
177
 
178
+ /**
179
+ * The DOM node of the movable "Files" section, used to locate the panel
180
+ * currently hosting the tree.
181
+ */
180
182
  readonly sectionNode: HTMLElement;
181
183
  }
182
184
 
@@ -228,67 +230,123 @@ export class XtralabFileBrowser
228
230
  layout.addWidget(this._section);
229
231
  }
230
232
 
233
+ /**
234
+ * The contents manager backing the tree.
235
+ */
231
236
  get contentsManager(): Contents.IManager {
232
237
  return this._contentsManager;
233
238
  }
234
239
 
240
+ /**
241
+ * The toolbar above the tree.
242
+ */
235
243
  get toolbar(): Toolbar {
236
244
  return this._toolbar;
237
245
  }
238
246
 
247
+ /**
248
+ * Canonical selected paths; folder paths carry a trailing slash.
249
+ */
239
250
  get selectedPaths(): readonly string[] {
240
251
  return this._selectedPaths;
241
252
  }
242
253
 
254
+ /**
255
+ * A signal emitted when the tree's selection changes.
256
+ */
243
257
  get selectionChanged(): ISignal<this, readonly string[]> {
244
258
  return this._selectionChanged;
245
259
  }
246
260
 
261
+ /**
262
+ * Git changes from the latest status poll.
263
+ */
264
+ get gitChanges(): readonly IFileChange[] {
265
+ return this._gitChanges;
266
+ }
267
+
268
+ /**
269
+ * A signal emitted when {@link refresh} is called.
270
+ */
247
271
  get refreshRequested(): ISignal<this, void> {
248
272
  return this._refreshRequested;
249
273
  }
250
274
 
275
+ /**
276
+ * A signal emitting a newly-created canonical path for the tree to insert.
277
+ */
251
278
  get pathAdded(): ISignal<this, string> {
252
279
  return this._pathAdded;
253
280
  }
254
281
 
282
+ /**
283
+ * A signal emitting a canonical path to load, expand, select, and scroll to.
284
+ */
255
285
  get revealRequested(): ISignal<this, string> {
256
286
  return this._revealRequested;
257
287
  }
258
288
 
289
+ /**
290
+ * A signal emitted when the tree should return to the workspace root.
291
+ */
259
292
  get rootRequested(): ISignal<this, void> {
260
293
  return this._rootRequested;
261
294
  }
262
295
 
296
+ /**
297
+ * A signal emitted when every expanded folder should collapse.
298
+ */
263
299
  get collapseAllRequested(): ISignal<this, void> {
264
300
  return this._collapseAllRequested;
265
301
  }
266
302
 
303
+ /**
304
+ * Whether the filter box above the tree is shown.
305
+ */
267
306
  get fileFilterVisible(): boolean {
268
307
  return this._fileFilterVisible;
269
308
  }
270
309
 
310
+ /**
311
+ * A signal emitted when {@link fileFilterVisible} changes.
312
+ */
271
313
  get fileFilterVisibleChanged(): ISignal<this, boolean> {
272
314
  return this._fileFilterVisibleChanged;
273
315
  }
274
316
 
275
317
  /**
276
- * Update the cached selection. Called from the React tree when the
277
- * underlying `@pierre/trees` model emits a selection change.
318
+ * Update the cached selection from the React tree.
278
319
  */
279
320
  updateSelection(paths: readonly string[]): void {
280
321
  this._selectedPaths = paths;
281
322
  this._selectionChanged.emit(paths);
282
323
  }
283
324
 
325
+ /**
326
+ * Update the cached git changes from the React tree's status poll.
327
+ */
328
+ updateGitChanges(changes: readonly IFileChange[]): void {
329
+ this._gitChanges = changes;
330
+ }
331
+
332
+ /**
333
+ * Trigger a refresh of every loaded directory in the tree.
334
+ */
284
335
  refresh(): void {
285
336
  this._refreshRequested.emit();
286
337
  }
287
338
 
339
+ /**
340
+ * Notify the tree that a new canonical path was created.
341
+ */
288
342
  notifyPathAdded(canonicalPath: string): void {
289
343
  this._pathAdded.emit(canonicalPath);
290
344
  }
291
345
 
346
+ /**
347
+ * Reveal a non-empty canonical path in the tree; use {@link scrollToRoot}
348
+ * for the workspace root, which has no tree row of its own.
349
+ */
292
350
  reveal(canonicalPath: string): void {
293
351
  if (canonicalPath.length === 0) {
294
352
  throw new Error(
@@ -298,14 +356,23 @@ export class XtralabFileBrowser
298
356
  this._revealRequested.emit(canonicalPath);
299
357
  }
300
358
 
359
+ /**
360
+ * Return to the workspace root: clear the selection and scroll to the top.
361
+ */
301
362
  scrollToRoot(): void {
302
363
  this._rootRequested.emit();
303
364
  }
304
365
 
366
+ /**
367
+ * Ask the tree to collapse every currently expanded folder.
368
+ */
305
369
  collapseAll(): void {
306
370
  this._collapseAllRequested.emit();
307
371
  }
308
372
 
373
+ /**
374
+ * Show or hide the filter box above the tree.
375
+ */
309
376
  setFileFilterVisible(visible: boolean): void {
310
377
  if (this._fileFilterVisible === visible) {
311
378
  return;
@@ -314,30 +381,51 @@ export class XtralabFileBrowser
314
381
  this._fileFilterVisibleChanged.emit(visible);
315
382
  }
316
383
 
384
+ /**
385
+ * Toggle the filter box above the tree.
386
+ */
317
387
  toggleFileFilter(): void {
318
388
  this.setFileFilterVisible(!this._fileFilterVisible);
319
389
  }
320
390
 
391
+ /**
392
+ * The DOM node of the movable "Files" section.
393
+ */
321
394
  get sectionNode(): HTMLElement {
322
395
  return this._section.node;
323
396
  }
324
397
 
398
+ /**
399
+ * Whether the panel currently holds no sections, its own tree included.
400
+ */
325
401
  get isEmpty(): boolean {
326
402
  return this._panelLayout.widgets.length === 0;
327
403
  }
328
404
 
405
+ /**
406
+ * A signal emitted when a section is added to or removed from the panel.
407
+ */
329
408
  get contentChanged(): ISignal<this, void> {
330
409
  return this._contentChanged;
331
410
  }
332
411
 
412
+ /**
413
+ * A signal emitting each section entry announced to the move plugin.
414
+ */
333
415
  get sectionAdded(): ISignal<this, ISectionEntry> {
334
416
  return this._sectionAdded;
335
417
  }
336
418
 
419
+ /**
420
+ * The accordion hosting the sections, or `null` while the tree sits alone.
421
+ */
337
422
  get accordionPanel(): AccordionPanel | null {
338
423
  return this._accordion;
339
424
  }
340
425
 
426
+ /**
427
+ * Section widgets hosted from other panels; the tree section is excluded.
428
+ */
341
429
  get sections(): ReadonlyArray<Widget> {
342
430
  if (!this._accordion) {
343
431
  return [];
@@ -361,6 +449,10 @@ export class XtralabFileBrowser
361
449
  : [];
362
450
  }
363
451
 
452
+ /**
453
+ * Detach and return the tree section so the move plugin can re-host it;
454
+ * `null` for any other id or when the section is already away from home.
455
+ */
364
456
  removeSectionById(sectionId: string): Widget | null {
365
457
  if (sectionId !== TREE_SECTION_ID || !this._isSectionHome()) {
366
458
  return null;
@@ -377,6 +469,9 @@ export class XtralabFileBrowser
377
469
  return this._section;
378
470
  }
379
471
 
472
+ /**
473
+ * Re-attach the tree section after a move back to this panel.
474
+ */
380
475
  reinsertSection(widget: Widget): void {
381
476
  if (this._accordion) {
382
477
  this._accordion.insertWidget(0, widget);
@@ -418,6 +513,10 @@ export class XtralabFileBrowser
418
513
  this._contentChanged.emit();
419
514
  }
420
515
 
516
+ /**
517
+ * Remove a hosted section; the accordion is torn down once only the
518
+ * panel's own tree section (or nothing) remains.
519
+ */
421
520
  removeSectionWidget(widget: Widget): void {
422
521
  if (!this._accordion || widget.parent !== this._accordion) {
423
522
  return;
@@ -459,6 +558,7 @@ export class XtralabFileBrowser
459
558
  private _translator: ITranslator | undefined;
460
559
  private _selectedPaths: readonly string[] = [];
461
560
  private _selectionChanged = new Signal<this, readonly string[]>(this);
561
+ private _gitChanges: readonly IFileChange[] = [];
462
562
  private _refreshRequested = new Signal<this, void>(this);
463
563
  private _pathAdded = new Signal<this, string>(this);
464
564
  private _revealRequested = new Signal<this, string>(this);
@@ -475,17 +575,35 @@ export class XtralabFileBrowser
475
575
  private _sectionAdded = new Signal<this, ISectionEntry>(this);
476
576
  }
477
577
 
578
+ /**
579
+ * The options used to create an {@link XtralabFileTreeContent}.
580
+ */
478
581
  interface IFileTreeContentOptions {
582
+ /**
583
+ * The contents manager backing the tree.
584
+ */
479
585
  contentsManager: Contents.IManager;
586
+ /**
587
+ * The document manager used to open files from the tree.
588
+ */
480
589
  docManager: IDocumentManager;
590
+ /**
591
+ * Callback invoked with a file's server path when its row is
592
+ * double-clicked. No double-click handler is installed when absent.
593
+ */
481
594
  onOpenFile?: (serverPath: string) => void;
595
+ /**
596
+ * The application language translator.
597
+ */
482
598
  translator?: ITranslator;
599
+ /**
600
+ * The host widget the React tree reports selection and state changes to.
601
+ */
483
602
  browser: XtralabFileBrowser;
484
603
  }
485
604
 
486
605
  /**
487
- * The React-rendered region of the file browser. Lives inside the host
488
- * widget below the toolbar and renders the actual tree.
606
+ * The React-rendered region below the toolbar that hosts the actual tree.
489
607
  */
490
608
  class XtralabFileTreeContent extends ReactWidget {
491
609
  constructor(options: IFileTreeContentOptions) {
@@ -493,6 +611,9 @@ class XtralabFileTreeContent extends ReactWidget {
493
611
  this._options = options;
494
612
  }
495
613
 
614
+ /**
615
+ * Render the file browser React component.
616
+ */
496
617
  protected render(): React.ReactElement {
497
618
  return (
498
619
  <FileBrowserComponent
@@ -25,13 +25,9 @@ function representativePath(
25
25
  if (extension === undefined || extension === '') {
26
26
  return null;
27
27
  }
28
- // Extensions are stored with a leading dot (e.g. `.py`, `.tar.gz`).
29
28
  return `file${extension.startsWith('.') ? extension : `.${extension}`}`;
30
29
  }
31
30
 
32
- /**
33
- * Point a file type's icon at the tree glyph for its extension.
34
- */
35
31
  function applyTreeIcon(fileType: DocumentRegistry.IFileType): void {
36
32
  const path = representativePath(fileType);
37
33
  if (path === null) {
@@ -79,9 +75,6 @@ const EXTRA_FILE_TYPE_EXTENSIONS = [
79
75
  '.wasm'
80
76
  ];
81
77
 
82
- /**
83
- * Register icon-only file types for unclaimed source extensions.
84
- */
85
78
  function registerExtraFileTypes(docRegistry: DocumentRegistry): void {
86
79
  for (const extension of EXTRA_FILE_TYPE_EXTENSIONS) {
87
80
  const icon = getSpecificTreeIcon(`file${extension}`);
@@ -125,7 +118,6 @@ const plugin: JupyterFrontEndPlugin<void> = {
125
118
  applyTreeIcon(fileType);
126
119
  }
127
120
  registerExtraFileTypes(docRegistry);
128
- // Pick up file types registered after this plugin activates.
129
121
  docRegistry.changed.connect((_, args) => {
130
122
  if (
131
123
  args.type === 'fileType' &&
@@ -148,8 +140,6 @@ const plugin: JupyterFrontEndPlugin<void> = {
148
140
  }
149
141
  };
150
142
  setIcon();
151
- // Keep the tab icon right after a rename, and release the listener
152
- // when the document closes.
153
143
  widget.context.pathChanged.connect(setIcon);
154
144
  widget.disposed.connect(() => {
155
145
  widget.context.pathChanged.disconnect(setIcon);
package/src/git/api.ts CHANGED
@@ -9,29 +9,15 @@ import {
9
9
  IGitStatusResult
10
10
  } from './tokens';
11
11
 
12
- /**
13
- * Root URL segment used by the `jupyterlab_git` server extension. Every
14
- * endpoint hangs off this prefix; the `<repoPath>` between it and the
15
- * endpoint suffix is the JupyterLab server-relative path of the git
16
- * repository (URL-encoded). For an empty `repoPath` the URL collapses to
17
- * `/git/<endpoint>` — a shape git's URL routing also accepts.
18
- */
19
12
  const GIT_NAMESPACE = 'git';
20
13
 
21
- /**
22
- * Issue a `POST` against a `jupyterlab_git` endpoint that takes a `<path>`
23
- * prefix (e.g. `/git/<path>/status`). The response body is decoded as JSON;
24
- * non-2xx responses are surfaced via the JupyterLab `ServerConnection`
25
- * machinery so callers can show their `message` field as-is.
26
- */
27
14
  async function postWithPath<T>(
28
15
  endpoint: string,
29
16
  repoPath: string,
30
17
  body: unknown
31
18
  ): Promise<T> {
32
19
  const settings = ServerConnection.makeSettings();
33
- // `URLExt.encodeParts` encodes each path segment but preserves slashes
34
- // between them — the server expects the path to look like a posix path.
20
+ // encodeParts keeps the slashes the server's posix-path routing expects.
35
21
  const encodedPath = repoPath.length > 0 ? URLExt.encodeParts(repoPath) : '';
36
22
  const url = URLExt.join(
37
23
  settings.baseUrl,
@@ -50,7 +36,7 @@ async function postWithPath<T>(
50
36
  try {
51
37
  data = JSON.parse(text) as T;
52
38
  } catch {
53
- // Fall through into the error handling below.
39
+ // Non-JSON bodies still surface via the ResponseError below.
54
40
  }
55
41
  }
56
42
  if (!response.ok) {
@@ -62,18 +48,16 @@ async function postWithPath<T>(
62
48
  }
63
49
 
64
50
  /**
65
- * Fetch `git status --porcelain` for the repository the server resolves at
66
- * `repoPath`. When `repoPath` is the empty string, git resolves the repo
67
- * from the JupyterLab server's root directory.
51
+ * Fetch `git status --porcelain` for the repo at `repoPath` (empty string
52
+ * resolves from the server's root directory).
68
53
  */
69
54
  export async function status(repoPath: string): Promise<IGitStatusResult> {
70
55
  return postWithPath<IGitStatusResult>('status', repoPath, {});
71
56
  }
72
57
 
73
58
  /**
74
- * Fetch a file's contents at a particular git reference. `filename` is the
75
- * file's path relative to the git repository root (matching the `to` field
76
- * of {@link status}'s response).
59
+ * Fetch a file's contents at a git reference; `filename` is relative to the
60
+ * git repository root.
77
61
  */
78
62
  export async function content(
79
63
  repoPath: string,
@@ -88,13 +72,7 @@ export async function content(
88
72
 
89
73
  /**
90
74
  * Expand the porcelain `files` array into one entry per logical change.
91
- * A single porcelain row may map to two `IFileChange` rows (a file that's
92
- * both staged and modified again in the worktree); callers that just want
93
- * a flat list of all changes — without any staged-vs-unstaged grouping —
94
- * can use this helper.
95
- *
96
- * Provided as a manual concat rather than `Array.prototype.flatMap` so we
97
- * don't pin our `tsconfig.lib` to ES2019.
75
+ * Manual concat rather than `flatMap` so `tsconfig.lib` need not be ES2019.
98
76
  */
99
77
  export function expandStatusFiles(files: IGitStatusFile[]): IFileChange[] {
100
78
  const result: IFileChange[] = [];
@@ -107,16 +85,12 @@ export function expandStatusFiles(files: IGitStatusFile[]): IFileChange[] {
107
85
  }
108
86
 
109
87
  /**
110
- * Translate one entry in the porcelain `files` array into the panel's
111
- * preferred shape. A single porcelain entry may represent up to two changes
112
- * (one in the index, one in the worktree); we emit one {@link IFileChange}
113
- * per non-empty side, so a "modified-staged-and-then-modified-again" file
114
- * shows up twice in the panel — once under "Staged Changes" and once under
115
- * "Changes" — matching the layout the user sees in VS Code.
88
+ * Map one porcelain entry to up to two {@link IFileChange} rows — one per
89
+ * non-empty index/worktree side — so a staged-then-modified-again file
90
+ * appears under both groups, as in VS Code.
116
91
  */
117
92
  function porcelainToFileChanges(file: IGitStatusFile): IFileChange[] {
118
93
  const result: IFileChange[] = [];
119
- // Untracked files are reported with `??` and only appear under "Changes".
120
94
  if (file.x === '?' && file.y === '?') {
121
95
  return [
122
96
  {
@@ -9,10 +9,8 @@ import type { IXtralabDiffModel } from './diffWidget';
9
9
  type DiffSide = 'deletions' | 'additions';
10
10
 
11
11
  /**
12
- * Agent-facing name of a diff content source: the special refs get plain
13
- * words, a git ref string is used verbatim, anything else falls back to the
14
- * side name. English on purpose — this ends up in the agent prompt, not in
15
- * the UI.
12
+ * Agent-facing name of a diff content source. English on purpose — this
13
+ * ends up in the agent prompt, not in the UI.
16
14
  */
17
15
  function describeSource(source: unknown, fallback: string): string {
18
16
  if (source === Git.Diff.SpecialRef.WORKING) {
@@ -34,9 +32,6 @@ function sideName(side: DiffSide): string {
34
32
  return side === 'deletions' ? 'old side' : 'new side';
35
33
  }
36
34
 
37
- /**
38
- * Extract lines `startLine`–`endLine` (1-indexed, inclusive) from `text`.
39
- */
40
35
  function sliceLines(text: string, startLine: number, endLine: number): string {
41
36
  return text
42
37
  .split('\n')
@@ -46,14 +41,10 @@ function sliceLines(text: string, startLine: number, endLine: number): string {
46
41
 
47
42
  /**
48
43
  * Build the ask-agent request for a line range selected in a git diff.
49
- *
50
- * The range's `side` decides which file version the line numbers (and the
51
- * embedded snippet) refer to: `deletions` reads from the reference text,
52
- * anything else — including the sideless context rows of a unified diff,
53
- * whose gutter shows new-side numbers — reads from the challenger text. A
54
- * gutter drag that crosses the two split columns has endpoints in different
55
- * line-number spaces, so it is described textually instead of as a numeric
56
- * range.
44
+ * `deletions` ranges index the reference text; other ranges — including
45
+ * sideless unified context rows, whose gutter shows new-side numbers —
46
+ * index the challenger. A drag across the two split columns mixes
47
+ * line-number spaces, so it is described textually instead of numerically.
57
48
  */
58
49
  export function buildDiffAskRequest(options: {
59
50
  model: IXtralabDiffModel;
@@ -97,15 +88,10 @@ export function buildDiffAskRequest(options: {
97
88
  ...base,
98
89
  startLine,
99
90
  endLine,
100
- // Only new-side lines of a working-tree diff index the file on disk;
101
- // old-side and historical line numbers must not be used to highlight
102
- // the working file.
103
91
  linesInWorkingFile:
104
92
  !isOld && model.challenger.source === Git.Diff.SpecialRef.WORKING,
105
93
  text: sliceLines(isOld ? oldText : newText, startLine, endLine),
106
94
  location: `the ${sideName(startSide)} (${ref}) of the current git diff`,
107
- // New-side lines read as the file's current content, so only old-side
108
- // ranges need a clarifying tag in the popup header.
109
95
  ...(isOld ? { note: trans.__('old version') } : {})
110
96
  },
111
97
  anchor