From bf73815f2bd40302a94c1b8ec34774094426d188 Mon Sep 17 00:00:00 2001 From: Andy Pickering Date: Fri, 13 Feb 2026 21:51:03 +0900 Subject: [PATCH] Automatically convert PromQL code blocks to QueryBrowser graphs --- src/components/GeneralPage.tsx | 14 +++++++- src/components/Prompt.tsx | 3 +- src/components/QueryBrowserGraph.tsx | 49 ++++++++++++++++++++++++++++ src/components/general-page.css | 6 ++++ src/llm-instructions.ts | 13 ++++++++ 5 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/components/QueryBrowserGraph.tsx create mode 100644 src/llm-instructions.ts diff --git a/src/components/GeneralPage.tsx b/src/components/GeneralPage.tsx index 5d8d3bfd..4237f4e9 100644 --- a/src/components/GeneralPage.tsx +++ b/src/components/GeneralPage.tsx @@ -64,6 +64,7 @@ import ImportAction from './ImportAction'; import NewChatModal from './NewChatModal'; import Prompt from './Prompt'; import ReadinessAlert from './ReadinessAlert'; +import QueryBrowserGraph from './QueryBrowserGraph'; import ResponseTools from './ResponseTools'; import WelcomeNotice from './WelcomeNotice'; @@ -94,7 +95,18 @@ const isURL = (s: string): boolean => { } }; -const Code = ({ children }: { children?: React.ReactNode }) => { +type CodeProps = { + children?: React.ReactNode; + className?: string; +}; + +const Code: React.FC = ({ children, className }) => { + const isPromQL = className?.includes('language-promql'); + + if (isPromQL && children) { + return ; + } + if (!children || !String(children).includes('\n')) { return {children}; } diff --git a/src/components/Prompt.tsx b/src/components/Prompt.tsx index 101b42dc..5c970189 100644 --- a/src/components/Prompt.tsx +++ b/src/components/Prompt.tsx @@ -25,6 +25,7 @@ import { FileCodeIcon, FileUploadIcon, InfoCircleIcon, TaskIcon } from '@pattern import { AttachmentTypes, toOLSAttachment } from '../attachments'; import { getApiUrl } from '../config'; +import { LLM_INSTRUCTIONS } from '../llm-instructions'; import { getFetchErrorMessage } from '../error'; import { getRequestInitWithAuthHeader } from '../hooks/useAuth'; import { useBoolean } from '../hooks/useBoolean'; @@ -498,7 +499,7 @@ const Prompt: React.FC = ({ scrollIntoView }) => { conversation_id: conversationID, // eslint-disable-next-line camelcase media_type: 'application/json', - query, + query: `${query}\n\n${LLM_INSTRUCTIONS}`, }; const streamResponse = async () => { diff --git a/src/components/QueryBrowserGraph.tsx b/src/components/QueryBrowserGraph.tsx new file mode 100644 index 00000000..71cfad9d --- /dev/null +++ b/src/components/QueryBrowserGraph.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; +import { debounce } from 'lodash'; +import { QueryBrowser } from '@openshift-console/dynamic-plugin-sdk'; +import { Spinner, TextArea } from '@patternfly/react-core'; + +const QueryBrowserGraph: React.FC<{ query: string }> = ({ query: initialQuery }) => { + const [inputValue, setInputValue] = React.useState(''); + const [query, setQuery] = React.useState(initialQuery); + + const debouncedSetQuery = React.useMemo( + () => + debounce((value: string) => { + setQuery(value); + }, 500), + [], + ); + + const effectiveQuery = inputValue || initialQuery; + + React.useEffect(() => { + debouncedSetQuery(effectiveQuery); + }, [debouncedSetQuery, effectiveQuery]); + + const onChange = React.useCallback( + (_event: React.ChangeEvent, value: string) => { + setInputValue(value); + }, + [], + ); + + return ( + <> + }> + + +