xtralab 0.14.1 → 0.15.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 (236) hide show
  1. package/README.md +10 -0
  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.d.ts +7 -0
  29. package/lib/customPanel/index.js +89 -0
  30. package/lib/editorBreadcrumbs/index.d.ts +3 -6
  31. package/lib/editorBreadcrumbs/index.js +10 -6
  32. package/lib/editorBreadcrumbs/widget.d.ts +17 -4
  33. package/lib/editorBreadcrumbs/widget.js +11 -17
  34. package/lib/editorIndent/index.d.ts +4 -12
  35. package/lib/editorIndent/index.js +8 -23
  36. package/lib/fileBrowser/commands.d.ts +28 -19
  37. package/lib/fileBrowser/commands.js +51 -98
  38. package/lib/fileBrowser/contents.d.ts +14 -0
  39. package/lib/fileBrowser/contents.js +28 -0
  40. package/lib/fileBrowser/dragAndDrop.d.ts +79 -0
  41. package/lib/fileBrowser/dragAndDrop.js +110 -0
  42. package/lib/fileBrowser/fileBrowser.d.ts +21 -9
  43. package/lib/fileBrowser/fileBrowser.js +211 -405
  44. package/lib/fileBrowser/gitStatus.d.ts +4 -9
  45. package/lib/fileBrowser/gitStatus.js +7 -17
  46. package/lib/fileBrowser/gitignore.d.ts +4 -17
  47. package/lib/fileBrowser/gitignore.js +4 -25
  48. package/lib/fileBrowser/icons.d.ts +7 -17
  49. package/lib/fileBrowser/icons.js +25 -80
  50. package/lib/fileBrowser/index.js +41 -10
  51. package/lib/fileBrowser/widget.d.ts +170 -53
  52. package/lib/fileBrowser/widget.js +242 -18
  53. package/lib/fileTypeIcons/index.js +0 -10
  54. package/lib/git/api.d.ts +5 -13
  55. package/lib/git/api.js +10 -36
  56. package/lib/git/askRequest.d.ts +4 -8
  57. package/lib/git/askRequest.js +6 -20
  58. package/lib/git/commands.d.ts +40 -5
  59. package/lib/git/commands.js +16 -10
  60. package/lib/git/diffModel.js +4 -0
  61. package/lib/git/diffSurface.d.ts +41 -55
  62. package/lib/git/diffSurface.js +42 -117
  63. package/lib/git/diffWidget.d.ts +81 -2
  64. package/lib/git/diffWidget.js +62 -12
  65. package/lib/git/imageDiff.d.ts +5 -6
  66. package/lib/git/imageDiff.js +18 -48
  67. package/lib/git/index.d.ts +0 -3
  68. package/lib/git/index.js +3 -8
  69. package/lib/git/notebookDiff.d.ts +110 -33
  70. package/lib/git/notebookDiff.js +52 -195
  71. package/lib/git/tokens.d.ts +73 -31
  72. package/lib/git/tokens.js +2 -3
  73. package/lib/highlight/index.d.ts +4 -10
  74. package/lib/highlight/index.js +10 -41
  75. package/lib/index.d.ts +0 -9
  76. package/lib/index.js +2 -9
  77. package/lib/launcher/agents.d.ts +55 -51
  78. package/lib/launcher/agents.js +25 -54
  79. package/lib/launcher/availability.d.ts +3 -9
  80. package/lib/launcher/availability.js +3 -9
  81. package/lib/launcher/commands.d.ts +7 -32
  82. package/lib/launcher/commands.js +14 -45
  83. package/lib/launcher/dashboard.d.ts +19 -24
  84. package/lib/launcher/dashboard.js +21 -67
  85. package/lib/launcher/editorRegistry.d.ts +10 -22
  86. package/lib/launcher/editorRegistry.js +14 -18
  87. package/lib/launcher/editors.d.ts +39 -45
  88. package/lib/launcher/editors.js +8 -34
  89. package/lib/launcher/icons.d.ts +0 -12
  90. package/lib/launcher/icons.js +14 -37
  91. package/lib/launcher/index.js +12 -55
  92. package/lib/launcher/invocation.d.ts +4 -10
  93. package/lib/launcher/invocation.js +7 -22
  94. package/lib/launcher/registry.d.ts +9 -7
  95. package/lib/launcher/registry.js +9 -7
  96. package/lib/launcher/schemaDefaults.d.ts +2 -9
  97. package/lib/launcher/schemaDefaults.js +37 -13
  98. package/lib/launcher/tokens.d.ts +10 -22
  99. package/lib/launcher/tokens.js +5 -8
  100. package/lib/menuBar/index.d.ts +3 -10
  101. package/lib/menuBar/index.js +11 -28
  102. package/lib/menus/index.d.ts +3 -9
  103. package/lib/menus/index.js +14 -30
  104. package/lib/omnibox/files.d.ts +2 -4
  105. package/lib/omnibox/files.js +5 -11
  106. package/lib/omnibox/index.d.ts +4 -14
  107. package/lib/omnibox/index.js +9 -27
  108. package/lib/omnibox/model.d.ts +38 -10
  109. package/lib/omnibox/model.js +15 -33
  110. package/lib/omnibox/recents.d.ts +15 -17
  111. package/lib/omnibox/recents.js +12 -17
  112. package/lib/omnibox/tokens.d.ts +5 -10
  113. package/lib/omnibox/tokens.js +2 -6
  114. package/lib/omnibox/widget.d.ts +11 -7
  115. package/lib/omnibox/widget.js +8 -16
  116. package/lib/searchReplace/index.d.ts +4 -20
  117. package/lib/searchReplace/index.js +7 -27
  118. package/lib/showOutput/index.d.ts +3 -12
  119. package/lib/showOutput/index.js +6 -22
  120. package/lib/sidebar/index.d.ts +4 -13
  121. package/lib/sidebar/index.js +73 -46
  122. package/lib/terminalNotifications/index.d.ts +4 -10
  123. package/lib/terminalNotifications/index.js +6 -20
  124. package/lib/terminals/agentTerminals.d.ts +12 -5
  125. package/lib/terminals/agentTerminals.js +25 -44
  126. package/lib/terminals/detection.d.ts +4 -8
  127. package/lib/terminals/detection.js +4 -8
  128. package/lib/terminals/index.d.ts +0 -33
  129. package/lib/terminals/index.js +24 -102
  130. package/lib/terminals/model.d.ts +64 -139
  131. package/lib/terminals/model.js +80 -220
  132. package/lib/terminals/tokens.d.ts +11 -22
  133. package/lib/terminals/tokens.js +2 -2
  134. package/lib/terminals/widget.d.ts +72 -38
  135. package/lib/terminals/widget.js +173 -73
  136. package/lib/topBar/icons.d.ts +3 -8
  137. package/lib/topBar/icons.js +3 -8
  138. package/lib/topBar/index.d.ts +4 -11
  139. package/lib/topBar/index.js +10 -38
  140. package/lib/walkthrough/index.d.ts +4 -9
  141. package/lib/walkthrough/index.js +6 -20
  142. package/lib/walkthrough/panel.d.ts +40 -9
  143. package/lib/walkthrough/panel.js +4 -10
  144. package/package.json +2 -2
  145. package/schema/launcher.json +2 -2
  146. package/schema/sidebar.json +47 -7
  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 +119 -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 +70 -98
  165. package/src/fileBrowser/contents.ts +33 -0
  166. package/src/fileBrowser/dragAndDrop.ts +188 -0
  167. package/src/fileBrowser/fileBrowser.tsx +259 -440
  168. package/src/fileBrowser/gitStatus.ts +7 -17
  169. package/src/fileBrowser/gitignore.ts +4 -25
  170. package/src/fileBrowser/icons.ts +25 -80
  171. package/src/fileBrowser/index.ts +43 -10
  172. package/src/fileBrowser/widget.tsx +344 -64
  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 +70 -10
  177. package/src/git/diffModel.ts +25 -0
  178. package/src/git/diffSurface.tsx +71 -159
  179. package/src/git/diffWidget.tsx +104 -14
  180. package/src/git/imageDiff.tsx +45 -51
  181. package/src/git/index.ts +3 -8
  182. package/src/git/notebookDiff.tsx +211 -238
  183. package/src/git/tokens.ts +73 -31
  184. package/src/highlight/index.ts +10 -41
  185. package/src/index.ts +2 -9
  186. package/src/launcher/agents.ts +74 -92
  187. package/src/launcher/availability.ts +3 -9
  188. package/src/launcher/commands.ts +14 -51
  189. package/src/launcher/dashboard.tsx +49 -88
  190. package/src/launcher/editorRegistry.ts +21 -29
  191. package/src/launcher/editors.ts +39 -58
  192. package/src/launcher/icons.ts +15 -37
  193. package/src/launcher/index.ts +12 -55
  194. package/src/launcher/invocation.ts +7 -22
  195. package/src/launcher/registry.ts +9 -7
  196. package/src/launcher/schemaDefaults.ts +43 -13
  197. package/src/launcher/tokens.ts +10 -22
  198. package/src/menuBar/index.ts +11 -28
  199. package/src/menus/index.ts +14 -30
  200. package/src/omnibox/files.ts +10 -14
  201. package/src/omnibox/index.ts +9 -27
  202. package/src/omnibox/model.ts +50 -38
  203. package/src/omnibox/recents.ts +17 -22
  204. package/src/omnibox/tokens.ts +5 -10
  205. package/src/omnibox/widget.tsx +14 -19
  206. package/src/searchReplace/index.ts +7 -27
  207. package/src/showOutput/index.ts +6 -22
  208. package/src/sidebar/index.ts +103 -52
  209. package/src/terminalNotifications/index.ts +42 -25
  210. package/src/terminals/agentTerminals.ts +25 -49
  211. package/src/terminals/detection.ts +4 -8
  212. package/src/terminals/index.ts +31 -102
  213. package/src/terminals/model.ts +130 -246
  214. package/src/terminals/tokens.ts +11 -22
  215. package/src/terminals/widget.tsx +229 -113
  216. package/src/topBar/icons.ts +3 -8
  217. package/src/topBar/index.ts +21 -44
  218. package/src/walkthrough/index.ts +6 -20
  219. package/src/walkthrough/panel.ts +40 -10
  220. package/style/about.css +2 -7
  221. package/style/askAgent.css +8 -40
  222. package/style/base.css +25 -73
  223. package/style/chrome.css +108 -308
  224. package/style/commandBar.css +4 -20
  225. package/style/customPanel.css +6 -0
  226. package/style/git.css +22 -184
  227. package/style/highlight.css +1 -3
  228. package/style/index.js +1 -0
  229. package/style/launcher.css +6 -70
  230. package/style/omnibox.css +2 -14
  231. package/style/showOutput.css +0 -6
  232. package/style/sidebar.css +29 -9
  233. package/style/tabs.css +5 -13
  234. package/style/terminals.css +18 -123
  235. package/style/topBar.css +2 -22
  236. package/style/walkthrough.css +0 -8
@@ -1,23 +1,28 @@
1
1
  import * as React from 'react';
2
- import { ReactWidget, Toolbar } from '@jupyterlab/ui-components';
2
+ import { AccordionToolbar, PanelWithToolbar, ReactWidget, Toolbar } from '@jupyterlab/ui-components';
3
3
  import { Signal } from '@lumino/signaling';
4
- import { PanelLayout, Widget } from '@lumino/widgets';
4
+ import { AccordionPanel, PanelLayout, Widget } from '@lumino/widgets';
5
5
  import { FileBrowserComponent } from './fileBrowser';
6
6
  import { xtralabFileBrowserIcon } from './icons';
7
7
  export const FILE_BROWSER_ID = 'xtralab:file-browser';
8
8
  /**
9
- * The CSS class added to the xtralab file browser widget. The selectors that
10
- * bind application context menu items hang off this class, so it must remain
11
- * specific enough not to clash with other browsers (such as the default
12
- * `jp-FileBrowser`).
9
+ * Persisted by the movable-sections plugin, so it must stay stable.
10
+ */
11
+ const TREE_SECTION_ID = 'xtralab-file-tree-section';
12
+ /**
13
+ * Context menu selectors hang off this class, so it sits on the section and
14
+ * keeps matching once the tree moves to another panel.
13
15
  */
14
16
  const FILE_BROWSER_CSS_CLASS = 'jp-xtralab-FileBrowser';
17
+ const PANEL_CSS_CLASS = 'jp-xtralab-FileBrowserPanel';
15
18
  const TOOLBAR_CSS_CLASS = 'jp-xtralab-FileBrowser-toolbar';
16
19
  const CONTENT_CSS_CLASS = 'jp-xtralab-FileBrowser-content';
20
+ const ACCORDION_CSS_CLASS = 'jp-xtralab-FileBrowser-accordion';
21
+ const MOVABLE_SECTION_CLASS = 'jp-movable-section';
17
22
  /**
18
- * Lumino widget that hosts the React-based `@pierre/trees` file browser.
19
- * The widget is laid out with a JupyterLab `Toolbar` on top and the tree
20
- * filling the remaining space.
23
+ * Lumino widget hosting the React-based `@pierre/trees` file browser. The tree
24
+ * and its toolbar form one movable "Files" section, and sections from other
25
+ * panels can be moved in below it.
21
26
  */
22
27
  export class XtralabFileBrowser extends Widget {
23
28
  constructor(options) {
@@ -31,86 +36,148 @@ export class XtralabFileBrowser extends Widget {
31
36
  this._collapseAllRequested = new Signal(this);
32
37
  this._fileFilterVisible = false;
33
38
  this._fileFilterVisibleChanged = new Signal(this);
34
- const layout = (this.layout = new PanelLayout());
39
+ this._accordion = null;
40
+ this._contentChanged = new Signal(this);
41
+ this._sectionAdded = new Signal(this);
42
+ const layout = (this._panelLayout = this.layout = new PanelLayout());
35
43
  this._contentsManager = options.contentsManager;
36
44
  this._docManager = options.docManager;
37
45
  this._onOpenFile = options.onOpenFile;
46
+ this._translator = options.translator;
38
47
  this.id = FILE_BROWSER_ID;
39
48
  this.title.icon = xtralabFileBrowserIcon;
40
49
  this.title.caption = 'xtralab File Browser';
41
- this.addClass(FILE_BROWSER_CSS_CLASS);
50
+ this.addClass(PANEL_CSS_CLASS);
42
51
  this._toolbar = new Toolbar();
43
52
  this._toolbar.addClass(TOOLBAR_CSS_CLASS);
44
53
  this._toolbar.node.setAttribute('aria-label', 'file browser');
45
- layout.addWidget(this._toolbar);
46
54
  this._content = new XtralabFileTreeContent({
47
55
  contentsManager: this._contentsManager,
48
56
  docManager: this._docManager,
49
57
  onOpenFile: this._onOpenFile,
58
+ translator: this._translator,
50
59
  browser: this
51
60
  });
52
61
  this._content.addClass(CONTENT_CSS_CLASS);
53
- layout.addWidget(this._content);
62
+ this._section = new PanelWithToolbar({ toolbar: this._toolbar });
63
+ this._section.id = TREE_SECTION_ID;
64
+ // Only an accordion host draws this; a plain `PanelLayout` shows no title.
65
+ this._section.title.label = 'Files';
66
+ this._section.addClass(FILE_BROWSER_CSS_CLASS);
67
+ this._section.addWidget(this._toolbar);
68
+ this._section.addWidget(this._content);
69
+ layout.addWidget(this._section);
54
70
  }
71
+ /**
72
+ * The contents manager backing the tree.
73
+ */
55
74
  get contentsManager() {
56
75
  return this._contentsManager;
57
76
  }
77
+ /**
78
+ * The toolbar above the tree.
79
+ */
58
80
  get toolbar() {
59
81
  return this._toolbar;
60
82
  }
83
+ /**
84
+ * Canonical selected paths; folder paths carry a trailing slash.
85
+ */
61
86
  get selectedPaths() {
62
87
  return this._selectedPaths;
63
88
  }
89
+ /**
90
+ * A signal emitted when the tree's selection changes.
91
+ */
64
92
  get selectionChanged() {
65
93
  return this._selectionChanged;
66
94
  }
95
+ /**
96
+ * A signal emitted when {@link refresh} is called.
97
+ */
67
98
  get refreshRequested() {
68
99
  return this._refreshRequested;
69
100
  }
101
+ /**
102
+ * A signal emitting a newly-created canonical path for the tree to insert.
103
+ */
70
104
  get pathAdded() {
71
105
  return this._pathAdded;
72
106
  }
107
+ /**
108
+ * A signal emitting a canonical path to load, expand, select, and scroll to.
109
+ */
73
110
  get revealRequested() {
74
111
  return this._revealRequested;
75
112
  }
113
+ /**
114
+ * A signal emitted when the tree should return to the workspace root.
115
+ */
76
116
  get rootRequested() {
77
117
  return this._rootRequested;
78
118
  }
119
+ /**
120
+ * A signal emitted when every expanded folder should collapse.
121
+ */
79
122
  get collapseAllRequested() {
80
123
  return this._collapseAllRequested;
81
124
  }
125
+ /**
126
+ * Whether the filter box above the tree is shown.
127
+ */
82
128
  get fileFilterVisible() {
83
129
  return this._fileFilterVisible;
84
130
  }
131
+ /**
132
+ * A signal emitted when {@link fileFilterVisible} changes.
133
+ */
85
134
  get fileFilterVisibleChanged() {
86
135
  return this._fileFilterVisibleChanged;
87
136
  }
88
137
  /**
89
- * Update the cached selection. Called from the React tree when the
90
- * underlying `@pierre/trees` model emits a selection change.
138
+ * Update the cached selection from the React tree.
91
139
  */
92
140
  updateSelection(paths) {
93
141
  this._selectedPaths = paths;
94
142
  this._selectionChanged.emit(paths);
95
143
  }
144
+ /**
145
+ * Trigger a refresh of every loaded directory in the tree.
146
+ */
96
147
  refresh() {
97
148
  this._refreshRequested.emit();
98
149
  }
150
+ /**
151
+ * Notify the tree that a new canonical path was created.
152
+ */
99
153
  notifyPathAdded(canonicalPath) {
100
154
  this._pathAdded.emit(canonicalPath);
101
155
  }
156
+ /**
157
+ * Reveal a non-empty canonical path in the tree; use {@link scrollToRoot}
158
+ * for the workspace root, which has no tree row of its own.
159
+ */
102
160
  reveal(canonicalPath) {
103
161
  if (canonicalPath.length === 0) {
104
162
  throw new Error('XtralabFileBrowser.reveal requires a non-empty canonical path; use scrollToRoot for the workspace root.');
105
163
  }
106
164
  this._revealRequested.emit(canonicalPath);
107
165
  }
166
+ /**
167
+ * Return to the workspace root: clear the selection and scroll to the top.
168
+ */
108
169
  scrollToRoot() {
109
170
  this._rootRequested.emit();
110
171
  }
172
+ /**
173
+ * Ask the tree to collapse every currently expanded folder.
174
+ */
111
175
  collapseAll() {
112
176
  this._collapseAllRequested.emit();
113
177
  }
178
+ /**
179
+ * Show or hide the filter box above the tree.
180
+ */
114
181
  setFileFilterVisible(visible) {
115
182
  if (this._fileFilterVisible === visible) {
116
183
  return;
@@ -118,20 +185,177 @@ export class XtralabFileBrowser extends Widget {
118
185
  this._fileFilterVisible = visible;
119
186
  this._fileFilterVisibleChanged.emit(visible);
120
187
  }
188
+ /**
189
+ * Toggle the filter box above the tree.
190
+ */
121
191
  toggleFileFilter() {
122
192
  this.setFileFilterVisible(!this._fileFilterVisible);
123
193
  }
194
+ /**
195
+ * The DOM node of the movable "Files" section.
196
+ */
197
+ get sectionNode() {
198
+ return this._section.node;
199
+ }
200
+ /**
201
+ * Whether the panel currently holds no sections, its own tree included.
202
+ */
203
+ get isEmpty() {
204
+ return this._panelLayout.widgets.length === 0;
205
+ }
206
+ /**
207
+ * A signal emitted when a section is added to or removed from the panel.
208
+ */
209
+ get contentChanged() {
210
+ return this._contentChanged;
211
+ }
212
+ /**
213
+ * A signal emitting each section entry announced to the move plugin.
214
+ */
215
+ get sectionAdded() {
216
+ return this._sectionAdded;
217
+ }
218
+ /**
219
+ * The accordion hosting the sections, or `null` while the tree sits alone.
220
+ */
221
+ get accordionPanel() {
222
+ return this._accordion;
223
+ }
224
+ /**
225
+ * Section widgets hosted from other panels; the tree section is excluded.
226
+ */
227
+ get sections() {
228
+ if (!this._accordion) {
229
+ return [];
230
+ }
231
+ return this._accordion.widgets.filter(w => w !== this._section);
232
+ }
233
+ /**
234
+ * The toolbar is the section's title node: it is the tree's header row at
235
+ * home, and an accordion host renders it inside the section title.
236
+ */
237
+ getSections() {
238
+ return this._isSectionHome()
239
+ ? [
240
+ {
241
+ id: TREE_SECTION_ID,
242
+ titleNode: this._toolbar.node,
243
+ widget: this._section
244
+ }
245
+ ]
246
+ : [];
247
+ }
248
+ /**
249
+ * Detach and return the tree section so the move plugin can re-host it;
250
+ * `null` for any other id or when the section is already away from home.
251
+ */
252
+ removeSectionById(sectionId) {
253
+ if (sectionId !== TREE_SECTION_ID || !this._isSectionHome()) {
254
+ return null;
255
+ }
256
+ this._section.parent = null;
257
+ if (this._accordion && this._accordion.widgets.length === 0) {
258
+ this._accordion.dispose();
259
+ this._accordion = null;
260
+ }
261
+ // Away from home the toolbar must not offer "Move to …" as well;
262
+ // `getSections` re-marks it on the way back.
263
+ this._toolbar.node.classList.remove(MOVABLE_SECTION_CLASS);
264
+ this._contentChanged.emit();
265
+ return this._section;
266
+ }
267
+ /**
268
+ * Re-attach the tree section after a move back to this panel.
269
+ */
270
+ reinsertSection(widget) {
271
+ if (this._accordion) {
272
+ this._accordion.insertWidget(0, widget);
273
+ }
274
+ else {
275
+ this._panelLayout.addWidget(widget);
276
+ this._restoreToolbar();
277
+ }
278
+ this._contentChanged.emit();
279
+ }
280
+ /**
281
+ * The move plugin reconciles persisted moves only against sections announced
282
+ * after `registerSource`, and this panel builds its section in the constructor.
283
+ */
284
+ announceSections() {
285
+ for (const entry of this.getSections()) {
286
+ this._sectionAdded.emit(entry);
287
+ }
288
+ }
289
+ /**
290
+ * The first hosted section moves the tree into an accordion.
291
+ */
292
+ addSection(widget) {
293
+ if (!this._accordion) {
294
+ const accordion = new AccordionPanel({
295
+ layout: AccordionToolbar.createLayout({})
296
+ });
297
+ accordion.addClass(ACCORDION_CSS_CLASS);
298
+ if (this._section.parent === this) {
299
+ accordion.addWidget(this._section);
300
+ }
301
+ accordion.addWidget(widget);
302
+ this._accordion = accordion;
303
+ this._panelLayout.addWidget(accordion);
304
+ }
305
+ else {
306
+ this._accordion.addWidget(widget);
307
+ }
308
+ this._contentChanged.emit();
309
+ }
310
+ /**
311
+ * Remove a hosted section; the accordion is torn down once only the
312
+ * panel's own tree section (or nothing) remains.
313
+ */
314
+ removeSectionWidget(widget) {
315
+ if (!this._accordion || widget.parent !== this._accordion) {
316
+ return;
317
+ }
318
+ widget.parent = null;
319
+ const remaining = this._accordion.widgets;
320
+ const treeOnly = remaining.length === 1 && remaining[0] === this._section;
321
+ if (treeOnly || remaining.length === 0) {
322
+ if (treeOnly) {
323
+ if (this._section.isHidden) {
324
+ this._section.show();
325
+ }
326
+ this._panelLayout.addWidget(this._section);
327
+ this._restoreToolbar();
328
+ }
329
+ this._accordion.dispose();
330
+ this._accordion = null;
331
+ }
332
+ this._contentChanged.emit();
333
+ }
334
+ _isSectionHome() {
335
+ const parent = this._section.parent;
336
+ return parent === this || (!!this._accordion && parent === this._accordion);
337
+ }
338
+ /**
339
+ * An accordion host moves the toolbar node into the section title and leaves
340
+ * it orphaned on detach; re-parenting makes the layout attach it again.
341
+ */
342
+ _restoreToolbar() {
343
+ this._toolbar.parent = null;
344
+ this._section.insertWidget(0, this._toolbar);
345
+ }
124
346
  }
125
347
  /**
126
- * The React-rendered region of the file browser. Lives inside the host
127
- * widget below the toolbar and renders the actual tree.
348
+ * The React-rendered region below the toolbar that hosts the actual tree.
128
349
  */
129
350
  class XtralabFileTreeContent extends ReactWidget {
130
351
  constructor(options) {
131
352
  super();
132
353
  this._options = options;
133
354
  }
355
+ /**
356
+ * Render the file browser React component.
357
+ */
134
358
  render() {
135
- return (React.createElement(FileBrowserComponent, { contentsManager: this._options.contentsManager, docManager: this._options.docManager, onOpenFile: this._options.onOpenFile, widget: this._options.browser }));
359
+ return (React.createElement(FileBrowserComponent, { contentsManager: this._options.contentsManager, docManager: this._options.docManager, onOpenFile: this._options.onOpenFile, translator: this._options.translator, widget: this._options.browser }));
136
360
  }
137
361
  }
@@ -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