diff --git a/.eslintrc.js b/.eslintrc.js index a9c53dc..60e4b47 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -35,6 +35,8 @@ module.exports = { ], '@typescript-eslint/no-unused-vars': ['warn', { args: 'none' }], '@typescript-eslint/no-explicit-any': 'off', + // TODO: Remove ban-ts-comment + '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/no-namespace': 'off', '@typescript-eslint/no-var-requires': 'off', '@typescript-eslint/no-use-before-define': 'off', diff --git a/packages/jupyterlab-gridstack/src/editor/format.ts b/packages/jupyterlab-gridstack/src/editor/format.ts index e1cbe57..c034892 100644 --- a/packages/jupyterlab-gridstack/src/editor/format.ts +++ b/packages/jupyterlab-gridstack/src/editor/format.ts @@ -87,17 +87,17 @@ export type DashboardCellView = { }; /** - * Signal argument to lock/unlock a GridstackItem. + * Signal argument for cell change. */ -export type LockSignal = { +export type CellChange = { /** * Cell id. */ - cellId: string; + id: string; /** - * Action. + * Cell view info. */ - lock: boolean; + info: DashboardCellView; }; /** diff --git a/packages/jupyterlab-gridstack/src/editor/gridstack/model.ts b/packages/jupyterlab-gridstack/src/editor/gridstack/model.ts index dc46714..c39b227 100644 --- a/packages/jupyterlab-gridstack/src/editor/gridstack/model.ts +++ b/packages/jupyterlab-gridstack/src/editor/gridstack/model.ts @@ -24,12 +24,14 @@ import { SimplifiedOutputArea } from '@jupyterlab/outputarea'; import { IObservableUndoableList } from '@jupyterlab/observables'; -import { createMutex } from '@jupyterlab/shared-models'; +import { YNotebook, createMutex } from '@jupyterlab/shared-models'; import { Widget } from '@lumino/widgets'; import { Signal, ISignal } from '@lumino/signaling'; +import * as Y from 'yjs'; + import { GridStackItemWidget, GridStackItemModel, ItemState } from '../item'; import { @@ -37,7 +39,7 @@ import { DashboardCellView, validateDashboardView, validateDashboardCellView, - LockSignal + CellChange } from '../format'; export const VIEW = 'grid_default'; @@ -60,8 +62,8 @@ export class GridStackModel { this._notebookConfig = options.notebookConfig; this._ready = new Signal(this); - this._cellRemoved = new Signal(this); - this._cellPinned = new Signal(this); + this._cellRemoved = new Signal(this); + this._cellPinned = new Signal(this); this._stateChanged = new Signal(this); this._contentChanged = new Signal(this); @@ -70,27 +72,29 @@ export class GridStackModel { type: 'grid', maxColumns: 12, cellMargin: 2, - defaultCellHeight: 60 + defaultCellHeight: 40 }; this._context.sessionContext.ready.then(() => { this._checkMetadata(); this._checkCellsMetadata(); + + const ymodel = this._context.model.sharedModel as YNotebook; + // TODO: Remove @ts-ignore + // @ts-ignore + this._ystate = ymodel.ystate; + if (this._ystate.get('executed') !== true) { + ymodel.transact(() => { + this._ystate.set('executed', false); + }, false); + } + this._context.save().then(v => { this._ready.emit(null); }); }); this._context.model.contentChanged.connect(() => { - console.debug('Content changed'); - this._updateCells(); - }, this); - this._context.model.stateChanged.connect(() => { - console.debug('State changed'); - this._updateCells(); - }, this); - this._context.model.sharedModel.changed.connect(() => { - console.debug('Shared model changed'); this._updateCells(); }, this); } @@ -105,14 +109,14 @@ export class GridStackModel { /** * A signal emitted when a cell is removed. */ - get cellRemoved(): ISignal { + get cellRemoved(): ISignal { return this._cellRemoved; } /** * A signal emitted when a cell pinned. */ - get cellPinned(): ISignal { + get cellPinned(): ISignal { return this._cellPinned; } @@ -173,6 +177,10 @@ export class GridStackModel { this._notebookConfig = value; } + set executed(value: boolean) { + this._ystate.set('executed', value); + } + /** * Getter for the dashboard metadata info. */ @@ -197,7 +205,7 @@ export class GridStackModel { (data as Record).extensions.jupyter_dashboards.views[ VIEW ] = this._info; - //this._context.model.metadata.set('extensions', data); + //this._context.model.metadata.set('extensions', data.extensions); this._context.model.sharedModel.setMetadata(data); this._context.model.dirty = true; }); @@ -227,7 +235,6 @@ export class GridStackModel { const cell = this._context.model.cells.get(i); if (cell.id === id) { - //const data = cell.metadata.get('extensions') as Record; const data = cell.sharedModel.getMetadata().extensions as Record< string, any @@ -251,13 +258,11 @@ export class GridStackModel { if (cell.id === id) { this._mutex(() => { - //const data = cell.metadata.get('extensions') as Record; const data = cell.sharedModel.getMetadata().extensions as Record< string, any >; data.jupyter_dashboards.views[VIEW] = info; - //cell.metadata.set('extensions', data); cell.sharedModel.setMetadata({ extensions: data }); this._context.model.dirty = true; }); @@ -277,15 +282,17 @@ export class GridStackModel { if (cell.id === id) { this._mutex(() => { - //const data = cell.metadata.get('extensions') as Record; const data = cell.sharedModel.getMetadata().extensions as Record< string, any >; data.jupyter_dashboards.views[VIEW].hidden = true; - //cell.metadata.set('extensions', data); cell.sharedModel.setMetadata({ extensions: data }); this._context.model.dirty = true; + this._cellRemoved.emit({ + id, + info: data.jupyter_dashboards.views[VIEW] + }); }); break; } @@ -303,15 +310,17 @@ export class GridStackModel { if (cell.id === id) { this._mutex(() => { - //const data = cell.metadata.get('extensions') as Record; const data = cell.sharedModel.getMetadata().extensions as Record< string, any >; data.jupyter_dashboards.views[VIEW].locked = lock; - //cell.metadata.set('extensions', data); cell.sharedModel.setMetadata({ extensions: data }); this._context.model.dirty = true; + this._cellPinned.emit({ + id, + info: data.jupyter_dashboards.views[VIEW] + }); }); break; } @@ -394,7 +403,7 @@ export class GridStackModel { * @param cell - `ICellModel`. */ public execute(cell: ICellModel): void { - if (cell.type !== 'code') { + if (cell.type !== 'code' || this._ystate.get('executed')) { return; } @@ -410,7 +419,16 @@ export class GridStackModel { cell.value.text, codeCell.outputArea, this._context.sessionContext - ).catch(reason => console.error(reason)); + ) + .then(resp => { + if ( + resp?.header.msg_type === 'execute_reply' && + resp.content.status === 'ok' + ) { + (cell as CodeCellModel).executionCount = resp.content.execution_count; + } + }) + .catch(reason => console.error(reason)); } private readonly _mutex = createMutex(); @@ -429,11 +447,6 @@ export class GridStackModel { * Check the dashboard notebook's metadata. */ private _checkMetadata(): void { - /* let data = this._context.model.metadata.get('extensions') as Record< - string, - any - >; */ - let data = this._context.model.sharedModel.getMetadata() .extensions as Record; @@ -461,8 +474,6 @@ export class GridStackModel { this._info = data.jupyter_dashboards?.views[VIEW] as DashboardView; } - //this._context.model.metadata.set('extensions', data); - console.debug('Metadata:', data); this._mutex(() => { this._context.model.sharedModel.updateMetadata({ extensions: data }); }); @@ -482,19 +493,16 @@ export class GridStackModel { switch (change) { case ItemState.CLOSED: this.hideCell(item.cellId); - this._cellRemoved.emit(item.cellId); item.stateChanged.disconnect(this._itemChanged); item.dispose(); break; case ItemState.LOCKED: this.lockCell(item.cellId, true); - this._cellPinned.emit({ cellId: item.cellId, lock: true }); break; case ItemState.UNLOCKED: this.lockCell(item.cellId, false); - this._cellPinned.emit({ cellId: item.cellId, lock: false }); break; default: @@ -508,7 +516,6 @@ export class GridStackModel { * @param cell - `ICellModel`. */ private _checkCellMetadata(cell: ICellModel): void { - //let data = cell.metadata.get('extensions') as Record; let data = cell.sharedModel.getMetadata().extensions as Record; if (!data) { @@ -522,12 +529,11 @@ export class GridStackModel { col: null, width: 2, height: 2, - locked: false + locked: true } } } }; - //cell.metadata.set('extensions', data); this._mutex(() => { cell.sharedModel.setMetadata({ extensions: data }); }); @@ -541,11 +547,10 @@ export class GridStackModel { col: null, width: 2, height: 2, - locked: false + locked: true } } }; - //cell.metadata.set('extensions', data); this._mutex(() => { cell.sharedModel.setMetadata({ extensions: data }); }); @@ -558,24 +563,23 @@ export class GridStackModel { col: null, width: 2, height: 2, - locked: false + locked: true }; - //cell.metadata.set('extensions', data); this._mutex(() => { cell.sharedModel.setMetadata({ extensions: data }); }); } - console.debug('Cell Medatada:', data); } private _context: DocumentRegistry.IContext; private _editorConfig: StaticNotebook.IEditorConfig; private _notebookConfig: StaticNotebook.INotebookConfig; private _info: DashboardView; + private _ystate: Y.Map = new Y.Map(); private _ready: Signal; - private _cellRemoved: Signal; - private _cellPinned: Signal; + private _cellRemoved: Signal; + private _cellPinned: Signal; private _stateChanged: Signal; private _contentChanged: Signal; } diff --git a/packages/jupyterlab-gridstack/src/editor/gridstack/widget.ts b/packages/jupyterlab-gridstack/src/editor/gridstack/widget.ts index 7a6f279..55b302d 100644 --- a/packages/jupyterlab-gridstack/src/editor/gridstack/widget.ts +++ b/packages/jupyterlab-gridstack/src/editor/gridstack/widget.ts @@ -20,7 +20,7 @@ import { GridStackModel } from './model'; import { DashboardMetadataEditor } from '../components/metadata'; -import { DashboardCellView, LockSignal } from '../format'; +import { CellChange } from '../format'; interface IDroppable { /** @@ -185,6 +185,8 @@ export class GridStackWidget extends Widget { this.layout.addGridItem(model.id, item, info); } } + + this._model.executed = true; } /** @@ -193,8 +195,8 @@ export class GridStackWidget extends Widget { * @param model - The `GridstackModel` that sends the signal. * @param id - The Cell id. */ - private _removeCell(model: GridStackModel, id: string): void { - this.layout.removeGridItem(id); + private _removeCell(model: GridStackModel, cell: CellChange): void { + this.layout.removeGridItem(cell.id); } /** @@ -203,10 +205,8 @@ export class GridStackWidget extends Widget { * @param model - The `GridstackModel` that sends the signal. * @param id - The Cell id. */ - private _lockCell(model: GridStackModel, evt: LockSignal): void { - const info = this._model.getCellInfo(evt.cellId); - info!.locked = evt.lock; - this.layout.updateGridItem(evt.cellId, info as DashboardCellView); + private _lockCell(model: GridStackModel, cell: CellChange): void { + this.layout.updateGridItem(cell.id, cell.info); } /** @@ -309,7 +309,7 @@ export class GridStackWidget extends Widget { row: el.y ?? 0, width: el.w ?? 2, height: el.h ?? 2, - locked: el.locked ?? false + locked: el.locked ?? true }); }); } @@ -479,7 +479,7 @@ export class GridStackWidget extends Widget { info.row = row; info.width = width; info.height = height; - info.locked = info.locked ? true : false; + info.locked = info.locked === false ? false : true; this._model.setCellInfo(widget.model.id, info); const item = this._model.createCell(widget.model, info.locked); this.layout.addGridItem(widget.model.id, item, info); @@ -492,7 +492,7 @@ export class GridStackWidget extends Widget { info.row = row; info.width = width; info.height = height; - info.locked = info.locked ? true : false; + info.locked = info.locked === false ? false : true; this._model.setCellInfo(widget.model.id, info); const item = this._model.createCell(widget.model, info.locked); this.layout.addGridItem(widget.model.id, item, info); @@ -507,7 +507,7 @@ export class GridStackWidget extends Widget { info.col = col; info.row = row; info.width = Math.min(width, info.width); - info.locked = info.locked ? true : false; + info.locked = info.locked === false ? false : true; this._model.setCellInfo(widget.model.id, info); this.layout.updateGridItem(widget.model.id, info); } else if (!info) {