8
8
* Originally adapted from jupyterlab-chat's ChatPanel
9
9
*/
10
10
11
- import {
12
- chatIcon ,
13
- ChatWidget ,
14
- IAttachmentOpenerRegistry ,
15
- IChatCommandRegistry ,
16
- IChatModel ,
17
- IInputToolbarRegistry ,
18
- IMessageFooterRegistry ,
19
- readIcon
20
- } from '../index' ;
21
11
import { InputDialog , IThemeManager } from '@jupyterlab/apputils' ;
22
12
import { IRenderMimeRegistry } from '@jupyterlab/rendermime' ;
23
13
import {
@@ -36,6 +26,20 @@ import { ISignal, Signal } from '@lumino/signaling';
36
26
import { AccordionPanel , Panel } from '@lumino/widgets' ;
37
27
import React , { useState } from 'react' ;
38
28
29
+ import { ChatWidget } from './chat-widget' ;
30
+ import {
31
+ Chat ,
32
+ IInputToolbarRegistry ,
33
+ IInputToolbarRegistryFactory
34
+ } from '../components' ;
35
+ import { chatIcon , readIcon } from '../icons' ;
36
+ import { IChatModel } from '../model' ;
37
+ import {
38
+ IAttachmentOpenerRegistry ,
39
+ IChatCommandRegistry ,
40
+ IMessageFooterRegistry
41
+ } from '../registers' ;
42
+
39
43
const SIDEPANEL_CLASS = 'jp-chat-sidepanel' ;
40
44
const ADD_BUTTON_CLASS = 'jp-chat-add' ;
41
45
const OPEN_SELECT_CLASS = 'jp-chat-open' ;
@@ -62,17 +66,18 @@ export class MultiChatPanel extends SidePanel {
62
66
this . _messageFooterRegistry = options . messageFooterRegistry ;
63
67
this . _welcomeMessage = options . welcomeMessage ;
64
68
65
- // Use the passed callback functions
66
69
this . _getChatNames = options . getChatNames ;
67
- this . _openChat = options . openChat ;
70
+ this . _createModel = options . createModel ;
68
71
this . _openInMain = options . openInMain ;
69
- this . _createChat = options . createChat ;
70
72
this . _renameChat = options . renameChat ;
71
73
72
- if ( this . _createChat ) {
74
+ if ( this . _createModel ) {
73
75
// Add chat button calls the createChat callback
74
76
const addChat = new ToolbarButton ( {
75
- onClick : ( ) => this . _createChat ?.( ) ,
77
+ onClick : async ( ) => {
78
+ const addChatArgs = await this . _createModel ! ( ) ;
79
+ this . addChat ( addChatArgs ) ;
80
+ } ,
76
81
icon : addIcon ,
77
82
label : 'Chat' ,
78
83
tooltip : 'Add a new chat'
@@ -81,7 +86,7 @@ export class MultiChatPanel extends SidePanel {
81
86
this . toolbar . addItem ( 'createChat' , addChat ) ;
82
87
}
83
88
84
- if ( this . _getChatNames && this . _openChat ) {
89
+ if ( this . _getChatNames && this . _createModel ) {
85
90
// Chat select dropdown
86
91
this . _openChatWidget = ReactWidget . create (
87
92
< ChatSelect
@@ -106,14 +111,30 @@ export class MultiChatPanel extends SidePanel {
106
111
return this . widgets as ChatSection [ ] ;
107
112
}
108
113
114
+ /**
115
+ * A signal emitting when a section is added to the panel.
116
+ */
117
+ get sectionAdded ( ) : ISignal < MultiChatPanel , ChatSection > {
118
+ return this . _sectionAdded ;
119
+ }
120
+
109
121
/**
110
122
* Add a new widget to the chat panel.
111
123
*
112
124
* @param model - the model of the chat widget
113
125
* @param displayName - the name of the chat.
114
126
*/
115
127
116
- addChat ( model : IChatModel , displayName ?: string ) : ChatWidget {
128
+ addChat ( args : MultiChatPanel . IAddChatArgs ) : ChatWidget | undefined {
129
+ const { model, displayName } = args ;
130
+ if ( ! model ) {
131
+ return ;
132
+ }
133
+
134
+ if ( this . openIfExists ( model . name ) ) {
135
+ return ;
136
+ }
137
+
117
138
const content = this . content as AccordionPanel ;
118
139
for ( let i = 0 ; i < this . widgets . length ; i ++ ) {
119
140
content . collapse ( i ) ;
@@ -147,15 +168,16 @@ export class MultiChatPanel extends SidePanel {
147
168
this . addWidget ( section ) ;
148
169
content . expand ( this . widgets . length - 1 ) ;
149
170
171
+ this . _sectionAdded . emit ( section ) ;
150
172
return widget ;
151
173
}
152
174
153
175
/**
154
176
* Invoke the update of the list of available chats.
155
177
*/
156
- updateChatList = ( ) => {
178
+ updateChatList ( ) {
157
179
this . _updateChatListDebouncer . invoke ( ) ;
158
- } ;
180
+ }
159
181
160
182
/**
161
183
* Update the list of available chats.
@@ -172,7 +194,7 @@ export class MultiChatPanel extends SidePanel {
172
194
/**
173
195
* Open a chat if it exists in the side panel.
174
196
*
175
- * @param path - the path of the chat.
197
+ * @param name - the name of the chat.
176
198
* @returns a boolean, whether the chat existed in the side panel or not.
177
199
*/
178
200
openIfExists ( name : string ) : boolean {
@@ -196,7 +218,7 @@ export class MultiChatPanel extends SidePanel {
196
218
* @param name - the chat name.
197
219
*/
198
220
private _getChatIndex ( name : string ) {
199
- return this . widgets . findIndex ( w => ( w as ChatSection ) . model ?. name === name ) ;
221
+ return this . sections . findIndex ( section => section . model ?. name === name ) ;
200
222
}
201
223
202
224
/**
@@ -211,12 +233,17 @@ export class MultiChatPanel extends SidePanel {
211
233
/**
212
234
* Handle `change` events for the HTMLSelect component.
213
235
*/
214
- private _chatSelected ( event : React . ChangeEvent < HTMLSelectElement > ) : void {
236
+ private async _chatSelected (
237
+ event : React . ChangeEvent < HTMLSelectElement >
238
+ ) : Promise < void > {
215
239
const selection = event . target . value ;
216
240
if ( selection === '-' ) {
217
241
return ;
218
242
}
219
- this . _openChat ?.( selection ) ;
243
+ if ( this . _createModel ) {
244
+ const addChatArgs = await this . _createModel ( selection ) ;
245
+ this . addChat ( addChatArgs ) ;
246
+ }
220
247
event . target . selectedIndex = 0 ;
221
248
}
222
249
@@ -238,19 +265,20 @@ export class MultiChatPanel extends SidePanel {
238
265
private _chatNamesChanged = new Signal < this, { [ name : string ] : string } > (
239
266
this
240
267
) ;
241
-
268
+ private _sectionAdded = new Signal < MultiChatPanel , ChatSection > ( this ) ;
242
269
private _rmRegistry : IRenderMimeRegistry ;
243
- private _themeManager : IThemeManager | null ;
270
+ private _themeManager ? : IThemeManager | null ;
244
271
private _chatCommandRegistry ?: IChatCommandRegistry ;
245
272
private _attachmentOpenerRegistry ?: IAttachmentOpenerRegistry ;
246
- private _inputToolbarFactory ?: MultiChatPanel . IInputToolbarRegistryFactory ;
273
+ private _inputToolbarFactory ?: IInputToolbarRegistryFactory ;
247
274
private _messageFooterRegistry ?: IMessageFooterRegistry ;
248
275
private _welcomeMessage ?: string ;
249
276
private _updateChatListDebouncer : Debouncer ;
250
277
278
+ private _createModel ?: (
279
+ name ?: string
280
+ ) => Promise < MultiChatPanel . IAddChatArgs > ;
251
281
private _getChatNames ?: ( ) => Promise < { [ name : string ] : string } > ;
252
- private _createChat ?: ( ) => void ;
253
- private _openChat ?: ( name : string ) => void ;
254
282
private _openInMain ?: ( name : string ) => void ;
255
283
private _renameChat ?: ( oldName : string , newName : string ) => Promise < boolean > ;
256
284
@@ -264,25 +292,54 @@ export namespace MultiChatPanel {
264
292
/**
265
293
* Options of the constructor of the chat panel.
266
294
*/
267
- export interface IOptions extends SidePanel . IOptions {
268
- rmRegistry : IRenderMimeRegistry ;
269
- themeManager : IThemeManager | null ;
270
-
295
+ export interface IOptions
296
+ extends SidePanel . IOptions ,
297
+ Omit < Chat . IOptions , 'model' | 'inputToolbarRegistry' > {
298
+ /**
299
+ * The input toolbar factory;
300
+ */
301
+ inputToolbarFactory ?: IInputToolbarRegistryFactory ;
302
+ /**
303
+ * An optional callback to create a chat model.
304
+ *
305
+ * @param name - the name of the chat, optional.
306
+ * @return an object that can be passed to add a chat section.
307
+ */
308
+ createModel ?: ( name ?: string ) => Promise < IAddChatArgs > ;
309
+ /**
310
+ * An optional callback to get the list of existing chats.
311
+ *
312
+ * @returns an object with display name as key and the "full" name as value.
313
+ */
271
314
getChatNames ?: ( ) => Promise < { [ name : string ] : string } > ;
272
- openChat ?: ( name : string ) => void ;
273
- createChat ?: ( ) => void ;
315
+ /**
316
+ * An optional callback to open the chat in the main area.
317
+ *
318
+ * @param name - the name of the chat to move.
319
+ */
274
320
openInMain ?: ( name : string ) => void ;
321
+ /**
322
+ * An optional callback to rename a chat.
323
+ *
324
+ * @param oldName - the old name of the chat.
325
+ * @param newName - the new name of the chat.
326
+ * @returns - a boolean, whether the chat has been renamed or not.
327
+ */
275
328
renameChat ?: ( oldName : string , newName : string ) => Promise < boolean > ;
276
-
277
- chatCommandRegistry ?: IChatCommandRegistry ;
278
- attachmentOpenerRegistry ?: IAttachmentOpenerRegistry ;
279
- inputToolbarFactory ?: IInputToolbarRegistryFactory ;
280
- messageFooterRegistry ?: IMessageFooterRegistry ;
281
- welcomeMessage ?: string ;
282
329
}
283
-
284
- export interface IInputToolbarRegistryFactory {
285
- create ( ) : IInputToolbarRegistry ;
330
+ /**
331
+ * The options for the add chat method.
332
+ */
333
+ export interface IAddChatArgs {
334
+ /**
335
+ * The model of the chat.
336
+ * No-op id undefined.
337
+ */
338
+ model ?: IChatModel ;
339
+ /**
340
+ * The display name of the chat in the section title.
341
+ */
342
+ displayName ?: string ;
286
343
}
287
344
}
288
345
@@ -295,7 +352,8 @@ export class ChatSection extends PanelWithToolbar {
295
352
*/
296
353
constructor ( options : ChatSection . IOptions ) {
297
354
super ( options ) ;
298
- this . addWidget ( options . widget ) ;
355
+ this . _chatWidget = options . widget ;
356
+ this . addWidget ( this . _chatWidget ) ;
299
357
this . addWidget ( this . _spinner ) ;
300
358
this . addClass ( SECTION_CLASS ) ;
301
359
this . toolbar . addClass ( TOOLBAR_CLASS ) ;
@@ -394,11 +452,18 @@ export class ChatSection extends PanelWithToolbar {
394
452
this . _updateTitle ( ) ;
395
453
}
396
454
455
+ /**
456
+ * The chat widget of the section.
457
+ */
458
+ get widget ( ) : ChatWidget {
459
+ return this . _chatWidget ;
460
+ }
461
+
397
462
/**
398
463
* The model of the widget.
399
464
*/
400
465
get model ( ) : IChatModel {
401
- return ( this . widgets [ 0 ] as ChatWidget ) . model ;
466
+ return this . _chatWidget . model ;
402
467
}
403
468
404
469
/**
@@ -433,6 +498,7 @@ export class ChatSection extends PanelWithToolbar {
433
498
this . _markAsRead . enabled = unread . length > 0 ;
434
499
} ;
435
500
501
+ private _chatWidget : ChatWidget ;
436
502
private _markAsRead : ToolbarButton ;
437
503
private _spinner = new Spinner ( ) ;
438
504
private _displayName : string ;
@@ -446,15 +512,39 @@ export namespace ChatSection {
446
512
* Options to build a chat section.
447
513
*/
448
514
export interface IOptions extends Panel . IOptions {
515
+ /**
516
+ * The widget to display in the section.
517
+ */
449
518
widget : ChatWidget ;
519
+ /**
520
+ * An optional callback to open the chat in the main area.
521
+ *
522
+ * @param name - the name of the chat to move.
523
+ */
450
524
openInMain ?: ( name : string ) => void ;
525
+ /**
526
+ * An optional callback to rename a chat.
527
+ *
528
+ * @param oldName - the old name of the chat.
529
+ * @param newName - the new name of the chat.
530
+ * @returns - a boolean, whether the chat has been renamed or not.
531
+ */
451
532
renameChat ?: ( oldName : string , newName : string ) => Promise < boolean > ;
533
+ /**
534
+ * The name to display in the section title.
535
+ */
452
536
displayName ?: string ;
453
537
}
454
538
}
455
539
456
540
type ChatSelectProps = {
541
+ /**
542
+ * A signal emitting when the list of chat changed.
543
+ */
457
544
chatNamesChanged : ISignal < MultiChatPanel , { [ name : string ] : string } > ;
545
+ /**
546
+ * The callback to call when the selection changed in the select.
547
+ */
458
548
handleChange : ( event : React . ChangeEvent < HTMLSelectElement > ) => void ;
459
549
} ;
460
550
0 commit comments