@jupyterlab/notebook 4.6.0-alpha.5 → 4.6.0-beta.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/lib/actions.d.ts +12 -0
- package/lib/actions.js +143 -2
- package/lib/actions.js.map +1 -1
- package/lib/cellexecutor.js +1 -0
- package/lib/cellexecutor.js.map +1 -1
- package/lib/model.d.ts +0 -1
- package/lib/model.js +6 -19
- package/lib/model.js.map +1 -1
- package/lib/panel.js +1 -0
- package/lib/panel.js.map +1 -1
- package/lib/searchprovider.js +1 -0
- package/lib/searchprovider.js.map +1 -1
- package/lib/testutils.d.ts +1 -1
- package/lib/testutils.js +1 -0
- package/lib/testutils.js.map +1 -1
- package/lib/toc.d.ts +12 -1
- package/lib/toc.js +42 -1
- package/lib/toc.js.map +1 -1
- package/lib/widget.d.ts +27 -0
- package/lib/widget.js +133 -0
- package/lib/widget.js.map +1 -1
- package/lib/widgetfactory.js +1 -0
- package/lib/widgetfactory.js.map +1 -1
- package/package.json +23 -22
- package/src/actions.tsx +183 -3
- package/src/cellexecutor.ts +1 -0
- package/src/model.ts +7 -18
- package/src/panel.ts +1 -0
- package/src/searchprovider.ts +1 -0
- package/src/testutils.ts +1 -0
- package/src/toc.ts +49 -1
- package/src/widget.ts +142 -1
- package/src/widgetfactory.ts +1 -0
- package/style/base.css +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupyterlab/notebook",
|
|
3
|
-
"version": "4.6.0-
|
|
3
|
+
"version": "4.6.0-beta.1",
|
|
4
4
|
"description": "JupyterLab - Notebook",
|
|
5
5
|
"homepage": "https://github.com/jupyterlab/jupyterlab",
|
|
6
6
|
"bugs": {
|
|
@@ -40,26 +40,27 @@
|
|
|
40
40
|
"watch": "tsc -b --watch"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@jupyter/ydoc": "^
|
|
44
|
-
"@jupyterlab/apputils": "^4.7.0-
|
|
45
|
-
"@jupyterlab/cells": "^4.6.0-
|
|
46
|
-
"@jupyterlab/codeeditor": "^4.6.0-
|
|
47
|
-
"@jupyterlab/codemirror": "^4.6.0-
|
|
48
|
-
"@jupyterlab/coreutils": "^6.6.0-
|
|
49
|
-
"@jupyterlab/docregistry": "^4.6.0-
|
|
50
|
-
"@jupyterlab/documentsearch": "^4.6.0-
|
|
51
|
-
"@jupyterlab/lsp": "^4.6.0-
|
|
52
|
-
"@jupyterlab/markedparser-extension": "^4.6.0-
|
|
53
|
-
"@jupyterlab/nbformat": "^4.6.0-
|
|
54
|
-
"@jupyterlab/observables": "^5.6.0-
|
|
55
|
-
"@jupyterlab/rendermime": "^4.6.0-
|
|
56
|
-
"@jupyterlab/services": "^7.6.0-
|
|
57
|
-
"@jupyterlab/settingregistry": "^4.6.0-
|
|
58
|
-
"@jupyterlab/statusbar": "^4.6.0-
|
|
59
|
-
"@jupyterlab/toc": "^6.6.0-
|
|
60
|
-
"@jupyterlab/translation": "^4.6.0-
|
|
61
|
-
"@jupyterlab/ui-components": "^4.6.0-
|
|
43
|
+
"@jupyter/ydoc": "^4.0.0-a3",
|
|
44
|
+
"@jupyterlab/apputils": "^4.7.0-beta.1",
|
|
45
|
+
"@jupyterlab/cells": "^4.6.0-beta.1",
|
|
46
|
+
"@jupyterlab/codeeditor": "^4.6.0-beta.1",
|
|
47
|
+
"@jupyterlab/codemirror": "^4.6.0-beta.1",
|
|
48
|
+
"@jupyterlab/coreutils": "^6.6.0-beta.1",
|
|
49
|
+
"@jupyterlab/docregistry": "^4.6.0-beta.1",
|
|
50
|
+
"@jupyterlab/documentsearch": "^4.6.0-beta.1",
|
|
51
|
+
"@jupyterlab/lsp": "^4.6.0-beta.1",
|
|
52
|
+
"@jupyterlab/markedparser-extension": "^4.6.0-beta.1",
|
|
53
|
+
"@jupyterlab/nbformat": "^4.6.0-beta.1",
|
|
54
|
+
"@jupyterlab/observables": "^5.6.0-beta.1",
|
|
55
|
+
"@jupyterlab/rendermime": "^4.6.0-beta.1",
|
|
56
|
+
"@jupyterlab/services": "^7.6.0-beta.1",
|
|
57
|
+
"@jupyterlab/settingregistry": "^4.6.0-beta.1",
|
|
58
|
+
"@jupyterlab/statusbar": "^4.6.0-beta.1",
|
|
59
|
+
"@jupyterlab/toc": "^6.6.0-beta.1",
|
|
60
|
+
"@jupyterlab/translation": "^4.6.0-beta.1",
|
|
61
|
+
"@jupyterlab/ui-components": "^4.6.0-beta.1",
|
|
62
62
|
"@lumino/algorithm": "^2.0.4",
|
|
63
|
+
"@lumino/commands": "^2.3.3",
|
|
63
64
|
"@lumino/coreutils": "^2.2.2",
|
|
64
65
|
"@lumino/disposable": "^2.1.5",
|
|
65
66
|
"@lumino/domutils": "^2.0.4",
|
|
@@ -69,11 +70,11 @@
|
|
|
69
70
|
"@lumino/properties": "^2.0.4",
|
|
70
71
|
"@lumino/signaling": "^2.1.5",
|
|
71
72
|
"@lumino/virtualdom": "^2.0.4",
|
|
72
|
-
"@lumino/widgets": "^2.
|
|
73
|
+
"@lumino/widgets": "^2.8.0",
|
|
73
74
|
"react": "^18.2.0"
|
|
74
75
|
},
|
|
75
76
|
"devDependencies": {
|
|
76
|
-
"@jupyterlab/testing": "^4.6.0-
|
|
77
|
+
"@jupyterlab/testing": "^4.6.0-beta.1",
|
|
77
78
|
"@types/jest": "^29.2.0",
|
|
78
79
|
"jest": "^29.2.0",
|
|
79
80
|
"rimraf": "~5.0.5",
|
package/src/actions.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Jupyter Development Team.
|
|
2
2
|
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
4
|
|
|
4
5
|
import type {
|
|
5
6
|
ISessionContext,
|
|
@@ -11,8 +12,9 @@ import {
|
|
|
11
12
|
showDialog,
|
|
12
13
|
SystemClipboard
|
|
13
14
|
} from '@jupyterlab/apputils';
|
|
14
|
-
import type { Cell,
|
|
15
|
+
import type { Cell, ICodeCellModel } from '@jupyterlab/cells';
|
|
15
16
|
import {
|
|
17
|
+
CodeCell,
|
|
16
18
|
CodeCellModel,
|
|
17
19
|
isMarkdownCellModel,
|
|
18
20
|
isRawCellModel,
|
|
@@ -21,8 +23,9 @@ import {
|
|
|
21
23
|
import { Notification } from '@jupyterlab/apputils';
|
|
22
24
|
import { signalToPromise } from '@jupyterlab/coreutils';
|
|
23
25
|
import * as nbformat from '@jupyterlab/nbformat';
|
|
24
|
-
import type { KernelMessage } from '@jupyterlab/services';
|
|
26
|
+
import type { Kernel, KernelMessage } from '@jupyterlab/services';
|
|
25
27
|
import type { ISharedAttachmentsCell } from '@jupyter/ydoc';
|
|
28
|
+
import type { YNotebook } from '@jupyter/ydoc';
|
|
26
29
|
import type { ITranslator } from '@jupyterlab/translation';
|
|
27
30
|
import { nullTranslator } from '@jupyterlab/translation';
|
|
28
31
|
import { every, findIndex } from '@lumino/algorithm';
|
|
@@ -442,6 +445,22 @@ export namespace NotebookActions {
|
|
|
442
445
|
: undefined
|
|
443
446
|
};
|
|
444
447
|
|
|
448
|
+
// Detach kernel futures from cells about to be deleted so OutputArea.dispose()
|
|
449
|
+
// does not terminate them - they stay live in kernel._futures for reconnection
|
|
450
|
+
// after undo. Handlers are cleared by detachFuture() so the future no
|
|
451
|
+
// longer holds references to the (soon-to-be-disposed) output area.
|
|
452
|
+
const storedExecutions: Private.IStoredCellExecution[] = [];
|
|
453
|
+
[active, ...toDelete].forEach(index => {
|
|
454
|
+
const cell = notebook.widgets[index];
|
|
455
|
+
if (!(cell instanceof CodeCell)) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
const stored = Private.captureExecution(cell);
|
|
459
|
+
if (stored) {
|
|
460
|
+
storedExecutions.push(stored);
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
|
|
445
464
|
// Make the changes while preserving history.
|
|
446
465
|
model.sharedModel.transact(() => {
|
|
447
466
|
model.sharedModel.deleteCell(active);
|
|
@@ -452,6 +471,14 @@ export namespace NotebookActions {
|
|
|
452
471
|
model.sharedModel.deleteCell(index);
|
|
453
472
|
});
|
|
454
473
|
});
|
|
474
|
+
|
|
475
|
+
// Store execution context in the undo stack item so undo() can restore state.
|
|
476
|
+
if (storedExecutions.length > 0) {
|
|
477
|
+
const undoManager = (model.sharedModel as YNotebook).undoManager;
|
|
478
|
+
const lastItem = undoManager.undoStack[undoManager.undoStack.length - 1];
|
|
479
|
+
lastItem?.meta.set(Private.CELL_EXECUTION_META_KEY, storedExecutions);
|
|
480
|
+
}
|
|
481
|
+
|
|
455
482
|
// If the original cell is a markdown cell, make sure
|
|
456
483
|
// the new cell is unrendered.
|
|
457
484
|
if (primary instanceof MarkdownCell) {
|
|
@@ -1581,7 +1608,60 @@ export namespace NotebookActions {
|
|
|
1581
1608
|
|
|
1582
1609
|
const state = Private.getState(notebook);
|
|
1583
1610
|
notebook.mode = 'command';
|
|
1611
|
+
|
|
1612
|
+
// Capture execution context from the stack item being popped.
|
|
1613
|
+
let storedExecutions: Private.IStoredCellExecution[] | undefined;
|
|
1614
|
+
const undoManager = (notebook.model.sharedModel as YNotebook).undoManager;
|
|
1615
|
+
const onStackItemPopped = ({
|
|
1616
|
+
stackItem
|
|
1617
|
+
}: {
|
|
1618
|
+
stackItem: { meta: Map<unknown, unknown> };
|
|
1619
|
+
}) => {
|
|
1620
|
+
storedExecutions = stackItem.meta.get(Private.CELL_EXECUTION_META_KEY) as
|
|
1621
|
+
| Private.IStoredCellExecution[]
|
|
1622
|
+
| undefined;
|
|
1623
|
+
};
|
|
1624
|
+
undoManager.on('stack-item-popped', onStackItemPopped);
|
|
1584
1625
|
notebook.model.sharedModel.undo();
|
|
1626
|
+
undoManager.off('stack-item-popped', onStackItemPopped);
|
|
1627
|
+
|
|
1628
|
+
// Restore execution state on resurrected cell widgets.
|
|
1629
|
+
storedExecutions?.forEach(({ cellId, future, isDone, buffered }) => {
|
|
1630
|
+
const cell = notebook.widgets.find(w => w.model.id === cellId);
|
|
1631
|
+
if (!(cell instanceof CodeCell)) {
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
if (isDone()) {
|
|
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
|
+
}
|
|
1662
|
+
);
|
|
1663
|
+
});
|
|
1664
|
+
|
|
1585
1665
|
notebook.deselectAll();
|
|
1586
1666
|
void Private.handleState(notebook, state);
|
|
1587
1667
|
}
|
|
@@ -1977,6 +2057,36 @@ export namespace NotebookActions {
|
|
|
1977
2057
|
}
|
|
1978
2058
|
}
|
|
1979
2059
|
|
|
2060
|
+
/**
|
|
2061
|
+
* Select the last modified cell and pop it from the back stack
|
|
2062
|
+
*
|
|
2063
|
+
* @param notebook - The target notebook widget.
|
|
2064
|
+
*/
|
|
2065
|
+
export async function selectLastModifiedCell(
|
|
2066
|
+
notebook: Notebook
|
|
2067
|
+
): Promise<void> {
|
|
2068
|
+
const cell = notebook.popLastModifiedCell();
|
|
2069
|
+
if (cell && cell !== notebook.activeCell && !cell.isDisposed) {
|
|
2070
|
+
notebook.activeCellIndex = notebook.widgets.indexOf(cell);
|
|
2071
|
+
await notebook.scrollToCell(cell);
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
/**
|
|
2076
|
+
* Select the next modified cell and pop it from the forward stack
|
|
2077
|
+
*
|
|
2078
|
+
* @param notebook - The target notebook widget.
|
|
2079
|
+
*/
|
|
2080
|
+
export async function selectNextModifiedCell(
|
|
2081
|
+
notebook: Notebook
|
|
2082
|
+
): Promise<void> {
|
|
2083
|
+
const cell = notebook.popNextModifiedCell();
|
|
2084
|
+
if (cell && cell !== notebook.activeCell && !cell.isDisposed) {
|
|
2085
|
+
notebook.activeCellIndex = notebook.widgets.indexOf(cell);
|
|
2086
|
+
await notebook.scrollToCell(cell);
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
|
|
1980
2090
|
/**
|
|
1981
2091
|
* Set the markdown header level.
|
|
1982
2092
|
*
|
|
@@ -2455,6 +2565,44 @@ export function setCellExecutor(executor: INotebookCellExecutor): void {
|
|
|
2455
2565
|
* A namespace for private data.
|
|
2456
2566
|
*/
|
|
2457
2567
|
namespace Private {
|
|
2568
|
+
/** Key used to store cell execution state in Y.js undo stack item metadata. */
|
|
2569
|
+
export const CELL_EXECUTION_META_KEY = Symbol('cellExecutionState');
|
|
2570
|
+
|
|
2571
|
+
export interface IStoredCellExecution {
|
|
2572
|
+
cellId: string;
|
|
2573
|
+
future: Kernel.IShellFuture<
|
|
2574
|
+
KernelMessage.IExecuteRequestMsg,
|
|
2575
|
+
KernelMessage.IExecuteReplyMsg
|
|
2576
|
+
>;
|
|
2577
|
+
isDone: () => boolean;
|
|
2578
|
+
/** IOPub messages that arrived while the future was detached. */
|
|
2579
|
+
buffered: KernelMessage.IIOPubMessage[];
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
/**
|
|
2583
|
+
* Detach the kernel future from a code cell, buffering any IOPub messages
|
|
2584
|
+
* that arrive while it is detached so they can be replayed on reattach.
|
|
2585
|
+
*
|
|
2586
|
+
* Returns null if the cell has no active future.
|
|
2587
|
+
*/
|
|
2588
|
+
export function captureExecution(
|
|
2589
|
+
cell: CodeCell
|
|
2590
|
+
): IStoredCellExecution | null {
|
|
2591
|
+
const future = cell.outputArea.detachFuture();
|
|
2592
|
+
if (!future) {
|
|
2593
|
+
return null;
|
|
2594
|
+
}
|
|
2595
|
+
let done = false;
|
|
2596
|
+
void future.done.finally(() => {
|
|
2597
|
+
done = true;
|
|
2598
|
+
});
|
|
2599
|
+
const buffered: KernelMessage.IIOPubMessage[] = [];
|
|
2600
|
+
future.onIOPub = msg => {
|
|
2601
|
+
buffered.push(msg);
|
|
2602
|
+
};
|
|
2603
|
+
return { cellId: cell.model.id, future, isDone: () => done, buffered };
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2458
2606
|
/**
|
|
2459
2607
|
* Notebook cell executor
|
|
2460
2608
|
*/
|
|
@@ -2901,6 +3049,11 @@ namespace Private {
|
|
|
2901
3049
|
if (headingLevel !== undefined) {
|
|
2902
3050
|
newSource = Private.setMarkdownHeader(newSource, headingLevel);
|
|
2903
3051
|
}
|
|
3052
|
+
// Detach future before the transaction so dispose() does not cancel it.
|
|
3053
|
+
const storedExecution =
|
|
3054
|
+
child instanceof CodeCell
|
|
3055
|
+
? Private.captureExecution(child)
|
|
3056
|
+
: undefined;
|
|
2904
3057
|
notebookSharedModel.transact(() => {
|
|
2905
3058
|
notebookSharedModel.deleteCell(index);
|
|
2906
3059
|
if (value === 'code') {
|
|
@@ -2922,6 +3075,14 @@ namespace Private {
|
|
|
2922
3075
|
raw.attachments as nbformat.IAttachments;
|
|
2923
3076
|
}
|
|
2924
3077
|
});
|
|
3078
|
+
if (storedExecution) {
|
|
3079
|
+
const undoManager = (notebookSharedModel as YNotebook).undoManager;
|
|
3080
|
+
const lastItem =
|
|
3081
|
+
undoManager.undoStack[undoManager.undoStack.length - 1];
|
|
3082
|
+
lastItem?.meta.set(Private.CELL_EXECUTION_META_KEY, [
|
|
3083
|
+
storedExecution
|
|
3084
|
+
]);
|
|
3085
|
+
}
|
|
2925
3086
|
} else if (value === 'markdown' && headingLevel !== undefined) {
|
|
2926
3087
|
notebookSharedModel.transact(() => {
|
|
2927
3088
|
child.model.sharedModel.setSource(
|
|
@@ -2971,6 +3132,19 @@ namespace Private {
|
|
|
2971
3132
|
|
|
2972
3133
|
// If cells are not deletable, we may not have anything to delete.
|
|
2973
3134
|
if (toDelete.length > 0) {
|
|
3135
|
+
// Detach futures before the transaction so dispose() does not cancel them.
|
|
3136
|
+
const storedExecutions: Private.IStoredCellExecution[] = [];
|
|
3137
|
+
toDelete.forEach(index => {
|
|
3138
|
+
const cell = notebook.widgets[index];
|
|
3139
|
+
if (!(cell instanceof CodeCell)) {
|
|
3140
|
+
return;
|
|
3141
|
+
}
|
|
3142
|
+
const stored = Private.captureExecution(cell);
|
|
3143
|
+
if (stored) {
|
|
3144
|
+
storedExecutions.push(stored);
|
|
3145
|
+
}
|
|
3146
|
+
});
|
|
3147
|
+
|
|
2974
3148
|
// Delete the cells as one undo event.
|
|
2975
3149
|
sharedModel.transact(() => {
|
|
2976
3150
|
// Delete cells in reverse order to maintain the correct indices.
|
|
@@ -2994,6 +3168,12 @@ namespace Private {
|
|
|
2994
3168
|
});
|
|
2995
3169
|
}
|
|
2996
3170
|
});
|
|
3171
|
+
if (storedExecutions.length > 0) {
|
|
3172
|
+
const undoManager = (sharedModel as YNotebook).undoManager;
|
|
3173
|
+
const lastItem =
|
|
3174
|
+
undoManager.undoStack[undoManager.undoStack.length - 1];
|
|
3175
|
+
lastItem?.meta.set(Private.CELL_EXECUTION_META_KEY, storedExecutions);
|
|
3176
|
+
}
|
|
2997
3177
|
// Select the *first* interior cell not deleted or the cell
|
|
2998
3178
|
// *after* the last selected cell.
|
|
2999
3179
|
// Note: The activeCellIndex is clamped to the available cells,
|
|
@@ -3013,7 +3193,7 @@ namespace Private {
|
|
|
3013
3193
|
*/
|
|
3014
3194
|
export function setMarkdownHeader(source: string, level: number): string {
|
|
3015
3195
|
// Remove existing header or leading white space.
|
|
3016
|
-
const regex =
|
|
3196
|
+
const regex = /^#+\s*|^\s*/;
|
|
3017
3197
|
const newHeader = Array(level + 1).join('#') + ' ';
|
|
3018
3198
|
const matches = regex.exec(source);
|
|
3019
3199
|
|
package/src/cellexecutor.ts
CHANGED
package/src/model.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Jupyter Development Team.
|
|
2
2
|
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
4
|
|
|
4
5
|
import { Dialog, showDialog } from '@jupyterlab/apputils';
|
|
5
6
|
import type { ICellModel } from '@jupyterlab/cells';
|
|
@@ -15,6 +16,7 @@ import type {
|
|
|
15
16
|
import { YNotebook } from '@jupyter/ydoc';
|
|
16
17
|
import type { ITranslator, TranslationBundle } from '@jupyterlab/translation';
|
|
17
18
|
import { nullTranslator } from '@jupyterlab/translation';
|
|
19
|
+
import type { JSONValue } from '@lumino/coreutils';
|
|
18
20
|
import { JSONExt } from '@lumino/coreutils';
|
|
19
21
|
import type { ISignal } from '@lumino/signaling';
|
|
20
22
|
import { Signal } from '@lumino/signaling';
|
|
@@ -160,19 +162,14 @@ export class NotebookModel implements INotebookModel {
|
|
|
160
162
|
* The dirty state of the document.
|
|
161
163
|
*/
|
|
162
164
|
get dirty(): boolean {
|
|
163
|
-
return this.
|
|
165
|
+
return this.sharedModel.dirty;
|
|
164
166
|
}
|
|
165
167
|
set dirty(newValue: boolean) {
|
|
166
|
-
const oldValue = this.
|
|
168
|
+
const oldValue = this.dirty;
|
|
167
169
|
if (newValue === oldValue) {
|
|
168
170
|
return;
|
|
169
171
|
}
|
|
170
|
-
this.
|
|
171
|
-
this.triggerStateChange({
|
|
172
|
-
name: 'dirty',
|
|
173
|
-
oldValue,
|
|
174
|
-
newValue
|
|
175
|
-
});
|
|
172
|
+
this.sharedModel.dirty = newValue;
|
|
176
173
|
}
|
|
177
174
|
|
|
178
175
|
/**
|
|
@@ -382,10 +379,9 @@ close the notebook without saving it.`,
|
|
|
382
379
|
{ cell_type: 'code', source: '', metadata: { trusted: true } }
|
|
383
380
|
];
|
|
384
381
|
}
|
|
385
|
-
this.sharedModel.
|
|
382
|
+
this.sharedModel.setSource(copy as JSONValue);
|
|
386
383
|
|
|
387
384
|
this._ensureMetadata();
|
|
388
|
-
this.dirty = true;
|
|
389
385
|
}
|
|
390
386
|
|
|
391
387
|
/**
|
|
@@ -431,12 +427,7 @@ close the notebook without saving it.`,
|
|
|
431
427
|
): void {
|
|
432
428
|
if (changes.stateChange) {
|
|
433
429
|
changes.stateChange.forEach(value => {
|
|
434
|
-
if (value.
|
|
435
|
-
// Setting `dirty` will trigger the state change.
|
|
436
|
-
// We always set `dirty` because the shared model state
|
|
437
|
-
// and the local attribute are synchronized one way shared model -> _dirty
|
|
438
|
-
this.dirty = value.newValue;
|
|
439
|
-
} else if (value.oldValue !== value.newValue) {
|
|
430
|
+
if (value.oldValue !== value.newValue) {
|
|
440
431
|
this.triggerStateChange({
|
|
441
432
|
newValue: undefined,
|
|
442
433
|
oldValue: undefined,
|
|
@@ -474,7 +465,6 @@ close the notebook without saving it.`,
|
|
|
474
465
|
*/
|
|
475
466
|
protected triggerContentChange(): void {
|
|
476
467
|
this._contentChanged.emit(void 0);
|
|
477
|
-
this.dirty = true;
|
|
478
468
|
}
|
|
479
469
|
|
|
480
470
|
/**
|
|
@@ -494,7 +484,6 @@ close the notebook without saving it.`,
|
|
|
494
484
|
*/
|
|
495
485
|
protected standaloneModel = false;
|
|
496
486
|
|
|
497
|
-
private _dirty = false;
|
|
498
487
|
private _readOnly = false;
|
|
499
488
|
private _contentChanged = new Signal<this, void>(this);
|
|
500
489
|
private _stateChanged = new Signal<this, IChangedArgs<any>>(this);
|
package/src/panel.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Jupyter Development Team.
|
|
2
2
|
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
4
|
|
|
4
5
|
import type { ISessionContext } from '@jupyterlab/apputils';
|
|
5
6
|
import { Dialog, Printing, showDialog } from '@jupyterlab/apputils';
|
package/src/searchprovider.ts
CHANGED
package/src/testutils.ts
CHANGED
package/src/toc.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Jupyter Development Team.
|
|
2
2
|
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
4
|
|
|
4
5
|
import type { CodeCell, ICellModel } from '@jupyterlab/cells';
|
|
5
6
|
import { Cell, MarkdownCell } from '@jupyterlab/cells';
|
|
@@ -15,6 +16,13 @@ import { NotebookActions } from './actions';
|
|
|
15
16
|
import type { NotebookPanel } from './panel';
|
|
16
17
|
import type { INotebookTracker } from './tokens';
|
|
17
18
|
import type { Notebook } from './widget';
|
|
19
|
+
import {
|
|
20
|
+
CommandToolbarButton,
|
|
21
|
+
jumpBackIcon,
|
|
22
|
+
jumpForwardIcon
|
|
23
|
+
} from '@jupyterlab/ui-components';
|
|
24
|
+
import type { ToolbarRegistry } from '@jupyterlab/apputils';
|
|
25
|
+
import type { CommandRegistry } from '@lumino/commands';
|
|
18
26
|
|
|
19
27
|
/**
|
|
20
28
|
* Cell running status
|
|
@@ -559,11 +567,13 @@ export class NotebookToCFactory extends TableOfContentsFactory<NotebookPanel> {
|
|
|
559
567
|
* @param tracker Widget tracker
|
|
560
568
|
* @param parser Markdown parser
|
|
561
569
|
* @param sanitizer Sanitizer
|
|
570
|
+
* @param commands A registry of commands
|
|
562
571
|
*/
|
|
563
572
|
constructor(
|
|
564
573
|
tracker: INotebookTracker,
|
|
565
574
|
protected parser: IMarkdownParser | null,
|
|
566
|
-
protected sanitizer: IRenderMime.ISanitizer
|
|
575
|
+
protected sanitizer: IRenderMime.ISanitizer,
|
|
576
|
+
protected commands: CommandRegistry | undefined
|
|
567
577
|
) {
|
|
568
578
|
super(tracker);
|
|
569
579
|
}
|
|
@@ -790,6 +800,44 @@ export class NotebookToCFactory extends TableOfContentsFactory<NotebookPanel> {
|
|
|
790
800
|
return model;
|
|
791
801
|
}
|
|
792
802
|
|
|
803
|
+
/**
|
|
804
|
+
* Get the toolbar items for the widget
|
|
805
|
+
*
|
|
806
|
+
* @param widget - widget
|
|
807
|
+
* @returns List of toolbar items
|
|
808
|
+
*/
|
|
809
|
+
getToolbarItems(widget: NotebookPanel): ToolbarRegistry.IToolbarItem[] {
|
|
810
|
+
if (!this.commands) {
|
|
811
|
+
return [];
|
|
812
|
+
}
|
|
813
|
+
return [
|
|
814
|
+
{
|
|
815
|
+
name: 'select-last-modified-back',
|
|
816
|
+
widget: new CommandToolbarButton({
|
|
817
|
+
commands: this.commands,
|
|
818
|
+
id: 'notebook:select-last-modified-cell',
|
|
819
|
+
args: {
|
|
820
|
+
toolbar: true
|
|
821
|
+
},
|
|
822
|
+
icon: jumpBackIcon,
|
|
823
|
+
label: ''
|
|
824
|
+
})
|
|
825
|
+
},
|
|
826
|
+
{
|
|
827
|
+
name: 'select-last-modified-forward',
|
|
828
|
+
widget: new CommandToolbarButton({
|
|
829
|
+
commands: this.commands,
|
|
830
|
+
id: 'notebook:select-next-modified-cell',
|
|
831
|
+
args: {
|
|
832
|
+
toolbar: true
|
|
833
|
+
},
|
|
834
|
+
icon: jumpForwardIcon,
|
|
835
|
+
label: ''
|
|
836
|
+
})
|
|
837
|
+
}
|
|
838
|
+
];
|
|
839
|
+
}
|
|
840
|
+
|
|
793
841
|
private _scrollToTop: boolean = true;
|
|
794
842
|
}
|
|
795
843
|
|