xtralab 0.15.1 → 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 +58 -33
- package/lib/fileBrowser/commands.d.ts +1 -0
- package/lib/fileBrowser/commands.js +46 -1
- package/lib/fileBrowser/fileBrowser.js +4 -8
- package/lib/fileBrowser/gitStatus.d.ts +16 -5
- package/lib/fileBrowser/gitStatus.js +28 -17
- package/lib/fileBrowser/widget.d.ts +15 -0
- package/lib/fileBrowser/widget.js +13 -0
- package/lib/git/commands.d.ts +5 -0
- package/lib/git/commands.js +4 -3
- 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 +34 -19
- package/lib/git/diffSurface.js +49 -115
- package/lib/git/diffTheme.d.ts +7 -0
- package/lib/git/diffTheme.js +23 -0
- package/lib/git/diffWidget.d.ts +7 -30
- package/lib/git/diffWidget.js +56 -100
- package/lib/git/imageDiff.d.ts +9 -0
- package/lib/git/imageDiff.js +2 -28
- package/lib/git/index.js +41 -2
- package/lib/git/notebookDiff.js +3 -2
- package/package.json +5 -3
- package/schema/git-diff-preferences.json +42 -0
- package/src/fileBrowser/commands.ts +64 -0
- package/src/fileBrowser/fileBrowser.tsx +4 -9
- package/src/fileBrowser/gitStatus.ts +33 -20
- package/src/fileBrowser/widget.tsx +22 -0
- package/src/git/commands.ts +13 -1
- package/src/git/diffPreferences.ts +192 -0
- package/src/git/diffProvider.tsx +11 -2
- package/src/git/diffSurface.tsx +113 -146
- package/src/git/diffTheme.ts +24 -0
- package/src/git/diffWidget.tsx +108 -138
- package/src/git/imageDiff.tsx +12 -34
- package/src/git/index.ts +51 -2
- package/src/git/notebookDiff.tsx +3 -2
- package/style/git.css +4 -2
package/src/git/diffSurface.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
|
5
5
|
import type { TranslationBundle } from '@jupyterlab/translation';
|
|
6
6
|
import { undoIcon } from '@jupyterlab/ui-components';
|
|
7
7
|
import { FileDiff } from '@pierre/diffs/react';
|
|
8
|
+
import { IconDiffSplit, IconDiffUnified, IconWordWrap } from '@pierre/icons';
|
|
8
9
|
import {
|
|
9
10
|
diffAcceptRejectHunk,
|
|
10
11
|
parseDiffFromFile,
|
|
@@ -20,16 +21,19 @@ import {
|
|
|
20
21
|
NotebookDiffView,
|
|
21
22
|
type INotebookDiffResult
|
|
22
23
|
} from './notebookDiff';
|
|
23
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
DEFAULT_SPLIT_RATIO,
|
|
26
|
+
MAX_SPLIT_RATIO,
|
|
27
|
+
MIN_SPLIT_RATIO,
|
|
28
|
+
type DiffStyle,
|
|
29
|
+
type DiffStyleControlMode,
|
|
30
|
+
type ImageDiffViewMode,
|
|
31
|
+
type NotebookDiffViewMode
|
|
32
|
+
} from './diffPreferences';
|
|
33
|
+
import { DIFF_SCROLLBAR_CSS, resolveDiffTheme } from './diffTheme';
|
|
24
34
|
|
|
25
35
|
export const DIFF_WIDGET_CSS_CLASS = 'jp-xtralab-DiffWidget';
|
|
26
36
|
|
|
27
|
-
const SPLIT_RATIO_STORAGE_KEY = 'xtralab:diff-split-ratio';
|
|
28
|
-
|
|
29
|
-
const MIN_SPLIT_RATIO = 0.1;
|
|
30
|
-
const MAX_SPLIT_RATIO = 0.9;
|
|
31
|
-
const DEFAULT_SPLIT_RATIO = 0.5;
|
|
32
|
-
|
|
33
37
|
/**
|
|
34
38
|
* Injected via the `unsafeCSS` option into the shadow root's `@layer
|
|
35
39
|
* unsafe`, beating the library's base rule that hardcodes `1fr 1fr`. Reads
|
|
@@ -40,95 +44,7 @@ const SPLIT_RESIZE_CSS = `pre[data-diff-type="split"][data-overflow="scroll"] {
|
|
|
40
44
|
grid-template-columns: var(--xtralab-split-cols, 1fr 1fr);
|
|
41
45
|
}`;
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
const raw = window.localStorage.getItem(SPLIT_RATIO_STORAGE_KEY);
|
|
46
|
-
if (raw === null) {
|
|
47
|
-
return DEFAULT_SPLIT_RATIO;
|
|
48
|
-
}
|
|
49
|
-
const parsed = Number.parseFloat(raw);
|
|
50
|
-
if (
|
|
51
|
-
Number.isFinite(parsed) &&
|
|
52
|
-
parsed >= MIN_SPLIT_RATIO &&
|
|
53
|
-
parsed <= MAX_SPLIT_RATIO
|
|
54
|
-
) {
|
|
55
|
-
return parsed;
|
|
56
|
-
}
|
|
57
|
-
} catch {
|
|
58
|
-
// localStorage can throw in privacy mode or sandboxed contexts.
|
|
59
|
-
}
|
|
60
|
-
return DEFAULT_SPLIT_RATIO;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function writeStoredSplitRatio(ratio: number): void {
|
|
64
|
-
try {
|
|
65
|
-
window.localStorage.setItem(SPLIT_RATIO_STORAGE_KEY, ratio.toString());
|
|
66
|
-
} catch {
|
|
67
|
-
// See readStoredSplitRatio — best-effort persistence.
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export type DiffStyle = 'split' | 'unified';
|
|
72
|
-
|
|
73
|
-
const DIFF_STYLE_STORAGE_KEY = 'xtralab:diff-style';
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Read the persisted diff style, defaulting to `'split'`.
|
|
77
|
-
*/
|
|
78
|
-
export function readStoredDiffStyle(): DiffStyle {
|
|
79
|
-
try {
|
|
80
|
-
const raw = window.localStorage.getItem(DIFF_STYLE_STORAGE_KEY);
|
|
81
|
-
if (raw === 'split' || raw === 'unified') {
|
|
82
|
-
return raw;
|
|
83
|
-
}
|
|
84
|
-
} catch {
|
|
85
|
-
// See readStoredSplitRatio.
|
|
86
|
-
}
|
|
87
|
-
return 'split';
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Persist the diff style to local storage (best-effort).
|
|
92
|
-
*/
|
|
93
|
-
export function writeStoredDiffStyle(style: DiffStyle): void {
|
|
94
|
-
try {
|
|
95
|
-
window.localStorage.setItem(DIFF_STYLE_STORAGE_KEY, style);
|
|
96
|
-
} catch {
|
|
97
|
-
// Best-effort.
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export type NotebookDiffViewMode = 'notebook' | 'json';
|
|
102
|
-
|
|
103
|
-
const NOTEBOOK_DIFF_VIEW_MODE_STORAGE_KEY = 'xtralab:notebook-diff-view-mode';
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Read the persisted notebook view mode, defaulting to `'notebook'`.
|
|
107
|
-
*/
|
|
108
|
-
export function readStoredNotebookViewMode(): NotebookDiffViewMode {
|
|
109
|
-
try {
|
|
110
|
-
const raw = window.localStorage.getItem(
|
|
111
|
-
NOTEBOOK_DIFF_VIEW_MODE_STORAGE_KEY
|
|
112
|
-
);
|
|
113
|
-
if (raw === 'json' || raw === 'notebook') {
|
|
114
|
-
return raw;
|
|
115
|
-
}
|
|
116
|
-
} catch {
|
|
117
|
-
// See readStoredSplitRatio.
|
|
118
|
-
}
|
|
119
|
-
return 'notebook';
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Persist the notebook view mode to local storage (best-effort).
|
|
124
|
-
*/
|
|
125
|
-
export function writeStoredNotebookViewMode(mode: NotebookDiffViewMode): void {
|
|
126
|
-
try {
|
|
127
|
-
window.localStorage.setItem(NOTEBOOK_DIFF_VIEW_MODE_STORAGE_KEY, mode);
|
|
128
|
-
} catch {
|
|
129
|
-
// Best-effort.
|
|
130
|
-
}
|
|
131
|
-
}
|
|
47
|
+
const DIFF_SURFACE_CSS = `${SPLIT_RESIZE_CSS}\n${DIFF_SCROLLBAR_CSS}`;
|
|
132
48
|
|
|
133
49
|
/**
|
|
134
50
|
* Annotation payload threaded back into `renderAnnotation`; carries the target hunk index.
|
|
@@ -238,6 +154,26 @@ interface IDiffSurfaceProps {
|
|
|
238
154
|
* Split vs unified layout for the textual file diff (host-controlled).
|
|
239
155
|
*/
|
|
240
156
|
diffStyle: DiffStyle;
|
|
157
|
+
/**
|
|
158
|
+
* Whether long lines wrap instead of scrolling horizontally (host-controlled).
|
|
159
|
+
*/
|
|
160
|
+
lineWrap: boolean;
|
|
161
|
+
/**
|
|
162
|
+
* Left pane width in split view, as a fraction of the diff width (host-controlled).
|
|
163
|
+
*/
|
|
164
|
+
splitRatio: number;
|
|
165
|
+
/**
|
|
166
|
+
* Called when the user finishes resizing the split panes.
|
|
167
|
+
*/
|
|
168
|
+
onSplitRatioChange: (ratio: number) => void;
|
|
169
|
+
/**
|
|
170
|
+
* 2-up, swipe or onion view for image diffs (host-controlled).
|
|
171
|
+
*/
|
|
172
|
+
imageViewMode: ImageDiffViewMode;
|
|
173
|
+
/**
|
|
174
|
+
* Called when the user picks another image view mode.
|
|
175
|
+
*/
|
|
176
|
+
onImageViewModeChange: (mode: ImageDiffViewMode) => void;
|
|
241
177
|
/**
|
|
242
178
|
* Fires when rendered-notebook availability changes so the host can toggle its Notebook/JSON control.
|
|
243
179
|
*/
|
|
@@ -283,6 +219,11 @@ export function DiffSurface(props: IDiffSurfaceProps): React.ReactElement {
|
|
|
283
219
|
rendermime,
|
|
284
220
|
notebookViewMode,
|
|
285
221
|
diffStyle,
|
|
222
|
+
lineWrap,
|
|
223
|
+
splitRatio,
|
|
224
|
+
onSplitRatioChange,
|
|
225
|
+
imageViewMode,
|
|
226
|
+
onImageViewModeChange,
|
|
286
227
|
onNotebookAvailabilityChange,
|
|
287
228
|
onFileDiffActiveChange,
|
|
288
229
|
onMetadataChange,
|
|
@@ -337,9 +278,11 @@ export function DiffSurface(props: IDiffSurfaceProps): React.ReactElement {
|
|
|
337
278
|
onFileDiffActiveChange?.(showFileDiff);
|
|
338
279
|
}, [onFileDiffActiveChange, showFileDiff]);
|
|
339
280
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
)
|
|
281
|
+
// Local while dragging; the host hears the ratio when a drag ends.
|
|
282
|
+
const [leftRatio, setLeftRatio] = React.useState<number>(splitRatio);
|
|
283
|
+
React.useEffect(() => {
|
|
284
|
+
setLeftRatio(splitRatio);
|
|
285
|
+
}, [splitRatio]);
|
|
343
286
|
const wrapperRef = React.useRef<HTMLDivElement | null>(null);
|
|
344
287
|
// Drag listeners read the live ratio from a ref to avoid stale captures.
|
|
345
288
|
const leftRatioRef = React.useRef(leftRatio);
|
|
@@ -461,19 +404,19 @@ export function DiffSurface(props: IDiffSurfaceProps): React.ReactElement {
|
|
|
461
404
|
handle.removeEventListener('pointermove', onPointerMove);
|
|
462
405
|
handle.removeEventListener('pointerup', onPointerEnd);
|
|
463
406
|
handle.removeEventListener('pointercancel', onPointerEnd);
|
|
464
|
-
|
|
407
|
+
onSplitRatioChange(leftRatioRef.current);
|
|
465
408
|
};
|
|
466
409
|
handle.addEventListener('pointermove', onPointerMove);
|
|
467
410
|
handle.addEventListener('pointerup', onPointerEnd);
|
|
468
411
|
handle.addEventListener('pointercancel', onPointerEnd);
|
|
469
412
|
},
|
|
470
|
-
[]
|
|
413
|
+
[onSplitRatioChange]
|
|
471
414
|
);
|
|
472
415
|
|
|
473
416
|
const handleResizerDoubleClick = React.useCallback(() => {
|
|
474
417
|
setLeftRatio(DEFAULT_SPLIT_RATIO);
|
|
475
|
-
|
|
476
|
-
}, []);
|
|
418
|
+
onSplitRatioChange(DEFAULT_SPLIT_RATIO);
|
|
419
|
+
}, [onSplitRatioChange]);
|
|
477
420
|
|
|
478
421
|
const handleResizerKeyDown = React.useCallback(
|
|
479
422
|
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
@@ -502,9 +445,9 @@ export function DiffSurface(props: IDiffSurfaceProps): React.ReactElement {
|
|
|
502
445
|
Math.min(MAX_SPLIT_RATIO, next)
|
|
503
446
|
);
|
|
504
447
|
setLeftRatio(clamped);
|
|
505
|
-
|
|
448
|
+
onSplitRatioChange(clamped);
|
|
506
449
|
},
|
|
507
|
-
[]
|
|
450
|
+
[onSplitRatioChange]
|
|
508
451
|
);
|
|
509
452
|
|
|
510
453
|
if (loading) {
|
|
@@ -535,6 +478,8 @@ export function DiffSurface(props: IDiffSurfaceProps): React.ReactElement {
|
|
|
535
478
|
reference={oldText}
|
|
536
479
|
challenger={newText}
|
|
537
480
|
fileType={imageType}
|
|
481
|
+
mode={imageViewMode}
|
|
482
|
+
onModeChange={onImageViewModeChange}
|
|
538
483
|
trans={trans}
|
|
539
484
|
/>
|
|
540
485
|
</div>
|
|
@@ -549,6 +494,9 @@ export function DiffSurface(props: IDiffSurfaceProps): React.ReactElement {
|
|
|
549
494
|
}
|
|
550
495
|
|
|
551
496
|
const leftPercent = leftRatio * 100;
|
|
497
|
+
// The wrapped split grid sizes its gutter tracks to their content, so a
|
|
498
|
+
// percentage split cannot line up with the handle.
|
|
499
|
+
const showResizer = showFileDiff && diffStyle === 'split' && !lineWrap;
|
|
552
500
|
const hostStyle = {
|
|
553
501
|
'--xtralab-split-cols': `${leftPercent}% ${100 - leftPercent}%`
|
|
554
502
|
} as React.CSSProperties;
|
|
@@ -576,11 +524,12 @@ export function DiffSurface(props: IDiffSurfaceProps): React.ReactElement {
|
|
|
576
524
|
disableWorkerPool={true}
|
|
577
525
|
options={{
|
|
578
526
|
diffStyle,
|
|
527
|
+
overflow: lineWrap ? 'wrap' : 'scroll',
|
|
579
528
|
disableFileHeader: true,
|
|
580
529
|
theme: resolveDiffTheme(dark, pierreTheme),
|
|
581
530
|
themeType: dark ? 'dark' : 'light',
|
|
582
531
|
// Constant string lets the library skip its unsafeCSS re-render path.
|
|
583
|
-
unsafeCSS:
|
|
532
|
+
unsafeCSS: DIFF_SURFACE_CSS,
|
|
584
533
|
...(onLineAsk !== undefined
|
|
585
534
|
? {
|
|
586
535
|
enableLineSelection: true,
|
|
@@ -592,7 +541,7 @@ export function DiffSurface(props: IDiffSurfaceProps): React.ReactElement {
|
|
|
592
541
|
/>
|
|
593
542
|
) : null}
|
|
594
543
|
</div>
|
|
595
|
-
{
|
|
544
|
+
{showResizer ? (
|
|
596
545
|
<div
|
|
597
546
|
className="jp-xtralab-DiffWidget-resizer"
|
|
598
547
|
style={{ left: `${leftPercent}%` }}
|
|
@@ -661,54 +610,41 @@ export function NotebookViewModeControl(props: {
|
|
|
661
610
|
}
|
|
662
611
|
|
|
663
612
|
/**
|
|
664
|
-
* Split
|
|
665
|
-
*
|
|
666
|
-
* `currentColor` lets the button styling drive the fill.
|
|
667
|
-
*/
|
|
668
|
-
function DiffSplitIcon(): React.ReactElement {
|
|
669
|
-
return (
|
|
670
|
-
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
671
|
-
<path d="M14 0H8.5v16H14a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2m-1.5 6.5v1h1a.5.5 0 0 1 0 1h-1v1a.5.5 0 0 1-1 0v-1h-1a.5.5 0 0 1 0-1h1v-1a.5.5 0 0 1 1 0" />
|
|
672
|
-
<path
|
|
673
|
-
d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h5.5V0zm.5 7.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1"
|
|
674
|
-
opacity=".3"
|
|
675
|
-
/>
|
|
676
|
-
</svg>
|
|
677
|
-
);
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
function DiffUnifiedIcon(): React.ReactElement {
|
|
681
|
-
return (
|
|
682
|
-
<svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
683
|
-
<path
|
|
684
|
-
fillRule="evenodd"
|
|
685
|
-
d="M16 14a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V8.5h16zm-8-4a.5.5 0 0 0-.5.5v1h-1a.5.5 0 0 0 0 1h1v1a.5.5 0 0 0 1 0v-1h1a.5.5 0 0 0 0-1h-1v-1A.5.5 0 0 0 8 10"
|
|
686
|
-
clipRule="evenodd"
|
|
687
|
-
/>
|
|
688
|
-
<path
|
|
689
|
-
fillRule="evenodd"
|
|
690
|
-
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"
|
|
691
|
-
clipRule="evenodd"
|
|
692
|
-
opacity=".4"
|
|
693
|
-
/>
|
|
694
|
-
</svg>
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
/**
|
|
699
|
-
* Segmented Split/Unified selector; mirrors {@link NotebookViewModeControl}.
|
|
613
|
+
* Split/Unified selector: two segmented buttons, or with `control: 'toggle'`
|
|
614
|
+
* one button that shows the layout it switches to.
|
|
700
615
|
* `available` mirrors the surface's `onFileDiffActiveChange`.
|
|
701
616
|
*/
|
|
702
617
|
export function DiffStyleControl(props: {
|
|
703
618
|
diffStyle: DiffStyle;
|
|
619
|
+
control: DiffStyleControlMode;
|
|
704
620
|
available: boolean;
|
|
705
621
|
onChange: (style: DiffStyle) => void;
|
|
706
622
|
trans: TranslationBundle;
|
|
707
623
|
}): React.ReactElement {
|
|
708
|
-
const { diffStyle, available, onChange, trans } = props;
|
|
624
|
+
const { diffStyle, control, available, onChange, trans } = props;
|
|
709
625
|
if (!available) {
|
|
710
626
|
return <></>;
|
|
711
627
|
}
|
|
628
|
+
if (control === 'toggle') {
|
|
629
|
+
const next: DiffStyle = diffStyle === 'split' ? 'unified' : 'split';
|
|
630
|
+
const label =
|
|
631
|
+
next === 'split'
|
|
632
|
+
? trans.__('Switch to split view')
|
|
633
|
+
: trans.__('Switch to unified view');
|
|
634
|
+
return (
|
|
635
|
+
<div className="jp-xtralab-DiffWidget-segmented">
|
|
636
|
+
<button
|
|
637
|
+
type="button"
|
|
638
|
+
className="jp-xtralab-DiffWidget-segmentedButton jp-xtralab-DiffWidget-segmentedButton-icon"
|
|
639
|
+
title={label}
|
|
640
|
+
aria-label={label}
|
|
641
|
+
onClick={() => onChange(next)}
|
|
642
|
+
>
|
|
643
|
+
{next === 'split' ? <IconDiffSplit /> : <IconDiffUnified />}
|
|
644
|
+
</button>
|
|
645
|
+
</div>
|
|
646
|
+
);
|
|
647
|
+
}
|
|
712
648
|
return (
|
|
713
649
|
<div
|
|
714
650
|
className="jp-xtralab-DiffWidget-segmented"
|
|
@@ -725,7 +661,7 @@ export function DiffStyleControl(props: {
|
|
|
725
661
|
aria-label={trans.__('Split view')}
|
|
726
662
|
onClick={() => onChange('split')}
|
|
727
663
|
>
|
|
728
|
-
<
|
|
664
|
+
<IconDiffSplit />
|
|
729
665
|
</button>
|
|
730
666
|
<button
|
|
731
667
|
type="button"
|
|
@@ -737,7 +673,38 @@ export function DiffStyleControl(props: {
|
|
|
737
673
|
aria-label={trans.__('Unified view')}
|
|
738
674
|
onClick={() => onChange('unified')}
|
|
739
675
|
>
|
|
740
|
-
<
|
|
676
|
+
<IconDiffUnified />
|
|
677
|
+
</button>
|
|
678
|
+
</div>
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Line-wrap toggle styled as a one-button segmented control.
|
|
684
|
+
* `available` mirrors the surface's `onFileDiffActiveChange`.
|
|
685
|
+
*/
|
|
686
|
+
export function LineWrapControl(props: {
|
|
687
|
+
lineWrap: boolean;
|
|
688
|
+
available: boolean;
|
|
689
|
+
onChange: (wrap: boolean) => void;
|
|
690
|
+
trans: TranslationBundle;
|
|
691
|
+
}): React.ReactElement {
|
|
692
|
+
const { lineWrap, available, onChange, trans } = props;
|
|
693
|
+
if (!available) {
|
|
694
|
+
return <></>;
|
|
695
|
+
}
|
|
696
|
+
return (
|
|
697
|
+
<div className="jp-xtralab-DiffWidget-segmented">
|
|
698
|
+
<button
|
|
699
|
+
type="button"
|
|
700
|
+
aria-pressed={lineWrap}
|
|
701
|
+
data-active={lineWrap}
|
|
702
|
+
className="jp-xtralab-DiffWidget-segmentedButton jp-xtralab-DiffWidget-segmentedButton-icon"
|
|
703
|
+
title={trans.__('Wrap long lines')}
|
|
704
|
+
aria-label={trans.__('Wrap lines')}
|
|
705
|
+
onClick={() => onChange(!lineWrap)}
|
|
706
|
+
>
|
|
707
|
+
<IconWordWrap />
|
|
741
708
|
</button>
|
|
742
709
|
</div>
|
|
743
710
|
);
|
package/src/git/diffTheme.ts
CHANGED
|
@@ -26,6 +26,30 @@ registerCustomCSSVariableTheme(JUPYTERLAB_DIFF_THEME, {
|
|
|
26
26
|
'token-link': 'var(--jp-mirror-editor-link-color)'
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* `unsafeCSS` for a taller, always-visible horizontal scrollbar. The library
|
|
31
|
+
* default is 6px and only paints the thumb on hover, in a color close to the
|
|
32
|
+
* background, so users miss that long lines scroll. Same thumb color as the
|
|
33
|
+
* app scrollbars in chrome.css.
|
|
34
|
+
*/
|
|
35
|
+
export const DIFF_SCROLLBAR_CSS = `:host {
|
|
36
|
+
--diffs-scrollbar-gutter-override: 10px;
|
|
37
|
+
--xtralab-diff-scrollbar-thumb: color-mix(in srgb, var(--jp-ui-font-color1) 28%, transparent);
|
|
38
|
+
}
|
|
39
|
+
[data-code]::-webkit-scrollbar-thumb {
|
|
40
|
+
background-color: var(--xtralab-diff-scrollbar-thumb);
|
|
41
|
+
border-width: 2px;
|
|
42
|
+
border-radius: 999px;
|
|
43
|
+
}
|
|
44
|
+
[data-code]::-webkit-scrollbar-thumb:hover {
|
|
45
|
+
background-color: color-mix(in srgb, var(--jp-ui-font-color1) 45%, transparent);
|
|
46
|
+
}
|
|
47
|
+
@supports (-moz-appearance: none) {
|
|
48
|
+
[data-code] {
|
|
49
|
+
scrollbar-color: var(--xtralab-diff-scrollbar-thumb) transparent;
|
|
50
|
+
}
|
|
51
|
+
}`;
|
|
52
|
+
|
|
29
53
|
/**
|
|
30
54
|
* Choose Pierre's palette or the JupyterLab CSS-variable theme.
|
|
31
55
|
*/
|