@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.
Files changed (146) hide show
  1. package/es/add-between-cell/add-between-cell.js +1 -1
  2. package/es/cell/jupyter-code-cell-view.d.ts +3 -1
  3. package/es/cell/jupyter-code-cell-view.d.ts.map +1 -1
  4. package/es/cell/jupyter-code-cell-view.js +12 -5
  5. package/es/command/command-contribution.d.ts.map +1 -1
  6. package/es/command/command-contribution.js +5 -5
  7. package/es/components/icons.js +1 -1
  8. package/es/contents/content-contribution.js +1 -1
  9. package/es/contents/save-content-contribution.d.ts +10 -0
  10. package/es/contents/save-content-contribution.d.ts.map +1 -0
  11. package/es/contents/save-content-contribution.js +110 -0
  12. package/es/file/file-create-modal.d.ts.map +1 -1
  13. package/es/file/file-create-modal.js +16 -5
  14. package/es/file/file-service.js +1 -1
  15. package/es/file/index.d.ts +1 -0
  16. package/es/file/index.d.ts.map +1 -1
  17. package/es/file/index.js +2 -1
  18. package/es/index.d.ts +1 -0
  19. package/es/index.d.ts.map +1 -1
  20. package/es/index.js +2 -1
  21. package/es/keybind-instructions/index.less +9 -9
  22. package/es/keybind-instructions/keybind-instructions-view.d.ts +2 -2
  23. package/es/keybind-instructions/keybind-instructions-view.d.ts.map +1 -1
  24. package/es/keybind-instructions/keybind-instructions-view.js +69 -63
  25. package/es/libro-jupyter-model.d.ts +2 -4
  26. package/es/libro-jupyter-model.d.ts.map +1 -1
  27. package/es/libro-jupyter-model.js +35 -120
  28. package/es/libro-jupyter-workspace.d.ts +18 -0
  29. package/es/libro-jupyter-workspace.d.ts.map +1 -0
  30. package/es/libro-jupyter-workspace.js +108 -0
  31. package/es/module.d.ts.map +1 -1
  32. package/es/module.js +5 -2
  33. package/es/output/libro-jupyter-outputarea.d.ts.map +1 -1
  34. package/es/output/libro-jupyter-outputarea.js +60 -56
  35. package/es/toolbar/save-file-error.d.ts.map +1 -1
  36. package/es/toolbar/save-file-error.js +20 -13
  37. package/es/widget/box/contribution.d.ts +10 -0
  38. package/es/widget/box/contribution.d.ts.map +1 -0
  39. package/es/widget/box/contribution.js +48 -0
  40. package/es/widget/box/index.d.ts +3 -0
  41. package/es/widget/box/index.d.ts.map +1 -0
  42. package/es/widget/box/index.js +2 -0
  43. package/es/widget/box/index.less +3 -0
  44. package/es/widget/box/view.d.ts +19 -0
  45. package/es/widget/box/view.d.ts.map +1 -0
  46. package/es/widget/box/view.js +114 -0
  47. package/es/widget/comm.d.ts +65 -0
  48. package/es/widget/comm.d.ts.map +1 -0
  49. package/es/widget/comm.js +153 -0
  50. package/es/widget/index.d.ts +10 -0
  51. package/es/widget/index.d.ts.map +1 -0
  52. package/es/widget/index.js +9 -0
  53. package/es/widget/index.less +7 -0
  54. package/es/widget/instance-progress/contribution.d.ts +10 -0
  55. package/es/widget/instance-progress/contribution.d.ts.map +1 -0
  56. package/es/widget/instance-progress/contribution.js +39 -0
  57. package/es/widget/instance-progress/index.d.ts +3 -0
  58. package/es/widget/instance-progress/index.d.ts.map +1 -0
  59. package/es/widget/instance-progress/index.js +2 -0
  60. package/es/widget/instance-progress/view.d.ts +30 -0
  61. package/es/widget/instance-progress/view.d.ts.map +1 -0
  62. package/es/widget/instance-progress/view.js +180 -0
  63. package/es/widget/libro-widgets.d.ts +84 -0
  64. package/es/widget/libro-widgets.d.ts.map +1 -0
  65. package/es/widget/libro-widgets.js +307 -0
  66. package/es/widget/module.d.ts +4 -0
  67. package/es/widget/module.d.ts.map +1 -0
  68. package/es/widget/module.js +38 -0
  69. package/es/widget/progress/contribution.d.ts +10 -0
  70. package/es/widget/progress/contribution.d.ts.map +1 -0
  71. package/es/widget/progress/contribution.js +39 -0
  72. package/es/widget/progress/index.d.ts +3 -0
  73. package/es/widget/progress/index.d.ts.map +1 -0
  74. package/es/widget/progress/index.js +2 -0
  75. package/es/widget/progress/progressBar.d.ts +15 -0
  76. package/es/widget/progress/progressBar.d.ts.map +1 -0
  77. package/es/widget/progress/progressBar.js +20 -0
  78. package/es/widget/progress/view.d.ts +19 -0
  79. package/es/widget/progress/view.d.ts.map +1 -0
  80. package/es/widget/progress/view.js +74 -0
  81. package/es/widget/protocol.d.ts +193 -0
  82. package/es/widget/protocol.d.ts.map +1 -0
  83. package/es/widget/protocol.js +33 -0
  84. package/es/widget/utils.d.ts +27 -0
  85. package/es/widget/utils.d.ts.map +1 -0
  86. package/es/widget/utils.js +59 -0
  87. package/es/widget/version.d.ts +3 -0
  88. package/es/widget/version.d.ts.map +1 -0
  89. package/es/widget/version.js +2 -0
  90. package/es/widget/widget-manager.d.ts +19 -0
  91. package/es/widget/widget-manager.d.ts.map +1 -0
  92. package/es/widget/widget-manager.js +77 -0
  93. package/es/widget/widget-render.d.ts +7 -0
  94. package/es/widget/widget-render.d.ts.map +1 -0
  95. package/es/widget/widget-render.js +46 -0
  96. package/es/widget/widget-rendermime-contribution.d.ts +16 -0
  97. package/es/widget/widget-rendermime-contribution.d.ts.map +1 -0
  98. package/es/widget/widget-rendermime-contribution.js +50 -0
  99. package/es/widget/widget-view-contribution.d.ts +10 -0
  100. package/es/widget/widget-view-contribution.d.ts.map +1 -0
  101. package/es/widget/widget-view-contribution.js +36 -0
  102. package/es/widget/widget-view.d.ts +71 -0
  103. package/es/widget/widget-view.d.ts.map +1 -0
  104. package/es/widget/widget-view.js +273 -0
  105. package/package.json +18 -18
  106. package/src/add-between-cell/add-between-cell.tsx +1 -1
  107. package/src/cell/jupyter-code-cell-view.tsx +14 -6
  108. package/src/command/command-contribution.ts +11 -10
  109. package/src/components/icons.tsx +1 -1
  110. package/src/contents/content-contribution.ts +1 -1
  111. package/src/contents/save-content-contribution.ts +67 -0
  112. package/src/file/file-create-modal.tsx +10 -1
  113. package/src/file/file-service.ts +1 -1
  114. package/src/file/index.ts +1 -0
  115. package/src/index.ts +1 -0
  116. package/src/keybind-instructions/index.less +9 -9
  117. package/src/keybind-instructions/keybind-instructions-view.tsx +72 -62
  118. package/src/libro-jupyter-model.ts +1 -69
  119. package/src/libro-jupyter-workspace.ts +49 -0
  120. package/src/module.ts +6 -0
  121. package/src/output/libro-jupyter-outputarea.tsx +56 -49
  122. package/src/toolbar/save-file-error.tsx +25 -15
  123. package/src/widget/box/contribution.ts +29 -0
  124. package/src/widget/box/index.less +3 -0
  125. package/src/widget/box/index.ts +2 -0
  126. package/src/widget/box/view.tsx +112 -0
  127. package/src/widget/comm.ts +152 -0
  128. package/src/widget/index.less +7 -0
  129. package/src/widget/index.ts +9 -0
  130. package/src/widget/instance-progress/contribution.ts +20 -0
  131. package/src/widget/instance-progress/index.ts +2 -0
  132. package/src/widget/instance-progress/view.tsx +155 -0
  133. package/src/widget/libro-widgets.ts +223 -0
  134. package/src/widget/module.ts +73 -0
  135. package/src/widget/progress/contribution.ts +24 -0
  136. package/src/widget/progress/index.ts +2 -0
  137. package/src/widget/progress/progressBar.tsx +29 -0
  138. package/src/widget/progress/view.tsx +70 -0
  139. package/src/widget/protocol.ts +255 -0
  140. package/src/widget/utils.ts +67 -0
  141. package/src/widget/version.ts +2 -0
  142. package/src/widget/widget-manager.ts +45 -0
  143. package/src/widget/widget-render.tsx +52 -0
  144. package/src/widget/widget-rendermime-contribution.ts +36 -0
  145. package/src/widget/widget-view-contribution.ts +14 -0
  146. 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,7 @@
1
+ .libro-widget-render {
2
+ margin: 10px 24px;
3
+ }
4
+
5
+ .libro-widget-render:empty {
6
+ margin: unset;
7
+ }
@@ -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,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
+ }