jupyter-specta 0.1.5 → 0.1.7
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 +52 -1
- package/lib/app.js +2 -0
- package/lib/create_notebook_panel.js +0 -1
- package/lib/document/factory.d.ts +6 -1
- package/lib/document/factory.js +30 -6
- package/lib/document/plugin.d.ts +2 -1
- package/lib/document/plugin.js +12 -6
- package/lib/extension_plugins.d.ts +1 -1
- package/lib/layout/index.js +1 -1
- package/lib/shell.d.ts +3 -2
- package/lib/shell.js +4 -0
- package/lib/specta_model.js +7 -31
- package/lib/specta_widget_factory.js +6 -1
- package/lib/token.d.ts +4 -0
- package/lib/tool.d.ts +8 -4
- package/lib/tool.js +23 -18
- package/lib/topbar/index.d.ts +2 -4
- package/lib/topbar/index.js +13 -5
- package/package.json +7 -3
- package/schema/app-meta.json +0 -6
- package/style/base.css +2 -0
- package/style/style.css +5 -6
package/README.md
CHANGED
|
@@ -1 +1,52 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">specta 🌟</h1>
|
|
2
|
+
|
|
3
|
+
[](https://github.com/trungleduc/specta/actions/workflows/build.yml)
|
|
4
|
+
[](https://specta.readthedocs.io/en/latest/?badge=latest)
|
|
5
|
+
[](https://trungleduc.github.io/specta/specta/)
|
|
6
|
+
|
|
7
|
+
<h2 align="center"> A JupyterLite app to present your Jupyter documents in different ways</h2>
|
|
8
|
+
|
|
9
|
+
Specta is a custom JupyterLite app for rendering notebooks and Jupyter‑supported files in multiple modes: dashboards, blog‑style articles, fullscreen viewers, and more. It is built on top of [JupyterLite](https://github.com/jupyterlite/jupyterlite), which allows you to share your documents through alternative interfaces to the IDE-like JupyterLab.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
### Multi-mode Notebook Rendering
|
|
14
|
+
|
|
15
|
+
Render notebooks in:
|
|
16
|
+
|
|
17
|
+
- **Dashboard mode** – structured panels for interactive widgets and outputs
|
|
18
|
+
- **Article mode** – a minimal, blog-like reading experience
|
|
19
|
+
|
|
20
|
+
### Clean Viewer for all Jupyter-supported file types
|
|
21
|
+
|
|
22
|
+
View any Jupyter-supported file using Specta's clean viewer with all Jupyter UI elements removed.
|
|
23
|
+
|
|
24
|
+
### Preview from JupyterLab
|
|
25
|
+
|
|
26
|
+
A `specta` preview can be launched directly from JupyterLab, letting users verify how their documents will look when published.
|
|
27
|
+
|
|
28
|
+
## Installation and Usage
|
|
29
|
+
|
|
30
|
+
You can install `specta` using `pip` or `conda`
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Install using pip
|
|
34
|
+
pip install specta
|
|
35
|
+
|
|
36
|
+
# Install using conda
|
|
37
|
+
conda install -c conda-forge specta
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Once installed, you can build your JupyterLite app, a `specta` app will be included automatically in the output directory of `jupyterlite`:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
jupyter lite build
|
|
44
|
+
```
|
|
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.
|
|
47
|
+
|
|
48
|
+
## Try it online!
|
|
49
|
+
|
|
50
|
+
You can try it online by clicking on this badge:
|
|
51
|
+
|
|
52
|
+
[](https://trungleduc.github.io/specta/specta/)
|
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);
|
|
@@ -2,16 +2,21 @@ import { IThemeManager } from '@jupyterlab/apputils';
|
|
|
2
2
|
import { ABCWidgetFactory, DocumentRegistry } from '@jupyterlab/docregistry';
|
|
3
3
|
import { INotebookModel } from '@jupyterlab/notebook';
|
|
4
4
|
import { SpectaWidgetFactory } from '../specta_widget_factory';
|
|
5
|
-
import { ISpectaLayoutRegistry } from '../token';
|
|
5
|
+
import { ISpectaLayoutRegistry, ISpectaShell } from '../token';
|
|
6
6
|
import { NotebookSpectaDocWidget } from './widget';
|
|
7
7
|
interface IOptions extends DocumentRegistry.IWidgetFactoryOptions {
|
|
8
8
|
spectaWidgetFactory: SpectaWidgetFactory;
|
|
9
9
|
layoutRegistry?: ISpectaLayoutRegistry;
|
|
10
10
|
themeManager?: IThemeManager;
|
|
11
|
+
shell: ISpectaShell;
|
|
12
|
+
spectaLayoutRegistry: ISpectaLayoutRegistry;
|
|
11
13
|
}
|
|
12
14
|
export declare class NotebookGridWidgetFactory extends ABCWidgetFactory<NotebookSpectaDocWidget, INotebookModel> {
|
|
13
15
|
constructor(options: IOptions);
|
|
14
16
|
protected createNewWidget(context: DocumentRegistry.IContext<INotebookModel>): NotebookSpectaDocWidget;
|
|
15
17
|
private _spectaWidgetFactory;
|
|
18
|
+
private _shell;
|
|
19
|
+
private _themeManager?;
|
|
20
|
+
private _layoutRegistry;
|
|
16
21
|
}
|
|
17
22
|
export {};
|
package/lib/document/factory.js
CHANGED
|
@@ -9,17 +9,41 @@ export class NotebookGridWidgetFactory extends ABCWidgetFactory {
|
|
|
9
9
|
constructor(options) {
|
|
10
10
|
super(options);
|
|
11
11
|
this._spectaWidgetFactory = options.spectaWidgetFactory;
|
|
12
|
+
this._shell = options.shell;
|
|
13
|
+
this._themeManager = options.themeManager;
|
|
14
|
+
this._layoutRegistry = options.spectaLayoutRegistry;
|
|
12
15
|
}
|
|
13
16
|
createNewWidget(context) {
|
|
14
17
|
const content = new Panel();
|
|
15
18
|
content.addClass('jp-specta-notebook-panel');
|
|
16
|
-
const spectaConfig = readSpectaConfig(context.model.metadata);
|
|
17
|
-
if (!isSpectaApp() && !spectaConfig.hideTopbar) {
|
|
18
|
-
// Not a specta app, add topbar to document widget
|
|
19
|
-
const topbar = ReactWidget.create(React.createElement(TopbarElement, null));
|
|
20
|
-
content.addWidget(topbar);
|
|
21
|
-
}
|
|
22
19
|
context.ready.then(async () => {
|
|
20
|
+
var _a;
|
|
21
|
+
const path = (_a = context.contentsModel) === null || _a === void 0 ? void 0 : _a.path;
|
|
22
|
+
const spectaConfig = readSpectaConfig({
|
|
23
|
+
nbMetadata: context.model.metadata,
|
|
24
|
+
nbPath: path
|
|
25
|
+
});
|
|
26
|
+
const isSpecta = isSpectaApp();
|
|
27
|
+
if (!spectaConfig.hideTopbar) {
|
|
28
|
+
const topbar = ReactWidget.create(React.createElement(TopbarElement, { config: spectaConfig.topBar, themeManager: this._themeManager, layoutRegistry: this._layoutRegistry }));
|
|
29
|
+
topbar.addClass('specta-topbar-element');
|
|
30
|
+
if (!isSpecta) {
|
|
31
|
+
// Not a specta app, add topbar to document widget
|
|
32
|
+
content.addWidget(topbar);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
// Specta app, add topbar to layout
|
|
36
|
+
topbar.id = 'specta-topbar-widget';
|
|
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
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (isSpecta) {
|
|
45
|
+
this._shell.hideTopBar();
|
|
46
|
+
}
|
|
23
47
|
const spectaWidget = await this._spectaWidgetFactory.createNew({
|
|
24
48
|
context
|
|
25
49
|
});
|
package/lib/document/plugin.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
|
|
2
2
|
import { IWidgetTracker } from '@jupyterlab/apputils';
|
|
3
|
-
|
|
3
|
+
import { ISpectaShell } from '../token';
|
|
4
|
+
export declare const spectaDocument: JupyterFrontEndPlugin<IWidgetTracker, ISpectaShell>;
|
|
4
5
|
export declare const spectaOpener: JupyterFrontEndPlugin<void>;
|
package/lib/document/plugin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { WidgetTracker } from '@jupyterlab/apputils';
|
|
1
|
+
import { IThemeManager, WidgetTracker } from '@jupyterlab/apputils';
|
|
2
2
|
import { IEditorServices } from '@jupyterlab/codeeditor';
|
|
3
3
|
import { PathExt } from '@jupyterlab/coreutils';
|
|
4
4
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
@@ -6,9 +6,10 @@ 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
|
-
const activate = (app, rendermime, tracker, editorServices, contentFactory, spectaLayoutRegistry) => {
|
|
12
|
+
const activate = (app, rendermime, tracker, editorServices, contentFactory, spectaLayoutRegistry, themeManager) => {
|
|
12
13
|
const namespace = 'specta';
|
|
13
14
|
const spectaTracker = new WidgetTracker({ namespace });
|
|
14
15
|
registerDocumentFactory({
|
|
@@ -19,7 +20,8 @@ const activate = (app, rendermime, tracker, editorServices, contentFactory, spec
|
|
|
19
20
|
editorServices,
|
|
20
21
|
contentFactory,
|
|
21
22
|
spectaTracker,
|
|
22
|
-
spectaLayoutRegistry
|
|
23
|
+
spectaLayoutRegistry,
|
|
24
|
+
themeManager
|
|
23
25
|
});
|
|
24
26
|
return spectaTracker;
|
|
25
27
|
};
|
|
@@ -31,7 +33,8 @@ export const spectaDocument = {
|
|
|
31
33
|
INotebookTracker,
|
|
32
34
|
IEditorServices,
|
|
33
35
|
NotebookPanel.IContentFactory,
|
|
34
|
-
ISpectaLayoutRegistry
|
|
36
|
+
ISpectaLayoutRegistry,
|
|
37
|
+
IThemeManager
|
|
35
38
|
],
|
|
36
39
|
activate,
|
|
37
40
|
provides: ISpectaDocTracker
|
|
@@ -68,7 +71,7 @@ export const spectaOpener = {
|
|
|
68
71
|
else {
|
|
69
72
|
let count = 0;
|
|
70
73
|
const tryOpen = () => {
|
|
71
|
-
const widget = docManager.openOrReveal(path);
|
|
74
|
+
const widget = docManager.openOrReveal(path, 'default');
|
|
72
75
|
if (widget) {
|
|
73
76
|
app.shell.add(widget, 'main');
|
|
74
77
|
hideAppLoadingIndicator();
|
|
@@ -77,7 +80,10 @@ export const spectaOpener = {
|
|
|
77
80
|
count++;
|
|
78
81
|
if (count > 10) {
|
|
79
82
|
console.error('Failed to open file', path);
|
|
80
|
-
|
|
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();
|
|
81
87
|
return;
|
|
82
88
|
}
|
|
83
89
|
setTimeout(tryOpen, 100);
|
|
@@ -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
|
|
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").ISpectaLayoutRegistry> | import("@jupyterlab/application").JupyterFrontEndPlugin<void, import("./token").ISpectaShell>)[];
|
|
4
4
|
export default _default;
|
package/lib/layout/index.js
CHANGED
|
@@ -8,7 +8,7 @@ export const spectaLayoutRegistry = {
|
|
|
8
8
|
activate: (app) => {
|
|
9
9
|
var _a;
|
|
10
10
|
const layoutRegistry = new SpectaLayoutRegistry();
|
|
11
|
-
const spectaConfig = readSpectaConfig();
|
|
11
|
+
const spectaConfig = readSpectaConfig({});
|
|
12
12
|
const defaultLayout = (_a = spectaConfig.defaultLayout) !== null && _a !== void 0 ? _a : 'default';
|
|
13
13
|
layoutRegistry.setSelectedLayout(defaultLayout);
|
|
14
14
|
return layoutRegistry;
|
package/lib/shell.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { JupyterFrontEnd } from '@jupyterlab/application';
|
|
2
1
|
import { DocumentRegistry } from '@jupyterlab/docregistry';
|
|
3
2
|
import { Widget } from '@lumino/widgets';
|
|
3
|
+
import { ISpectaShell } from './token';
|
|
4
4
|
/**
|
|
5
5
|
* A namespace for Shell statics
|
|
6
6
|
*/
|
|
@@ -13,7 +13,7 @@ export declare namespace IShell {
|
|
|
13
13
|
/**
|
|
14
14
|
* The application shell.
|
|
15
15
|
*/
|
|
16
|
-
export declare class SpectaShell extends Widget implements
|
|
16
|
+
export declare class SpectaShell extends Widget implements ISpectaShell {
|
|
17
17
|
constructor();
|
|
18
18
|
/**
|
|
19
19
|
* The current widget in the shell's main area.
|
|
@@ -30,6 +30,7 @@ export declare class SpectaShell extends Widget implements JupyterFrontEnd.IShel
|
|
|
30
30
|
*
|
|
31
31
|
*/
|
|
32
32
|
add(widget: Widget, area?: IShell.Area, options?: DocumentRegistry.IOpenOptions): void;
|
|
33
|
+
hideTopBar(): void;
|
|
33
34
|
widgets(area: IShell.Area): IterableIterator<Widget>;
|
|
34
35
|
/**
|
|
35
36
|
* Add a widget to the top content area.
|
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);
|
|
@@ -78,6 +79,9 @@ export class SpectaShell extends Widget {
|
|
|
78
79
|
break;
|
|
79
80
|
}
|
|
80
81
|
}
|
|
82
|
+
hideTopBar() {
|
|
83
|
+
this._topHandler.panel.hide();
|
|
84
|
+
}
|
|
81
85
|
widgets(area) {
|
|
82
86
|
switch (area) {
|
|
83
87
|
case 'top':
|
package/lib/specta_model.js
CHANGED
|
@@ -2,7 +2,6 @@ import { CodeCell, MarkdownCell, RawCell } from '@jupyterlab/cells';
|
|
|
2
2
|
import { OutputAreaModel, SimplifiedOutputArea } from '@jupyterlab/outputarea';
|
|
3
3
|
import { createNotebookContext, createNotebookPanel } from './create_notebook_panel';
|
|
4
4
|
import { SpectaCellOutput } from './specta_cell_output';
|
|
5
|
-
import { PromiseDelegate } from '@lumino/coreutils';
|
|
6
5
|
import { readCellConfig } from './tool';
|
|
7
6
|
export const VIEW = 'grid_default';
|
|
8
7
|
export class AppModel {
|
|
@@ -49,41 +48,18 @@ export class AppModel {
|
|
|
49
48
|
return this._notebookPanel;
|
|
50
49
|
}
|
|
51
50
|
async initialize() {
|
|
52
|
-
var _a;
|
|
53
|
-
const pd = new PromiseDelegate();
|
|
54
51
|
this._context = await createNotebookContext({
|
|
55
52
|
manager: this._manager,
|
|
56
53
|
kernelPreference: this._kernelPreference
|
|
57
54
|
});
|
|
58
55
|
this._context.model.fromJSON(this._notebookModelJson);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
this.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
this.options.tracker.widgetAdded.emit(this._notebookPanel);
|
|
67
|
-
};
|
|
68
|
-
const kernel = (_a = this._context.sessionContext.session) === null || _a === void 0 ? void 0 : _a.kernel;
|
|
69
|
-
if (kernel) {
|
|
70
|
-
const status = kernel.status;
|
|
71
|
-
if (status !== 'unknown') {
|
|
72
|
-
// Connected to an existing kernel.
|
|
73
|
-
connectKernel();
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
this._context.sessionContext.connectionStatusChanged.connect((_, status) => {
|
|
78
|
-
var _a;
|
|
79
|
-
if (status === 'connected') {
|
|
80
|
-
const kernel = (_a = this._context.sessionContext.session) === null || _a === void 0 ? void 0 : _a.kernel;
|
|
81
|
-
if (kernel) {
|
|
82
|
-
connectKernel();
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}, this);
|
|
86
|
-
return pd.promise;
|
|
56
|
+
this._notebookPanel = createNotebookPanel({
|
|
57
|
+
context: this._context,
|
|
58
|
+
rendermime: this.options.rendermime,
|
|
59
|
+
editorServices: this.options.editorServices
|
|
60
|
+
});
|
|
61
|
+
this.options.tracker.add(this._notebookPanel);
|
|
62
|
+
await this._context.sessionContext.initialize();
|
|
87
63
|
}
|
|
88
64
|
createCell(cellModel) {
|
|
89
65
|
let item;
|
|
@@ -8,6 +8,7 @@ export class SpectaWidgetFactory {
|
|
|
8
8
|
this._options = options;
|
|
9
9
|
}
|
|
10
10
|
async createNew(options) {
|
|
11
|
+
var _a;
|
|
11
12
|
const { context } = options;
|
|
12
13
|
const rendermime = this._options.rendermime.clone({
|
|
13
14
|
resolver: context.urlResolver
|
|
@@ -23,12 +24,16 @@ export class SpectaWidgetFactory {
|
|
|
23
24
|
notebookConfig: StaticNotebook.defaultNotebookConfig,
|
|
24
25
|
editorServices: this._options.editorServices
|
|
25
26
|
});
|
|
27
|
+
const spectaConfig = readSpectaConfig({
|
|
28
|
+
nbMetadata: context.model.metadata,
|
|
29
|
+
nbPath: (_a = context.contentsModel) === null || _a === void 0 ? void 0 : _a.path
|
|
30
|
+
});
|
|
26
31
|
const panel = new AppWidget({
|
|
27
32
|
id: UUID.uuid4(),
|
|
28
33
|
label: '',
|
|
29
34
|
model,
|
|
30
35
|
layoutRegistry: this._options.spectaLayoutRegistry,
|
|
31
|
-
spectaConfig
|
|
36
|
+
spectaConfig
|
|
32
37
|
});
|
|
33
38
|
return panel;
|
|
34
39
|
}
|
package/lib/token.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ import { SpectaCellOutput } from './specta_cell_output';
|
|
|
4
4
|
import * as nbformat from '@jupyterlab/nbformat';
|
|
5
5
|
import { ISignal } from '@lumino/signaling';
|
|
6
6
|
import { IWidgetTracker } from '@jupyterlab/apputils';
|
|
7
|
+
import { JupyterFrontEnd } from '@jupyterlab/application';
|
|
8
|
+
export interface ISpectaShell extends JupyterFrontEnd.IShell {
|
|
9
|
+
hideTopBar: () => void;
|
|
10
|
+
}
|
|
7
11
|
export interface ISpectaLayout {
|
|
8
12
|
render(options: {
|
|
9
13
|
host: Panel;
|
package/lib/tool.d.ts
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
import { JupyterFrontEnd } from '@jupyterlab/application';
|
|
2
|
-
import { WidgetTracker } from '@jupyterlab/apputils';
|
|
2
|
+
import { IThemeManager, WidgetTracker } from '@jupyterlab/apputils';
|
|
3
3
|
import { IEditorServices } from '@jupyterlab/codeeditor';
|
|
4
4
|
import { IDefaultFileBrowser } from '@jupyterlab/filebrowser';
|
|
5
5
|
import { ICell, INotebookMetadata } from '@jupyterlab/nbformat';
|
|
6
6
|
import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook';
|
|
7
7
|
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
8
|
-
import { ISpectaAppConfig, ISpectaCellConfig, ISpectaLayoutRegistry } from './token';
|
|
8
|
+
import { ISpectaAppConfig, ISpectaCellConfig, ISpectaLayoutRegistry, ISpectaShell } from './token';
|
|
9
9
|
export declare function registerDocumentFactory(options: {
|
|
10
10
|
factoryName: string;
|
|
11
|
-
app: JupyterFrontEnd
|
|
11
|
+
app: JupyterFrontEnd<ISpectaShell>;
|
|
12
12
|
rendermime: IRenderMimeRegistry;
|
|
13
13
|
tracker: INotebookTracker;
|
|
14
14
|
editorServices: IEditorServices;
|
|
15
15
|
contentFactory: NotebookPanel.IContentFactory;
|
|
16
16
|
spectaTracker: WidgetTracker;
|
|
17
17
|
spectaLayoutRegistry: ISpectaLayoutRegistry;
|
|
18
|
+
themeManager?: IThemeManager;
|
|
18
19
|
}): void;
|
|
19
20
|
export declare function createFileBrowser(options: {
|
|
20
21
|
defaultBrowser: IDefaultFileBrowser;
|
|
21
22
|
}): any;
|
|
22
23
|
export declare function hideAppLoadingIndicator(): void;
|
|
23
24
|
export declare function isSpectaApp(): boolean;
|
|
24
|
-
export declare function readSpectaConfig(nbMetadata
|
|
25
|
+
export declare function readSpectaConfig({ nbMetadata, nbPath }: {
|
|
26
|
+
nbMetadata?: INotebookMetadata;
|
|
27
|
+
nbPath?: string | null;
|
|
28
|
+
}): ISpectaAppConfig;
|
|
25
29
|
export declare function readCellConfig(cell?: ICell): Required<ISpectaCellConfig>;
|
package/lib/tool.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
1
12
|
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
|
2
|
-
import { NotebookModelFactory } from '@jupyterlab/notebook';
|
|
3
13
|
import { NotebookGridWidgetFactory } from './document/factory';
|
|
4
14
|
import { SpectaWidgetFactory } from './specta_widget_factory';
|
|
5
15
|
export function registerDocumentFactory(options) {
|
|
6
|
-
const { factoryName, app, rendermime, tracker, editorServices, contentFactory, spectaTracker, spectaLayoutRegistry } = options;
|
|
16
|
+
const { factoryName, app, rendermime, tracker, editorServices, contentFactory, spectaTracker, spectaLayoutRegistry, themeManager } = options;
|
|
7
17
|
const spectaWidgetFactory = new SpectaWidgetFactory({
|
|
8
18
|
manager: app.serviceManager,
|
|
9
19
|
rendermime,
|
|
@@ -16,23 +26,14 @@ export function registerDocumentFactory(options) {
|
|
|
16
26
|
const widgetFactory = new NotebookGridWidgetFactory({
|
|
17
27
|
name: factoryName,
|
|
18
28
|
modelName: 'notebook',
|
|
19
|
-
fileTypes: ['
|
|
20
|
-
|
|
29
|
+
fileTypes: ['notebook'],
|
|
30
|
+
shell: app.shell,
|
|
31
|
+
spectaWidgetFactory,
|
|
32
|
+
themeManager,
|
|
33
|
+
spectaLayoutRegistry
|
|
21
34
|
});
|
|
22
35
|
// Registering the widget factory
|
|
23
36
|
app.docRegistry.addWidgetFactory(widgetFactory);
|
|
24
|
-
// Creating and registering the model factory for our custom DocumentModelAdd commentMore actions
|
|
25
|
-
const modelFactory = new NotebookModelFactory({});
|
|
26
|
-
app.docRegistry.addModelFactory(modelFactory);
|
|
27
|
-
// register the filetype
|
|
28
|
-
app.docRegistry.addFileType({
|
|
29
|
-
name: 'ipynb',
|
|
30
|
-
displayName: 'IPYNB',
|
|
31
|
-
mimeTypes: ['text/json'],
|
|
32
|
-
extensions: ['.ipynb', '.IPYNB'],
|
|
33
|
-
fileFormat: 'json',
|
|
34
|
-
contentType: 'notebook'
|
|
35
|
-
});
|
|
36
37
|
widgetFactory.widgetCreated.connect((sender, widget) => {
|
|
37
38
|
widget.context.pathChanged.connect(() => {
|
|
38
39
|
spectaTracker.save(widget);
|
|
@@ -89,13 +90,17 @@ export function hideAppLoadingIndicator() {
|
|
|
89
90
|
export function isSpectaApp() {
|
|
90
91
|
return !!document.querySelector('meta[name="specta-config"]');
|
|
91
92
|
}
|
|
92
|
-
export function readSpectaConfig(nbMetadata) {
|
|
93
|
+
export function readSpectaConfig({ nbMetadata, nbPath }) {
|
|
93
94
|
var _a;
|
|
94
95
|
let rawConfig = PageConfig.getOption('spectaConfig');
|
|
95
96
|
if (!rawConfig || rawConfig.length === 0) {
|
|
96
97
|
rawConfig = '{}';
|
|
97
98
|
}
|
|
98
|
-
const
|
|
99
|
+
const _b = JSON.parse(rawConfig), { perFileConfig } = _b, globalConfig = __rest(_b, ["perFileConfig"]);
|
|
100
|
+
let spectaConfig = Object.assign({}, (globalConfig !== null && globalConfig !== void 0 ? globalConfig : {}));
|
|
101
|
+
if (perFileConfig && nbPath && perFileConfig[nbPath]) {
|
|
102
|
+
spectaConfig = Object.assign(Object.assign({}, spectaConfig), perFileConfig[nbPath]);
|
|
103
|
+
}
|
|
99
104
|
const spectaMetadata = ((_a = nbMetadata === null || nbMetadata === void 0 ? void 0 : nbMetadata.specta) !== null && _a !== void 0 ? _a : {});
|
|
100
105
|
return Object.assign(Object.assign({}, spectaConfig), spectaMetadata);
|
|
101
106
|
}
|
package/lib/topbar/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*/
|
|
5
|
-
export declare const topbarPlugin: JupyterFrontEndPlugin<void>;
|
|
2
|
+
import { ISpectaShell } from '../token';
|
|
3
|
+
export declare const topbarPlugin: JupyterFrontEndPlugin<void, ISpectaShell>;
|
package/lib/topbar/index.js
CHANGED
|
@@ -4,11 +4,9 @@ import { TopbarElement } from './widget';
|
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { isSpectaApp, readSpectaConfig } from '../tool';
|
|
6
6
|
import { ISpectaLayoutRegistry } from '../token';
|
|
7
|
-
|
|
8
|
-
* Initialization data for the voila_topbar extension.
|
|
9
|
-
*/
|
|
7
|
+
import { PathExt } from '@jupyterlab/coreutils';
|
|
10
8
|
export const topbarPlugin = {
|
|
11
|
-
id: 'specta:
|
|
9
|
+
id: 'specta:topbar',
|
|
12
10
|
description: 'Specta topbar extension',
|
|
13
11
|
autoStart: true,
|
|
14
12
|
requires: [IThemeManager, ISpectaLayoutRegistry],
|
|
@@ -17,7 +15,17 @@ export const topbarPlugin = {
|
|
|
17
15
|
if (!isSpecta) {
|
|
18
16
|
return;
|
|
19
17
|
}
|
|
20
|
-
const
|
|
18
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
19
|
+
const path = urlParams.get('path');
|
|
20
|
+
if (path && PathExt.extname(path) === '.ipynb') {
|
|
21
|
+
// Specta document will handle the top bar.
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const config = readSpectaConfig({ nbPath: path });
|
|
25
|
+
if (config.hideTopbar) {
|
|
26
|
+
app.shell.hideTopBar();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
21
29
|
const widget = ReactWidget.create(React.createElement(TopbarElement, { config: config.topBar, themeManager: themeManager, layoutRegistry: layoutRegistry }));
|
|
22
30
|
widget.id = 'specta-topbar-widget';
|
|
23
31
|
widget.addClass('specta-topbar-element');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyter-specta",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/trungleduc/specta",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"build:all": "npm run build:app && npm run build:labextension",
|
|
37
37
|
"build:all:dev": "npm run build:app:dev && npm run build:labextension:dev",
|
|
38
38
|
"build:demo": "cd demo && rm -rf .jupyterlite.doit.db _output && jupyter lite build .",
|
|
39
|
+
"update:demo": "node script/build-dev.mjs",
|
|
39
40
|
"lint": "npm run lint:prettier && npm run lint:eslint",
|
|
40
41
|
"lint:prettier": "prettier --no-error-on-unmatched-pattern --write \"**/*{.ts,.tsx,.jsx,.css,.json,.md,.yml}\"",
|
|
41
42
|
"lint:eslint": "eslint --ext .ts,.tsx .",
|
|
@@ -56,9 +57,11 @@
|
|
|
56
57
|
"@jupyterlab/coreutils": "^6.4.2",
|
|
57
58
|
"@jupyterlab/docmanager": "^4.4.2",
|
|
58
59
|
"@jupyterlab/docmanager-extension": "^4.4.2",
|
|
60
|
+
"@jupyterlab/docregistry": "^4.4.2",
|
|
59
61
|
"@jupyterlab/filebrowser": "^4.4.2",
|
|
60
62
|
"@jupyterlab/filebrowser-extension": "^4.4.2",
|
|
61
|
-
"@jupyterlab/
|
|
63
|
+
"@jupyterlab/imageviewer": "^4.4.2",
|
|
64
|
+
"@jupyterlab/imageviewer-extension": "^4.4.2",
|
|
62
65
|
"@jupyterlab/json-extension": "^4.4.2",
|
|
63
66
|
"@jupyterlab/logconsole": "^4.4.2",
|
|
64
67
|
"@jupyterlab/mainmenu": "^4.4.2",
|
|
@@ -109,14 +112,15 @@
|
|
|
109
112
|
},
|
|
110
113
|
"devDependencies": {
|
|
111
114
|
"@jupyterlab/builder": "~4.4.2",
|
|
115
|
+
"@types/react": "^18.0.26",
|
|
112
116
|
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
|
113
117
|
"@typescript-eslint/parser": "^6.1.0",
|
|
114
|
-
"@types/react": "^18.0.26",
|
|
115
118
|
"css-loader": "^7.1.2",
|
|
116
119
|
"eslint": "^8.36.0",
|
|
117
120
|
"eslint-config-prettier": "^8.8.0",
|
|
118
121
|
"eslint-plugin-prettier": "^5.0.0",
|
|
119
122
|
"eslint-plugin-react-hooks": "^5.0.0",
|
|
123
|
+
"fs-extra": "^11.3.0",
|
|
120
124
|
"handlebars": "^4.7.8",
|
|
121
125
|
"html-webpack-plugin": "^5.5.3",
|
|
122
126
|
"ignore-loader": "^0.1.2",
|
package/schema/app-meta.json
CHANGED
|
@@ -9,12 +9,6 @@
|
|
|
9
9
|
"metadataSchema": {
|
|
10
10
|
"type": "object",
|
|
11
11
|
"properties": {
|
|
12
|
-
"/specta/hideTopbar": {
|
|
13
|
-
"title": "Hide top bar",
|
|
14
|
-
"type": "boolean",
|
|
15
|
-
"enum": [true, false],
|
|
16
|
-
"default": false
|
|
17
|
-
},
|
|
18
12
|
"/specta/defaultLayout": {
|
|
19
13
|
"title": "Page layout",
|
|
20
14
|
"type": "string",
|
package/style/base.css
CHANGED
|
@@ -55,6 +55,7 @@
|
|
|
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 {
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
display: flex;
|
|
63
64
|
box-shadow: unset !important;
|
|
64
65
|
z-index: 100;
|
|
66
|
+
contain: unset !important;
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
.specta-topbar-element {
|
package/style/style.css
CHANGED
|
@@ -8,12 +8,6 @@
|
|
|
8
8
|
display: none;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
#specta-top-panel {
|
|
12
|
-
contain: unset !important;
|
|
13
|
-
box-shadow: unset !important;
|
|
14
|
-
z-index: 100;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
11
|
.specta-file-browser {
|
|
18
12
|
max-width: 1200px;
|
|
19
13
|
margin: 0 auto;
|
|
@@ -32,3 +26,8 @@
|
|
|
32
26
|
background: var(--jp-layout-color1);
|
|
33
27
|
box-shadow: var(--jp-elevation-z4);
|
|
34
28
|
}
|
|
29
|
+
|
|
30
|
+
.specta-main-content-panel {
|
|
31
|
+
padding-left: 5px;
|
|
32
|
+
padding-right: 5px;
|
|
33
|
+
}
|