6
6
*/
7
7
8
8
import {
9
+ EuiButton ,
9
10
EuiCallOut ,
10
11
euiCanAnimate ,
11
12
EuiFlexGroup ,
12
13
EuiFlexItem ,
13
14
EuiHorizontalRule ,
15
+ EuiIcon ,
14
16
EuiPanel ,
15
17
euiScrollBarStyles ,
16
18
EuiSpacer ,
19
+ EuiText ,
17
20
useEuiTheme ,
18
21
UseEuiTheme ,
19
22
} from '@elastic/eui' ;
@@ -46,8 +49,8 @@ import { SimulatedFunctionCallingCallout } from './simulated_function_calling_ca
46
49
import { WelcomeMessage } from './welcome_message' ;
47
50
import { useLicense } from '../hooks/use_license' ;
48
51
import { PromptEditor } from '../prompt_editor/prompt_editor' ;
49
- import { deserializeMessage } from '../utils/deserialize_message' ;
50
52
import { useKibana } from '../hooks/use_kibana' ;
53
+ import { deserializeMessage } from '../utils/deserialize_message' ;
51
54
52
55
const fullHeightClassName = css `
53
56
height : 100% ;
@@ -118,16 +121,18 @@ export function ChatBody({
118
121
onConversationUpdate,
119
122
onToggleFlyoutPositionMode,
120
123
navigateToConversation,
124
+ onConversationDuplicate,
121
125
} : {
122
126
connectors : ReturnType < typeof useGenAIConnectors > ;
123
- currentUser ?: Pick < AuthenticatedUser , 'full_name' | 'username' > ;
127
+ currentUser ?: Pick < AuthenticatedUser , 'full_name' | 'username' | 'profile_uid' > ;
124
128
flyoutPositionMode ?: FlyoutPositionMode ;
125
129
initialTitle ?: string ;
126
130
initialMessages ?: Message [ ] ;
127
131
initialConversationId ?: string ;
128
132
knowledgeBase : UseKnowledgeBaseResult ;
129
133
showLinkToConversationsApp : boolean ;
130
134
onConversationUpdate : ( conversation : { conversation : Conversation [ 'conversation' ] } ) => void ;
135
+ onConversationDuplicate : ( conversation : Conversation ) => void ;
131
136
onToggleFlyoutPositionMode ?: ( flyoutPositionMode : FlyoutPositionMode ) => void ;
132
137
navigateToConversation ?: ( conversationId ?: string ) => void ;
133
138
} ) {
@@ -148,13 +153,26 @@ export function ChatBody({
148
153
false
149
154
) ;
150
155
151
- const { conversation, messages, next, state, stop, saveTitle } = useConversation ( {
156
+ const {
157
+ conversation,
158
+ conversationId,
159
+ messages,
160
+ next,
161
+ state,
162
+ stop,
163
+ saveTitle,
164
+ duplicateConversation,
165
+ isConversationOwnedByCurrentUser,
166
+ user : conversationUser ,
167
+ } = useConversation ( {
168
+ currentUser,
152
169
initialConversationId,
153
170
initialMessages,
154
171
initialTitle,
155
172
chatService,
156
173
connectorId : connectors . selectedConnector ,
157
174
onConversationUpdate,
175
+ onConversationDuplicate,
158
176
} ) ;
159
177
160
178
const timelineContainerRef = useRef < HTMLDivElement | null > ( null ) ;
@@ -391,28 +409,65 @@ export function ChatBody({
391
409
}
392
410
/>
393
411
) : (
394
- < ChatTimeline
395
- messages = { messages }
396
- knowledgeBase = { knowledgeBase }
397
- chatService = { chatService }
398
- currentUser = { currentUser }
399
- chatState = { state }
400
- hasConnector = { ! ! connectors . connectors ?. length }
401
- onEdit = { ( editedMessage , newMessage ) => {
402
- setStickToBottom ( true ) ;
403
- const indexOf = messages . indexOf ( editedMessage ) ;
404
- next ( messages . slice ( 0 , indexOf ) . concat ( newMessage ) ) ;
405
- } }
406
- onFeedback = { handleFeedback }
407
- onRegenerate = { ( message ) => {
408
- next ( reverseToLastUserMessage ( messages , message ) ) ;
409
- } }
410
- onSendTelemetry = { ( eventWithPayload ) =>
411
- chatService . sendAnalyticsEvent ( eventWithPayload )
412
- }
413
- onStopGenerating = { stop }
414
- onActionClick = { handleActionClick }
415
- />
412
+ < >
413
+ < ChatTimeline
414
+ conversationId = { conversationId }
415
+ messages = { messages }
416
+ knowledgeBase = { knowledgeBase }
417
+ chatService = { chatService }
418
+ currentUser = { conversationUser }
419
+ isConversationOwnedByCurrentUser = { isConversationOwnedByCurrentUser }
420
+ chatState = { state }
421
+ hasConnector = { ! ! connectors . connectors ?. length }
422
+ onEdit = { ( editedMessage , newMessage ) => {
423
+ setStickToBottom ( true ) ;
424
+ const indexOf = messages . indexOf ( editedMessage ) ;
425
+ next ( messages . slice ( 0 , indexOf ) . concat ( newMessage ) ) ;
426
+ } }
427
+ onFeedback = { handleFeedback }
428
+ onRegenerate = { ( message ) => {
429
+ next ( reverseToLastUserMessage ( messages , message ) ) ;
430
+ } }
431
+ onSendTelemetry = { ( eventWithPayload ) =>
432
+ chatService . sendAnalyticsEvent ( eventWithPayload )
433
+ }
434
+ onStopGenerating = { stop }
435
+ onActionClick = { handleActionClick }
436
+ />
437
+ { conversationId && ! isConversationOwnedByCurrentUser ? (
438
+ < >
439
+ < EuiPanel paddingSize = "m" hasShadow = { false } color = "subdued" >
440
+ < EuiFlexGroup >
441
+ < EuiFlexItem grow = { false } >
442
+ < EuiIcon size = "l" type = "users" />
443
+ </ EuiFlexItem >
444
+ < EuiFlexItem grow >
445
+ < EuiText size = "xs" >
446
+ < h3 >
447
+ { i18n . translate ( 'xpack.aiAssistant.sharedBanner.title' , {
448
+ defaultMessage : 'This conversation is shared with your team.' ,
449
+ } ) }
450
+ </ h3 >
451
+ < p >
452
+ { i18n . translate ( 'xpack.aiAssistant.sharedBanner.description' , {
453
+ defaultMessage : `You can’t edit or continue this conversation, but you can duplicate
454
+ it into a new private conversation. The original conversation will
455
+ remain unchanged.` ,
456
+ } ) }
457
+ </ p >
458
+ < EuiButton onClick = { duplicateConversation } iconType = "copy" size = "s" >
459
+ { i18n . translate ( 'xpack.aiAssistant.duplicateButton' , {
460
+ defaultMessage : 'Duplicate' ,
461
+ } ) }
462
+ </ EuiButton >
463
+ </ EuiText >
464
+ </ EuiFlexItem >
465
+ </ EuiFlexGroup >
466
+ </ EuiPanel >
467
+ < EuiSpacer size = "m" />
468
+ </ >
469
+ ) : null }
470
+ </ >
416
471
) }
417
472
</ EuiPanel >
418
473
</ div >
@@ -438,7 +493,11 @@ export function ChatBody({
438
493
className = { promptEditorContainerClassName }
439
494
>
440
495
< PromptEditor
441
- disabled = { ! connectors . selectedConnector || ! hasCorrectLicense }
496
+ disabled = {
497
+ ! connectors . selectedConnector ||
498
+ ! hasCorrectLicense ||
499
+ ( ! ! conversationId && ! isConversationOwnedByCurrentUser )
500
+ }
442
501
hidden = { connectors . loading || connectors . connectors ?. length === 0 }
443
502
loading = { isLoading }
444
503
onChangeHeight = { handleChangeHeight }
@@ -515,23 +574,21 @@ export function ChatBody({
515
574
< EuiFlexItem grow = { false } className = { headerContainerClassName } >
516
575
< ChatHeader
517
576
connectors = { connectors }
518
- conversationId = {
519
- conversation . value ?. conversation && 'id' in conversation . value . conversation
520
- ? conversation . value . conversation . id
521
- : undefined
522
- }
577
+ conversationId = { conversationId }
523
578
flyoutPositionMode = { flyoutPositionMode }
524
579
licenseInvalid = { ! hasCorrectLicense && ! initialConversationId }
525
580
loading = { isLoading }
526
581
title = { title }
527
582
onCopyConversation = { handleCopyConversation }
583
+ onDuplicateConversation = { duplicateConversation }
528
584
onSaveTitle = { ( newTitle ) => {
529
585
saveTitle ( newTitle ) ;
530
586
} }
531
587
onToggleFlyoutPositionMode = { onToggleFlyoutPositionMode }
532
588
navigateToConversation = {
533
589
initialMessages ?. length && ! initialConversationId ? undefined : navigateToConversation
534
590
}
591
+ isConversationOwnedByCurrentUser = { isConversationOwnedByCurrentUser }
535
592
/>
536
593
</ EuiFlexItem >
537
594
< EuiFlexItem grow = { false } >
0 commit comments