88 * Originally adapted from jupyterlab-chat's ChatPanel
99 */
1010
11- import {
12- chatIcon ,
13- ChatWidget ,
14- IAttachmentOpenerRegistry ,
15- IChatCommandRegistry ,
16- IChatModel ,
17- IInputToolbarRegistry ,
18- IMessageFooterRegistry ,
19- readIcon
20- } from '../index' ;
2111import { InputDialog , IThemeManager } from '@jupyterlab/apputils' ;
2212import { IRenderMimeRegistry } from '@jupyterlab/rendermime' ;
2313import {
@@ -36,6 +26,20 @@ import { ISignal, Signal } from '@lumino/signaling';
3626import { AccordionPanel , Panel } from '@lumino/widgets' ;
3727import React , { useState } from 'react' ;
3828
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+
3943const SIDEPANEL_CLASS = 'jp-chat-sidepanel' ;
4044const ADD_BUTTON_CLASS = 'jp-chat-add' ;
4145const OPEN_SELECT_CLASS = 'jp-chat-open' ;
@@ -62,17 +66,18 @@ export class MultiChatPanel extends SidePanel {
6266 this . _messageFooterRegistry = options . messageFooterRegistry ;
6367 this . _welcomeMessage = options . welcomeMessage ;
6468
65- // Use the passed callback functions
6669 this . _getChatNames = options . getChatNames ;
67- this . _openChat = options . openChat ;
70+ this . _createModel = options . createModel ;
6871 this . _openInMain = options . openInMain ;
69- this . _createChat = options . createChat ;
7072 this . _renameChat = options . renameChat ;
7173
72- if ( this . _createChat ) {
74+ if ( this . _createModel ) {
7375 // Add chat button calls the createChat callback
7476 const addChat = new ToolbarButton ( {
75- onClick : ( ) => this . _createChat ?.( ) ,
77+ onClick : async ( ) => {
78+ const addChatArgs = await this . _createModel ! ( ) ;
79+ this . addChat ( addChatArgs ) ;
80+ } ,
7681 icon : addIcon ,
7782 label : 'Chat' ,
7883 tooltip : 'Add a new chat'
@@ -81,7 +86,7 @@ export class MultiChatPanel extends SidePanel {
8186 this . toolbar . addItem ( 'createChat' , addChat ) ;
8287 }
8388
84- if ( this . _getChatNames && this . _openChat ) {
89+ if ( this . _getChatNames && this . _createModel ) {
8590 // Chat select dropdown
8691 this . _openChatWidget = ReactWidget . create (
8792 < ChatSelect
@@ -106,14 +111,30 @@ export class MultiChatPanel extends SidePanel {
106111 return this . widgets as ChatSection [ ] ;
107112 }
108113
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+
109121 /**
110122 * Add a new widget to the chat panel.
111123 *
112124 * @param model - the model of the chat widget
113125 * @param displayName - the name of the chat.
114126 */
115127
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+
117138 const content = this . content as AccordionPanel ;
118139 for ( let i = 0 ; i < this . widgets . length ; i ++ ) {
119140 content . collapse ( i ) ;
@@ -147,15 +168,16 @@ export class MultiChatPanel extends SidePanel {
147168 this . addWidget ( section ) ;
148169 content . expand ( this . widgets . length - 1 ) ;
149170
171+ this . _sectionAdded . emit ( section ) ;
150172 return widget ;
151173 }
152174
153175 /**
154176 * Invoke the update of the list of available chats.
155177 */
156- updateChatList = ( ) => {
178+ updateChatList ( ) {
157179 this . _updateChatListDebouncer . invoke ( ) ;
158- } ;
180+ }
159181
160182 /**
161183 * Update the list of available chats.
@@ -172,7 +194,7 @@ export class MultiChatPanel extends SidePanel {
172194 /**
173195 * Open a chat if it exists in the side panel.
174196 *
175- * @param path - the path of the chat.
197+ * @param name - the name of the chat.
176198 * @returns a boolean, whether the chat existed in the side panel or not.
177199 */
178200 openIfExists ( name : string ) : boolean {
@@ -196,7 +218,7 @@ export class MultiChatPanel extends SidePanel {
196218 * @param name - the chat name.
197219 */
198220 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 ) ;
200222 }
201223
202224 /**
@@ -211,12 +233,17 @@ export class MultiChatPanel extends SidePanel {
211233 /**
212234 * Handle `change` events for the HTMLSelect component.
213235 */
214- private _chatSelected ( event : React . ChangeEvent < HTMLSelectElement > ) : void {
236+ private async _chatSelected (
237+ event : React . ChangeEvent < HTMLSelectElement >
238+ ) : Promise < void > {
215239 const selection = event . target . value ;
216240 if ( selection === '-' ) {
217241 return ;
218242 }
219- this . _openChat ?.( selection ) ;
243+ if ( this . _createModel ) {
244+ const addChatArgs = await this . _createModel ( selection ) ;
245+ this . addChat ( addChatArgs ) ;
246+ }
220247 event . target . selectedIndex = 0 ;
221248 }
222249
@@ -238,19 +265,20 @@ export class MultiChatPanel extends SidePanel {
238265 private _chatNamesChanged = new Signal < this, { [ name : string ] : string } > (
239266 this
240267 ) ;
241-
268+ private _sectionAdded = new Signal < MultiChatPanel , ChatSection > ( this ) ;
242269 private _rmRegistry : IRenderMimeRegistry ;
243- private _themeManager : IThemeManager | null ;
270+ private _themeManager ? : IThemeManager | null ;
244271 private _chatCommandRegistry ?: IChatCommandRegistry ;
245272 private _attachmentOpenerRegistry ?: IAttachmentOpenerRegistry ;
246- private _inputToolbarFactory ?: MultiChatPanel . IInputToolbarRegistryFactory ;
273+ private _inputToolbarFactory ?: IInputToolbarRegistryFactory ;
247274 private _messageFooterRegistry ?: IMessageFooterRegistry ;
248275 private _welcomeMessage ?: string ;
249276 private _updateChatListDebouncer : Debouncer ;
250277
278+ private _createModel ?: (
279+ name ?: string
280+ ) => Promise < MultiChatPanel . IAddChatArgs > ;
251281 private _getChatNames ?: ( ) => Promise < { [ name : string ] : string } > ;
252- private _createChat ?: ( ) => void ;
253- private _openChat ?: ( name : string ) => void ;
254282 private _openInMain ?: ( name : string ) => void ;
255283 private _renameChat ?: ( oldName : string , newName : string ) => Promise < boolean > ;
256284
@@ -264,25 +292,54 @@ export namespace MultiChatPanel {
264292 /**
265293 * Options of the constructor of the chat panel.
266294 */
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+ */
271314 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+ */
274320 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+ */
275328 renameChat ?: ( oldName : string , newName : string ) => Promise < boolean > ;
276-
277- chatCommandRegistry ?: IChatCommandRegistry ;
278- attachmentOpenerRegistry ?: IAttachmentOpenerRegistry ;
279- inputToolbarFactory ?: IInputToolbarRegistryFactory ;
280- messageFooterRegistry ?: IMessageFooterRegistry ;
281- welcomeMessage ?: string ;
282329 }
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 ;
286343 }
287344}
288345
@@ -295,7 +352,8 @@ export class ChatSection extends PanelWithToolbar {
295352 */
296353 constructor ( options : ChatSection . IOptions ) {
297354 super ( options ) ;
298- this . addWidget ( options . widget ) ;
355+ this . _chatWidget = options . widget ;
356+ this . addWidget ( this . _chatWidget ) ;
299357 this . addWidget ( this . _spinner ) ;
300358 this . addClass ( SECTION_CLASS ) ;
301359 this . toolbar . addClass ( TOOLBAR_CLASS ) ;
@@ -394,11 +452,18 @@ export class ChatSection extends PanelWithToolbar {
394452 this . _updateTitle ( ) ;
395453 }
396454
455+ /**
456+ * The chat widget of the section.
457+ */
458+ get widget ( ) : ChatWidget {
459+ return this . _chatWidget ;
460+ }
461+
397462 /**
398463 * The model of the widget.
399464 */
400465 get model ( ) : IChatModel {
401- return ( this . widgets [ 0 ] as ChatWidget ) . model ;
466+ return this . _chatWidget . model ;
402467 }
403468
404469 /**
@@ -433,6 +498,7 @@ export class ChatSection extends PanelWithToolbar {
433498 this . _markAsRead . enabled = unread . length > 0 ;
434499 } ;
435500
501+ private _chatWidget : ChatWidget ;
436502 private _markAsRead : ToolbarButton ;
437503 private _spinner = new Spinner ( ) ;
438504 private _displayName : string ;
@@ -446,15 +512,39 @@ export namespace ChatSection {
446512 * Options to build a chat section.
447513 */
448514 export interface IOptions extends Panel . IOptions {
515+ /**
516+ * The widget to display in the section.
517+ */
449518 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+ */
450524 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+ */
451532 renameChat ?: ( oldName : string , newName : string ) => Promise < boolean > ;
533+ /**
534+ * The name to display in the section title.
535+ */
452536 displayName ?: string ;
453537 }
454538}
455539
456540type ChatSelectProps = {
541+ /**
542+ * A signal emitting when the list of chat changed.
543+ */
457544 chatNamesChanged : ISignal < MultiChatPanel , { [ name : string ] : string } > ;
545+ /**
546+ * The callback to call when the selection changed in the select.
547+ */
458548 handleChange : ( event : React . ChangeEvent < HTMLSelectElement > ) => void ;
459549} ;
460550
0 commit comments