3
3
* Distributed under the terms of the Modified BSD License.
4
4
*/
5
5
6
- import { IDocumentManager } from '@jupyterlab/docmanager' ;
7
6
import {
8
7
Autocomplete ,
9
8
AutocompleteInputChangeReason ,
@@ -17,16 +16,20 @@ import clsx from 'clsx';
17
16
import React , { useEffect , useRef , useState } from 'react' ;
18
17
19
18
import { AttachmentPreviewList } from './attachments' ;
20
- import { AttachButton , CancelButton , SendButton } from './input' ;
19
+ import {
20
+ IInputToolbarRegistry ,
21
+ InputToolbarRegistry ,
22
+ useChatCommands
23
+ } from './input' ;
21
24
import { IInputModel , InputModel } from '../input-model' ;
22
- import { IAttachment , Selection } from '../types' ;
23
- import { useChatCommands } from './input/use-chat-commands' ;
25
+ import { IAttachment } from '../types' ;
24
26
import { IChatCommandRegistry } from '../chat-commands' ;
25
27
26
28
const INPUT_BOX_CLASS = 'jp-chat-input-container' ;
29
+ const INPUT_TOOLBAR_CLASS = 'jp-chat-input-toolbar' ;
27
30
28
31
export function ChatInput ( props : ChatInput . IProps ) : JSX . Element {
29
- const { documentManager , model } = props ;
32
+ const { model , toolbarRegistry } = props ;
30
33
const [ input , setInput ] = useState < string > ( model . value ) ;
31
34
const inputRef = useRef < HTMLInputElement > ( ) ;
32
35
@@ -38,14 +41,16 @@ export function ChatInput(props: ChatInput.IProps): JSX.Element {
38
41
const [ attachments , setAttachments ] = useState < IAttachment [ ] > (
39
42
model . attachments
40
43
) ;
44
+ const [ toolbarElements , setToolbarElements ] = useState <
45
+ InputToolbarRegistry . IToolbarItem [ ]
46
+ > ( [ ] ) ;
41
47
42
- // Display the include selection menu if it is not explicitly hidden, and if at least
43
- // one of the tool to check for text or cell selection is enabled.
44
- let hideIncludeSelection = props . hideIncludeSelection ?? false ;
45
- if ( model . activeCellManager === null && model . selectionWatcher === null ) {
46
- hideIncludeSelection = true ;
47
- }
48
-
48
+ /**
49
+ * Handle the changes on the model that affect the input.
50
+ * - focus requested
51
+ * - config changed
52
+ * - attachments changed
53
+ */
49
54
useEffect ( ( ) => {
50
55
const inputChanged = ( _ : IInputModel , value : string ) => {
51
56
setInput ( value ) ;
@@ -76,6 +81,22 @@ export function ChatInput(props: ChatInput.IProps): JSX.Element {
76
81
} ;
77
82
} , [ model ] ) ;
78
83
84
+ /**
85
+ * Handle the changes in the toolbar items.
86
+ */
87
+ useEffect ( ( ) => {
88
+ const updateToolbar = ( ) => {
89
+ setToolbarElements ( toolbarRegistry . getItems ( ) ) ;
90
+ } ;
91
+
92
+ toolbarRegistry . itemsChanged . connect ( updateToolbar ) ;
93
+ updateToolbar ( ) ;
94
+
95
+ return ( ) => {
96
+ toolbarRegistry . itemsChanged . disconnect ( updateToolbar ) ;
97
+ } ;
98
+ } , [ toolbarRegistry ] ) ;
99
+
79
100
const inputExists = ! ! input . trim ( ) ;
80
101
81
102
/**
@@ -136,38 +157,12 @@ export function ChatInput(props: ChatInput.IProps): JSX.Element {
136
157
( sendWithShiftEnter && event . shiftKey ) ||
137
158
( ! sendWithShiftEnter && ! event . shiftKey )
138
159
) {
139
- onSend ( ) ;
160
+ model . send ( input ) ;
140
161
event . stopPropagation ( ) ;
141
162
event . preventDefault ( ) ;
142
163
}
143
164
}
144
165
145
- /**
146
- * Triggered when sending the message.
147
- *
148
- * Add code block if cell or text is selected.
149
- */
150
- function onSend ( selection ?: Selection ) {
151
- let content = input ;
152
- if ( selection ) {
153
- content += `
154
-
155
- \`\`\`
156
- ${ selection . source }
157
- \`\`\`
158
- ` ;
159
- }
160
- props . onSend ( content ) ;
161
- model . value = '' ;
162
- }
163
-
164
- /**
165
- * Triggered when cancelling edition.
166
- */
167
- function onCancel ( ) {
168
- props . onCancel ?.( ) ;
169
- }
170
-
171
166
// Set the helper text based on whether Shift+Enter is used for sending.
172
167
const helperText = sendWithShiftEnter ? (
173
168
< span >
@@ -221,22 +216,10 @@ ${selection.source}
221
216
InputProps = { {
222
217
...params . InputProps ,
223
218
endAdornment : (
224
- < InputAdornment position = "end" >
225
- { documentManager && model . addAttachment && (
226
- < AttachButton
227
- documentManager = { documentManager }
228
- onAttach = { model . addAttachment }
229
- />
230
- ) }
231
- { props . onCancel && < CancelButton onCancel = { onCancel } /> }
232
- < SendButton
233
- model = { model }
234
- sendWithShiftEnter = { sendWithShiftEnter }
235
- inputExists = { inputExists || attachments . length > 0 }
236
- onSend = { onSend }
237
- hideIncludeSelection = { hideIncludeSelection }
238
- hasButtonOnLeft = { ! ! props . onCancel }
239
- />
219
+ < InputAdornment position = "end" className = { INPUT_TOOLBAR_CLASS } >
220
+ { toolbarElements . map ( item => (
221
+ < item . element model = { model } />
222
+ ) ) }
240
223
</ InputAdornment >
241
224
)
242
225
} }
@@ -277,25 +260,17 @@ export namespace ChatInput {
277
260
*/
278
261
model : IInputModel ;
279
262
/**
280
- * The function to be called to send the message .
263
+ * The toolbar registry .
281
264
*/
282
- onSend : ( input : string ) => unknown ;
265
+ toolbarRegistry : IInputToolbarRegistry ;
283
266
/**
284
267
* The function to be called to cancel editing.
285
268
*/
286
269
onCancel ?: ( ) => unknown ;
287
- /**
288
- * Whether to allow or not including selection.
289
- */
290
- hideIncludeSelection ?: boolean ;
291
270
/**
292
271
* Custom mui/material styles.
293
272
*/
294
273
sx ?: SxProps < Theme > ;
295
- /**
296
- * The document manager.
297
- */
298
- documentManager ?: IDocumentManager ;
299
274
/**
300
275
* Chat command registry.
301
276
*/
0 commit comments