xtralab 0.4.0 → 0.6.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 (96) hide show
  1. package/README.md +110 -73
  2. package/lib/agentSessions.d.ts +50 -0
  3. package/lib/agentSessions.js +45 -0
  4. package/lib/fileBrowser/index.js +5 -1
  5. package/lib/git/commands.d.ts +12 -13
  6. package/lib/git/commands.js +7 -28
  7. package/lib/git/diffProvider.d.ts +8 -0
  8. package/lib/git/diffProvider.js +314 -0
  9. package/lib/git/diffSurface.d.ts +115 -0
  10. package/lib/git/diffSurface.js +342 -0
  11. package/lib/git/diffWidget.d.ts +8 -13
  12. package/lib/git/diffWidget.js +65 -311
  13. package/lib/git/imageDiff.d.ts +23 -0
  14. package/lib/git/imageDiff.js +256 -0
  15. package/lib/git/index.d.ts +10 -8
  16. package/lib/git/index.js +37 -52
  17. package/lib/index.d.ts +4 -2
  18. package/lib/index.js +14 -8
  19. package/lib/launcher/agents.d.ts +1 -1
  20. package/lib/launcher/agents.js +22 -13
  21. package/lib/launcher/commands.d.ts +32 -6
  22. package/lib/launcher/commands.js +81 -57
  23. package/lib/launcher/dashboard.d.ts +15 -0
  24. package/lib/launcher/dashboard.js +119 -5
  25. package/lib/launcher/editorRegistry.d.ts +43 -0
  26. package/lib/launcher/editorRegistry.js +96 -0
  27. package/lib/launcher/editors.d.ts +78 -0
  28. package/lib/launcher/editors.js +110 -0
  29. package/lib/launcher/icons.d.ts +25 -6
  30. package/lib/launcher/icons.js +116 -19
  31. package/lib/launcher/index.d.ts +2 -22
  32. package/lib/launcher/index.js +48 -25
  33. package/lib/launcher/registry.d.ts +21 -0
  34. package/lib/launcher/registry.js +28 -0
  35. package/lib/launcher/tokens.d.ts +41 -0
  36. package/lib/launcher/tokens.js +17 -0
  37. package/lib/terminals/detection.d.ts +11 -0
  38. package/lib/terminals/detection.js +50 -0
  39. package/lib/terminals/index.d.ts +27 -0
  40. package/lib/terminals/index.js +194 -0
  41. package/lib/terminals/model.d.ts +191 -0
  42. package/lib/terminals/model.js +418 -0
  43. package/lib/terminals/widget.d.ts +72 -0
  44. package/lib/terminals/widget.js +93 -0
  45. package/lib/topBar/icons.d.ts +13 -0
  46. package/lib/topBar/icons.js +27 -0
  47. package/lib/topBar/index.d.ts +16 -0
  48. package/lib/topBar/index.js +86 -0
  49. package/package.json +52 -35
  50. package/schema/launcher.json +56 -1
  51. package/src/agentSessions.ts +94 -0
  52. package/src/fileBrowser/index.ts +5 -1
  53. package/src/git/commands.ts +12 -38
  54. package/src/git/diffProvider.tsx +462 -0
  55. package/src/git/diffSurface.tsx +588 -0
  56. package/src/git/diffWidget.tsx +105 -465
  57. package/src/git/imageDiff.tsx +469 -0
  58. package/src/git/index.ts +38 -68
  59. package/src/index.ts +14 -8
  60. package/src/launcher/agents.ts +23 -14
  61. package/src/launcher/commands.ts +98 -60
  62. package/src/launcher/dashboard.tsx +205 -3
  63. package/src/launcher/editorRegistry.ts +149 -0
  64. package/src/launcher/editors.ts +183 -0
  65. package/src/launcher/icons.ts +120 -20
  66. package/src/launcher/index.ts +62 -27
  67. package/src/launcher/registry.ts +33 -0
  68. package/src/launcher/tokens.ts +51 -0
  69. package/src/terminals/detection.ts +65 -0
  70. package/src/terminals/index.ts +222 -0
  71. package/src/terminals/model.ts +497 -0
  72. package/src/terminals/widget.tsx +240 -0
  73. package/src/topBar/icons.ts +30 -0
  74. package/src/topBar/index.ts +121 -0
  75. package/style/git.css +267 -266
  76. package/style/index.css +2 -1
  77. package/style/index.js +2 -1
  78. package/style/launcher.css +124 -2
  79. package/style/terminals.css +235 -0
  80. package/style/topBar.css +38 -0
  81. package/lib/git/icons.d.ts +0 -14
  82. package/lib/git/icons.js +0 -19
  83. package/lib/git/panel.d.ts +0 -29
  84. package/lib/git/panel.js +0 -42
  85. package/lib/git/panelComponent.d.ts +0 -28
  86. package/lib/git/panelComponent.js +0 -320
  87. package/lib/statusBar/index.d.ts +0 -14
  88. package/lib/statusBar/index.js +0 -89
  89. package/lib/statusBar/widget.d.ts +0 -121
  90. package/lib/statusBar/widget.js +0 -237
  91. package/src/git/icons.ts +0 -20
  92. package/src/git/panel.tsx +0 -56
  93. package/src/git/panelComponent.tsx +0 -575
  94. package/src/statusBar/index.ts +0 -115
  95. package/src/statusBar/widget.tsx +0 -318
  96. package/style/statusBar.css +0 -95
@@ -0,0 +1,314 @@
1
+ import * as React from 'react';
2
+ import { IThemeManager } from '@jupyterlab/apputils';
3
+ import { PathExt } from '@jupyterlab/coreutils';
4
+ import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
5
+ import { ReactWidget } from '@jupyterlab/ui-components';
6
+ import { Git, IGitExtension } from '@jupyterlab/git';
7
+ import { PromiseDelegate } from '@lumino/coreutils';
8
+ import { Signal } from '@lumino/signaling';
9
+ import { DIFF_WIDGET_CSS_CLASS, DiffSurface, NotebookViewModeControl, isDarkTheme, readStoredNotebookViewMode, writeStoredNotebookViewMode } from './diffSurface';
10
+ import { IMAGE_DIFF_EXTENSIONS, imageDataType } from './imageDiff';
11
+ /**
12
+ * A `Git.Diff.IDiffWidget` that renders through {@link DiffSurface}.
13
+ *
14
+ * `jupyterlab-git` constructs this via the registered factory, hosts it in
15
+ * its own `PreviewMainAreaWidget`, and reads `model` / calls `refresh()` /
16
+ * `getResolvedFile()` on it. It owns the notebook view-mode state (mounted
17
+ * into the diff toolbar `jupyterlab-git` hands the factory) and a reload
18
+ * nonce so `refresh()` re-pulls the model's content.
19
+ */
20
+ class XtralabDiffWidget extends ReactWidget {
21
+ constructor(model, context) {
22
+ super();
23
+ this._reloadNonce = 0;
24
+ this._pendingRefresh = null;
25
+ this._notebookViewModeChanged = new Signal(this);
26
+ this._hasNotebookView = false;
27
+ this._hasNotebookViewChanged = new Signal(this);
28
+ this._model = model;
29
+ this._context = context;
30
+ this._notebookViewMode = readStoredNotebookViewMode();
31
+ // `jp-git-diff-root` borrows jupyterlab-git's host sizing (height:100%
32
+ // flex column inside its PreviewMainAreaWidget); `jp-xtralab-DiffWidget`
33
+ // brings our diff theming + the `.jp-xtralab-DiffWidget-*` styles.
34
+ this.addClass('jp-git-diff-root');
35
+ this.addClass(DIFF_WIDGET_CSS_CLASS);
36
+ }
37
+ /** The diff model, as required by `Git.Diff.IDiffWidget`. */
38
+ get model() {
39
+ return this._model;
40
+ }
41
+ /**
42
+ * We render a two-way (reference vs. challenger) view and do not provide
43
+ * a merge editor, so there is nothing to leave unresolved — report
44
+ * resolved so jupyterlab-git's merge-conflict flow is not blocked.
45
+ */
46
+ get isFileResolved() {
47
+ return true;
48
+ }
49
+ /**
50
+ * Used by jupyterlab-git's "Mark as resolved" button. For merge
51
+ * conflicts, jupyterlab-git passes the working-tree result as `base`;
52
+ * keep that content rather than overwriting it with either compared side.
53
+ */
54
+ async getResolvedFile() {
55
+ const source = this._model.hasConflict === true && this._model.base !== undefined
56
+ ? this._model.base
57
+ : this._model.challenger;
58
+ const content = await source.content();
59
+ return {
60
+ type: 'file',
61
+ format: imageDataType(this._model.filename) !== null ? 'base64' : 'text',
62
+ content
63
+ };
64
+ }
65
+ /**
66
+ * Re-pull both sides of the model and re-render. jupyterlab-git wires a
67
+ * "Refresh" toolbar button to this whenever `model.changed` fires (e.g.
68
+ * the compared `HEAD` moved).
69
+ */
70
+ async refresh() {
71
+ const done = new PromiseDelegate();
72
+ this._pendingRefresh = done;
73
+ this._reloadNonce += 1;
74
+ this.update();
75
+ await done.promise;
76
+ }
77
+ /** The notebook rendered-vs-JSON choice; mirrored into the toolbar. */
78
+ get notebookViewMode() {
79
+ return this._notebookViewMode;
80
+ }
81
+ setNotebookViewMode(mode) {
82
+ if (mode === this._notebookViewMode) {
83
+ return;
84
+ }
85
+ this._notebookViewMode = mode;
86
+ writeStoredNotebookViewMode(mode);
87
+ this._notebookViewModeChanged.emit(mode);
88
+ }
89
+ get notebookViewModeChanged() {
90
+ return this._notebookViewModeChanged;
91
+ }
92
+ get hasNotebookView() {
93
+ return this._hasNotebookView;
94
+ }
95
+ setHasNotebookView(value) {
96
+ if (value === this._hasNotebookView) {
97
+ return;
98
+ }
99
+ this._hasNotebookView = value;
100
+ this._hasNotebookViewChanged.emit(value);
101
+ }
102
+ get hasNotebookViewChanged() {
103
+ return this._hasNotebookViewChanged;
104
+ }
105
+ /** Resolve the in-flight {@link refresh} promise, if any. */
106
+ settleRefresh() {
107
+ const pending = this._pendingRefresh;
108
+ this._pendingRefresh = null;
109
+ pending === null || pending === void 0 ? void 0 : pending.resolve();
110
+ }
111
+ get reloadNonce() {
112
+ return this._reloadNonce;
113
+ }
114
+ get context() {
115
+ return this._context;
116
+ }
117
+ render() {
118
+ return React.createElement(ModelDiffView, { widget: this });
119
+ }
120
+ }
121
+ function ModelDiffView(props) {
122
+ const { widget } = props;
123
+ const model = widget.model;
124
+ const { contentsManager, rendermime, themeManager } = widget.context;
125
+ const nonce = widget.reloadNonce;
126
+ const [state, setState] = React.useState({
127
+ loading: true,
128
+ oldText: '',
129
+ newText: '',
130
+ error: null
131
+ });
132
+ // Mirror the widget's notebook view mode so toolbar-driven changes
133
+ // re-render the diff. The widget owns the canonical value (so the
134
+ // toolbar can read/write it without going through React).
135
+ const [notebookViewMode, setNotebookViewMode] = React.useState(() => widget.notebookViewMode);
136
+ React.useEffect(() => {
137
+ const handler = (_sender, mode) => {
138
+ setNotebookViewMode(mode);
139
+ };
140
+ widget.notebookViewModeChanged.connect(handler);
141
+ setNotebookViewMode(widget.notebookViewMode);
142
+ return () => {
143
+ widget.notebookViewModeChanged.disconnect(handler);
144
+ };
145
+ }, [widget]);
146
+ const handleNotebookAvailabilityChange = React.useCallback((available) => {
147
+ widget.setHasNotebookView(available);
148
+ }, [widget]);
149
+ const [dark, setDark] = React.useState(() => isDarkTheme(themeManager));
150
+ React.useEffect(() => {
151
+ if (themeManager !== null) {
152
+ const handler = () => setDark(isDarkTheme(themeManager));
153
+ themeManager.themeChanged.connect(handler);
154
+ return () => {
155
+ themeManager.themeChanged.disconnect(handler);
156
+ };
157
+ }
158
+ const observer = new MutationObserver(() => setDark(isDarkTheme(themeManager)));
159
+ observer.observe(document.body, {
160
+ attributes: true,
161
+ attributeFilter: ['data-jp-theme-light']
162
+ });
163
+ return () => {
164
+ observer.disconnect();
165
+ };
166
+ }, [themeManager]);
167
+ // Fetch both sides whenever the model or the reload nonce changes. The
168
+ // model's `content()` getters target the right git refs internally
169
+ // (working tree, index, a commit, …) so we stay content-source-agnostic.
170
+ React.useEffect(() => {
171
+ let cancelled = false;
172
+ setState({ loading: true, oldText: '', newText: '', error: null });
173
+ void (async () => {
174
+ try {
175
+ const [oldText, newText] = await Promise.all([
176
+ model.reference.content(),
177
+ model.challenger.content()
178
+ ]);
179
+ if (cancelled) {
180
+ return;
181
+ }
182
+ setState({
183
+ loading: false,
184
+ oldText: oldText !== null && oldText !== void 0 ? oldText : '',
185
+ newText: newText !== null && newText !== void 0 ? newText : '',
186
+ error: null
187
+ });
188
+ }
189
+ catch (err) {
190
+ if (cancelled) {
191
+ return;
192
+ }
193
+ setState({
194
+ loading: false,
195
+ oldText: '',
196
+ newText: '',
197
+ error: err instanceof Error ? err.message : String(err)
198
+ });
199
+ }
200
+ finally {
201
+ if (!cancelled) {
202
+ // Let an in-flight `widget.refresh()` resolve once the reload
203
+ // has settled (jupyterlab-git only awaits it to hide its
204
+ // refresh button).
205
+ widget.settleRefresh();
206
+ }
207
+ }
208
+ })();
209
+ return () => {
210
+ cancelled = true;
211
+ };
212
+ }, [widget, model, nonce]);
213
+ // Per-hunk discard is only meaningful when the challenger is the working
214
+ // tree (an unstaged change) and we are not in a three-way merge view.
215
+ const canDiscardHunk = model.challenger.source === Git.Diff.SpecialRef.WORKING &&
216
+ model.hasConflict !== true;
217
+ const serverPath = React.useMemo(() => { var _a; return PathExt.join((_a = model.repositoryPath) !== null && _a !== void 0 ? _a : '', model.filename); }, [model.repositoryPath, model.filename]);
218
+ const hunkDiscard = React.useMemo(() => ({
219
+ enabled: canDiscardHunk,
220
+ save: async (text) => {
221
+ await contentsManager.save(serverPath, {
222
+ type: 'file',
223
+ format: 'text',
224
+ content: text
225
+ });
226
+ },
227
+ onAfterSave: () => {
228
+ // jupyterlab-git's panel auto-refreshes off the contents
229
+ // `fileChanged` signal the save above emits; we just need to
230
+ // re-pull so the diff reflects the reverted hunk. (We do not
231
+ // auto-close: jupyterlab-git owns the diff tab.)
232
+ void widget.refresh();
233
+ }
234
+ }), [canDiscardHunk, contentsManager, serverPath, widget]);
235
+ return (React.createElement(DiffSurface, { loading: state.loading, error: state.error, isBinary: false, oldText: state.oldText, newText: state.newText, newName: model.filename, oldName: model.filename, dark: dark, rendermime: rendermime, notebookViewMode: notebookViewMode, onNotebookAvailabilityChange: handleNotebookAvailabilityChange, hunkDiscard: hunkDiscard }));
236
+ }
237
+ /**
238
+ * Notebook/JSON toggle mounted into the diff toolbar that jupyterlab-git
239
+ * passes to the factory. Bound to the widget's signals so it stays in sync
240
+ * with the rendered surface and only appears for parseable notebooks.
241
+ */
242
+ class NotebookViewModeToolbarItem extends ReactWidget {
243
+ constructor(widget) {
244
+ super();
245
+ this._widget = widget;
246
+ this.addClass('jp-xtralab-DiffWidget-viewModeToolbarItem');
247
+ }
248
+ render() {
249
+ return React.createElement(ToolbarControl, { widget: this._widget });
250
+ }
251
+ }
252
+ function ToolbarControl(props) {
253
+ const { widget } = props;
254
+ const [mode, setMode] = React.useState(() => widget.notebookViewMode);
255
+ const [available, setAvailable] = React.useState(() => widget.hasNotebookView);
256
+ React.useEffect(() => {
257
+ const onMode = (_sender, next) => {
258
+ setMode(next);
259
+ };
260
+ const onAvailable = (_sender, next) => {
261
+ setAvailable(next);
262
+ };
263
+ widget.notebookViewModeChanged.connect(onMode);
264
+ widget.hasNotebookViewChanged.connect(onAvailable);
265
+ setMode(widget.notebookViewMode);
266
+ setAvailable(widget.hasNotebookView);
267
+ return () => {
268
+ widget.notebookViewModeChanged.disconnect(onMode);
269
+ widget.hasNotebookViewChanged.disconnect(onAvailable);
270
+ };
271
+ }, [widget]);
272
+ return (React.createElement(NotebookViewModeControl, { mode: mode, available: available, onChange: next => widget.setNotebookViewMode(next) }));
273
+ }
274
+ /**
275
+ * Build the `Git.Diff.Factory` xtralab registers with jupyterlab-git. The
276
+ * factory closes over the application-level context (contents manager for
277
+ * hunk discard, rendermime for notebook outputs, theme manager) that the
278
+ * `Git.Diff.IFactoryOptions` does not carry.
279
+ */
280
+ function makeXtralabDiffFactory(context) {
281
+ return async (options) => {
282
+ const widget = new XtralabDiffWidget(options.model, context);
283
+ if (options.toolbar) {
284
+ options.toolbar.addItem('xtralab-notebook-view-mode', new NotebookViewModeToolbarItem(widget));
285
+ }
286
+ return widget;
287
+ };
288
+ }
289
+ /**
290
+ * Registers xtralab's diff rendering as jupyterlab-git's diff providers:
291
+ * `@pierre/diffs` for text and notebooks, the bundled `<img>`-based view
292
+ * for raster images.
293
+ */
294
+ const diffProviderPlugin = {
295
+ id: 'xtralab:git-diff-providers',
296
+ description: "Replaces jupyterlab-git's notebook, text and image diff plugins with xtralab's renderer.",
297
+ autoStart: true,
298
+ requires: [IGitExtension],
299
+ optional: [IRenderMimeRegistry, IThemeManager],
300
+ activate: (app, gitExtension, rendermime, themeManager) => {
301
+ const factory = makeXtralabDiffFactory({
302
+ contentsManager: app.serviceManager.contents,
303
+ rendermime,
304
+ themeManager
305
+ });
306
+ // Notebooks and raster images get extension-specific providers; every
307
+ // other text file falls back to the same factory. One factory serves
308
+ // all three — the surface auto-detects notebooks/images by filename.
309
+ gitExtension.registerDiffProvider('XtralabNotebook', ['.ipynb'], factory);
310
+ gitExtension.registerDiffProvider('XtralabImage', IMAGE_DIFF_EXTENSIONS, factory);
311
+ gitExtension.registerFallbackDiffProvider(factory);
312
+ }
313
+ };
314
+ export default diffProviderPlugin;
@@ -0,0 +1,115 @@
1
+ import * as React from 'react';
2
+ import { IThemeManager } from '@jupyterlab/apputils';
3
+ import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
4
+ /**
5
+ * Shared diff rendering surface used by both diff hosts in this extension:
6
+ *
7
+ * - the launcher dashboard's own `xtralab:git:open-diff` command
8
+ * (via {@link DiffContentWidget} in `diffWidget.tsx`), and
9
+ * - the `jupyterlab-git` diff providers registered into the upstream git
10
+ * panel (via `XtralabDiffWidget` in `diffProvider.tsx`).
11
+ *
12
+ * The surface is deliberately content-source agnostic: callers resolve the
13
+ * old/new text however they like (xtralab's REST helper, or a
14
+ * `Git.Diff.IModel`'s `content()` getters) and hand the resolved strings in.
15
+ * Everything visual — the `@pierre/diffs` split view, the cell-by-cell
16
+ * notebook view, the draggable column resizer, and the per-hunk discard
17
+ * affordance — lives here so the two hosts stay pixel-identical.
18
+ */
19
+ /**
20
+ * The CSS class added to the diff main-area widget. The selectors that
21
+ * style the embedded `@pierre/diffs` viewer hang off this class.
22
+ */
23
+ export declare const DIFF_WIDGET_CSS_CLASS = "jp-xtralab-DiffWidget";
24
+ /**
25
+ * View modes available for `.ipynb` diffs. `notebook` is the cell-by-cell
26
+ * rendered view; `json` is the raw nbformat JSON file diff, for inspecting
27
+ * exactly which bytes changed.
28
+ */
29
+ export type NotebookDiffViewMode = 'notebook' | 'json';
30
+ export declare function readStoredNotebookViewMode(): NotebookDiffViewMode;
31
+ export declare function writeStoredNotebookViewMode(mode: NotebookDiffViewMode): void;
32
+ /**
33
+ * Decide whether the active JupyterLab theme is a dark theme. Prefers the
34
+ * `IThemeManager` token when available, and falls back to sniffing the
35
+ * `data-jp-theme-light` attribute that JupyterLab sets on `<body>` so the
36
+ * component still works in stripped-down hosts where the token isn't
37
+ * provided (lite, certain notebook configurations, …).
38
+ */
39
+ export declare function isDarkTheme(themeManager: IThemeManager | null): boolean;
40
+ /**
41
+ * Optional per-hunk discard wiring. When supplied and `enabled`, each hunk
42
+ * in a plain-text/code diff gets an inline "discard" button; clicking it
43
+ * reverts that hunk and hands the rebuilt full-file text back to the host
44
+ * via {@link IHunkDiscard.save}. The host owns the actual write (it knows
45
+ * the on-disk path) and triggers any follow-up refresh in
46
+ * {@link IHunkDiscard.onAfterSave}.
47
+ */
48
+ export interface IHunkDiscard {
49
+ enabled: boolean;
50
+ save: (fullText: string) => Promise<void>;
51
+ onAfterSave: () => void;
52
+ }
53
+ export interface IDiffSurfaceProps {
54
+ /** Whether the host is still resolving the file contents. */
55
+ loading: boolean;
56
+ /** Fatal error message to show instead of a diff, or `null`. */
57
+ error: string | null;
58
+ /** Whether the file is binary (no textual diff is rendered). */
59
+ isBinary: boolean;
60
+ /** Resolved old/reference text. Ignored while loading/binary/errored. */
61
+ oldText: string;
62
+ /** Resolved new/challenger text. Ignored while loading/binary/errored. */
63
+ newText: string;
64
+ /**
65
+ * New-side path. Drives notebook detection and the `@pierre/diffs` file
66
+ * name on the additions side.
67
+ */
68
+ newName: string;
69
+ /**
70
+ * Old-side path. Differs from {@link newName} only for renames; defaults
71
+ * to {@link newName} when the host does not track a previous path.
72
+ */
73
+ oldName?: string;
74
+ /** Whether to render with the dark `@pierre/diffs` theme. */
75
+ dark: boolean;
76
+ /**
77
+ * Rendermime registry used by the notebook view to render outputs and
78
+ * markdown cells. May be `null` in stripped-down hosts; the notebook
79
+ * diff falls back to a textual representation in that case.
80
+ */
81
+ rendermime: IRenderMimeRegistry | null;
82
+ /** Current rendered-vs-JSON choice for notebook diffs (host-controlled). */
83
+ notebookViewMode: NotebookDiffViewMode;
84
+ /**
85
+ * Called whenever the availability of a rendered notebook view changes,
86
+ * so the host can show/hide its Notebook/JSON toolbar toggle.
87
+ */
88
+ onNotebookAvailabilityChange?: (available: boolean) => void;
89
+ /**
90
+ * Called after each (re)computation of the diff with the number of hunks
91
+ * (or `null` when no textual diff exists). Hosts use this to implement
92
+ * post-discard behaviors such as auto-closing an emptied diff.
93
+ */
94
+ onMetadataChange?: (info: {
95
+ hunkCount: number | null;
96
+ }) => void;
97
+ /** Optional per-hunk discard wiring; omit for a read-only diff. */
98
+ hunkDiscard?: IHunkDiscard;
99
+ }
100
+ /**
101
+ * The shared, content-source-agnostic diff renderer. See the module
102
+ * docstring for the division of labor between this surface and its hosts.
103
+ */
104
+ export declare function DiffSurface(props: IDiffSurfaceProps): React.ReactElement;
105
+ /**
106
+ * Segmented Notebook/JSON selector. Hosts mount this into whatever toolbar
107
+ * they own (the launcher's `MainAreaWidget` toolbar, or the
108
+ * `jupyterlab-git`-provided diff toolbar) and drive its value/visibility
109
+ * from the same state they pass to {@link DiffSurface}.
110
+ */
111
+ export declare function NotebookViewModeControl(props: {
112
+ mode: NotebookDiffViewMode;
113
+ available: boolean;
114
+ onChange: (mode: NotebookDiffViewMode) => void;
115
+ }): React.ReactElement;