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.
- package/README.md +110 -73
- package/lib/agentSessions.d.ts +50 -0
- package/lib/agentSessions.js +45 -0
- package/lib/fileBrowser/index.js +5 -1
- package/lib/git/commands.d.ts +12 -13
- package/lib/git/commands.js +7 -28
- package/lib/git/diffProvider.d.ts +8 -0
- package/lib/git/diffProvider.js +314 -0
- package/lib/git/diffSurface.d.ts +115 -0
- package/lib/git/diffSurface.js +342 -0
- package/lib/git/diffWidget.d.ts +8 -13
- package/lib/git/diffWidget.js +65 -311
- package/lib/git/imageDiff.d.ts +23 -0
- package/lib/git/imageDiff.js +256 -0
- package/lib/git/index.d.ts +10 -8
- package/lib/git/index.js +37 -52
- package/lib/index.d.ts +4 -2
- package/lib/index.js +14 -8
- package/lib/launcher/agents.d.ts +1 -1
- package/lib/launcher/agents.js +22 -13
- package/lib/launcher/commands.d.ts +32 -6
- package/lib/launcher/commands.js +81 -57
- package/lib/launcher/dashboard.d.ts +15 -0
- package/lib/launcher/dashboard.js +119 -5
- package/lib/launcher/editorRegistry.d.ts +43 -0
- package/lib/launcher/editorRegistry.js +96 -0
- package/lib/launcher/editors.d.ts +78 -0
- package/lib/launcher/editors.js +110 -0
- package/lib/launcher/icons.d.ts +25 -6
- package/lib/launcher/icons.js +116 -19
- package/lib/launcher/index.d.ts +2 -22
- package/lib/launcher/index.js +48 -25
- package/lib/launcher/registry.d.ts +21 -0
- package/lib/launcher/registry.js +28 -0
- package/lib/launcher/tokens.d.ts +41 -0
- package/lib/launcher/tokens.js +17 -0
- package/lib/terminals/detection.d.ts +11 -0
- package/lib/terminals/detection.js +50 -0
- package/lib/terminals/index.d.ts +27 -0
- package/lib/terminals/index.js +194 -0
- package/lib/terminals/model.d.ts +191 -0
- package/lib/terminals/model.js +418 -0
- package/lib/terminals/widget.d.ts +72 -0
- package/lib/terminals/widget.js +93 -0
- package/lib/topBar/icons.d.ts +13 -0
- package/lib/topBar/icons.js +27 -0
- package/lib/topBar/index.d.ts +16 -0
- package/lib/topBar/index.js +86 -0
- package/package.json +52 -35
- package/schema/launcher.json +56 -1
- package/src/agentSessions.ts +94 -0
- package/src/fileBrowser/index.ts +5 -1
- package/src/git/commands.ts +12 -38
- package/src/git/diffProvider.tsx +462 -0
- package/src/git/diffSurface.tsx +588 -0
- package/src/git/diffWidget.tsx +105 -465
- package/src/git/imageDiff.tsx +469 -0
- package/src/git/index.ts +38 -68
- package/src/index.ts +14 -8
- package/src/launcher/agents.ts +23 -14
- package/src/launcher/commands.ts +98 -60
- package/src/launcher/dashboard.tsx +205 -3
- package/src/launcher/editorRegistry.ts +149 -0
- package/src/launcher/editors.ts +183 -0
- package/src/launcher/icons.ts +120 -20
- package/src/launcher/index.ts +62 -27
- package/src/launcher/registry.ts +33 -0
- package/src/launcher/tokens.ts +51 -0
- package/src/terminals/detection.ts +65 -0
- package/src/terminals/index.ts +222 -0
- package/src/terminals/model.ts +497 -0
- package/src/terminals/widget.tsx +240 -0
- package/src/topBar/icons.ts +30 -0
- package/src/topBar/index.ts +121 -0
- package/style/git.css +267 -266
- package/style/index.css +2 -1
- package/style/index.js +2 -1
- package/style/launcher.css +124 -2
- package/style/terminals.css +235 -0
- package/style/topBar.css +38 -0
- package/lib/git/icons.d.ts +0 -14
- package/lib/git/icons.js +0 -19
- package/lib/git/panel.d.ts +0 -29
- package/lib/git/panel.js +0 -42
- package/lib/git/panelComponent.d.ts +0 -28
- package/lib/git/panelComponent.js +0 -320
- package/lib/statusBar/index.d.ts +0 -14
- package/lib/statusBar/index.js +0 -89
- package/lib/statusBar/widget.d.ts +0 -121
- package/lib/statusBar/widget.js +0 -237
- package/src/git/icons.ts +0 -20
- package/src/git/panel.tsx +0 -56
- package/src/git/panelComponent.tsx +0 -575
- package/src/statusBar/index.ts +0 -115
- package/src/statusBar/widget.tsx +0 -318
- 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;
|