@difizen/libro-jupyter 0.1.32 → 0.1.34

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 (114) hide show
  1. package/es/cell/jupyter-code-cell-view.d.ts +3 -1
  2. package/es/cell/jupyter-code-cell-view.d.ts.map +1 -1
  3. package/es/cell/jupyter-code-cell-view.js +9 -0
  4. package/es/components/icons.js +1 -1
  5. package/es/index.d.ts +1 -0
  6. package/es/index.d.ts.map +1 -1
  7. package/es/index.js +2 -1
  8. package/es/keybind-instructions/index.less +0 -5
  9. package/es/keybind-instructions/keybind-instructions-view.d.ts.map +1 -1
  10. package/es/keybind-instructions/keybind-instructions-view.js +67 -61
  11. package/es/module.d.ts.map +1 -1
  12. package/es/module.js +2 -3
  13. package/es/output/libro-jupyter-outputarea.d.ts.map +1 -1
  14. package/es/output/libro-jupyter-outputarea.js +60 -56
  15. package/es/toolbar/save-file-error.d.ts.map +1 -1
  16. package/es/toolbar/save-file-error.js +20 -13
  17. package/es/widget/box/contribution.d.ts +10 -0
  18. package/es/widget/box/contribution.d.ts.map +1 -0
  19. package/es/widget/box/contribution.js +48 -0
  20. package/es/widget/box/index.d.ts +3 -0
  21. package/es/widget/box/index.d.ts.map +1 -0
  22. package/es/widget/box/index.js +2 -0
  23. package/es/widget/box/index.less +3 -0
  24. package/es/widget/box/view.d.ts +19 -0
  25. package/es/widget/box/view.d.ts.map +1 -0
  26. package/es/widget/box/view.js +114 -0
  27. package/es/widget/comm.d.ts +65 -0
  28. package/es/widget/comm.d.ts.map +1 -0
  29. package/es/widget/comm.js +153 -0
  30. package/es/widget/index.d.ts +7 -0
  31. package/es/widget/index.d.ts.map +1 -1
  32. package/es/widget/index.js +8 -1
  33. package/es/widget/instance-progress/contribution.d.ts +10 -0
  34. package/es/widget/instance-progress/contribution.d.ts.map +1 -0
  35. package/es/widget/instance-progress/contribution.js +39 -0
  36. package/es/widget/instance-progress/index.d.ts +3 -0
  37. package/es/widget/instance-progress/index.d.ts.map +1 -0
  38. package/es/widget/instance-progress/index.js +2 -0
  39. package/es/widget/instance-progress/view.d.ts +30 -0
  40. package/es/widget/instance-progress/view.d.ts.map +1 -0
  41. package/es/widget/instance-progress/view.js +180 -0
  42. package/es/widget/libro-widgets.d.ts +84 -0
  43. package/es/widget/libro-widgets.d.ts.map +1 -0
  44. package/es/widget/libro-widgets.js +307 -0
  45. package/es/widget/module.d.ts +4 -0
  46. package/es/widget/module.d.ts.map +1 -0
  47. package/es/widget/module.js +38 -0
  48. package/es/widget/progress/contribution.d.ts +10 -0
  49. package/es/widget/progress/contribution.d.ts.map +1 -0
  50. package/es/widget/progress/contribution.js +39 -0
  51. package/es/widget/progress/index.d.ts +3 -0
  52. package/es/widget/progress/index.d.ts.map +1 -0
  53. package/es/widget/progress/index.js +2 -0
  54. package/es/widget/progress/progressBar.d.ts +15 -0
  55. package/es/widget/progress/progressBar.d.ts.map +1 -0
  56. package/es/widget/progress/progressBar.js +20 -0
  57. package/es/widget/progress/view.d.ts +19 -0
  58. package/es/widget/progress/view.d.ts.map +1 -0
  59. package/es/widget/progress/view.js +74 -0
  60. package/es/widget/protocol.d.ts +193 -0
  61. package/es/widget/protocol.d.ts.map +1 -0
  62. package/es/widget/protocol.js +33 -0
  63. package/es/widget/utils.d.ts +27 -0
  64. package/es/widget/utils.d.ts.map +1 -0
  65. package/es/widget/utils.js +59 -0
  66. package/es/widget/version.d.ts +3 -0
  67. package/es/widget/version.d.ts.map +1 -0
  68. package/es/widget/version.js +2 -0
  69. package/es/widget/widget-manager.d.ts +19 -0
  70. package/es/widget/widget-manager.d.ts.map +1 -0
  71. package/es/widget/widget-manager.js +77 -0
  72. package/es/widget/widget-render.d.ts.map +1 -1
  73. package/es/widget/widget-render.js +7 -3
  74. package/es/widget/widget-rendermime-contribution.d.ts +2 -1
  75. package/es/widget/widget-rendermime-contribution.d.ts.map +1 -1
  76. package/es/widget/widget-rendermime-contribution.js +2 -1
  77. package/es/widget/widget-view-contribution.d.ts +10 -0
  78. package/es/widget/widget-view-contribution.d.ts.map +1 -0
  79. package/es/widget/widget-view-contribution.js +36 -0
  80. package/es/widget/widget-view.d.ts +71 -0
  81. package/es/widget/widget-view.d.ts.map +1 -0
  82. package/es/widget/widget-view.js +273 -0
  83. package/package.json +17 -18
  84. package/src/cell/jupyter-code-cell-view.tsx +10 -1
  85. package/src/components/icons.tsx +1 -1
  86. package/src/index.ts +1 -0
  87. package/src/keybind-instructions/index.less +0 -5
  88. package/src/keybind-instructions/keybind-instructions-view.tsx +70 -60
  89. package/src/module.ts +1 -3
  90. package/src/output/libro-jupyter-outputarea.tsx +56 -49
  91. package/src/toolbar/save-file-error.tsx +25 -15
  92. package/src/widget/box/contribution.ts +29 -0
  93. package/src/widget/box/index.less +3 -0
  94. package/src/widget/box/index.ts +2 -0
  95. package/src/widget/box/view.tsx +112 -0
  96. package/src/widget/comm.ts +152 -0
  97. package/src/widget/index.ts +7 -0
  98. package/src/widget/instance-progress/contribution.ts +20 -0
  99. package/src/widget/instance-progress/index.ts +2 -0
  100. package/src/widget/instance-progress/view.tsx +155 -0
  101. package/src/widget/libro-widgets.ts +223 -0
  102. package/src/widget/module.ts +73 -0
  103. package/src/widget/progress/contribution.ts +24 -0
  104. package/src/widget/progress/index.ts +2 -0
  105. package/src/widget/progress/progressBar.tsx +29 -0
  106. package/src/widget/progress/view.tsx +70 -0
  107. package/src/widget/protocol.ts +255 -0
  108. package/src/widget/utils.ts +67 -0
  109. package/src/widget/version.ts +2 -0
  110. package/src/widget/widget-manager.ts +45 -0
  111. package/src/widget/widget-render.tsx +10 -5
  112. package/src/widget/widget-rendermime-contribution.ts +2 -1
  113. package/src/widget/widget-view-contribution.ts +14 -0
  114. package/src/widget/widget-view.tsx +259 -0
@@ -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,2 @@
1
+ export * from './contribution.js';
2
+ export * from './view.js';
@@ -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
+ }
@@ -0,0 +1,73 @@
1
+ import { LibroKernelManageModule } from '@difizen/libro-kernel';
2
+ import { ManaModule } from '@difizen/mana-app';
3
+
4
+ import { VBoxWidget, VBoxWidgetContribution } from './box/index.js';
5
+ import { Comm } from './comm.js';
6
+ import {
7
+ InstancesProgressWidget,
8
+ InstancesProgressWidgetViewContribution,
9
+ } from './instance-progress/index.js';
10
+ import { LibroWidgets } from './libro-widgets.js';
11
+ import { ProgressWidget, ProgressWidgetViewContribution } from './progress/index.js';
12
+ import {
13
+ LibroWidgetCommFactory,
14
+ LibroWidgetsFactory,
15
+ WidgetCommOption,
16
+ WidgetsOption,
17
+ WidgetViewContribution,
18
+ } from './protocol.js';
19
+ import { LibroWidgetManager } from './widget-manager.js';
20
+ import { LibroWidgetMimeContribution } from './widget-rendermime-contribution.js';
21
+ import { DefaultWidgetViewContribution } from './widget-view-contribution.js';
22
+ import { WidgetView } from './widget-view.js';
23
+
24
+ export const BaseWidgetModule = ManaModule.create()
25
+ .contribution(WidgetViewContribution)
26
+ .register(
27
+ Comm,
28
+ {
29
+ token: LibroWidgetCommFactory,
30
+ useFactory: (ctx) => {
31
+ return (options: WidgetCommOption) => {
32
+ const child = ctx.container.createChild();
33
+ child.register({
34
+ token: WidgetCommOption,
35
+ useValue: options,
36
+ });
37
+ return child.get(Comm);
38
+ };
39
+ },
40
+ },
41
+ LibroWidgets,
42
+ {
43
+ token: LibroWidgetsFactory,
44
+ useFactory: (ctx) => {
45
+ return (options: WidgetsOption) => {
46
+ const child = ctx.container.createChild();
47
+ child.register({
48
+ token: WidgetsOption,
49
+ useValue: options,
50
+ });
51
+ return child.get(LibroWidgets);
52
+ };
53
+ },
54
+ },
55
+ LibroWidgetManager,
56
+ WidgetView,
57
+ DefaultWidgetViewContribution,
58
+ LibroWidgetMimeContribution,
59
+ )
60
+ .dependOn(LibroKernelManageModule);
61
+
62
+ export const WidgetModule = ManaModule.create()
63
+ .register(
64
+ VBoxWidget,
65
+ VBoxWidgetContribution,
66
+
67
+ ProgressWidget,
68
+ ProgressWidgetViewContribution,
69
+
70
+ InstancesProgressWidget,
71
+ InstancesProgressWidgetViewContribution,
72
+ )
73
+ .dependOn(BaseWidgetModule);
@@ -0,0 +1,24 @@
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 { ProgressWidget } from './view.js';
7
+
8
+ @singleton({ contrib: WidgetViewContribution })
9
+ export class ProgressWidgetViewContribution implements WidgetViewContribution {
10
+ @inject(ViewManager) viewManager: ViewManager;
11
+ canHandle = (attributes: any) => {
12
+ if (
13
+ attributes._model_name === 'FloatProgressModel' ||
14
+ attributes._model_name === 'IntProgressModel' ||
15
+ attributes._model_name === 'TransientProgressModel'
16
+ ) {
17
+ return 100;
18
+ }
19
+ return 1;
20
+ };
21
+ factory(props: IWidgetViewProps) {
22
+ return this.viewManager.getOrCreateView(ProgressWidget, props);
23
+ }
24
+ }
@@ -0,0 +1,2 @@
1
+ export * from './contribution.js';
2
+ export * from './view.js';
@@ -0,0 +1,29 @@
1
+ import { Progress } from 'antd';
2
+
3
+ /**
4
+ * Props for the ProgressBar.
5
+ */
6
+ export interface IProgressBarProps {
7
+ /**
8
+ * The current progress percentage, from 0 to 100
9
+ */
10
+ percent: number;
11
+ /**
12
+ * Width of progress bar in pixel.
13
+ */
14
+ width?: number;
15
+ }
16
+
17
+ export function ProgressBar(props: IProgressBarProps) {
18
+ return (
19
+ <>
20
+ <Progress
21
+ strokeLinecap="butt"
22
+ percent={props.percent}
23
+ strokeWidth={18}
24
+ showInfo={false}
25
+ style={{ width: '200px' }}
26
+ />
27
+ </>
28
+ );
29
+ }
@@ -0,0 +1,70 @@
1
+ import type { JSONObject } from '@difizen/libro-common';
2
+ import { LibroContextKey } from '@difizen/libro-core';
3
+ import {
4
+ view,
5
+ ViewOption,
6
+ transient,
7
+ useInject,
8
+ ViewInstance,
9
+ inject,
10
+ prop,
11
+ } from '@difizen/mana-app';
12
+ import { forwardRef } from 'react';
13
+
14
+ import type { IWidgetViewProps, WidgetState } from '../protocol.js';
15
+ import { defaultWidgetState } from '../protocol.js';
16
+ import { WidgetView } from '../widget-view.js';
17
+
18
+ import { ProgressBar } from './progressBar.js';
19
+
20
+ export const LibroProgressWidgetComponent = forwardRef<HTMLDivElement>(
21
+ function LibroProgressWidgetComponent() {
22
+ const widgetView = useInject<ProgressWidget>(ViewInstance);
23
+ const percent =
24
+ widgetView.state.max && widgetView.state.min
25
+ ? widgetView.state.value / ((widgetView.state.max - widgetView.state.min) / 100)
26
+ : 0;
27
+ if (widgetView.isCommClosed) {
28
+ return null;
29
+ }
30
+ return (
31
+ <div className="libro-progress-widget">
32
+ <div className="libro-progress-widget-description">
33
+ {widgetView.state.description}
34
+ </div>
35
+ <ProgressBar percent={percent} />
36
+ </div>
37
+ );
38
+ },
39
+ );
40
+
41
+ interface ProgressState extends WidgetState {
42
+ max?: number;
43
+ min?: number;
44
+ bar_style?: string;
45
+ value: number;
46
+ }
47
+ @transient()
48
+ @view('libro-widget-progress-view')
49
+ export class ProgressWidget extends WidgetView {
50
+ override view = LibroProgressWidgetComponent;
51
+
52
+ @prop()
53
+ override state: JSONObject & ProgressState = {
54
+ ...defaultWidgetState,
55
+ max: 1,
56
+ min: 0,
57
+ value: 0,
58
+ };
59
+ constructor(
60
+ @inject(ViewOption) props: IWidgetViewProps,
61
+ @inject(LibroContextKey) libroContextKey: LibroContextKey,
62
+ ) {
63
+ super(props, libroContextKey);
64
+
65
+ const attributes = props.attributes;
66
+ this.state.max = attributes.max;
67
+ this.state.min = attributes.min;
68
+ this.setState(attributes);
69
+ }
70
+ }