jupyter-specta 0.3.3 → 0.3.5

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.
@@ -12,6 +12,8 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  import React from 'react';
13
13
  export const GearIcon = (_a) => {
14
14
  var props = __rest(_a, []);
15
- return (React.createElement("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", height: "24px", viewBox: "0 -960 960 960", width: "24px" }, props),
16
- React.createElement("path", { d: "m370-80-16-128q-13-5-24.5-12T307-235l-119 50L78-375l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78-585l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12L590-80H370Zm70-80h79l14-106q31-8 57.5-23.5T639-327l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533-694l-13-106h-79l-14 106q-31 8-57.5 23.5T321-633l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427-266l13 106Zm42-180q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342-480q0 58 40.5 99t99.5 41Zm-2-140Z" })));
15
+ return (React.createElement("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, props),
16
+ React.createElement("path", { d: "M4 5h16" }),
17
+ React.createElement("path", { d: "M4 12h16" }),
18
+ React.createElement("path", { d: "M4 19h16" })));
17
19
  };
@@ -34,7 +34,7 @@ export class NotebookGridWidgetFactory extends ABCWidgetFactory {
34
34
  else {
35
35
  // Specta app, add topbar to layout
36
36
  topbar.id = 'specta-topbar-widget';
37
- this._shell.add(topbar, 'top');
37
+ this._shell.add(topbar, 'top', { rank: 100 });
38
38
  }
39
39
  }
40
40
  else if (isSpecta) {
@@ -50,12 +50,10 @@ export const spectaOpener = {
50
50
  ],
51
51
  activate: async (app, docManager, defaultBrowser) => {
52
52
  const urlParams = new URLSearchParams(window.location.search);
53
- const path = urlParams.get('path');
54
53
  if (!isSpectaApp()) {
54
+ // Not a specta app
55
+ const path = urlParams.get('specta-path');
55
56
  if (!path) {
56
- app.restored.then(async () => {
57
- await app.commands.execute('application:reset-layout');
58
- });
59
57
  return;
60
58
  }
61
59
  app.restored.then(async () => {
@@ -63,6 +61,7 @@ export const spectaOpener = {
63
61
  if (PathExt.extname(path) === '.ipynb') {
64
62
  const commands = app.commands;
65
63
  const spectaConfig = readSpectaConfig({});
64
+ console.log('spectaConfig', spectaConfig);
66
65
  await configLabLayout({
67
66
  config: spectaConfig.labConfig,
68
67
  labShell,
@@ -74,10 +73,11 @@ export const spectaOpener = {
74
73
  }
75
74
  }
76
75
  });
77
- // Not a specta app
78
76
  return;
79
77
  }
80
78
  else {
79
+ // Specta app
80
+ const path = urlParams.get('path');
81
81
  if (!path) {
82
82
  const browser = createFileBrowser({ defaultBrowser });
83
83
  app.shell.add(browser, 'main', { rank: 100 });
@@ -6,6 +6,7 @@ import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
6
6
  import { ServiceManager } from '@jupyterlab/services';
7
7
  import { IExecuteReplyMsg } from '@jupyterlab/services/lib/kernel/messages';
8
8
  import { SpectaCellOutput } from './specta_cell_output';
9
+ import { ISignal } from '@lumino/signaling';
9
10
  export declare class AppModel {
10
11
  private options;
11
12
  constructor(options: AppModel.IOptions);
@@ -13,6 +14,7 @@ export declare class AppModel {
13
14
  * Whether the handler is disposed.
14
15
  */
15
16
  get isDisposed(): boolean;
17
+ get fileChanged(): ISignal<this, CellList>;
16
18
  dispose(): void;
17
19
  get rendermime(): IRenderMimeRegistry;
18
20
  get cells(): CellList | undefined;
@@ -27,6 +29,7 @@ export declare class AppModel {
27
29
  private _isDisposed;
28
30
  private _manager;
29
31
  private _kernelPreference;
32
+ private _fileChanged;
30
33
  }
31
34
  export declare namespace AppModel {
32
35
  interface IOptions {
@@ -3,10 +3,12 @@ import { OutputAreaModel, SimplifiedOutputArea } from '@jupyterlab/outputarea';
3
3
  import { createNotebookContext, createNotebookPanel } from './create_notebook_panel';
4
4
  import { SpectaCellOutput } from './specta_cell_output';
5
5
  import { emitResizeEvent, readCellConfig } from './tool';
6
+ import { Signal } from '@lumino/signaling';
6
7
  export class AppModel {
7
8
  constructor(options) {
8
9
  this.options = options;
9
10
  this._isDisposed = false;
11
+ this._fileChanged = new Signal(this);
10
12
  this._notebookModelJson = options.context.model.toJSON();
11
13
  this._kernelPreference = {
12
14
  shouldStart: true,
@@ -17,6 +19,9 @@ export class AppModel {
17
19
  language: options.context.model.defaultKernelLanguage
18
20
  };
19
21
  this._manager = options.manager;
22
+ options.context.fileChanged.connect(e => {
23
+ this._fileChanged.emit(e.model.cells);
24
+ });
20
25
  }
21
26
  /**
22
27
  * Whether the handler is disposed.
@@ -24,6 +29,9 @@ export class AppModel {
24
29
  get isDisposed() {
25
30
  return this._isDisposed;
26
31
  }
32
+ get fileChanged() {
33
+ return this._fileChanged;
34
+ }
27
35
  dispose() {
28
36
  var _a, _b;
29
37
  if (this.isDisposed) {
@@ -1,7 +1,9 @@
1
+ import { CellList } from '@jupyterlab/notebook';
1
2
  import { Message } from '@lumino/messaging';
2
3
  import { Panel } from '@lumino/widgets';
4
+ import { SpectaCellOutput } from './specta_cell_output';
3
5
  import { AppModel } from './specta_model';
4
- import { ISpectaAppConfig, ISpectaLayoutRegistry } from './token';
6
+ import { ISpectaAppConfig, ISpectaLayout, ISpectaLayoutRegistry } from './token';
5
7
  export declare class AppWidget extends Panel {
6
8
  constructor(options: AppWidget.IOptions);
7
9
  /**
@@ -10,8 +12,12 @@ export declare class AppWidget extends Panel {
10
12
  get ready(): Promise<void>;
11
13
  get model(): AppModel;
12
14
  addSpinner(): void;
15
+ removeSpinner(): void;
13
16
  dispose(): void;
17
+ generateOutputs(cellList?: CellList): Promise<SpectaCellOutput[]>;
18
+ getLayout(): ISpectaLayout;
14
19
  render(): Promise<void>;
20
+ rerender(newCells: CellList): Promise<void>;
15
21
  protected onCloseRequest(msg: Message): void;
16
22
  private _onSelectedLayoutChanged;
17
23
  private _model;
@@ -29,9 +29,13 @@ export class AppWidget extends Panel {
29
29
  console.log(`Waiting for ${waitTime}ms`);
30
30
  }
31
31
  await new Promise(resolve => setTimeout(resolve, parseInt(waitTime + '')));
32
- this.render().catch(console.error).then(emitResizeEvent);
32
+ await this.render();
33
+ emitResizeEvent();
33
34
  });
34
35
  this._layoutRegistry.selectedLayoutChanged.connect(this._onSelectedLayoutChanged, this);
36
+ this._model.fileChanged.connect((_, newCells) => {
37
+ this.rerender(newCells);
38
+ });
35
39
  }
36
40
  /**
37
41
  * A promise that is fulfilled when the model is ready.
@@ -55,6 +59,18 @@ export class AppWidget extends Panel {
55
59
  loaderHost.node.appendChild(text);
56
60
  this.addWidget(loaderHost);
57
61
  }
62
+ removeSpinner() {
63
+ if (this._loaderHost) {
64
+ this._loaderHost.node.style.opacity = '0';
65
+ setTimeout(() => {
66
+ var _a;
67
+ (_a = this.layout) === null || _a === void 0 ? void 0 : _a.removeWidget(this._loaderHost);
68
+ }, 100);
69
+ }
70
+ else {
71
+ hideAppLoadingIndicator();
72
+ }
73
+ }
58
74
  dispose() {
59
75
  if (this.isDisposed) {
60
76
  return;
@@ -62,10 +78,11 @@ export class AppWidget extends Panel {
62
78
  this._model.dispose();
63
79
  super.dispose();
64
80
  }
65
- async render() {
66
- var _a, _b, _c, _d, _e;
67
- const cellList = (_a = this._model.cells) !== null && _a !== void 0 ? _a : [];
68
- const layout = (_c = (_b = this._spectaAppConfig) === null || _b === void 0 ? void 0 : _b.defaultLayout) !== null && _c !== void 0 ? _c : 'default';
81
+ async generateOutputs(cellList) {
82
+ const outs = [];
83
+ if (!cellList) {
84
+ return outs;
85
+ }
69
86
  for (const cell of cellList) {
70
87
  const src = cell.sharedModel.source;
71
88
  if (src.length === 0) {
@@ -73,29 +90,53 @@ export class AppWidget extends Panel {
73
90
  }
74
91
  const el = this._model.createCell(cell);
75
92
  this._model.executeCell(cell, el);
76
- this._outputs.push(el);
93
+ outs.push(el);
77
94
  }
78
- const readyCallback = async () => {
79
- if (this._loaderHost) {
80
- this._loaderHost.node.style.opacity = '0';
81
- setTimeout(() => {
82
- var _a;
83
- (_a = this.layout) === null || _a === void 0 ? void 0 : _a.removeWidget(this._loaderHost);
84
- }, 100);
85
- }
86
- else {
87
- hideAppLoadingIndicator();
88
- }
89
- };
90
- const spectaLayout = (_d = this._layoutRegistry.get(layout)) !== null && _d !== void 0 ? _d : this._layoutRegistry.getDefaultLayout();
95
+ return outs;
96
+ }
97
+ getLayout() {
98
+ var _a, _b, _c;
99
+ const layout = (_b = (_a = this._spectaAppConfig) === null || _a === void 0 ? void 0 : _a.defaultLayout) !== null && _b !== void 0 ? _b : 'default';
100
+ const spectaLayout = (_c = this._layoutRegistry.get(layout)) !== null && _c !== void 0 ? _c : this._layoutRegistry.getDefaultLayout();
101
+ return spectaLayout;
102
+ }
103
+ async render() {
104
+ var _a;
105
+ const cellList = this._model.cells;
106
+ this._outputs = await this.generateOutputs(cellList);
107
+ const spectaLayout = this.getLayout();
108
+ const readyCallback = async () => this.removeSpinner();
91
109
  await spectaLayout.render({
92
110
  host: this._host,
93
111
  items: this._outputs,
94
- notebook: (_e = this._model.context) === null || _e === void 0 ? void 0 : _e.model.toJSON(),
112
+ notebook: (_a = this._model.context) === null || _a === void 0 ? void 0 : _a.model.toJSON(),
95
113
  readyCallback,
96
114
  spectaConfig: this._spectaAppConfig
97
115
  });
98
116
  }
117
+ async rerender(newCells) {
118
+ var _a;
119
+ this.addSpinner();
120
+ for (const element of this._outputs) {
121
+ element.dispose();
122
+ }
123
+ const currentEls = [...this._host.widgets];
124
+ currentEls.forEach(el => {
125
+ var _a;
126
+ (_a = this._host.layout) === null || _a === void 0 ? void 0 : _a.removeWidget(el);
127
+ });
128
+ this._outputs = await this.generateOutputs(newCells);
129
+ const spectaLayout = this.getLayout();
130
+ await spectaLayout.render({
131
+ host: this._host,
132
+ items: this._outputs,
133
+ notebook: (_a = this._model.context) === null || _a === void 0 ? void 0 : _a.model.toJSON(),
134
+ readyCallback: async () => { },
135
+ spectaConfig: this._spectaAppConfig
136
+ });
137
+ emitResizeEvent();
138
+ this.removeSpinner();
139
+ }
99
140
  onCloseRequest(msg) {
100
141
  this._model.dispose();
101
142
  super.onCloseRequest(msg);
package/lib/tool.js CHANGED
@@ -92,6 +92,7 @@ export function mergeObjects(...objects) {
92
92
  for (const obj of objects) {
93
93
  for (const [key, value] of Object.entries(obj)) {
94
94
  if (value !== null && value !== undefined) {
95
+ console.log('setting', key, value);
95
96
  result[key] = value;
96
97
  }
97
98
  }
@@ -107,7 +108,7 @@ export function isSpectaApp() {
107
108
  return !!document.querySelector('meta[name="specta-config"]');
108
109
  }
109
110
  export function readSpectaConfig({ nbMetadata, nbPath }) {
110
- var _a;
111
+ var _a, _b;
111
112
  let rawConfig = PageConfig.getOption('spectaConfig');
112
113
  if (!rawConfig || rawConfig.length === 0) {
113
114
  rawConfig = '{}';
@@ -117,10 +118,12 @@ export function readSpectaConfig({ nbMetadata, nbPath }) {
117
118
  if (paths && paths.length > 1) {
118
119
  pathWithoutDrive = paths[1];
119
120
  }
120
- const _b = JSON.parse(rawConfig), { perFileConfig } = _b, globalConfig = __rest(_b, ["perFileConfig"]);
121
+ const _c = JSON.parse(rawConfig), { perFileConfig } = _c, globalConfig = __rest(_c, ["perFileConfig"]);
121
122
  let spectaConfig = Object.assign({}, (globalConfig !== null && globalConfig !== void 0 ? globalConfig : {}));
123
+ let fileConfig = {};
122
124
  if (perFileConfig && pathWithoutDrive && perFileConfig[pathWithoutDrive]) {
123
- spectaConfig = Object.assign(Object.assign({}, spectaConfig), perFileConfig[pathWithoutDrive]);
125
+ fileConfig = perFileConfig[pathWithoutDrive];
126
+ spectaConfig = Object.assign(Object.assign({}, spectaConfig), fileConfig);
124
127
  }
125
128
  const spectaMetadata = JSON.parse(JSON.stringify((_a = nbMetadata === null || nbMetadata === void 0 ? void 0 : nbMetadata.specta) !== null && _a !== void 0 ? _a : {}));
126
129
  if (spectaMetadata.hideTopbar === 'Yes') {
@@ -132,11 +135,29 @@ export function readSpectaConfig({ nbMetadata, nbPath }) {
132
135
  else {
133
136
  if (spectaConfig.hideTopbar === null ||
134
137
  spectaConfig.hideTopbar === undefined) {
135
- // Hide topbar by default if not specified in the global config
138
+ // Show topbar by default if not specified in the global config
136
139
  // and notebook metadata
137
- spectaMetadata.hideTopbar = true;
140
+ spectaMetadata.hideTopbar = false;
141
+ }
142
+ }
143
+ if (!spectaMetadata.hideTopbar) {
144
+ spectaMetadata.topBar = {};
145
+ if (spectaMetadata.topbarTitle && spectaMetadata.topbarTitle !== '') {
146
+ spectaMetadata.topBar.title = spectaMetadata.topbarTitle;
147
+ delete spectaMetadata.topbarTitle;
148
+ }
149
+ else if (!((_b = fileConfig === null || fileConfig === void 0 ? void 0 : fileConfig.topBar) === null || _b === void 0 ? void 0 : _b.title)) {
150
+ spectaMetadata.topBar.title = pathWithoutDrive;
151
+ }
152
+ if (spectaMetadata.topbarThemeToggle === 'No') {
153
+ spectaMetadata.topBar.themeToggle = false;
154
+ }
155
+ else {
156
+ spectaMetadata.topBar.themeToggle = true;
138
157
  }
158
+ delete spectaMetadata.topbarThemeToggle;
139
159
  }
160
+ // merge spectaConfig and spectaMetadata (spectaMetadata has higher priority
140
161
  return mergeObjects(spectaConfig, spectaMetadata);
141
162
  }
142
163
  export function readCellConfig(cell) {
@@ -7,7 +7,7 @@ export function TopbarElement(props) {
7
7
  const config = React.useMemo(() => {
8
8
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
9
9
  return {
10
- background: (_b = (_a = props.config) === null || _a === void 0 ? void 0 : _a.background) !== null && _b !== void 0 ? _b : 'var(--jp-layout-color2)',
10
+ background: (_b = (_a = props.config) === null || _a === void 0 ? void 0 : _a.background) !== null && _b !== void 0 ? _b : 'var(--jp-layout-color1)',
11
11
  title: (_d = (_c = props.config) === null || _c === void 0 ? void 0 : _c.title) !== null && _d !== void 0 ? _d : 'Specta',
12
12
  themeToggle: Boolean((_e = props.config) === null || _e === void 0 ? void 0 : _e.themeToggle),
13
13
  kernelActivity: Boolean((_f = props.config) === null || _f === void 0 ? void 0 : _f.kernelActivity),
@@ -30,7 +30,8 @@ export function TopbarElement(props) {
30
30
  document.addEventListener('mousedown', handleClickOutside);
31
31
  return () => document.removeEventListener('mousedown', handleClickOutside);
32
32
  }, []);
33
- return (React.createElement("div", { className: "specta-topbar", style: { background: (_a = config.background) !== null && _a !== void 0 ? _a : 'var(--jp-layout-color2)' } },
33
+ console.log('config.background ', config.background);
34
+ return (React.createElement("div", { className: "specta-topbar", style: { background: (_a = config.background) !== null && _a !== void 0 ? _a : 'var(--jp-layout-color1)' } },
34
35
  React.createElement("div", { className: "specta-topbar-left" },
35
36
  React.createElement("div", { className: "specta-topbar-icon-container" }, config.icon && React.createElement("img", { style: { height: '100%' }, src: config.icon })),
36
37
  React.createElement("div", { className: "specta-topbar-title", style: { color: (_b = config.textColor) !== null && _b !== void 0 ? _b : 'var(--jp-ui-font-color1)' } }, config.title)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyter-specta",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/trungleduc/specta",
@@ -9,6 +9,17 @@
9
9
  "metadataSchema": {
10
10
  "type": "object",
11
11
  "properties": {
12
+ "/specta/topbarTitle": {
13
+ "title": "Top Bar Title",
14
+ "type": "string",
15
+ "default": ""
16
+ },
17
+ "/specta/topbarThemeToggle": {
18
+ "title": "Top Bar Theme Toggle",
19
+ "type": "string",
20
+ "enum": ["Yes", "No"],
21
+ "default": "Yes"
22
+ },
12
23
  "/specta/defaultLayout": {
13
24
  "title": "Page layout",
14
25
  "type": "string",
@@ -55,6 +66,14 @@
55
66
  "/specta/slidesTheme": {
56
67
  "metadataLevel": "notebook",
57
68
  "writeDefault": false
69
+ },
70
+ "/specta/topbarTitle": {
71
+ "metadataLevel": "notebook",
72
+ "writeDefault": false
73
+ },
74
+ "/specta/topbarThemeToggle": {
75
+ "metadataLevel": "notebook",
76
+ "writeDefault": false
58
77
  }
59
78
  }
60
79
  }
package/style/base.css CHANGED
@@ -69,11 +69,13 @@
69
69
  }
70
70
 
71
71
  #specta-top-panel {
72
- min-height: 36px;
72
+ min-height: 28px;
73
73
  display: flex;
74
74
  box-shadow: unset !important;
75
75
  z-index: 100;
76
76
  contain: unset !important;
77
+ border-bottom: var(--jp-border-width) solid var(--jp-border-color0);
78
+ background: var(--jp-layout-color1);
77
79
  }
78
80
 
79
81
  .specta-topbar-element {
@@ -84,7 +86,7 @@
84
86
  .specta-topbar {
85
87
  display: flex;
86
88
  flex-direction: row;
87
- height: 36px;
89
+ height: 28px;
88
90
  width: calc(100% + 20px);
89
91
  border-bottom: solid 0.5px var(--jp-border-color1);
90
92
  gap: 10px;
@@ -128,7 +130,9 @@
128
130
  border-radius: 50% !important;
129
131
  transition: background 0.2s ease;
130
132
  background: transparent;
133
+ padding: 0 !important;
131
134
  }
135
+
132
136
  .specta-icon-button:hover {
133
137
  background-color: var('--jp-layout-color3');
134
138
  }
package/style/style.css CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  .specta-document-viewer #jp-main-content-panel jp-toolbar:first-of-type {
8
8
  display: none;
9
+ height: 0px !important;
9
10
  }
10
11
 
11
12
  .specta-file-browser {
@@ -28,6 +29,6 @@
28
29
  }
29
30
 
30
31
  .specta-main-content-panel {
31
- padding-left: 5px;
32
- padding-right: 5px;
32
+ padding-left: 0px;
33
+ padding-right: 0px;
33
34
  }