xtralab 0.11.1 → 0.12.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 +38 -231
- package/lib/agentSessions.d.ts +6 -2
- package/lib/askAgent/editorSelection.d.ts +53 -0
- package/lib/askAgent/editorSelection.js +95 -0
- package/lib/askAgent/icons.d.ts +7 -0
- package/lib/askAgent/icons.js +15 -0
- package/lib/askAgent/index.d.ts +32 -0
- package/lib/askAgent/index.js +741 -0
- package/lib/askAgent/popup.d.ts +83 -0
- package/lib/askAgent/popup.js +221 -0
- package/lib/askAgent/prompt.d.ts +24 -0
- package/lib/askAgent/prompt.js +116 -0
- package/lib/askAgent/queue.d.ts +93 -0
- package/lib/askAgent/queue.js +207 -0
- package/lib/askAgent/queuePanel.d.ts +76 -0
- package/lib/askAgent/queuePanel.js +188 -0
- package/lib/askAgent/targetPicker.d.ts +58 -0
- package/lib/askAgent/targetPicker.js +74 -0
- package/lib/askAgent/tokens.d.ts +124 -0
- package/lib/askAgent/tokens.js +13 -0
- package/lib/commandBar/index.d.ts +12 -8
- package/lib/commandBar/index.js +58 -41
- package/lib/editorBreadcrumbs/index.js +9 -4
- package/lib/editorBreadcrumbs/widget.d.ts +7 -7
- package/lib/editorBreadcrumbs/widget.js +6 -8
- package/lib/fileBrowser/commands.d.ts +2 -8
- package/lib/fileBrowser/commands.js +39 -30
- package/lib/fileBrowser/contents.d.ts +8 -3
- package/lib/fileBrowser/fileBrowser.d.ts +2 -1
- package/lib/fileBrowser/widget.d.ts +5 -9
- package/lib/fileBrowser/widget.js +1 -1
- package/lib/fileTypeIcons/index.js +5 -2
- package/lib/git/api.d.ts +0 -26
- package/lib/git/api.js +2 -44
- package/lib/git/askRequest.d.ts +23 -0
- package/lib/git/askRequest.js +90 -0
- package/lib/git/commands.d.ts +14 -2
- package/lib/git/commands.js +16 -13
- package/lib/git/diffModel.d.ts +1 -9
- package/lib/git/diffModel.js +1 -1
- package/lib/git/diffProvider.js +8 -3
- package/lib/git/diffSurface.d.ts +19 -2
- package/lib/git/diffSurface.js +64 -18
- package/lib/git/diffTheme.d.ts +0 -4
- package/lib/git/diffTheme.js +1 -1
- package/lib/git/diffWidget.d.ts +9 -0
- package/lib/git/diffWidget.js +38 -10
- package/lib/git/imageDiff.d.ts +17 -4
- package/lib/git/imageDiff.js +90 -30
- package/lib/git/index.js +7 -2
- package/lib/git/notebookDiff.d.ts +10 -67
- package/lib/git/notebookDiff.js +62 -69
- package/lib/git/tokens.d.ts +5 -2
- package/lib/highlight/index.js +12 -8
- package/lib/index.js +2 -0
- package/lib/launcher/agents.js +3 -4
- package/lib/launcher/commands.d.ts +6 -2
- package/lib/launcher/dashboard.d.ts +8 -1
- package/lib/launcher/dashboard.js +39 -33
- package/lib/launcher/editorRegistry.js +5 -3
- package/lib/launcher/editors.d.ts +15 -5
- package/lib/launcher/icons.d.ts +0 -37
- package/lib/launcher/icons.js +11 -12
- package/lib/launcher/index.js +2 -1
- package/lib/launcher/invocation.d.ts +0 -15
- package/lib/launcher/invocation.js +1 -1
- package/lib/launcher/schemaDefaults.d.ts +3 -1
- package/lib/launcher/schemaDefaults.js +3 -1
- package/lib/mimeData.d.ts +7 -0
- package/lib/mimeData.js +16 -0
- package/lib/omnibox/index.d.ts +4 -0
- package/lib/omnibox/index.js +43 -2
- package/lib/omnibox/model.d.ts +43 -14
- package/lib/omnibox/model.js +117 -14
- package/lib/omnibox/recents.d.ts +65 -0
- package/lib/omnibox/recents.js +109 -0
- package/lib/omnibox/tokens.d.ts +6 -2
- package/lib/omnibox/widget.d.ts +23 -6
- package/lib/omnibox/widget.js +26 -7
- package/lib/showOutput/index.js +15 -18
- package/lib/terminals/agentTerminals.d.ts +63 -0
- package/lib/terminals/agentTerminals.js +157 -0
- package/lib/terminals/index.d.ts +7 -1
- package/lib/terminals/index.js +50 -30
- package/lib/terminals/model.d.ts +11 -2
- package/lib/terminals/model.js +27 -8
- package/lib/terminals/tokens.d.ts +66 -0
- package/lib/terminals/tokens.js +7 -0
- package/lib/terminals/widget.d.ts +3 -6
- package/lib/terminals/widget.js +1 -1
- package/lib/topBar/index.js +3 -3
- package/lib/walkthrough/index.js +14 -3
- package/lib/walkthrough/panel.d.ts +12 -4
- package/lib/walkthrough/panel.js +9 -3
- package/package.json +49 -51
- package/schema/command-bar.json +10 -0
- package/schema/omnibox.json +15 -0
- package/src/agentSessions.ts +6 -2
- package/src/askAgent/editorSelection.ts +138 -0
- package/src/askAgent/icons.ts +16 -0
- package/src/askAgent/index.ts +915 -0
- package/src/askAgent/popup.tsx +407 -0
- package/src/askAgent/prompt.ts +144 -0
- package/src/askAgent/queue.ts +269 -0
- package/src/askAgent/queuePanel.tsx +418 -0
- package/src/askAgent/targetPicker.tsx +209 -0
- package/src/askAgent/tokens.ts +139 -0
- package/src/commandBar/index.ts +75 -49
- package/src/editorBreadcrumbs/index.ts +14 -4
- package/src/editorBreadcrumbs/widget.ts +11 -9
- package/src/fileBrowser/commands.ts +45 -31
- package/src/fileBrowser/contents.ts +7 -3
- package/src/fileBrowser/fileBrowser.tsx +1 -1
- package/src/fileBrowser/widget.tsx +5 -3
- package/src/fileTypeIcons/index.ts +6 -3
- package/src/git/api.ts +2 -61
- package/src/git/askRequest.ts +113 -0
- package/src/git/commands.ts +41 -14
- package/src/git/diffModel.ts +1 -1
- package/src/git/diffProvider.tsx +11 -3
- package/src/git/diffSurface.tsx +114 -22
- package/src/git/diffTheme.ts +1 -1
- package/src/git/diffWidget.tsx +54 -7
- package/src/git/imageDiff.tsx +169 -58
- package/src/git/index.ts +10 -2
- package/src/git/notebookDiff.tsx +91 -57
- package/src/git/tokens.ts +4 -2
- package/src/highlight/index.ts +12 -8
- package/src/index.ts +2 -0
- package/src/launcher/agents.ts +3 -4
- package/src/launcher/commands.ts +6 -2
- package/src/launcher/dashboard.tsx +86 -38
- package/src/launcher/editorRegistry.ts +10 -4
- package/src/launcher/editors.ts +15 -5
- package/src/launcher/icons.ts +11 -12
- package/src/launcher/index.ts +2 -1
- package/src/launcher/invocation.ts +1 -1
- package/src/launcher/schemaDefaults.ts +3 -1
- package/src/mimeData.ts +20 -0
- package/src/omnibox/index.ts +48 -1
- package/src/omnibox/model.ts +173 -33
- package/src/omnibox/recents.ts +155 -0
- package/src/omnibox/tokens.ts +6 -2
- package/src/omnibox/widget.tsx +54 -13
- package/src/showOutput/index.ts +20 -23
- package/src/terminals/agentTerminals.ts +238 -0
- package/src/terminals/index.ts +54 -30
- package/src/terminals/model.ts +28 -7
- package/src/terminals/tokens.ts +77 -0
- package/src/terminals/widget.tsx +4 -2
- package/src/topBar/index.ts +12 -6
- package/src/walkthrough/index.ts +19 -4
- package/src/walkthrough/panel.ts +15 -5
- package/style/askAgent.css +544 -0
- package/style/commandBar.css +12 -33
- package/style/git.css +72 -31
- package/style/index.css +1 -0
- package/style/index.js +2 -0
- package/style/topBar.css +4 -4
package/lib/git/diffSurface.js
CHANGED
|
@@ -143,7 +143,7 @@ export function isPierreTheme(themeManager) {
|
|
|
143
143
|
* Shared renderer for text, notebook and image diffs.
|
|
144
144
|
*/
|
|
145
145
|
export function DiffSurface(props) {
|
|
146
|
-
const { loading, error, isBinary, oldText, newText, newName, oldName, dark, pierreTheme, rendermime, notebookViewMode, diffStyle, onNotebookAvailabilityChange, onFileDiffActiveChange, onMetadataChange, hunkDiscard } = props;
|
|
146
|
+
const { loading, error, isBinary, oldText, newText, newName, oldName, dark, pierreTheme, rendermime, notebookViewMode, diffStyle, onNotebookAvailabilityChange, onFileDiffActiveChange, onMetadataChange, hunkDiscard, onLineAsk, trans } = props;
|
|
147
147
|
const hasContent = !loading && error === null && !isBinary;
|
|
148
148
|
// Raster images take a dedicated `<img>`-based view instead of a
|
|
149
149
|
// text/notebook diff; the host passes the two sides as base64 strings in
|
|
@@ -248,9 +248,20 @@ export function DiffSurface(props) {
|
|
|
248
248
|
}
|
|
249
249
|
const { hunkIndex } = annotation.metadata;
|
|
250
250
|
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-hunkAnnotation" },
|
|
251
|
-
React.createElement("button", { type: "button", className: "jp-xtralab-DiffWidget-hunkButton", title: "Discard this hunk's changes", "aria-label":
|
|
251
|
+
React.createElement("button", { type: "button", className: "jp-xtralab-DiffWidget-hunkButton", title: trans.__("Discard this hunk's changes"), "aria-label": trans.__('Discard hunk'), onClick: () => void handleDiscardHunk(hunkIndex) },
|
|
252
252
|
React.createElement(undoIcon.react, { tag: "span", className: "jp-xtralab-DiffWidget-hunkButton-icon", elementSize: "normal" }))));
|
|
253
|
-
}, [handleDiscardHunk]);
|
|
253
|
+
}, [handleDiscardHunk, trans]);
|
|
254
|
+
const handleGutterUtilityClick = React.useCallback((range) => {
|
|
255
|
+
var _a, _b, _c;
|
|
256
|
+
if (onLineAsk === undefined) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
// Anchor to the gutter "+" button that was just clicked. It lives in
|
|
260
|
+
// the `@pierre/diffs` shadow root (which is open), parked on the
|
|
261
|
+
// hovered/selected line's number element.
|
|
262
|
+
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
|
+
onLineAsk(range, slot instanceof Element ? slot.getBoundingClientRect() : null);
|
|
264
|
+
}, [onLineAsk]);
|
|
254
265
|
const handleResizerPointerDown = React.useCallback((event) => {
|
|
255
266
|
// Only react to primary-button drags. Right-click / middle-click on
|
|
256
267
|
// the handle should pass through to the browser default.
|
|
@@ -294,24 +305,49 @@ export function DiffSurface(props) {
|
|
|
294
305
|
setLeftRatio(DEFAULT_SPLIT_RATIO);
|
|
295
306
|
writeStoredSplitRatio(DEFAULT_SPLIT_RATIO);
|
|
296
307
|
}, []);
|
|
308
|
+
const handleResizerKeyDown = React.useCallback((event) => {
|
|
309
|
+
const step = event.shiftKey ? 0.1 : 0.01;
|
|
310
|
+
let next = null;
|
|
311
|
+
switch (event.key) {
|
|
312
|
+
case 'ArrowLeft':
|
|
313
|
+
next = leftRatioRef.current - step;
|
|
314
|
+
break;
|
|
315
|
+
case 'ArrowRight':
|
|
316
|
+
next = leftRatioRef.current + step;
|
|
317
|
+
break;
|
|
318
|
+
case 'Home':
|
|
319
|
+
next = MIN_SPLIT_RATIO;
|
|
320
|
+
break;
|
|
321
|
+
case 'End':
|
|
322
|
+
next = MAX_SPLIT_RATIO;
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
if (next === null) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
event.preventDefault();
|
|
329
|
+
const clamped = Math.max(MIN_SPLIT_RATIO, Math.min(MAX_SPLIT_RATIO, next));
|
|
330
|
+
setLeftRatio(clamped);
|
|
331
|
+
writeStoredSplitRatio(clamped);
|
|
332
|
+
}, []);
|
|
297
333
|
if (loading) {
|
|
298
|
-
return React.createElement("div", { className: "jp-xtralab-DiffWidget-status" },
|
|
334
|
+
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-status" }, trans.__('Loading diff…')));
|
|
299
335
|
}
|
|
300
336
|
if (error !== null) {
|
|
301
337
|
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-status", "data-error": "true" }, error));
|
|
302
338
|
}
|
|
303
339
|
if (isBinary) {
|
|
304
|
-
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-status" },
|
|
340
|
+
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-status" }, trans.__('Binary file — diff not supported.')));
|
|
305
341
|
}
|
|
306
342
|
if (imageType !== null) {
|
|
307
343
|
// `oldText`/`newText` carry the two revisions as base64 here. The
|
|
308
344
|
// image view owns its own layout and mode selector, so it does not
|
|
309
345
|
// use the split resizer or the notebook/JSON toggle.
|
|
310
346
|
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-content" },
|
|
311
|
-
React.createElement(ImageDiffView, { reference: oldText, challenger: newText, fileType: imageType })));
|
|
347
|
+
React.createElement(ImageDiffView, { reference: oldText, challenger: newText, fileType: imageType, trans: trans })));
|
|
312
348
|
}
|
|
313
349
|
if (!showNotebookView && !showFileDiff) {
|
|
314
|
-
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-status" },
|
|
350
|
+
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-status" }, trans.__('No content to diff.')));
|
|
315
351
|
}
|
|
316
352
|
// Custom property set on the diffs-container host. The library's
|
|
317
353
|
// shadow-root rule consumes it via `var(--xtralab-split-cols, …)` (see
|
|
@@ -323,7 +359,7 @@ export function DiffSurface(props) {
|
|
|
323
359
|
};
|
|
324
360
|
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-content" },
|
|
325
361
|
React.createElement("div", { ref: wrapperRef, className: "jp-xtralab-DiffWidget-body" },
|
|
326
|
-
React.createElement("div", { className: "jp-xtralab-DiffWidget-scroll" }, showNotebookView && notebookDiff !== null ? (React.createElement(NotebookDiffView, { diff: notebookDiff, dark: dark, pierreTheme: pierreTheme, rendermime: rendermime })) : showFileDiff && metadata !== null ? (React.createElement(FileDiff, { fileDiff: metadata, lineAnnotations: lineAnnotations, renderAnnotation: renderAnnotation, style: hostStyle,
|
|
362
|
+
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,
|
|
327
363
|
// Disable the worker pool: JupyterLab's webpack federation
|
|
328
364
|
// pipeline doesn't ship the library's
|
|
329
365
|
// `@pierre/diffs/worker/worker.js` file at a URL the worker
|
|
@@ -343,9 +379,19 @@ export function DiffSurface(props) {
|
|
|
343
379
|
// string constant lets the library short-circuit the
|
|
344
380
|
// re-render path it uses when this option actually
|
|
345
381
|
// changes.
|
|
346
|
-
unsafeCSS: SPLIT_RESIZE_CSS
|
|
382
|
+
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
|
+
...(onLineAsk !== undefined
|
|
387
|
+
? {
|
|
388
|
+
enableLineSelection: true,
|
|
389
|
+
enableGutterUtility: true,
|
|
390
|
+
onGutterUtilityClick: handleGutterUtilityClick
|
|
391
|
+
}
|
|
392
|
+
: {})
|
|
347
393
|
} })) : null),
|
|
348
|
-
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), title:
|
|
394
|
+
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)));
|
|
349
395
|
}
|
|
350
396
|
/**
|
|
351
397
|
* Segmented Notebook/JSON selector. Hosts mount this into whatever toolbar
|
|
@@ -354,13 +400,13 @@ export function DiffSurface(props) {
|
|
|
354
400
|
* from the same state they pass to {@link DiffSurface}.
|
|
355
401
|
*/
|
|
356
402
|
export function NotebookViewModeControl(props) {
|
|
357
|
-
const { mode, available, onChange } = props;
|
|
403
|
+
const { mode, available, onChange, trans } = props;
|
|
358
404
|
if (!available) {
|
|
359
405
|
return React.createElement(React.Fragment, null);
|
|
360
406
|
}
|
|
361
|
-
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-segmented", role: "tablist", "aria-label":
|
|
362
|
-
React.createElement("button", { type: "button", role: "tab", "aria-selected": mode === 'notebook', "data-active": mode === 'notebook', className: "jp-xtralab-DiffWidget-segmentedButton", onClick: () => onChange('notebook') },
|
|
363
|
-
React.createElement("button", { type: "button", role: "tab", "aria-selected": mode === 'json', "data-active": mode === 'json', className: "jp-xtralab-DiffWidget-segmentedButton", onClick: () => onChange('json') },
|
|
407
|
+
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-segmented", role: "tablist", "aria-label": trans.__('Notebook diff view mode') },
|
|
408
|
+
React.createElement("button", { type: "button", role: "tab", "aria-selected": mode === 'notebook', "data-active": mode === 'notebook', className: "jp-xtralab-DiffWidget-segmentedButton", onClick: () => onChange('notebook') }, trans.__('Notebook')),
|
|
409
|
+
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'))));
|
|
364
410
|
}
|
|
365
411
|
/**
|
|
366
412
|
* Split / unified glyphs, inlined from the `@pierre/diffs` icon sprite
|
|
@@ -387,13 +433,13 @@ function DiffUnifiedIcon() {
|
|
|
387
433
|
* surface's `onFileDiffActiveChange`.
|
|
388
434
|
*/
|
|
389
435
|
export function DiffStyleControl(props) {
|
|
390
|
-
const { diffStyle, available, onChange } = props;
|
|
436
|
+
const { diffStyle, available, onChange, trans } = props;
|
|
391
437
|
if (!available) {
|
|
392
438
|
return React.createElement(React.Fragment, null);
|
|
393
439
|
}
|
|
394
|
-
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-segmented", role: "tablist", "aria-label":
|
|
395
|
-
React.createElement("button", { type: "button", role: "tab", "aria-selected": diffStyle === 'split', "data-active": diffStyle === 'split', className: "jp-xtralab-DiffWidget-segmentedButton jp-xtralab-DiffWidget-segmentedButton-icon", title:
|
|
440
|
+
return (React.createElement("div", { className: "jp-xtralab-DiffWidget-segmented", role: "tablist", "aria-label": trans.__('Diff view style') },
|
|
441
|
+
React.createElement("button", { type: "button", role: "tab", "aria-selected": diffStyle === 'split', "data-active": diffStyle === 'split', className: "jp-xtralab-DiffWidget-segmentedButton jp-xtralab-DiffWidget-segmentedButton-icon", title: trans.__('Split (side-by-side) view'), "aria-label": trans.__('Split view'), onClick: () => onChange('split') },
|
|
396
442
|
React.createElement(DiffSplitIcon, null)),
|
|
397
|
-
React.createElement("button", { type: "button", role: "tab", "aria-selected": diffStyle === 'unified', "data-active": diffStyle === 'unified', className: "jp-xtralab-DiffWidget-segmentedButton jp-xtralab-DiffWidget-segmentedButton-icon", title:
|
|
443
|
+
React.createElement("button", { type: "button", role: "tab", "aria-selected": diffStyle === 'unified', "data-active": diffStyle === 'unified', className: "jp-xtralab-DiffWidget-segmentedButton jp-xtralab-DiffWidget-segmentedButton-icon", title: trans.__('Unified (inline) view'), "aria-label": trans.__('Unified view'), onClick: () => onChange('unified') },
|
|
398
444
|
React.createElement(DiffUnifiedIcon, null))));
|
|
399
445
|
}
|
package/lib/git/diffTheme.d.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
import { type DiffsThemeNames } from '@pierre/diffs';
|
|
2
|
-
/**
|
|
3
|
-
* Shiki CSS-variable theme used for non-Pierre JupyterLab themes.
|
|
4
|
-
*/
|
|
5
|
-
export declare const JUPYTERLAB_DIFF_THEME: DiffsThemeNames;
|
|
6
2
|
/**
|
|
7
3
|
* Choose Pierre's palette or the JupyterLab CSS-variable theme.
|
|
8
4
|
*/
|
package/lib/git/diffTheme.js
CHANGED
|
@@ -2,7 +2,7 @@ import { registerCustomCSSVariableTheme } from '@pierre/diffs';
|
|
|
2
2
|
/**
|
|
3
3
|
* Shiki CSS-variable theme used for non-Pierre JupyterLab themes.
|
|
4
4
|
*/
|
|
5
|
-
|
|
5
|
+
const JUPYTERLAB_DIFF_THEME = 'jupyterlab';
|
|
6
6
|
/**
|
|
7
7
|
* Register a Shiki theme whose token colors read JupyterLab's editor
|
|
8
8
|
* variables. The values are CSS variables, so theme changes recolor the diff.
|
package/lib/git/diffWidget.d.ts
CHANGED
|
@@ -3,9 +3,11 @@ import { IThemeManager } from '@jupyterlab/apputils';
|
|
|
3
3
|
import { Git } from '@jupyterlab/git';
|
|
4
4
|
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
5
5
|
import { Contents } from '@jupyterlab/services';
|
|
6
|
+
import type { TranslationBundle } from '@jupyterlab/translation';
|
|
6
7
|
import { ReactWidget } from '@jupyterlab/ui-components';
|
|
7
8
|
import { ISignal } from '@lumino/signaling';
|
|
8
9
|
import { Widget } from '@lumino/widgets';
|
|
10
|
+
import type { IAskAgent } from '../askAgent/tokens';
|
|
9
11
|
import { DIFF_WIDGET_CSS_CLASS, type DiffStyle, type NotebookDiffViewMode } from './diffSurface';
|
|
10
12
|
export { DIFF_WIDGET_CSS_CLASS };
|
|
11
13
|
/**
|
|
@@ -23,6 +25,12 @@ export interface IXtralabDiffContext {
|
|
|
23
25
|
contentsManager: Contents.IManager;
|
|
24
26
|
rendermime: IRenderMimeRegistry | null;
|
|
25
27
|
themeManager: IThemeManager | null;
|
|
28
|
+
/**
|
|
29
|
+
* The ask-agent popup; when available, diff line selections get an
|
|
30
|
+
* "ask an agent about these lines" gutter button.
|
|
31
|
+
*/
|
|
32
|
+
askAgent: IAskAgent | null;
|
|
33
|
+
trans: TranslationBundle;
|
|
26
34
|
}
|
|
27
35
|
/**
|
|
28
36
|
* Shared diff widget used by the launcher and by `jupyterlab-git`.
|
|
@@ -67,6 +75,7 @@ export declare class XtralabDiffWidget extends ReactWidget implements Git.Diff.I
|
|
|
67
75
|
get emptied(): ISignal<this, void>;
|
|
68
76
|
notifyEmptied(): void;
|
|
69
77
|
settleRefresh(): void;
|
|
78
|
+
dispose(): void;
|
|
70
79
|
get reloadNonce(): number;
|
|
71
80
|
get context(): IXtralabDiffContext;
|
|
72
81
|
protected render(): React.ReactElement;
|
package/lib/git/diffWidget.js
CHANGED
|
@@ -4,6 +4,7 @@ import { Git } from '@jupyterlab/git';
|
|
|
4
4
|
import { ReactWidget } from '@jupyterlab/ui-components';
|
|
5
5
|
import { PromiseDelegate } from '@lumino/coreutils';
|
|
6
6
|
import { Signal } from '@lumino/signaling';
|
|
7
|
+
import { buildDiffAskRequest } from './askRequest';
|
|
7
8
|
import { DIFF_WIDGET_CSS_CLASS, DiffStyleControl, DiffSurface, NotebookViewModeControl, isDarkTheme, isPierreTheme, readStoredDiffStyle, readStoredNotebookViewMode, writeStoredDiffStyle, writeStoredNotebookViewMode } from './diffSurface';
|
|
8
9
|
import { imageDataType } from './imageDiff';
|
|
9
10
|
export { DIFF_WIDGET_CSS_CLASS };
|
|
@@ -64,6 +65,9 @@ export class XtralabDiffWidget extends ReactWidget {
|
|
|
64
65
|
* Re-pull both sides of the model and re-render.
|
|
65
66
|
*/
|
|
66
67
|
async refresh() {
|
|
68
|
+
// Settle any refresh still awaiting so a rapid second call cannot orphan
|
|
69
|
+
// the earlier caller's promise.
|
|
70
|
+
this.settleRefresh();
|
|
67
71
|
const done = new PromiseDelegate();
|
|
68
72
|
this._pendingRefresh = done;
|
|
69
73
|
this._reloadNonce += 1;
|
|
@@ -141,6 +145,13 @@ export class XtralabDiffWidget extends ReactWidget {
|
|
|
141
145
|
this._pendingRefresh = null;
|
|
142
146
|
pending === null || pending === void 0 ? void 0 : pending.resolve();
|
|
143
147
|
}
|
|
148
|
+
dispose() {
|
|
149
|
+
// Release any in-flight refresh awaiter so a host that awaits refresh()
|
|
150
|
+
// (jupyterlab-git re-shows its diff button only once it resolves) does not
|
|
151
|
+
// hang when the widget is torn down mid-fetch.
|
|
152
|
+
this.settleRefresh();
|
|
153
|
+
super.dispose();
|
|
154
|
+
}
|
|
144
155
|
get reloadNonce() {
|
|
145
156
|
return this._reloadNonce;
|
|
146
157
|
}
|
|
@@ -155,7 +166,7 @@ function ModelDiffView(props) {
|
|
|
155
166
|
var _a, _b;
|
|
156
167
|
const { widget } = props;
|
|
157
168
|
const model = widget.model;
|
|
158
|
-
const { contentsManager, rendermime, themeManager } = widget.context;
|
|
169
|
+
const { contentsManager, rendermime, themeManager, askAgent, trans } = widget.context;
|
|
159
170
|
const nonce = widget.reloadNonce;
|
|
160
171
|
const [state, setState] = React.useState({
|
|
161
172
|
loading: true,
|
|
@@ -166,7 +177,7 @@ function ModelDiffView(props) {
|
|
|
166
177
|
// Mirror toolbar-driven notebook view changes into React state.
|
|
167
178
|
const [notebookViewMode, setNotebookViewMode] = React.useState(() => widget.notebookViewMode);
|
|
168
179
|
React.useEffect(() => {
|
|
169
|
-
const handler = (
|
|
180
|
+
const handler = (sender, mode) => {
|
|
170
181
|
setNotebookViewMode(mode);
|
|
171
182
|
};
|
|
172
183
|
widget.notebookViewModeChanged.connect(handler);
|
|
@@ -181,7 +192,7 @@ function ModelDiffView(props) {
|
|
|
181
192
|
// Mirror toolbar-driven diff-style changes into React state.
|
|
182
193
|
const [diffStyle, setDiffStyle] = React.useState(() => widget.diffStyle);
|
|
183
194
|
React.useEffect(() => {
|
|
184
|
-
const handler = (
|
|
195
|
+
const handler = (sender, style) => {
|
|
185
196
|
setDiffStyle(style);
|
|
186
197
|
};
|
|
187
198
|
widget.diffStyleChanged.connect(handler);
|
|
@@ -302,7 +313,22 @@ function ModelDiffView(props) {
|
|
|
302
313
|
void widget.refresh();
|
|
303
314
|
}
|
|
304
315
|
}), [canDiscardHunk, contentsManager, serverPath, widget]);
|
|
305
|
-
|
|
316
|
+
const handleLineAsk = React.useMemo(() => {
|
|
317
|
+
if (askAgent === null) {
|
|
318
|
+
return undefined;
|
|
319
|
+
}
|
|
320
|
+
return (range, anchor) => {
|
|
321
|
+
askAgent.open(buildDiffAskRequest({
|
|
322
|
+
model,
|
|
323
|
+
oldText: state.oldText,
|
|
324
|
+
newText: state.newText,
|
|
325
|
+
range,
|
|
326
|
+
anchor,
|
|
327
|
+
trans
|
|
328
|
+
}));
|
|
329
|
+
};
|
|
330
|
+
}, [askAgent, model, state.oldText, state.newText, trans]);
|
|
331
|
+
return (React.createElement(DiffSurface, { loading: state.loading, error: state.error, isBinary: isBinary, oldText: state.oldText, newText: state.newText, newName: model.filename, oldName: (_b = model.oldFilename) !== null && _b !== void 0 ? _b : model.filename, dark: dark, pierreTheme: pierre, rendermime: rendermime, notebookViewMode: notebookViewMode, diffStyle: diffStyle, onNotebookAvailabilityChange: handleNotebookAvailabilityChange, onFileDiffActiveChange: handleFileDiffActiveChange, onMetadataChange: handleMetadataChange, hunkDiscard: hunkDiscard, onLineAsk: handleLineAsk, trans: trans }));
|
|
306
332
|
}
|
|
307
333
|
/**
|
|
308
334
|
* Notebook/JSON toggle mounted into a host-owned toolbar.
|
|
@@ -322,10 +348,10 @@ function NotebookViewModeToolbarControl(props) {
|
|
|
322
348
|
const [mode, setMode] = React.useState(() => widget.notebookViewMode);
|
|
323
349
|
const [available, setAvailable] = React.useState(() => widget.hasNotebookView);
|
|
324
350
|
React.useEffect(() => {
|
|
325
|
-
const onMode = (
|
|
351
|
+
const onMode = (sender, next) => {
|
|
326
352
|
setMode(next);
|
|
327
353
|
};
|
|
328
|
-
const onAvailable = (
|
|
354
|
+
const onAvailable = (sender, next) => {
|
|
329
355
|
setAvailable(next);
|
|
330
356
|
};
|
|
331
357
|
widget.notebookViewModeChanged.connect(onMode);
|
|
@@ -337,7 +363,8 @@ function NotebookViewModeToolbarControl(props) {
|
|
|
337
363
|
widget.hasNotebookViewChanged.disconnect(onAvailable);
|
|
338
364
|
};
|
|
339
365
|
}, [widget]);
|
|
340
|
-
|
|
366
|
+
const trans = widget.context.trans;
|
|
367
|
+
return (React.createElement(NotebookViewModeControl, { mode: mode, available: available, onChange: next => widget.setNotebookViewMode(next), trans: trans }));
|
|
341
368
|
}
|
|
342
369
|
/**
|
|
343
370
|
* Split/Unified toggle mounted into a host-owned toolbar.
|
|
@@ -357,10 +384,10 @@ function DiffStyleToolbarControl(props) {
|
|
|
357
384
|
const [style, setStyle] = React.useState(() => widget.diffStyle);
|
|
358
385
|
const [available, setAvailable] = React.useState(() => widget.fileDiffActive);
|
|
359
386
|
React.useEffect(() => {
|
|
360
|
-
const onStyle = (
|
|
387
|
+
const onStyle = (sender, next) => {
|
|
361
388
|
setStyle(next);
|
|
362
389
|
};
|
|
363
|
-
const onAvailable = (
|
|
390
|
+
const onAvailable = (sender, next) => {
|
|
364
391
|
setAvailable(next);
|
|
365
392
|
};
|
|
366
393
|
widget.diffStyleChanged.connect(onStyle);
|
|
@@ -372,7 +399,8 @@ function DiffStyleToolbarControl(props) {
|
|
|
372
399
|
widget.fileDiffActiveChanged.disconnect(onAvailable);
|
|
373
400
|
};
|
|
374
401
|
}, [widget]);
|
|
375
|
-
|
|
402
|
+
const trans = widget.context.trans;
|
|
403
|
+
return (React.createElement(DiffStyleControl, { diffStyle: style, available: available, onChange: next => widget.setDiffStyle(next), trans: trans }));
|
|
376
404
|
}
|
|
377
405
|
/**
|
|
378
406
|
* Mount the shared diff toolbar controls into a host-owned toolbar.
|
package/lib/git/imageDiff.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
|
|
2
|
+
import type { TranslationBundle } from '@jupyterlab/translation';
|
|
3
|
+
/**
|
|
4
|
+
* The file extensions registered as the xtralab image diff provider.
|
|
5
|
+
*/
|
|
3
6
|
export declare const IMAGE_DIFF_EXTENSIONS: string[];
|
|
4
7
|
/**
|
|
5
8
|
* The `data:` MIME subtype for a path xtralab renders as an image, or
|
|
@@ -7,12 +10,22 @@ export declare const IMAGE_DIFF_EXTENSIONS: string[];
|
|
|
7
10
|
*/
|
|
8
11
|
export declare function imageDataType(path: string): string | null;
|
|
9
12
|
interface IImageDiffViewProps {
|
|
10
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Base64 of the reference (old) revision; empty when added.
|
|
15
|
+
*/
|
|
11
16
|
reference: string;
|
|
12
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* Base64 of the challenger (new) revision; empty when deleted.
|
|
19
|
+
*/
|
|
13
20
|
challenger: string;
|
|
14
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* MIME subtype from {@link imageDataType}.
|
|
23
|
+
*/
|
|
15
24
|
fileType: string;
|
|
25
|
+
/**
|
|
26
|
+
* Translation bundle for user-facing strings.
|
|
27
|
+
*/
|
|
28
|
+
trans: TranslationBundle;
|
|
16
29
|
}
|
|
17
30
|
/**
|
|
18
31
|
* The image comparison surface: a segmented mode selector (reusing the
|
package/lib/git/imageDiff.js
CHANGED
|
@@ -25,7 +25,9 @@ const IMAGE_DATA_TYPES = {
|
|
|
25
25
|
'.bmp': 'bmp',
|
|
26
26
|
'.ico': 'x-icon'
|
|
27
27
|
};
|
|
28
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* The file extensions registered as the xtralab image diff provider.
|
|
30
|
+
*/
|
|
29
31
|
export const IMAGE_DIFF_EXTENSIONS = Object.keys(IMAGE_DATA_TYPES);
|
|
30
32
|
/**
|
|
31
33
|
* The `data:` MIME subtype for a path xtralab renders as an image, or
|
|
@@ -71,7 +73,9 @@ function writeStoredImageViewMode(mode) {
|
|
|
71
73
|
function cleanBase64(value) {
|
|
72
74
|
return value.replace(/\s+/g, '');
|
|
73
75
|
}
|
|
74
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* Decoded byte length of a (whitespace-free) base64 string.
|
|
78
|
+
*/
|
|
75
79
|
function base64ByteLength(clean) {
|
|
76
80
|
if (clean.length === 0) {
|
|
77
81
|
return 0;
|
|
@@ -108,7 +112,7 @@ function toSide(raw, fileType) {
|
|
|
108
112
|
* plus the selected 2-up / swipe / onion-skin view.
|
|
109
113
|
*/
|
|
110
114
|
export function ImageDiffView(props) {
|
|
111
|
-
const { reference, challenger, fileType } = props;
|
|
115
|
+
const { reference, challenger, fileType, trans } = props;
|
|
112
116
|
const ref = React.useMemo(() => toSide(reference, fileType), [reference, fileType]);
|
|
113
117
|
const chall = React.useMemo(() => toSide(challenger, fileType), [challenger, fileType]);
|
|
114
118
|
const [mode, setMode] = React.useState(() => readStoredImageViewMode());
|
|
@@ -118,12 +122,12 @@ export function ImageDiffView(props) {
|
|
|
118
122
|
}, []);
|
|
119
123
|
return (React.createElement("div", { className: "jp-xtralab-ImageDiff" },
|
|
120
124
|
React.createElement("div", { className: "jp-xtralab-ImageDiff-header" },
|
|
121
|
-
React.createElement("div", { className: "jp-xtralab-DiffWidget-segmented", role: "tablist", "aria-label":
|
|
122
|
-
['2-up', '2-up'],
|
|
123
|
-
['swipe', 'Swipe'],
|
|
124
|
-
['onion', 'Onion Skin']
|
|
125
|
+
React.createElement("div", { className: "jp-xtralab-DiffWidget-segmented", role: "tablist", "aria-label": trans.__('Image diff view mode') }, [
|
|
126
|
+
['2-up', trans.__('2-up')],
|
|
127
|
+
['swipe', trans.__('Swipe')],
|
|
128
|
+
['onion', trans.__('Onion Skin')]
|
|
125
129
|
].map(([value, label]) => (React.createElement("button", { key: value, type: "button", role: "tab", "aria-selected": mode === value, "data-active": mode === value, className: "jp-xtralab-DiffWidget-segmentedButton", onClick: () => changeMode(value) }, label))))),
|
|
126
|
-
React.createElement("div", { className: "jp-xtralab-ImageDiff-body" }, mode === '2-up' ? (React.createElement(TwoUp, { reference: ref, challenger: chall })) : mode === 'swipe' ? (React.createElement(Swipe, { reference: ref, challenger: chall })) : (React.createElement(OnionSkin, { reference: ref, challenger: chall })))));
|
|
130
|
+
React.createElement("div", { className: "jp-xtralab-ImageDiff-body" }, mode === '2-up' ? (React.createElement(TwoUp, { reference: ref, challenger: chall, trans: trans })) : mode === 'swipe' ? (React.createElement(Swipe, { reference: ref, challenger: chall, trans: trans })) : (React.createElement(OnionSkin, { reference: ref, challenger: chall, trans: trans })))));
|
|
127
131
|
}
|
|
128
132
|
function useNaturalSize() {
|
|
129
133
|
const [size, setSize] = React.useState(null);
|
|
@@ -142,16 +146,16 @@ function caption(side, size, emptyLabel) {
|
|
|
142
146
|
const dims = size !== null ? `${size[0]}×${size[1]} · ` : '';
|
|
143
147
|
return `${dims}${formatBytes(side.bytes)}`;
|
|
144
148
|
}
|
|
145
|
-
function TwoUp({ reference, challenger }) {
|
|
149
|
+
function TwoUp({ reference, challenger, trans }) {
|
|
146
150
|
const [refSize, onRefLoad] = useNaturalSize();
|
|
147
151
|
const [challSize, onChallLoad] = useNaturalSize();
|
|
148
152
|
return (React.createElement("div", { className: "jp-xtralab-ImageDiff-twoUp" },
|
|
149
153
|
React.createElement("figure", { className: "jp-xtralab-ImageDiff-col" },
|
|
150
|
-
React.createElement("div", { className: "jp-xtralab-ImageDiff-frame", "data-side": "reference" }, reference.uri !== null ? (React.createElement("img", { className: "jp-xtralab-ImageDiff-img", src: reference.uri, alt:
|
|
151
|
-
React.createElement("figcaption", { className: "jp-xtralab-ImageDiff-caption" }, caption(reference, refSize, 'Added'))),
|
|
154
|
+
React.createElement("div", { className: "jp-xtralab-ImageDiff-frame", "data-side": "reference" }, reference.uri !== null ? (React.createElement("img", { className: "jp-xtralab-ImageDiff-img", src: reference.uri, alt: trans.__('Reference revision'), onLoad: onRefLoad })) : (React.createElement("div", { className: "jp-xtralab-ImageDiff-empty" }, trans.__('No previous image')))),
|
|
155
|
+
React.createElement("figcaption", { className: "jp-xtralab-ImageDiff-caption" }, caption(reference, refSize, trans.__('Added')))),
|
|
152
156
|
React.createElement("figure", { className: "jp-xtralab-ImageDiff-col" },
|
|
153
|
-
React.createElement("div", { className: "jp-xtralab-ImageDiff-frame", "data-side": "challenger" }, challenger.uri !== null ? (React.createElement("img", { className: "jp-xtralab-ImageDiff-img", src: challenger.uri, alt:
|
|
154
|
-
React.createElement("figcaption", { className: "jp-xtralab-ImageDiff-caption" }, caption(challenger, challSize, 'Deleted')))));
|
|
157
|
+
React.createElement("div", { className: "jp-xtralab-ImageDiff-frame", "data-side": "challenger" }, challenger.uri !== null ? (React.createElement("img", { className: "jp-xtralab-ImageDiff-img", src: challenger.uri, alt: trans.__('Challenger revision'), onLoad: onChallLoad })) : (React.createElement("div", { className: "jp-xtralab-ImageDiff-empty" }, trans.__('Deleted')))),
|
|
158
|
+
React.createElement("figcaption", { className: "jp-xtralab-ImageDiff-caption" }, caption(challenger, challSize, trans.__('Deleted'))))));
|
|
155
159
|
}
|
|
156
160
|
/**
|
|
157
161
|
* Range slider shared by the swipe and onion-skin views. A native
|
|
@@ -162,10 +166,64 @@ function RangeSlider(props) {
|
|
|
162
166
|
const { value, onChange, ariaLabel } = props;
|
|
163
167
|
return (React.createElement("input", { className: "jp-xtralab-ImageDiff-slider", type: "range", min: 0, max: 100, step: 1, value: value, "aria-label": ariaLabel, onChange: event => onChange(Number(event.target.value)) }));
|
|
164
168
|
}
|
|
165
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Fit a `naturalWidth × naturalHeight` image inside an `areaWidth ×
|
|
171
|
+
* areaHeight` box without upscaling past its intrinsic size — the same
|
|
172
|
+
* "contain, never enlarge" rule the 2-up and onion views get for free from
|
|
173
|
+
* `max-width/height: 100%; width/height: auto`. Returns the displayed pixel
|
|
174
|
+
* box, or `null` until both the image and the area have been measured.
|
|
175
|
+
*/
|
|
176
|
+
function containBox(naturalWidth, naturalHeight, areaWidth, areaHeight) {
|
|
177
|
+
if (naturalWidth <= 0 ||
|
|
178
|
+
naturalHeight <= 0 ||
|
|
179
|
+
areaWidth <= 0 ||
|
|
180
|
+
areaHeight <= 0) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
const scale = Math.min(areaWidth / naturalWidth, areaHeight / naturalHeight, 1);
|
|
184
|
+
return { width: naturalWidth * scale, height: naturalHeight * scale };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Track an element's content-box size, kept current across layout changes.
|
|
188
|
+
*/
|
|
189
|
+
function useElementSize() {
|
|
190
|
+
const ref = React.useRef(null);
|
|
191
|
+
const [size, setSize] = React.useState({ width: 0, height: 0 });
|
|
192
|
+
React.useEffect(() => {
|
|
193
|
+
const el = ref.current;
|
|
194
|
+
if (el === null) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const measure = () => {
|
|
198
|
+
setSize({ width: el.clientWidth, height: el.clientHeight });
|
|
199
|
+
};
|
|
200
|
+
measure();
|
|
201
|
+
const observer = new ResizeObserver(measure);
|
|
202
|
+
observer.observe(el);
|
|
203
|
+
return () => {
|
|
204
|
+
observer.disconnect();
|
|
205
|
+
};
|
|
206
|
+
}, []);
|
|
207
|
+
return [ref, size];
|
|
208
|
+
}
|
|
209
|
+
function Swipe({ reference, challenger, trans }) {
|
|
210
|
+
var _a, _b, _c, _d;
|
|
166
211
|
const [position, setPosition] = React.useState(50);
|
|
167
212
|
const [dragging, setDragging] = React.useState(false);
|
|
213
|
+
const [refSize, onRefLoad] = useNaturalSize();
|
|
214
|
+
const [challSize, onChallLoad] = useNaturalSize();
|
|
215
|
+
const [areaRef, area] = useElementSize();
|
|
168
216
|
const frameRef = React.useRef(null);
|
|
217
|
+
// Display both revisions in a single box — the union of their intrinsic
|
|
218
|
+
// sizes, scaled down to fit but never enlarged — and size the frame to
|
|
219
|
+
// exactly that box. The divider's `left: N%`, the pointer-to-percent
|
|
220
|
+
// mapping (below) and each image's `clip-path` inset then all resolve
|
|
221
|
+
// against the same width, so the white line sits precisely on the seam
|
|
222
|
+
// between the two revisions at every position. A full-width frame instead
|
|
223
|
+
// let the line drift off the seam by as much as half the letterbox.
|
|
224
|
+
const naturalWidth = Math.max((_a = refSize === null || refSize === void 0 ? void 0 : refSize[0]) !== null && _a !== void 0 ? _a : 0, (_b = challSize === null || challSize === void 0 ? void 0 : challSize[0]) !== null && _b !== void 0 ? _b : 0);
|
|
225
|
+
const naturalHeight = Math.max((_c = refSize === null || refSize === void 0 ? void 0 : refSize[1]) !== null && _c !== void 0 ? _c : 0, (_d = challSize === null || challSize === void 0 ? void 0 : challSize[1]) !== null && _d !== void 0 ? _d : 0);
|
|
226
|
+
const box = containBox(naturalWidth, naturalHeight, area.width, area.height);
|
|
169
227
|
const setFromClientX = React.useCallback((clientX) => {
|
|
170
228
|
const frame = frameRef.current;
|
|
171
229
|
if (frame === null) {
|
|
@@ -230,27 +288,29 @@ function Swipe({ reference, challenger }) {
|
|
|
230
288
|
setPosition(Math.max(0, Math.min(100, next)));
|
|
231
289
|
}, [position]);
|
|
232
290
|
return (React.createElement("div", { className: "jp-xtralab-ImageDiff-interactive" },
|
|
233
|
-
React.createElement("div", { className: "jp-xtralab-ImageDiff-
|
|
234
|
-
React.createElement("div", { className: "jp-xtralab-ImageDiff-
|
|
235
|
-
|
|
291
|
+
React.createElement("div", { className: "jp-xtralab-ImageDiff-swipeArea", ref: areaRef },
|
|
292
|
+
React.createElement("div", { className: "jp-xtralab-ImageDiff-swipeFrame", ref: frameRef, style: box !== null
|
|
293
|
+
? { width: `${box.width}px`, height: `${box.height}px` }
|
|
294
|
+
: { width: 0, height: 0, visibility: 'hidden' }, onPointerDown: onPointerDown, "data-dragging": dragging ? 'true' : undefined },
|
|
295
|
+
reference.uri !== null ? (React.createElement("img", { className: "jp-xtralab-ImageDiff-swipeImg", src: reference.uri, alt: trans.__('Reference revision'), draggable: false, onLoad: onRefLoad, style: {
|
|
236
296
|
clipPath: `inset(0 ${100 - position}% 0 0)`
|
|
237
297
|
} })) : null,
|
|
238
|
-
challenger.uri !== null ? (React.createElement("img", { className: "jp-xtralab-ImageDiff-
|
|
298
|
+
challenger.uri !== null ? (React.createElement("img", { className: "jp-xtralab-ImageDiff-swipeImg", src: challenger.uri, alt: trans.__('Challenger revision'), draggable: false, onLoad: onChallLoad, style: {
|
|
239
299
|
clipPath: `inset(0 0 0 ${position}%)`
|
|
240
|
-
} })) : null
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
300
|
+
} })) : null,
|
|
301
|
+
React.createElement("div", { className: "jp-xtralab-ImageDiff-swipeDivider", style: { left: `${position}%` }, role: "slider", "aria-label": trans.__('Swipe between reference and challenger'), "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": Math.round(position), "aria-orientation": "vertical", tabIndex: 0, onKeyDown: onKeyDown },
|
|
302
|
+
React.createElement("div", { className: "jp-xtralab-ImageDiff-swipeDividerHandle jp-mod-top" },
|
|
303
|
+
React.createElement("div", { className: "jp-xtralab-ImageDiff-swipeDividerArrow jp-mod-left" }),
|
|
304
|
+
React.createElement("div", { className: "jp-xtralab-ImageDiff-swipeDividerArrow jp-mod-right" })),
|
|
305
|
+
React.createElement("div", { className: "jp-xtralab-ImageDiff-swipeDividerHandle jp-mod-bottom" },
|
|
306
|
+
React.createElement("div", { className: "jp-xtralab-ImageDiff-swipeDividerArrow jp-mod-left" }),
|
|
307
|
+
React.createElement("div", { className: "jp-xtralab-ImageDiff-swipeDividerArrow jp-mod-right" })))))));
|
|
248
308
|
}
|
|
249
|
-
function OnionSkin({ reference, challenger }) {
|
|
309
|
+
function OnionSkin({ reference, challenger, trans }) {
|
|
250
310
|
const [opacity, setOpacity] = React.useState(50);
|
|
251
311
|
return (React.createElement("div", { className: "jp-xtralab-ImageDiff-interactive" },
|
|
252
312
|
React.createElement("div", { className: "jp-xtralab-ImageDiff-stack" },
|
|
253
|
-
reference.uri !== null ? (React.createElement("img", { className: "jp-xtralab-ImageDiff-stackImg", src: reference.uri, alt:
|
|
254
|
-
challenger.uri !== null ? (React.createElement("img", { className: "jp-xtralab-ImageDiff-stackImg", src: challenger.uri, alt:
|
|
255
|
-
React.createElement(RangeSlider, { value: opacity, onChange: setOpacity, ariaLabel:
|
|
313
|
+
reference.uri !== null ? (React.createElement("img", { className: "jp-xtralab-ImageDiff-stackImg", src: reference.uri, alt: trans.__('Reference revision') })) : null,
|
|
314
|
+
challenger.uri !== null ? (React.createElement("img", { className: "jp-xtralab-ImageDiff-stackImg", src: challenger.uri, alt: trans.__('Challenger revision'), style: { opacity: opacity / 100 } })) : null),
|
|
315
|
+
React.createElement(RangeSlider, { value: opacity, onChange: setOpacity, ariaLabel: trans.__('Fade between reference and challenger') })));
|
|
256
316
|
}
|
package/lib/git/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { IThemeManager, WidgetTracker } from '@jupyterlab/apputils';
|
|
2
2
|
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
3
|
+
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
|
4
|
+
import { IAskAgent } from '../askAgent/tokens';
|
|
3
5
|
import { CommandIDs, PREVIEW_DIFF_WIDGET_ID, pinnedDiffWidgetId, registerGitCommands } from './commands';
|
|
4
6
|
import diffProviderPlugin from './diffProvider';
|
|
5
7
|
const GIT_DIFF_COMMAND_PLUGIN_ID = 'xtralab:git-diff-command';
|
|
@@ -14,8 +16,9 @@ const diffCommandPlugin = {
|
|
|
14
16
|
id: GIT_DIFF_COMMAND_PLUGIN_ID,
|
|
15
17
|
description: "The launcher dashboard's side-by-side git diff command, powered by @pierre/diffs.",
|
|
16
18
|
autoStart: true,
|
|
17
|
-
optional: [IThemeManager, IRenderMimeRegistry],
|
|
18
|
-
activate: (app, themeManager, rendermime) => {
|
|
19
|
+
optional: [IThemeManager, IRenderMimeRegistry, ITranslator, IAskAgent],
|
|
20
|
+
activate: (app, themeManager, rendermime, translator, askAgent) => {
|
|
21
|
+
const trans = (translator !== null && translator !== void 0 ? translator : nullTranslator).load('jupyterlab');
|
|
19
22
|
// Track open diffs so repeated opens reveal the existing tab.
|
|
20
23
|
const tracker = new WidgetTracker({
|
|
21
24
|
namespace: GIT_DIFF_TRACKER_NAMESPACE
|
|
@@ -30,6 +33,8 @@ const diffCommandPlugin = {
|
|
|
30
33
|
themeManager,
|
|
31
34
|
contentsManager: app.serviceManager.contents,
|
|
32
35
|
rendermime,
|
|
36
|
+
askAgent,
|
|
37
|
+
trans,
|
|
33
38
|
trackDiff: widget => tracker.add(widget),
|
|
34
39
|
onPinned: current => {
|
|
35
40
|
const existing = findDiff(current.change, true);
|