@difizen/libro-jupyter 0.2.0 → 0.2.2
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/es/add-between-cell/add-between-cell.js +1 -1
- package/es/cell/jupyter-code-cell-view.d.ts +3 -1
- package/es/cell/jupyter-code-cell-view.d.ts.map +1 -1
- package/es/cell/jupyter-code-cell-view.js +12 -5
- package/es/command/command-contribution.d.ts.map +1 -1
- package/es/command/command-contribution.js +5 -5
- package/es/components/icons.js +1 -1
- package/es/contents/content-contribution.js +1 -1
- package/es/contents/save-content-contribution.d.ts +10 -0
- package/es/contents/save-content-contribution.d.ts.map +1 -0
- package/es/contents/save-content-contribution.js +110 -0
- package/es/file/file-create-modal.d.ts.map +1 -1
- package/es/file/file-create-modal.js +16 -5
- package/es/file/file-service.js +1 -1
- package/es/file/index.d.ts +1 -0
- package/es/file/index.d.ts.map +1 -1
- package/es/file/index.js +2 -1
- package/es/index.d.ts +1 -0
- package/es/index.d.ts.map +1 -1
- package/es/index.js +2 -1
- package/es/keybind-instructions/index.less +9 -9
- package/es/keybind-instructions/keybind-instructions-view.d.ts +2 -2
- package/es/keybind-instructions/keybind-instructions-view.d.ts.map +1 -1
- package/es/keybind-instructions/keybind-instructions-view.js +69 -63
- package/es/libro-jupyter-model.d.ts +2 -4
- package/es/libro-jupyter-model.d.ts.map +1 -1
- package/es/libro-jupyter-model.js +35 -120
- package/es/libro-jupyter-workspace.d.ts +18 -0
- package/es/libro-jupyter-workspace.d.ts.map +1 -0
- package/es/libro-jupyter-workspace.js +108 -0
- package/es/module.d.ts.map +1 -1
- package/es/module.js +5 -2
- package/es/output/libro-jupyter-outputarea.d.ts.map +1 -1
- package/es/output/libro-jupyter-outputarea.js +60 -56
- package/es/toolbar/save-file-error.d.ts.map +1 -1
- package/es/toolbar/save-file-error.js +20 -13
- package/es/widget/box/contribution.d.ts +10 -0
- package/es/widget/box/contribution.d.ts.map +1 -0
- package/es/widget/box/contribution.js +48 -0
- package/es/widget/box/index.d.ts +3 -0
- package/es/widget/box/index.d.ts.map +1 -0
- package/es/widget/box/index.js +2 -0
- package/es/widget/box/index.less +3 -0
- package/es/widget/box/view.d.ts +19 -0
- package/es/widget/box/view.d.ts.map +1 -0
- package/es/widget/box/view.js +114 -0
- package/es/widget/comm.d.ts +65 -0
- package/es/widget/comm.d.ts.map +1 -0
- package/es/widget/comm.js +153 -0
- package/es/widget/index.d.ts +10 -0
- package/es/widget/index.d.ts.map +1 -0
- package/es/widget/index.js +9 -0
- package/es/widget/index.less +7 -0
- package/es/widget/instance-progress/contribution.d.ts +10 -0
- package/es/widget/instance-progress/contribution.d.ts.map +1 -0
- package/es/widget/instance-progress/contribution.js +39 -0
- package/es/widget/instance-progress/index.d.ts +3 -0
- package/es/widget/instance-progress/index.d.ts.map +1 -0
- package/es/widget/instance-progress/index.js +2 -0
- package/es/widget/instance-progress/view.d.ts +30 -0
- package/es/widget/instance-progress/view.d.ts.map +1 -0
- package/es/widget/instance-progress/view.js +180 -0
- package/es/widget/libro-widgets.d.ts +84 -0
- package/es/widget/libro-widgets.d.ts.map +1 -0
- package/es/widget/libro-widgets.js +307 -0
- package/es/widget/module.d.ts +4 -0
- package/es/widget/module.d.ts.map +1 -0
- package/es/widget/module.js +38 -0
- package/es/widget/progress/contribution.d.ts +10 -0
- package/es/widget/progress/contribution.d.ts.map +1 -0
- package/es/widget/progress/contribution.js +39 -0
- package/es/widget/progress/index.d.ts +3 -0
- package/es/widget/progress/index.d.ts.map +1 -0
- package/es/widget/progress/index.js +2 -0
- package/es/widget/progress/progressBar.d.ts +15 -0
- package/es/widget/progress/progressBar.d.ts.map +1 -0
- package/es/widget/progress/progressBar.js +20 -0
- package/es/widget/progress/view.d.ts +19 -0
- package/es/widget/progress/view.d.ts.map +1 -0
- package/es/widget/progress/view.js +74 -0
- package/es/widget/protocol.d.ts +193 -0
- package/es/widget/protocol.d.ts.map +1 -0
- package/es/widget/protocol.js +33 -0
- package/es/widget/utils.d.ts +27 -0
- package/es/widget/utils.d.ts.map +1 -0
- package/es/widget/utils.js +59 -0
- package/es/widget/version.d.ts +3 -0
- package/es/widget/version.d.ts.map +1 -0
- package/es/widget/version.js +2 -0
- package/es/widget/widget-manager.d.ts +19 -0
- package/es/widget/widget-manager.d.ts.map +1 -0
- package/es/widget/widget-manager.js +77 -0
- package/es/widget/widget-render.d.ts +7 -0
- package/es/widget/widget-render.d.ts.map +1 -0
- package/es/widget/widget-render.js +46 -0
- package/es/widget/widget-rendermime-contribution.d.ts +16 -0
- package/es/widget/widget-rendermime-contribution.d.ts.map +1 -0
- package/es/widget/widget-rendermime-contribution.js +50 -0
- package/es/widget/widget-view-contribution.d.ts +10 -0
- package/es/widget/widget-view-contribution.d.ts.map +1 -0
- package/es/widget/widget-view-contribution.js +36 -0
- package/es/widget/widget-view.d.ts +71 -0
- package/es/widget/widget-view.d.ts.map +1 -0
- package/es/widget/widget-view.js +273 -0
- package/package.json +18 -18
- package/src/add-between-cell/add-between-cell.tsx +1 -1
- package/src/cell/jupyter-code-cell-view.tsx +14 -6
- package/src/command/command-contribution.ts +11 -10
- package/src/components/icons.tsx +1 -1
- package/src/contents/content-contribution.ts +1 -1
- package/src/contents/save-content-contribution.ts +67 -0
- package/src/file/file-create-modal.tsx +10 -1
- package/src/file/file-service.ts +1 -1
- package/src/file/index.ts +1 -0
- package/src/index.ts +1 -0
- package/src/keybind-instructions/index.less +9 -9
- package/src/keybind-instructions/keybind-instructions-view.tsx +72 -62
- package/src/libro-jupyter-model.ts +1 -69
- package/src/libro-jupyter-workspace.ts +49 -0
- package/src/module.ts +6 -0
- package/src/output/libro-jupyter-outputarea.tsx +56 -49
- package/src/toolbar/save-file-error.tsx +25 -15
- package/src/widget/box/contribution.ts +29 -0
- package/src/widget/box/index.less +3 -0
- package/src/widget/box/index.ts +2 -0
- package/src/widget/box/view.tsx +112 -0
- package/src/widget/comm.ts +152 -0
- package/src/widget/index.less +7 -0
- package/src/widget/index.ts +9 -0
- package/src/widget/instance-progress/contribution.ts +20 -0
- package/src/widget/instance-progress/index.ts +2 -0
- package/src/widget/instance-progress/view.tsx +155 -0
- package/src/widget/libro-widgets.ts +223 -0
- package/src/widget/module.ts +73 -0
- package/src/widget/progress/contribution.ts +24 -0
- package/src/widget/progress/index.ts +2 -0
- package/src/widget/progress/progressBar.tsx +29 -0
- package/src/widget/progress/view.tsx +70 -0
- package/src/widget/protocol.ts +255 -0
- package/src/widget/utils.ts +67 -0
- package/src/widget/version.ts +2 -0
- package/src/widget/widget-manager.ts +45 -0
- package/src/widget/widget-render.tsx +52 -0
- package/src/widget/widget-rendermime-contribution.ts +36 -0
- package/src/widget/widget-view-contribution.ts +14 -0
- package/src/widget/widget-view.tsx +259 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { CellView } from '@difizen/libro-core';
|
|
2
|
+
import { LibroContextKey } from '@difizen/libro-core';
|
|
3
|
+
import {
|
|
4
|
+
view,
|
|
5
|
+
transient,
|
|
6
|
+
useInject,
|
|
7
|
+
ViewInstance,
|
|
8
|
+
prop,
|
|
9
|
+
inject,
|
|
10
|
+
ViewOption,
|
|
11
|
+
ViewRender,
|
|
12
|
+
getOrigin,
|
|
13
|
+
} from '@difizen/mana-app';
|
|
14
|
+
import { forwardRef } from 'react';
|
|
15
|
+
|
|
16
|
+
import type { IWidgets, IWidgetViewProps, WidgetState } from '../protocol.js';
|
|
17
|
+
import { defaultWidgetState } from '../protocol.js';
|
|
18
|
+
import { WidgetView } from '../widget-view.js';
|
|
19
|
+
|
|
20
|
+
import './index.less';
|
|
21
|
+
|
|
22
|
+
const WidgetRender = (props: {
|
|
23
|
+
cell?: CellView;
|
|
24
|
+
widgets: IWidgets | undefined;
|
|
25
|
+
modelId: string;
|
|
26
|
+
}) => {
|
|
27
|
+
const { widgets, modelId, cell } = props;
|
|
28
|
+
if (!widgets) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
let widgetView;
|
|
32
|
+
try {
|
|
33
|
+
widgetView = widgets.getModel(modelId);
|
|
34
|
+
} catch (ex) {
|
|
35
|
+
//
|
|
36
|
+
}
|
|
37
|
+
if (!widgetView) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
if (cell) {
|
|
41
|
+
widgetView.setCell(getOrigin(cell));
|
|
42
|
+
}
|
|
43
|
+
if (widgetView.isCommClosed) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return (
|
|
47
|
+
<div className="libro-widget-render-container">
|
|
48
|
+
<div className="libro-widget-render">
|
|
49
|
+
<ViewRender view={widgetView} />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const LibroWidgetBoxComponent = forwardRef<HTMLDivElement>(
|
|
56
|
+
function LibroWidgetBoxComponent(props, ref) {
|
|
57
|
+
const widget = useInject<VBoxWidget>(ViewInstance);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div className={`libro-widget-box ${widget.getCls()}`} ref={ref}>
|
|
61
|
+
{widget.state.children.map((modelId) => (
|
|
62
|
+
<WidgetRender
|
|
63
|
+
key={modelId}
|
|
64
|
+
cell={widget.cell}
|
|
65
|
+
widgets={widget.widgets}
|
|
66
|
+
modelId={modelId}
|
|
67
|
+
/>
|
|
68
|
+
))}
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
interface BoxState extends WidgetState {
|
|
75
|
+
children: string[];
|
|
76
|
+
box_style?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@transient()
|
|
80
|
+
@view('libro-widget-box-view')
|
|
81
|
+
export class VBoxWidget extends WidgetView {
|
|
82
|
+
override view = LibroWidgetBoxComponent;
|
|
83
|
+
|
|
84
|
+
@prop()
|
|
85
|
+
override state: BoxState = {
|
|
86
|
+
...defaultWidgetState,
|
|
87
|
+
children: [],
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
constructor(
|
|
91
|
+
@inject(ViewOption) props: IWidgetViewProps,
|
|
92
|
+
@inject(LibroContextKey) libroContextKey: LibroContextKey,
|
|
93
|
+
) {
|
|
94
|
+
super(props, libroContextKey);
|
|
95
|
+
this.initialize(props);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
protected initialize(props: IWidgetViewProps): void {
|
|
99
|
+
const attributes = props.attributes;
|
|
100
|
+
this.setState(attributes);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
getCls = () => {
|
|
104
|
+
if (this.model_name === 'HBoxModel') {
|
|
105
|
+
return 'libro-widget-hbox';
|
|
106
|
+
}
|
|
107
|
+
if (this.model_name === 'VBoxModel') {
|
|
108
|
+
return 'libro-widget-vbox';
|
|
109
|
+
}
|
|
110
|
+
return '';
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { JSONObject } from '@difizen/libro-common';
|
|
2
|
+
import type { IComm, IKernelConnection, IShellFuture } from '@difizen/libro-kernel';
|
|
3
|
+
import { inject, transient } from '@difizen/mana-app';
|
|
4
|
+
|
|
5
|
+
import type { ICallbacks, IClassicComm } from './protocol.js';
|
|
6
|
+
import { WidgetCommOption } from './protocol.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Public constructor
|
|
10
|
+
* @param {IComm} jsServicesComm - @jupyterlab/services IComm instance
|
|
11
|
+
*/
|
|
12
|
+
@transient()
|
|
13
|
+
export class Comm implements IClassicComm {
|
|
14
|
+
constructor(@inject(WidgetCommOption) options: WidgetCommOption) {
|
|
15
|
+
this.jsServicesComm = options.comm;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Comm id
|
|
20
|
+
* @return {string}
|
|
21
|
+
*/
|
|
22
|
+
get comm_id(): string {
|
|
23
|
+
return this.jsServicesComm.commId;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Target name
|
|
28
|
+
* @return {string}
|
|
29
|
+
*/
|
|
30
|
+
get target_name(): string {
|
|
31
|
+
return this.jsServicesComm.targetName;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Opens a sibling comm in the backend
|
|
36
|
+
* @param data
|
|
37
|
+
* @param callbacks
|
|
38
|
+
* @param metadata
|
|
39
|
+
* @return msg id
|
|
40
|
+
*/
|
|
41
|
+
open(
|
|
42
|
+
data: JSONObject,
|
|
43
|
+
callbacks?: ICallbacks,
|
|
44
|
+
metadata?: JSONObject,
|
|
45
|
+
buffers?: ArrayBuffer[] | ArrayBufferView[],
|
|
46
|
+
): string {
|
|
47
|
+
const future = this.jsServicesComm.open(data, metadata, buffers);
|
|
48
|
+
this._hookupCallbacks(future, callbacks);
|
|
49
|
+
return future.msg.header.msg_id;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Sends a message to the sibling comm in the backend
|
|
54
|
+
* @param data
|
|
55
|
+
* @param callbacks
|
|
56
|
+
* @param metadata
|
|
57
|
+
* @param buffers
|
|
58
|
+
* @return message id
|
|
59
|
+
*/
|
|
60
|
+
send(
|
|
61
|
+
data: JSONObject,
|
|
62
|
+
callbacks?: ICallbacks,
|
|
63
|
+
metadata?: JSONObject,
|
|
64
|
+
buffers?: ArrayBuffer[] | ArrayBufferView[],
|
|
65
|
+
): string {
|
|
66
|
+
const future = this.jsServicesComm.send(data, metadata, buffers);
|
|
67
|
+
this._hookupCallbacks(future, callbacks);
|
|
68
|
+
return future.msg.header.msg_id;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Closes the sibling comm in the backend
|
|
73
|
+
* @param data
|
|
74
|
+
* @param callbacks
|
|
75
|
+
* @param metadata
|
|
76
|
+
* @return msg id
|
|
77
|
+
*/
|
|
78
|
+
close(
|
|
79
|
+
data?: JSONObject,
|
|
80
|
+
callbacks?: ICallbacks,
|
|
81
|
+
metadata?: JSONObject,
|
|
82
|
+
buffers?: ArrayBuffer[] | ArrayBufferView[],
|
|
83
|
+
): string {
|
|
84
|
+
const future = this.jsServicesComm.close(data, metadata, buffers);
|
|
85
|
+
this._hookupCallbacks(future, callbacks);
|
|
86
|
+
return future.msg.header.msg_id;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Register a message handler
|
|
91
|
+
* @param callback, which is given a message
|
|
92
|
+
*/
|
|
93
|
+
onMsg(callback: (x: any) => void): void {
|
|
94
|
+
this.jsServicesComm.onMsg = callback.bind(this);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Register a handler for when the comm is closed by the backend
|
|
99
|
+
* @param callback, which is given a message
|
|
100
|
+
*/
|
|
101
|
+
onClose(callback: (x: any) => void): void {
|
|
102
|
+
this.jsServicesComm.onClose = callback.bind(this);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Hooks callback object up with @jupyterlab/services IKernelFuture
|
|
107
|
+
* @param @jupyterlab/services IKernelFuture instance
|
|
108
|
+
* @param callbacks
|
|
109
|
+
*/
|
|
110
|
+
_hookupCallbacks(future: IShellFuture, callbacks?: ICallbacks): void {
|
|
111
|
+
if (callbacks) {
|
|
112
|
+
future.onReply = function (msg): void {
|
|
113
|
+
if (callbacks.shell && callbacks.shell['reply']) {
|
|
114
|
+
callbacks.shell['reply'](msg);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
future.onStdin = function (msg): void {
|
|
119
|
+
if (callbacks.input) {
|
|
120
|
+
callbacks.input(msg);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
future.onIOPub = function (msg): void {
|
|
125
|
+
if (callbacks.iopub) {
|
|
126
|
+
if (callbacks.iopub['status'] && msg.header.msg_type === 'status') {
|
|
127
|
+
callbacks.iopub['status'](msg);
|
|
128
|
+
} else if (
|
|
129
|
+
callbacks.iopub['clear_output'] &&
|
|
130
|
+
msg.header.msg_type === 'clear_output'
|
|
131
|
+
) {
|
|
132
|
+
callbacks.iopub['clear_output'](msg);
|
|
133
|
+
} else if (callbacks.iopub['output']) {
|
|
134
|
+
switch (msg.header.msg_type) {
|
|
135
|
+
case 'display_data':
|
|
136
|
+
case 'execute_result':
|
|
137
|
+
case 'stream':
|
|
138
|
+
case 'error':
|
|
139
|
+
callbacks.iopub['output'](msg);
|
|
140
|
+
break;
|
|
141
|
+
default:
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
jsServicesComm: IComm;
|
|
151
|
+
kernel: IKernelConnection;
|
|
152
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './widget-render.js';
|
|
2
|
+
export * from './widget-rendermime-contribution.js';
|
|
3
|
+
export * from './libro-widgets.js';
|
|
4
|
+
export * from './widget-view.js';
|
|
5
|
+
export * from './widget-manager.js';
|
|
6
|
+
export * from './comm.js';
|
|
7
|
+
export * from './widget-view-contribution.js';
|
|
8
|
+
export * from './module.js';
|
|
9
|
+
export * from './protocol.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ViewManager, inject, singleton } from '@difizen/mana-app';
|
|
2
|
+
|
|
3
|
+
import type { IWidgetViewProps } from '../protocol.js';
|
|
4
|
+
import { WidgetViewContribution } from '../protocol.js';
|
|
5
|
+
|
|
6
|
+
import { InstancesProgressWidget } from './view.js';
|
|
7
|
+
|
|
8
|
+
@singleton({ contrib: WidgetViewContribution })
|
|
9
|
+
export class InstancesProgressWidgetViewContribution implements WidgetViewContribution {
|
|
10
|
+
@inject(ViewManager) viewManager: ViewManager;
|
|
11
|
+
canHandle = (attributes: any) => {
|
|
12
|
+
if (attributes._model_name === 'InstancesProgressModel') {
|
|
13
|
+
return 100;
|
|
14
|
+
}
|
|
15
|
+
return 1;
|
|
16
|
+
};
|
|
17
|
+
factory(props: IWidgetViewProps) {
|
|
18
|
+
return this.viewManager.getOrCreateView(InstancesProgressWidget, props);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { LibroContextKey } from '@difizen/libro-core';
|
|
2
|
+
import type { KernelMessage } from '@difizen/libro-kernel';
|
|
3
|
+
import {
|
|
4
|
+
useInject,
|
|
5
|
+
view,
|
|
6
|
+
ViewInstance,
|
|
7
|
+
ViewOption,
|
|
8
|
+
transient,
|
|
9
|
+
inject,
|
|
10
|
+
prop,
|
|
11
|
+
} from '@difizen/mana-app';
|
|
12
|
+
import { forwardRef } from 'react';
|
|
13
|
+
|
|
14
|
+
import type { IWidgetViewProps } from '../protocol.js';
|
|
15
|
+
import type { InstanceRecord, InstancesRecords, ProgressItem } from '../protocol.js';
|
|
16
|
+
import { WidgetView } from '../widget-view.js';
|
|
17
|
+
|
|
18
|
+
export interface ProgressOverviewProps {
|
|
19
|
+
progressMap: Record<string, ProgressItem>;
|
|
20
|
+
workingProgressKeys: string[];
|
|
21
|
+
prefix: string;
|
|
22
|
+
suffix: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const LibroInstancesProgressWidgetViewComponent = forwardRef<HTMLDivElement>(
|
|
26
|
+
function LibroInstancesProgressWidgetViewComponent(_props, ref) {
|
|
27
|
+
const widgetView = useInject<InstancesProgressWidget>(ViewInstance);
|
|
28
|
+
if (widgetView.isCommClosed) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return (
|
|
32
|
+
<div ref={ref} className="libro-instances-progress-widget">
|
|
33
|
+
<span style={{ paddingRight: 5 }}>{widgetView.prefix}</span>
|
|
34
|
+
<span>
|
|
35
|
+
{widgetView.workingProgressKeys.map((progressKey) => {
|
|
36
|
+
const progressItem = widgetView.progressMap[progressKey];
|
|
37
|
+
return (
|
|
38
|
+
<a
|
|
39
|
+
className="pyodps-progress-launcher"
|
|
40
|
+
style={{ marginRight: 5 }}
|
|
41
|
+
key={progressKey}
|
|
42
|
+
>
|
|
43
|
+
{progressItem && progressItem.name}
|
|
44
|
+
</a>
|
|
45
|
+
);
|
|
46
|
+
})}
|
|
47
|
+
</span>
|
|
48
|
+
<span>{widgetView.suffix}</span>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
);
|
|
53
|
+
@transient()
|
|
54
|
+
@view('libro-widget-instances-progress-view')
|
|
55
|
+
export class InstancesProgressWidget extends WidgetView {
|
|
56
|
+
override view = LibroInstancesProgressWidgetViewComponent;
|
|
57
|
+
@prop()
|
|
58
|
+
prefix: string;
|
|
59
|
+
@prop()
|
|
60
|
+
suffix: string;
|
|
61
|
+
progressMap: Record<string, ProgressItem> = {};
|
|
62
|
+
workingProgressKeys: string[] = []; // Order of groups by time of insertion
|
|
63
|
+
instanceRecords: InstancesRecords = {};
|
|
64
|
+
modalVisible = false;
|
|
65
|
+
modalProgressItemKey = '';
|
|
66
|
+
constructor(
|
|
67
|
+
@inject(ViewOption) props: IWidgetViewProps,
|
|
68
|
+
@inject(LibroContextKey) libroContextKey: LibroContextKey,
|
|
69
|
+
) {
|
|
70
|
+
super(props, libroContextKey);
|
|
71
|
+
this.prefix = props.attributes.prefix;
|
|
72
|
+
this.suffix = props.attributes.suffix;
|
|
73
|
+
}
|
|
74
|
+
updateRecords(progressKey: string) {
|
|
75
|
+
const progressItem = this.progressMap[progressKey];
|
|
76
|
+
if (progressItem) {
|
|
77
|
+
const { instances = [] } = progressItem;
|
|
78
|
+
instances.forEach((instance) => {
|
|
79
|
+
const { id, status } = instance;
|
|
80
|
+
if (!this.instanceRecords[id]) {
|
|
81
|
+
this.instanceRecords[id] = {
|
|
82
|
+
startDate: Date.now(),
|
|
83
|
+
} as InstanceRecord;
|
|
84
|
+
}
|
|
85
|
+
if (status === 'Terminated') {
|
|
86
|
+
if (!this.instanceRecords[id].endDate) {
|
|
87
|
+
this.instanceRecords[id].endDate = Date.now();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Handle incoming comm msg.
|
|
95
|
+
*/
|
|
96
|
+
override handleCommMsg(msg: KernelMessage.ICommMsgMsg): Promise<void> {
|
|
97
|
+
const data = msg.content.data as any;
|
|
98
|
+
const method = data.method;
|
|
99
|
+
switch (method) {
|
|
100
|
+
case 'update':
|
|
101
|
+
if (data.state.prefix) {
|
|
102
|
+
this.prefix = data.state.prefix;
|
|
103
|
+
}
|
|
104
|
+
if (data.state.suffix) {
|
|
105
|
+
this.suffix = data.state.suffix;
|
|
106
|
+
}
|
|
107
|
+
// eslint-disable-next-line no-fallthrough
|
|
108
|
+
case 'custom':
|
|
109
|
+
// eslint-disable-next-line no-case-declarations
|
|
110
|
+
const customMsg = data.content;
|
|
111
|
+
if (customMsg) {
|
|
112
|
+
// message format: '{"action": "action", content: ["content1", "content2"]}'
|
|
113
|
+
const msgObj: { action: 'update' | 'delete' | 'clear'; content: string[] } =
|
|
114
|
+
JSON.parse(customMsg);
|
|
115
|
+
const action: string = msgObj.action;
|
|
116
|
+
const content: string[] = [];
|
|
117
|
+
if (msgObj.content) {
|
|
118
|
+
content.push(...msgObj.content);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
switch (action) {
|
|
122
|
+
case 'update':
|
|
123
|
+
content.forEach((groupJson) => {
|
|
124
|
+
const parsedProgressItem: ProgressItem = JSON.parse(groupJson);
|
|
125
|
+
if (!this.progressMap[parsedProgressItem.key]) {
|
|
126
|
+
this.workingProgressKeys.push(parsedProgressItem.key);
|
|
127
|
+
}
|
|
128
|
+
this.progressMap[parsedProgressItem.key] = parsedProgressItem;
|
|
129
|
+
this.updateRecords(parsedProgressItem.key);
|
|
130
|
+
});
|
|
131
|
+
// ? TODO: 发出一个更新 modal signal 的信号,这里需要取到之前的 key
|
|
132
|
+
break;
|
|
133
|
+
case 'delete':
|
|
134
|
+
content.forEach((groupKey: string) => {
|
|
135
|
+
if (!this.progressMap[groupKey]) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const i = this.workingProgressKeys.indexOf(groupKey);
|
|
139
|
+
if (i >= 0) {
|
|
140
|
+
this.workingProgressKeys.splice(i, 1);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
break;
|
|
144
|
+
case 'clear':
|
|
145
|
+
this.progressMap = {};
|
|
146
|
+
this.workingProgressKeys = [];
|
|
147
|
+
// ? TODO: 发出更新 overview 以及 modal 的 signal
|
|
148
|
+
break;
|
|
149
|
+
default:
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return Promise.resolve();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type { JSONObject } from '@difizen/libro-common';
|
|
2
|
+
import type { IKernelConnection, KernelMessage } from '@difizen/libro-kernel';
|
|
3
|
+
import type { Contribution } from '@difizen/mana-app';
|
|
4
|
+
import { contrib, inject, Priority, prop, transient } from '@difizen/mana-app';
|
|
5
|
+
import { Emitter } from '@difizen/mana-app';
|
|
6
|
+
|
|
7
|
+
import type { Comm } from './comm.js';
|
|
8
|
+
import type {
|
|
9
|
+
ISerializedState,
|
|
10
|
+
IWidgets,
|
|
11
|
+
IWidgetViewOptions,
|
|
12
|
+
IClassicComm,
|
|
13
|
+
WidgetCommOption,
|
|
14
|
+
} from './protocol.js';
|
|
15
|
+
import {
|
|
16
|
+
LibroWidgetCommFactory,
|
|
17
|
+
WidgetsOption,
|
|
18
|
+
WidgetViewContribution,
|
|
19
|
+
} from './protocol.js';
|
|
20
|
+
import { put_buffers, reject } from './utils.js';
|
|
21
|
+
import { PROTOCOL_VERSION } from './version.js';
|
|
22
|
+
import type { WidgetView } from './widget-view.js';
|
|
23
|
+
|
|
24
|
+
const PROTOCOL_MAJOR_VERSION = PROTOCOL_VERSION.split('.', 1)[0];
|
|
25
|
+
|
|
26
|
+
@transient()
|
|
27
|
+
export class LibroWidgets implements IWidgets {
|
|
28
|
+
@contrib(WidgetViewContribution)
|
|
29
|
+
WidgetViewProvider: Contribution.Provider<WidgetViewContribution>;
|
|
30
|
+
widgetEmitter: Emitter<{ WidgetViewName: string }> = new Emitter();
|
|
31
|
+
widgetCommFactory: (options: WidgetCommOption) => Comm;
|
|
32
|
+
kernelConnection: IKernelConnection;
|
|
33
|
+
|
|
34
|
+
constructor(
|
|
35
|
+
@inject(WidgetsOption) options: WidgetsOption,
|
|
36
|
+
@inject(LibroWidgetCommFactory)
|
|
37
|
+
widgetCommFactory: (options: WidgetCommOption) => Comm,
|
|
38
|
+
) {
|
|
39
|
+
this.kernelConnection = options.kc;
|
|
40
|
+
this.id = options.id;
|
|
41
|
+
this.widgetCommFactory = widgetCommFactory;
|
|
42
|
+
this.kernelConnection.registerCommTarget(this.commTargetName, async (comm, msg) => {
|
|
43
|
+
const widgetComm = this.widgetCommFactory({ comm });
|
|
44
|
+
await this.handleCommOpen(widgetComm, msg);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
get onWidgetRender() {
|
|
48
|
+
return this.widgetEmitter.event;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected findProvider(attributes: any): WidgetViewContribution {
|
|
52
|
+
const prioritized = Priority.sortSync(
|
|
53
|
+
this.WidgetViewProvider.getContributions(),
|
|
54
|
+
(contribution) => contribution.canHandle(attributes),
|
|
55
|
+
);
|
|
56
|
+
const sorted = prioritized.map((c) => c.value);
|
|
57
|
+
return sorted[0]!;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create a comm which can be used for communication for a widget.
|
|
62
|
+
*
|
|
63
|
+
* If the data/metadata is passed in, open the comm before returning (i.e.,
|
|
64
|
+
* send the comm_open message). If the data and metadata is undefined, we
|
|
65
|
+
* want to reconstruct a comm that already exists in the kernel, so do not
|
|
66
|
+
* open the comm by sending the comm_open message.
|
|
67
|
+
*
|
|
68
|
+
* @param comm_target_name Comm target name
|
|
69
|
+
* @param model_id The comm id
|
|
70
|
+
* @param data The initial data for the comm
|
|
71
|
+
* @param metadata The metadata in the open message
|
|
72
|
+
*/
|
|
73
|
+
async createComm(
|
|
74
|
+
comm_target_name: string,
|
|
75
|
+
model_id?: string,
|
|
76
|
+
data?: JSONObject,
|
|
77
|
+
metadata?: JSONObject,
|
|
78
|
+
buffers?: ArrayBuffer[] | ArrayBufferView[],
|
|
79
|
+
): Promise<IClassicComm> {
|
|
80
|
+
const kernel = this.kernelConnection;
|
|
81
|
+
if (!kernel) {
|
|
82
|
+
throw new Error('No current kernel');
|
|
83
|
+
}
|
|
84
|
+
const comm = kernel.createComm(comm_target_name, model_id);
|
|
85
|
+
if (data || metadata) {
|
|
86
|
+
comm.open(data, metadata, buffers);
|
|
87
|
+
}
|
|
88
|
+
return this.widgetCommFactory({ comm });
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Get a model by model id.
|
|
93
|
+
*
|
|
94
|
+
* #### Notes
|
|
95
|
+
* If the model is not found, throw error.
|
|
96
|
+
*
|
|
97
|
+
* If you would like to synchronously test if a model exists, use .hasModel().
|
|
98
|
+
*/
|
|
99
|
+
getModel(model_id: string): WidgetView {
|
|
100
|
+
const model = this.models.get(model_id);
|
|
101
|
+
if (model === undefined) {
|
|
102
|
+
throw new Error('widget model not found');
|
|
103
|
+
}
|
|
104
|
+
return model;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Returns true if the given model is registered, otherwise false.
|
|
109
|
+
*
|
|
110
|
+
* #### Notes
|
|
111
|
+
* This is a synchronous way to check if a model is registered.
|
|
112
|
+
*/
|
|
113
|
+
hasModel(model_id: string): boolean {
|
|
114
|
+
return this.models.get(model_id) !== undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Handle when a comm is opened.
|
|
119
|
+
*/
|
|
120
|
+
async handleCommOpen(
|
|
121
|
+
comm: IClassicComm,
|
|
122
|
+
msg: KernelMessage.ICommOpenMsg,
|
|
123
|
+
): Promise<WidgetView> {
|
|
124
|
+
const protocolVersion = ((msg.metadata || {})['version'] as string) || '';
|
|
125
|
+
if (protocolVersion.split('.', 1)[0] !== PROTOCOL_MAJOR_VERSION) {
|
|
126
|
+
const error = `Wrong widget protocol version: received protocol version '${protocolVersion}', but was expecting major version '${PROTOCOL_MAJOR_VERSION}'`;
|
|
127
|
+
console.error(error);
|
|
128
|
+
return Promise.reject(error);
|
|
129
|
+
}
|
|
130
|
+
const data = msg.content.data as unknown as ISerializedState;
|
|
131
|
+
const buffer_paths = data.buffer_paths || [];
|
|
132
|
+
const buffers = msg.buffers || [];
|
|
133
|
+
put_buffers(data.state, buffer_paths, buffers);
|
|
134
|
+
// this.createComm(msg.content.target_name, msg.content.comm_id, msg.content.data, msg.metadata);
|
|
135
|
+
return this.newWidgetView(data.state, {
|
|
136
|
+
model_id: msg.content.comm_id,
|
|
137
|
+
comm,
|
|
138
|
+
}).catch(reject('Could not create a model.', true));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
registerWidgetView(model_id: string, model: Promise<WidgetView>): void {
|
|
142
|
+
model
|
|
143
|
+
.then((model) => {
|
|
144
|
+
this.models.set(model_id, model);
|
|
145
|
+
this.models.set(model.toModelKey(), model);
|
|
146
|
+
this.widgetEmitter.fire({ WidgetViewName: model.model_name });
|
|
147
|
+
return;
|
|
148
|
+
})
|
|
149
|
+
.catch(() => {
|
|
150
|
+
//
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
handleCommClose(msg: KernelMessage.ICommCloseMsg) {
|
|
155
|
+
const comm_id = msg.content.comm_id;
|
|
156
|
+
const model = this.getModel(comm_id);
|
|
157
|
+
model.isCommClosed = true;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
unregisterWidgetView(model_id: string): void {
|
|
161
|
+
const model = this.models.get(model_id);
|
|
162
|
+
model?.dispose();
|
|
163
|
+
this.models.delete(model_id);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async newWidgetView(
|
|
167
|
+
attributes: any,
|
|
168
|
+
options: IWidgetViewOptions,
|
|
169
|
+
): Promise<WidgetView> {
|
|
170
|
+
const model_id = options.model_id;
|
|
171
|
+
if (!model_id) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
'Neither comm nor model_id provided in options object. At least one must exist.',
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
options.model_id = model_id;
|
|
177
|
+
const provider = this.findProvider(attributes);
|
|
178
|
+
const WidgetView = provider.factory({
|
|
179
|
+
attributes: attributes,
|
|
180
|
+
options: options,
|
|
181
|
+
widgetsId: this.id,
|
|
182
|
+
});
|
|
183
|
+
this.registerWidgetView(model_id, WidgetView);
|
|
184
|
+
return WidgetView;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Close all widgets and empty the widget state.
|
|
189
|
+
* @return Promise that resolves when the widget state is cleared.
|
|
190
|
+
*/
|
|
191
|
+
clearState() {
|
|
192
|
+
this.models.clear();
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Disconnect the widget manager from the kernel, setting each model's comm
|
|
196
|
+
* as dead.
|
|
197
|
+
*/
|
|
198
|
+
disconnect(): void {
|
|
199
|
+
// this.models.forEach(model => model.clear());
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Dictionary of model ids and model instance promises
|
|
203
|
+
*/
|
|
204
|
+
@prop()
|
|
205
|
+
protected models: Map<string, WidgetView> = new Map();
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The comm target name to register
|
|
209
|
+
*/
|
|
210
|
+
id: string;
|
|
211
|
+
readonly commTargetName = 'jupyter.widget';
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Serialize the model. See the deserialization function at the top of this file
|
|
215
|
+
* and the kernel-side serializer/deserializer.
|
|
216
|
+
*/
|
|
217
|
+
toJSON(): string {
|
|
218
|
+
return JSON.stringify({
|
|
219
|
+
kc_id: this.kernelConnection.id,
|
|
220
|
+
id: this.id,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|