mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-20 01:25:37 -05:00
Introduce tabs for notebook views (#19526)
* Introduce tabs for notebook views Cards have been restructured to contain tabs instead of cells directly. Tabs then contain the cards that are displayed. Cards may contain one or more cards. The panel component has been reused to implement the cells. There is still some cleanup left to do of unused functions, but I want to reduce the size of the PR as much as possible.
This commit is contained in:
@@ -8,7 +8,7 @@ import { NotebookChangeType } from 'sql/workbench/services/notebook/common/contr
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
|
||||
export class NotebookExtension<TNotebookMeta, TCellMeta> {
|
||||
readonly version = 1;
|
||||
readonly version: number = 1;
|
||||
readonly extensionNamespace = 'extensions';
|
||||
|
||||
private _extensionName: string;
|
||||
|
||||
@@ -6,8 +6,9 @@ import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/
|
||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { localize } from 'vs/nls';
|
||||
import { INotebookView, INotebookViewCell } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews';
|
||||
import { INotebookView, INotebookViewCard, INotebookViewCell, ViewsTabConfig } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IconPath } from 'azdata';
|
||||
|
||||
export const DEFAULT_VIEW_CARD_HEIGHT = 4;
|
||||
export const DEFAULT_VIEW_CARD_WIDTH = 12;
|
||||
@@ -15,10 +16,42 @@ export const GRID_COLUMNS = 12;
|
||||
|
||||
export class ViewNameTakenError extends Error { }
|
||||
|
||||
function cellCollides(c1: INotebookViewCell, c2: INotebookViewCell): boolean {
|
||||
function cellCollides(c1: INotebookViewCard, c2: INotebookViewCard): boolean {
|
||||
return !((c1.y + c1.height <= c2.y) || (c1.x + c1.width <= c2.x) || (c1.x + c1.width <= c2.x) || (c2.x + c2.width <= c1.x));
|
||||
}
|
||||
|
||||
export class ViewsTab implements ViewsTabConfig {
|
||||
cell: INotebookViewCell;
|
||||
title: string;
|
||||
id?: string;
|
||||
group: string;
|
||||
icon?: IconPath;
|
||||
cellModel: ICellModel;
|
||||
|
||||
constructor(config: ViewsTabConfig, cell: ICellModel) {
|
||||
this.fromJSON(config, cell);
|
||||
}
|
||||
|
||||
public toJSON() {
|
||||
return {
|
||||
cell: this.cell,
|
||||
title: this.title,
|
||||
id: this.id,
|
||||
group: this.group,
|
||||
icon: this.icon
|
||||
};
|
||||
}
|
||||
|
||||
public fromJSON(config: ViewsTabConfig, cell: ICellModel): void {
|
||||
this.cell = config.cell;
|
||||
this.title = config.title;
|
||||
this.id = config.id;
|
||||
this.group = config.group;
|
||||
this.icon = config.icon;
|
||||
this.cellModel = cell;
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookViewModel implements INotebookView {
|
||||
private _onDeleted = new Emitter<INotebookView>();
|
||||
private _onCellVisibilityChanged = new Emitter<ICellModel>();
|
||||
@@ -31,14 +64,22 @@ export class NotebookViewModel implements INotebookView {
|
||||
constructor(
|
||||
protected _name: string,
|
||||
private _notebookViews: NotebookViewsExtension,
|
||||
private _cards: INotebookViewCard[] = [],
|
||||
guid?: string
|
||||
) {
|
||||
this.guid = guid ?? generateUuid();
|
||||
|
||||
|
||||
this._cards.forEach(card => {
|
||||
card.tabs.forEach(tab => {
|
||||
tab.cellModel = this.cells.find(c => c.cellGuid === tab.cell.guid);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public static load(guid: string, notebookViews: NotebookViewsExtension): INotebookView {
|
||||
const view = notebookViews.getViews().find(v => v.guid === guid);
|
||||
return new NotebookViewModel(view.name, notebookViews, view.guid);
|
||||
return new NotebookViewModel(view.name, notebookViews, view.cards, view.guid);
|
||||
}
|
||||
|
||||
public initialize(isNew?: boolean): void {
|
||||
@@ -46,29 +87,43 @@ export class NotebookViewModel implements INotebookView {
|
||||
this._isNew = isNew;
|
||||
}
|
||||
|
||||
const cells = this._notebookViews.notebook.cells;
|
||||
cells.forEach((cell, idx) => { this.initializeCell(cell, idx); });
|
||||
/// Initialize cards
|
||||
/// 0. Check that the cards object is created, or load them
|
||||
/// 1. Create a card per cell
|
||||
/// 2. Create a tab per cell
|
||||
/// 3. Title the tab with a sequential number i.e., Untitled Tab {1,2,3..}
|
||||
if (isNew) {
|
||||
this.initializeCards();
|
||||
}
|
||||
}
|
||||
|
||||
protected initializeCell(cell: ICellModel, idx: number) {
|
||||
let meta = this._notebookViews.getExtensionCellMetadata(cell);
|
||||
public initializeCards() {
|
||||
const cells = this._notebookViews.notebook.cells;
|
||||
|
||||
if (!meta) {
|
||||
this._notebookViews.initializeCell(cell);
|
||||
meta = this._notebookViews.getExtensionCellMetadata(cell);
|
||||
}
|
||||
|
||||
// Ensure that we are not duplicting view entries in cell metadata
|
||||
if (!meta.views.find(v => v.guid === this.guid)) {
|
||||
meta.views.push({
|
||||
guid: this.guid,
|
||||
hidden: false,
|
||||
let card: INotebookViewCard;
|
||||
cells.forEach((cell, idx) => {
|
||||
card = {
|
||||
guid: generateUuid(),
|
||||
y: idx * DEFAULT_VIEW_CARD_HEIGHT,
|
||||
x: 0,
|
||||
width: DEFAULT_VIEW_CARD_WIDTH,
|
||||
height: DEFAULT_VIEW_CARD_HEIGHT
|
||||
});
|
||||
height: DEFAULT_VIEW_CARD_HEIGHT,
|
||||
tabs: []
|
||||
};
|
||||
|
||||
this.createTab(cell, card);
|
||||
|
||||
this._cards.push(card);
|
||||
});
|
||||
}
|
||||
|
||||
protected createTab(cell: ICellModel, card: INotebookViewCard): void {
|
||||
if (card === undefined) {
|
||||
throw new Error('A card must be specified to create a tab');
|
||||
}
|
||||
|
||||
const newTab: ViewsTabConfig = { title: localize('Untitled', 'Untitled'), id: generateUuid(), group: card.guid, cell: { guid: cell.cellGuid } };
|
||||
card.tabs.push(new ViewsTab(newTab, cell));
|
||||
}
|
||||
|
||||
public cellInitialized(cell: ICellModel): boolean {
|
||||
@@ -90,13 +145,18 @@ export class NotebookViewModel implements INotebookView {
|
||||
return !this._notebookViews.viewNameIsTaken(name);
|
||||
}
|
||||
|
||||
public getCellMetadata(cell: ICellModel): INotebookViewCell {
|
||||
public getCellMetadata(cell: ICellModel): INotebookViewCard {
|
||||
const meta = this._notebookViews.getExtensionCellMetadata(cell);
|
||||
return meta?.views?.find(view => view.guid === this.guid);
|
||||
}
|
||||
|
||||
public get hiddenCells(): Readonly<ICellModel[]> {
|
||||
return this.cells.filter(cell => this.getCellMetadata(cell)?.hidden !== false);
|
||||
const allTabs = this.cards.flatMap(card => card.tabs);
|
||||
return this.cells.filter(cell => !allTabs.find(t => t.cell.guid === cell.cellGuid));
|
||||
}
|
||||
|
||||
public get cards(): INotebookViewCard[] {
|
||||
return this._cards;
|
||||
}
|
||||
|
||||
public get cells(): Readonly<ICellModel[]> {
|
||||
@@ -111,30 +171,69 @@ export class NotebookViewModel implements INotebookView {
|
||||
return this._notebookViews.notebook.cells.find(cell => cell.cellGuid === guid);
|
||||
}
|
||||
|
||||
public updateCell(cell: ICellModel, currentView: INotebookView, cellData: INotebookViewCell, override: boolean = false) {
|
||||
if (!this.cellInitialized(cell)) {
|
||||
this.initializeCell(cell, 0);
|
||||
}
|
||||
|
||||
public updateCell(cell: ICellModel, currentView: INotebookView, cellData: INotebookViewCard, override: boolean = false) {
|
||||
this._notebookViews.updateCell(cell, currentView, cellData, override);
|
||||
}
|
||||
|
||||
public insertCell(cell: ICellModel) {
|
||||
this.updateCell(cell, this, { hidden: false });
|
||||
public insertCell(cell: ICellModel): INotebookViewCard {
|
||||
let card: INotebookViewCard = {
|
||||
guid: generateUuid(),
|
||||
y: 0,
|
||||
x: 0,
|
||||
width: DEFAULT_VIEW_CARD_WIDTH,
|
||||
height: DEFAULT_VIEW_CARD_HEIGHT,
|
||||
tabs: []
|
||||
};
|
||||
|
||||
this.createTab(cell, card);
|
||||
|
||||
this._cards.push(card);
|
||||
|
||||
this._onCellVisibilityChanged.fire(cell);
|
||||
|
||||
this.save();
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
public hideCell(cell: ICellModel) {
|
||||
this.updateCell(cell, this, { hidden: true });
|
||||
this.cards.forEach((card) => {
|
||||
const updatedTabs = card.tabs.filter(t => t.cell.guid !== cell.cellGuid);
|
||||
this._notebookViews.updateCard(card, { tabs: updatedTabs }, this);
|
||||
|
||||
// If there are no tabs left in the card, delete the card
|
||||
if (!updatedTabs.length) {
|
||||
const index = this.cards.findIndex(c => c.guid === card.guid);
|
||||
const removedCard = this._cards.splice(index, 1);
|
||||
if (removedCard.length === 1) {
|
||||
this.compactCells();
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
});
|
||||
this._onCellVisibilityChanged.fire(cell);
|
||||
}
|
||||
|
||||
public moveCell(cell: ICellModel, x: number, y: number) {
|
||||
this.updateCell(cell, this, { x, y });
|
||||
public moveCard(card: INotebookViewCard, x: number, y: number) {
|
||||
this._notebookViews.updateCard(card, { x, y }, this);
|
||||
}
|
||||
|
||||
public resizeCard(card: INotebookViewCard, width?: number, height?: number) {
|
||||
let data: INotebookViewCard = {};
|
||||
|
||||
if (width) {
|
||||
data.width = width;
|
||||
}
|
||||
|
||||
if (height) {
|
||||
data.height = height;
|
||||
}
|
||||
|
||||
this._notebookViews.updateCard(card, data, this);
|
||||
}
|
||||
|
||||
public resizeCell(cell: ICellModel, width?: number, height?: number) {
|
||||
let data: INotebookViewCell = {};
|
||||
let data: INotebookViewCard = {};
|
||||
|
||||
if (width) {
|
||||
data.width = width;
|
||||
@@ -153,22 +252,21 @@ export class NotebookViewModel implements INotebookView {
|
||||
}
|
||||
|
||||
public compactCells() {
|
||||
let cellsPlaced: INotebookViewCell[] = [];
|
||||
let cardsPlaced: INotebookViewCard[] = [];
|
||||
|
||||
this.displayedCells.forEach((cell: ICellModel) => {
|
||||
const c1 = this.getCellMetadata(cell);
|
||||
this.cards.forEach((card: INotebookViewCard) => {
|
||||
|
||||
for (let i = 0; ; i++) {
|
||||
const row = i % GRID_COLUMNS;
|
||||
const column = Math.floor(i / GRID_COLUMNS);
|
||||
|
||||
if (row + c1.width > GRID_COLUMNS) {
|
||||
if (row + card.width > GRID_COLUMNS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cellsPlaced.find((c2) => cellCollides(c2, { ...c1, x: row, y: column }))) {
|
||||
this._notebookViews.updateCell(cell, this, { x: row, y: column });
|
||||
cellsPlaced.push({ ...c1, x: row, y: column });
|
||||
if (!cardsPlaced.find((c2) => cellCollides(c2, { ...card, x: row, y: column }))) {
|
||||
this._notebookViews.updateCard(card, { x: row, y: column }, this);
|
||||
cardsPlaced.push({ ...card, x: row, y: column });
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -193,6 +291,6 @@ export class NotebookViewModel implements INotebookView {
|
||||
}
|
||||
|
||||
public toJSON() {
|
||||
return { guid: this.guid, name: this._name } as NotebookViewModel;
|
||||
return { guid: this.guid, name: this._name, cards: this.cards } as INotebookView;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TabConfig } from 'sql/workbench/browser/modelComponents/tabbedPanel.component';
|
||||
import { ICellModel, INotebookModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { ViewsTab } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewModel';
|
||||
import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
export type CellChangeEventType = 'hide' | 'insert' | 'active' | 'execution' | 'update';
|
||||
@@ -13,6 +16,13 @@ export type CellChangeEvent = {
|
||||
event: CellChangeEventType
|
||||
};
|
||||
|
||||
export interface INotebookViewsExtensionUpgrade {
|
||||
readonly sourceVersion: number;
|
||||
readonly targetVersion: number;
|
||||
versionCheck(version: number): boolean;
|
||||
apply(extension: NotebookViewsExtension): void;
|
||||
}
|
||||
|
||||
export interface INotebookViews {
|
||||
onActiveViewChanged: Event<void>;
|
||||
createNewView(name?: string): INotebookView;
|
||||
@@ -29,19 +39,21 @@ export interface INotebookView {
|
||||
readonly onCellVisibilityChanged: Event<ICellModel>;
|
||||
|
||||
isNew: boolean;
|
||||
cards: INotebookViewCard[];
|
||||
cells: Readonly<ICellModel[]>;
|
||||
hiddenCells: Readonly<ICellModel[]>;
|
||||
displayedCells: Readonly<ICellModel[]>;
|
||||
name: string;
|
||||
initialize(isNew?: boolean): void;
|
||||
nameAvailable(name: string): boolean;
|
||||
getCellMetadata(cell: ICellModel): INotebookViewCell;
|
||||
getCellMetadata(cell: ICellModel): INotebookViewCard;
|
||||
hideCell(cell: ICellModel): void;
|
||||
moveCell(cell: ICellModel, x: number, y: number): void;
|
||||
moveCard(card: INotebookViewCard, x: number, y: number): void;
|
||||
compactCells();
|
||||
resizeCard(card: INotebookViewCard, width: number, height: number): void;
|
||||
resizeCell(cell: ICellModel, width: number, height: number): void;
|
||||
getCell(guid: string): Readonly<ICellModel>;
|
||||
insertCell(cell: ICellModel): void;
|
||||
insertCell(cell: ICellModel): INotebookViewCard;
|
||||
markAsViewed(): void;
|
||||
save(): void;
|
||||
delete(): void;
|
||||
@@ -49,11 +61,22 @@ export interface INotebookView {
|
||||
|
||||
export interface INotebookViewCell {
|
||||
readonly guid?: string;
|
||||
}
|
||||
|
||||
|
||||
export interface INotebookViewCard {
|
||||
readonly guid?: string;
|
||||
hidden?: boolean;
|
||||
x?: number;
|
||||
y?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
tabs?: ViewsTab[];
|
||||
activeTab?: ViewsTab;
|
||||
}
|
||||
|
||||
interface ViewsTabConfig extends TabConfig {
|
||||
cell: INotebookViewCell
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -71,7 +94,7 @@ export interface INotebookViewMetadata {
|
||||
* view at the cell level.
|
||||
*/
|
||||
export interface INotebookViewCellMetadata {
|
||||
views: INotebookViewCell[];
|
||||
views: INotebookViewCard[];
|
||||
}
|
||||
|
||||
export interface INotebookViews {
|
||||
|
||||
@@ -9,14 +9,20 @@ import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { localize } from 'vs/nls';
|
||||
import { NotebookViewModel } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewModel';
|
||||
import { NotebookExtension } from 'sql/workbench/services/notebook/browser/models/notebookExtension';
|
||||
import { INotebookView, INotebookViewCell, INotebookViewCellMetadata, INotebookViewMetadata, INotebookViews } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews';
|
||||
import { INotebookView, INotebookViewCard, INotebookViewCellMetadata, INotebookViewMetadata, INotebookViews, INotebookViewsExtensionUpgrade } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViews';
|
||||
import { NotebookViewsUpgrades } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsUpgrades';
|
||||
|
||||
|
||||
|
||||
export class NotebookViewsExtension extends NotebookExtension<INotebookViewMetadata, INotebookViewCellMetadata> implements INotebookViews {
|
||||
static readonly defaultViewName = localize('notebookView.untitledView', "Untitled View");
|
||||
static readonly extension = 'notebookviews';
|
||||
static readonly upgrades: Array<INotebookViewsExtensionUpgrade> = [
|
||||
new NotebookViewsUpgrades.V1ToV2NotebookViewsExtensionUpgrade()
|
||||
];
|
||||
|
||||
readonly maxNameIterationAttempts = 100;
|
||||
override readonly version = 1;
|
||||
override readonly version: number = 2;
|
||||
|
||||
protected _metadata: INotebookViewMetadata | undefined;
|
||||
private _initialized: boolean = false;
|
||||
@@ -32,6 +38,13 @@ export class NotebookViewsExtension extends NotebookExtension<INotebookViewMetad
|
||||
this._metadata = this.getExtensionMetadata();
|
||||
|
||||
if (this._metadata) {
|
||||
NotebookViewsExtension.upgrades.forEach(upgrade => {
|
||||
if (upgrade.versionCheck(this._metadata.version)) {
|
||||
upgrade.apply(this);
|
||||
this._metadata = this.getExtensionMetadata();
|
||||
}
|
||||
});
|
||||
|
||||
this._metadata.views = this._metadata.views.map(view => NotebookViewModel.load(view.guid, this));
|
||||
this._initialized = true;
|
||||
}
|
||||
@@ -94,16 +107,7 @@ export class NotebookViewsExtension extends NotebookExtension<INotebookViewMetad
|
||||
public removeView(guid: string) {
|
||||
let viewToRemove = this._metadata?.views.findIndex(view => view.guid === guid);
|
||||
if (viewToRemove >= 0) {
|
||||
let removedView = this._metadata?.views.splice(viewToRemove, 1);
|
||||
|
||||
// Remove view data for each cell
|
||||
if (removedView.length === 1) {
|
||||
this._notebook?.cells.forEach((cell) => {
|
||||
let meta = this.getExtensionCellMetadata(cell);
|
||||
meta.views = meta.views.filter(x => x.guid !== removedView[0].guid);
|
||||
this.setExtensionCellMetadata(cell, meta);
|
||||
});
|
||||
}
|
||||
this._metadata?.views.splice(viewToRemove, 1);
|
||||
}
|
||||
|
||||
if (guid === this._metadata?.activeView) {
|
||||
@@ -127,7 +131,7 @@ export class NotebookViewsExtension extends NotebookExtension<INotebookViewMetad
|
||||
return i <= this.maxNameIterationAttempts ? name : generateUuid();
|
||||
}
|
||||
|
||||
public updateCell(cell: ICellModel, currentView: INotebookView, cellData: INotebookViewCell, override: boolean = false) {
|
||||
public updateCell(cell: ICellModel, currentView: INotebookView, cellData: INotebookViewCard, override: boolean = false) {
|
||||
const cellMetadata = this.getExtensionCellMetadata(cell);
|
||||
if (cellMetadata) {
|
||||
const viewToUpdate = cellMetadata.views.findIndex(view => view.guid === currentView.guid);
|
||||
@@ -139,6 +143,22 @@ export class NotebookViewsExtension extends NotebookExtension<INotebookViewMetad
|
||||
}
|
||||
}
|
||||
|
||||
public updateCard(card: INotebookViewCard, cardData: INotebookViewCard, currentView: INotebookView, override: boolean = false) {
|
||||
const notebookMetadata = this.getExtensionMetadata();
|
||||
if (notebookMetadata) {
|
||||
const viewToUpdate = notebookMetadata.views.findIndex(view => view.guid === currentView.guid);
|
||||
|
||||
if (viewToUpdate >= 0) {
|
||||
const cardToUpdate = notebookMetadata.views[viewToUpdate].cards.findIndex(c => c.guid === card.guid);
|
||||
|
||||
if (cardToUpdate >= 0) {
|
||||
notebookMetadata.views[viewToUpdate].cards[cardToUpdate] = override ? cardData : { ...notebookMetadata.views[viewToUpdate].cards[cardToUpdate], ...cardData };
|
||||
this.setExtensionMetadata(this._notebook, notebookMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public get notebook(): INotebookModel {
|
||||
return this._notebook;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
|
||||
export namespace NotebookViewsUpgrades {
|
||||
export class V1ToV2NotebookViewsExtensionUpgrade {
|
||||
sourceVersion = 1;
|
||||
targetVersion = 2;
|
||||
|
||||
versionCheck(version: number): boolean {
|
||||
return version > this.targetVersion;
|
||||
}
|
||||
|
||||
apply(notebookViewsExtension: NotebookViewsExtension): void {
|
||||
const extensions = notebookViewsExtension.notebook.getMetaValue('extensions');
|
||||
const notebookviews = extensions['notebookviews'];
|
||||
const views = notebookviews['views'];
|
||||
|
||||
const newmeta = {
|
||||
version: 2,
|
||||
activeView: null,
|
||||
views: []
|
||||
};
|
||||
|
||||
views.forEach((view, viewIdx) => {
|
||||
const viewData = {
|
||||
guid: view.guid,
|
||||
name: view.name,
|
||||
cards: []
|
||||
};
|
||||
|
||||
const cells = notebookViewsExtension.notebook.cells;
|
||||
cells.forEach((cell) => {
|
||||
const cellmeta = cell.metadata['extensions']?.['notebookviews']?.['views']?.[viewIdx];
|
||||
if (cellmeta && !cellmeta?.hidden) {
|
||||
const card = {
|
||||
guid: generateUuid(),
|
||||
y: cellmeta.y,
|
||||
x: cellmeta.x,
|
||||
width: cellmeta.width,
|
||||
height: cellmeta.height,
|
||||
tabs: [{
|
||||
title: 'Untitled',
|
||||
guid: generateUuid(),
|
||||
cell: {
|
||||
guid: cell.cellGuid
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
viewData.cards.push(card);
|
||||
}
|
||||
});
|
||||
|
||||
newmeta.views.push(viewData);
|
||||
});
|
||||
|
||||
notebookViewsExtension.setExtensionMetadata(notebookViewsExtension.notebook, newmeta);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user