jupyter-specta 0.1.4 → 0.1.6
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/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 +45 -13
- 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 +3 -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 +12 -10
- package/lib/tool.js +38 -21
- package/lib/topbar/index.d.ts +2 -4
- package/lib/topbar/index.js +13 -5
- package/package.json +10 -3
- package/schema/app-meta.json +0 -6
- package/style/base.css +1 -0
- package/style/style.css +1 -7
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/)
|
|
@@ -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,12 +1,14 @@
|
|
|
1
|
-
import { WidgetTracker } from '@jupyterlab/apputils';
|
|
1
|
+
import { IThemeManager, WidgetTracker } from '@jupyterlab/apputils';
|
|
2
2
|
import { IEditorServices } from '@jupyterlab/codeeditor';
|
|
3
|
+
import { PathExt } from '@jupyterlab/coreutils';
|
|
4
|
+
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
5
|
+
import { IDefaultFileBrowser } from '@jupyterlab/filebrowser';
|
|
3
6
|
import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook';
|
|
4
7
|
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
5
|
-
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
6
|
-
import { createFileBrowser, hideAppLoadingIndicator, isSpectaApp, registerDocumentFactory } from '../tool';
|
|
7
|
-
import { ISpectaDocTracker, ISpectaLayoutRegistry } from '../token';
|
|
8
8
|
import { IKernelSpecManager } from '@jupyterlab/services';
|
|
9
|
-
|
|
9
|
+
import { ISpectaDocTracker, ISpectaLayoutRegistry } from '../token';
|
|
10
|
+
import { createFileBrowser, hideAppLoadingIndicator, isSpectaApp, registerDocumentFactory } from '../tool';
|
|
11
|
+
const activate = (app, rendermime, tracker, editorServices, contentFactory, spectaLayoutRegistry, themeManager) => {
|
|
10
12
|
const namespace = 'specta';
|
|
11
13
|
const spectaTracker = new WidgetTracker({ namespace });
|
|
12
14
|
registerDocumentFactory({
|
|
@@ -17,7 +19,8 @@ const activate = (app, rendermime, tracker, editorServices, contentFactory, spec
|
|
|
17
19
|
editorServices,
|
|
18
20
|
contentFactory,
|
|
19
21
|
spectaTracker,
|
|
20
|
-
spectaLayoutRegistry
|
|
22
|
+
spectaLayoutRegistry,
|
|
23
|
+
themeManager
|
|
21
24
|
});
|
|
22
25
|
return spectaTracker;
|
|
23
26
|
};
|
|
@@ -29,7 +32,8 @@ export const spectaDocument = {
|
|
|
29
32
|
INotebookTracker,
|
|
30
33
|
IEditorServices,
|
|
31
34
|
NotebookPanel.IContentFactory,
|
|
32
|
-
ISpectaLayoutRegistry
|
|
35
|
+
ISpectaLayoutRegistry,
|
|
36
|
+
IThemeManager
|
|
33
37
|
],
|
|
34
38
|
activate,
|
|
35
39
|
provides: ISpectaDocTracker
|
|
@@ -37,8 +41,13 @@ export const spectaDocument = {
|
|
|
37
41
|
export const spectaOpener = {
|
|
38
42
|
id: 'specta/application-extension:opener',
|
|
39
43
|
autoStart: true,
|
|
40
|
-
requires: [
|
|
41
|
-
|
|
44
|
+
requires: [
|
|
45
|
+
IDocumentManager,
|
|
46
|
+
IDefaultFileBrowser,
|
|
47
|
+
ISpectaDocTracker,
|
|
48
|
+
IKernelSpecManager
|
|
49
|
+
],
|
|
50
|
+
activate: async (app, docManager, defaultBrowser) => {
|
|
42
51
|
if (!isSpectaApp()) {
|
|
43
52
|
// Not a specta app, return
|
|
44
53
|
return;
|
|
@@ -46,14 +55,37 @@ export const spectaOpener = {
|
|
|
46
55
|
const urlParams = new URLSearchParams(window.location.search);
|
|
47
56
|
const path = urlParams.get('path');
|
|
48
57
|
if (!path) {
|
|
49
|
-
const browser = createFileBrowser({
|
|
58
|
+
const browser = createFileBrowser({ defaultBrowser });
|
|
50
59
|
app.shell.add(browser, 'main', { rank: 100 });
|
|
51
60
|
hideAppLoadingIndicator();
|
|
52
61
|
}
|
|
53
62
|
else {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
63
|
+
if (PathExt.extname(path) === '.ipynb') {
|
|
64
|
+
app.shell.addClass('specta-document-viewer');
|
|
65
|
+
const widget = docManager.openOrReveal(path, 'specta');
|
|
66
|
+
if (widget) {
|
|
67
|
+
app.shell.add(widget, 'main');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
let count = 0;
|
|
72
|
+
const tryOpen = () => {
|
|
73
|
+
const widget = docManager.openOrReveal(path);
|
|
74
|
+
if (widget) {
|
|
75
|
+
app.shell.add(widget, 'main');
|
|
76
|
+
hideAppLoadingIndicator();
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
count++;
|
|
80
|
+
if (count > 10) {
|
|
81
|
+
console.error('Failed to open file', path);
|
|
82
|
+
//TODO Open in text editor?
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
setTimeout(tryOpen, 100);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
tryOpen();
|
|
57
89
|
}
|
|
58
90
|
}
|
|
59
91
|
}
|
|
@@ -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
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,27 +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
|
+
import { IDefaultFileBrowser } from '@jupyterlab/filebrowser';
|
|
5
|
+
import { ICell, INotebookMetadata } from '@jupyterlab/nbformat';
|
|
4
6
|
import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook';
|
|
5
7
|
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
6
|
-
import {
|
|
7
|
-
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
8
|
-
import { ISpectaAppConfig, ISpectaCellConfig, ISpectaLayoutRegistry } from './token';
|
|
9
|
-
import { INotebookMetadata } from '@jupyterlab/nbformat';
|
|
10
|
-
import { ICell } from '@jupyterlab/nbformat';
|
|
8
|
+
import { ISpectaAppConfig, ISpectaCellConfig, ISpectaLayoutRegistry, ISpectaShell } from './token';
|
|
11
9
|
export declare function registerDocumentFactory(options: {
|
|
12
10
|
factoryName: string;
|
|
13
|
-
app: JupyterFrontEnd
|
|
11
|
+
app: JupyterFrontEnd<ISpectaShell>;
|
|
14
12
|
rendermime: IRenderMimeRegistry;
|
|
15
13
|
tracker: INotebookTracker;
|
|
16
14
|
editorServices: IEditorServices;
|
|
17
15
|
contentFactory: NotebookPanel.IContentFactory;
|
|
18
16
|
spectaTracker: WidgetTracker;
|
|
19
17
|
spectaLayoutRegistry: ISpectaLayoutRegistry;
|
|
18
|
+
themeManager?: IThemeManager;
|
|
20
19
|
}): void;
|
|
21
20
|
export declare function createFileBrowser(options: {
|
|
22
|
-
|
|
23
|
-
}):
|
|
21
|
+
defaultBrowser: IDefaultFileBrowser;
|
|
22
|
+
}): any;
|
|
24
23
|
export declare function hideAppLoadingIndicator(): void;
|
|
25
24
|
export declare function isSpectaApp(): boolean;
|
|
26
|
-
export declare function readSpectaConfig(nbMetadata
|
|
25
|
+
export declare function readSpectaConfig({ nbMetadata, nbPath }: {
|
|
26
|
+
nbMetadata?: INotebookMetadata;
|
|
27
|
+
nbPath?: string | null;
|
|
28
|
+
}): ISpectaAppConfig;
|
|
27
29
|
export declare function readCellConfig(cell?: ICell): Required<ISpectaCellConfig>;
|
package/lib/tool.js
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
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 { FilterFileBrowserModel } from '@jupyterlab/filebrowser';
|
|
3
13
|
import { NotebookModelFactory } from '@jupyterlab/notebook';
|
|
4
|
-
import { VoilaFileBrowser } from '@voila-dashboards/voila';
|
|
5
14
|
import { NotebookGridWidgetFactory } from './document/factory';
|
|
6
15
|
import { SpectaWidgetFactory } from './specta_widget_factory';
|
|
7
16
|
export function registerDocumentFactory(options) {
|
|
8
|
-
const { factoryName, app, rendermime, tracker, editorServices, contentFactory, spectaTracker, spectaLayoutRegistry } = options;
|
|
17
|
+
const { factoryName, app, rendermime, tracker, editorServices, contentFactory, spectaTracker, spectaLayoutRegistry, themeManager } = options;
|
|
9
18
|
const spectaWidgetFactory = new SpectaWidgetFactory({
|
|
10
19
|
manager: app.serviceManager,
|
|
11
20
|
rendermime,
|
|
@@ -19,7 +28,10 @@ export function registerDocumentFactory(options) {
|
|
|
19
28
|
name: factoryName,
|
|
20
29
|
modelName: 'notebook',
|
|
21
30
|
fileTypes: ['ipynb'],
|
|
22
|
-
spectaWidgetFactory
|
|
31
|
+
spectaWidgetFactory,
|
|
32
|
+
shell: app.shell,
|
|
33
|
+
themeManager,
|
|
34
|
+
spectaLayoutRegistry
|
|
23
35
|
});
|
|
24
36
|
// Registering the widget factory
|
|
25
37
|
app.docRegistry.addWidgetFactory(widgetFactory);
|
|
@@ -43,11 +55,12 @@ export function registerDocumentFactory(options) {
|
|
|
43
55
|
});
|
|
44
56
|
}
|
|
45
57
|
export function createFileBrowser(options) {
|
|
46
|
-
const {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
const { defaultBrowser } = options;
|
|
59
|
+
const browser = defaultBrowser;
|
|
60
|
+
browser.singleClickNavigation = true;
|
|
61
|
+
browser.showFileCheckboxes = false;
|
|
62
|
+
browser.showLastModifiedColumn = false;
|
|
63
|
+
browser.addClass('specta-file-browser');
|
|
51
64
|
const urlFactory = (path) => {
|
|
52
65
|
const baseUrl = PageConfig.getBaseUrl();
|
|
53
66
|
let appUrl = PageConfig.getOption('appUrl');
|
|
@@ -63,16 +76,16 @@ export function createFileBrowser(options) {
|
|
|
63
76
|
});
|
|
64
77
|
return url.toString();
|
|
65
78
|
};
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
const oldHandler = browser.listing.handleOpen.bind(browser.listing);
|
|
80
|
+
browser.listing.handleOpen = (item) => {
|
|
81
|
+
if (item.type === 'directory') {
|
|
82
|
+
oldHandler(item);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
window.open(urlFactory(item.path), '_blank');
|
|
87
|
+
}
|
|
88
|
+
};
|
|
76
89
|
return browser;
|
|
77
90
|
}
|
|
78
91
|
export function hideAppLoadingIndicator() {
|
|
@@ -90,13 +103,17 @@ export function hideAppLoadingIndicator() {
|
|
|
90
103
|
export function isSpectaApp() {
|
|
91
104
|
return !!document.querySelector('meta[name="specta-config"]');
|
|
92
105
|
}
|
|
93
|
-
export function readSpectaConfig(nbMetadata) {
|
|
106
|
+
export function readSpectaConfig({ nbMetadata, nbPath }) {
|
|
94
107
|
var _a;
|
|
95
108
|
let rawConfig = PageConfig.getOption('spectaConfig');
|
|
96
109
|
if (!rawConfig || rawConfig.length === 0) {
|
|
97
110
|
rawConfig = '{}';
|
|
98
111
|
}
|
|
99
|
-
const
|
|
112
|
+
const _b = JSON.parse(rawConfig), { perFileConfig } = _b, globalConfig = __rest(_b, ["perFileConfig"]);
|
|
113
|
+
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]);
|
|
116
|
+
}
|
|
100
117
|
const spectaMetadata = ((_a = nbMetadata === null || nbMetadata === void 0 ? void 0 : nbMetadata.specta) !== null && _a !== void 0 ? _a : {});
|
|
101
118
|
return Object.assign(Object.assign({}, spectaConfig), spectaMetadata);
|
|
102
119
|
}
|
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.6",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/trungleduc/specta",
|
|
@@ -36,11 +36,15 @@
|
|
|
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 .",
|
|
42
43
|
"install:extension": "npm run build:lib && npm run build:labextension:dev"
|
|
43
44
|
},
|
|
45
|
+
"overrides": {
|
|
46
|
+
"@microsoft/fast-foundation": "~2.49.5"
|
|
47
|
+
},
|
|
44
48
|
"dependencies": {
|
|
45
49
|
"@jupyter-notebook/tree": "^7.4.3",
|
|
46
50
|
"@jupyter-widgets/base": "^6.0.6",
|
|
@@ -49,12 +53,13 @@
|
|
|
49
53
|
"@jupyterlab/apputils": "^4.4.2",
|
|
50
54
|
"@jupyterlab/apputils-extension": "^4.4.2",
|
|
51
55
|
"@jupyterlab/console": "^4.4.2",
|
|
56
|
+
"@jupyterlab/console-extension": "^4.4.2",
|
|
52
57
|
"@jupyterlab/coreutils": "^6.4.2",
|
|
53
58
|
"@jupyterlab/docmanager": "^4.4.2",
|
|
54
59
|
"@jupyterlab/docmanager-extension": "^4.4.2",
|
|
60
|
+
"@jupyterlab/docregistry": "^4.4.2",
|
|
55
61
|
"@jupyterlab/filebrowser": "^4.4.2",
|
|
56
62
|
"@jupyterlab/filebrowser-extension": "^4.4.2",
|
|
57
|
-
"@jupyterlab/docregistry": "^4.4.2",
|
|
58
63
|
"@jupyterlab/json-extension": "^4.4.2",
|
|
59
64
|
"@jupyterlab/logconsole": "^4.4.2",
|
|
60
65
|
"@jupyterlab/mainmenu": "^4.4.2",
|
|
@@ -76,6 +81,7 @@
|
|
|
76
81
|
"@jupyterlab/translation": "^4.4.2",
|
|
77
82
|
"@jupyterlab/translation-extension": "^4.4.2",
|
|
78
83
|
"@jupyterlab/ui-components": "^4.4.2",
|
|
84
|
+
"@jupyterlab/ui-components-extension": "~4.4.2",
|
|
79
85
|
"@jupyterlite/application": "^0.6.2",
|
|
80
86
|
"@jupyterlite/application-extension": "^0.6.2",
|
|
81
87
|
"@jupyterlite/contents": "^0.6.2",
|
|
@@ -104,14 +110,15 @@
|
|
|
104
110
|
},
|
|
105
111
|
"devDependencies": {
|
|
106
112
|
"@jupyterlab/builder": "~4.4.2",
|
|
113
|
+
"@types/react": "^18.0.26",
|
|
107
114
|
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
|
108
115
|
"@typescript-eslint/parser": "^6.1.0",
|
|
109
|
-
"@types/react": "^18.0.26",
|
|
110
116
|
"css-loader": "^7.1.2",
|
|
111
117
|
"eslint": "^8.36.0",
|
|
112
118
|
"eslint-config-prettier": "^8.8.0",
|
|
113
119
|
"eslint-plugin-prettier": "^5.0.0",
|
|
114
120
|
"eslint-plugin-react-hooks": "^5.0.0",
|
|
121
|
+
"fs-extra": "^11.3.0",
|
|
115
122
|
"handlebars": "^4.7.8",
|
|
116
123
|
"html-webpack-plugin": "^5.5.3",
|
|
117
124
|
"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
package/style/style.css
CHANGED
|
@@ -4,16 +4,10 @@
|
|
|
4
4
|
background: var(--jp-layout-color1);
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
#jp-main-content-panel jp-toolbar {
|
|
7
|
+
.specta-document-viewer #jp-main-content-panel jp-toolbar {
|
|
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;
|