xtralab 0.7.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 (54) hide show
  1. package/README.md +18 -10
  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 -284
  16. package/lib/git/diffSurface.d.ts +64 -25
  17. package/lib/git/diffSurface.js +84 -27
  18. package/lib/git/diffTheme.d.ts +9 -0
  19. package/lib/git/diffTheme.js +31 -0
  20. package/lib/git/diffWidget.d.ts +67 -85
  21. package/lib/git/diffWidget.js +242 -318
  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 -409
  41. package/src/git/diffSurface.tsx +187 -35
  42. package/src/git/diffTheme.ts +40 -0
  43. package/src/git/diffWidget.tsx +338 -393
  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/base.css +1 -0
  50. package/style/git.css +40 -12
  51. package/style/index.css +1 -0
  52. package/style/index.js +1 -0
  53. package/style/launcher.css +46 -18
  54. 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/base.css CHANGED
@@ -21,6 +21,7 @@
21
21
  --trees-bg-muted-override: var(--jp-layout-color2);
22
22
  --trees-fg-override: var(--jp-ui-font-color1);
23
23
  --trees-fg-muted-override: var(--jp-ui-font-color2);
24
+ --trees-status-ignored-override: var(--jp-ui-font-color2);
24
25
  --trees-accent-override: var(--jp-brand-color1);
25
26
  --trees-border-color-override: var(--jp-border-color2);
26
27
  --trees-indent-guide-bg-override: var(--jp-border-color3);
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
  /*
@@ -79,16 +76,25 @@
79
76
  }
80
77
 
81
78
  /*
82
- * Notebook/JSON view-mode selector that lives in the `MainAreaWidget`
83
- * toolbar for `.ipynb` diffs. The host toolbar provides its own
84
- * background and border, so this is just the segmented control itself.
79
+ * Segmented selectors that live in the diff toolbar: the Notebook/JSON
80
+ * view-mode toggle (for `.ipynb` diffs) and the Split/Unified diff-style
81
+ * toggle (for textual/code diffs). The host toolbar provides its own
82
+ * background and border, so these are just the segmented controls.
85
83
  */
86
- .jp-xtralab-DiffWidget-viewModeToolbarItem {
84
+ .jp-xtralab-DiffWidget-viewModeToolbarItem,
85
+ .jp-xtralab-DiffWidget-diffStyleToolbarItem {
87
86
  display: flex;
88
87
  align-items: center;
89
88
  font-size: var(--jp-ui-font-size0);
90
89
  }
91
90
 
91
+ /*
92
+ * Breathing room when both toggles appear (notebook diff in JSON view).
93
+ */
94
+ .jp-xtralab-DiffWidget-diffStyleToolbarItem {
95
+ margin-left: 4px;
96
+ }
97
+
92
98
  .jp-xtralab-DiffWidget-segmented {
93
99
  display: inline-flex;
94
100
  border: 1px solid var(--jp-border-color2);
@@ -120,6 +126,24 @@
120
126
  background-color: var(--jp-brand-color1);
121
127
  }
122
128
 
129
+ /*
130
+ * Icon variant of the segmented button, used by the Split/Unified toggle.
131
+ * The inline SVGs fill with `currentColor`, so the active/hover color rules
132
+ * above carry over to the glyph.
133
+ */
134
+ .jp-xtralab-DiffWidget-segmentedButton-icon {
135
+ display: inline-flex;
136
+ align-items: center;
137
+ justify-content: center;
138
+ padding: 3px 8px;
139
+ }
140
+
141
+ .jp-xtralab-DiffWidget-segmentedButton-icon svg {
142
+ display: block;
143
+ width: 14px;
144
+ height: 14px;
145
+ }
146
+
123
147
  /*
124
148
  * Draggable column splitter. Sits on top of the diff (z-index above the
125
149
  * content) but stays a thin strip so it doesn't visually intrude. The
@@ -564,7 +588,9 @@
564
588
 
565
589
  .jp-xtralab-ImageDiff-frame,
566
590
  .jp-xtralab-ImageDiff-stack {
567
- /* Checkerboard so transparent regions are obvious; theme-neutral. */
591
+ /*
592
+ * Checkerboard so transparent regions are obvious; theme-neutral.
593
+ */
568
594
  background-color: #fff;
569
595
  background-image:
570
596
  linear-gradient(45deg, #c8c8c8 25%, transparent 25%),
@@ -741,7 +767,9 @@
741
767
  bottom: -14px;
742
768
  }
743
769
 
744
- /* Chevron triangles drawn with borders so no asset is needed. */
770
+ /*
771
+ * Chevron triangles drawn with borders so no asset is needed.
772
+ */
745
773
  .jp-xtralab-ImageDiff-swipeDividerArrow {
746
774
  width: 0;
747
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
+ }