@jupyterlab/notebook 4.6.0 → 4.6.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.d.ts +16 -0
- package/lib/actions.js +162 -41
- package/lib/actions.js.map +1 -1
- package/lib/toc.js +13 -8
- package/lib/toc.js.map +1 -1
- package/lib/widget.js +6 -2
- package/lib/widget.js.map +1 -1
- package/package.json +20 -20
- package/src/actions.tsx +208 -46
- package/src/toc.ts +17 -7
- package/src/widget.ts +6 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupyterlab/notebook",
|
|
3
|
-
"version": "4.6.
|
|
3
|
+
"version": "4.6.2",
|
|
4
4
|
"description": "JupyterLab - Notebook",
|
|
5
5
|
"homepage": "https://github.com/jupyterlab/jupyterlab",
|
|
6
6
|
"bugs": {
|
|
@@ -41,24 +41,24 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@jupyter/ydoc": "^4.0.0",
|
|
44
|
-
"@jupyterlab/apputils": "^4.7.
|
|
45
|
-
"@jupyterlab/cells": "^4.6.
|
|
46
|
-
"@jupyterlab/codeeditor": "^4.6.
|
|
47
|
-
"@jupyterlab/codemirror": "^4.6.
|
|
48
|
-
"@jupyterlab/coreutils": "^6.6.
|
|
49
|
-
"@jupyterlab/docregistry": "^4.6.
|
|
50
|
-
"@jupyterlab/documentsearch": "^4.6.
|
|
51
|
-
"@jupyterlab/lsp": "^4.6.
|
|
52
|
-
"@jupyterlab/markedparser-extension": "^4.6.
|
|
53
|
-
"@jupyterlab/nbformat": "^4.6.
|
|
54
|
-
"@jupyterlab/observables": "^5.6.
|
|
55
|
-
"@jupyterlab/rendermime": "^4.6.
|
|
56
|
-
"@jupyterlab/services": "^7.6.
|
|
57
|
-
"@jupyterlab/settingregistry": "^4.6.
|
|
58
|
-
"@jupyterlab/statusbar": "^4.6.
|
|
59
|
-
"@jupyterlab/toc": "^6.6.
|
|
60
|
-
"@jupyterlab/translation": "^4.6.
|
|
61
|
-
"@jupyterlab/ui-components": "^4.6.
|
|
44
|
+
"@jupyterlab/apputils": "^4.7.2",
|
|
45
|
+
"@jupyterlab/cells": "^4.6.2",
|
|
46
|
+
"@jupyterlab/codeeditor": "^4.6.2",
|
|
47
|
+
"@jupyterlab/codemirror": "^4.6.2",
|
|
48
|
+
"@jupyterlab/coreutils": "^6.6.2",
|
|
49
|
+
"@jupyterlab/docregistry": "^4.6.2",
|
|
50
|
+
"@jupyterlab/documentsearch": "^4.6.2",
|
|
51
|
+
"@jupyterlab/lsp": "^4.6.2",
|
|
52
|
+
"@jupyterlab/markedparser-extension": "^4.6.2",
|
|
53
|
+
"@jupyterlab/nbformat": "^4.6.2",
|
|
54
|
+
"@jupyterlab/observables": "^5.6.2",
|
|
55
|
+
"@jupyterlab/rendermime": "^4.6.2",
|
|
56
|
+
"@jupyterlab/services": "^7.6.2",
|
|
57
|
+
"@jupyterlab/settingregistry": "^4.6.2",
|
|
58
|
+
"@jupyterlab/statusbar": "^4.6.2",
|
|
59
|
+
"@jupyterlab/toc": "^6.6.2",
|
|
60
|
+
"@jupyterlab/translation": "^4.6.2",
|
|
61
|
+
"@jupyterlab/ui-components": "^4.6.2",
|
|
62
62
|
"@lumino/algorithm": "^2.0.4",
|
|
63
63
|
"@lumino/commands": "^2.3.3",
|
|
64
64
|
"@lumino/coreutils": "^2.2.2",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"react": "^18.2.0"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@jupyterlab/testing": "^4.6.
|
|
77
|
+
"@jupyterlab/testing": "^4.6.2",
|
|
78
78
|
"@types/jest": "^29.2.0",
|
|
79
79
|
"jest": "^29.2.0",
|
|
80
80
|
"rimraf": "~5.0.5",
|
package/src/actions.tsx
CHANGED
|
@@ -603,15 +603,77 @@ export namespace NotebookActions {
|
|
|
603
603
|
lastIndex = notebook.model.cells.length;
|
|
604
604
|
}
|
|
605
605
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
} else {
|
|
609
|
-
notebook.moveCell(firstIndex, firstIndex + shift, lastIndex - firstIndex);
|
|
610
|
-
}
|
|
606
|
+
const toIndex = shift > 0 ? lastIndex : firstIndex + shift;
|
|
607
|
+
moveCells(notebook, firstIndex, toIndex, lastIndex - firstIndex);
|
|
611
608
|
|
|
612
609
|
void Private.handleState(notebook, state, true);
|
|
613
610
|
}
|
|
614
611
|
|
|
612
|
+
/**
|
|
613
|
+
* Move cells while preserving in-flight kernel futures.
|
|
614
|
+
*
|
|
615
|
+
* The underlying `jupyter-ydoc` `moveCells` implementation currently
|
|
616
|
+
* serializes cells to JSON and recreates them via a delete + insert
|
|
617
|
+
* transaction, which disposes any active kernel futures attached to the
|
|
618
|
+
* old widgets. This wrapper detaches futures before the move and
|
|
619
|
+
* reattaches them to the new widgets afterwards, and stores them in the
|
|
620
|
+
* undo stack so that undoing the move also restores execution state.
|
|
621
|
+
*
|
|
622
|
+
* @param notebook - The target notebook.
|
|
623
|
+
* @param from - Index of the first cell to move.
|
|
624
|
+
* @param to - Target index (as passed to `notebook.moveCell`).
|
|
625
|
+
* @param n - Number of cells to move.
|
|
626
|
+
*/
|
|
627
|
+
export function moveCells(
|
|
628
|
+
notebook: Notebook,
|
|
629
|
+
from: number,
|
|
630
|
+
to: number,
|
|
631
|
+
n = 1
|
|
632
|
+
): void {
|
|
633
|
+
if (!notebook.model) {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// Mirror `Notebook.moveCell`'s bounding/no-op logic so that we do not
|
|
638
|
+
// capture futures or touch the undo stack when the move won't happen.
|
|
639
|
+
// Otherwise we could attach execution metadata to an unrelated previous
|
|
640
|
+
// undo item, corrupting subsequent undo behavior.
|
|
641
|
+
const boundedTo = Math.min(
|
|
642
|
+
notebook.model.cells.length - 1,
|
|
643
|
+
Math.max(0, to)
|
|
644
|
+
);
|
|
645
|
+
if (boundedTo === from) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// moveCells serializes cells to JSON and recreates widgets (delete+insert),
|
|
650
|
+
// which would dispose any in-flight futures. Capture them first.
|
|
651
|
+
const storedExecutions: Private.IStoredCellExecution[] = [];
|
|
652
|
+
notebook.widgets.slice(from, from + n).forEach(child => {
|
|
653
|
+
if (!(child instanceof CodeCell)) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
const stored = Private.captureExecution(child);
|
|
657
|
+
if (stored) {
|
|
658
|
+
storedExecutions.push(stored);
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
notebook.moveCell(from, to, n);
|
|
663
|
+
|
|
664
|
+
// Immediately reconnect futures to the newly created widgets.
|
|
665
|
+
for (const stored of storedExecutions) {
|
|
666
|
+
Private.restoreExecution(notebook, stored);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Store in the undo stack so that undoing the move can also restore state.
|
|
670
|
+
if (storedExecutions.length > 0) {
|
|
671
|
+
const undoManager = (notebook.model.sharedModel as YNotebook).undoManager;
|
|
672
|
+
const lastItem = undoManager.undoStack[undoManager.undoStack.length - 1];
|
|
673
|
+
lastItem?.meta.set(Private.CELL_EXECUTION_META_KEY, storedExecutions);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
615
677
|
/**
|
|
616
678
|
* Move the selected cell(s) down.
|
|
617
679
|
*
|
|
@@ -1609,9 +1671,37 @@ export namespace NotebookActions {
|
|
|
1609
1671
|
const state = Private.getState(notebook);
|
|
1610
1672
|
notebook.mode = 'command';
|
|
1611
1673
|
|
|
1674
|
+
const undoManager = (notebook.model.sharedModel as YNotebook).undoManager;
|
|
1675
|
+
|
|
1676
|
+
// For cells that will be MOVED by the undo (i.e. they still exist in the
|
|
1677
|
+
// notebook at their current position), pre-capture their futures now before
|
|
1678
|
+
// sharedModel.undo() destroys those widgets. This prevents OutputArea.dispose()
|
|
1679
|
+
// from cancelling the future during the Y.js delete+insert that implements the move.
|
|
1680
|
+
const topItem = undoManager.undoStack[undoManager.undoStack.length - 1];
|
|
1681
|
+
const pendingExecutions = topItem?.meta.get(
|
|
1682
|
+
Private.CELL_EXECUTION_META_KEY
|
|
1683
|
+
) as Private.IStoredCellExecution[] | undefined;
|
|
1684
|
+
const preCaptured = new Map<string, Private.IStoredCellExecution>();
|
|
1685
|
+
pendingExecutions?.forEach(stored => {
|
|
1686
|
+
const cell = notebook.widgets.find(w => w.model.id === stored.cellId);
|
|
1687
|
+
if (!(cell instanceof CodeCell)) {
|
|
1688
|
+
return; // cell was deleted (not moved) — handled via stored future below
|
|
1689
|
+
}
|
|
1690
|
+
// Cell still present → move undo. Fresh capture protects the future.
|
|
1691
|
+
const fresh = Private.captureExecution(cell) ?? {
|
|
1692
|
+
...stored,
|
|
1693
|
+
isDone: () => true,
|
|
1694
|
+
buffered: []
|
|
1695
|
+
};
|
|
1696
|
+
// The undo will roll the outputs back to their state at the time of
|
|
1697
|
+
// the move; snapshot the current outputs so anything received since
|
|
1698
|
+
// then can be re-applied after the undo.
|
|
1699
|
+
fresh.outputs = cell.model.outputs.toJSON();
|
|
1700
|
+
preCaptured.set(stored.cellId, fresh);
|
|
1701
|
+
});
|
|
1702
|
+
|
|
1612
1703
|
// Capture execution context from the stack item being popped.
|
|
1613
1704
|
let storedExecutions: Private.IStoredCellExecution[] | undefined;
|
|
1614
|
-
const undoManager = (notebook.model.sharedModel as YNotebook).undoManager;
|
|
1615
1705
|
const onStackItemPopped = ({
|
|
1616
1706
|
stackItem
|
|
1617
1707
|
}: {
|
|
@@ -1625,40 +1715,13 @@ export namespace NotebookActions {
|
|
|
1625
1715
|
notebook.model.sharedModel.undo();
|
|
1626
1716
|
undoManager.off('stack-item-popped', onStackItemPopped);
|
|
1627
1717
|
|
|
1628
|
-
// Restore execution state on resurrected cell widgets.
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
// Execution finished or was interrupted before undo - ensure state is idle.
|
|
1636
|
-
cell.model.executionState = 'idle';
|
|
1637
|
-
return;
|
|
1638
|
-
}
|
|
1639
|
-
// Still running - reconnect the future so the cell receives remaining
|
|
1640
|
-
// output and stdin requests (e.g. input()), and tracks the idle transition.
|
|
1641
|
-
cell.outputArea.reattachFuture(future);
|
|
1642
|
-
// Replay any IOPub messages that arrived while the future was detached.
|
|
1643
|
-
// After reattachFuture, future.onIOPub routes to the new output area.
|
|
1644
|
-
for (const msg of buffered) {
|
|
1645
|
-
void future.onIOPub(msg);
|
|
1646
|
-
}
|
|
1647
|
-
// The original execute() call targeted the old cell widget, so it will
|
|
1648
|
-
// not update executionCount/executionState on this resurrected cell.
|
|
1649
|
-
// Drive the idle transition here instead.
|
|
1650
|
-
const cellRef = cell;
|
|
1651
|
-
void future.done.then(
|
|
1652
|
-
reply => {
|
|
1653
|
-
if (!cellRef.isDisposed) {
|
|
1654
|
-
cellRef.model.executionCount = reply.content.execution_count;
|
|
1655
|
-
}
|
|
1656
|
-
},
|
|
1657
|
-
() => {
|
|
1658
|
-
if (!cellRef.isDisposed) {
|
|
1659
|
-
cellRef.model.executionState = 'idle';
|
|
1660
|
-
}
|
|
1661
|
-
}
|
|
1718
|
+
// Restore execution state on resurrected/moved cell widgets.
|
|
1719
|
+
// For move-undo: use freshly pre-captured data (stored data is stale).
|
|
1720
|
+
// For delete-undo: stored data has the futures captured at deletion time.
|
|
1721
|
+
storedExecutions?.forEach(stored => {
|
|
1722
|
+
Private.restoreExecution(
|
|
1723
|
+
notebook,
|
|
1724
|
+
preCaptured.get(stored.cellId) ?? stored
|
|
1662
1725
|
);
|
|
1663
1726
|
});
|
|
1664
1727
|
|
|
@@ -2568,6 +2631,15 @@ namespace Private {
|
|
|
2568
2631
|
/** Key used to store cell execution state in Y.js undo stack item metadata. */
|
|
2569
2632
|
export const CELL_EXECUTION_META_KEY = Symbol('cellExecutionState');
|
|
2570
2633
|
|
|
2634
|
+
/**
|
|
2635
|
+
* A kernel message that arrived while the future was detached, tagged with
|
|
2636
|
+
* its channel so it can be dispatched to the right handler on replay.
|
|
2637
|
+
*/
|
|
2638
|
+
export type IBufferedMessage =
|
|
2639
|
+
| { channel: 'iopub'; msg: KernelMessage.IIOPubMessage }
|
|
2640
|
+
| { channel: 'stdin'; msg: KernelMessage.IStdinMessage }
|
|
2641
|
+
| { channel: 'reply'; msg: KernelMessage.IExecuteReplyMsg };
|
|
2642
|
+
|
|
2571
2643
|
export interface IStoredCellExecution {
|
|
2572
2644
|
cellId: string;
|
|
2573
2645
|
future: Kernel.IShellFuture<
|
|
@@ -2575,13 +2647,28 @@ namespace Private {
|
|
|
2575
2647
|
KernelMessage.IExecuteReplyMsg
|
|
2576
2648
|
>;
|
|
2577
2649
|
isDone: () => boolean;
|
|
2578
|
-
/**
|
|
2579
|
-
|
|
2650
|
+
/**
|
|
2651
|
+
* Kernel messages (IOPub, stdin and reply) that arrived while the future
|
|
2652
|
+
* was detached, in arrival order, so they can be replayed on reattach.
|
|
2653
|
+
*/
|
|
2654
|
+
buffered: IBufferedMessage[];
|
|
2655
|
+
/**
|
|
2656
|
+
* Snapshot of the cell outputs to restore after an undo.
|
|
2657
|
+
*
|
|
2658
|
+
* The Y.js undo of a move (a delete + insert transaction) resurrects the
|
|
2659
|
+
* cell as it was when the move happened, rolling back any output received
|
|
2660
|
+
* since. Re-applying this snapshot after the undo prevents that loss.
|
|
2661
|
+
*/
|
|
2662
|
+
outputs?: nbformat.IOutput[];
|
|
2580
2663
|
}
|
|
2581
2664
|
|
|
2582
2665
|
/**
|
|
2583
|
-
* Detach the kernel future from a code cell, buffering any
|
|
2584
|
-
*
|
|
2666
|
+
* Detach the kernel future from a code cell, buffering any messages that
|
|
2667
|
+
* arrive while it is detached so they can be replayed on reattach.
|
|
2668
|
+
*
|
|
2669
|
+
* `detachFuture` clears the IOPub, stdin and reply handlers, so all three
|
|
2670
|
+
* channels are buffered here; otherwise a message arriving during the
|
|
2671
|
+
* detached window (e.g. an `input()` request on stdin) would be dropped.
|
|
2585
2672
|
*
|
|
2586
2673
|
* Returns null if the cell has no active future.
|
|
2587
2674
|
*/
|
|
@@ -2596,13 +2683,88 @@ namespace Private {
|
|
|
2596
2683
|
void future.done.finally(() => {
|
|
2597
2684
|
done = true;
|
|
2598
2685
|
});
|
|
2599
|
-
const buffered:
|
|
2686
|
+
const buffered: IBufferedMessage[] = [];
|
|
2600
2687
|
future.onIOPub = msg => {
|
|
2601
|
-
buffered.push(msg);
|
|
2688
|
+
buffered.push({ channel: 'iopub', msg });
|
|
2689
|
+
};
|
|
2690
|
+
future.onStdin = msg => {
|
|
2691
|
+
buffered.push({ channel: 'stdin', msg });
|
|
2692
|
+
};
|
|
2693
|
+
future.onReply = msg => {
|
|
2694
|
+
buffered.push({ channel: 'reply', msg });
|
|
2602
2695
|
};
|
|
2603
2696
|
return { cellId: cell.model.id, future, isDone: () => done, buffered };
|
|
2604
2697
|
}
|
|
2605
2698
|
|
|
2699
|
+
/**
|
|
2700
|
+
* Reconnect a captured execution to the cell widget that now holds the model.
|
|
2701
|
+
*
|
|
2702
|
+
* Handles both the "still running" and "already finished" cases.
|
|
2703
|
+
*/
|
|
2704
|
+
export function restoreExecution(
|
|
2705
|
+
notebook: Notebook,
|
|
2706
|
+
{ cellId, future, isDone, buffered, outputs }: IStoredCellExecution
|
|
2707
|
+
): void {
|
|
2708
|
+
const cell = notebook.widgets.find(w => w.model.id === cellId);
|
|
2709
|
+
if (!(cell instanceof CodeCell)) {
|
|
2710
|
+
return;
|
|
2711
|
+
}
|
|
2712
|
+
if (outputs && !JSONExt.deepEqual(outputs, cell.model.outputs.toJSON())) {
|
|
2713
|
+
// Re-apply the output snapshot taken just before the undo: the Y.js
|
|
2714
|
+
// undo rolled the outputs back to their state at the time of the
|
|
2715
|
+
// undone action. Going through the output area model keeps the
|
|
2716
|
+
// in-memory model and the shared model in sync (output changes are
|
|
2717
|
+
// not tracked by the undo manager, so this does not pollute history).
|
|
2718
|
+
cell.model.outputs.fromJSON(outputs);
|
|
2719
|
+
}
|
|
2720
|
+
// Reattach the future (without clearing existing outputs) and replay any
|
|
2721
|
+
// messages buffered while it was detached, in arrival order. This is done
|
|
2722
|
+
// whether or not the execution has already finished, so that final outputs
|
|
2723
|
+
// (or a pending stdin request) that arrived while detached are not lost
|
|
2724
|
+
// (e.g. if the kernel completed in the brief window during the undo).
|
|
2725
|
+
cell.outputArea.reattachFuture(future);
|
|
2726
|
+
for (const buf of buffered) {
|
|
2727
|
+
switch (buf.channel) {
|
|
2728
|
+
case 'iopub':
|
|
2729
|
+
void future.onIOPub(buf.msg);
|
|
2730
|
+
break;
|
|
2731
|
+
case 'stdin':
|
|
2732
|
+
void future.onStdin(buf.msg);
|
|
2733
|
+
break;
|
|
2734
|
+
case 'reply':
|
|
2735
|
+
void future.onReply(buf.msg);
|
|
2736
|
+
break;
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
if (isDone()) {
|
|
2740
|
+
// The execution already finished (e.g. it completed or was interrupted
|
|
2741
|
+
// during the undo). The resurrected cell may carry a stale 'running'
|
|
2742
|
+
// state from its restored snapshot, so reset it synchronously rather
|
|
2743
|
+
// than relying solely on the asynchronous `future.done` handler below.
|
|
2744
|
+
cell.model.executionState = 'idle';
|
|
2745
|
+
} else {
|
|
2746
|
+
// Restore the running state on the recreated cell widget.
|
|
2747
|
+
cell.model.executionState = 'running';
|
|
2748
|
+
}
|
|
2749
|
+
const cellRef = cell;
|
|
2750
|
+
void future.done.then(
|
|
2751
|
+
reply => {
|
|
2752
|
+
if (!cellRef.isDisposed) {
|
|
2753
|
+
// The future is authoritative for the prompt number; a snapshot
|
|
2754
|
+
// taken before completion would be stale (still null). Setting a
|
|
2755
|
+
// non-null execution count also flips the state back to 'idle'.
|
|
2756
|
+
cellRef.model.executionCount = reply.content.execution_count;
|
|
2757
|
+
cellRef.model.executionState = 'idle';
|
|
2758
|
+
}
|
|
2759
|
+
},
|
|
2760
|
+
() => {
|
|
2761
|
+
if (!cellRef.isDisposed) {
|
|
2762
|
+
cellRef.model.executionState = 'idle';
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
);
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2606
2768
|
/**
|
|
2607
2769
|
* Notebook cell executor
|
|
2608
2770
|
*/
|
package/src/toc.ts
CHANGED
|
@@ -244,8 +244,10 @@ export class NotebookToCModel extends TableOfContentsModel<
|
|
|
244
244
|
*/
|
|
245
245
|
protected async getHeadings(): Promise<INotebookHeading[] | null> {
|
|
246
246
|
const cells = this.widget.content.widgets;
|
|
247
|
+
const currentCells = new Set(cells);
|
|
247
248
|
const headings: INotebookHeading[] = [];
|
|
248
249
|
const documentLevels = new Array<number>();
|
|
250
|
+
const cellToHeadingIndex = new WeakMap<Cell, number>();
|
|
249
251
|
|
|
250
252
|
// Generate headings by iterating through all notebook cells...
|
|
251
253
|
for (let i = 0; i < cells.length; i++) {
|
|
@@ -306,12 +308,15 @@ export class NotebookToCModel extends TableOfContentsModel<
|
|
|
306
308
|
}
|
|
307
309
|
|
|
308
310
|
if (headings.length > 0) {
|
|
309
|
-
|
|
310
|
-
} else {
|
|
311
|
-
// If no headings were found, remove the cell from the map
|
|
312
|
-
this._cellToHeadingIndex.delete(cell);
|
|
311
|
+
cellToHeadingIndex.set(cell, headings.length - 1);
|
|
313
312
|
}
|
|
314
313
|
}
|
|
314
|
+
|
|
315
|
+
this._cellToHeadingIndex = cellToHeadingIndex;
|
|
316
|
+
this._runningCells = this._runningCells.filter(cell =>
|
|
317
|
+
currentCells.has(cell)
|
|
318
|
+
);
|
|
319
|
+
this._errorCells = this._errorCells.filter(cell => currentCells.has(cell));
|
|
315
320
|
this.updateRunningStatus(headings);
|
|
316
321
|
return Promise.resolve(headings);
|
|
317
322
|
}
|
|
@@ -329,7 +334,8 @@ export class NotebookToCModel extends TableOfContentsModel<
|
|
|
329
334
|
): boolean {
|
|
330
335
|
return (
|
|
331
336
|
super.isHeadingEqual(heading1, heading2) &&
|
|
332
|
-
heading1.cellRef === heading2.cellRef
|
|
337
|
+
heading1.cellRef === heading2.cellRef &&
|
|
338
|
+
heading1.isRunning === heading2.isRunning
|
|
333
339
|
);
|
|
334
340
|
}
|
|
335
341
|
|
|
@@ -463,11 +469,15 @@ export class NotebookToCModel extends TableOfContentsModel<
|
|
|
463
469
|
}
|
|
464
470
|
|
|
465
471
|
protected updateRunningStatus(headings: INotebookHeading[]): void {
|
|
472
|
+
headings.forEach(heading => {
|
|
473
|
+
heading.isRunning = RunningStatus.Idle;
|
|
474
|
+
});
|
|
475
|
+
|
|
466
476
|
// Update isRunning
|
|
467
477
|
this._runningCells.forEach((cell, index) => {
|
|
468
478
|
const headingIndex = this._cellToHeadingIndex.get(cell);
|
|
469
479
|
if (headingIndex !== undefined) {
|
|
470
|
-
const heading =
|
|
480
|
+
const heading = headings[headingIndex];
|
|
471
481
|
// Running is prioritized over Scheduled, so if a heading is
|
|
472
482
|
// running don't change status
|
|
473
483
|
if (heading.isRunning !== RunningStatus.Running) {
|
|
@@ -480,7 +490,7 @@ export class NotebookToCModel extends TableOfContentsModel<
|
|
|
480
490
|
this._errorCells.forEach((cell, index) => {
|
|
481
491
|
const headingIndex = this._cellToHeadingIndex.get(cell);
|
|
482
492
|
if (headingIndex !== undefined) {
|
|
483
|
-
const heading =
|
|
493
|
+
const heading = headings[headingIndex];
|
|
484
494
|
// Running and Scheduled are prioritized over Error, so only if
|
|
485
495
|
// a heading is idle will it be set to Error
|
|
486
496
|
if (heading.isRunning === RunningStatus.Idle) {
|
package/src/widget.ts
CHANGED
|
@@ -2834,6 +2834,10 @@ export class Notebook extends StaticNotebook {
|
|
|
2834
2834
|
* Ensure that the notebook has proper focus.
|
|
2835
2835
|
*/
|
|
2836
2836
|
private _ensureFocus(force = false): void {
|
|
2837
|
+
const activeElement = document.activeElement;
|
|
2838
|
+
if (!force && activeElement && !this.node.contains(activeElement)) {
|
|
2839
|
+
return;
|
|
2840
|
+
}
|
|
2837
2841
|
// No-op is the footer has the focus.
|
|
2838
2842
|
const footer = (this.layout as NotebookWindowedLayout).footer;
|
|
2839
2843
|
if (footer && document.activeElement === footer.node) {
|
|
@@ -3374,8 +3378,8 @@ export class Notebook extends StaticNotebook {
|
|
|
3374
3378
|
return;
|
|
3375
3379
|
}
|
|
3376
3380
|
|
|
3377
|
-
// Move the cells
|
|
3378
|
-
|
|
3381
|
+
// Move the selected block of cells, preserving in-flight executions.
|
|
3382
|
+
NotebookActions.moveCells(this, fromIndex, toIndex, toMove.length);
|
|
3379
3383
|
} else {
|
|
3380
3384
|
// Handle the case where we are copying cells between
|
|
3381
3385
|
// notebooks.
|