xtralab 0.1.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/LICENSE +28 -0
- package/README.md +69 -0
- package/lib/fileBrowser/commands.d.ts +62 -0
- package/lib/fileBrowser/commands.js +418 -0
- package/lib/fileBrowser/contents.d.ts +28 -0
- package/lib/fileBrowser/contents.js +48 -0
- package/lib/fileBrowser/fileBrowser.d.ts +24 -0
- package/lib/fileBrowser/fileBrowser.js +535 -0
- package/lib/fileBrowser/icons.d.ts +37 -0
- package/lib/fileBrowser/icons.js +153 -0
- package/lib/fileBrowser/index.d.ts +7 -0
- package/lib/fileBrowser/index.js +38 -0
- package/lib/fileBrowser/widget.d.ts +97 -0
- package/lib/fileBrowser/widget.js +95 -0
- package/lib/git/api.d.ts +39 -0
- package/lib/git/api.js +164 -0
- package/lib/git/commands.d.ts +69 -0
- package/lib/git/commands.js +67 -0
- package/lib/git/diffWidget.d.ts +108 -0
- package/lib/git/diffWidget.js +624 -0
- package/lib/git/icons.d.ts +14 -0
- package/lib/git/icons.js +19 -0
- package/lib/git/index.d.ts +11 -0
- package/lib/git/index.js +74 -0
- package/lib/git/notebookDiff.d.ts +176 -0
- package/lib/git/notebookDiff.js +805 -0
- package/lib/git/panel.d.ts +29 -0
- package/lib/git/panel.js +42 -0
- package/lib/git/panelComponent.d.ts +26 -0
- package/lib/git/panelComponent.js +300 -0
- package/lib/git/tokens.d.ts +78 -0
- package/lib/git/tokens.js +6 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +14 -0
- package/package.json +216 -0
- package/schema/plugin.json +67 -0
- package/src/fileBrowser/commands.ts +507 -0
- package/src/fileBrowser/contents.ts +66 -0
- package/src/fileBrowser/fileBrowser.tsx +635 -0
- package/src/fileBrowser/icons.ts +172 -0
- package/src/fileBrowser/index.ts +56 -0
- package/src/fileBrowser/widget.tsx +202 -0
- package/src/git/api.ts +207 -0
- package/src/git/commands.ts +143 -0
- package/src/git/diffWidget.tsx +903 -0
- package/src/git/icons.ts +20 -0
- package/src/git/index.ts +109 -0
- package/src/git/notebookDiff.tsx +1294 -0
- package/src/git/panel.tsx +56 -0
- package/src/git/panelComponent.tsx +558 -0
- package/src/git/tokens.ts +91 -0
- package/src/index.ts +18 -0
- package/style/base.css +175 -0
- package/style/git.css +770 -0
- package/style/index.css +2 -0
- package/style/index.js +2 -0
package/lib/git/index.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { ILayoutRestorer } from '@jupyterlab/application';
|
|
2
|
+
import { Dialog, ICommandPalette, IThemeManager, WidgetTracker, showDialog, showErrorMessage } from '@jupyterlab/apputils';
|
|
3
|
+
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
4
|
+
import { CommandIDs, registerGitCommands } from './commands';
|
|
5
|
+
import { diffWidgetId } from './diffWidget';
|
|
6
|
+
import { GitPanel } from './panel';
|
|
7
|
+
const GIT_PLUGIN_ID = 'xtralab:git';
|
|
8
|
+
const GIT_DIFF_TRACKER_NAMESPACE = 'xtralab-git-diff';
|
|
9
|
+
/**
|
|
10
|
+
* The git plugin. Adds a VS Code-style "Source Control" panel to the
|
|
11
|
+
* JupyterLab left sidebar and opens file diffs in the main area when the
|
|
12
|
+
* user clicks an entry in the panel. Backed by the `jupyterlab_git` server
|
|
13
|
+
* extension's REST API; the bundled `@jupyterlab/git` frontend is disabled
|
|
14
|
+
* via `package.json`'s `jupyterlab.disabledExtensions` so the two panels do
|
|
15
|
+
* not coexist.
|
|
16
|
+
*/
|
|
17
|
+
const plugin = {
|
|
18
|
+
id: GIT_PLUGIN_ID,
|
|
19
|
+
description: 'A VS Code-style git changes panel and diff viewer powered by jupyterlab-git and @pierre/diffs.',
|
|
20
|
+
autoStart: true,
|
|
21
|
+
optional: [
|
|
22
|
+
ILayoutRestorer,
|
|
23
|
+
IThemeManager,
|
|
24
|
+
ICommandPalette,
|
|
25
|
+
IRenderMimeRegistry
|
|
26
|
+
],
|
|
27
|
+
activate: (app, restorer, themeManager, commandPalette, rendermime) => {
|
|
28
|
+
// Match the panel's repo path: empty string means "use the JupyterLab
|
|
29
|
+
// server's root and let git resolve the enclosing repo".
|
|
30
|
+
const repoPath = '';
|
|
31
|
+
// Track open diff widgets so the layout restorer can recreate them on
|
|
32
|
+
// reload, and so the openDiff handler can reveal an already-open diff
|
|
33
|
+
// for the same file instead of creating a duplicate.
|
|
34
|
+
const tracker = new WidgetTracker({
|
|
35
|
+
namespace: GIT_DIFF_TRACKER_NAMESPACE
|
|
36
|
+
});
|
|
37
|
+
const findDiff = (change) => {
|
|
38
|
+
var _a;
|
|
39
|
+
const id = diffWidgetId(change);
|
|
40
|
+
return (_a = tracker.find(widget => widget.id === id)) !== null && _a !== void 0 ? _a : undefined;
|
|
41
|
+
};
|
|
42
|
+
const panel = new GitPanel({
|
|
43
|
+
openDiff: change => {
|
|
44
|
+
const args = { repoPath, change };
|
|
45
|
+
void app.commands.execute(CommandIDs.openDiff, args);
|
|
46
|
+
},
|
|
47
|
+
confirm: async (title, body, accept) => {
|
|
48
|
+
const result = await showDialog({
|
|
49
|
+
title,
|
|
50
|
+
body,
|
|
51
|
+
buttons: [Dialog.cancelButton(), Dialog.warnButton({ label: accept })]
|
|
52
|
+
});
|
|
53
|
+
return result.button.accept === true;
|
|
54
|
+
},
|
|
55
|
+
showError: showErrorMessage
|
|
56
|
+
});
|
|
57
|
+
app.shell.add(panel, 'left', { rank: 800 });
|
|
58
|
+
if (restorer !== null) {
|
|
59
|
+
restorer.add(panel, panel.id);
|
|
60
|
+
}
|
|
61
|
+
registerGitCommands({
|
|
62
|
+
app,
|
|
63
|
+
panel,
|
|
64
|
+
themeManager,
|
|
65
|
+
commandPalette,
|
|
66
|
+
contentsManager: app.serviceManager.contents,
|
|
67
|
+
rendermime,
|
|
68
|
+
onChanged: () => panel.refresh(),
|
|
69
|
+
trackDiff: widget => tracker.add(widget),
|
|
70
|
+
findDiff
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
export default plugin;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
3
|
+
import { type FileDiffMetadata } from '@pierre/diffs';
|
|
4
|
+
/**
|
|
5
|
+
* The CSS class added to the root of the notebook diff. Selectors that
|
|
6
|
+
* style cell sections, headers, and the per-cell diff blocks hang off this
|
|
7
|
+
* class.
|
|
8
|
+
*/
|
|
9
|
+
export declare const NOTEBOOK_DIFF_CSS_CLASS = "jp-xtralab-NotebookDiff";
|
|
10
|
+
/**
|
|
11
|
+
* Minimal slice of nbformat 4.x that the diff inspects. Fields we don't
|
|
12
|
+
* read (attachments, e.g.) are intentionally not narrowed — we still pass
|
|
13
|
+
* them around as part of the cell, but we never project them into the diff.
|
|
14
|
+
*/
|
|
15
|
+
export interface INotebookCell {
|
|
16
|
+
cell_type: string;
|
|
17
|
+
id?: string;
|
|
18
|
+
source: string | string[];
|
|
19
|
+
outputs?: INotebookOutput[];
|
|
20
|
+
metadata?: Record<string, unknown>;
|
|
21
|
+
execution_count?: number | null;
|
|
22
|
+
attachments?: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
export interface INotebookOutput {
|
|
25
|
+
output_type: string;
|
|
26
|
+
data?: Record<string, unknown>;
|
|
27
|
+
text?: string | string[];
|
|
28
|
+
name?: string;
|
|
29
|
+
ename?: string;
|
|
30
|
+
evalue?: string;
|
|
31
|
+
traceback?: string[];
|
|
32
|
+
execution_count?: number | null;
|
|
33
|
+
metadata?: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
export interface INotebook {
|
|
36
|
+
cells: INotebookCell[];
|
|
37
|
+
metadata: Record<string, unknown>;
|
|
38
|
+
nbformat: number;
|
|
39
|
+
nbformat_minor: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* One entry in the per-cell diff. `unchanged` cells are rendered as a
|
|
43
|
+
* collapsed summary; the other variants render their source / outputs /
|
|
44
|
+
* metadata via {@link FileDiff}. The `modified` and `unchanged` branches
|
|
45
|
+
* carry the same payload but live on separate union members so `Extract`
|
|
46
|
+
* narrowing works on `kind` literals downstream.
|
|
47
|
+
*/
|
|
48
|
+
export type NotebookCellDiff = {
|
|
49
|
+
kind: 'modified';
|
|
50
|
+
oldCell: INotebookCell;
|
|
51
|
+
newCell: INotebookCell;
|
|
52
|
+
newIndex: number;
|
|
53
|
+
oldIndex: number;
|
|
54
|
+
} | {
|
|
55
|
+
kind: 'unchanged';
|
|
56
|
+
oldCell: INotebookCell;
|
|
57
|
+
newCell: INotebookCell;
|
|
58
|
+
newIndex: number;
|
|
59
|
+
oldIndex: number;
|
|
60
|
+
} | {
|
|
61
|
+
kind: 'added';
|
|
62
|
+
newCell: INotebookCell;
|
|
63
|
+
newIndex: number;
|
|
64
|
+
} | {
|
|
65
|
+
kind: 'removed';
|
|
66
|
+
oldCell: INotebookCell;
|
|
67
|
+
oldIndex: number;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Notebook-level diff result returned by {@link buildNotebookDiff}. Returns
|
|
71
|
+
* `null` from the builder when either side fails to parse — the caller then
|
|
72
|
+
* falls back to a raw JSON file diff.
|
|
73
|
+
*/
|
|
74
|
+
export interface INotebookDiffResult {
|
|
75
|
+
oldNotebook: INotebook;
|
|
76
|
+
newNotebook: INotebook;
|
|
77
|
+
cells: NotebookCellDiff[];
|
|
78
|
+
/**
|
|
79
|
+
* Kernel language pulled from `metadata.language_info.name`, used to pick
|
|
80
|
+
* a syntax-highlighting filename for code cells.
|
|
81
|
+
*/
|
|
82
|
+
language: string | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* Pre-computed diff for notebook-level metadata (kernelspec, language_info,
|
|
85
|
+
* nbformat) when it differs between revisions; `null` otherwise so the
|
|
86
|
+
* section can be hidden without further checks.
|
|
87
|
+
*/
|
|
88
|
+
notebookMetadataDiff: FileDiffMetadata | null;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Parse notebook JSON. Returns `null` if the text doesn't parse or doesn't
|
|
92
|
+
* look like a notebook (missing `cells` array). The caller falls back to a
|
|
93
|
+
* raw text diff so a malformed file is still inspectable.
|
|
94
|
+
*/
|
|
95
|
+
export declare function parseNotebook(text: string): INotebook | null;
|
|
96
|
+
/**
|
|
97
|
+
* nbformat allows multiline string fields (`source`, stream `text`,
|
|
98
|
+
* `text/plain` outputs, …) to be either a single string or an array of
|
|
99
|
+
* strings. Normalize to one string so equality checks and diffs see the
|
|
100
|
+
* same shape regardless of how the writer chose to serialise it.
|
|
101
|
+
*/
|
|
102
|
+
export declare function joinMultiline(value: string | string[] | undefined): string;
|
|
103
|
+
export declare function cellSource(cell: INotebookCell): string;
|
|
104
|
+
/**
|
|
105
|
+
* Canonical text representation of a cell's outputs, used both for equality
|
|
106
|
+
* checks and for the per-cell output diff. Stream and `text/plain` data
|
|
107
|
+
* round-trip verbatim; richer mime types (image/png, text/html, …) collapse
|
|
108
|
+
* to a `<mime-type>` placeholder so diffing image bytes doesn't drown the
|
|
109
|
+
* panel in base64 noise.
|
|
110
|
+
*/
|
|
111
|
+
export declare function canonicalOutputs(cell: INotebookCell): string;
|
|
112
|
+
/**
|
|
113
|
+
* Stable JSON of the cell's `metadata` object. Keys are sorted recursively
|
|
114
|
+
* so a writer that re-orders the metadata object doesn't surface as a diff.
|
|
115
|
+
* An empty metadata object returns the empty string so freshly-added cells
|
|
116
|
+
* that carry the default `{}` don't produce a `+{}` placeholder section.
|
|
117
|
+
*/
|
|
118
|
+
export declare function canonicalMetadata(cell: INotebookCell): string;
|
|
119
|
+
/**
|
|
120
|
+
* Stable JSON of the notebook-level metadata bundle (kernelspec, language
|
|
121
|
+
* info, nbformat). We diff the bundle as a single block so the user sees
|
|
122
|
+
* all environment-level changes in one place.
|
|
123
|
+
*/
|
|
124
|
+
export declare function canonicalNotebookMetadata(notebook: INotebook): string;
|
|
125
|
+
/**
|
|
126
|
+
* Equality check used to classify matched cells as `unchanged` vs
|
|
127
|
+
* `modified`. Compares the three things we actually render — type, source,
|
|
128
|
+
* outputs, metadata — using the same canonical representations the diff
|
|
129
|
+
* itself sees, so an "equal" verdict here implies all per-cell diff blocks
|
|
130
|
+
* would be empty.
|
|
131
|
+
*/
|
|
132
|
+
export declare function cellsAreEqual(a: INotebookCell, b: INotebookCell): boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Align cells between two notebook revisions. Cells that carry a stable id
|
|
135
|
+
* (nbformat ≥ 4.5) match on their id; remaining cells fall back to
|
|
136
|
+
* positional matching against the next un-consumed unidentified old cell so
|
|
137
|
+
* older notebooks still produce a sensible diff.
|
|
138
|
+
*
|
|
139
|
+
* Output ordering: entries follow new-file order, with cells that exist
|
|
140
|
+
* only in the old revision appended at the end as `removed`. We don't try
|
|
141
|
+
* to interleave removals at their original position — for typical notebook
|
|
142
|
+
* edits this is more readable than guessing alignment between unrelated
|
|
143
|
+
* insertions and deletions.
|
|
144
|
+
*/
|
|
145
|
+
export declare function alignNotebookCells(oldCells: INotebookCell[], newCells: INotebookCell[]): NotebookCellDiff[];
|
|
146
|
+
export interface IBuildNotebookDiffOptions {
|
|
147
|
+
oldText: string;
|
|
148
|
+
newText: string;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Build a complete notebook diff from the textual contents of two
|
|
152
|
+
* revisions. Returns `null` if either side fails to parse as a notebook;
|
|
153
|
+
* the caller is expected to fall back to a raw text diff in that case.
|
|
154
|
+
*
|
|
155
|
+
* An empty string on either side is treated as an empty notebook (no
|
|
156
|
+
* cells, default metadata) so a freshly-added or deleted notebook still
|
|
157
|
+
* produces a sensible cell-by-cell diff.
|
|
158
|
+
*/
|
|
159
|
+
export declare function buildNotebookDiff(options: IBuildNotebookDiffOptions): INotebookDiffResult | null;
|
|
160
|
+
interface INotebookDiffViewProps {
|
|
161
|
+
diff: INotebookDiffResult;
|
|
162
|
+
dark: boolean;
|
|
163
|
+
rendermime: IRenderMimeRegistry | null;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The notebook-level diff view. Lays out cells in a 2-column grid where
|
|
167
|
+
* the left column tracks the *old* notebook and the right column tracks
|
|
168
|
+
* the *new* one — modified and unchanged cells span both columns (their
|
|
169
|
+
* internal split-mode FileDiff aligns with the outer columns), added
|
|
170
|
+
* cells live in the right column with an empty placeholder on the left,
|
|
171
|
+
* and removed cells live in the left column with an empty placeholder on
|
|
172
|
+
* the right. The diff library's red/green tint within each cell makes
|
|
173
|
+
* the orientation clear without a separate column header.
|
|
174
|
+
*/
|
|
175
|
+
export declare function NotebookDiffView(props: INotebookDiffViewProps): React.ReactElement;
|
|
176
|
+
export {};
|