jupyter-specta 0.1.6 → 0.1.8

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/README.md CHANGED
@@ -43,7 +43,55 @@ Once installed, you can build your JupyterLite app, a `specta` app will be inclu
43
43
  jupyter lite build
44
44
  ```
45
45
 
46
- Then serve the contents of the output directory (by default `./_output`) using any static file server. You can access the `specta` app at the `/specta/` path.
46
+ Then serve the contents of the output directory (by default `./_output`) using any static file server. You can access the `Specta` app at the `/specta/` path.
47
+
48
+ ## Specta Configuration
49
+
50
+ ### Top-level configuration
51
+
52
+ Specta can be configured using the typicall JupyterLite configuration file: `jupyter-lite.json`. You can add a `spectaConfig` key to the `jupyter-config-data` section of this file to customize the Specta app.
53
+
54
+ The following options are available:
55
+
56
+ - `defaultLayout`: The default layout when opening a file.
57
+ - `hideTopbar`: Boolean flag to show or hide the top bar.
58
+ - `topBar`: Configuration for the top bar.
59
+
60
+ ```json
61
+ "topBar": {
62
+ "icon": "url to the icon file, it's shown on the left of the top bar",
63
+ "title": "Title on the left of the top bar",
64
+ "themeToggle": "Boolean flag to show/hide the theme selector",
65
+ "textColor": "Color of the text on the top bar",
66
+ "background": "Background color of the top bar"
67
+ },
68
+ ```
69
+
70
+ - `perFileConfig`: an object with key is the file path and value is the above configuration, it's used to have different layout/top bar config for each files, for example:
71
+
72
+ ```json
73
+ "perFileConfig": {
74
+ "blog.ipynb": {
75
+ "hideTopbar": false,
76
+ "defaultLayout": "article",
77
+ "topBar": {
78
+ "title": "My blog",
79
+ "themeToggle": false
80
+ }
81
+ }
82
+ }
83
+ ```
84
+
85
+ ### Notebook specific configuration
86
+
87
+ By default, when you open a notebook in Specta, all code cells are hidden, and placeholder skeletons are shown instead at the position of the cell. You can configure the visibility of each cell by using the Specta cell metadata toolbar.
88
+
89
+ ![Cell toolbar](./docs/images/specta-config.jpg)
90
+
91
+ By opening the `Property Inspector` panel of JupyterLab and selecting the `Specta Cell Config` section, you can change the visibility of each cell as follows:
92
+
93
+ - `Show cell source`: use this toggle to show or hide the cell source code. Default to `false`
94
+ - `Show output placeholder`: use this toggle to show or hide the output skeleton. Default to `true`
47
95
 
48
96
  ## Try it online!
49
97
 
package/lib/app.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { JupyterFrontEnd, createRendermimePlugins } from '@jupyterlab/application';
2
2
  import { SpectaShell } from './shell';
3
3
  import { PageConfig } from '@jupyterlab/coreutils';
4
+ import { Base64ModelFactory } from '@jupyterlab/docregistry';
4
5
  export class SpectaApp extends JupyterFrontEnd {
5
6
  constructor(options) {
6
7
  var _a;
@@ -17,6 +18,7 @@ export class SpectaApp extends JupyterFrontEnd {
17
18
  * The version of the application.
18
19
  */
19
20
  this.version = '1.0.0';
21
+ this.docRegistry.addModelFactory(new Base64ModelFactory());
20
22
  if (options.mimeExtensions) {
21
23
  for (const plugin of createRendermimePlugins(options.mimeExtensions)) {
22
24
  this.registerPlugin(plugin);
@@ -35,10 +35,6 @@ export class NotebookGridWidgetFactory extends ABCWidgetFactory {
35
35
  // Specta app, add topbar to layout
36
36
  topbar.id = 'specta-topbar-widget';
37
37
  this._shell.add(topbar, 'top');
38
- if (topbar.parent) {
39
- topbar.parent.node.style.boxShadow =
40
- 'rgba(0 0 0 / 20%) 0 2px 4px -1px, rgba(0 0 0 / 14%) 0 4px 5px 0, rgba(0 0 0 / 12%) 0 1px 10px 0';
41
- }
42
38
  }
43
39
  }
44
40
  else if (isSpecta) {
@@ -6,6 +6,7 @@ import { IDefaultFileBrowser } from '@jupyterlab/filebrowser';
6
6
  import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook';
7
7
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
8
8
  import { IKernelSpecManager } from '@jupyterlab/services';
9
+ import { Widget } from '@lumino/widgets';
9
10
  import { ISpectaDocTracker, ISpectaLayoutRegistry } from '../token';
10
11
  import { createFileBrowser, hideAppLoadingIndicator, isSpectaApp, registerDocumentFactory } from '../tool';
11
12
  const activate = (app, rendermime, tracker, editorServices, contentFactory, spectaLayoutRegistry, themeManager) => {
@@ -70,7 +71,7 @@ export const spectaOpener = {
70
71
  else {
71
72
  let count = 0;
72
73
  const tryOpen = () => {
73
- const widget = docManager.openOrReveal(path);
74
+ const widget = docManager.openOrReveal(path, 'default');
74
75
  if (widget) {
75
76
  app.shell.add(widget, 'main');
76
77
  hideAppLoadingIndicator();
@@ -79,7 +80,10 @@ export const spectaOpener = {
79
80
  count++;
80
81
  if (count > 10) {
81
82
  console.error('Failed to open file', path);
82
- //TODO Open in text editor?
83
+ const widget = new Widget();
84
+ widget.node.innerHTML = `<h2 style="text-align: center; margin-top: 200px;">Failed to open file ${path}</h2>`;
85
+ app.shell.add(widget, 'main');
86
+ hideAppLoadingIndicator();
83
87
  return;
84
88
  }
85
89
  setTimeout(tryOpen, 100);
package/lib/shell.js CHANGED
@@ -33,6 +33,7 @@ export class SpectaShell extends Widget {
33
33
  rootLayout.addWidget(topHandler.panel);
34
34
  const hboxPanel = (this._mainPanel = new BoxPanel());
35
35
  hboxPanel.id = 'jp-main-content-panel';
36
+ hboxPanel.addClass('specta-main-content-panel');
36
37
  hboxPanel.direction = 'top-to-bottom';
37
38
  BoxLayout.setStretch(hboxPanel, 1);
38
39
  rootLayout.addWidget(hboxPanel);
package/lib/tool.js CHANGED
@@ -10,7 +10,6 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import { PageConfig, URLExt } from '@jupyterlab/coreutils';
13
- import { NotebookModelFactory } from '@jupyterlab/notebook';
14
13
  import { NotebookGridWidgetFactory } from './document/factory';
15
14
  import { SpectaWidgetFactory } from './specta_widget_factory';
16
15
  export function registerDocumentFactory(options) {
@@ -27,26 +26,14 @@ export function registerDocumentFactory(options) {
27
26
  const widgetFactory = new NotebookGridWidgetFactory({
28
27
  name: factoryName,
29
28
  modelName: 'notebook',
30
- fileTypes: ['ipynb'],
31
- spectaWidgetFactory,
29
+ fileTypes: ['notebook'],
32
30
  shell: app.shell,
31
+ spectaWidgetFactory,
33
32
  themeManager,
34
33
  spectaLayoutRegistry
35
34
  });
36
35
  // Registering the widget factory
37
36
  app.docRegistry.addWidgetFactory(widgetFactory);
38
- // Creating and registering the model factory for our custom DocumentModelAdd commentMore actions
39
- const modelFactory = new NotebookModelFactory({});
40
- app.docRegistry.addModelFactory(modelFactory);
41
- // register the filetype
42
- app.docRegistry.addFileType({
43
- name: 'ipynb',
44
- displayName: 'IPYNB',
45
- mimeTypes: ['text/json'],
46
- extensions: ['.ipynb', '.IPYNB'],
47
- fileFormat: 'json',
48
- contentType: 'notebook'
49
- });
50
37
  widgetFactory.widgetCreated.connect((sender, widget) => {
51
38
  widget.context.pathChanged.connect(() => {
52
39
  spectaTracker.save(widget);
@@ -109,10 +96,15 @@ export function readSpectaConfig({ nbMetadata, nbPath }) {
109
96
  if (!rawConfig || rawConfig.length === 0) {
110
97
  rawConfig = '{}';
111
98
  }
99
+ let pathWithoutDrive = nbPath;
100
+ const paths = nbPath === null || nbPath === void 0 ? void 0 : nbPath.split(':');
101
+ if (paths && paths.length > 1) {
102
+ pathWithoutDrive = paths[1];
103
+ }
112
104
  const _b = JSON.parse(rawConfig), { perFileConfig } = _b, globalConfig = __rest(_b, ["perFileConfig"]);
113
105
  let spectaConfig = Object.assign({}, (globalConfig !== null && globalConfig !== void 0 ? globalConfig : {}));
114
- if (perFileConfig && nbPath && perFileConfig[nbPath]) {
115
- spectaConfig = Object.assign(Object.assign({}, spectaConfig), perFileConfig[nbPath]);
106
+ if (perFileConfig && pathWithoutDrive && perFileConfig[pathWithoutDrive]) {
107
+ spectaConfig = Object.assign(Object.assign({}, spectaConfig), perFileConfig[pathWithoutDrive]);
116
108
  }
117
109
  const spectaMetadata = ((_a = nbMetadata === null || nbMetadata === void 0 ? void 0 : nbMetadata.specta) !== null && _a !== void 0 ? _a : {});
118
110
  return Object.assign(Object.assign({}, spectaConfig), spectaMetadata);
@@ -30,9 +30,5 @@ export const topbarPlugin = {
30
30
  widget.id = 'specta-topbar-widget';
31
31
  widget.addClass('specta-topbar-element');
32
32
  app.shell.add(widget, 'top');
33
- if (widget.parent) {
34
- widget.parent.node.style.boxShadow =
35
- 'rgba(0 0 0 / 20%) 0 2px 4px -1px, rgba(0 0 0 / 14%) 0 4px 5px 0, rgba(0 0 0 / 12%) 0 1px 10px 0';
36
- }
37
33
  }
38
34
  };
@@ -5,13 +5,14 @@ import { SettingContent } from './settingDialog';
5
5
  export function TopbarElement(props) {
6
6
  var _a, _b;
7
7
  const config = React.useMemo(() => {
8
- var _a, _b, _c, _d, _e, _f, _g, _h;
8
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
9
9
  return {
10
10
  background: (_b = (_a = props.config) === null || _a === void 0 ? void 0 : _a.background) !== null && _b !== void 0 ? _b : 'var(--jp-layout-color2)',
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),
14
- textColor: (_h = (_g = props.config) === null || _g === void 0 ? void 0 : _g.textColor) !== null && _h !== void 0 ? _h : 'var(--jp-ui-font-color1)'
14
+ textColor: (_h = (_g = props.config) === null || _g === void 0 ? void 0 : _g.textColor) !== null && _h !== void 0 ? _h : 'var(--jp-ui-font-color1)',
15
+ icon: (_j = props.config) === null || _j === void 0 ? void 0 : _j.icon
15
16
  };
16
17
  }, [props.config]);
17
18
  const [open, setOpen] = useState(false);
@@ -34,7 +35,7 @@ export function TopbarElement(props) {
34
35
  React.createElement("div", null, config.icon && React.createElement("img", { style: { width: '50px' }, src: config.icon })),
35
36
  React.createElement("div", { className: "specta-topbar-title", style: { color: (_b = config.textColor) !== null && _b !== void 0 ? _b : 'var(--jp-ui-font-color1)' } }, config.title)),
36
37
  React.createElement("div", { className: "specta-topbar-right" },
37
- React.createElement(IconButton, { ref: buttonRef, onClick: () => setOpen(!open), icon: React.createElement(GearIcon, { fill: "var(--jp-ui-font-color2)", height: 24, width: 24 }) }),
38
+ React.createElement(IconButton, { ref: buttonRef, onClick: () => setOpen(!open), icon: React.createElement(GearIcon, { fill: "var(--jp-ui-font-color2)", height: 23, width: 23 }) }),
38
39
  open && (React.createElement("div", { ref: dialogRef, className: "jp-Dialog-content specta-config-dialog" },
39
40
  React.createElement("div", { className: "specta-config-arrow" }),
40
41
  React.createElement(SettingContent, { themeManager: props.themeManager, layoutRegistry: props.layoutRegistry }))))));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyter-specta",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/trungleduc/specta",
@@ -60,6 +60,8 @@
60
60
  "@jupyterlab/docregistry": "^4.4.2",
61
61
  "@jupyterlab/filebrowser": "^4.4.2",
62
62
  "@jupyterlab/filebrowser-extension": "^4.4.2",
63
+ "@jupyterlab/imageviewer": "^4.4.2",
64
+ "@jupyterlab/imageviewer-extension": "^4.4.2",
63
65
  "@jupyterlab/json-extension": "^4.4.2",
64
66
  "@jupyterlab/logconsole": "^4.4.2",
65
67
  "@jupyterlab/mainmenu": "^4.4.2",
package/style/base.css CHANGED
@@ -55,10 +55,11 @@
55
55
 
56
56
  .jp-specta-notebook-panel {
57
57
  overflow: auto;
58
+ padding: 0 5px 5px 5px;
58
59
  }
59
60
 
60
61
  #specta-top-panel {
61
- min-height: 40px;
62
+ min-height: 36px;
62
63
  display: flex;
63
64
  box-shadow: unset !important;
64
65
  z-index: 100;
@@ -73,12 +74,9 @@
73
74
  .specta-topbar {
74
75
  display: flex;
75
76
  flex-direction: row;
76
- height: 40px;
77
+ height: 36px;
77
78
  width: calc(100% + 20px);
78
- box-shadow:
79
- rgba(0 0 0 / 20%) 0 2px 4px -1px,
80
- rgba(0 0 0 / 14%) 0 4px 5px 0,
81
- rgba(0 0 0 / 12%) 0 1px 10px 0;
79
+ border-bottom: solid 0.5px var(--jp-border-color1);
82
80
  gap: 10px;
83
81
  align-items: center;
84
82
  justify-content: space-between;
@@ -97,8 +95,8 @@
97
95
  }
98
96
 
99
97
  .specta-topbar-title {
100
- line-height: 40px;
101
- font-size: 1.5rem;
98
+ line-height: 36px;
99
+ font-size: 1.25rem;
102
100
  }
103
101
 
104
102
  .specta-topbar-theme {
package/style/style.css CHANGED
@@ -4,7 +4,7 @@
4
4
  background: var(--jp-layout-color1);
5
5
  }
6
6
 
7
- .specta-document-viewer #jp-main-content-panel jp-toolbar {
7
+ .specta-document-viewer #jp-main-content-panel jp-toolbar:first-of-type {
8
8
  display: none;
9
9
  }
10
10
 
@@ -26,3 +26,8 @@
26
26
  background: var(--jp-layout-color1);
27
27
  box-shadow: var(--jp-elevation-z4);
28
28
  }
29
+
30
+ .specta-main-content-panel {
31
+ padding-left: 5px;
32
+ padding-right: 5px;
33
+ }