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.
Files changed (51) hide show
  1. package/lib/app.js +1 -1
  2. package/lib/components/cellSkeleton.d.ts +6 -0
  3. package/lib/components/cellSkeleton.js +19 -0
  4. package/lib/components/divider.d.ts +2 -0
  5. package/lib/components/divider.js +4 -0
  6. package/lib/components/icon/gear.d.ts +2 -0
  7. package/lib/components/icon/gear.js +17 -0
  8. package/lib/components/iconButton.d.ts +8 -0
  9. package/lib/components/iconButton.js +4 -0
  10. package/lib/create_notebook_panel.d.ts +7 -1
  11. package/lib/create_notebook_panel.js +37 -1
  12. package/lib/document/factory.d.ts +5 -1
  13. package/lib/document/factory.js +13 -3
  14. package/lib/extension_plugins.js +11 -2
  15. package/lib/layout/article.d.ts +12 -0
  16. package/lib/layout/article.js +35 -0
  17. package/lib/layout/default.js +2 -5
  18. package/lib/layout/{plugin.js → index.js} +5 -0
  19. package/lib/layout/layout_registry.d.ts +1 -0
  20. package/lib/layout/layout_registry.js +6 -0
  21. package/lib/metadata/index.d.ts +3 -0
  22. package/lib/metadata/index.js +14 -0
  23. package/lib/shell.js +0 -1
  24. package/lib/specta_cell_output.d.ts +11 -2
  25. package/lib/specta_cell_output.js +22 -1
  26. package/lib/specta_model.d.ts +6 -4
  27. package/lib/specta_model.js +89 -13
  28. package/lib/specta_widget.d.ts +5 -1
  29. package/lib/specta_widget.js +25 -8
  30. package/lib/specta_widget_factory.js +7 -2
  31. package/lib/token.d.ts +18 -0
  32. package/lib/tool.d.ts +5 -1
  33. package/lib/tool.js +40 -6
  34. package/lib/topbar/index.d.ts +5 -0
  35. package/lib/topbar/index.js +30 -0
  36. package/lib/topbar/kernelStatus.d.ts +28 -0
  37. package/lib/topbar/kernelStatus.js +20 -0
  38. package/lib/topbar/settingDialog.d.ts +7 -0
  39. package/lib/topbar/settingDialog.js +73 -0
  40. package/lib/topbar/widget.d.ts +9 -0
  41. package/lib/topbar/widget.js +41 -0
  42. package/package.json +23 -13
  43. package/schema/app-meta.json +39 -0
  44. package/schema/cell-meta.json +37 -0
  45. package/style/article.css +314 -0
  46. package/style/base.css +125 -0
  47. package/style/index.css +2 -0
  48. package/style/index.js +3 -1
  49. package/style/skeleton.css +87 -0
  50. package/style/style.css +9 -4
  51. /package/lib/layout/{plugin.d.ts → index.d.ts} +0 -0
@@ -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
  }
@@ -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 outputs = [];
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
- await this._model.executeCell(cell, el.cellOutput);
67
- outputs.push(el);
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
- await this._layoutRegistry.selectedLayout.layout.render({
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: outputs,
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: this._options.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: ['notebook'],
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,5 @@
1
+ import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2
+ /**
3
+ * Initialization data for the voila_topbar extension.
4
+ */
5
+ export declare const topbarPlugin: JupyterFrontEndPlugin<void>;
@@ -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.2",
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.0",
78
- "@jupyterlite/application-extension": "^0.6.0",
79
- "@jupyterlite/contents": "^0.6.0",
80
- "@jupyterlite/iframe-extension": "^0.6.0",
81
- "@jupyterlite/kernel": "^0.6.0",
82
- "@jupyterlite/server": "^0.6.0",
83
- "@jupyterlite/services-extension": "^0.6.0",
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
+ }