xtralab 0.15.0 → 0.15.2
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 +65 -30
- 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 +29 -19
- package/lib/fileBrowser/commands.js +83 -97
- 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 +70 -222
- package/lib/fileBrowser/gitStatus.d.ts +16 -10
- package/lib/fileBrowser/gitStatus.js +31 -30
- 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 +141 -48
- package/lib/fileBrowser/widget.js +102 -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 +45 -5
- package/lib/git/commands.js +20 -13
- package/lib/git/diffModel.js +4 -0
- package/lib/git/diffPreferences.d.ts +90 -0
- package/lib/git/diffPreferences.js +107 -0
- package/lib/git/diffProvider.js +10 -2
- package/lib/git/diffSurface.d.ts +64 -63
- package/lib/git/diffSurface.js +72 -213
- package/lib/git/diffTheme.d.ts +7 -0
- package/lib/git/diffTheme.js +23 -0
- package/lib/git/diffWidget.d.ts +70 -14
- package/lib/git/diffWidget.js +100 -94
- package/lib/git/imageDiff.d.ts +14 -6
- package/lib/git/imageDiff.js +19 -75
- package/lib/git/index.d.ts +0 -3
- package/lib/git/index.js +44 -10
- package/lib/git/notebookDiff.d.ts +110 -33
- package/lib/git/notebookDiff.js +55 -197
- 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 +5 -3
- package/schema/git-diff-preferences.json +42 -0
- 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 +119 -96
- package/src/fileBrowser/contents.ts +3 -0
- package/src/fileBrowser/dragAndDrop.ts +17 -7
- package/src/fileBrowser/fileBrowser.tsx +86 -230
- package/src/fileBrowser/gitStatus.ts +36 -33
- 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 +171 -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 +83 -11
- package/src/git/diffModel.ts +25 -0
- package/src/git/diffPreferences.ts +192 -0
- package/src/git/diffProvider.tsx +11 -2
- package/src/git/diffSurface.tsx +165 -286
- package/src/git/diffTheme.ts +24 -0
- package/src/git/diffWidget.tsx +194 -134
- package/src/git/imageDiff.tsx +56 -84
- package/src/git/index.ts +54 -10
- package/src/git/notebookDiff.tsx +214 -240
- 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 +26 -186
- 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
|
-
import { resolveDiffTheme } from './diffTheme';
|
|
16
|
+
import { DIFF_SCROLLBAR_CSS, 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,45 +541,33 @@ 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,
|
|
565
561
|
disableFileHeader: true,
|
|
566
562
|
theme,
|
|
567
|
-
themeType: dark ? ('dark' as const) : ('light' as const)
|
|
563
|
+
themeType: dark ? ('dark' as const) : ('light' as const),
|
|
564
|
+
unsafeCSS: DIFF_SCROLLBAR_CSS
|
|
568
565
|
};
|
|
569
566
|
}
|
|
570
567
|
|
|
571
568
|
/**
|
|
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.
|
|
569
|
+
* Mount a Lumino {@link Widget} inside a React tree; detach + dispose
|
|
570
|
+
* happens on unmount or when a new widget arrives.
|
|
577
571
|
*/
|
|
578
572
|
function LuminoWidget(props: {
|
|
579
573
|
widget: Widget;
|
|
@@ -589,31 +583,14 @@ function LuminoWidget(props: {
|
|
|
589
583
|
try {
|
|
590
584
|
Widget.attach(widget, host);
|
|
591
585
|
} 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.
|
|
586
|
+
// Lumino refuses to attach when the host is not in the document or
|
|
587
|
+
// the widget is attached elsewhere; log and let the tree mount.
|
|
596
588
|
console.warn('xtralab: Widget.attach failed', err);
|
|
597
589
|
return;
|
|
598
590
|
}
|
|
599
591
|
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.
|
|
592
|
+
// On parent unmount React removes the host first; detach (and dispose,
|
|
593
|
+
// which re-enters it) throws on a disconnected node, so message by hand.
|
|
617
594
|
if (widget.isAttached) {
|
|
618
595
|
if (widget.node.isConnected) {
|
|
619
596
|
try {
|
|
@@ -643,14 +620,8 @@ function LuminoWidget(props: {
|
|
|
643
620
|
}
|
|
644
621
|
|
|
645
622
|
/**
|
|
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.
|
|
623
|
+
* Render nbformat outputs through rendermime like a live notebook would.
|
|
624
|
+
* Trusted: the source git ref already lives on the user's machine.
|
|
654
625
|
*/
|
|
655
626
|
function OutputsPreview(props: {
|
|
656
627
|
outputs: INotebookOutput[];
|
|
@@ -669,11 +640,8 @@ function OutputsPreview(props: {
|
|
|
669
640
|
}
|
|
670
641
|
|
|
671
642
|
/**
|
|
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.
|
|
643
|
+
* Render markdown source through rendermime so the preview matches a live
|
|
644
|
+
* notebook (LaTeX, code fences, sanitized HTML).
|
|
677
645
|
*/
|
|
678
646
|
function MarkdownPreview(props: {
|
|
679
647
|
source: string;
|
|
@@ -698,11 +666,8 @@ function MarkdownPreview(props: {
|
|
|
698
666
|
}
|
|
699
667
|
|
|
700
668
|
/**
|
|
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).
|
|
669
|
+
* Two-column "old | new" layout; collapses to a single full-width pane
|
|
670
|
+
* when only one side has content.
|
|
706
671
|
*/
|
|
707
672
|
function SideBySidePanes(props: {
|
|
708
673
|
label: string;
|
|
@@ -745,11 +710,8 @@ function SideBySidePanes(props: {
|
|
|
745
710
|
}
|
|
746
711
|
|
|
747
712
|
/**
|
|
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.
|
|
713
|
+
* Rendered outputs for a cell, old / new through rendermime; `null` when
|
|
714
|
+
* neither side has outputs.
|
|
753
715
|
*/
|
|
754
716
|
function OutputsSection(props: {
|
|
755
717
|
entry: NotebookCellDiff;
|
|
@@ -767,11 +729,6 @@ function OutputsSection(props: {
|
|
|
767
729
|
if (!hasOld && !hasNew) {
|
|
768
730
|
return null;
|
|
769
731
|
}
|
|
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
732
|
const showOld = placement !== 'right' && hasOld;
|
|
776
733
|
const showNew = placement !== 'left' && hasNew;
|
|
777
734
|
if (rendermime === null) {
|
|
@@ -805,9 +762,7 @@ function OutputsSection(props: {
|
|
|
805
762
|
}
|
|
806
763
|
|
|
807
764
|
/**
|
|
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.
|
|
765
|
+
* Rendered markdown preview beside the source diff; skipped for non-markdown cells.
|
|
811
766
|
*/
|
|
812
767
|
function MarkdownPreviewSection(props: {
|
|
813
768
|
entry: NotebookCellDiff;
|
|
@@ -838,14 +793,25 @@ function MarkdownPreviewSection(props: {
|
|
|
838
793
|
);
|
|
839
794
|
}
|
|
840
795
|
|
|
796
|
+
/**
|
|
797
|
+
* Props for {@link NotebookDiffView}.
|
|
798
|
+
*/
|
|
841
799
|
interface INotebookDiffViewProps {
|
|
800
|
+
/**
|
|
801
|
+
* The notebook diff to render.
|
|
802
|
+
*/
|
|
842
803
|
diff: INotebookDiffResult;
|
|
804
|
+
/**
|
|
805
|
+
* Whether the active JupyterLab theme is dark.
|
|
806
|
+
*/
|
|
843
807
|
dark: boolean;
|
|
844
808
|
/**
|
|
845
|
-
* Whether a Pierre JupyterLab theme is active; selects Pierre
|
|
846
|
-
* highlighting vs the CSS-variable theme (see {@link resolveDiffTheme}).
|
|
809
|
+
* Whether a Pierre JupyterLab theme is active; selects Pierre highlighting vs the CSS-variable theme.
|
|
847
810
|
*/
|
|
848
811
|
pierreTheme: boolean;
|
|
812
|
+
/**
|
|
813
|
+
* Registry rendering markdown and output previews; `null` falls back to plain text.
|
|
814
|
+
*/
|
|
849
815
|
rendermime: IRenderMimeRegistry | null;
|
|
850
816
|
/**
|
|
851
817
|
* Translation bundle for user-facing strings.
|
|
@@ -854,14 +820,9 @@ interface INotebookDiffViewProps {
|
|
|
854
820
|
}
|
|
855
821
|
|
|
856
822
|
/**
|
|
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.
|
|
823
|
+
* Notebook-level diff view: a 2-column grid where the left column tracks
|
|
824
|
+
* the old notebook and the right the new one — modified/unchanged cells
|
|
825
|
+
* span both, added cells sit right, removed cells sit left.
|
|
865
826
|
*/
|
|
866
827
|
export function NotebookDiffView(
|
|
867
828
|
props: INotebookDiffViewProps
|
|
@@ -898,12 +859,8 @@ export function NotebookDiffView(
|
|
|
898
859
|
}
|
|
899
860
|
|
|
900
861
|
/**
|
|
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.
|
|
862
|
+
* Place one cell entry into the 2-column grid, with a visible placeholder
|
|
863
|
+
* marking where a cell was inserted or removed on the other side.
|
|
907
864
|
*/
|
|
908
865
|
function CellEntryRow(props: ICellDiffBlockProps): React.ReactElement {
|
|
909
866
|
const { entry, trans } = props;
|
|
@@ -951,10 +908,25 @@ function EmptyCellPlaceholder(props: {
|
|
|
951
908
|
);
|
|
952
909
|
}
|
|
953
910
|
|
|
911
|
+
/**
|
|
912
|
+
* Cell counts per diff kind, shown in the header.
|
|
913
|
+
*/
|
|
954
914
|
interface INotebookDiffSummary {
|
|
915
|
+
/**
|
|
916
|
+
* The number of added cells.
|
|
917
|
+
*/
|
|
955
918
|
added: number;
|
|
919
|
+
/**
|
|
920
|
+
* The number of removed cells.
|
|
921
|
+
*/
|
|
956
922
|
removed: number;
|
|
923
|
+
/**
|
|
924
|
+
* The number of modified cells.
|
|
925
|
+
*/
|
|
957
926
|
modified: number;
|
|
927
|
+
/**
|
|
928
|
+
* The number of unchanged cells.
|
|
929
|
+
*/
|
|
958
930
|
unchanged: number;
|
|
959
931
|
}
|
|
960
932
|
|
|
@@ -1023,38 +995,50 @@ function cellEntryKey(entry: NotebookCellDiff): string {
|
|
|
1023
995
|
return `${entry.kind}:${entry.newIndex}:${entry.newCell.id ?? entry.oldCell.id ?? ''}`;
|
|
1024
996
|
}
|
|
1025
997
|
|
|
998
|
+
/**
|
|
999
|
+
* Props shared by {@link CellEntryRow} and {@link CellDiffBlock}.
|
|
1000
|
+
*/
|
|
1026
1001
|
interface ICellDiffBlockProps {
|
|
1002
|
+
/**
|
|
1003
|
+
* The cell diff entry to render.
|
|
1004
|
+
*/
|
|
1027
1005
|
entry: NotebookCellDiff;
|
|
1006
|
+
/**
|
|
1007
|
+
* The kernel language driving code-cell highlighting.
|
|
1008
|
+
*/
|
|
1028
1009
|
language: string | undefined;
|
|
1010
|
+
/**
|
|
1011
|
+
* The `@pierre/diffs` theme name.
|
|
1012
|
+
*/
|
|
1029
1013
|
theme: DiffsThemeNames;
|
|
1014
|
+
/**
|
|
1015
|
+
* Whether the active JupyterLab theme is dark.
|
|
1016
|
+
*/
|
|
1030
1017
|
dark: boolean;
|
|
1018
|
+
/**
|
|
1019
|
+
* Registry rendering markdown and output previews; `null` falls back to plain text.
|
|
1020
|
+
*/
|
|
1031
1021
|
rendermime: IRenderMimeRegistry | null;
|
|
1022
|
+
/**
|
|
1023
|
+
* The translation bundle for user-facing strings.
|
|
1024
|
+
*/
|
|
1032
1025
|
trans: TranslationBundle;
|
|
1033
1026
|
}
|
|
1034
1027
|
|
|
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
1028
|
type CellPlacement = 'full' | 'left' | 'right';
|
|
1048
1029
|
|
|
1030
|
+
/**
|
|
1031
|
+
* Cell diff block props with a resolved grid placement.
|
|
1032
|
+
*/
|
|
1049
1033
|
interface IPlacedCellDiffBlockProps extends ICellDiffBlockProps {
|
|
1034
|
+
/**
|
|
1035
|
+
* The grid column the block occupies: both ('full'), old ('left'), or new ('right').
|
|
1036
|
+
*/
|
|
1050
1037
|
placement: CellPlacement;
|
|
1051
1038
|
}
|
|
1052
1039
|
|
|
1053
1040
|
function CellDiffBlock(props: IPlacedCellDiffBlockProps): React.ReactElement {
|
|
1054
1041
|
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
1042
|
const [collapsed, setCollapsed] = React.useState<boolean>(
|
|
1059
1043
|
entry.kind === 'unchanged'
|
|
1060
1044
|
);
|
|
@@ -1067,8 +1051,7 @@ function CellDiffBlock(props: IPlacedCellDiffBlockProps): React.ReactElement {
|
|
|
1067
1051
|
const cellType = referenceCellType(entry);
|
|
1068
1052
|
const indexLabel = cellIndexLabel(entry);
|
|
1069
1053
|
const isCodeCell = cellType === 'code';
|
|
1070
|
-
//
|
|
1071
|
-
// never carry outputs in nbformat.
|
|
1054
|
+
// Only code cells carry outputs in nbformat.
|
|
1072
1055
|
const showOutputs = isCodeCell && entry.kind !== 'unchanged';
|
|
1073
1056
|
|
|
1074
1057
|
return (
|
|
@@ -1171,11 +1154,8 @@ function CellSubDiff(props: {
|
|
|
1171
1154
|
trans: TranslationBundle;
|
|
1172
1155
|
}): React.ReactElement {
|
|
1173
1156
|
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.
|
|
1157
|
+
// Full-width cells align split mode with the outer old|new lanes;
|
|
1158
|
+
// single-column cells read better unified.
|
|
1179
1159
|
const diffStyle: 'split' | 'unified' =
|
|
1180
1160
|
placement === 'full' ? 'split' : 'unified';
|
|
1181
1161
|
return (
|
|
@@ -1185,10 +1165,8 @@ function CellSubDiff(props: {
|
|
|
1185
1165
|
</div>
|
|
1186
1166
|
<FileDiff
|
|
1187
1167
|
fileDiff={metadata}
|
|
1188
|
-
//
|
|
1189
|
-
//
|
|
1190
|
-
// runs on the main thread. Cell diffs are small enough that this is
|
|
1191
|
-
// not a performance concern.
|
|
1168
|
+
// The worker bootstrap can't resolve through JupyterLab's federation
|
|
1169
|
+
// pipeline (see diffSurface.tsx); run on the main thread.
|
|
1192
1170
|
disableWorkerPool={true}
|
|
1193
1171
|
options={{ ...diffLibraryOptions(theme, dark), diffStyle }}
|
|
1194
1172
|
/>
|
|
@@ -1239,12 +1217,8 @@ function NotebookMetadataBlock(props: {
|
|
|
1239
1217
|
}
|
|
1240
1218
|
|
|
1241
1219
|
/**
|
|
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.
|
|
1220
|
+
* Expanded body of an unchanged cell, rendered once full-width: markdown
|
|
1221
|
+
* through rendermime, code verbatim with outputs below.
|
|
1248
1222
|
*/
|
|
1249
1223
|
function UnchangedCellBody(props: {
|
|
1250
1224
|
entry: Extract<NotebookCellDiff, { kind: 'unchanged' }>;
|