xtralab 0.15.0 → 0.15.1
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 +10 -0
- package/lib/about/icons.d.ts +0 -5
- package/lib/about/icons.js +0 -5
- package/lib/about/index.d.ts +4 -8
- package/lib/about/index.js +6 -17
- package/lib/agentSessions.d.ts +9 -29
- package/lib/agentSessions.js +2 -7
- package/lib/askAgent/editorSelection.d.ts +20 -26
- package/lib/askAgent/editorSelection.js +11 -26
- package/lib/askAgent/icons.d.ts +2 -3
- package/lib/askAgent/icons.js +2 -3
- package/lib/askAgent/index.d.ts +4 -26
- package/lib/askAgent/index.js +54 -158
- package/lib/askAgent/popup.d.ts +12 -16
- package/lib/askAgent/popup.js +18 -37
- package/lib/askAgent/prompt.d.ts +3 -5
- package/lib/askAgent/prompt.js +6 -19
- package/lib/askAgent/queue.d.ts +11 -21
- package/lib/askAgent/queue.js +9 -19
- package/lib/askAgent/queuePanel.d.ts +13 -18
- package/lib/askAgent/queuePanel.js +15 -37
- package/lib/askAgent/targetPicker.d.ts +2 -9
- package/lib/askAgent/targetPicker.js +6 -11
- package/lib/askAgent/tokens.d.ts +32 -46
- package/lib/askAgent/tokens.js +4 -6
- package/lib/commandBar/index.d.ts +4 -13
- package/lib/commandBar/index.js +21 -39
- package/lib/customPanel/index.js +15 -0
- package/lib/editorBreadcrumbs/index.d.ts +3 -6
- package/lib/editorBreadcrumbs/index.js +10 -6
- package/lib/editorBreadcrumbs/widget.d.ts +17 -4
- package/lib/editorBreadcrumbs/widget.js +11 -17
- package/lib/editorIndent/index.d.ts +4 -12
- package/lib/editorIndent/index.js +8 -23
- package/lib/fileBrowser/commands.d.ts +28 -19
- package/lib/fileBrowser/commands.js +39 -98
- package/lib/fileBrowser/contents.d.ts +3 -0
- package/lib/fileBrowser/dragAndDrop.d.ts +17 -4
- package/lib/fileBrowser/dragAndDrop.js +2 -7
- package/lib/fileBrowser/fileBrowser.d.ts +19 -9
- package/lib/fileBrowser/fileBrowser.js +68 -216
- package/lib/fileBrowser/gitStatus.d.ts +4 -9
- package/lib/fileBrowser/gitStatus.js +7 -17
- package/lib/fileBrowser/gitignore.d.ts +4 -17
- package/lib/fileBrowser/gitignore.js +4 -25
- package/lib/fileBrowser/icons.d.ts +7 -17
- package/lib/fileBrowser/icons.js +25 -80
- package/lib/fileBrowser/index.js +2 -4
- package/lib/fileBrowser/widget.d.ts +126 -48
- package/lib/fileBrowser/widget.js +89 -4
- package/lib/fileTypeIcons/index.js +0 -10
- package/lib/git/api.d.ts +5 -13
- package/lib/git/api.js +10 -36
- package/lib/git/askRequest.d.ts +4 -8
- package/lib/git/askRequest.js +6 -20
- package/lib/git/commands.d.ts +40 -5
- package/lib/git/commands.js +16 -10
- package/lib/git/diffModel.js +4 -0
- package/lib/git/diffSurface.d.ts +41 -55
- package/lib/git/diffSurface.js +42 -117
- package/lib/git/diffWidget.d.ts +81 -2
- package/lib/git/diffWidget.js +62 -12
- package/lib/git/imageDiff.d.ts +5 -6
- package/lib/git/imageDiff.js +18 -48
- package/lib/git/index.d.ts +0 -3
- package/lib/git/index.js +3 -8
- package/lib/git/notebookDiff.d.ts +110 -33
- package/lib/git/notebookDiff.js +52 -195
- package/lib/git/tokens.d.ts +73 -31
- package/lib/git/tokens.js +2 -3
- package/lib/highlight/index.d.ts +4 -10
- package/lib/highlight/index.js +10 -41
- package/lib/index.d.ts +0 -9
- package/lib/index.js +0 -9
- package/lib/launcher/agents.d.ts +55 -51
- package/lib/launcher/agents.js +15 -57
- package/lib/launcher/availability.d.ts +3 -9
- package/lib/launcher/availability.js +3 -9
- package/lib/launcher/commands.d.ts +7 -32
- package/lib/launcher/commands.js +14 -45
- package/lib/launcher/dashboard.d.ts +19 -24
- package/lib/launcher/dashboard.js +21 -67
- package/lib/launcher/editorRegistry.d.ts +10 -22
- package/lib/launcher/editorRegistry.js +14 -18
- package/lib/launcher/editors.d.ts +39 -45
- package/lib/launcher/editors.js +8 -34
- package/lib/launcher/icons.d.ts +0 -12
- package/lib/launcher/icons.js +5 -39
- package/lib/launcher/index.js +12 -55
- package/lib/launcher/invocation.d.ts +4 -10
- package/lib/launcher/invocation.js +7 -22
- package/lib/launcher/registry.d.ts +9 -7
- package/lib/launcher/registry.js +9 -7
- package/lib/launcher/tokens.d.ts +10 -22
- package/lib/launcher/tokens.js +5 -8
- package/lib/menuBar/index.d.ts +3 -10
- package/lib/menuBar/index.js +11 -28
- package/lib/menus/index.d.ts +3 -9
- package/lib/menus/index.js +14 -30
- package/lib/omnibox/files.d.ts +2 -4
- package/lib/omnibox/files.js +5 -11
- package/lib/omnibox/index.d.ts +4 -14
- package/lib/omnibox/index.js +9 -27
- package/lib/omnibox/model.d.ts +38 -10
- package/lib/omnibox/model.js +15 -33
- package/lib/omnibox/recents.d.ts +15 -17
- package/lib/omnibox/recents.js +12 -17
- package/lib/omnibox/tokens.d.ts +5 -10
- package/lib/omnibox/tokens.js +2 -6
- package/lib/omnibox/widget.d.ts +11 -7
- package/lib/omnibox/widget.js +8 -16
- package/lib/searchReplace/index.d.ts +4 -20
- package/lib/searchReplace/index.js +7 -27
- package/lib/showOutput/index.d.ts +3 -12
- package/lib/showOutput/index.js +6 -22
- package/lib/sidebar/index.d.ts +4 -7
- package/lib/sidebar/index.js +5 -12
- package/lib/terminalNotifications/index.d.ts +4 -10
- package/lib/terminalNotifications/index.js +6 -20
- package/lib/terminals/agentTerminals.d.ts +12 -5
- package/lib/terminals/agentTerminals.js +25 -44
- package/lib/terminals/detection.d.ts +4 -8
- package/lib/terminals/detection.js +4 -8
- package/lib/terminals/index.d.ts +0 -35
- package/lib/terminals/index.js +12 -101
- package/lib/terminals/model.d.ts +64 -139
- package/lib/terminals/model.js +80 -220
- package/lib/terminals/tokens.d.ts +11 -22
- package/lib/terminals/tokens.js +2 -2
- package/lib/terminals/widget.d.ts +43 -13
- package/lib/terminals/widget.js +33 -18
- package/lib/topBar/icons.d.ts +3 -8
- package/lib/topBar/icons.js +3 -8
- package/lib/topBar/index.d.ts +4 -11
- package/lib/topBar/index.js +10 -38
- package/lib/walkthrough/index.d.ts +4 -9
- package/lib/walkthrough/index.js +6 -20
- package/lib/walkthrough/panel.d.ts +40 -9
- package/lib/walkthrough/panel.js +4 -10
- package/package.json +2 -2
- package/src/about/icons.ts +0 -5
- package/src/about/index.tsx +6 -17
- package/src/agentSessions.ts +9 -29
- package/src/askAgent/editorSelection.ts +22 -29
- package/src/askAgent/icons.ts +2 -3
- package/src/askAgent/index.ts +54 -158
- package/src/askAgent/popup.tsx +23 -45
- package/src/askAgent/prompt.ts +6 -19
- package/src/askAgent/queue.ts +13 -24
- package/src/askAgent/queuePanel.tsx +21 -46
- package/src/askAgent/targetPicker.tsx +5 -15
- package/src/askAgent/tokens.ts +32 -46
- package/src/commandBar/index.ts +24 -39
- package/src/customPanel/index.ts +15 -0
- package/src/editorBreadcrumbs/index.ts +10 -6
- package/src/editorBreadcrumbs/widget.ts +23 -17
- package/src/editorIndent/index.ts +17 -23
- package/src/fileBrowser/commands.ts +57 -98
- package/src/fileBrowser/contents.ts +3 -0
- package/src/fileBrowser/dragAndDrop.ts +17 -7
- package/src/fileBrowser/fileBrowser.tsx +84 -223
- package/src/fileBrowser/gitStatus.ts +7 -17
- package/src/fileBrowser/gitignore.ts +4 -25
- package/src/fileBrowser/icons.ts +25 -80
- package/src/fileBrowser/index.ts +2 -4
- package/src/fileBrowser/widget.tsx +149 -50
- package/src/fileTypeIcons/index.ts +0 -10
- package/src/git/api.ts +10 -36
- package/src/git/askRequest.ts +6 -20
- package/src/git/commands.ts +70 -10
- package/src/git/diffModel.ts +25 -0
- package/src/git/diffSurface.tsx +71 -159
- package/src/git/diffWidget.tsx +104 -14
- package/src/git/imageDiff.tsx +45 -51
- package/src/git/index.ts +3 -8
- package/src/git/notebookDiff.tsx +211 -238
- package/src/git/tokens.ts +73 -31
- package/src/highlight/index.ts +10 -41
- package/src/index.ts +0 -9
- package/src/launcher/agents.ts +64 -95
- package/src/launcher/availability.ts +3 -9
- package/src/launcher/commands.ts +14 -51
- package/src/launcher/dashboard.tsx +49 -88
- package/src/launcher/editorRegistry.ts +21 -29
- package/src/launcher/editors.ts +39 -58
- package/src/launcher/icons.ts +5 -39
- package/src/launcher/index.ts +12 -55
- package/src/launcher/invocation.ts +7 -22
- package/src/launcher/registry.ts +9 -7
- package/src/launcher/tokens.ts +10 -22
- package/src/menuBar/index.ts +11 -28
- package/src/menus/index.ts +14 -30
- package/src/omnibox/files.ts +10 -14
- package/src/omnibox/index.ts +9 -27
- package/src/omnibox/model.ts +50 -38
- package/src/omnibox/recents.ts +17 -22
- package/src/omnibox/tokens.ts +5 -10
- package/src/omnibox/widget.tsx +14 -19
- package/src/searchReplace/index.ts +7 -27
- package/src/showOutput/index.ts +6 -22
- package/src/sidebar/index.ts +20 -12
- package/src/terminalNotifications/index.ts +42 -25
- package/src/terminals/agentTerminals.ts +25 -49
- package/src/terminals/detection.ts +4 -8
- package/src/terminals/index.ts +12 -101
- package/src/terminals/model.ts +130 -246
- package/src/terminals/tokens.ts +11 -22
- package/src/terminals/widget.tsx +48 -31
- package/src/topBar/icons.ts +3 -8
- package/src/topBar/index.ts +21 -44
- package/src/walkthrough/index.ts +6 -20
- package/src/walkthrough/panel.ts +40 -10
- package/style/about.css +2 -7
- package/style/askAgent.css +8 -40
- package/style/base.css +9 -45
- package/style/chrome.css +108 -308
- package/style/commandBar.css +4 -20
- package/style/customPanel.css +1 -4
- package/style/git.css +22 -184
- package/style/highlight.css +1 -3
- package/style/launcher.css +6 -70
- package/style/omnibox.css +2 -14
- package/style/showOutput.css +0 -6
- package/style/sidebar.css +29 -9
- package/style/tabs.css +5 -13
- package/style/terminals.css +6 -53
- package/style/topBar.css +2 -22
- package/style/walkthrough.css +0 -8
package/src/git/notebookDiff.tsx
CHANGED
|
@@ -10,59 +10,114 @@ import { FileDiff } from '@pierre/diffs/react';
|
|
|
10
10
|
import {
|
|
11
11
|
parseDiffFromFile,
|
|
12
12
|
type DiffsThemeNames,
|
|
13
|
-
type FileContents,
|
|
14
13
|
type FileDiffMetadata
|
|
15
14
|
} from '@pierre/diffs';
|
|
16
15
|
|
|
17
16
|
import { resolveDiffTheme } from './diffTheme';
|
|
18
17
|
|
|
19
|
-
/**
|
|
20
|
-
* The CSS class added to the root of the notebook diff. Selectors that
|
|
21
|
-
* style cell sections, headers, and the per-cell diff blocks hang off this
|
|
22
|
-
* class.
|
|
23
|
-
*/
|
|
24
18
|
const NOTEBOOK_DIFF_CSS_CLASS = 'jp-xtralab-NotebookDiff';
|
|
25
19
|
|
|
26
20
|
/**
|
|
27
|
-
* Minimal slice of nbformat 4.x that the diff inspects.
|
|
28
|
-
* read (attachments, e.g.) are intentionally not narrowed — we still pass
|
|
29
|
-
* them around as part of the cell, but we never project them into the diff.
|
|
21
|
+
* Minimal slice of nbformat 4.x that the diff inspects.
|
|
30
22
|
*/
|
|
31
23
|
interface INotebookCell {
|
|
24
|
+
/**
|
|
25
|
+
* The cell type ('code', 'markdown', or 'raw').
|
|
26
|
+
*/
|
|
32
27
|
cell_type: string;
|
|
28
|
+
/**
|
|
29
|
+
* Stable cell id (nbformat >= 4.5); absent in older notebooks.
|
|
30
|
+
*/
|
|
33
31
|
id?: string;
|
|
32
|
+
/**
|
|
33
|
+
* The cell source, as a single string or an array of lines.
|
|
34
|
+
*/
|
|
34
35
|
source: string | string[];
|
|
36
|
+
/**
|
|
37
|
+
* Execution outputs; present on code cells only.
|
|
38
|
+
*/
|
|
35
39
|
outputs?: INotebookOutput[];
|
|
40
|
+
/**
|
|
41
|
+
* The cell-level metadata.
|
|
42
|
+
*/
|
|
36
43
|
metadata?: Record<string, unknown>;
|
|
44
|
+
/**
|
|
45
|
+
* Execution count of a code cell; `null` when the cell has not run.
|
|
46
|
+
*/
|
|
37
47
|
execution_count?: number | null;
|
|
48
|
+
/**
|
|
49
|
+
* Media attachments keyed by name (markdown and raw cells).
|
|
50
|
+
*/
|
|
38
51
|
attachments?: Record<string, unknown>;
|
|
39
52
|
}
|
|
40
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Minimal slice of an nbformat 4.x cell output that the diff inspects.
|
|
56
|
+
*/
|
|
41
57
|
interface INotebookOutput {
|
|
58
|
+
/**
|
|
59
|
+
* The output type ('stream', 'execute_result', 'display_data', or 'error').
|
|
60
|
+
*/
|
|
42
61
|
output_type: string;
|
|
62
|
+
/**
|
|
63
|
+
* The mime bundle of an `execute_result` or `display_data` output.
|
|
64
|
+
*/
|
|
43
65
|
data?: Record<string, unknown>;
|
|
66
|
+
/**
|
|
67
|
+
* The text of a `stream` output.
|
|
68
|
+
*/
|
|
44
69
|
text?: string | string[];
|
|
70
|
+
/**
|
|
71
|
+
* The stream name ('stdout' or 'stderr') of a `stream` output.
|
|
72
|
+
*/
|
|
45
73
|
name?: string;
|
|
74
|
+
/**
|
|
75
|
+
* The exception name of an `error` output.
|
|
76
|
+
*/
|
|
46
77
|
ename?: string;
|
|
78
|
+
/**
|
|
79
|
+
* The exception message of an `error` output.
|
|
80
|
+
*/
|
|
47
81
|
evalue?: string;
|
|
82
|
+
/**
|
|
83
|
+
* The traceback lines of an `error` output; may contain ANSI escapes.
|
|
84
|
+
*/
|
|
48
85
|
traceback?: string[];
|
|
86
|
+
/**
|
|
87
|
+
* The execution count of an `execute_result` output.
|
|
88
|
+
*/
|
|
49
89
|
execution_count?: number | null;
|
|
90
|
+
/**
|
|
91
|
+
* The output-level metadata.
|
|
92
|
+
*/
|
|
50
93
|
metadata?: Record<string, unknown>;
|
|
51
94
|
}
|
|
52
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Minimal slice of an nbformat 4.x notebook that the diff inspects.
|
|
98
|
+
*/
|
|
53
99
|
interface INotebook {
|
|
100
|
+
/**
|
|
101
|
+
* The notebook cells.
|
|
102
|
+
*/
|
|
54
103
|
cells: INotebookCell[];
|
|
104
|
+
/**
|
|
105
|
+
* The notebook-level metadata.
|
|
106
|
+
*/
|
|
55
107
|
metadata: Record<string, unknown>;
|
|
108
|
+
/**
|
|
109
|
+
* The nbformat major version.
|
|
110
|
+
*/
|
|
56
111
|
nbformat: number;
|
|
112
|
+
/**
|
|
113
|
+
* The nbformat minor version.
|
|
114
|
+
*/
|
|
57
115
|
nbformat_minor: number;
|
|
58
116
|
}
|
|
59
117
|
|
|
60
118
|
/**
|
|
61
|
-
* One entry in the per-cell diff. `
|
|
62
|
-
*
|
|
63
|
-
* metadata via {@link FileDiff}. The `modified` and `unchanged` branches
|
|
64
|
-
* carry the same payload but live on separate union members so `Extract`
|
|
65
|
-
* narrowing works on `kind` literals downstream.
|
|
119
|
+
* One entry in the per-cell diff. `modified` and `unchanged` carry the same
|
|
120
|
+
* payload on separate union members so `Extract` narrowing on `kind` works.
|
|
66
121
|
*/
|
|
67
122
|
type NotebookCellDiff =
|
|
68
123
|
| {
|
|
@@ -91,31 +146,34 @@ type NotebookCellDiff =
|
|
|
91
146
|
};
|
|
92
147
|
|
|
93
148
|
/**
|
|
94
|
-
* Notebook-level diff result returned by {@link buildNotebookDiff}.
|
|
95
|
-
* `null` from the builder when either side fails to parse — the caller then
|
|
96
|
-
* falls back to a raw JSON file diff.
|
|
149
|
+
* Notebook-level diff result returned by {@link buildNotebookDiff}.
|
|
97
150
|
*/
|
|
98
151
|
export interface INotebookDiffResult {
|
|
152
|
+
/**
|
|
153
|
+
* The parsed old revision of the notebook.
|
|
154
|
+
*/
|
|
99
155
|
oldNotebook: INotebook;
|
|
156
|
+
/**
|
|
157
|
+
* The parsed new revision of the notebook.
|
|
158
|
+
*/
|
|
100
159
|
newNotebook: INotebook;
|
|
160
|
+
/**
|
|
161
|
+
* Per-cell diff entries, in new-notebook order with removed cells appended.
|
|
162
|
+
*/
|
|
101
163
|
cells: NotebookCellDiff[];
|
|
102
164
|
/**
|
|
103
|
-
* Kernel language
|
|
104
|
-
* a syntax-highlighting filename for code cells.
|
|
165
|
+
* Kernel language from `metadata.language_info.name`; picks the highlighting filename.
|
|
105
166
|
*/
|
|
106
167
|
language: string | undefined;
|
|
107
168
|
/**
|
|
108
|
-
*
|
|
109
|
-
* nbformat) when it differs between revisions; `null` otherwise so the
|
|
110
|
-
* section can be hidden without further checks.
|
|
169
|
+
* Diff of notebook-level metadata when it differs between revisions; `null` otherwise.
|
|
111
170
|
*/
|
|
112
171
|
notebookMetadataDiff: FileDiffMetadata | null;
|
|
113
172
|
}
|
|
114
173
|
|
|
115
174
|
/**
|
|
116
|
-
* Parse notebook JSON. Returns `null`
|
|
117
|
-
*
|
|
118
|
-
* raw text diff so a malformed file is still inspectable.
|
|
175
|
+
* Parse notebook JSON. Returns `null` when the text is not a notebook so
|
|
176
|
+
* the caller can fall back to a raw text diff.
|
|
119
177
|
*/
|
|
120
178
|
function parseNotebook(text: string): INotebook | null {
|
|
121
179
|
if (text.length === 0) {
|
|
@@ -150,12 +208,6 @@ function emptyNotebook(): INotebook {
|
|
|
150
208
|
};
|
|
151
209
|
}
|
|
152
210
|
|
|
153
|
-
/**
|
|
154
|
-
* nbformat allows multiline string fields (`source`, stream `text`,
|
|
155
|
-
* `text/plain` outputs, …) to be either a single string or an array of
|
|
156
|
-
* strings. Normalize to one string so equality checks and diffs see the
|
|
157
|
-
* same shape regardless of how the writer chose to serialize it.
|
|
158
|
-
*/
|
|
159
211
|
function joinMultiline(value: string | string[] | undefined): string {
|
|
160
212
|
if (value === undefined) {
|
|
161
213
|
return '';
|
|
@@ -168,11 +220,9 @@ function cellSource(cell: INotebookCell): string {
|
|
|
168
220
|
}
|
|
169
221
|
|
|
170
222
|
/**
|
|
171
|
-
* Canonical text
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
* to a `<mime-type>` placeholder so diffing image bytes doesn't drown the
|
|
175
|
-
* panel in base64 noise.
|
|
223
|
+
* Canonical text form of a cell's outputs, used for equality checks and
|
|
224
|
+
* diffs. Rich mime types collapse to a `<mime-type>` placeholder so base64
|
|
225
|
+
* payloads don't drown the diff.
|
|
176
226
|
*/
|
|
177
227
|
function canonicalOutputs(cell: INotebookCell): string {
|
|
178
228
|
const outputs = cell.outputs ?? [];
|
|
@@ -213,10 +263,6 @@ function formatOutput(output: INotebookOutput): string {
|
|
|
213
263
|
case 'error': {
|
|
214
264
|
const ename = output.ename ?? 'Error';
|
|
215
265
|
const evalue = output.evalue ?? '';
|
|
216
|
-
// Tracebacks contain ANSI escape codes by default. Strip them so the
|
|
217
|
-
// diff stays readable — coloring information has no analog in a plain
|
|
218
|
-
// text diff, and the escapes themselves would inflate every traceback
|
|
219
|
-
// line into a fake change.
|
|
220
266
|
const traceback = (output.traceback ?? []).map(stripAnsi).join('\n');
|
|
221
267
|
return `[error]\n${ename}: ${evalue}\n${traceback}`;
|
|
222
268
|
}
|
|
@@ -225,10 +271,7 @@ function formatOutput(output: INotebookOutput): string {
|
|
|
225
271
|
}
|
|
226
272
|
}
|
|
227
273
|
|
|
228
|
-
// Matching ANSI
|
|
229
|
-
// the pattern, which is what no-control-regex flags. Disable the rule on
|
|
230
|
-
// this single literal — silently emitting un-stripped escapes would just
|
|
231
|
-
// reintroduce the very noise stripAnsi exists to remove.
|
|
274
|
+
// Matching ANSI escapes requires a control character in the pattern.
|
|
232
275
|
// eslint-disable-next-line no-control-regex
|
|
233
276
|
const ANSI_ESCAPE_RE = /\x1B\[[0-9;]*[A-Za-z]/g;
|
|
234
277
|
|
|
@@ -237,10 +280,8 @@ function stripAnsi(value: string): string {
|
|
|
237
280
|
}
|
|
238
281
|
|
|
239
282
|
/**
|
|
240
|
-
* Stable JSON of the cell's `metadata
|
|
241
|
-
* so
|
|
242
|
-
* An empty metadata object returns the empty string so freshly-added cells
|
|
243
|
-
* that carry the default `{}` don't produce a `+{}` placeholder section.
|
|
283
|
+
* Stable sorted JSON of the cell's `metadata`; empty metadata returns ''
|
|
284
|
+
* so cells carrying the default `{}` don't produce a `+{}` diff section.
|
|
244
285
|
*/
|
|
245
286
|
function canonicalMetadata(cell: INotebookCell): string {
|
|
246
287
|
const md = cell.metadata ?? {};
|
|
@@ -250,11 +291,6 @@ function canonicalMetadata(cell: INotebookCell): string {
|
|
|
250
291
|
return stableStringify(md);
|
|
251
292
|
}
|
|
252
293
|
|
|
253
|
-
/**
|
|
254
|
-
* Stable JSON of the notebook-level metadata bundle (kernelspec, language
|
|
255
|
-
* info, nbformat). We diff the bundle as a single block so the user sees
|
|
256
|
-
* all environment-level changes in one place.
|
|
257
|
-
*/
|
|
258
294
|
function canonicalNotebookMetadata(notebook: INotebook): string {
|
|
259
295
|
return stableStringify({
|
|
260
296
|
metadata: notebook.metadata,
|
|
@@ -279,11 +315,8 @@ function sortKeysReplacer(key: string, value: unknown): unknown {
|
|
|
279
315
|
}
|
|
280
316
|
|
|
281
317
|
/**
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
* outputs, metadata — using the same canonical representations the diff
|
|
285
|
-
* itself sees, so an "equal" verdict here implies all per-cell diff blocks
|
|
286
|
-
* would be empty.
|
|
318
|
+
* Classify matched cells as `unchanged` vs `modified` using the same
|
|
319
|
+
* canonical forms the diff renders, so "equal" implies empty per-cell diffs.
|
|
287
320
|
*/
|
|
288
321
|
function cellsAreEqual(a: INotebookCell, b: INotebookCell): boolean {
|
|
289
322
|
return (
|
|
@@ -295,16 +328,9 @@ function cellsAreEqual(a: INotebookCell, b: INotebookCell): boolean {
|
|
|
295
328
|
}
|
|
296
329
|
|
|
297
330
|
/**
|
|
298
|
-
* Align cells between two
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
* older notebooks still produce a sensible diff.
|
|
302
|
-
*
|
|
303
|
-
* Output ordering: entries follow new-file order, with cells that exist
|
|
304
|
-
* only in the old revision appended at the end as `removed`. We don't try
|
|
305
|
-
* to interleave removals at their original position — for typical notebook
|
|
306
|
-
* edits this is more readable than guessing alignment between unrelated
|
|
307
|
-
* insertions and deletions.
|
|
331
|
+
* Align cells between two revisions: stable ids (nbformat ≥ 4.5) match by
|
|
332
|
+
* id, the rest positionally. Entries follow new-file order with old-only
|
|
333
|
+
* cells appended as `removed`.
|
|
308
334
|
*/
|
|
309
335
|
function alignNotebookCells(
|
|
310
336
|
oldCells: INotebookCell[],
|
|
@@ -320,8 +346,6 @@ function alignNotebookCells(
|
|
|
320
346
|
const consumed = new Set<number>();
|
|
321
347
|
const entries: NotebookCellDiff[] = [];
|
|
322
348
|
|
|
323
|
-
// Cursor used for positional matching of unidentified cells. Only advances
|
|
324
|
-
// forward so we don't double-match a single old cell.
|
|
325
349
|
let positionalCursor = 0;
|
|
326
350
|
|
|
327
351
|
for (let newIndex = 0; newIndex < newCells.length; newIndex++) {
|
|
@@ -374,19 +398,25 @@ function alignNotebookCells(
|
|
|
374
398
|
return entries;
|
|
375
399
|
}
|
|
376
400
|
|
|
401
|
+
/**
|
|
402
|
+
* Options for {@link buildNotebookDiff}.
|
|
403
|
+
*/
|
|
377
404
|
interface IBuildNotebookDiffOptions {
|
|
405
|
+
/**
|
|
406
|
+
* The raw text of the old notebook revision.
|
|
407
|
+
*/
|
|
378
408
|
oldText: string;
|
|
409
|
+
/**
|
|
410
|
+
* The raw text of the new notebook revision.
|
|
411
|
+
*/
|
|
379
412
|
newText: string;
|
|
380
413
|
}
|
|
381
414
|
|
|
382
415
|
/**
|
|
383
|
-
* Build a
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
* An empty string on either side is treated as an empty notebook (no
|
|
388
|
-
* cells, default metadata) so a freshly-added or deleted notebook still
|
|
389
|
-
* produces a sensible cell-by-cell diff.
|
|
416
|
+
* Build a notebook diff from the text of two revisions. Returns `null` when
|
|
417
|
+
* either side fails to parse (caller falls back to a raw text diff); an
|
|
418
|
+
* empty string counts as an empty notebook so added/deleted notebooks still
|
|
419
|
+
* diff cell by cell.
|
|
390
420
|
*/
|
|
391
421
|
export function buildNotebookDiff(
|
|
392
422
|
options: IBuildNotebookDiffOptions
|
|
@@ -425,10 +455,8 @@ function detectLanguage(notebook: INotebook): string | undefined {
|
|
|
425
455
|
}
|
|
426
456
|
|
|
427
457
|
/**
|
|
428
|
-
* Pick a filename whose extension drives `@pierre/diffs`
|
|
429
|
-
*
|
|
430
|
-
* influences token-level rendering, so any plausible extension that maps
|
|
431
|
-
* to the right language is fine.
|
|
458
|
+
* Pick a filename whose extension drives the `@pierre/diffs` highlighter;
|
|
459
|
+
* the library never reads the file, only the extension matters.
|
|
432
460
|
*/
|
|
433
461
|
function cellFilename(
|
|
434
462
|
cell: INotebookCell,
|
|
@@ -463,49 +491,23 @@ function cellFilename(
|
|
|
463
491
|
}
|
|
464
492
|
|
|
465
493
|
/**
|
|
466
|
-
*
|
|
467
|
-
* filename so each per-cell sub-diff (source, metadata) gets the right
|
|
468
|
-
* extension and therefore the right highlighter.
|
|
494
|
+
* One rendered sub-diff (source or metadata) of a cell entry.
|
|
469
495
|
*/
|
|
470
|
-
function pierreFiles(
|
|
471
|
-
kind: 'source' | 'metadata',
|
|
472
|
-
oldText: string,
|
|
473
|
-
newText: string,
|
|
474
|
-
oldCell: INotebookCell | null,
|
|
475
|
-
newCell: INotebookCell | null,
|
|
476
|
-
language: string | undefined
|
|
477
|
-
): { oldFile: FileContents; newFile: FileContents } {
|
|
478
|
-
let name: string;
|
|
479
|
-
if (kind === 'metadata') {
|
|
480
|
-
name = 'cell-metadata.json';
|
|
481
|
-
} else {
|
|
482
|
-
// Use the new cell's type/language when available; fall back to the old
|
|
483
|
-
// side so a deleted cell still gets the right highlighter.
|
|
484
|
-
const reference = newCell ?? oldCell;
|
|
485
|
-
name = reference !== null ? cellFilename(reference, language) : 'cell.txt';
|
|
486
|
-
}
|
|
487
|
-
return {
|
|
488
|
-
oldFile: { name, contents: oldText },
|
|
489
|
-
newFile: { name, contents: newText }
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
|
|
493
496
|
interface ICellSubDiff {
|
|
497
|
+
/**
|
|
498
|
+
* The cell section the diff covers.
|
|
499
|
+
*/
|
|
494
500
|
kind: 'source' | 'metadata';
|
|
501
|
+
/**
|
|
502
|
+
* The parsed `@pierre/diffs` diff for the section.
|
|
503
|
+
*/
|
|
495
504
|
metadata: FileDiffMetadata;
|
|
496
505
|
}
|
|
497
506
|
|
|
498
507
|
/**
|
|
499
|
-
*
|
|
500
|
-
*
|
|
501
|
-
*
|
|
502
|
-
* an empty counterpart so the library treats every line as an addition or
|
|
503
|
-
* deletion respectively.
|
|
504
|
-
*
|
|
505
|
-
* Outputs are deliberately *not* included here — they're rendered through
|
|
506
|
-
* rendermime side-by-side in {@link OutputsSection} so images render as
|
|
507
|
-
* images, HTML as HTML, etc. The canonical text form survives for equality
|
|
508
|
-
* (it drives the `unchanged` classification) but not for display.
|
|
508
|
+
* Source / metadata sub-diffs for one cell entry; empty for `unchanged`.
|
|
509
|
+
* Outputs are excluded on purpose — they render through rendermime in
|
|
510
|
+
* {@link OutputsSection}; their canonical text form only drives equality.
|
|
509
511
|
*/
|
|
510
512
|
function buildCellSubDiffs(
|
|
511
513
|
entry: NotebookCellDiff,
|
|
@@ -516,6 +518,10 @@ function buildCellSubDiffs(
|
|
|
516
518
|
}
|
|
517
519
|
const oldCell = entry.kind === 'added' ? null : entry.oldCell;
|
|
518
520
|
const newCell = entry.kind === 'removed' ? null : entry.newCell;
|
|
521
|
+
const sourceName = cellFilename(
|
|
522
|
+
entry.kind === 'removed' ? entry.oldCell : entry.newCell,
|
|
523
|
+
language
|
|
524
|
+
);
|
|
519
525
|
|
|
520
526
|
const oldSource = oldCell !== null ? cellSource(oldCell) : '';
|
|
521
527
|
const newSource = newCell !== null ? cellSource(newCell) : '';
|
|
@@ -535,30 +541,20 @@ function buildCellSubDiffs(
|
|
|
535
541
|
if (section.oldText === section.newText) {
|
|
536
542
|
continue;
|
|
537
543
|
}
|
|
538
|
-
const
|
|
539
|
-
section.kind
|
|
540
|
-
section.oldText,
|
|
541
|
-
section.newText,
|
|
542
|
-
oldCell,
|
|
543
|
-
newCell,
|
|
544
|
-
language
|
|
545
|
-
);
|
|
544
|
+
const name =
|
|
545
|
+
section.kind === 'metadata' ? 'cell-metadata.json' : sourceName;
|
|
546
546
|
subdiffs.push({
|
|
547
547
|
kind: section.kind,
|
|
548
|
-
metadata: parseDiffFromFile(
|
|
548
|
+
metadata: parseDiffFromFile(
|
|
549
|
+
oldCell === null ? null : { name, contents: section.oldText },
|
|
550
|
+
newCell === null ? null : { name, contents: section.newText }
|
|
551
|
+
)
|
|
549
552
|
});
|
|
550
553
|
}
|
|
551
554
|
|
|
552
555
|
return subdiffs;
|
|
553
556
|
}
|
|
554
557
|
|
|
555
|
-
/**
|
|
556
|
-
* Common diff library options shared across every per-cell sub-diff and
|
|
557
|
-
* the notebook-level metadata diff. Split mode matches the file-diff path
|
|
558
|
-
* and gives the user the same left=old / right=new mental model inside
|
|
559
|
-
* each cell — cells stack vertically but each cell internally reads
|
|
560
|
-
* side-by-side, which matches how nbdime renders.
|
|
561
|
-
*/
|
|
562
558
|
function diffLibraryOptions(theme: DiffsThemeNames, dark: boolean) {
|
|
563
559
|
return {
|
|
564
560
|
diffStyle: 'split' as const,
|
|
@@ -569,11 +565,8 @@ function diffLibraryOptions(theme: DiffsThemeNames, dark: boolean) {
|
|
|
569
565
|
}
|
|
570
566
|
|
|
571
567
|
/**
|
|
572
|
-
* Mount a Lumino {@link Widget} inside a React tree
|
|
573
|
-
*
|
|
574
|
-
* Lumino owns the content the widget paints into it. Detach + dispose
|
|
575
|
-
* happens on unmount or when a new widget arrives, so the parent doesn't
|
|
576
|
-
* need to manage the widget's lifecycle separately.
|
|
568
|
+
* Mount a Lumino {@link Widget} inside a React tree; detach + dispose
|
|
569
|
+
* happens on unmount or when a new widget arrives.
|
|
577
570
|
*/
|
|
578
571
|
function LuminoWidget(props: {
|
|
579
572
|
widget: Widget;
|
|
@@ -589,31 +582,14 @@ function LuminoWidget(props: {
|
|
|
589
582
|
try {
|
|
590
583
|
Widget.attach(widget, host);
|
|
591
584
|
} catch (err) {
|
|
592
|
-
// Lumino refuses to attach
|
|
593
|
-
// the
|
|
594
|
-
// there's nothing useful to do here — log and bail so the parent
|
|
595
|
-
// tree still mounts.
|
|
585
|
+
// Lumino refuses to attach when the host is not in the document or
|
|
586
|
+
// the widget is attached elsewhere; log and let the tree mount.
|
|
596
587
|
console.warn('xtralab: Widget.attach failed', err);
|
|
597
588
|
return;
|
|
598
589
|
}
|
|
599
590
|
return () => {
|
|
600
|
-
//
|
|
601
|
-
//
|
|
602
|
-
// connected to the document. During a parent React unmount the
|
|
603
|
-
// host element gets removed from the DOM before this cleanup
|
|
604
|
-
// runs, so `node.isConnected` is already `false` while the
|
|
605
|
-
// `IsAttached` flag is still set from our earlier `Widget.attach`
|
|
606
|
-
// call. Calling `Widget.detach` in that state would throw, and —
|
|
607
|
-
// worse — `widget.dispose()` re-enters the same detach branch
|
|
608
|
-
// internally (its `else if (this.isAttached)` guard does not
|
|
609
|
-
// check the DOM connection), so leaving the flag stale produces
|
|
610
|
-
// the noisy "Widget is not attached" warning out of `dispose`.
|
|
611
|
-
//
|
|
612
|
-
// Resolve the race by driving Lumino's detach lifecycle by hand
|
|
613
|
-
// when the host has already been torn down: send `BeforeDetach`
|
|
614
|
-
// and `AfterDetach` so the layout cleanup hooks run and the flag
|
|
615
|
-
// clears, then dispose. When the host is still connected the
|
|
616
|
-
// ordinary `Widget.detach` path handles both steps for us.
|
|
591
|
+
// On parent unmount React removes the host first; detach (and dispose,
|
|
592
|
+
// which re-enters it) throws on a disconnected node, so message by hand.
|
|
617
593
|
if (widget.isAttached) {
|
|
618
594
|
if (widget.node.isConnected) {
|
|
619
595
|
try {
|
|
@@ -643,14 +619,8 @@ function LuminoWidget(props: {
|
|
|
643
619
|
}
|
|
644
620
|
|
|
645
621
|
/**
|
|
646
|
-
* Render
|
|
647
|
-
*
|
|
648
|
-
* cell layout can drop a fully-rendered output column on either side
|
|
649
|
-
* without re-implementing rich mime rendering.
|
|
650
|
-
*
|
|
651
|
-
* The output area is created `trusted` because the source git ref is on
|
|
652
|
-
* the user's machine (working tree / index / HEAD). A diff viewer that
|
|
653
|
-
* sandboxed its own user's outputs would just be inconvenient.
|
|
622
|
+
* Render nbformat outputs through rendermime like a live notebook would.
|
|
623
|
+
* Trusted: the source git ref already lives on the user's machine.
|
|
654
624
|
*/
|
|
655
625
|
function OutputsPreview(props: {
|
|
656
626
|
outputs: INotebookOutput[];
|
|
@@ -669,11 +639,8 @@ function OutputsPreview(props: {
|
|
|
669
639
|
}
|
|
670
640
|
|
|
671
641
|
/**
|
|
672
|
-
* Render markdown source through rendermime so the
|
|
673
|
-
*
|
|
674
|
-
* code fences, sanitized HTML, …). Used for both the rendered preview that
|
|
675
|
-
* sits next to a markdown cell's source diff, and for unchanged markdown
|
|
676
|
-
* cells when the user expands them.
|
|
642
|
+
* Render markdown source through rendermime so the preview matches a live
|
|
643
|
+
* notebook (LaTeX, code fences, sanitized HTML).
|
|
677
644
|
*/
|
|
678
645
|
function MarkdownPreview(props: {
|
|
679
646
|
source: string;
|
|
@@ -698,11 +665,8 @@ function MarkdownPreview(props: {
|
|
|
698
665
|
}
|
|
699
666
|
|
|
700
667
|
/**
|
|
701
|
-
* Two-column "old | new" layout
|
|
702
|
-
*
|
|
703
|
-
* kind: modified shows both, added shows only new, removed shows only old,
|
|
704
|
-
* unchanged collapses to a single full-width pane (since both sides
|
|
705
|
-
* render the same content).
|
|
668
|
+
* Two-column "old | new" layout; collapses to a single full-width pane
|
|
669
|
+
* when only one side has content.
|
|
706
670
|
*/
|
|
707
671
|
function SideBySidePanes(props: {
|
|
708
672
|
label: string;
|
|
@@ -745,11 +709,8 @@ function SideBySidePanes(props: {
|
|
|
745
709
|
}
|
|
746
710
|
|
|
747
711
|
/**
|
|
748
|
-
* Rendered outputs
|
|
749
|
-
*
|
|
750
|
-
* side is shown. Returns `null` (so the section is hidden entirely) when
|
|
751
|
-
* neither side has any outputs, or when the same set of outputs appears
|
|
752
|
-
* on both sides and the host doesn't want the duplicate render.
|
|
712
|
+
* Rendered outputs for a cell, old / new through rendermime; `null` when
|
|
713
|
+
* neither side has outputs.
|
|
753
714
|
*/
|
|
754
715
|
function OutputsSection(props: {
|
|
755
716
|
entry: NotebookCellDiff;
|
|
@@ -767,11 +728,6 @@ function OutputsSection(props: {
|
|
|
767
728
|
if (!hasOld && !hasNew) {
|
|
768
729
|
return null;
|
|
769
730
|
}
|
|
770
|
-
// For added / removed cells the parent already lives in a single outer
|
|
771
|
-
// column — rendering the populated side full width within that column
|
|
772
|
-
// is the right move. SideBySidePanes itself collapses to a single grid
|
|
773
|
-
// column when only one side has content, so we can use it for both
|
|
774
|
-
// cases by feeding it only the side(s) we want.
|
|
775
731
|
const showOld = placement !== 'right' && hasOld;
|
|
776
732
|
const showNew = placement !== 'left' && hasNew;
|
|
777
733
|
if (rendermime === null) {
|
|
@@ -805,9 +761,7 @@ function OutputsSection(props: {
|
|
|
805
761
|
}
|
|
806
762
|
|
|
807
763
|
/**
|
|
808
|
-
* Rendered markdown preview
|
|
809
|
-
* source diff so the user sees both the source-level changes and how the
|
|
810
|
-
* rendered cell looks. Skipped for non-markdown cells.
|
|
764
|
+
* Rendered markdown preview beside the source diff; skipped for non-markdown cells.
|
|
811
765
|
*/
|
|
812
766
|
function MarkdownPreviewSection(props: {
|
|
813
767
|
entry: NotebookCellDiff;
|
|
@@ -838,14 +792,25 @@ function MarkdownPreviewSection(props: {
|
|
|
838
792
|
);
|
|
839
793
|
}
|
|
840
794
|
|
|
795
|
+
/**
|
|
796
|
+
* Props for {@link NotebookDiffView}.
|
|
797
|
+
*/
|
|
841
798
|
interface INotebookDiffViewProps {
|
|
799
|
+
/**
|
|
800
|
+
* The notebook diff to render.
|
|
801
|
+
*/
|
|
842
802
|
diff: INotebookDiffResult;
|
|
803
|
+
/**
|
|
804
|
+
* Whether the active JupyterLab theme is dark.
|
|
805
|
+
*/
|
|
843
806
|
dark: boolean;
|
|
844
807
|
/**
|
|
845
|
-
* Whether a Pierre JupyterLab theme is active; selects Pierre
|
|
846
|
-
* highlighting vs the CSS-variable theme (see {@link resolveDiffTheme}).
|
|
808
|
+
* Whether a Pierre JupyterLab theme is active; selects Pierre highlighting vs the CSS-variable theme.
|
|
847
809
|
*/
|
|
848
810
|
pierreTheme: boolean;
|
|
811
|
+
/**
|
|
812
|
+
* Registry rendering markdown and output previews; `null` falls back to plain text.
|
|
813
|
+
*/
|
|
849
814
|
rendermime: IRenderMimeRegistry | null;
|
|
850
815
|
/**
|
|
851
816
|
* Translation bundle for user-facing strings.
|
|
@@ -854,14 +819,9 @@ interface INotebookDiffViewProps {
|
|
|
854
819
|
}
|
|
855
820
|
|
|
856
821
|
/**
|
|
857
|
-
*
|
|
858
|
-
* the
|
|
859
|
-
*
|
|
860
|
-
* internal split-mode FileDiff aligns with the outer columns), added
|
|
861
|
-
* cells live in the right column with an empty placeholder on the left,
|
|
862
|
-
* and removed cells live in the left column with an empty placeholder on
|
|
863
|
-
* the right. The diff library's red/green tint within each cell makes
|
|
864
|
-
* the orientation clear without a separate column header.
|
|
822
|
+
* Notebook-level diff view: a 2-column grid where the left column tracks
|
|
823
|
+
* the old notebook and the right the new one — modified/unchanged cells
|
|
824
|
+
* span both, added cells sit right, removed cells sit left.
|
|
865
825
|
*/
|
|
866
826
|
export function NotebookDiffView(
|
|
867
827
|
props: INotebookDiffViewProps
|
|
@@ -898,12 +858,8 @@ export function NotebookDiffView(
|
|
|
898
858
|
}
|
|
899
859
|
|
|
900
860
|
/**
|
|
901
|
-
* Place
|
|
902
|
-
*
|
|
903
|
-
* column with an empty placeholder on the left; removed entries take the
|
|
904
|
-
* left column with an empty placeholder on the right. The placeholders
|
|
905
|
-
* are visible (subtle dashed outline) so the user can see *where* a
|
|
906
|
-
* cell was inserted or removed relative to the other side.
|
|
861
|
+
* Place one cell entry into the 2-column grid, with a visible placeholder
|
|
862
|
+
* marking where a cell was inserted or removed on the other side.
|
|
907
863
|
*/
|
|
908
864
|
function CellEntryRow(props: ICellDiffBlockProps): React.ReactElement {
|
|
909
865
|
const { entry, trans } = props;
|
|
@@ -951,10 +907,25 @@ function EmptyCellPlaceholder(props: {
|
|
|
951
907
|
);
|
|
952
908
|
}
|
|
953
909
|
|
|
910
|
+
/**
|
|
911
|
+
* Cell counts per diff kind, shown in the header.
|
|
912
|
+
*/
|
|
954
913
|
interface INotebookDiffSummary {
|
|
914
|
+
/**
|
|
915
|
+
* The number of added cells.
|
|
916
|
+
*/
|
|
955
917
|
added: number;
|
|
918
|
+
/**
|
|
919
|
+
* The number of removed cells.
|
|
920
|
+
*/
|
|
956
921
|
removed: number;
|
|
922
|
+
/**
|
|
923
|
+
* The number of modified cells.
|
|
924
|
+
*/
|
|
957
925
|
modified: number;
|
|
926
|
+
/**
|
|
927
|
+
* The number of unchanged cells.
|
|
928
|
+
*/
|
|
958
929
|
unchanged: number;
|
|
959
930
|
}
|
|
960
931
|
|
|
@@ -1023,38 +994,50 @@ function cellEntryKey(entry: NotebookCellDiff): string {
|
|
|
1023
994
|
return `${entry.kind}:${entry.newIndex}:${entry.newCell.id ?? entry.oldCell.id ?? ''}`;
|
|
1024
995
|
}
|
|
1025
996
|
|
|
997
|
+
/**
|
|
998
|
+
* Props shared by {@link CellEntryRow} and {@link CellDiffBlock}.
|
|
999
|
+
*/
|
|
1026
1000
|
interface ICellDiffBlockProps {
|
|
1001
|
+
/**
|
|
1002
|
+
* The cell diff entry to render.
|
|
1003
|
+
*/
|
|
1027
1004
|
entry: NotebookCellDiff;
|
|
1005
|
+
/**
|
|
1006
|
+
* The kernel language driving code-cell highlighting.
|
|
1007
|
+
*/
|
|
1028
1008
|
language: string | undefined;
|
|
1009
|
+
/**
|
|
1010
|
+
* The `@pierre/diffs` theme name.
|
|
1011
|
+
*/
|
|
1029
1012
|
theme: DiffsThemeNames;
|
|
1013
|
+
/**
|
|
1014
|
+
* Whether the active JupyterLab theme is dark.
|
|
1015
|
+
*/
|
|
1030
1016
|
dark: boolean;
|
|
1017
|
+
/**
|
|
1018
|
+
* Registry rendering markdown and output previews; `null` falls back to plain text.
|
|
1019
|
+
*/
|
|
1031
1020
|
rendermime: IRenderMimeRegistry | null;
|
|
1021
|
+
/**
|
|
1022
|
+
* The translation bundle for user-facing strings.
|
|
1023
|
+
*/
|
|
1032
1024
|
trans: TranslationBundle;
|
|
1033
1025
|
}
|
|
1034
1026
|
|
|
1035
|
-
/**
|
|
1036
|
-
* How the cell card should sit inside the outer 2-column grid:
|
|
1037
|
-
* - `full` → spans both columns (modified / unchanged cells)
|
|
1038
|
-
* - `left` → left column only (removed cells)
|
|
1039
|
-
* - `right` → right column only (added cells)
|
|
1040
|
-
*
|
|
1041
|
-
* Drives both grid placement and the inner layout choices: a single-column
|
|
1042
|
-
* placement uses unified-mode FileDiff for the source (since one side is
|
|
1043
|
-
* empty) and renders only the populated side of outputs / markdown
|
|
1044
|
-
* previews, while a `full` placement keeps split mode and side-by-side
|
|
1045
|
-
* panes.
|
|
1046
|
-
*/
|
|
1047
1027
|
type CellPlacement = 'full' | 'left' | 'right';
|
|
1048
1028
|
|
|
1029
|
+
/**
|
|
1030
|
+
* Cell diff block props with a resolved grid placement.
|
|
1031
|
+
*/
|
|
1049
1032
|
interface IPlacedCellDiffBlockProps extends ICellDiffBlockProps {
|
|
1033
|
+
/**
|
|
1034
|
+
* The grid column the block occupies: both ('full'), old ('left'), or new ('right').
|
|
1035
|
+
*/
|
|
1050
1036
|
placement: CellPlacement;
|
|
1051
1037
|
}
|
|
1052
1038
|
|
|
1053
1039
|
function CellDiffBlock(props: IPlacedCellDiffBlockProps): React.ReactElement {
|
|
1054
1040
|
const { entry, language, theme, dark, rendermime, placement, trans } = props;
|
|
1055
|
-
// Unchanged cells start collapsed — the user opted out of seeing those
|
|
1056
|
-
// sections by virtue of them being unchanged. A toggle lets them peek if
|
|
1057
|
-
// they want.
|
|
1058
1041
|
const [collapsed, setCollapsed] = React.useState<boolean>(
|
|
1059
1042
|
entry.kind === 'unchanged'
|
|
1060
1043
|
);
|
|
@@ -1067,8 +1050,7 @@ function CellDiffBlock(props: IPlacedCellDiffBlockProps): React.ReactElement {
|
|
|
1067
1050
|
const cellType = referenceCellType(entry);
|
|
1068
1051
|
const indexLabel = cellIndexLabel(entry);
|
|
1069
1052
|
const isCodeCell = cellType === 'code';
|
|
1070
|
-
//
|
|
1071
|
-
// never carry outputs in nbformat.
|
|
1053
|
+
// Only code cells carry outputs in nbformat.
|
|
1072
1054
|
const showOutputs = isCodeCell && entry.kind !== 'unchanged';
|
|
1073
1055
|
|
|
1074
1056
|
return (
|
|
@@ -1171,11 +1153,8 @@ function CellSubDiff(props: {
|
|
|
1171
1153
|
trans: TranslationBundle;
|
|
1172
1154
|
}): React.ReactElement {
|
|
1173
1155
|
const { kind, metadata, theme, dark, placement, trans } = props;
|
|
1174
|
-
//
|
|
1175
|
-
//
|
|
1176
|
-
// added / removed cells the diff sits inside a single outer column, so
|
|
1177
|
-
// unified mode keeps the content readable instead of leaving half the
|
|
1178
|
-
// column empty.
|
|
1156
|
+
// Full-width cells align split mode with the outer old|new lanes;
|
|
1157
|
+
// single-column cells read better unified.
|
|
1179
1158
|
const diffStyle: 'split' | 'unified' =
|
|
1180
1159
|
placement === 'full' ? 'split' : 'unified';
|
|
1181
1160
|
return (
|
|
@@ -1185,10 +1164,8 @@ function CellSubDiff(props: {
|
|
|
1185
1164
|
</div>
|
|
1186
1165
|
<FileDiff
|
|
1187
1166
|
fileDiff={metadata}
|
|
1188
|
-
//
|
|
1189
|
-
//
|
|
1190
|
-
// runs on the main thread. Cell diffs are small enough that this is
|
|
1191
|
-
// not a performance concern.
|
|
1167
|
+
// The worker bootstrap can't resolve through JupyterLab's federation
|
|
1168
|
+
// pipeline (see diffSurface.tsx); run on the main thread.
|
|
1192
1169
|
disableWorkerPool={true}
|
|
1193
1170
|
options={{ ...diffLibraryOptions(theme, dark), diffStyle }}
|
|
1194
1171
|
/>
|
|
@@ -1239,12 +1216,8 @@ function NotebookMetadataBlock(props: {
|
|
|
1239
1216
|
}
|
|
1240
1217
|
|
|
1241
1218
|
/**
|
|
1242
|
-
*
|
|
1243
|
-
*
|
|
1244
|
-
* cells go through rendermime (if available) so they look like the live
|
|
1245
|
-
* notebook would render them, code cells fall through to a verbatim
|
|
1246
|
-
* pre-formatted block, and any code outputs render through rendermime
|
|
1247
|
-
* below the source.
|
|
1219
|
+
* Expanded body of an unchanged cell, rendered once full-width: markdown
|
|
1220
|
+
* through rendermime, code verbatim with outputs below.
|
|
1248
1221
|
*/
|
|
1249
1222
|
function UnchangedCellBody(props: {
|
|
1250
1223
|
entry: Extract<NotebookCellDiff, { kind: 'unchanged' }>;
|