jupyter-specta 0.1.2 → 0.1.4
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/lib/app.js +1 -1
- package/lib/components/cellSkeleton.d.ts +6 -0
- package/lib/components/cellSkeleton.js +19 -0
- package/lib/components/divider.d.ts +2 -0
- package/lib/components/divider.js +4 -0
- package/lib/components/icon/gear.d.ts +2 -0
- package/lib/components/icon/gear.js +17 -0
- package/lib/components/iconButton.d.ts +8 -0
- package/lib/components/iconButton.js +4 -0
- package/lib/create_notebook_panel.d.ts +7 -1
- package/lib/create_notebook_panel.js +37 -1
- package/lib/document/factory.d.ts +5 -1
- package/lib/document/factory.js +13 -3
- package/lib/extension_plugins.js +11 -2
- package/lib/layout/article.d.ts +12 -0
- package/lib/layout/article.js +35 -0
- package/lib/layout/default.js +2 -5
- package/lib/layout/{plugin.js → index.js} +5 -0
- package/lib/layout/layout_registry.d.ts +1 -0
- package/lib/layout/layout_registry.js +6 -0
- package/lib/metadata/index.d.ts +3 -0
- package/lib/metadata/index.js +14 -0
- package/lib/shell.js +0 -1
- package/lib/specta_cell_output.d.ts +11 -2
- package/lib/specta_cell_output.js +22 -1
- package/lib/specta_model.d.ts +6 -4
- package/lib/specta_model.js +89 -13
- package/lib/specta_widget.d.ts +5 -1
- package/lib/specta_widget.js +25 -8
- package/lib/specta_widget_factory.js +7 -2
- package/lib/token.d.ts +18 -0
- package/lib/tool.d.ts +5 -1
- package/lib/tool.js +40 -6
- package/lib/topbar/index.d.ts +5 -0
- package/lib/topbar/index.js +30 -0
- package/lib/topbar/kernelStatus.d.ts +28 -0
- package/lib/topbar/kernelStatus.js +20 -0
- package/lib/topbar/settingDialog.d.ts +7 -0
- package/lib/topbar/settingDialog.js +73 -0
- package/lib/topbar/widget.d.ts +9 -0
- package/lib/topbar/widget.js +41 -0
- package/package.json +23 -13
- package/schema/app-meta.json +39 -0
- package/schema/cell-meta.json +37 -0
- package/style/article.css +314 -0
- package/style/base.css +125 -0
- package/style/index.css +2 -0
- package/style/index.js +3 -1
- package/style/skeleton.css +87 -0
- package/style/style.css +9 -4
- /package/lib/layout/{plugin.d.ts → index.d.ts} +0 -0
package/lib/specta_widget.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Message } from '@lumino/messaging';
|
|
2
2
|
import { Panel } from '@lumino/widgets';
|
|
3
3
|
import { AppModel } from './specta_model';
|
|
4
|
-
import { ISpectaLayoutRegistry } from './token';
|
|
4
|
+
import { ISpectaAppConfig, ISpectaLayoutRegistry } from './token';
|
|
5
5
|
export declare class AppWidget extends Panel {
|
|
6
6
|
constructor(options: AppWidget.IOptions);
|
|
7
7
|
/**
|
|
@@ -13,11 +13,14 @@ export declare class AppWidget extends Panel {
|
|
|
13
13
|
dispose(): void;
|
|
14
14
|
render(): Promise<void>;
|
|
15
15
|
protected onCloseRequest(msg: Message): void;
|
|
16
|
+
private _onSelectedLayoutChanged;
|
|
16
17
|
private _model;
|
|
17
18
|
private _ready;
|
|
18
19
|
private _host;
|
|
19
20
|
private _layoutRegistry;
|
|
20
21
|
private _loaderHost?;
|
|
22
|
+
private _outputs;
|
|
23
|
+
private _spectaAppConfig;
|
|
21
24
|
}
|
|
22
25
|
export declare namespace AppWidget {
|
|
23
26
|
interface IOptions {
|
|
@@ -25,5 +28,6 @@ export declare namespace AppWidget {
|
|
|
25
28
|
label: string;
|
|
26
29
|
model: AppModel;
|
|
27
30
|
layoutRegistry: ISpectaLayoutRegistry;
|
|
31
|
+
spectaConfig: ISpectaAppConfig;
|
|
28
32
|
}
|
|
29
33
|
}
|
package/lib/specta_widget.js
CHANGED
|
@@ -5,12 +5,13 @@ export class AppWidget extends Panel {
|
|
|
5
5
|
constructor(options) {
|
|
6
6
|
super();
|
|
7
7
|
this._ready = new PromiseDelegate();
|
|
8
|
+
this._outputs = [];
|
|
8
9
|
this.node.id = options.id;
|
|
9
10
|
this.title.label = options.label;
|
|
10
11
|
this.title.closable = true;
|
|
11
12
|
this._model = options.model;
|
|
13
|
+
this._spectaAppConfig = options.spectaConfig;
|
|
12
14
|
this._layoutRegistry = options.layoutRegistry;
|
|
13
|
-
this.node.style.padding = '5px';
|
|
14
15
|
this._host = new Panel();
|
|
15
16
|
this._host.addClass('specta-output-host');
|
|
16
17
|
this.addWidget(this._host);
|
|
@@ -24,6 +25,7 @@ export class AppWidget extends Panel {
|
|
|
24
25
|
.catch(console.error)
|
|
25
26
|
.then(() => window.dispatchEvent(new Event('resize')));
|
|
26
27
|
});
|
|
28
|
+
this._layoutRegistry.selectedLayoutChanged.connect(this._onSelectedLayoutChanged, this);
|
|
27
29
|
}
|
|
28
30
|
/**
|
|
29
31
|
* A promise that is fulfilled when the model is ready.
|
|
@@ -54,17 +56,17 @@ export class AppWidget extends Panel {
|
|
|
54
56
|
super.dispose();
|
|
55
57
|
}
|
|
56
58
|
async render() {
|
|
57
|
-
var _a;
|
|
59
|
+
var _a, _b, _c, _d, _e;
|
|
58
60
|
const cellList = (_a = this._model.cells) !== null && _a !== void 0 ? _a : [];
|
|
59
|
-
const
|
|
61
|
+
const layout = (_c = (_b = this._spectaAppConfig) === null || _b === void 0 ? void 0 : _b.defaultLayout) !== null && _c !== void 0 ? _c : 'default';
|
|
60
62
|
for (const cell of cellList) {
|
|
61
63
|
const src = cell.sharedModel.source;
|
|
62
64
|
if (src.length === 0) {
|
|
63
65
|
continue;
|
|
64
66
|
}
|
|
65
67
|
const el = this._model.createCell(cell);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
this._model.executeCell(cell, el);
|
|
69
|
+
this._outputs.push(el);
|
|
68
70
|
}
|
|
69
71
|
const readyCallback = async () => {
|
|
70
72
|
if (this._loaderHost) {
|
|
@@ -78,10 +80,11 @@ export class AppWidget extends Panel {
|
|
|
78
80
|
hideAppLoadingIndicator();
|
|
79
81
|
}
|
|
80
82
|
};
|
|
81
|
-
|
|
83
|
+
const spectaLayout = (_d = this._layoutRegistry.get(layout)) !== null && _d !== void 0 ? _d : this._layoutRegistry.getDefaultLayout();
|
|
84
|
+
await spectaLayout.render({
|
|
82
85
|
host: this._host,
|
|
83
|
-
items:
|
|
84
|
-
notebook: this._model.context.model.toJSON(),
|
|
86
|
+
items: this._outputs,
|
|
87
|
+
notebook: (_e = this._model.context) === null || _e === void 0 ? void 0 : _e.model.toJSON(),
|
|
85
88
|
readyCallback
|
|
86
89
|
});
|
|
87
90
|
}
|
|
@@ -89,4 +92,18 @@ export class AppWidget extends Panel {
|
|
|
89
92
|
this._model.dispose();
|
|
90
93
|
super.onCloseRequest(msg);
|
|
91
94
|
}
|
|
95
|
+
_onSelectedLayoutChanged(sender, args) {
|
|
96
|
+
var _a;
|
|
97
|
+
const currentEls = [...this._host.widgets];
|
|
98
|
+
currentEls.forEach(el => {
|
|
99
|
+
var _a;
|
|
100
|
+
(_a = this._host.layout) === null || _a === void 0 ? void 0 : _a.removeWidget(el);
|
|
101
|
+
});
|
|
102
|
+
args.layout.render({
|
|
103
|
+
host: this._host,
|
|
104
|
+
items: this._outputs,
|
|
105
|
+
notebook: (_a = this._model.context) === null || _a === void 0 ? void 0 : _a.model.toJSON(),
|
|
106
|
+
readyCallback: async () => { }
|
|
107
|
+
});
|
|
108
|
+
}
|
|
92
109
|
}
|
|
@@ -2,16 +2,20 @@ import { StaticNotebook } from '@jupyterlab/notebook';
|
|
|
2
2
|
import { AppModel } from './specta_model';
|
|
3
3
|
import { AppWidget } from './specta_widget';
|
|
4
4
|
import { UUID } from '@lumino/coreutils';
|
|
5
|
+
import { readSpectaConfig } from './tool';
|
|
5
6
|
export class SpectaWidgetFactory {
|
|
6
7
|
constructor(options) {
|
|
7
8
|
this._options = options;
|
|
8
9
|
}
|
|
9
10
|
async createNew(options) {
|
|
10
11
|
const { context } = options;
|
|
12
|
+
const rendermime = this._options.rendermime.clone({
|
|
13
|
+
resolver: context.urlResolver
|
|
14
|
+
});
|
|
11
15
|
const model = new AppModel({
|
|
12
16
|
context,
|
|
13
17
|
manager: this._options.manager,
|
|
14
|
-
rendermime
|
|
18
|
+
rendermime,
|
|
15
19
|
tracker: this._options.tracker,
|
|
16
20
|
contentFactory: this._options.contentFactory,
|
|
17
21
|
mimeTypeService: this._options.mimeTypeService,
|
|
@@ -23,7 +27,8 @@ export class SpectaWidgetFactory {
|
|
|
23
27
|
id: UUID.uuid4(),
|
|
24
28
|
label: '',
|
|
25
29
|
model,
|
|
26
|
-
layoutRegistry: this._options.spectaLayoutRegistry
|
|
30
|
+
layoutRegistry: this._options.spectaLayoutRegistry,
|
|
31
|
+
spectaConfig: readSpectaConfig(context.model.metadata)
|
|
27
32
|
});
|
|
28
33
|
return panel;
|
|
29
34
|
}
|
package/lib/token.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface ISpectaLayout {
|
|
|
14
14
|
}
|
|
15
15
|
export interface ISpectaLayoutRegistry {
|
|
16
16
|
get(name: string): ISpectaLayout | undefined;
|
|
17
|
+
getDefaultLayout(): ISpectaLayout;
|
|
17
18
|
register(name: string, layout: ISpectaLayout): void;
|
|
18
19
|
allLayouts(): string[];
|
|
19
20
|
layoutAdded: ISignal<ISpectaLayoutRegistry, string>;
|
|
@@ -27,5 +28,22 @@ export interface ISpectaLayoutRegistry {
|
|
|
27
28
|
layout: ISpectaLayout;
|
|
28
29
|
}>;
|
|
29
30
|
}
|
|
31
|
+
export interface ITopbarConfig {
|
|
32
|
+
background?: string;
|
|
33
|
+
textColor?: string;
|
|
34
|
+
title?: string;
|
|
35
|
+
icon?: string;
|
|
36
|
+
kernelActivity?: boolean;
|
|
37
|
+
themeToggle?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface ISpectaAppConfig {
|
|
40
|
+
topBar?: ITopbarConfig;
|
|
41
|
+
defaultLayout?: string;
|
|
42
|
+
hideTopbar?: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface ISpectaCellConfig {
|
|
45
|
+
showSource?: boolean;
|
|
46
|
+
showOutput?: boolean;
|
|
47
|
+
}
|
|
30
48
|
export declare const ISpectaLayoutRegistry: Token<ISpectaLayoutRegistry>;
|
|
31
49
|
export declare const ISpectaDocTracker: Token<IWidgetTracker<Widget>>;
|
package/lib/tool.d.ts
CHANGED
|
@@ -5,7 +5,9 @@ import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook';
|
|
|
5
5
|
import { IRenderMimeRegistry } from '@jupyterlab/rendermime';
|
|
6
6
|
import { VoilaFileBrowser } from '@voila-dashboards/voila';
|
|
7
7
|
import { IDocumentManager } from '@jupyterlab/docmanager';
|
|
8
|
-
import { ISpectaLayoutRegistry } from './token';
|
|
8
|
+
import { ISpectaAppConfig, ISpectaCellConfig, ISpectaLayoutRegistry } from './token';
|
|
9
|
+
import { INotebookMetadata } from '@jupyterlab/nbformat';
|
|
10
|
+
import { ICell } from '@jupyterlab/nbformat';
|
|
9
11
|
export declare function registerDocumentFactory(options: {
|
|
10
12
|
factoryName: string;
|
|
11
13
|
app: JupyterFrontEnd;
|
|
@@ -21,3 +23,5 @@ export declare function createFileBrowser(options: {
|
|
|
21
23
|
}): VoilaFileBrowser;
|
|
22
24
|
export declare function hideAppLoadingIndicator(): void;
|
|
23
25
|
export declare function isSpectaApp(): boolean;
|
|
26
|
+
export declare function readSpectaConfig(nbMetadata?: INotebookMetadata): ISpectaAppConfig;
|
|
27
|
+
export declare function readCellConfig(cell?: ICell): Required<ISpectaCellConfig>;
|
package/lib/tool.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
|
2
2
|
import { FilterFileBrowserModel } from '@jupyterlab/filebrowser';
|
|
3
|
+
import { NotebookModelFactory } from '@jupyterlab/notebook';
|
|
3
4
|
import { VoilaFileBrowser } from '@voila-dashboards/voila';
|
|
4
5
|
import { NotebookGridWidgetFactory } from './document/factory';
|
|
5
6
|
import { SpectaWidgetFactory } from './specta_widget_factory';
|
|
@@ -17,15 +18,23 @@ export function registerDocumentFactory(options) {
|
|
|
17
18
|
const widgetFactory = new NotebookGridWidgetFactory({
|
|
18
19
|
name: factoryName,
|
|
19
20
|
modelName: 'notebook',
|
|
20
|
-
fileTypes: ['
|
|
21
|
-
spectaWidgetFactory
|
|
22
|
-
preferKernel: true,
|
|
23
|
-
canStartKernel: true,
|
|
24
|
-
autoStartDefault: true,
|
|
25
|
-
shutdownOnClose: true
|
|
21
|
+
fileTypes: ['ipynb'],
|
|
22
|
+
spectaWidgetFactory
|
|
26
23
|
});
|
|
27
24
|
// Registering the widget factory
|
|
28
25
|
app.docRegistry.addWidgetFactory(widgetFactory);
|
|
26
|
+
// Creating and registering the model factory for our custom DocumentModelAdd commentMore actions
|
|
27
|
+
const modelFactory = new NotebookModelFactory({});
|
|
28
|
+
app.docRegistry.addModelFactory(modelFactory);
|
|
29
|
+
// register the filetype
|
|
30
|
+
app.docRegistry.addFileType({
|
|
31
|
+
name: 'ipynb',
|
|
32
|
+
displayName: 'IPYNB',
|
|
33
|
+
mimeTypes: ['text/json'],
|
|
34
|
+
extensions: ['.ipynb', '.IPYNB'],
|
|
35
|
+
fileFormat: 'json',
|
|
36
|
+
contentType: 'notebook'
|
|
37
|
+
});
|
|
29
38
|
widgetFactory.widgetCreated.connect((sender, widget) => {
|
|
30
39
|
widget.context.pathChanged.connect(() => {
|
|
31
40
|
spectaTracker.save(widget);
|
|
@@ -81,3 +90,28 @@ export function hideAppLoadingIndicator() {
|
|
|
81
90
|
export function isSpectaApp() {
|
|
82
91
|
return !!document.querySelector('meta[name="specta-config"]');
|
|
83
92
|
}
|
|
93
|
+
export function readSpectaConfig(nbMetadata) {
|
|
94
|
+
var _a;
|
|
95
|
+
let rawConfig = PageConfig.getOption('spectaConfig');
|
|
96
|
+
if (!rawConfig || rawConfig.length === 0) {
|
|
97
|
+
rawConfig = '{}';
|
|
98
|
+
}
|
|
99
|
+
const spectaConfig = JSON.parse(rawConfig);
|
|
100
|
+
const spectaMetadata = ((_a = nbMetadata === null || nbMetadata === void 0 ? void 0 : nbMetadata.specta) !== null && _a !== void 0 ? _a : {});
|
|
101
|
+
return Object.assign(Object.assign({}, spectaConfig), spectaMetadata);
|
|
102
|
+
}
|
|
103
|
+
export function readCellConfig(cell) {
|
|
104
|
+
var _a, _b;
|
|
105
|
+
const metaData = ((_b = (_a = cell === null || cell === void 0 ? void 0 : cell.metadata) === null || _a === void 0 ? void 0 : _a.specta) !== null && _b !== void 0 ? _b : {});
|
|
106
|
+
const spectaCellConfig = {
|
|
107
|
+
showSource: false,
|
|
108
|
+
showOutput: true
|
|
109
|
+
};
|
|
110
|
+
if (metaData.showSource && metaData.showSource === 'Yes') {
|
|
111
|
+
spectaCellConfig.showSource = true;
|
|
112
|
+
}
|
|
113
|
+
if (metaData.showOutput && metaData.showOutput === 'No') {
|
|
114
|
+
spectaCellConfig.showOutput = false;
|
|
115
|
+
}
|
|
116
|
+
return spectaCellConfig;
|
|
117
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { IThemeManager } from '@jupyterlab/apputils';
|
|
2
|
+
import { ReactWidget } from '@jupyterlab/ui-components';
|
|
3
|
+
import { TopbarElement } from './widget';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { isSpectaApp, readSpectaConfig } from '../tool';
|
|
6
|
+
import { ISpectaLayoutRegistry } from '../token';
|
|
7
|
+
/**
|
|
8
|
+
* Initialization data for the voila_topbar extension.
|
|
9
|
+
*/
|
|
10
|
+
export const topbarPlugin = {
|
|
11
|
+
id: 'specta:topba',
|
|
12
|
+
description: 'Specta topbar extension',
|
|
13
|
+
autoStart: true,
|
|
14
|
+
requires: [IThemeManager, ISpectaLayoutRegistry],
|
|
15
|
+
activate: (app, themeManager, layoutRegistry) => {
|
|
16
|
+
const isSpecta = isSpectaApp();
|
|
17
|
+
if (!isSpecta) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const config = readSpectaConfig();
|
|
21
|
+
const widget = ReactWidget.create(React.createElement(TopbarElement, { config: config.topBar, themeManager: themeManager, layoutRegistry: layoutRegistry }));
|
|
22
|
+
widget.id = 'specta-topbar-widget';
|
|
23
|
+
widget.addClass('specta-topbar-element');
|
|
24
|
+
app.shell.add(widget, 'top');
|
|
25
|
+
if (widget.parent) {
|
|
26
|
+
widget.parent.node.style.boxShadow =
|
|
27
|
+
'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';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare namespace ProgressCircle {
|
|
2
|
+
/**
|
|
3
|
+
* Props for the ProgressBar.
|
|
4
|
+
*/
|
|
5
|
+
interface IProps {
|
|
6
|
+
/**
|
|
7
|
+
* The current progress percentage, from 0 to 100
|
|
8
|
+
*/
|
|
9
|
+
progress: number;
|
|
10
|
+
/**
|
|
11
|
+
* The aria-label for the widget
|
|
12
|
+
*/
|
|
13
|
+
label?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Element width
|
|
16
|
+
*/
|
|
17
|
+
width?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Element height
|
|
20
|
+
*/
|
|
21
|
+
height?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Color ò the stroke
|
|
24
|
+
*/
|
|
25
|
+
stroke?: string;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export declare function ProgressCircle(props: ProgressCircle.IProps): JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export function ProgressCircle(props) {
|
|
3
|
+
var _a, _b;
|
|
4
|
+
const radius = 104;
|
|
5
|
+
const d = (progress) => {
|
|
6
|
+
const angle = Math.max(progress * 3.6, 0.1);
|
|
7
|
+
const rad = (angle * Math.PI) / 180, x = Math.sin(rad) * radius, y = Math.cos(rad) * -radius, mid = angle < 180 ? 1 : 0, shape = `M 0 0 v -${radius} A ${radius} ${radius} 1 ` +
|
|
8
|
+
mid +
|
|
9
|
+
' 0 ' +
|
|
10
|
+
x.toFixed(4) +
|
|
11
|
+
' ' +
|
|
12
|
+
y.toFixed(4) +
|
|
13
|
+
' z';
|
|
14
|
+
return shape;
|
|
15
|
+
};
|
|
16
|
+
return (React.createElement("div", { className: 'jp-Statusbar-ProgressCircle', role: "progressbar", "aria-label": props.label || 'Unlabelled progress circle', "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": props.progress, style: { margin: 'auto' } },
|
|
17
|
+
React.createElement("svg", { viewBox: "0 0 250 250" },
|
|
18
|
+
React.createElement("circle", { cx: "125", cy: "125", r: `${radius}`, strokeWidth: "20", fill: "none", stroke: (_a = props.stroke) !== null && _a !== void 0 ? _a : 'var(--jp-inverse-layout-color2)' }),
|
|
19
|
+
React.createElement("path", { transform: "translate(125,125) scale(.9)", d: d(props.progress), fill: (_b = props.stroke) !== null && _b !== void 0 ? _b : 'var(--jp-inverse-layout-color2)' }))));
|
|
20
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { IThemeManager } from '@jupyterlab/apputils';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { ISpectaLayoutRegistry } from '../token';
|
|
4
|
+
export declare const SettingContent: (props: {
|
|
5
|
+
themeManager?: IThemeManager;
|
|
6
|
+
layoutRegistry?: ISpectaLayoutRegistry;
|
|
7
|
+
}) => React.JSX.Element;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { Divider } from '../components/divider';
|
|
3
|
+
export const SettingContent = (props) => {
|
|
4
|
+
var _a, _b, _c, _d, _e;
|
|
5
|
+
const { themeManager, layoutRegistry } = props;
|
|
6
|
+
const [themeOptions, setThemeOptions] = useState([
|
|
7
|
+
...((_a = themeManager === null || themeManager === void 0 ? void 0 : themeManager.themes) !== null && _a !== void 0 ? _a : [])
|
|
8
|
+
]);
|
|
9
|
+
const [selectedTheme, setSelectedTheme] = useState((_b = themeManager === null || themeManager === void 0 ? void 0 : themeManager.theme) !== null && _b !== void 0 ? _b : 'light');
|
|
10
|
+
const [layoutOptions, setLayoutOptions] = useState((_c = layoutRegistry === null || layoutRegistry === void 0 ? void 0 : layoutRegistry.allLayouts()) !== null && _c !== void 0 ? _c : []);
|
|
11
|
+
const [selectedLayout, setSelectedLayout] = useState((_e = (_d = layoutRegistry === null || layoutRegistry === void 0 ? void 0 : layoutRegistry.selectedLayout) === null || _d === void 0 ? void 0 : _d.name) !== null && _e !== void 0 ? _e : 'default');
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
let cb;
|
|
14
|
+
if (themeManager) {
|
|
15
|
+
cb = (sender, args) => {
|
|
16
|
+
if (args.newValue.length > 0) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
setThemeOptions([...themeManager.themes]);
|
|
20
|
+
if (themeManager.theme) {
|
|
21
|
+
setSelectedTheme(themeManager.theme);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
themeManager.themeChanged.connect(cb);
|
|
25
|
+
}
|
|
26
|
+
if (layoutRegistry) {
|
|
27
|
+
const layoutAddedCb = (sender, newLayout) => {
|
|
28
|
+
setLayoutOptions(layoutRegistry.allLayouts());
|
|
29
|
+
};
|
|
30
|
+
layoutRegistry.layoutAdded.connect(layoutAddedCb);
|
|
31
|
+
}
|
|
32
|
+
return () => {
|
|
33
|
+
if (themeManager && cb) {
|
|
34
|
+
themeManager.themeChanged.disconnect(cb);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}, [themeManager, layoutRegistry]);
|
|
38
|
+
const onThemeChange = useCallback((e) => {
|
|
39
|
+
var _a;
|
|
40
|
+
const theme = (_a = e.currentTarget) === null || _a === void 0 ? void 0 : _a.value;
|
|
41
|
+
if (theme) {
|
|
42
|
+
themeManager === null || themeManager === void 0 ? void 0 : themeManager.setTheme(theme);
|
|
43
|
+
setSelectedTheme(theme);
|
|
44
|
+
}
|
|
45
|
+
}, [themeManager]);
|
|
46
|
+
const onLayoutChange = useCallback((e) => {
|
|
47
|
+
var _a;
|
|
48
|
+
const layout = (_a = e.currentTarget) === null || _a === void 0 ? void 0 : _a.value;
|
|
49
|
+
if (layout && layoutRegistry) {
|
|
50
|
+
layoutRegistry.setSelectedLayout(layout);
|
|
51
|
+
setSelectedLayout(layout);
|
|
52
|
+
}
|
|
53
|
+
}, [layoutRegistry]);
|
|
54
|
+
return (React.createElement("div", { style: { padding: '0 10px' } },
|
|
55
|
+
React.createElement("p", { style: { marginTop: 0, marginBottom: '5px', fontSize: '1rem' } }, "SETTINGS"),
|
|
56
|
+
React.createElement(Divider, null),
|
|
57
|
+
layoutRegistry && (React.createElement("div", null,
|
|
58
|
+
React.createElement("label", { htmlFor: "" }, "Select layout"),
|
|
59
|
+
React.createElement("div", { className: "jp-select-wrapper" },
|
|
60
|
+
React.createElement("select", { className: " jp-mod-styled specta-topbar-theme", onChange: onLayoutChange, value: selectedLayout }, layoutOptions.map(el => {
|
|
61
|
+
return (React.createElement("option", { key: el, value: el, style: {
|
|
62
|
+
background: 'var(--jp-layout-color2)'
|
|
63
|
+
} }, el.charAt(0).toUpperCase() + el.slice(1)));
|
|
64
|
+
}))))),
|
|
65
|
+
themeManager && (React.createElement("div", null,
|
|
66
|
+
React.createElement("label", { htmlFor: "" }, "Select theme"),
|
|
67
|
+
React.createElement("div", { className: "jp-select-wrapper" },
|
|
68
|
+
React.createElement("select", { className: " jp-mod-styled specta-topbar-theme", onChange: onThemeChange, value: selectedTheme }, themeOptions.map(el => {
|
|
69
|
+
return (React.createElement("option", { key: el, value: el, style: {
|
|
70
|
+
background: 'var(--jp-layout-color2)'
|
|
71
|
+
} }, el));
|
|
72
|
+
})))))));
|
|
73
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IThemeManager } from '@jupyterlab/apputils';
|
|
2
|
+
import { ISpectaLayoutRegistry, ITopbarConfig } from '../token';
|
|
3
|
+
interface IProps {
|
|
4
|
+
config?: ITopbarConfig;
|
|
5
|
+
themeManager?: IThemeManager;
|
|
6
|
+
layoutRegistry?: ISpectaLayoutRegistry;
|
|
7
|
+
}
|
|
8
|
+
export declare function TopbarElement(props: IProps): JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React, { useState, useRef, useEffect } from 'react';
|
|
2
|
+
import { GearIcon } from '../components/icon/gear';
|
|
3
|
+
import { IconButton } from '../components/iconButton';
|
|
4
|
+
import { SettingContent } from './settingDialog';
|
|
5
|
+
export function TopbarElement(props) {
|
|
6
|
+
var _a, _b;
|
|
7
|
+
const config = React.useMemo(() => {
|
|
8
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
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)',
|
|
11
|
+
title: (_d = (_c = props.config) === null || _c === void 0 ? void 0 : _c.title) !== null && _d !== void 0 ? _d : 'Specta',
|
|
12
|
+
themeToggle: Boolean((_e = props.config) === null || _e === void 0 ? void 0 : _e.themeToggle),
|
|
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)'
|
|
15
|
+
};
|
|
16
|
+
}, [props.config]);
|
|
17
|
+
const [open, setOpen] = useState(false);
|
|
18
|
+
const buttonRef = useRef(null);
|
|
19
|
+
const dialogRef = useRef(null);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const handleClickOutside = (e) => {
|
|
22
|
+
if (dialogRef.current &&
|
|
23
|
+
!dialogRef.current.contains(e.target) &&
|
|
24
|
+
buttonRef.current &&
|
|
25
|
+
!buttonRef.current.contains(e.target)) {
|
|
26
|
+
setOpen(false);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
30
|
+
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
31
|
+
}, []);
|
|
32
|
+
return (React.createElement("div", { className: "specta-topbar", style: { background: (_a = config.background) !== null && _a !== void 0 ? _a : 'var(--jp-layout-color2)' } },
|
|
33
|
+
React.createElement("div", { className: "specta-topbar-left" },
|
|
34
|
+
React.createElement("div", null, config.icon && React.createElement("img", { style: { width: '50px' }, src: config.icon })),
|
|
35
|
+
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
|
+
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
|
+
open && (React.createElement("div", { ref: dialogRef, className: "jp-Dialog-content specta-config-dialog" },
|
|
39
|
+
React.createElement("div", { className: "specta-config-arrow" }),
|
|
40
|
+
React.createElement(SettingContent, { themeManager: props.themeManager, layoutRegistry: props.layoutRegistry }))))));
|
|
41
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyter-specta",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://github.com/trungleduc/specta",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
"lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}",
|
|
17
17
|
"style/**/*.{css,js,eot,gif,html,jpg,json,png,svg,woff2,ttf}",
|
|
18
|
-
"dist/**/*.{js,js.map,.txt}"
|
|
18
|
+
"dist/**/*.{js,js.map,.txt}",
|
|
19
|
+
"schema/*.json"
|
|
19
20
|
],
|
|
20
21
|
"main": "lib/extension_plugins.js",
|
|
21
22
|
"types": "lib/extension_plugins.d.ts",
|
|
@@ -29,6 +30,7 @@
|
|
|
29
30
|
"build:lib": "tsc",
|
|
30
31
|
"build:labextension": "jupyter labextension build .",
|
|
31
32
|
"build:labextension:dev": "jupyter labextension build --development True .",
|
|
33
|
+
"build:lab": "npm run clean && npm run build:lib && npm run build:labextension",
|
|
32
34
|
"build:app": "npm run clean && npm run build:lib && cd app && NODE_OPTIONS=--max-old-space-size=4096 NODE_ENV=production webpack --mode production",
|
|
33
35
|
"build:app:dev": "npm run clean && npm run build:lib && cd app && webpack",
|
|
34
36
|
"build:all": "npm run build:app && npm run build:labextension",
|
|
@@ -74,13 +76,13 @@
|
|
|
74
76
|
"@jupyterlab/translation": "^4.4.2",
|
|
75
77
|
"@jupyterlab/translation-extension": "^4.4.2",
|
|
76
78
|
"@jupyterlab/ui-components": "^4.4.2",
|
|
77
|
-
"@jupyterlite/application": "^0.6.
|
|
78
|
-
"@jupyterlite/application-extension": "^0.6.
|
|
79
|
-
"@jupyterlite/contents": "^0.6.
|
|
80
|
-
"@jupyterlite/iframe-extension": "^0.6.
|
|
81
|
-
"@jupyterlite/kernel": "^0.6.
|
|
82
|
-
"@jupyterlite/server": "^0.6.
|
|
83
|
-
"@jupyterlite/services-extension": "^0.6.
|
|
79
|
+
"@jupyterlite/application": "^0.6.2",
|
|
80
|
+
"@jupyterlite/application-extension": "^0.6.2",
|
|
81
|
+
"@jupyterlite/contents": "^0.6.2",
|
|
82
|
+
"@jupyterlite/iframe-extension": "^0.6.2",
|
|
83
|
+
"@jupyterlite/kernel": "^0.6.2",
|
|
84
|
+
"@jupyterlite/server": "^0.6.2",
|
|
85
|
+
"@jupyterlite/services-extension": "^0.6.2",
|
|
84
86
|
"@lumino/algorithm": "^2.0.0",
|
|
85
87
|
"@lumino/application": "^2.0.0",
|
|
86
88
|
"@lumino/commands": "^2.0.0",
|
|
@@ -96,16 +98,20 @@
|
|
|
96
98
|
"@lumino/signaling": "^2.0.0",
|
|
97
99
|
"@lumino/virtualdom": "^2.0.0",
|
|
98
100
|
"@lumino/widgets": "^2.0.0",
|
|
99
|
-
"@voila-dashboards/voila": "^0.5.5"
|
|
101
|
+
"@voila-dashboards/voila": "^0.5.5",
|
|
102
|
+
"react": "^18.3.0",
|
|
103
|
+
"react-dom": "^18.3.0"
|
|
100
104
|
},
|
|
101
105
|
"devDependencies": {
|
|
102
106
|
"@jupyterlab/builder": "~4.4.2",
|
|
103
107
|
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
|
104
108
|
"@typescript-eslint/parser": "^6.1.0",
|
|
109
|
+
"@types/react": "^18.0.26",
|
|
105
110
|
"css-loader": "^7.1.2",
|
|
106
111
|
"eslint": "^8.36.0",
|
|
107
112
|
"eslint-config-prettier": "^8.8.0",
|
|
108
113
|
"eslint-plugin-prettier": "^5.0.0",
|
|
114
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
|
109
115
|
"handlebars": "^4.7.8",
|
|
110
116
|
"html-webpack-plugin": "^5.5.3",
|
|
111
117
|
"ignore-loader": "^0.1.2",
|
|
@@ -140,7 +146,8 @@
|
|
|
140
146
|
"eslint:recommended",
|
|
141
147
|
"plugin:@typescript-eslint/eslint-recommended",
|
|
142
148
|
"plugin:@typescript-eslint/recommended",
|
|
143
|
-
"plugin:prettier/recommended"
|
|
149
|
+
"plugin:prettier/recommended",
|
|
150
|
+
"plugin:react-hooks/recommended"
|
|
144
151
|
],
|
|
145
152
|
"parser": "@typescript-eslint/parser",
|
|
146
153
|
"parserOptions": {
|
|
@@ -148,9 +155,11 @@
|
|
|
148
155
|
"sourceType": "module"
|
|
149
156
|
},
|
|
150
157
|
"plugins": [
|
|
151
|
-
"@typescript-eslint"
|
|
158
|
+
"@typescript-eslint",
|
|
159
|
+
"react-hooks"
|
|
152
160
|
],
|
|
153
161
|
"rules": {
|
|
162
|
+
"react-hooks/exhaustive-deps": "error",
|
|
154
163
|
"@typescript-eslint/naming-convention": [
|
|
155
164
|
"error",
|
|
156
165
|
{
|
|
@@ -226,6 +235,7 @@
|
|
|
226
235
|
},
|
|
227
236
|
"jupyterlab": {
|
|
228
237
|
"extension": true,
|
|
229
|
-
"outputDir": "specta/labextension"
|
|
238
|
+
"outputDir": "specta/labextension",
|
|
239
|
+
"schemaDir": "schema"
|
|
230
240
|
}
|
|
231
241
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"title": "Metadata Form example",
|
|
4
|
+
"description": "Settings of the metadata form extension.",
|
|
5
|
+
"jupyter.lab.metadataforms": [
|
|
6
|
+
{
|
|
7
|
+
"id": "jupyter-specta:app-meta",
|
|
8
|
+
"label": "Specta App Config",
|
|
9
|
+
"metadataSchema": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"properties": {
|
|
12
|
+
"/specta/hideTopbar": {
|
|
13
|
+
"title": "Hide top bar",
|
|
14
|
+
"type": "boolean",
|
|
15
|
+
"enum": [true, false],
|
|
16
|
+
"default": false
|
|
17
|
+
},
|
|
18
|
+
"/specta/defaultLayout": {
|
|
19
|
+
"title": "Page layout",
|
|
20
|
+
"type": "string",
|
|
21
|
+
"enum": ["default", "article"],
|
|
22
|
+
"default": "default"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"metadataOptions": {
|
|
27
|
+
"/specta/hideTopbar": {
|
|
28
|
+
"metadataLevel": "notebook",
|
|
29
|
+
"writeDefault": false
|
|
30
|
+
},
|
|
31
|
+
"/specta/defaultLayout": {
|
|
32
|
+
"metadataLevel": "notebook",
|
|
33
|
+
"writeDefault": false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"additionalProperties": false
|
|
39
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"title": "Metadata Form example",
|
|
4
|
+
"description": "Settings of the metadata form extension.",
|
|
5
|
+
"jupyter.lab.metadataforms": [
|
|
6
|
+
{
|
|
7
|
+
"id": "jupyter-specta:cell-meta",
|
|
8
|
+
"label": "Specta Cell Config",
|
|
9
|
+
"metadataSchema": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"properties": {
|
|
12
|
+
"/specta/showSource": {
|
|
13
|
+
"title": "Show cell source",
|
|
14
|
+
"type": "string",
|
|
15
|
+
"enum": ["Yes", "No"],
|
|
16
|
+
"default": "No"
|
|
17
|
+
},
|
|
18
|
+
"/specta/showOutput": {
|
|
19
|
+
"title": "Show output placeholder",
|
|
20
|
+
"type": "string",
|
|
21
|
+
"enum": ["Yes", "No"],
|
|
22
|
+
"default": "Yes"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"metadataOptions": {
|
|
27
|
+
"/specta/showSource": {
|
|
28
|
+
"writeDefault": false
|
|
29
|
+
},
|
|
30
|
+
"/specta/showOutput": {
|
|
31
|
+
"writeDefault": false
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"additionalProperties": false
|
|
37
|
+
}
|