11import { InfoBanner } from '../InfoBanner'
22import { KeyboardShortcutsFooter } from '../KeyboardShortcutsFooter'
3- import { SearchOrAskAiErrorCallout } from '../SearchOrAskAiErrorCallout '
3+ import { LegalDisclaimer } from '../LegalDisclaimer '
44import AiIcon from '../ai-icon.svg'
55import { useModalActions } from '../modal.store'
66import { AskAiSuggestions } from './AskAiSuggestions'
77import { ChatInput } from './ChatInput'
88import { ChatMessageList } from './ChatMessageList'
99import {
10- ChatMessage ,
1110 useChatActions ,
1211 useChatMessages ,
1312 useChatScrollPosition ,
13+ useIsChatEmpty ,
1414 useIsStreaming ,
1515} from './chat.store'
1616import { useIsAskAiCooldownActive } from './useAskAiCooldown'
@@ -22,7 +22,6 @@ import {
2222 EuiHorizontalRule ,
2323 EuiIcon ,
2424 EuiSpacer ,
25- EuiText ,
2625 EuiToolTip ,
2726 useEuiFontSize ,
2827 useEuiOverflowScroll ,
@@ -32,7 +31,8 @@ import { css } from '@emotion/react'
3231import { RefObject , useCallback , useEffect , useRef , useState } from 'react'
3332
3433export const Chat = ( ) => {
35- const messages = useChatMessages ( )
34+ const { euiTheme } = useEuiTheme ( )
35+ const isEmpty = useIsChatEmpty ( )
3636 const inputRef = useRef < HTMLTextAreaElement > ( null )
3737 const scrollRef = useRef < HTMLDivElement > ( null )
3838
@@ -44,15 +44,11 @@ export const Chat = () => {
4444 inputRef . current ?. focus ( )
4545 } , [ ] )
4646
47- const {
48- inputValue,
49- setInputValue,
50- handleSubmit,
51- handleAbort,
52- handleAbortReady,
53- isStreaming,
54- isCooldownActive,
55- } = useChatSubmit ( scrollRef )
47+ const [ scrollAreaProps , setScrollAreaProps ] = useState < {
48+ onAbortReady : ( abort : ( ) => void ) => void
49+ } > ( {
50+ onAbortReady : ( ) => { } ,
51+ } )
5652
5753 return (
5854 < EuiFlexGroup
@@ -62,26 +58,51 @@ export const Chat = () => {
6258 >
6359 < ChatHeader />
6460
65- < ChatScrollArea
66- scrollRef = { scrollRef }
67- onScroll = { handleScroll }
68- messages = { messages }
69- isCooldownActive = { isCooldownActive }
70- onAbortReady = { handleAbortReady }
71- />
61+ { isEmpty ? (
62+ < EuiFlexItem grow = { true } css = { emptyStateContainerStyles } >
63+ < EuiEmptyPrompt
64+ icon = { < EuiIcon type = { AiIcon } size = "xxl" /> }
65+ title = { < h2 > Hi! I'm the Elastic Docs AI Assistant</ h2 > }
66+ body = {
67+ < p >
68+ I'm here to help you find answers about Elastic,
69+ powered entirely by our technical documentation.
70+ How can I help?
71+ </ p >
72+ }
73+ />
74+ </ EuiFlexItem >
75+ ) : (
76+ < ChatScrollArea
77+ scrollRef = { scrollRef }
78+ onScroll = { handleScroll }
79+ onAbortReady = { scrollAreaProps . onAbortReady }
80+ />
81+ ) }
7282
7383 < ChatInputArea
7484 inputRef = { inputRef }
75- value = { inputValue }
76- onChange = { setInputValue }
77- onSubmit = { handleSubmit }
78- onAbort = { handleAbort }
79- disabled = { isCooldownActive }
80- isStreaming = { isStreaming }
85+ scrollRef = { scrollRef }
8186 onMetaSemicolon = { handleMetaSemicolon }
87+ onStateChange = { setScrollAreaProps }
8288 />
8389
90+ { isEmpty && (
91+ < >
92+ < AskAiSuggestions />
93+ < EuiSpacer size = "m" />
94+ < div
95+ css = { css `
96+ padding-inline : ${ euiTheme . size . base } ;
97+ ` }
98+ >
99+ < LegalDisclaimer />
100+ </ div >
101+ </ >
102+ ) }
103+
84104 < InfoBanner />
105+
85106 < KeyboardShortcutsFooter shortcuts = { KEYBOARD_SHORTCUTS } />
86107 </ EuiFlexGroup >
87108 )
@@ -154,18 +175,15 @@ const ChatHeader = () => {
154175interface ChatScrollAreaProps {
155176 scrollRef : RefObject < HTMLDivElement >
156177 onScroll : ( ) => void
157- messages : ChatMessage [ ]
158- isCooldownActive : boolean
159178 onAbortReady : ( abort : ( ) => void ) => void
160179}
161180
162181const ChatScrollArea = ( {
163182 scrollRef,
164183 onScroll,
165- messages,
166- isCooldownActive,
167184 onAbortReady,
168185} : ChatScrollAreaProps ) => {
186+ const messages = useChatMessages ( )
169187 const { euiTheme } = useEuiTheme ( )
170188
171189 const scrollableStyles = css `
@@ -179,69 +197,48 @@ const ChatScrollArea = ({
179197 return (
180198 < EuiFlexItem grow = { true } css = { scrollContainerStyles } >
181199 < div ref = { scrollRef } css = { scrollableStyles } onScroll = { onScroll } >
182- { messages . length === 0 ? (
183- < ChatEmptyState disabled = { isCooldownActive } />
184- ) : (
185- < div css = { messagesStyles } >
186- < ChatMessageList
187- messages = { messages }
188- onAbortReady = { onAbortReady }
189- />
190- </ div >
191- ) }
200+ < div css = { messagesStyles } >
201+ < ChatMessageList
202+ messages = { messages }
203+ onAbortReady = { onAbortReady }
204+ />
205+ </ div >
192206 </ div >
193207 </ EuiFlexItem >
194208 )
195209}
196210
197- const ChatEmptyState = ( { disabled } : { disabled : boolean } ) => (
198- < >
199- < EuiEmptyPrompt
200- icon = { < EuiIcon type = { AiIcon } size = "xxl" /> }
201- title = { < h2 > Hi! I'm the Elastic Docs AI Assistant</ h2 > }
202- body = {
203- < p >
204- I'm here to help you find answers about Elastic, powered
205- entirely by our technical documentation. How can I help?
206- </ p >
207- }
208- />
209- < EuiSpacer size = "s" />
210- < div >
211- < EuiText size = "xs" color = "subdued" >
212- Example questions
213- </ EuiText >
214- < EuiSpacer size = "s" />
215- < AskAiSuggestions disabled = { disabled } />
216- </ div >
217- < div css = { messagesStyles } >
218- < SearchOrAskAiErrorCallout error = { null } domain = "askAi" />
219- </ div >
220- </ >
221- )
222-
223211interface ChatInputAreaProps {
224212 inputRef : RefObject < HTMLTextAreaElement >
225- value : string
226- onChange : ( value : string ) => void
227- onSubmit : ( question : string ) => void
228- onAbort : ( ) => void
229- disabled : boolean
230- isStreaming : boolean
213+ scrollRef : RefObject < HTMLDivElement >
231214 onMetaSemicolon ?: ( ) => void
215+ onStateChange ?: ( state : {
216+ onAbortReady : ( abort : ( ) => void ) => void
217+ } ) => void
232218}
233219
234220const ChatInputArea = ( {
235221 inputRef,
236- value,
237- onChange,
238- onSubmit,
239- onAbort,
240- disabled,
241- isStreaming,
222+ scrollRef,
242223 onMetaSemicolon,
224+ onStateChange,
243225} : ChatInputAreaProps ) => {
244226 const { euiTheme } = useEuiTheme ( )
227+ const {
228+ inputValue,
229+ setInputValue,
230+ handleSubmit,
231+ handleAbort,
232+ handleAbortReady,
233+ isStreaming,
234+ isCooldownActive,
235+ } = useChatSubmit ( scrollRef )
236+
237+ useEffect ( ( ) => {
238+ onStateChange ?.( {
239+ onAbortReady : handleAbortReady ,
240+ } )
241+ } , [ handleAbortReady , onStateChange ] )
245242
246243 return (
247244 < EuiFlexItem grow = { false } >
@@ -253,11 +250,11 @@ const ChatInputArea = ({
253250 ` }
254251 >
255252 < ChatInput
256- value = { value }
257- onChange = { onChange }
258- onSubmit = { onSubmit }
259- onAbort = { onAbort }
260- disabled = { disabled }
253+ value = { inputValue }
254+ onChange = { setInputValue }
255+ onSubmit = { handleSubmit }
256+ onAbort = { handleAbort }
257+ disabled = { isCooldownActive }
261258 inputRef = { inputRef }
262259 isStreaming = { isStreaming }
263260 onMetaSemicolon = { onMetaSemicolon }
@@ -394,6 +391,12 @@ const scrollContainerStyles = css`
394391 overflow : hidden;
395392`
396393
394+ const emptyStateContainerStyles = css `
395+ display : flex;
396+ align-items : center;
397+ justify-content : center;
398+ `
399+
397400const messagesStyles = css `
398401 max-width : 800px ;
399402 margin : 0 auto;
0 commit comments