@jupyterlab/notebook 4.7.0-alpha.1 → 4.7.0-alpha.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/lib/actions.js +37 -5
- package/lib/actions.js.map +1 -1
- package/lib/executionindicator.d.ts +9 -0
- package/lib/executionindicator.js +36 -7
- package/lib/executionindicator.js.map +1 -1
- package/lib/notebooklspadapter.js +34 -9
- package/lib/notebooklspadapter.js.map +1 -1
- package/lib/panel.js +5 -2
- package/lib/panel.js.map +1 -1
- package/lib/searchprovider.d.ts +1 -0
- package/lib/searchprovider.js +19 -12
- package/lib/searchprovider.js.map +1 -1
- package/lib/testutils.js +9 -2
- package/lib/testutils.js.map +1 -1
- package/lib/widget.d.ts +34 -2
- package/lib/widget.js +148 -18
- package/lib/widget.js.map +1 -1
- package/package.json +21 -21
- package/src/actions.tsx +45 -5
- package/src/executionindicator.tsx +42 -9
- package/src/notebooklspadapter.ts +34 -9
- package/src/panel.ts +8 -2
- package/src/searchprovider.ts +21 -13
- package/src/testutils.ts +9 -1
- package/src/widget.ts +160 -20
- package/style/base.css +36 -0
package/lib/widget.js
CHANGED
|
@@ -301,6 +301,12 @@ export class StaticNotebook extends WindowedList {
|
|
|
301
301
|
this._contentVisibilityObserver = null;
|
|
302
302
|
}
|
|
303
303
|
super.dispose();
|
|
304
|
+
// Dispose cells that windowing modes may have detached from the layout.
|
|
305
|
+
for (const cell of this.cellsArray) {
|
|
306
|
+
cell.dispose();
|
|
307
|
+
}
|
|
308
|
+
this.cellsArray.length = 0;
|
|
309
|
+
this.viewModel.dispose();
|
|
304
310
|
}
|
|
305
311
|
onBeforeDetach(msg) {
|
|
306
312
|
// Disconnect the content-visibility observer
|
|
@@ -510,7 +516,7 @@ export class StaticNotebook extends WindowedList {
|
|
|
510
516
|
for (const cell of cells) {
|
|
511
517
|
this._insertCell(++index, cell);
|
|
512
518
|
}
|
|
513
|
-
this.
|
|
519
|
+
this._syncCellTrust();
|
|
514
520
|
newValue.cells.changed.connect(this._onCellsChanged, this);
|
|
515
521
|
newValue.metadataChanged.connect(this.onMetadataChanged, this);
|
|
516
522
|
newValue.contentChanged.connect(this.onModelContentChanged, this);
|
|
@@ -562,7 +568,7 @@ export class StaticNotebook extends WindowedList {
|
|
|
562
568
|
if (!this.model.sharedModel.cells.length) {
|
|
563
569
|
this.addHeader();
|
|
564
570
|
}
|
|
565
|
-
this.
|
|
571
|
+
this._syncCellTrust();
|
|
566
572
|
this.update();
|
|
567
573
|
}
|
|
568
574
|
/**
|
|
@@ -588,7 +594,7 @@ export class StaticNotebook extends WindowedList {
|
|
|
588
594
|
widget.inViewportChanged.connect(this._onCellInViewportChanged, this);
|
|
589
595
|
widget.addClass(NB_CELL_CLASS);
|
|
590
596
|
ArrayExt.insert(this.cellsArray, index, widget);
|
|
591
|
-
this.
|
|
597
|
+
this._syncCellTrust(widget);
|
|
592
598
|
this.onCellInserted(index, widget);
|
|
593
599
|
this._scheduleCellRenderOnIdle();
|
|
594
600
|
}
|
|
@@ -692,8 +698,8 @@ export class StaticNotebook extends WindowedList {
|
|
|
692
698
|
this.onCellRemoved(index, widget);
|
|
693
699
|
widget.dispose();
|
|
694
700
|
}
|
|
695
|
-
|
|
696
|
-
// Note: this returns false in a notebook without
|
|
701
|
+
_shouldTrustCell() {
|
|
702
|
+
// Note: this returns false in a notebook without trusted code cells;
|
|
697
703
|
// This is intended since only Code cells carry trust status on disk.
|
|
698
704
|
if (!this._notebookModel) {
|
|
699
705
|
return false;
|
|
@@ -709,12 +715,18 @@ export class StaticNotebook extends WindowedList {
|
|
|
709
715
|
}
|
|
710
716
|
return hasCodeCell;
|
|
711
717
|
}
|
|
712
|
-
|
|
713
|
-
const trusted = this.
|
|
718
|
+
_syncCellTrust(cell) {
|
|
719
|
+
const trusted = this._shouldTrustCell();
|
|
714
720
|
const trustHandler = this.rendermime.trustHandler;
|
|
715
721
|
if (!trustHandler) {
|
|
716
722
|
return;
|
|
717
723
|
}
|
|
724
|
+
if (trusted) {
|
|
725
|
+
trustHandler.markTrusted(this.node);
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
trustHandler.unmarkTrusted(this.node);
|
|
729
|
+
}
|
|
718
730
|
const cells = cell ? [cell] : this.widgets;
|
|
719
731
|
for (const widget of cells) {
|
|
720
732
|
if (!(widget instanceof MarkdownCell)) {
|
|
@@ -730,7 +742,7 @@ export class StaticNotebook extends WindowedList {
|
|
|
730
742
|
}
|
|
731
743
|
_onCellStateChanged(model, args) {
|
|
732
744
|
if (args.name === 'trusted') {
|
|
733
|
-
this.
|
|
745
|
+
this._syncCellTrust();
|
|
734
746
|
}
|
|
735
747
|
}
|
|
736
748
|
/**
|
|
@@ -827,7 +839,7 @@ export class StaticNotebook extends WindowedList {
|
|
|
827
839
|
}
|
|
828
840
|
_scheduleCellRenderOnIdle() {
|
|
829
841
|
if (this.notebookConfig.windowingMode !== 'none' && !this.isDisposed) {
|
|
830
|
-
if (
|
|
842
|
+
if (this._idleCallBack === null) {
|
|
831
843
|
this._idleCallBack = requestIdleCallback((deadline) => {
|
|
832
844
|
this._idleCallBack = null;
|
|
833
845
|
// In case of timeout, render for some time even if it means freezing the UI
|
|
@@ -908,7 +920,7 @@ export class StaticNotebook extends WindowedList {
|
|
|
908
920
|
}
|
|
909
921
|
}
|
|
910
922
|
else {
|
|
911
|
-
if (this._idleCallBack) {
|
|
923
|
+
if (this._idleCallBack !== null) {
|
|
912
924
|
window.cancelIdleCallback(this._idleCallBack);
|
|
913
925
|
this._idleCallBack = null;
|
|
914
926
|
}
|
|
@@ -1012,7 +1024,7 @@ export class StaticNotebook extends WindowedList {
|
|
|
1012
1024
|
this._contentVisibilityObserver.observe(cell.node);
|
|
1013
1025
|
});
|
|
1014
1026
|
});
|
|
1015
|
-
});
|
|
1027
|
+
}, this);
|
|
1016
1028
|
}
|
|
1017
1029
|
}
|
|
1018
1030
|
/**
|
|
@@ -1190,7 +1202,7 @@ class ScrollbarItem {
|
|
|
1190
1202
|
else if (model.executionState == 'running') {
|
|
1191
1203
|
content = '[*]';
|
|
1192
1204
|
}
|
|
1193
|
-
else if (model.executionCount) {
|
|
1205
|
+
else if (model.executionCount !== null) {
|
|
1194
1206
|
content = `[${model.executionCount}]`;
|
|
1195
1207
|
}
|
|
1196
1208
|
else {
|
|
@@ -2348,6 +2360,67 @@ export class Notebook extends StaticNotebook {
|
|
|
2348
2360
|
});
|
|
2349
2361
|
}
|
|
2350
2362
|
}
|
|
2363
|
+
/**
|
|
2364
|
+
* Find the cell containing a node, traversing open shadow roots.
|
|
2365
|
+
*
|
|
2366
|
+
* Returned cell is `null` if the node is not in a cell of this notebook.
|
|
2367
|
+
*/
|
|
2368
|
+
_cellContaining(node) {
|
|
2369
|
+
let current = node;
|
|
2370
|
+
while (current) {
|
|
2371
|
+
const element = current instanceof Element ? current : current.parentElement;
|
|
2372
|
+
if (element) {
|
|
2373
|
+
const index = this._findCell(element);
|
|
2374
|
+
if (index !== -1) {
|
|
2375
|
+
return this.widgets[index];
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
const root = (element !== null && element !== void 0 ? element : current).getRootNode();
|
|
2379
|
+
current = root instanceof ShadowRoot ? root.host : null;
|
|
2380
|
+
}
|
|
2381
|
+
return null;
|
|
2382
|
+
}
|
|
2383
|
+
/**
|
|
2384
|
+
* Whether a shift-click on `target` should be left to the browser to handle
|
|
2385
|
+
* as an extension of the current text selection, rather than be turned into
|
|
2386
|
+
* a cell range selection.
|
|
2387
|
+
*
|
|
2388
|
+
* This is the case when the click lands in a region of a cell whose text the
|
|
2389
|
+
* browser selects - the output area of a code cell, or the rendered input of
|
|
2390
|
+
* a rendered markdown cell - and the current selection starts in such a
|
|
2391
|
+
* region too; see https://github.com/jupyterlab/jupyterlab/issues/4800. A
|
|
2392
|
+
* click on a cell prompt, on cell chrome, or on a cell without such a region
|
|
2393
|
+
* (an unrendered markdown cell, a code cell without outputs) always selects
|
|
2394
|
+
* cells.
|
|
2395
|
+
*
|
|
2396
|
+
* #### Notes
|
|
2397
|
+
* Only the anchor of the selection is considered, because a shift-click
|
|
2398
|
+
* moves the focus and leaves the anchor in place. The anchor may sit in a
|
|
2399
|
+
* different cell than the one clicked, which is what allows a selection to
|
|
2400
|
+
* be extended across the outputs of several cells; and disregarding the
|
|
2401
|
+
* focus keeps this working when a drag overshot the edge of an output and
|
|
2402
|
+
* left the focus outside of it.
|
|
2403
|
+
*
|
|
2404
|
+
* This trade-off between extending the text selection and extending the cell
|
|
2405
|
+
* selection can be adjusted in the future. For now the text selection wins to
|
|
2406
|
+
* preserve the behaviour from before refactor that users may be accustomed to.
|
|
2407
|
+
*/
|
|
2408
|
+
_isExtendingTextSelection(selection, targetCell, target) {
|
|
2409
|
+
if (!selection || selection.isCollapsed) {
|
|
2410
|
+
return false;
|
|
2411
|
+
}
|
|
2412
|
+
if (!Private.isInTextRegion(targetCell, target)) {
|
|
2413
|
+
return false;
|
|
2414
|
+
}
|
|
2415
|
+
const { anchorNode } = selection;
|
|
2416
|
+
// The selection usually starts in the very cell which was clicked, which
|
|
2417
|
+
// can be checked without having to locate the cell of the anchor.
|
|
2418
|
+
if (Private.isInTextRegion(targetCell, anchorNode)) {
|
|
2419
|
+
return true;
|
|
2420
|
+
}
|
|
2421
|
+
const anchorCell = this._cellContaining(anchorNode);
|
|
2422
|
+
return !!anchorCell && Private.isInTextRegion(anchorCell, anchorNode);
|
|
2423
|
+
}
|
|
2351
2424
|
/**
|
|
2352
2425
|
* Find the cell index containing the target html element.
|
|
2353
2426
|
*
|
|
@@ -2552,15 +2625,18 @@ export class Notebook extends StaticNotebook {
|
|
|
2552
2625
|
this.deselectAll();
|
|
2553
2626
|
}
|
|
2554
2627
|
else if (targetArea === 'prompt' || targetArea === 'cell') {
|
|
2555
|
-
// We don't want to prevent the default selection behavior
|
|
2556
|
-
// if there is currently text selected in an output.
|
|
2557
|
-
const hasSelection = ((_c = window.getSelection()) !== null && _c !== void 0 ? _c : '').toString() !== '';
|
|
2558
2628
|
if (button === 0 &&
|
|
2559
2629
|
shiftKey &&
|
|
2560
|
-
!
|
|
2561
|
-
|
|
2630
|
+
!['INPUT', 'OPTION'].includes(target.tagName) &&
|
|
2631
|
+
// We don't want to prevent the default selection behavior when the
|
|
2632
|
+
// user is extending a text selection into the clicked region.
|
|
2633
|
+
!this._isExtendingTextSelection(window.getSelection(), widget, target)) {
|
|
2562
2634
|
// Prevent browser selecting text in prompt or output
|
|
2563
2635
|
event.preventDefault();
|
|
2636
|
+
// Preventing the default also stops the browser from collapsing any
|
|
2637
|
+
// text selection which is already there; drop it explicitly so that
|
|
2638
|
+
// stale highlighted text is not left over the new cell selection.
|
|
2639
|
+
(_c = window.getSelection()) === null || _c === void 0 ? void 0 : _c.removeAllRanges();
|
|
2564
2640
|
// Shift-click - extend selection
|
|
2565
2641
|
try {
|
|
2566
2642
|
this.extendContiguousSelectionTo(index);
|
|
@@ -2841,7 +2917,7 @@ export class Notebook extends StaticNotebook {
|
|
|
2841
2917
|
const executionCount = activeCell.model
|
|
2842
2918
|
.executionCount;
|
|
2843
2919
|
countString = ' ';
|
|
2844
|
-
if (executionCount) {
|
|
2920
|
+
if (executionCount !== null) {
|
|
2845
2921
|
countString = executionCount.toString();
|
|
2846
2922
|
}
|
|
2847
2923
|
}
|
|
@@ -3101,6 +3177,60 @@ var Private;
|
|
|
3101
3177
|
}
|
|
3102
3178
|
}
|
|
3103
3179
|
Private.NotebookPanelLayout = NotebookPanelLayout;
|
|
3180
|
+
/**
|
|
3181
|
+
* Check whether `node` is `parent` or a descendant of it.
|
|
3182
|
+
*
|
|
3183
|
+
* Unlike `Node.contains()` this traverses out of open shadow roots, so a
|
|
3184
|
+
* node rendered by a widget library which attaches a shadow root (such as
|
|
3185
|
+
* Panel or Bokeh) is still recognised as belonging to its host subtree.
|
|
3186
|
+
*/
|
|
3187
|
+
function containsDeep(parent, node) {
|
|
3188
|
+
let current = node;
|
|
3189
|
+
while (current) {
|
|
3190
|
+
if (parent.contains(current)) {
|
|
3191
|
+
return true;
|
|
3192
|
+
}
|
|
3193
|
+
const root = current.getRootNode();
|
|
3194
|
+
current = root instanceof ShadowRoot ? root.host : null;
|
|
3195
|
+
}
|
|
3196
|
+
return false;
|
|
3197
|
+
}
|
|
3198
|
+
/**
|
|
3199
|
+
* Whether a cell exposes an output area.
|
|
3200
|
+
*
|
|
3201
|
+
* #### Notes
|
|
3202
|
+
* This is a structural check rather than `instanceof CodeCell` so that it
|
|
3203
|
+
* still holds when a federated extension loads its own copy of
|
|
3204
|
+
* `@jupyterlab/cells`.
|
|
3205
|
+
*/
|
|
3206
|
+
function hasOutputArea(cell) {
|
|
3207
|
+
return 'outputArea' in cell;
|
|
3208
|
+
}
|
|
3209
|
+
/**
|
|
3210
|
+
* The regions of a cell whose text the browser, rather than the notebook,
|
|
3211
|
+
* is responsible for selecting: the output area of a code cell and the
|
|
3212
|
+
* rendered input of a cell which renders it, such as a rendered markdown
|
|
3213
|
+
* cell.
|
|
3214
|
+
*/
|
|
3215
|
+
function textRegionsOf(cell) {
|
|
3216
|
+
var _a;
|
|
3217
|
+
const regions = [];
|
|
3218
|
+
if (hasOutputArea(cell)) {
|
|
3219
|
+
regions.push(cell.outputArea.node);
|
|
3220
|
+
}
|
|
3221
|
+
const rendered = (_a = cell.inputArea) === null || _a === void 0 ? void 0 : _a.renderedInput;
|
|
3222
|
+
if (rendered) {
|
|
3223
|
+
regions.push(rendered.node);
|
|
3224
|
+
}
|
|
3225
|
+
return regions;
|
|
3226
|
+
}
|
|
3227
|
+
/**
|
|
3228
|
+
* Whether a node is in a region of a cell whose text the browser selects.
|
|
3229
|
+
*/
|
|
3230
|
+
function isInTextRegion(cell, node) {
|
|
3231
|
+
return textRegionsOf(cell).some(region => containsDeep(region, node));
|
|
3232
|
+
}
|
|
3233
|
+
Private.isInTextRegion = isInTextRegion;
|
|
3104
3234
|
/**
|
|
3105
3235
|
* Create a cell drag image.
|
|
3106
3236
|
*/
|