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.
Files changed (56) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +69 -0
  3. package/lib/fileBrowser/commands.d.ts +62 -0
  4. package/lib/fileBrowser/commands.js +418 -0
  5. package/lib/fileBrowser/contents.d.ts +28 -0
  6. package/lib/fileBrowser/contents.js +48 -0
  7. package/lib/fileBrowser/fileBrowser.d.ts +24 -0
  8. package/lib/fileBrowser/fileBrowser.js +535 -0
  9. package/lib/fileBrowser/icons.d.ts +37 -0
  10. package/lib/fileBrowser/icons.js +153 -0
  11. package/lib/fileBrowser/index.d.ts +7 -0
  12. package/lib/fileBrowser/index.js +38 -0
  13. package/lib/fileBrowser/widget.d.ts +97 -0
  14. package/lib/fileBrowser/widget.js +95 -0
  15. package/lib/git/api.d.ts +39 -0
  16. package/lib/git/api.js +164 -0
  17. package/lib/git/commands.d.ts +69 -0
  18. package/lib/git/commands.js +67 -0
  19. package/lib/git/diffWidget.d.ts +108 -0
  20. package/lib/git/diffWidget.js +624 -0
  21. package/lib/git/icons.d.ts +14 -0
  22. package/lib/git/icons.js +19 -0
  23. package/lib/git/index.d.ts +11 -0
  24. package/lib/git/index.js +74 -0
  25. package/lib/git/notebookDiff.d.ts +176 -0
  26. package/lib/git/notebookDiff.js +805 -0
  27. package/lib/git/panel.d.ts +29 -0
  28. package/lib/git/panel.js +42 -0
  29. package/lib/git/panelComponent.d.ts +26 -0
  30. package/lib/git/panelComponent.js +300 -0
  31. package/lib/git/tokens.d.ts +78 -0
  32. package/lib/git/tokens.js +6 -0
  33. package/lib/index.d.ts +10 -0
  34. package/lib/index.js +14 -0
  35. package/package.json +216 -0
  36. package/schema/plugin.json +67 -0
  37. package/src/fileBrowser/commands.ts +507 -0
  38. package/src/fileBrowser/contents.ts +66 -0
  39. package/src/fileBrowser/fileBrowser.tsx +635 -0
  40. package/src/fileBrowser/icons.ts +172 -0
  41. package/src/fileBrowser/index.ts +56 -0
  42. package/src/fileBrowser/widget.tsx +202 -0
  43. package/src/git/api.ts +207 -0
  44. package/src/git/commands.ts +143 -0
  45. package/src/git/diffWidget.tsx +903 -0
  46. package/src/git/icons.ts +20 -0
  47. package/src/git/index.ts +109 -0
  48. package/src/git/notebookDiff.tsx +1294 -0
  49. package/src/git/panel.tsx +56 -0
  50. package/src/git/panelComponent.tsx +558 -0
  51. package/src/git/tokens.ts +91 -0
  52. package/src/index.ts +18 -0
  53. package/style/base.css +175 -0
  54. package/style/git.css +770 -0
  55. package/style/index.css +2 -0
  56. package/style/index.js +2 -0
@@ -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 {};