mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DashboardAI: UX improvements (#84934)
This commit is contained in:
parent
26473a0074
commit
5b0b8cb4bf
@ -85,10 +85,12 @@ export const availableIconsIndex = {
|
|||||||
'dice-three': true,
|
'dice-three': true,
|
||||||
docker: true,
|
docker: true,
|
||||||
'document-info': true,
|
'document-info': true,
|
||||||
|
'document-layout-left': true,
|
||||||
'download-alt': true,
|
'download-alt': true,
|
||||||
draggabledots: true,
|
draggabledots: true,
|
||||||
edit: true,
|
edit: true,
|
||||||
'ellipsis-v': true,
|
'ellipsis-v': true,
|
||||||
|
enter: true,
|
||||||
envelope: true,
|
envelope: true,
|
||||||
'exchange-alt': true,
|
'exchange-alt': true,
|
||||||
'exclamation-triangle': true,
|
'exclamation-triangle': true,
|
||||||
@ -176,6 +178,7 @@ export const availableIconsIndex = {
|
|||||||
monitor: true,
|
monitor: true,
|
||||||
palette: true,
|
palette: true,
|
||||||
'panel-add': true,
|
'panel-add': true,
|
||||||
|
paragraph: true,
|
||||||
'pathfinder-unite': true,
|
'pathfinder-unite': true,
|
||||||
pause: true,
|
pause: true,
|
||||||
pen: true,
|
pen: true,
|
||||||
|
@ -43,15 +43,7 @@ export const GenAIButton = ({
|
|||||||
}: GenAIButtonProps) => {
|
}: GenAIButtonProps) => {
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
const {
|
const { setMessages, setStopGeneration, reply, value, error, streamStatus } = useOpenAIStream(model, temperature);
|
||||||
messages: streamMessages,
|
|
||||||
setMessages,
|
|
||||||
setStopGeneration,
|
|
||||||
reply,
|
|
||||||
value,
|
|
||||||
error,
|
|
||||||
streamStatus,
|
|
||||||
} = useOpenAIStream(model, temperature);
|
|
||||||
|
|
||||||
const [history, setHistory] = useState<string[]>([]);
|
const [history, setHistory] = useState<string[]>([]);
|
||||||
const [showHistory, setShowHistory] = useState(false);
|
const [showHistory, setShowHistory] = useState(false);
|
||||||
@ -68,7 +60,7 @@ export const GenAIButton = ({
|
|||||||
} else {
|
} else {
|
||||||
if (!hasHistory) {
|
if (!hasHistory) {
|
||||||
onClickProp?.(e);
|
onClickProp?.(e);
|
||||||
setMessages(typeof messages === 'function' ? messages() : messages);
|
setMessages(getMessages());
|
||||||
} else {
|
} else {
|
||||||
setShowHistory(true);
|
setShowHistory(true);
|
||||||
}
|
}
|
||||||
@ -158,6 +150,13 @@ export const GenAIButton = ({
|
|||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getMessages = () => {
|
||||||
|
if (typeof messages === 'function') {
|
||||||
|
return messages();
|
||||||
|
}
|
||||||
|
return messages;
|
||||||
|
};
|
||||||
|
|
||||||
const renderButtonWithToggletip = () => {
|
const renderButtonWithToggletip = () => {
|
||||||
if (hasHistory) {
|
if (hasHistory) {
|
||||||
const title = <Text element="p">{toggleTipTitle}</Text>;
|
const title = <Text element="p">{toggleTipTitle}</Text>;
|
||||||
@ -168,7 +167,7 @@ export const GenAIButton = ({
|
|||||||
content={
|
content={
|
||||||
<GenAIHistory
|
<GenAIHistory
|
||||||
history={history}
|
history={history}
|
||||||
messages={streamMessages}
|
messages={getMessages()}
|
||||||
onApplySuggestion={onApplySuggestion}
|
onApplySuggestion={onApplySuggestion}
|
||||||
updateHistory={pushHistoryEntry}
|
updateHistory={pushHistoryEntry}
|
||||||
eventTrackingSrc={eventTrackingSrc}
|
eventTrackingSrc={eventTrackingSrc}
|
||||||
|
@ -2,7 +2,7 @@ import { css } from '@emotion/css';
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { Alert, Button, Icon, IconButton, Input, Stack, Text, TextLink, useStyles2 } from '@grafana/ui';
|
import { Alert, Button, Icon, Input, Stack, Text, TextLink, useStyles2 } from '@grafana/ui';
|
||||||
|
|
||||||
import { STOP_GENERATION_TEXT } from './GenAIButton';
|
import { STOP_GENERATION_TEXT } from './GenAIButton';
|
||||||
import { GenerationHistoryCarousel } from './GenerationHistoryCarousel';
|
import { GenerationHistoryCarousel } from './GenerationHistoryCarousel';
|
||||||
@ -120,31 +120,37 @@ export const GenAIHistory = ({
|
|||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<GenerationHistoryCarousel
|
||||||
|
history={history}
|
||||||
|
index={currentIndex}
|
||||||
|
onNavigate={onNavigate}
|
||||||
|
reply={sanitizeReply(reply)}
|
||||||
|
streamStatus={streamStatus}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className={styles.actionButtons}>
|
||||||
|
<QuickFeedback onSuggestionClick={onGenerateWithFeedback} isGenerating={isStreamGenerating} />
|
||||||
|
</div>
|
||||||
|
|
||||||
<Input
|
<Input
|
||||||
placeholder="Tell AI what to do next..."
|
placeholder="Tell AI what to do next..."
|
||||||
suffix={
|
suffix={
|
||||||
<IconButton
|
<Button
|
||||||
name="corner-down-right-alt"
|
icon="enter"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
fill="text"
|
||||||
aria-label="Send custom feedback"
|
aria-label="Send custom feedback"
|
||||||
onClick={onClickSubmitCustomFeedback}
|
onClick={onClickSubmitCustomFeedback}
|
||||||
disabled={customFeedback === ''}
|
disabled={customFeedback === ''}
|
||||||
/>
|
>
|
||||||
|
Send
|
||||||
|
</Button>
|
||||||
}
|
}
|
||||||
value={customFeedback}
|
value={customFeedback}
|
||||||
onChange={onChangeCustomFeedback}
|
onChange={onChangeCustomFeedback}
|
||||||
onKeyDown={onKeyDownCustomFeedbackInput}
|
onKeyDown={onKeyDownCustomFeedbackInput}
|
||||||
/>
|
/>
|
||||||
<div className={styles.actions}>
|
|
||||||
<QuickFeedback onSuggestionClick={onGenerateWithFeedback} isGenerating={isStreamGenerating} />
|
|
||||||
<GenerationHistoryCarousel
|
|
||||||
history={history}
|
|
||||||
index={currentIndex}
|
|
||||||
onNavigate={onNavigate}
|
|
||||||
reply={sanitizeReply(reply)}
|
|
||||||
streamStatus={streamStatus}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className={styles.applySuggestion}>
|
<div className={styles.applySuggestion}>
|
||||||
<Stack justifyContent={'flex-end'} direction={'row'}>
|
<Stack justifyContent={'flex-end'} direction={'row'}>
|
||||||
<Button icon={!isStreamGenerating ? 'check' : 'fa fa-spinner'} onClick={onApply}>
|
<Button icon={!isStreamGenerating ? 'check' : 'fa fa-spinner'} onClick={onApply}>
|
||||||
@ -152,12 +158,14 @@ export const GenAIHistory = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.footer}>
|
<div className={styles.footer}>
|
||||||
<Icon name="exclamation-circle" aria-label="exclamation-circle" className={styles.infoColor} />
|
<Icon name="exclamation-circle" aria-label="exclamation-circle" className={styles.infoColor} />
|
||||||
<Text variant="bodySmall" color="secondary">
|
<Text variant="bodySmall" color="secondary">
|
||||||
This content is AI-generated using the{' '}
|
This content is AI-generated using the{' '}
|
||||||
<TextLink
|
<TextLink
|
||||||
variant="bodySmall"
|
variant="bodySmall"
|
||||||
|
inline={true}
|
||||||
href="https://grafana.com/docs/grafana-cloud/alerting-and-irm/machine-learning/llm-plugin/"
|
href="https://grafana.com/docs/grafana-cloud/alerting-and-irm/machine-learning/llm-plugin/"
|
||||||
external
|
external
|
||||||
onClick={onClickDocs}
|
onClick={onClickDocs}
|
||||||
@ -177,10 +185,10 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
width: 520,
|
width: 520,
|
||||||
maxHeight: 350,
|
maxHeight: 350,
|
||||||
// This is the space the footer height
|
// This is the space the footer height
|
||||||
paddingBottom: 35,
|
paddingBottom: 25,
|
||||||
}),
|
}),
|
||||||
applySuggestion: css({
|
applySuggestion: css({
|
||||||
marginTop: theme.spacing(1),
|
paddingTop: theme.spacing(2),
|
||||||
}),
|
}),
|
||||||
actions: css({
|
actions: css({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -206,4 +214,10 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
infoColor: css({
|
infoColor: css({
|
||||||
color: theme.colors.info.main,
|
color: theme.colors.info.main,
|
||||||
}),
|
}),
|
||||||
|
actionButtons: css({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
padding: '24px 0 8px 0',
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
@ -35,18 +35,18 @@ export const GenerationHistoryCarousel = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MinimalisticPagination
|
|
||||||
currentPage={index}
|
|
||||||
numberOfPages={historySize}
|
|
||||||
onNavigate={onNavigate}
|
|
||||||
hideWhenSinglePage={true}
|
|
||||||
className={styles.paginationWrapper}
|
|
||||||
/>
|
|
||||||
<div className={styles.contentWrapper}>
|
<div className={styles.contentWrapper}>
|
||||||
<Text element="p" color="secondary">
|
<Text element="p" color="secondary">
|
||||||
{getHistoryText()}
|
{getHistoryText()}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
|
<MinimalisticPagination
|
||||||
|
currentPage={index}
|
||||||
|
numberOfPages={historySize}
|
||||||
|
onNavigate={onNavigate}
|
||||||
|
hideWhenSinglePage={false}
|
||||||
|
className={styles.paginationWrapper}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -63,8 +63,11 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
flexBasis: '100%',
|
flexBasis: '100%',
|
||||||
flexGrow: 3,
|
flexGrow: 3,
|
||||||
whiteSpace: 'pre-wrap',
|
whiteSpace: 'pre-wrap',
|
||||||
marginTop: 20,
|
maxHeight: 110,
|
||||||
height: 110,
|
|
||||||
overflowY: 'scroll',
|
overflowY: 'scroll',
|
||||||
|
backgroundColor: theme.colors.background.secondary,
|
||||||
|
border: `1px solid ${theme.colors.border.weak}`,
|
||||||
|
padding: theme.spacing(1),
|
||||||
|
minHeight: 60,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -20,6 +20,7 @@ export const QuickFeedback = ({ onSuggestionClick, isGenerating }: QuickActionsP
|
|||||||
onClick={() => onSuggestionClick(QuickFeedbackType.Shorter)}
|
onClick={() => onSuggestionClick(QuickFeedbackType.Shorter)}
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
icon="paragraph"
|
||||||
disabled={isGenerating}
|
disabled={isGenerating}
|
||||||
>
|
>
|
||||||
{QuickFeedbackType.Shorter}
|
{QuickFeedbackType.Shorter}
|
||||||
@ -28,12 +29,14 @@ export const QuickFeedback = ({ onSuggestionClick, isGenerating }: QuickActionsP
|
|||||||
onClick={() => onSuggestionClick(QuickFeedbackType.MoreDescriptive)}
|
onClick={() => onSuggestionClick(QuickFeedbackType.MoreDescriptive)}
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
icon="document-layout-left"
|
||||||
disabled={isGenerating}
|
disabled={isGenerating}
|
||||||
>
|
>
|
||||||
{QuickFeedbackType.MoreDescriptive}
|
{QuickFeedbackType.MoreDescriptive}
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => onSuggestionClick(QuickFeedbackType.Regenerate)}
|
onClick={() => onSuggestionClick(QuickFeedbackType.Regenerate)}
|
||||||
|
icon="sync"
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
disabled={isGenerating}
|
disabled={isGenerating}
|
||||||
@ -48,10 +51,6 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
quickSuggestionsWrapper: css({
|
quickSuggestionsWrapper: css({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
flexGrow: 1,
|
|
||||||
gap: 8,
|
gap: 8,
|
||||||
paddingTop: 10,
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user