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/lib/git/diffSurface.d.ts
CHANGED
|
@@ -3,33 +3,28 @@ import { IThemeManager } from '@jupyterlab/apputils';
|
|
|
3
3
|
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
4
4
|
import type { TranslationBundle } from '@jupyterlab/translation';
|
|
5
5
|
import { type SelectedLineRange } from '@pierre/diffs';
|
|
6
|
-
/**
|
|
7
|
-
* The CSS class added to the diff main-area widget. The selectors that
|
|
8
|
-
* style the embedded `@pierre/diffs` viewer hang off this class.
|
|
9
|
-
*/
|
|
10
6
|
export declare const DIFF_WIDGET_CSS_CLASS = "jp-xtralab-DiffWidget";
|
|
7
|
+
export type DiffStyle = 'split' | 'unified';
|
|
11
8
|
/**
|
|
12
|
-
*
|
|
13
|
-
* `diffStyle` option. `split` is the side-by-side old|new view; `unified`
|
|
14
|
-
* is the single-column inline view (what diffs.com calls "stacked").
|
|
9
|
+
* Read the persisted diff style, defaulting to `'split'`.
|
|
15
10
|
*/
|
|
16
|
-
export type DiffStyle = 'split' | 'unified';
|
|
17
11
|
export declare function readStoredDiffStyle(): DiffStyle;
|
|
18
|
-
export declare function writeStoredDiffStyle(style: DiffStyle): void;
|
|
19
12
|
/**
|
|
20
|
-
*
|
|
21
|
-
* rendered view; `json` is the raw nbformat JSON file diff, for inspecting
|
|
22
|
-
* exactly which bytes changed.
|
|
13
|
+
* Persist the diff style to local storage (best-effort).
|
|
23
14
|
*/
|
|
15
|
+
export declare function writeStoredDiffStyle(style: DiffStyle): void;
|
|
24
16
|
export type NotebookDiffViewMode = 'notebook' | 'json';
|
|
17
|
+
/**
|
|
18
|
+
* Read the persisted notebook view mode, defaulting to `'notebook'`.
|
|
19
|
+
*/
|
|
25
20
|
export declare function readStoredNotebookViewMode(): NotebookDiffViewMode;
|
|
21
|
+
/**
|
|
22
|
+
* Persist the notebook view mode to local storage (best-effort).
|
|
23
|
+
*/
|
|
26
24
|
export declare function writeStoredNotebookViewMode(mode: NotebookDiffViewMode): void;
|
|
27
25
|
/**
|
|
28
|
-
*
|
|
29
|
-
* `
|
|
30
|
-
* `data-jp-theme-light` attribute that JupyterLab sets on `<body>` so the
|
|
31
|
-
* component still works in stripped-down hosts where the token isn't
|
|
32
|
-
* provided (lite, certain notebook configurations, …).
|
|
26
|
+
* Dark-theme check via `IThemeManager`, falling back to the
|
|
27
|
+
* `data-jp-theme-light` body attribute for hosts without the token.
|
|
33
28
|
*/
|
|
34
29
|
export declare function isDarkTheme(themeManager: IThemeManager | null): boolean;
|
|
35
30
|
/**
|
|
@@ -37,18 +32,26 @@ export declare function isDarkTheme(themeManager: IThemeManager | null): boolean
|
|
|
37
32
|
*/
|
|
38
33
|
export declare function isPierreTheme(themeManager: IThemeManager | null): boolean;
|
|
39
34
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* reverts that hunk and hands the rebuilt full-file text back to the host
|
|
43
|
-
* via {@link IHunkDiscard.save}. The host owns the actual write (it knows
|
|
44
|
-
* the on-disk path) and triggers any follow-up refresh in
|
|
45
|
-
* {@link IHunkDiscard.onAfterSave}.
|
|
35
|
+
* Per-hunk discard wiring: discarding rebuilds the full file text and the
|
|
36
|
+
* host owns the write via `save`, then refreshes in `onAfterSave`.
|
|
46
37
|
*/
|
|
47
38
|
interface IHunkDiscard {
|
|
39
|
+
/**
|
|
40
|
+
* Whether hunk discarding is available.
|
|
41
|
+
*/
|
|
48
42
|
enabled: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Persist the full post-discard file text.
|
|
45
|
+
*/
|
|
49
46
|
save: (fullText: string) => Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Called after a successful save so the host can refresh the diff.
|
|
49
|
+
*/
|
|
50
50
|
onAfterSave: () => void;
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* The props for the {@link DiffSurface} component.
|
|
54
|
+
*/
|
|
52
55
|
interface IDiffSurfaceProps {
|
|
53
56
|
/**
|
|
54
57
|
* Whether the host is still resolving the file contents.
|
|
@@ -71,13 +74,11 @@ interface IDiffSurfaceProps {
|
|
|
71
74
|
*/
|
|
72
75
|
newText: string;
|
|
73
76
|
/**
|
|
74
|
-
* New-side path
|
|
75
|
-
* name on the additions side.
|
|
77
|
+
* New-side path; drives notebook detection and the highlighting filename.
|
|
76
78
|
*/
|
|
77
79
|
newName: string;
|
|
78
80
|
/**
|
|
79
|
-
* Old-side path
|
|
80
|
-
* to {@link newName} when the host does not track a previous path.
|
|
81
|
+
* Old-side path; differs from {@link newName} only for renames.
|
|
81
82
|
*/
|
|
82
83
|
oldName?: string;
|
|
83
84
|
/**
|
|
@@ -89,35 +90,28 @@ interface IDiffSurfaceProps {
|
|
|
89
90
|
*/
|
|
90
91
|
pierreTheme: boolean;
|
|
91
92
|
/**
|
|
92
|
-
*
|
|
93
|
-
* markdown cells. May be `null` in stripped-down hosts; the notebook
|
|
94
|
-
* diff falls back to a textual representation in that case.
|
|
93
|
+
* Used by the notebook view; `null` in stripped-down hosts, which forces a textual fallback.
|
|
95
94
|
*/
|
|
96
95
|
rendermime: IRenderMimeRegistry | null;
|
|
97
96
|
/**
|
|
98
|
-
*
|
|
97
|
+
* Rendered-vs-JSON choice for notebook diffs (host-controlled).
|
|
99
98
|
*/
|
|
100
99
|
notebookViewMode: NotebookDiffViewMode;
|
|
101
100
|
/**
|
|
102
|
-
* Split vs unified layout for the textual
|
|
101
|
+
* Split vs unified layout for the textual file diff (host-controlled).
|
|
103
102
|
*/
|
|
104
103
|
diffStyle: DiffStyle;
|
|
105
104
|
/**
|
|
106
|
-
*
|
|
107
|
-
* so the host can show/hide its Notebook/JSON toolbar toggle.
|
|
105
|
+
* Fires when rendered-notebook availability changes so the host can toggle its Notebook/JSON control.
|
|
108
106
|
*/
|
|
109
107
|
onNotebookAvailabilityChange?: (available: boolean) => void;
|
|
110
108
|
/**
|
|
111
|
-
*
|
|
112
|
-
* unified choice affects) becomes the active view, so the host can
|
|
113
|
-
* show/hide its Split/Unified toolbar toggle. False for image, binary,
|
|
114
|
-
* rendered-notebook, loading and error states.
|
|
109
|
+
* Fires when the textual file diff becomes (in)active — the only view the split/unified choice affects.
|
|
115
110
|
*/
|
|
116
111
|
onFileDiffActiveChange?: (active: boolean) => void;
|
|
117
112
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* post-discard behaviors such as auto-closing an emptied diff.
|
|
113
|
+
* Fires after each diff computation with the hunk count (`null` when no
|
|
114
|
+
* textual diff); hosts use it to auto-close emptied diffs.
|
|
121
115
|
*/
|
|
122
116
|
onMetadataChange?: (info: {
|
|
123
117
|
hunkCount: number | null;
|
|
@@ -127,11 +121,8 @@ interface IDiffSurfaceProps {
|
|
|
127
121
|
*/
|
|
128
122
|
hunkDiscard?: IHunkDiscard;
|
|
129
123
|
/**
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* button appears on the hovered/selected lines. Clicking that button
|
|
133
|
-
* invokes this callback with the selected range and the button's viewport
|
|
134
|
-
* rectangle (to anchor a popup to), `null` when it cannot be measured.
|
|
124
|
+
* Enables line selection and the gutter "+" button; called with the
|
|
125
|
+
* selected range and the button's viewport rect (`null` when unmeasurable).
|
|
135
126
|
*/
|
|
136
127
|
onLineAsk?: (range: SelectedLineRange, anchor: DOMRect | null) => void;
|
|
137
128
|
/**
|
|
@@ -144,10 +135,8 @@ interface IDiffSurfaceProps {
|
|
|
144
135
|
*/
|
|
145
136
|
export declare function DiffSurface(props: IDiffSurfaceProps): React.ReactElement;
|
|
146
137
|
/**
|
|
147
|
-
* Segmented Notebook/JSON selector. Hosts mount
|
|
148
|
-
*
|
|
149
|
-
* `jupyterlab-git`-provided diff toolbar) and drive its value/visibility
|
|
150
|
-
* from the same state they pass to {@link DiffSurface}.
|
|
138
|
+
* Segmented Notebook/JSON selector. Hosts mount it into their own toolbar
|
|
139
|
+
* and drive value/visibility from the state they pass to {@link DiffSurface}.
|
|
151
140
|
*/
|
|
152
141
|
export declare function NotebookViewModeControl(props: {
|
|
153
142
|
mode: NotebookDiffViewMode;
|
|
@@ -156,11 +145,8 @@ export declare function NotebookViewModeControl(props: {
|
|
|
156
145
|
trans: TranslationBundle;
|
|
157
146
|
}): React.ReactElement;
|
|
158
147
|
/**
|
|
159
|
-
* Segmented Split/Unified selector
|
|
160
|
-
*
|
|
161
|
-
* the same state they pass to {@link DiffSurface}. Only meaningful while the
|
|
162
|
-
* textual/code file diff is the active view, so `available` mirrors the
|
|
163
|
-
* surface's `onFileDiffActiveChange`.
|
|
148
|
+
* Segmented Split/Unified selector; mirrors {@link NotebookViewModeControl}.
|
|
149
|
+
* `available` mirrors the surface's `onFileDiffActiveChange`.
|
|
164
150
|
*/
|
|
165
151
|
export declare function DiffStyleControl(props: {
|
|
166
152
|
diffStyle: DiffStyle;
|
package/lib/git/diffSurface.js
CHANGED
|
@@ -5,35 +5,16 @@ import { diffAcceptRejectHunk, parseDiffFromFile } from '@pierre/diffs';
|
|
|
5
5
|
import { ImageDiffView, imageDataType } from './imageDiff';
|
|
6
6
|
import { buildNotebookDiff, NotebookDiffView } from './notebookDiff';
|
|
7
7
|
import { resolveDiffTheme } from './diffTheme';
|
|
8
|
-
/**
|
|
9
|
-
* The CSS class added to the diff main-area widget. The selectors that
|
|
10
|
-
* style the embedded `@pierre/diffs` viewer hang off this class.
|
|
11
|
-
*/
|
|
12
8
|
export const DIFF_WIDGET_CSS_CLASS = 'jp-xtralab-DiffWidget';
|
|
13
|
-
/**
|
|
14
|
-
* `localStorage` key for the user's last-chosen split ratio, persisted so
|
|
15
|
-
* the preference survives reloads (the library has no built-in way to
|
|
16
|
-
* remember it).
|
|
17
|
-
*/
|
|
18
9
|
const SPLIT_RATIO_STORAGE_KEY = 'xtralab:diff-split-ratio';
|
|
19
|
-
/**
|
|
20
|
-
* Bounds on the left-pane fraction. Keeping a few percent on either side
|
|
21
|
-
* guarantees both panes remain visible — dragging to 0/100 would hide a
|
|
22
|
-
* column and is almost never what the user wants.
|
|
23
|
-
*/
|
|
24
10
|
const MIN_SPLIT_RATIO = 0.1;
|
|
25
11
|
const MAX_SPLIT_RATIO = 0.9;
|
|
26
12
|
const DEFAULT_SPLIT_RATIO = 0.5;
|
|
27
13
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* the
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* The override reads from `--xtralab-split-cols`, a custom property we set
|
|
34
|
-
* on the host element via the `style` prop. Custom properties inherit
|
|
35
|
-
* through the shadow DOM boundary, so adjusting it on the host instantly
|
|
36
|
-
* resizes the columns without re-rendering the diff.
|
|
14
|
+
* Injected via the `unsafeCSS` option into the shadow root's `@layer
|
|
15
|
+
* unsafe`, beating the library's base rule that hardcodes `1fr 1fr`. Reads
|
|
16
|
+
* `--xtralab-split-cols` set on the host — custom properties cross the
|
|
17
|
+
* shadow boundary, so resizing needs no re-render.
|
|
37
18
|
*/
|
|
38
19
|
const SPLIT_RESIZE_CSS = `pre[data-diff-type="split"][data-overflow="scroll"] {
|
|
39
20
|
grid-template-columns: var(--xtralab-split-cols, 1fr 1fr);
|
|
@@ -52,8 +33,7 @@ function readStoredSplitRatio() {
|
|
|
52
33
|
}
|
|
53
34
|
}
|
|
54
35
|
catch (_a) {
|
|
55
|
-
// localStorage
|
|
56
|
-
// back to the default is fine — the user can resize again on this run.
|
|
36
|
+
// localStorage can throw in privacy mode or sandboxed contexts.
|
|
57
37
|
}
|
|
58
38
|
return DEFAULT_SPLIT_RATIO;
|
|
59
39
|
}
|
|
@@ -66,6 +46,9 @@ function writeStoredSplitRatio(ratio) {
|
|
|
66
46
|
}
|
|
67
47
|
}
|
|
68
48
|
const DIFF_STYLE_STORAGE_KEY = 'xtralab:diff-style';
|
|
49
|
+
/**
|
|
50
|
+
* Read the persisted diff style, defaulting to `'split'`.
|
|
51
|
+
*/
|
|
69
52
|
export function readStoredDiffStyle() {
|
|
70
53
|
try {
|
|
71
54
|
const raw = window.localStorage.getItem(DIFF_STYLE_STORAGE_KEY);
|
|
@@ -74,10 +57,13 @@ export function readStoredDiffStyle() {
|
|
|
74
57
|
}
|
|
75
58
|
}
|
|
76
59
|
catch (_a) {
|
|
77
|
-
// See readStoredSplitRatio
|
|
60
|
+
// See readStoredSplitRatio.
|
|
78
61
|
}
|
|
79
62
|
return 'split';
|
|
80
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Persist the diff style to local storage (best-effort).
|
|
66
|
+
*/
|
|
81
67
|
export function writeStoredDiffStyle(style) {
|
|
82
68
|
try {
|
|
83
69
|
window.localStorage.setItem(DIFF_STYLE_STORAGE_KEY, style);
|
|
@@ -87,6 +73,9 @@ export function writeStoredDiffStyle(style) {
|
|
|
87
73
|
}
|
|
88
74
|
}
|
|
89
75
|
const NOTEBOOK_DIFF_VIEW_MODE_STORAGE_KEY = 'xtralab:notebook-diff-view-mode';
|
|
76
|
+
/**
|
|
77
|
+
* Read the persisted notebook view mode, defaulting to `'notebook'`.
|
|
78
|
+
*/
|
|
90
79
|
export function readStoredNotebookViewMode() {
|
|
91
80
|
try {
|
|
92
81
|
const raw = window.localStorage.getItem(NOTEBOOK_DIFF_VIEW_MODE_STORAGE_KEY);
|
|
@@ -95,11 +84,13 @@ export function readStoredNotebookViewMode() {
|
|
|
95
84
|
}
|
|
96
85
|
}
|
|
97
86
|
catch (_a) {
|
|
98
|
-
// See readStoredSplitRatio
|
|
99
|
-
// throw on access. The notebook view is the better default anyway.
|
|
87
|
+
// See readStoredSplitRatio.
|
|
100
88
|
}
|
|
101
89
|
return 'notebook';
|
|
102
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Persist the notebook view mode to local storage (best-effort).
|
|
93
|
+
*/
|
|
103
94
|
export function writeStoredNotebookViewMode(mode) {
|
|
104
95
|
try {
|
|
105
96
|
window.localStorage.setItem(NOTEBOOK_DIFF_VIEW_MODE_STORAGE_KEY, mode);
|
|
@@ -109,21 +100,15 @@ export function writeStoredNotebookViewMode(mode) {
|
|
|
109
100
|
}
|
|
110
101
|
}
|
|
111
102
|
/**
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* another `.ipynb`-named file for a notebook is harmless because
|
|
115
|
-
* {@link buildNotebookDiff} validates the JSON shape and we fall back to
|
|
116
|
-
* the file diff if parsing fails.
|
|
103
|
+
* Extension-only check; a false positive is harmless because
|
|
104
|
+
* {@link buildNotebookDiff} validates the JSON and the file diff is the fallback.
|
|
117
105
|
*/
|
|
118
106
|
function isNotebookPath(path) {
|
|
119
107
|
return path.toLowerCase().endsWith('.ipynb');
|
|
120
108
|
}
|
|
121
109
|
/**
|
|
122
|
-
*
|
|
123
|
-
* `
|
|
124
|
-
* `data-jp-theme-light` attribute that JupyterLab sets on `<body>` so the
|
|
125
|
-
* component still works in stripped-down hosts where the token isn't
|
|
126
|
-
* provided (lite, certain notebook configurations, …).
|
|
110
|
+
* Dark-theme check via `IThemeManager`, falling back to the
|
|
111
|
+
* `data-jp-theme-light` body attribute for hosts without the token.
|
|
127
112
|
*/
|
|
128
113
|
export function isDarkTheme(themeManager) {
|
|
129
114
|
if (themeManager !== null && themeManager.theme !== null) {
|
|
@@ -145,14 +130,10 @@ export function isPierreTheme(themeManager) {
|
|
|
145
130
|
export function DiffSurface(props) {
|
|
146
131
|
const { loading, error, isBinary, oldText, newText, newName, oldName, dark, pierreTheme, rendermime, notebookViewMode, diffStyle, onNotebookAvailabilityChange, onFileDiffActiveChange, onMetadataChange, hunkDiscard, onLineAsk, trans } = props;
|
|
147
132
|
const hasContent = !loading && error === null && !isBinary;
|
|
148
|
-
//
|
|
149
|
-
// text/notebook diff; the host passes the two sides as base64 strings in
|
|
150
|
-
// `oldText`/`newText` (the git server base64-encodes binary content).
|
|
133
|
+
// For raster images the host passes both sides as base64 in oldText/newText.
|
|
151
134
|
const imageType = React.useMemo(() => imageDataType(newName), [newName]);
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
// {@link diffAcceptRejectHunk} when the user discards a hunk — so hunk
|
|
155
|
-
// indexes line up between what the user clicked and what we mutate.
|
|
135
|
+
// Owning the metadata here keeps hunk indexes aligned between FileDiff
|
|
136
|
+
// and diffAcceptRejectHunk on discard.
|
|
156
137
|
const metadata = React.useMemo(() => {
|
|
157
138
|
if (!hasContent || imageType !== null) {
|
|
158
139
|
return null;
|
|
@@ -164,47 +145,31 @@ export function DiffSurface(props) {
|
|
|
164
145
|
const newFile = { name: newName, contents: newText };
|
|
165
146
|
return parseDiffFromFile(oldFile, newFile);
|
|
166
147
|
}, [hasContent, imageType, oldText, newText, oldName, newName]);
|
|
167
|
-
// Cell-by-cell notebook diff for `.ipynb` files. When non-null the
|
|
168
|
-
// renderer uses a notebook-aware view instead of the line-oriented file
|
|
169
|
-
// diff — kept distinct from {@link metadata} so a notebook whose JSON we
|
|
170
|
-
// cannot parse can fall back to the file diff path.
|
|
171
148
|
const notebookDiff = React.useMemo(() => {
|
|
172
149
|
if (!hasContent || imageType !== null || !isNotebookPath(newName)) {
|
|
173
150
|
return null;
|
|
174
151
|
}
|
|
175
152
|
return buildNotebookDiff({ oldText, newText });
|
|
176
153
|
}, [hasContent, imageType, oldText, newText, newName]);
|
|
177
|
-
// Tell the host whether a rendered notebook view is currently available
|
|
178
|
-
// so it can show/hide its Notebook/JSON toggle.
|
|
179
154
|
React.useEffect(() => {
|
|
180
155
|
onNotebookAvailabilityChange === null || onNotebookAvailabilityChange === void 0 ? void 0 : onNotebookAvailabilityChange(notebookDiff !== null);
|
|
181
156
|
}, [onNotebookAvailabilityChange, notebookDiff]);
|
|
182
|
-
// Report the diff shape so hosts can react (e.g. auto-close on empty).
|
|
183
157
|
React.useEffect(() => {
|
|
184
158
|
onMetadataChange === null || onMetadataChange === void 0 ? void 0 : onMetadataChange({
|
|
185
159
|
hunkCount: metadata !== null ? metadata.hunks.length : null
|
|
186
160
|
});
|
|
187
161
|
}, [onMetadataChange, metadata]);
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
// Computed before the early returns below so the file-diff-active effect
|
|
191
|
-
// can depend on it without violating the rules of hooks.
|
|
162
|
+
// Computed before the early returns so the hooks below can depend on
|
|
163
|
+
// them without breaking the rules of hooks.
|
|
192
164
|
const hasNotebookView = notebookDiff !== null;
|
|
193
165
|
const showNotebookView = hasNotebookView && notebookViewMode === 'notebook';
|
|
194
166
|
const showFileDiff = !showNotebookView && metadata !== null;
|
|
195
|
-
// The split vs unified choice only affects the textual/code file diff, so
|
|
196
|
-
// tell the host whether that view is active to drive its Split/Unified
|
|
197
|
-
// toolbar toggle.
|
|
198
167
|
React.useEffect(() => {
|
|
199
168
|
onFileDiffActiveChange === null || onFileDiffActiveChange === void 0 ? void 0 : onFileDiffActiveChange(showFileDiff);
|
|
200
169
|
}, [onFileDiffActiveChange, showFileDiff]);
|
|
201
|
-
// Split ratio for the diff columns: fraction of width given to the
|
|
202
|
-
// deletions (left) pane. Persisted across sessions so the user only
|
|
203
|
-
// dials in their layout once.
|
|
204
170
|
const [leftRatio, setLeftRatio] = React.useState(() => readStoredSplitRatio());
|
|
205
171
|
const wrapperRef = React.useRef(null);
|
|
206
|
-
//
|
|
207
|
-
// attach on pointer-down don't capture a stale value across renders.
|
|
172
|
+
// Drag listeners read the live ratio from a ref to avoid stale captures.
|
|
208
173
|
const leftRatioRef = React.useRef(leftRatio);
|
|
209
174
|
React.useEffect(() => {
|
|
210
175
|
leftRatioRef.current = leftRatio;
|
|
@@ -216,8 +181,6 @@ export function DiffSurface(props) {
|
|
|
216
181
|
}
|
|
217
182
|
return metadata.hunks.map((hunk, hunkIndex) => ({
|
|
218
183
|
side: 'additions',
|
|
219
|
-
// The first line of each hunk in the new file. We anchor the button
|
|
220
|
-
// there so it sits at the top of the hunk's body.
|
|
221
184
|
lineNumber: hunk.additionStart,
|
|
222
185
|
metadata: { hunkIndex }
|
|
223
186
|
}));
|
|
@@ -227,12 +190,8 @@ export function DiffSurface(props) {
|
|
|
227
190
|
return;
|
|
228
191
|
}
|
|
229
192
|
const updated = diffAcceptRejectHunk(metadata, hunkIndex, 'reject');
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
// file contents with `/(?<=\n)/` (a lookbehind that keeps the
|
|
233
|
-
// trailing `\n` on each line), so the entries already carry their
|
|
234
|
-
// own line endings — joining with `''` rebuilds the original text
|
|
235
|
-
// verbatim, while joining with `'\n'` would double every newline.
|
|
193
|
+
// additionLines holds the full new file with the hunk reverted; lines keep
|
|
194
|
+
// their trailing `\n` (lookbehind split), so join('') rebuilds it verbatim.
|
|
236
195
|
const text = updated.additionLines.join('');
|
|
237
196
|
try {
|
|
238
197
|
await hunkDiscard.save(text);
|
|
@@ -256,15 +215,11 @@ export function DiffSurface(props) {
|
|
|
256
215
|
if (onLineAsk === undefined) {
|
|
257
216
|
return;
|
|
258
217
|
}
|
|
259
|
-
// Anchor to the gutter "+" button
|
|
260
|
-
// the `@pierre/diffs` shadow root (which is open), parked on the
|
|
261
|
-
// hovered/selected line's number element.
|
|
218
|
+
// Anchor to the just-clicked gutter "+" button inside the library's open shadow root.
|
|
262
219
|
const slot = (_c = (_b = (_a = wrapperRef.current) === null || _a === void 0 ? void 0 : _a.querySelector('diffs-container')) === null || _b === void 0 ? void 0 : _b.shadowRoot) === null || _c === void 0 ? void 0 : _c.querySelector('[data-gutter-utility-slot]');
|
|
263
220
|
onLineAsk(range, slot instanceof Element ? slot.getBoundingClientRect() : null);
|
|
264
221
|
}, [onLineAsk]);
|
|
265
222
|
const handleResizerPointerDown = React.useCallback((event) => {
|
|
266
|
-
// Only react to primary-button drags. Right-click / middle-click on
|
|
267
|
-
// the handle should pass through to the browser default.
|
|
268
223
|
if (event.button !== 0) {
|
|
269
224
|
return;
|
|
270
225
|
}
|
|
@@ -293,8 +248,6 @@ export function DiffSurface(props) {
|
|
|
293
248
|
handle.removeEventListener('pointermove', onPointerMove);
|
|
294
249
|
handle.removeEventListener('pointerup', onPointerEnd);
|
|
295
250
|
handle.removeEventListener('pointercancel', onPointerEnd);
|
|
296
|
-
// Only persist on drag-end so a quick drag isn't followed by a
|
|
297
|
-
// burst of writes for every intermediate position.
|
|
298
251
|
writeStoredSplitRatio(leftRatioRef.current);
|
|
299
252
|
};
|
|
300
253
|
handle.addEventListener('pointermove', onPointerMove);
|
|
@@ -340,19 +293,12 @@ export function DiffSurface(props) {
|
|
|
340
293
|
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-status" }, trans.__('Binary file — diff not supported.')));
|
|
341
294
|
}
|
|
342
295
|
if (imageType !== null) {
|
|
343
|
-
// `oldText`/`newText` carry the two revisions as base64 here. The
|
|
344
|
-
// image view owns its own layout and mode selector, so it does not
|
|
345
|
-
// use the split resizer or the notebook/JSON toggle.
|
|
346
296
|
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-content" },
|
|
347
297
|
React.createElement(ImageDiffView, { reference: oldText, challenger: newText, fileType: imageType, trans: trans })));
|
|
348
298
|
}
|
|
349
299
|
if (!showNotebookView && !showFileDiff) {
|
|
350
300
|
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-status" }, trans.__('No content to diff.')));
|
|
351
301
|
}
|
|
352
|
-
// Custom property set on the diffs-container host. The library's
|
|
353
|
-
// shadow-root rule consumes it via `var(--xtralab-split-cols, …)` (see
|
|
354
|
-
// SPLIT_RESIZE_CSS) and recomputes the column tracks instantly. Only
|
|
355
|
-
// relevant in the file-diff path; the notebook view doesn't use it.
|
|
356
302
|
const leftPercent = leftRatio * 100;
|
|
357
303
|
const hostStyle = {
|
|
358
304
|
'--xtralab-split-cols': `${leftPercent}% ${100 - leftPercent}%`
|
|
@@ -360,29 +306,15 @@ export function DiffSurface(props) {
|
|
|
360
306
|
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-content" },
|
|
361
307
|
React.createElement("div", { ref: wrapperRef, className: "jp-xtralab-DiffWidget-body" },
|
|
362
308
|
React.createElement("div", { className: "jp-xtralab-DiffWidget-scroll" }, showNotebookView && notebookDiff !== null ? (React.createElement(NotebookDiffView, { diff: notebookDiff, dark: dark, pierreTheme: pierreTheme, rendermime: rendermime, trans: trans })) : showFileDiff && metadata !== null ? (React.createElement(FileDiff, { fileDiff: metadata, lineAnnotations: lineAnnotations, renderAnnotation: renderAnnotation, style: hostStyle,
|
|
363
|
-
//
|
|
364
|
-
//
|
|
365
|
-
// `@pierre/diffs/worker/worker.js` file at a URL the worker
|
|
366
|
-
// bootstrap can resolve from, so leaving the pool enabled
|
|
367
|
-
// crashes on instantiation. Running on the main thread is
|
|
368
|
-
// fine for the small files git diffs typically operate on.
|
|
309
|
+
// JupyterLab's federation pipeline can't serve the worker bundle
|
|
310
|
+
// at a resolvable URL, so the pool crashes; run on the main thread.
|
|
369
311
|
disableWorkerPool: true, options: {
|
|
370
312
|
diffStyle,
|
|
371
|
-
// Drop the file header — the tab title already shows the
|
|
372
|
-
// file name and the panel header carries the change
|
|
373
|
-
// context.
|
|
374
313
|
disableFileHeader: true,
|
|
375
314
|
theme: resolveDiffTheme(dark, pierreTheme),
|
|
376
315
|
themeType: dark ? 'dark' : 'light',
|
|
377
|
-
//
|
|
378
|
-
// via the library's `@layer unsafe` channel. Keeping the
|
|
379
|
-
// string constant lets the library short-circuit the
|
|
380
|
-
// re-render path it uses when this option actually
|
|
381
|
-
// changes.
|
|
316
|
+
// Constant string lets the library skip its unsafeCSS re-render path.
|
|
382
317
|
unsafeCSS: SPLIT_RESIZE_CSS,
|
|
383
|
-
// Line selection + the gutter "+" button feed the
|
|
384
|
-
// ask-agent popup; only wired up when a handler exists so
|
|
385
|
-
// a plain diff keeps its passive gutter.
|
|
386
318
|
...(onLineAsk !== undefined
|
|
387
319
|
? {
|
|
388
320
|
enableLineSelection: true,
|
|
@@ -394,10 +326,8 @@ export function DiffSurface(props) {
|
|
|
394
326
|
showFileDiff && diffStyle === 'split' ? (React.createElement("div", { className: "jp-xtralab-DiffWidget-resizer", style: { left: `${leftPercent}%` }, role: "separator", "aria-orientation": "vertical", "aria-valuenow": Math.round(leftPercent), "aria-valuemin": Math.round(MIN_SPLIT_RATIO * 100), "aria-valuemax": Math.round(MAX_SPLIT_RATIO * 100), "aria-label": trans.__('Resize the diff panes'), tabIndex: 0, title: trans.__('Drag to resize the diff panes (double-click to reset)'), onPointerDown: handleResizerPointerDown, onDoubleClick: handleResizerDoubleClick, onKeyDown: handleResizerKeyDown })) : null)));
|
|
395
327
|
}
|
|
396
328
|
/**
|
|
397
|
-
* Segmented Notebook/JSON selector. Hosts mount
|
|
398
|
-
*
|
|
399
|
-
* `jupyterlab-git`-provided diff toolbar) and drive its value/visibility
|
|
400
|
-
* from the same state they pass to {@link DiffSurface}.
|
|
329
|
+
* Segmented Notebook/JSON selector. Hosts mount it into their own toolbar
|
|
330
|
+
* and drive value/visibility from the state they pass to {@link DiffSurface}.
|
|
401
331
|
*/
|
|
402
332
|
export function NotebookViewModeControl(props) {
|
|
403
333
|
const { mode, available, onChange, trans } = props;
|
|
@@ -409,11 +339,9 @@ export function NotebookViewModeControl(props) {
|
|
|
409
339
|
React.createElement("button", { type: "button", role: "tab", "aria-selected": mode === 'json', "data-active": mode === 'json', className: "jp-xtralab-DiffWidget-segmentedButton", onClick: () => onChange('json') }, trans.__('JSON'))));
|
|
410
340
|
}
|
|
411
341
|
/**
|
|
412
|
-
* Split / unified glyphs
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
* sprite sheet being injected into the document. `currentColor` lets the
|
|
416
|
-
* segmented-button styling drive the fill (including the active state).
|
|
342
|
+
* Split / unified glyphs inlined from the `@pierre/diffs` icon sprite so
|
|
343
|
+
* the toggle matches the library's docs site without the sprite sheet;
|
|
344
|
+
* `currentColor` lets the button styling drive the fill.
|
|
417
345
|
*/
|
|
418
346
|
function DiffSplitIcon() {
|
|
419
347
|
return (React.createElement("svg", { viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": "true" },
|
|
@@ -426,11 +354,8 @@ function DiffUnifiedIcon() {
|
|
|
426
354
|
React.createElement("path", { fillRule: "evenodd", d: "M14 0a2 2 0 0 1 2 2v5.5H0V2a2 2 0 0 1 2-2zM6.5 3.5a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1z", clipRule: "evenodd", opacity: ".4" })));
|
|
427
355
|
}
|
|
428
356
|
/**
|
|
429
|
-
* Segmented Split/Unified selector
|
|
430
|
-
*
|
|
431
|
-
* the same state they pass to {@link DiffSurface}. Only meaningful while the
|
|
432
|
-
* textual/code file diff is the active view, so `available` mirrors the
|
|
433
|
-
* surface's `onFileDiffActiveChange`.
|
|
357
|
+
* Segmented Split/Unified selector; mirrors {@link NotebookViewModeControl}.
|
|
358
|
+
* `available` mirrors the surface's `onFileDiffActiveChange`.
|
|
434
359
|
*/
|
|
435
360
|
export function DiffStyleControl(props) {
|
|
436
361
|
const { diffStyle, available, onChange, trans } = props;
|