xtralab 0.8.0 → 0.9.0

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 (53) hide show
  1. package/README.md +13 -6
  2. package/lib/fileBrowser/commands.d.ts +7 -0
  3. package/lib/fileBrowser/commands.js +74 -1
  4. package/lib/fileBrowser/fileBrowser.js +4 -1
  5. package/lib/fileBrowser/icons.d.ts +5 -9
  6. package/lib/fileBrowser/icons.js +97 -13
  7. package/lib/fileBrowser/index.js +5 -3
  8. package/lib/fileTypeIcons/index.d.ts +6 -0
  9. package/lib/fileTypeIcons/index.js +144 -0
  10. package/lib/git/commands.d.ts +34 -33
  11. package/lib/git/commands.js +132 -10
  12. package/lib/git/diffModel.d.ts +14 -0
  13. package/lib/git/diffModel.js +75 -0
  14. package/lib/git/diffProvider.d.ts +0 -5
  15. package/lib/git/diffProvider.js +6 -376
  16. package/lib/git/diffSurface.d.ts +36 -26
  17. package/lib/git/diffSurface.js +13 -20
  18. package/lib/git/diffTheme.d.ts +9 -0
  19. package/lib/git/diffTheme.js +31 -0
  20. package/lib/git/diffWidget.d.ts +54 -91
  21. package/lib/git/diffWidget.js +184 -361
  22. package/lib/git/index.d.ts +1 -6
  23. package/lib/git/index.js +7 -23
  24. package/lib/git/notebookDiff.d.ts +6 -1
  25. package/lib/git/notebookDiff.js +4 -3
  26. package/lib/index.js +2 -0
  27. package/lib/terminals/model.d.ts +1 -1
  28. package/lib/terminals/model.js +1 -1
  29. package/lib/terminals/widget.d.ts +1 -1
  30. package/lib/terminals/widget.js +1 -1
  31. package/package.json +3 -3
  32. package/schema/plugin.json +6 -0
  33. package/src/fileBrowser/commands.ts +91 -2
  34. package/src/fileBrowser/fileBrowser.tsx +4 -1
  35. package/src/fileBrowser/icons.ts +103 -13
  36. package/src/fileBrowser/index.ts +9 -3
  37. package/src/fileTypeIcons/index.ts +174 -0
  38. package/src/git/commands.ts +177 -43
  39. package/src/git/diffModel.ts +102 -0
  40. package/src/git/diffProvider.tsx +8 -540
  41. package/src/git/diffSurface.tsx +44 -28
  42. package/src/git/diffTheme.ts +40 -0
  43. package/src/git/diffWidget.tsx +256 -448
  44. package/src/git/index.ts +14 -28
  45. package/src/git/notebookDiff.tsx +10 -3
  46. package/src/index.ts +2 -0
  47. package/src/terminals/model.ts +1 -1
  48. package/src/terminals/widget.tsx +1 -1
  49. package/style/git.css +12 -9
  50. package/style/index.css +1 -0
  51. package/style/index.js +1 -0
  52. package/style/launcher.css +46 -18
  53. package/style/sidebar.css +14 -0
package/src/git/index.ts CHANGED
@@ -5,14 +5,15 @@ import {
5
5
  import { IThemeManager, WidgetTracker } from '@jupyterlab/apputils';
6
6
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
7
7
 
8
- import { CommandArguments, CommandIDs, registerGitCommands } from './commands';
9
8
  import {
10
- DiffContentWidget,
9
+ CommandArguments,
10
+ CommandIDs,
11
+ DiffMainAreaWidget,
11
12
  PREVIEW_DIFF_WIDGET_ID,
12
- pinnedDiffWidgetId
13
- } from './diffWidget';
13
+ pinnedDiffWidgetId,
14
+ registerGitCommands
15
+ } from './commands';
14
16
  import diffProviderPlugin from './diffProvider';
15
- import type { MainAreaWidget } from '@jupyterlab/apputils';
16
17
  import { IFileChange } from './tokens';
17
18
 
18
19
  const GIT_DIFF_COMMAND_PLUGIN_ID = 'xtralab:git-diff-command';
@@ -21,12 +22,8 @@ const GIT_DIFF_TRACKER_NAMESPACE = 'xtralab-git-diff';
21
22
  /**
22
23
  * The launcher's git diff command plugin.
23
24
  *
24
- * The `@jupyterlab/git` frontend stays enabled (only its diff plugins are
25
- * swapped — see `diffProvider.tsx`), so the upstream git panel is what the
26
- * user sees in the sidebar. This plugin is the launcher dashboard's
27
- * independent diff path: the `xtralab:git:open-diff` command the
28
- * dashboard's "Changes" section calls, plus a tracker so reopening a diff
29
- * reveals the existing tab and the preview/pin behavior keeps working.
25
+ * The upstream git panel stays enabled; this plugin only gives the launcher
26
+ * its own `xtralab:git:open-diff` command and preview/pinned-tab tracking.
30
27
  */
31
28
  const diffCommandPlugin: JupyterFrontEndPlugin<void> = {
32
29
  id: GIT_DIFF_COMMAND_PLUGIN_ID,
@@ -39,17 +36,15 @@ const diffCommandPlugin: JupyterFrontEndPlugin<void> = {
39
36
  themeManager: IThemeManager | null,
40
37
  rendermime: IRenderMimeRegistry | null
41
38
  ): void => {
42
- // Track open diff widgets so the openDiff handler can reveal an
43
- // already-open diff for the same file instead of creating a duplicate
44
- // (and so a promoted preview can find its pinned twin).
45
- const tracker = new WidgetTracker<MainAreaWidget<DiffContentWidget>>({
39
+ // Track open diffs so repeated opens reveal the existing tab.
40
+ const tracker = new WidgetTracker<DiffMainAreaWidget>({
46
41
  namespace: GIT_DIFF_TRACKER_NAMESPACE
47
42
  });
48
43
 
49
44
  const findDiff = (
50
45
  change: IFileChange,
51
46
  pin = false
52
- ): MainAreaWidget<DiffContentWidget> | undefined => {
47
+ ): DiffMainAreaWidget | undefined => {
53
48
  const id = pin ? pinnedDiffWidgetId(change) : PREVIEW_DIFF_WIDGET_ID;
54
49
  const existing = tracker.find(
55
50
  widget => !widget.isDisposed && widget.id === id
@@ -62,13 +57,9 @@ const diffCommandPlugin: JupyterFrontEndPlugin<void> = {
62
57
  themeManager,
63
58
  contentsManager: app.serviceManager.contents,
64
59
  rendermime,
65
- // The launcher polls git status on its own cadence and the
66
- // jupyterlab-git panel auto-refreshes on the contents `fileChanged`
67
- // signal a hunk-discard save emits, so no extra refresh is needed.
68
- onChanged: () => undefined,
69
60
  trackDiff: widget => tracker.add(widget),
70
61
  onPinned: current => {
71
- const existing = findDiff(current.content.change, true);
62
+ const existing = findDiff(current.change, true);
72
63
  if (
73
64
  existing !== undefined &&
74
65
  existing !== current &&
@@ -78,7 +69,7 @@ const diffCommandPlugin: JupyterFrontEndPlugin<void> = {
78
69
  current.close();
79
70
  return;
80
71
  }
81
- current.id = pinnedDiffWidgetId(current.content.change);
72
+ current.id = pinnedDiffWidgetId(current.change);
82
73
  current.title.className = '';
83
74
  app.shell.activateById(current.id);
84
75
  },
@@ -88,12 +79,7 @@ const diffCommandPlugin: JupyterFrontEndPlugin<void> = {
88
79
  };
89
80
 
90
81
  /**
91
- * Every plugin contributed by xtralab's git integration:
92
- *
93
- * 1. {@link diffCommandPlugin} — the launcher's `xtralab:git:open-diff`.
94
- * 2. `diffProviderPlugin` — registers the `@pierre/diffs` rendering as
95
- * jupyterlab-git's diff providers (replacing its disabled
96
- * notebook/text diff plugins).
82
+ * Plugins contributed by xtralab's git integration.
97
83
  */
98
84
  const plugins: JupyterFrontEndPlugin<unknown>[] = [
99
85
  diffCommandPlugin,
@@ -13,6 +13,8 @@ import {
13
13
  type FileDiffMetadata
14
14
  } from '@pierre/diffs';
15
15
 
16
+ import { resolveDiffTheme } from './diffTheme';
17
+
16
18
  /**
17
19
  * The CSS class added to the root of the notebook diff. Selectors that
18
20
  * style cell sections, headers, and the per-cell diff blocks hang off this
@@ -151,7 +153,7 @@ function emptyNotebook(): INotebook {
151
153
  * nbformat allows multiline string fields (`source`, stream `text`,
152
154
  * `text/plain` outputs, …) to be either a single string or an array of
153
155
  * strings. Normalize to one string so equality checks and diffs see the
154
- * same shape regardless of how the writer chose to serialise it.
156
+ * same shape regardless of how the writer chose to serialize it.
155
157
  */
156
158
  export function joinMultiline(value: string | string[] | undefined): string {
157
159
  if (value === undefined) {
@@ -836,6 +838,11 @@ function MarkdownPreviewSection(props: {
836
838
  interface INotebookDiffViewProps {
837
839
  diff: INotebookDiffResult;
838
840
  dark: boolean;
841
+ /**
842
+ * Whether a Pierre JupyterLab theme is active; selects Pierre's rich
843
+ * highlighting vs the CSS-variable theme (see {@link resolveDiffTheme}).
844
+ */
845
+ pierreTheme: boolean;
839
846
  rendermime: IRenderMimeRegistry | null;
840
847
  }
841
848
 
@@ -852,8 +859,8 @@ interface INotebookDiffViewProps {
852
859
  export function NotebookDiffView(
853
860
  props: INotebookDiffViewProps
854
861
  ): React.ReactElement {
855
- const { diff, dark, rendermime } = props;
856
- const theme: DiffsThemeNames = dark ? 'pierre-dark' : 'pierre-light';
862
+ const { diff, dark, pierreTheme, rendermime } = props;
863
+ const theme: DiffsThemeNames = resolveDiffTheme(dark, pierreTheme);
857
864
  const summary = React.useMemo(() => summarize(diff.cells), [diff.cells]);
858
865
  return (
859
866
  <div className={NOTEBOOK_DIFF_CSS_CLASS}>
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@ import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
2
 
3
3
  import agentSessionsPlugin from './agentSessions';
4
4
  import fileBrowserPlugin from './fileBrowser';
5
+ import fileTypeIconsPlugin from './fileTypeIcons';
5
6
  import editorBreadcrumbsPlugin from './editorBreadcrumbs';
6
7
  import editorIndentPlugin from './editorIndent';
7
8
  import gitPlugins from './git';
@@ -25,6 +26,7 @@ const plugins: JupyterFrontEndPlugin<unknown>[] = [
25
26
  editorBreadcrumbsPlugin,
26
27
  editorIndentPlugin,
27
28
  fileBrowserPlugin,
29
+ fileTypeIconsPlugin,
28
30
  ...gitPlugins,
29
31
  ...launcherPlugins,
30
32
  menusPlugin,
@@ -194,7 +194,7 @@ export class SessionRegistry implements IDisposable {
194
194
 
195
195
  /**
196
196
  * Return the open widget for a session, if any. The panel uses this to
197
- * switch behaviour between "activate existing tab" and "open a new tab
197
+ * switch behavior between "activate existing tab" and "open a new tab
198
198
  * connected to the live session".
199
199
  */
200
200
  widgetFor(name: string): TerminalWidget | null {
@@ -32,7 +32,7 @@ export const RUNNING_TERMINALS_ID = 'xtralab-running-terminals';
32
32
  * Backed by {@link SessionRegistry}: a `UseSignal` re-renders the list
33
33
  * whenever the registry emits `stateChanged`, so it tracks
34
34
  * `runningChanged` and the agent-title cache without any local state.
35
- * Click behaviour (activate vs. reopen, shutdown, new terminal) is
35
+ * Click behavior (activate vs. reopen, shutdown, new terminal) is
36
36
  * delegated to plugin-supplied callbacks so the widget never imports
37
37
  * `app`.
38
38
  */
package/style/git.css CHANGED
@@ -40,12 +40,7 @@
40
40
  color: var(--jp-ui-font-color1);
41
41
 
42
42
  /*
43
- * Map JupyterLab's theme variables onto the `--diffs-*-override`
44
- * properties the library exposes. The `*-override` variants take
45
- * precedence over the library's defaults, so these values track the
46
- * JupyterLab theme automatically — no JS needed when the theme
47
- * switches. Theme-dependent colors (insertion green, deletion red, …)
48
- * still come from the active Shiki theme passed via `options.theme`.
43
+ * Feed JupyterLab theme colors into @pierre/diffs.
49
44
  */
50
45
  --diffs-bg-override: var(--jp-layout-color1);
51
46
  --diffs-bg-buffer-override: var(--jp-layout-color1);
@@ -58,6 +53,8 @@
58
53
  --diffs-fg-number-override: var(--jp-ui-font-color3);
59
54
  --diffs-fg-conflict-marker-override: var(--jp-warn-color1);
60
55
  --diffs-modified-color-override: var(--jp-warn-color1);
56
+ --diffs-addition-color-override: var(--jp-success-color1);
57
+ --diffs-deletion-color-override: var(--jp-error-color1);
61
58
  }
62
59
 
63
60
  /*
@@ -91,7 +88,9 @@
91
88
  font-size: var(--jp-ui-font-size0);
92
89
  }
93
90
 
94
- /* Breathing room when both toggles appear (notebook diff in JSON view). */
91
+ /*
92
+ * Breathing room when both toggles appear (notebook diff in JSON view).
93
+ */
95
94
  .jp-xtralab-DiffWidget-diffStyleToolbarItem {
96
95
  margin-left: 4px;
97
96
  }
@@ -589,7 +588,9 @@
589
588
 
590
589
  .jp-xtralab-ImageDiff-frame,
591
590
  .jp-xtralab-ImageDiff-stack {
592
- /* Checkerboard so transparent regions are obvious; theme-neutral. */
591
+ /*
592
+ * Checkerboard so transparent regions are obvious; theme-neutral.
593
+ */
593
594
  background-color: #fff;
594
595
  background-image:
595
596
  linear-gradient(45deg, #c8c8c8 25%, transparent 25%),
@@ -766,7 +767,9 @@
766
767
  bottom: -14px;
767
768
  }
768
769
 
769
- /* Chevron triangles drawn with borders so no asset is needed. */
770
+ /*
771
+ * Chevron triangles drawn with borders so no asset is needed.
772
+ */
770
773
  .jp-xtralab-ImageDiff-swipeDividerArrow {
771
774
  width: 0;
772
775
  height: 0;
package/style/index.css CHANGED
@@ -2,6 +2,7 @@
2
2
  @import url('editorBreadcrumbs.css');
3
3
  @import url('git.css');
4
4
  @import url('launcher.css');
5
+ @import url('sidebar.css');
5
6
  @import url('tabs.css');
6
7
  @import url('terminals.css');
7
8
  @import url('topBar.css');
package/style/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import './base.css';
2
2
  import './git.css';
3
3
  import './launcher.css';
4
+ import './sidebar.css';
4
5
  import './tabs.css';
5
6
  import './terminals.css';
6
7
  import './topBar.css';
@@ -166,8 +166,8 @@
166
166
 
167
167
  /*
168
168
  * Changed files list. Each row is a button so click + keyboard activate
169
- * both work; the badge on the left mirrors the single-letter status
170
- * indicator the git panel uses, kept in sync via FileChangeStatus.
169
+ * both work; the letter on the left is the file's single-letter git status,
170
+ * colored to match the @pierre/trees file tree's decorations.
171
171
  */
172
172
  .jp-xtralab-Launcher-changes {
173
173
  list-style: none;
@@ -178,6 +178,27 @@
178
178
  border: 1px solid var(--jp-border-color2);
179
179
  border-radius: 4px;
180
180
  overflow: hidden;
181
+
182
+ /*
183
+ * Git status letter palette, mirroring the @pierre/trees `--trees-status-*`
184
+ * defaults. The file tree resolves those through `light-dark()` under
185
+ * `color-scheme: light dark` — i.e. by OS appearance — so the
186
+ * `prefers-color-scheme` override below reproduces that exact resolution
187
+ * and keeps these letters in lockstep with the tree's.
188
+ */
189
+ --xtralab-git-status-added: #16a994;
190
+ --xtralab-git-status-modified: #1ca1c7;
191
+ --xtralab-git-status-renamed: #d5a910;
192
+ --xtralab-git-status-deleted: #ff2e3f;
193
+ }
194
+
195
+ @media (prefers-color-scheme: dark) {
196
+ .jp-xtralab-Launcher-changes {
197
+ --xtralab-git-status-added: #00cab1;
198
+ --xtralab-git-status-modified: #08c0ef;
199
+ --xtralab-git-status-renamed: #ffd452;
200
+ --xtralab-git-status-deleted: #ff6762;
201
+ }
181
202
  }
182
203
 
183
204
  .jp-xtralab-Launcher-changes > li + li {
@@ -207,35 +228,42 @@
207
228
  outline-offset: -1px;
208
229
  }
209
230
 
231
+ /*
232
+ * Bare colored letter, not a filled badge — the same treatment the
233
+ * @pierre/trees file tree gives its git status decorations. The fixed width
234
+ * keeps the letters in a tidy gutter so the paths stay column-aligned.
235
+ */
210
236
  .jp-xtralab-Launcher-change-badge {
211
237
  display: inline-flex;
212
238
  align-items: center;
213
239
  justify-content: center;
214
- width: 18px;
215
- height: 18px;
216
- font-size: 10px;
217
- font-weight: 700;
218
- border-radius: 3px;
240
+ width: 14px;
241
+ flex: 0 0 auto;
219
242
  font-family: var(--jp-code-font-family);
220
- background-color: var(--jp-layout-color2);
243
+ font-size: var(--jp-code-font-size);
244
+ font-weight: 600;
221
245
  color: var(--jp-ui-font-color2);
222
- flex: 0 0 auto;
223
- }
224
-
225
- .jp-xtralab-Launcher-change-modified {
226
- background-color: var(--jp-warn-color3, var(--jp-layout-color2));
227
- color: var(--jp-warn-color1, var(--jp-ui-font-color1));
228
246
  }
229
247
 
230
248
  .jp-xtralab-Launcher-change-added,
231
249
  .jp-xtralab-Launcher-change-untracked {
232
- background-color: var(--jp-success-color3, var(--jp-layout-color2));
233
- color: var(--jp-success-color1, var(--jp-ui-font-color1));
250
+ color: var(--xtralab-git-status-added);
251
+ }
252
+
253
+ /* The file tree folds unmerged/typechange/unknown onto modified; match it. */
254
+ .jp-xtralab-Launcher-change-modified,
255
+ .jp-xtralab-Launcher-change-unmerged,
256
+ .jp-xtralab-Launcher-change-typechange,
257
+ .jp-xtralab-Launcher-change-unknown {
258
+ color: var(--xtralab-git-status-modified);
259
+ }
260
+
261
+ .jp-xtralab-Launcher-change-renamed {
262
+ color: var(--xtralab-git-status-renamed);
234
263
  }
235
264
 
236
265
  .jp-xtralab-Launcher-change-deleted {
237
- background-color: var(--jp-error-color3, var(--jp-layout-color2));
238
- color: var(--jp-error-color1, var(--jp-ui-font-color1));
266
+ color: var(--xtralab-git-status-deleted);
239
267
  }
240
268
 
241
269
  .jp-xtralab-Launcher-change-path {
@@ -0,0 +1,14 @@
1
+ /*
2
+ * The side activity bar is shipped horizontally (activityBarPosition: "top",
3
+ * see jupyter-config/labconfig). In that mode the TabBar content node spans
4
+ * the full width (@jupyterlab/application/style/sidepanel.css) with no
5
+ * justify-content, so the icons sit at the leading edge. Center them under
6
+ * the menu bar instead.
7
+ *
8
+ * `safe center` keeps the row start-aligned should the icons ever exceed the
9
+ * bar width, so none are pushed past the leading edge out of reach of the
10
+ * content node's horizontal overflow scroll.
11
+ */
12
+ .jp-SideBar[data-orientation='horizontal'] .lm-TabBar-content {
13
+ justify-content: safe center;
14
+ }