diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx index 64375b0bd9a98..5b7e6b509e69f 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx @@ -28,7 +28,7 @@ import type { UseGenAIConnectorsResult } from '../../hooks/use_genai_connectors' import type { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base'; import { type Conversation, type Message, MessageRole } from '../../../common/types'; import { ChatHeader } from './chat_header'; -import { ChatPromptEditor } from './chat_prompt_editor'; +import { PromptEditor } from '../prompt_editor/prompt_editor'; import { ChatTimeline } from './chat_timeline'; import { Feedback } from '../feedback_buttons'; import { IncorrectLicensePanel } from './incorrect_license_panel'; @@ -214,7 +214,7 @@ export function ChatBody({ - + ); } diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor_function.tsx b/x-pack/plugins/observability_ai_assistant/public/components/prompt_editor/prompt_editor_function.tsx similarity index 82% rename from x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor_function.tsx rename to x-pack/plugins/observability_ai_assistant/public/components/prompt_editor/prompt_editor_function.tsx index 74e5d63702c4d..f7cbb55782dd7 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor_function.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/prompt_editor/prompt_editor_function.tsx @@ -4,12 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import usePrevious from 'react-use/lib/usePrevious'; +import { css } from '@emotion/css'; import { CodeEditor } from '@kbn/code-editor'; +import { monaco } from '@kbn/monaco'; import { i18n } from '@kbn/i18n'; -import usePrevious from 'react-use/lib/usePrevious'; import { EuiCode, EuiPanel } from '@elastic/eui'; -import { css } from '@emotion/css'; import { useJsonEditorModel } from '../../hooks/use_json_editor_model'; import { type Message, MessageRole } from '../../../common'; @@ -17,13 +18,22 @@ export interface Props { functionName: string; functionPayload?: string; onChange: (message: Message['message']) => void; + onFocus: () => void; + onBlur: () => void; } const functionNameClassName = css` display: inline-block; `; -export function ChatPromptEditorFunction({ functionName, functionPayload, onChange }: Props) { +export function PromptEditorFunction({ + functionName, + functionPayload, + onChange, + onFocus, + onBlur, +}: Props) { + const editorRef = useRef(null); const [functionEditorLineCount, setFunctionEditorLineCount] = useState(0); const previousPayload = usePrevious(functionPayload); @@ -33,8 +43,8 @@ export function ChatPromptEditorFunction({ functionName, functionPayload, onChan initialJson: functionPayload, }); - const handleChangeFunctionPayload = (params: string) => { - recalculateFunctionEditorLineCount(); + const handleChangePayload = (args: string) => { + recalculateLineCount(); onChange({ role: MessageRole.Assistant, @@ -42,12 +52,12 @@ export function ChatPromptEditorFunction({ functionName, functionPayload, onChan function_call: { name: functionName, trigger: MessageRole.User, - arguments: params, + arguments: args, }, }); }; - const recalculateFunctionEditorLineCount = useCallback(() => { + const recalculateLineCount = useCallback(() => { const newLineCount = model?.getLineCount() || 0; if (newLineCount !== functionEditorLineCount) { setFunctionEditorLineCount(newLineCount + 1); @@ -55,8 +65,8 @@ export function ChatPromptEditorFunction({ functionName, functionPayload, onChan }, [functionEditorLineCount, model]); useEffect(() => { - recalculateFunctionEditorLineCount(); - }, [model, recalculateFunctionEditorLineCount]); + recalculateLineCount(); + }, [model, recalculateLineCount]); useEffect(() => { if (previousPayload === undefined && initialJsonString) { @@ -72,6 +82,10 @@ export function ChatPromptEditorFunction({ functionName, functionPayload, onChan } }, [functionName, functionPayload, initialJsonString, onChange, previousPayload]); + editorRef.current?.onDidBlurEditorWidget(() => { + onBlur(); + }); + return ( {functionName} @@ -81,10 +95,15 @@ export function ChatPromptEditorFunction({ functionName, functionPayload, onChan { defaultMessage: 'payloadEditor' } )} data-test-subj="observabilityAiAssistantChatPromptEditorCodeEditor" + editorDidMount={(editor) => { + editorRef.current = editor; + editor.focus(); + onFocus(); + }} fullWidth height={'180px'} - languageId="json" isCopyable + languageId="json" languageConfiguration={{ autoClosingPairs: [ { @@ -93,9 +112,6 @@ export function ChatPromptEditorFunction({ functionName, functionPayload, onChan }, ], }} - editorDidMount={(editor) => { - editor.focus(); - }} options={{ accessibilitySupport: 'off', acceptSuggestionOnEnter: 'on', @@ -121,7 +137,7 @@ export function ChatPromptEditorFunction({ functionName, functionPayload, onChan }} transparentBackground value={functionPayload || ''} - onChange={handleChangeFunctionPayload} + onChange={handleChangePayload} /> ); diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor_prompt.tsx b/x-pack/plugins/observability_ai_assistant/public/components/prompt_editor/prompt_editor_natural_language.tsx similarity index 88% rename from x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor_prompt.tsx rename to x-pack/plugins/observability_ai_assistant/public/components/prompt_editor/prompt_editor_natural_language.tsx index 57c083d9704fd..e7bd833aee356 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_prompt_editor_prompt.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/prompt_editor/prompt_editor_natural_language.tsx @@ -14,9 +14,18 @@ interface Props { prompt: string | undefined; onChange: (message: Message['message']) => void; onChangeHeight: (height: number) => void; + onFocus: () => void; + onBlur: () => void; } -export function ChatPromptEditorPrompt({ disabled, prompt, onChange, onChangeHeight }: Props) { +export function PromptEditorNaturalLanguage({ + disabled, + prompt, + onChange, + onChangeHeight, + onFocus, + onBlur, +}: Props) { const textAreaRef = useRef(null); const handleChange = (event: React.ChangeEvent) => { @@ -44,6 +53,7 @@ export function ChatPromptEditorPrompt({ disabled, prompt, onChange, onChangeHei if (textarea) { textarea.focus(); + textarea.select(); } }, [handleResizeTextArea]); @@ -65,6 +75,8 @@ export function ChatPromptEditorPrompt({ disabled, prompt, onChange, onChangeHei rows={1} value={prompt || ''} onChange={handleChange} + onFocus={onFocus} + onBlur={onBlur} /> ); } diff --git a/x-pack/plugins/observability_ai_assistant/public/hooks/use_json_editor_model.ts b/x-pack/plugins/observability_ai_assistant/public/hooks/use_json_editor_model.ts index 230d0065e29c3..6f4535d84acef 100644 --- a/x-pack/plugins/observability_ai_assistant/public/hooks/use_json_editor_model.ts +++ b/x-pack/plugins/observability_ai_assistant/public/hooks/use_json_editor_model.ts @@ -12,9 +12,6 @@ import { safeJsonParse } from '../utils/safe_json_parse'; const { editor, languages, Uri } = monaco; -const SCHEMA_URI = 'http://elastic.co/foo.json'; -const modelUri = Uri.parse(SCHEMA_URI); - export const useJsonEditorModel = ({ functionName, initialJson, @@ -28,13 +25,17 @@ export const useJsonEditorModel = ({ const [initialJsonValue, setInitialJsonValue] = useState(initialJson); + const SCHEMA_URI = `http://elastic.co/${functionName}.json`; + + const modelUri = useMemo(() => Uri.parse(SCHEMA_URI), [SCHEMA_URI]); + useEffect(() => { setInitialJsonValue(initialJson); // eslint-disable-next-line react-hooks/exhaustive-deps }, [functionName]); return useMemo(() => { - if (!functionDefinition) { + if (!functionDefinition || !modelUri) { return {}; } @@ -66,5 +67,5 @@ export const useJsonEditorModel = ({ } return { model, initialJsonString }; - }, [functionDefinition, initialJsonValue]); + }, [SCHEMA_URI, functionDefinition, initialJsonValue, modelUri]); };