jupyter-specta 0.3.4 → 0.4.0

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
@@ -52,6 +52,8 @@ jupyter lite build
52
52
 
53
53
  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.
54
54
 
55
+ If you want to disable specta loading spinner, you can set the environment variable `SPECTA_NO_LOADING_SCREEN` to `1`before calling jupyterlite build command
56
+
55
57
  ## Specta Configuration
56
58
 
57
59
  ### Available layouts
@@ -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) {
@@ -1,5 +1,6 @@
1
1
  import { ILabShell, JupyterFrontEndPlugin } from '@jupyterlab/application';
2
2
  import { IWidgetTracker } from '@jupyterlab/apputils';
3
- import { ISpectaShell } from '../token';
3
+ import { ISpectaShell, ISpectaUrlFactory } from '../token';
4
4
  export declare const spectaDocument: JupyterFrontEndPlugin<IWidgetTracker, ISpectaShell>;
5
+ export declare const spectaUrlFactory: JupyterFrontEndPlugin<ISpectaUrlFactory>;
5
6
  export declare const spectaOpener: JupyterFrontEndPlugin<void, ILabShell>;
@@ -1,13 +1,13 @@
1
1
  import { IThemeManager, WidgetTracker } from '@jupyterlab/apputils';
2
2
  import { IEditorServices } from '@jupyterlab/codeeditor';
3
- import { PathExt } from '@jupyterlab/coreutils';
3
+ import { PageConfig, PathExt, URLExt } from '@jupyterlab/coreutils';
4
4
  import { IDocumentManager } from '@jupyterlab/docmanager';
5
5
  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
9
  import { Widget } from '@lumino/widgets';
10
- import { ISpectaDocTracker, ISpectaLayoutRegistry } from '../token';
10
+ import { ISpectaDocTracker, ISpectaLayoutRegistry, ISpectaUrlFactoryToken } from '../token';
11
11
  import { configLabLayout, createFileBrowser, hideAppLoadingIndicator, isSpectaApp, readSpectaConfig, registerDocumentFactory } from '../tool';
12
12
  const activate = (app, rendermime, tracker, editorServices, contentFactory, spectaLayoutRegistry, themeManager) => {
13
13
  const namespace = 'specta';
@@ -39,6 +39,29 @@ export const spectaDocument = {
39
39
  activate,
40
40
  provides: ISpectaDocTracker
41
41
  };
42
+ export const spectaUrlFactory = {
43
+ id: 'specta/application-extension:urlFactory',
44
+ autoStart: true,
45
+ provides: ISpectaUrlFactoryToken,
46
+ activate() {
47
+ const urlFactory = (path) => {
48
+ const baseUrl = PageConfig.getBaseUrl();
49
+ let appUrl = PageConfig.getOption('appUrl');
50
+ if (!appUrl.endsWith('/')) {
51
+ appUrl = `${appUrl}/`;
52
+ }
53
+ const url = new URL(URLExt.join(baseUrl, appUrl));
54
+ url.searchParams.set('path', path);
55
+ const queries = PageConfig.getOption('query').split('&').filter(Boolean);
56
+ queries.forEach(query => {
57
+ const [key, value] = query.split('=');
58
+ url.searchParams.set(key, value);
59
+ });
60
+ return url.toString();
61
+ };
62
+ return urlFactory;
63
+ }
64
+ };
42
65
  export const spectaOpener = {
43
66
  id: 'specta/application-extension:opener',
44
67
  autoStart: true,
@@ -48,14 +71,13 @@ export const spectaOpener = {
48
71
  ISpectaDocTracker,
49
72
  IKernelSpecManager
50
73
  ],
51
- activate: async (app, docManager, defaultBrowser) => {
74
+ optional: [ISpectaUrlFactoryToken],
75
+ activate: async (app, docManager, defaultBrowser, tracker, kernelSpecManager, urlFactory) => {
52
76
  const urlParams = new URLSearchParams(window.location.search);
53
- const path = urlParams.get('path');
54
77
  if (!isSpectaApp()) {
78
+ // Not a specta app
79
+ const path = urlParams.get('specta-path');
55
80
  if (!path) {
56
- app.restored.then(async () => {
57
- await app.commands.execute('application:reset-layout');
58
- });
59
81
  return;
60
82
  }
61
83
  app.restored.then(async () => {
@@ -74,12 +96,13 @@ export const spectaOpener = {
74
96
  }
75
97
  }
76
98
  });
77
- // Not a specta app
78
99
  return;
79
100
  }
80
101
  else {
102
+ // Specta app
103
+ const path = urlParams.get('path');
81
104
  if (!path) {
82
- const browser = createFileBrowser({ defaultBrowser });
105
+ const browser = createFileBrowser({ defaultBrowser, urlFactory });
83
106
  app.shell.add(browser, 'main', { rank: 100 });
84
107
  hideAppLoadingIndicator();
85
108
  }
@@ -1,4 +1,4 @@
1
1
  export * from './tool';
2
2
  export * from './token';
3
- declare const _default: (import("@jupyterlab/application").JupyterFrontEndPlugin<import("@jupyterlab/apputils").IWidgetTracker<import("@lumino/widgets").Widget>, import("./token").ISpectaShell> | import("@jupyterlab/application").JupyterFrontEndPlugin<void, import("@jupyterlab/application").ILabShell> | import("@jupyterlab/application").JupyterFrontEndPlugin<import("./token").ISpectaLayoutRegistry> | import("@jupyterlab/application").JupyterFrontEndPlugin<void, import("./token").ISpectaShell>)[];
3
+ declare const _default: (import("@jupyterlab/application").JupyterFrontEndPlugin<import("@jupyterlab/apputils").IWidgetTracker<import("@lumino/widgets").Widget>, import("./token").ISpectaShell> | import("@jupyterlab/application").JupyterFrontEndPlugin<import("./token").ISpectaUrlFactory> | import("@jupyterlab/application").JupyterFrontEndPlugin<void, import("@jupyterlab/application").ILabShell> | import("@jupyterlab/application").JupyterFrontEndPlugin<import("./token").ISpectaLayoutRegistry> | import("@jupyterlab/application").JupyterFrontEndPlugin<void, import("./token").ISpectaShell>)[];
4
4
  export default _default;
@@ -1,4 +1,4 @@
1
- import { spectaDocument, spectaOpener } from './document/plugin';
1
+ import { spectaDocument, spectaOpener, spectaUrlFactory } from './document/plugin';
2
2
  import { spectaLayoutRegistry } from './layout';
3
3
  import { appMeta, cellMeta } from './metadata';
4
4
  import { topbarPlugin } from './topbar';
@@ -10,5 +10,6 @@ export default [
10
10
  spectaLayoutRegistry,
11
11
  topbarPlugin,
12
12
  appMeta,
13
- cellMeta
13
+ cellMeta,
14
+ spectaUrlFactory
14
15
  ];
package/lib/token.d.ts CHANGED
@@ -64,5 +64,7 @@ export interface ISpectaCellConfig {
64
64
  showOutput?: boolean;
65
65
  outputSize?: 'Small' | 'Big' | 'Full';
66
66
  }
67
+ export type ISpectaUrlFactory = (path: string) => string;
67
68
  export declare const ISpectaLayoutRegistry: Token<ISpectaLayoutRegistry>;
68
69
  export declare const ISpectaDocTracker: Token<IWidgetTracker<Widget>>;
70
+ export declare const ISpectaUrlFactoryToken: Token<ISpectaUrlFactory>;
package/lib/token.js CHANGED
@@ -1,3 +1,4 @@
1
1
  import { Token } from '@lumino/coreutils';
2
2
  export const ISpectaLayoutRegistry = new Token('specta:ISpectaLayoutRegistry');
3
3
  export const ISpectaDocTracker = new Token('exampleDocTracker');
4
+ export const ISpectaUrlFactoryToken = new Token('specta:ISpectaUrlFactoryToken');
package/lib/tool.d.ts CHANGED
@@ -6,7 +6,7 @@ import { ICell, INotebookMetadata } from '@jupyterlab/nbformat';
6
6
  import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook';
7
7
  import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
8
8
  import { CommandRegistry } from '@lumino/commands';
9
- import { ISpectaAppConfig, ISpectaCellConfig, ISpectaLayoutRegistry, ISpectaShell } from './token';
9
+ import { ISpectaAppConfig, ISpectaCellConfig, ISpectaLayoutRegistry, ISpectaShell, ISpectaUrlFactory } from './token';
10
10
  export declare function registerDocumentFactory(options: {
11
11
  factoryName: string;
12
12
  app: JupyterFrontEnd<ISpectaShell>;
@@ -20,6 +20,7 @@ export declare function registerDocumentFactory(options: {
20
20
  }): void;
21
21
  export declare function createFileBrowser(options: {
22
22
  defaultBrowser: IDefaultFileBrowser;
23
+ urlFactory: ISpectaUrlFactory | null;
23
24
  }): any;
24
25
  export declare function hideAppLoadingIndicator(): void;
25
26
  export declare function mergeObjects(...objects: Record<string, any>[]): Record<string, any>;
package/lib/tool.js CHANGED
@@ -42,27 +42,31 @@ export function registerDocumentFactory(options) {
42
42
  });
43
43
  }
44
44
  export function createFileBrowser(options) {
45
- const { defaultBrowser } = options;
45
+ const { defaultBrowser, urlFactory } = options;
46
46
  const browser = defaultBrowser;
47
47
  browser.singleClickNavigation = true;
48
48
  browser.showFileCheckboxes = false;
49
49
  browser.showLastModifiedColumn = false;
50
50
  browser.addClass('specta-file-browser');
51
- const urlFactory = (path) => {
52
- const baseUrl = PageConfig.getBaseUrl();
53
- let appUrl = PageConfig.getOption('appUrl');
54
- if (!appUrl.endsWith('/')) {
55
- appUrl = `${appUrl}/`;
56
- }
57
- const url = new URL(URLExt.join(baseUrl, appUrl));
58
- url.searchParams.set('path', path);
59
- const queries = PageConfig.getOption('query').split('&').filter(Boolean);
60
- queries.forEach(query => {
61
- const [key, value] = query.split('=');
62
- url.searchParams.set(key, value);
63
- });
64
- return url.toString();
65
- };
51
+ const urlFactoryFn = urlFactory
52
+ ? urlFactory
53
+ : (path) => {
54
+ const baseUrl = PageConfig.getBaseUrl();
55
+ let appUrl = PageConfig.getOption('appUrl');
56
+ if (!appUrl.endsWith('/')) {
57
+ appUrl = `${appUrl}/`;
58
+ }
59
+ const url = new URL(URLExt.join(baseUrl, appUrl));
60
+ url.searchParams.set('path', path);
61
+ const queries = PageConfig.getOption('query')
62
+ .split('&')
63
+ .filter(Boolean);
64
+ queries.forEach(query => {
65
+ const [key, value] = query.split('=');
66
+ url.searchParams.set(key, value);
67
+ });
68
+ return url.toString();
69
+ };
66
70
  const oldHandler = browser.listing.handleOpen.bind(browser.listing);
67
71
  browser.listing.handleOpen = (item) => {
68
72
  if (item.type === 'directory') {
@@ -70,7 +74,7 @@ export function createFileBrowser(options) {
70
74
  return;
71
75
  }
72
76
  else {
73
- window.open(urlFactory(item.path), '_blank');
77
+ window.open(urlFactoryFn(item.path), '_blank');
74
78
  }
75
79
  };
76
80
  return browser;
@@ -107,7 +111,7 @@ export function isSpectaApp() {
107
111
  return !!document.querySelector('meta[name="specta-config"]');
108
112
  }
109
113
  export function readSpectaConfig({ nbMetadata, nbPath }) {
110
- var _a;
114
+ var _a, _b;
111
115
  let rawConfig = PageConfig.getOption('spectaConfig');
112
116
  if (!rawConfig || rawConfig.length === 0) {
113
117
  rawConfig = '{}';
@@ -117,10 +121,12 @@ export function readSpectaConfig({ nbMetadata, nbPath }) {
117
121
  if (paths && paths.length > 1) {
118
122
  pathWithoutDrive = paths[1];
119
123
  }
120
- const _b = JSON.parse(rawConfig), { perFileConfig } = _b, globalConfig = __rest(_b, ["perFileConfig"]);
124
+ const _c = JSON.parse(rawConfig), { perFileConfig } = _c, globalConfig = __rest(_c, ["perFileConfig"]);
121
125
  let spectaConfig = Object.assign({}, (globalConfig !== null && globalConfig !== void 0 ? globalConfig : {}));
126
+ let fileConfig = {};
122
127
  if (perFileConfig && pathWithoutDrive && perFileConfig[pathWithoutDrive]) {
123
- spectaConfig = Object.assign(Object.assign({}, spectaConfig), perFileConfig[pathWithoutDrive]);
128
+ fileConfig = perFileConfig[pathWithoutDrive];
129
+ spectaConfig = Object.assign(Object.assign({}, spectaConfig), fileConfig);
124
130
  }
125
131
  const spectaMetadata = JSON.parse(JSON.stringify((_a = nbMetadata === null || nbMetadata === void 0 ? void 0 : nbMetadata.specta) !== null && _a !== void 0 ? _a : {}));
126
132
  if (spectaMetadata.hideTopbar === 'Yes') {
@@ -132,11 +138,29 @@ export function readSpectaConfig({ nbMetadata, nbPath }) {
132
138
  else {
133
139
  if (spectaConfig.hideTopbar === null ||
134
140
  spectaConfig.hideTopbar === undefined) {
135
- // Hide topbar by default if not specified in the global config
141
+ // Show topbar by default if not specified in the global config
136
142
  // and notebook metadata
137
- spectaMetadata.hideTopbar = true;
143
+ spectaMetadata.hideTopbar = false;
144
+ }
145
+ }
146
+ if (!spectaMetadata.hideTopbar) {
147
+ spectaMetadata.topBar = {};
148
+ if (spectaMetadata.topbarTitle && spectaMetadata.topbarTitle !== '') {
149
+ spectaMetadata.topBar.title = spectaMetadata.topbarTitle;
150
+ delete spectaMetadata.topbarTitle;
151
+ }
152
+ else if (!((_b = fileConfig === null || fileConfig === void 0 ? void 0 : fileConfig.topBar) === null || _b === void 0 ? void 0 : _b.title)) {
153
+ spectaMetadata.topBar.title = pathWithoutDrive;
154
+ }
155
+ if (spectaMetadata.topbarThemeToggle === 'No') {
156
+ spectaMetadata.topBar.themeToggle = false;
157
+ }
158
+ else {
159
+ spectaMetadata.topBar.themeToggle = true;
138
160
  }
161
+ delete spectaMetadata.topbarThemeToggle;
139
162
  }
163
+ // merge spectaConfig and spectaMetadata (spectaMetadata has higher priority
140
164
  return mergeObjects(spectaConfig, spectaMetadata);
141
165
  }
142
166
  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,7 @@ 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
+ return (React.createElement("div", { className: "specta-topbar", style: { background: (_a = config.background) !== null && _a !== void 0 ? _a : 'var(--jp-layout-color1)' } },
34
34
  React.createElement("div", { className: "specta-topbar-left" },
35
35
  React.createElement("div", { className: "specta-topbar-icon-container" }, config.icon && React.createElement("img", { style: { height: '100%' }, src: config.icon })),
36
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)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyter-specta",
3
- "version": "0.3.4",
3
+ "version": "0.4.0",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/trungleduc/specta",
@@ -78,7 +78,7 @@
78
78
  "@jupyterlab/outputarea": "^4.5.0",
79
79
  "@jupyterlab/rendermime": "^4.5.0",
80
80
  "@jupyterlab/rendermime-extension": "^4.5.0",
81
- "@jupyterlab/services": "^7.2.4",
81
+ "@jupyterlab/services": "^7.5.1",
82
82
  "@jupyterlab/services-extension": "^4.5.0",
83
83
  "@jupyterlab/settingeditor-extension": "^4.5.0",
84
84
  "@jupyterlab/settingregistry": "^4.5.0",
@@ -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
  }