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,13 +1,8 @@
1
1
  import type { GitStatusEntry } from '@pierre/trees';
2
2
  /**
3
- * Fetch the porcelain status for `repoPath` and convert it into a flat list
4
- * of `GitStatusEntry` values consumable by `FileTree.setGitStatus`. Returns
5
- * an empty array when the path is not in a git repo, the request fails, or
6
- * the server reports a non-zero exit — the file browser must still work
7
- * without git, so any error here is a no-op rather than a thrown exception.
8
- *
9
- * Files that have both a staged and an unstaged change are collapsed into a
10
- * single entry: the unstaged status wins because that mirrors what is
11
- * actually different on disk and matches VS Code's tree decoration behavior.
3
+ * Fetch the porcelain status for `repoPath` as `GitStatusEntry` values.
4
+ * Any failure yields an empty array — the file browser must work without
5
+ * git. A file both staged and unstaged gets one entry, the unstaged status
6
+ * winning (mirrors what is on disk, matching VS Code).
12
7
  */
13
8
  export declare function loadGitStatusEntries(repoPath: string): Promise<GitStatusEntry[]>;
@@ -1,10 +1,8 @@
1
1
  import { expandStatusFiles, status } from '../git/api';
2
2
  /**
3
- * Translate a `jupyterlab_git` porcelain status into the closest
4
- * `@pierre/trees` `GitStatus` enum value. The tree library only supports the
5
- * six common statuses (added, deleted, ignored, modified, renamed, untracked)
6
- * so the rare ones (unmerged, typechange, unknown) collapse onto `modified`
7
- * — they all denote a pending change the user should see decorated.
3
+ * Translate a `jupyterlab_git` porcelain status into a `@pierre/trees`
4
+ * `GitStatus`. The tree only supports the six common statuses, so the rare
5
+ * ones (unmerged, typechange, unknown) collapse onto `modified`.
8
6
  */
9
7
  function toTreeStatus(value) {
10
8
  switch (value) {
@@ -22,15 +20,10 @@ function toTreeStatus(value) {
22
20
  }
23
21
  }
24
22
  /**
25
- * Fetch the porcelain status for `repoPath` and convert it into a flat list
26
- * of `GitStatusEntry` values consumable by `FileTree.setGitStatus`. Returns
27
- * an empty array when the path is not in a git repo, the request fails, or
28
- * the server reports a non-zero exit — the file browser must still work
29
- * without git, so any error here is a no-op rather than a thrown exception.
30
- *
31
- * Files that have both a staged and an unstaged change are collapsed into a
32
- * single entry: the unstaged status wins because that mirrors what is
33
- * actually different on disk and matches VS Code's tree decoration behavior.
23
+ * Fetch the porcelain status for `repoPath` as `GitStatusEntry` values.
24
+ * Any failure yields an empty array — the file browser must work without
25
+ * git. A file both staged and unstaged gets one entry, the unstaged status
26
+ * winning (mirrors what is on disk, matching VS Code).
34
27
  */
35
28
  export async function loadGitStatusEntries(repoPath) {
36
29
  try {
@@ -39,9 +32,6 @@ export async function loadGitStatusEntries(repoPath) {
39
32
  return [];
40
33
  }
41
34
  const changes = expandStatusFiles(result.files);
42
- // Collapse staged + unstaged into one decoration per file. Prefer the
43
- // unstaged group when both exist; otherwise keep whichever group is
44
- // present.
45
35
  const byPath = new Map();
46
36
  for (const change of changes) {
47
37
  if (change.group === 'unstaged' || !byPath.has(change.path)) {
@@ -8,22 +8,9 @@ import type { GitStatusEntry } from '@pierre/trees';
8
8
  */
9
9
  export declare function loadGitignoreMatcher(contentsManager: Contents.IManager): Promise<Ignore | null>;
10
10
  /**
11
- * Build `GitStatusEntry`s for the canonical paths that match the given
12
- * gitignore matcher.
13
- *
14
- * `@pierre/trees` propagates the `ignored` style from a directory entry
15
- * down to every descendant via its internal `ignoredDirectoryPaths` set, so
16
- * we only emit one entry per ignored subtree root: emitting descendants
17
- * would also pollute the library's `directoriesWithChanges` set, causing
18
- * parent directories of nested ignored items to render a "contains git
19
- * status items" dot.
20
- *
21
- * Paths are sorted lexicographically so a directory always precedes its
22
- * descendants, allowing a simple ancestor-suppression check via
23
- * `path.startsWith(dir)`. The trailing slash on canonical directory paths
24
- * (required by `@pierre/trees`, also recognized by `ignore` as the
25
- * "directory" indicator) keeps the prefix check from accidentally matching
26
- * sibling paths that happen to share a name prefix (e.g. `node_modules_old`
27
- * vs `node_modules/`).
11
+ * One `GitStatusEntry` per ignored subtree root: descendant entries would
12
+ * pollute the tree's `directoriesWithChanges` set (bogus dots on parents),
13
+ * and `ignored` already propagates downward. The trailing slash on directory
14
+ * paths keeps the sorted `startsWith` ancestor check off name-prefix siblings.
28
15
  */
29
16
  export declare function buildIgnoredEntries(matcher: Ignore, canonicalPaths: Iterable<string>): GitStatusEntry[];
@@ -1,9 +1,4 @@
1
1
  import ignore from 'ignore';
2
- /**
3
- * The path of the workspace `.gitignore` file. Loaded relative to the
4
- * Jupyter contents root, which matches the directory the gitignore patterns
5
- * are evaluated against — patterns and tested paths share the same base.
6
- */
7
2
  const GITIGNORE_PATH = '.gitignore';
8
3
  /**
9
4
  * Load the workspace `.gitignore` and return an `ignore` matcher built from
@@ -20,9 +15,6 @@ export async function loadGitignoreMatcher(contentsManager) {
20
15
  });
21
16
  }
22
17
  catch (_a) {
23
- // The most common failure here is `.gitignore` simply not existing.
24
- // Other transient errors (permission denied, server unreachable) are
25
- // also treated as "no matcher" so the file browser still works.
26
18
  return null;
27
19
  }
28
20
  if (model.type !== 'file' || typeof model.content !== 'string') {
@@ -34,23 +26,10 @@ export async function loadGitignoreMatcher(contentsManager) {
34
26
  return ignore().add(model.content);
35
27
  }
36
28
  /**
37
- * Build `GitStatusEntry`s for the canonical paths that match the given
38
- * gitignore matcher.
39
- *
40
- * `@pierre/trees` propagates the `ignored` style from a directory entry
41
- * down to every descendant via its internal `ignoredDirectoryPaths` set, so
42
- * we only emit one entry per ignored subtree root: emitting descendants
43
- * would also pollute the library's `directoriesWithChanges` set, causing
44
- * parent directories of nested ignored items to render a "contains git
45
- * status items" dot.
46
- *
47
- * Paths are sorted lexicographically so a directory always precedes its
48
- * descendants, allowing a simple ancestor-suppression check via
49
- * `path.startsWith(dir)`. The trailing slash on canonical directory paths
50
- * (required by `@pierre/trees`, also recognized by `ignore` as the
51
- * "directory" indicator) keeps the prefix check from accidentally matching
52
- * sibling paths that happen to share a name prefix (e.g. `node_modules_old`
53
- * vs `node_modules/`).
29
+ * One `GitStatusEntry` per ignored subtree root: descendant entries would
30
+ * pollute the tree's `directoriesWithChanges` set (bogus dots on parents),
31
+ * and `ignored` already propagates downward. The trailing slash on directory
32
+ * paths keeps the sorted `startsWith` ancestor check off name-prefix siblings.
54
33
  */
55
34
  export function buildIgnoredEntries(matcher, canonicalPaths) {
56
35
  const sorted = Array.from(canonicalPaths).sort();
@@ -1,26 +1,16 @@
1
1
  import { LabIcon } from '@jupyterlab/ui-components';
2
2
  import type { FileTreeIconConfig } from '@pierre/trees';
3
3
  /**
4
- * The glyph used as the xtralab file browser's sidebar tab icon. Defined as a
5
- * `LabIcon` (rather than the default `folderIcon` from
6
- * `@jupyterlab/ui-components`) so the xtralab browser is visually distinguishable
7
- * from JupyterLab's built-in file browser, which also lives in the left
8
- * sidebar.
9
- *
10
- * Source: `file-tree` from SVG Repo (https://www.svgrepo.com/svg/371275/file-tree).
11
- * The original `fill="#444"` is replaced with `fill="currentColor"` and the
12
- * root element is given the `jp-icon3` class so the icon picks up JupyterLab's
13
- * theme color and the sidebar's inverse-color painting automatically.
4
+ * Sidebar tab icon, distinct from the core `folderIcon` so the xtralab
5
+ * browser is distinguishable from the built-in one. `file-tree` from SVG Repo
6
+ * (https://www.svgrepo.com/svg/371275/file-tree), refit to follow the theme
7
+ * (`currentColor`, `jp-icon3`) and padded to the size of the stock glyphs.
14
8
  */
15
9
  export declare const xtralabFileBrowserIcon: LabIcon;
16
10
  /**
17
- * Icon configuration for the xtralab file browser. Keeps the default `complete`
18
- * built-in icon set (the colored language icons) and layers a Jupyter notebook
19
- * symbol on top so `.ipynb` files render with the JupyterLab notebook glyph.
20
- *
21
- * `set` must be set explicitly: when a config object includes any custom
22
- * overrides without `set`, `@pierre/trees` defaults `set` to `'none'`, which
23
- * disables the built-in icons entirely.
11
+ * Icon config: the built-in `complete` set plus the Jupyter notebook symbol
12
+ * for `.ipynb`. `set` must be explicit — with any custom overrides present,
13
+ * `@pierre/trees` defaults it to `'none'`, disabling the built-in icons.
24
14
  */
25
15
  export declare const FILE_BROWSER_ICONS: FileTreeIconConfig;
26
16
  /**
@@ -2,36 +2,22 @@ var _a;
2
2
  import { LabIcon, fileIcon } from '@jupyterlab/ui-components';
3
3
  import { createFileTreeIconResolver, getBuiltInSpriteSheet } from '@pierre/trees';
4
4
  /**
5
- * The glyph used as the xtralab file browser's sidebar tab icon. Defined as a
6
- * `LabIcon` (rather than the default `folderIcon` from
7
- * `@jupyterlab/ui-components`) so the xtralab browser is visually distinguishable
8
- * from JupyterLab's built-in file browser, which also lives in the left
9
- * sidebar.
10
- *
11
- * Source: `file-tree` from SVG Repo (https://www.svgrepo.com/svg/371275/file-tree).
12
- * The original `fill="#444"` is replaced with `fill="currentColor"` and the
13
- * root element is given the `jp-icon3` class so the icon picks up JupyterLab's
14
- * theme color and the sidebar's inverse-color painting automatically.
5
+ * Sidebar tab icon, distinct from the core `folderIcon` so the xtralab
6
+ * browser is distinguishable from the built-in one. `file-tree` from SVG Repo
7
+ * (https://www.svgrepo.com/svg/371275/file-tree), refit to follow the theme
8
+ * (`currentColor`, `jp-icon3`) and padded to the size of the stock glyphs.
15
9
  */
16
10
  export const xtralabFileBrowserIcon = new LabIcon({
17
11
  name: 'xtralab:file-browser',
18
- svgstr: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="jp-icon3" fill="currentColor">
12
+ svgstr: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2 20 20" class="jp-icon3" fill="currentColor">
19
13
  <path d="M16 10v-4h-11v1h-2v-3h9v-4h-12v4h2v10h3v2h11v-4h-11v1h-2v-5h2v2z"/>
20
14
  </svg>`
21
15
  });
22
16
  /**
23
- * Sprite sheet injected into the `@pierre/trees` shadow DOM. Contains a single
24
- * `<symbol>` for the Jupyter notebook icon, drawn from the JupyterLab core
25
- * notebook icon (`@jupyterlab/ui-components/style/icons/filetype/notebook.svg`).
26
- *
27
- * `@pierre/trees` does not ship a Jupyter token, so `.ipynb` files would
28
- * otherwise fall through to the generic file icon. The symbol id below is
29
- * referenced from `byFileExtension` in {@link FILE_BROWSER_ICONS}.
30
- *
31
- * The orange `#EF6C00` fill is set as a presentation attribute on the inner
32
- * `<g>` so it survives the `fill: currentColor` rule the tree applies on the
33
- * icon container — the inherited color does not override a presentation
34
- * attribute set directly on a child.
17
+ * Sprite sheet injected into the tree's shadow DOM: the JupyterLab notebook
18
+ * glyph, since `@pierre/trees` ships no Jupyter token. The `#EF6C00` fill is
19
+ * a presentation attribute on the inner `<g>` so it survives the tree's
20
+ * `fill: currentColor` rule on the icon container.
35
21
  */
36
22
  const JUPYTER_NOTEBOOK_SPRITE_SHEET = `<svg data-icon-sprite aria-hidden="true" width="0" height="0">
37
23
  <symbol id="jupyter-notebook" viewBox="0 0 22 22">
@@ -42,13 +28,9 @@ const JUPYTER_NOTEBOOK_SPRITE_SHEET = `<svg data-icon-sprite aria-hidden="true"
42
28
  </symbol>
43
29
  </svg>`;
44
30
  /**
45
- * Icon configuration for the xtralab file browser. Keeps the default `complete`
46
- * built-in icon set (the colored language icons) and layers a Jupyter notebook
47
- * symbol on top so `.ipynb` files render with the JupyterLab notebook glyph.
48
- *
49
- * `set` must be set explicitly: when a config object includes any custom
50
- * overrides without `set`, `@pierre/trees` defaults `set` to `'none'`, which
51
- * disables the built-in icons entirely.
31
+ * Icon config: the built-in `complete` set plus the Jupyter notebook symbol
32
+ * for `.ipynb`. `set` must be explicit — with any custom overrides present,
33
+ * `@pierre/trees` defaults it to `'none'`, disabling the built-in icons.
52
34
  */
53
35
  export const FILE_BROWSER_ICONS = {
54
36
  set: 'complete',
@@ -58,39 +40,19 @@ export const FILE_BROWSER_ICONS = {
58
40
  ipynb: 'jupyter-notebook'
59
41
  }
60
42
  };
61
- /**
62
- * Concatenated sprite-sheet text. Combines the built-in sheet for the active
63
- * icon set with our custom Jupyter notebook sheet so {@link extractSymbol}
64
- * can find the body of any symbol the tree itself can render.
65
- */
66
43
  const COMBINED_SPRITE_SHEETS = [
67
44
  getBuiltInSpriteSheet((_a = FILE_BROWSER_ICONS.set) !== null && _a !== void 0 ? _a : 'complete'),
68
45
  JUPYTER_NOTEBOOK_SPRITE_SHEET
69
46
  ].join('\n');
70
- /**
71
- * Resolver instance reused across calls to {@link getTreeIcon}. The resolver
72
- * is pure relative to the icon config, so building it once at module load
73
- * avoids re-walking the icon rules on every diff that opens.
74
- */
75
47
  const treeIconResolver = createFileTreeIconResolver(FILE_BROWSER_ICONS);
76
48
  /**
77
- * Cache of LabIcons keyed by the resolved sprite symbol id (e.g.
78
- * `file-tree-builtin-python`). All Python files share the same icon, so
79
- * caching by symbol id prevents repeated LabIcon registration warnings and
80
- * keeps the JupyterLab icon registry small.
49
+ * LabIcons keyed by resolved symbol id — re-registering a LabIcon under the
50
+ * same name logs warnings.
81
51
  */
82
52
  const TREE_ICON_CACHE = new Map();
83
- /**
84
- * Extract the inner SVG markup and viewBox of the `<symbol>` with the given
85
- * id from {@link COMBINED_SPRITE_SHEETS}. Returns `null` if no matching
86
- * symbol exists, in which case the caller falls back to JupyterLab's default
87
- * file icon.
88
- */
89
53
  function extractSymbol(symbolId) {
90
54
  var _a;
91
- // Match a `<symbol>` whose `id` attribute equals `symbolId` (allowing other
92
- // attributes in any order). The `[\s\S]*?` body is non-greedy so we stop
93
- // at the first matching `</symbol>` even when multiple symbols sit
55
+ // Non-greedy body: stop at the first `</symbol>` when symbols sit
94
56
  // back-to-back in the sheet.
95
57
  const escaped = symbolId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
96
58
  const symbolRegex = new RegExp(`<symbol\\b[^>]*\\bid="${escaped}"[^>]*>([\\s\\S]*?)</symbol>`, 'i');
@@ -105,14 +67,10 @@ function extractSymbol(symbolId) {
105
67
  };
106
68
  }
107
69
  /**
108
- * Fallback colors for the built-in file icons, keyed by the icon token
109
- * `@pierre/trees` resolves for a path (e.g. `python`, `typescript`).
110
- *
111
- * The tree only exposes these as `--trees-file-icon-color-<token>` CSS
112
- * variables scoped to its shadow host, so they resolve to nothing for the
113
- * standalone {@link LabIcon}s {@link getTreeIcon} builds for main-area tabs.
114
- * Mirroring them here lets {@link getBuiltInFileIconColor} bottom out in a
115
- * real color. Each is a `light-dark(…)` pair for dark themes.
70
+ * Fallback colors keyed by the tree's icon token. The tree exposes its colors
71
+ * only as `--trees-file-icon-color-*` vars scoped to its shadow host, so the
72
+ * standalone LabIcons built for main-area tabs need these literals;
73
+ * `light-dark(…)` covers dark themes.
116
74
  */
117
75
  const BUILT_IN_FILE_ICON_COLOR_FALLBACKS = {
118
76
  astro: 'light-dark(#a631be, #d568ea)',
@@ -167,11 +125,8 @@ const BUILT_IN_FILE_ICON_COLOR_FALLBACKS = {
167
125
  zip: 'light-dark(#d47628, #ffa359)'
168
126
  };
169
127
  /**
170
- * Color the file tree would paint for the built-in icon `token`, as a
171
- * self-contained CSS value usable outside the tree's shadow DOM: the tree's
172
- * `--trees-file-icon-color-*` properties when in scope, else the literal from
173
- * {@link BUILT_IN_FILE_ICON_COLOR_FALLBACKS}. Returns `undefined` for unknown
174
- * tokens.
128
+ * The tree's color for `token` as a self-contained CSS value: its CSS vars
129
+ * when in scope, else the mirrored literal. `undefined` for unknown tokens.
175
130
  */
176
131
  function getBuiltInFileIconColor(token) {
177
132
  const fallback = BUILT_IN_FILE_ICON_COLOR_FALLBACKS[token];
@@ -180,9 +135,6 @@ function getBuiltInFileIconColor(token) {
180
135
  }
181
136
  return `var(--trees-file-icon-color-${token}, var(--trees-file-icon-color, ${fallback}))`;
182
137
  }
183
- /**
184
- * Resolve a tree glyph into a standalone `LabIcon`.
185
- */
186
138
  function resolveTreeIcon(filePath) {
187
139
  var _a;
188
140
  const resolved = treeIconResolver.resolveIcon('file-tree-icon-file', filePath);
@@ -196,22 +148,15 @@ function resolveTreeIcon(filePath) {
196
148
  if (symbol === null) {
197
149
  return { icon: null, specific };
198
150
  }
199
- // Built-in language icons paint with `fill="currentColor"` so the colored
200
- // tier of the tree can recolor them by setting `color` on the host
201
- // element. Reproduce that here by setting the same color value as an
202
- // inline `style="color: …"` on the outer `<svg>`. `getBuiltInFileIconColor`
203
- // returns a `var(--trees-…)` chain that bottoms out in `light-dark(…)`,
204
- // which respects the page's `color-scheme` declaration so dark themes pick
205
- // up the brighter variants automatically.
151
+ // Built-in glyphs paint with `fill="currentColor"`; an inline `color`
152
+ // style reproduces the tree's colored tier outside its shadow DOM.
206
153
  const colored = FILE_BROWSER_ICONS.colored === true;
207
154
  const color = colored && resolved.token !== undefined
208
155
  ? getBuiltInFileIconColor(resolved.token)
209
156
  : undefined;
210
157
  const styleAttr = color !== undefined ? ` style="color: ${color};"` : '';
211
- // Prefer a viewBox the resolver supplies (custom sprite overrides may set
212
- // one), then fall back to the symbol's own viewBox so 22x22 sprites like
213
- // the Jupyter notebook glyph render at the right scale rather than being
214
- // cropped to a 16x16 box.
158
+ // Prefer the resolver's viewBox, then the symbol's own, so 22x22 sprites
159
+ // like the notebook glyph aren't cropped to a 16x16 box.
215
160
  const viewBox = (_a = resolved.viewBox) !== null && _a !== void 0 ? _a : symbol.viewBox;
216
161
  const svgstr = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox}"${styleAttr}>${symbol.inner}</svg>`;
217
162
  const icon = new LabIcon({
@@ -1,10 +1,10 @@
1
1
  import { ILayoutRestorer } from '@jupyterlab/application';
2
- import { ICommandPalette } from '@jupyterlab/apputils';
2
+ import { ICommandPalette, IMovableSectionRegistry } from '@jupyterlab/apputils';
3
3
  import { IDocumentManager } from '@jupyterlab/docmanager';
4
4
  import { ISettingRegistry } from '@jupyterlab/settingregistry';
5
- import { ITranslator } from '@jupyterlab/translation';
5
+ import { ITranslator, nullTranslator } from '@jupyterlab/translation';
6
6
  import { populateToolbar, registerCommands } from './commands';
7
- import { XtralabFileBrowser } from './widget';
7
+ import { FILE_BROWSER_ID, XtralabFileBrowser } from './widget';
8
8
  const PLUGIN_ID = 'xtralab:plugin';
9
9
  /**
10
10
  * The file browser plugin. Adds a `@pierre/trees`-powered file browser to the
@@ -15,25 +15,56 @@ const plugin = {
15
15
  description: 'A path-first file browser for JupyterLab built on @pierre/trees.',
16
16
  autoStart: true,
17
17
  requires: [IDocumentManager],
18
- optional: [ILayoutRestorer, ISettingRegistry, ICommandPalette, ITranslator],
19
- activate: (app, docManager, restorer, settingRegistry, palette, translator) => {
18
+ optional: [
19
+ ILayoutRestorer,
20
+ ISettingRegistry,
21
+ ICommandPalette,
22
+ ITranslator,
23
+ IMovableSectionRegistry
24
+ ],
25
+ activate: (app, docManager, restorer, settingRegistry, palette, translator, movableSections) => {
20
26
  const browser = new XtralabFileBrowser({
21
27
  contentsManager: app.serviceManager.contents,
22
28
  docManager,
23
29
  onOpenFile: (serverPath) => {
24
30
  void app.commands.execute('docmanager:open', { path: serverPath });
25
- }
31
+ },
32
+ translator: translator !== null && translator !== void 0 ? translator : undefined
26
33
  });
27
- // Ranks just under the Terminals panel in the left sidebar. The shipped
28
- // `layout` setting assigns the default ranks (rank only, no area pin),
29
- // so this browser stays movable via the "Move Widget" context menu; the
30
- // `rank` here is the in-code fallback for when that setting is absent.
34
+ // Rank-only fallback for when the shipped `layout` setting is absent;
35
+ // no area pin, so the browser stays movable via "Move Widget".
31
36
  app.shell.add(browser, 'left', { rank: 2 });
32
37
  if (restorer) {
33
38
  restorer.add(browser, browser.id);
34
39
  }
35
40
  registerCommands({ app, browser, docManager, palette, translator });
36
41
  populateToolbar({ app, browser });
42
+ if (movableSections) {
43
+ const label = (translator !== null && translator !== void 0 ? translator : nullTranslator)
44
+ .load('jupyterlab')
45
+ .__('Files');
46
+ movableSections.registerSource(FILE_BROWSER_ID, label, browser);
47
+ movableSections.registerTarget(FILE_BROWSER_ID, label, browser);
48
+ // Only a user move should switch the sidebar, not the startup restoration.
49
+ let settled = false;
50
+ void app.restored.then(() => {
51
+ settled = true;
52
+ });
53
+ browser.contentChanged.connect(() => {
54
+ if (browser.isEmpty) {
55
+ if (browser.parent) {
56
+ browser.parent = null;
57
+ }
58
+ }
59
+ else if (!browser.parent) {
60
+ app.shell.add(browser, 'left', { rank: 2 });
61
+ if (settled) {
62
+ app.shell.activateById(browser.id);
63
+ }
64
+ }
65
+ });
66
+ browser.announceSections();
67
+ }
37
68
  if (settingRegistry) {
38
69
  settingRegistry.load(PLUGIN_ID).catch(reason => {
39
70
  console.error('Failed to load settings for xtralab.', reason);