mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +01:00
chore: Unleash AI UX adjustments: placement, icon, color (#8521)
https://linear.app/unleash/issue/2-2870/ux-adjustments-following-the-breakathon-placement-on-demo-bot-icon-and Post-breakathon UX adjustments, including: - Properly positions the Unleash AI chat option to the left of the demo steps, when the demo steps are visible. - Replaces the bot icon with a friendlier, more upbeat version. - Switches the chat purple color in the light theme to `primary.main` for better accessibility. Additionally, I’ve added the mode property to our themes for easier future maintenance. This makes it simple to check the currently active theme. ![image](https://github.com/user-attachments/assets/bc0c2f99-5460-4bc7-8aa0-e8d94156b669)
This commit is contained in:
parent
b7b5a8ae48
commit
ababe9fe75
3
frontend/src/assets/icons/AI.svg
Normal file
3
frontend/src/assets/icons/AI.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="22" height="19" viewBox="0 0 22 19" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M19 7V5C19 3.9 18.1 3 17 3H14C14 1.34 12.66 0 11 0C9.34 0 8 1.34 8 3H5C3.9 3 3 3.9 3 5V7C1.34 7 0 8.34 0 10C0 11.66 1.34 13 3 13V17C3 18.1 3.9 19 5 19H17C18.1 19 19 18.1 19 17V13C20.66 13 22 11.66 22 10C22 8.34 20.66 7 19 7ZM17 17H5V5H17V17ZM8 11C7.17 11 6.5 10.33 6.5 9.5C6.5 8.67 7.17 8 8 8C8.83 8 9.5 8.67 9.5 9.5C9.5 10.33 8.83 11 8 11ZM15.5 9.5C15.5 10.33 14.83 11 14 11C13.17 11 12.5 10.33 12.5 9.5C12.5 8.67 13.17 8 14 8C14.83 8 15.5 8.67 15.5 9.5ZM7 13L11 14L15 13C15 13 15.5 14 15 14.5C14.5 15 11 15.5 11 15.5C11 15.5 7.5 15 7 14.5C6.5 14 7 13 7 13Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 687 B |
@ -1,6 +1,6 @@
|
|||||||
import { mutate } from 'swr';
|
import { mutate } from 'swr';
|
||||||
import SmartToyIcon from '@mui/icons-material/SmartToy';
|
import { ReactComponent as AIIcon } from 'assets/icons/AI.svg';
|
||||||
import { IconButton, styled } from '@mui/material';
|
import { IconButton, styled, useMediaQuery } from '@mui/material';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
@ -16,6 +16,7 @@ import { AIChatHeader } from './AIChatHeader';
|
|||||||
import { Resizable } from 'component/common/Resizable/Resizable';
|
import { Resizable } from 'component/common/Resizable/Resizable';
|
||||||
import { AIChatDisclaimer } from './AIChatDisclaimer';
|
import { AIChatDisclaimer } from './AIChatDisclaimer';
|
||||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||||
|
import theme from 'themes/theme';
|
||||||
|
|
||||||
const AI_ERROR_MESSAGE = {
|
const AI_ERROR_MESSAGE = {
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
@ -26,10 +27,15 @@ type ScrollOptions = ScrollIntoViewOptions & {
|
|||||||
onlyIfAtEnd?: boolean;
|
onlyIfAtEnd?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledAIIconContainer = styled('div')(({ theme }) => ({
|
const StyledAIIconContainer = styled('div', {
|
||||||
|
shouldForwardProp: (prop) => prop !== 'demoStepsVisible',
|
||||||
|
})<{ demoStepsVisible: boolean }>(({ theme, demoStepsVisible }) => ({
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
bottom: 20,
|
bottom: 20,
|
||||||
right: 20,
|
right: 20,
|
||||||
|
...(demoStepsVisible && {
|
||||||
|
right: 260,
|
||||||
|
}),
|
||||||
zIndex: theme.zIndex.fab,
|
zIndex: theme.zIndex.fab,
|
||||||
animation: 'fadeInBottom 0.5s',
|
animation: 'fadeInBottom 0.5s',
|
||||||
'@keyframes fadeInBottom': {
|
'@keyframes fadeInBottom': {
|
||||||
@ -44,10 +50,15 @@ const StyledAIIconContainer = styled('div')(({ theme }) => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledAIChatContainer = styled(StyledAIIconContainer)({
|
const StyledAIChatContainer = styled(StyledAIIconContainer, {
|
||||||
|
shouldForwardProp: (prop) => prop !== 'demoStepsVisible',
|
||||||
|
})<{ demoStepsVisible: boolean }>(({ demoStepsVisible }) => ({
|
||||||
bottom: 10,
|
bottom: 10,
|
||||||
right: 10,
|
right: 10,
|
||||||
});
|
...(demoStepsVisible && {
|
||||||
|
right: 250,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
const StyledResizable = styled(Resizable)(({ theme }) => ({
|
const StyledResizable = styled(Resizable)(({ theme }) => ({
|
||||||
boxShadow: theme.boxShadows.popup,
|
boxShadow: theme.boxShadows.popup,
|
||||||
@ -55,13 +66,20 @@ const StyledResizable = styled(Resizable)(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledAIIconButton = styled(IconButton)(({ theme }) => ({
|
const StyledAIIconButton = styled(IconButton)(({ theme }) => ({
|
||||||
background: theme.palette.primary.light,
|
background:
|
||||||
|
theme.mode === 'light'
|
||||||
|
? theme.palette.primary.main
|
||||||
|
: theme.palette.primary.light,
|
||||||
color: theme.palette.primary.contrastText,
|
color: theme.palette.primary.contrastText,
|
||||||
boxShadow: theme.boxShadows.popup,
|
boxShadow: theme.boxShadows.popup,
|
||||||
transition: 'background 0.3s',
|
transition: 'background 0.3s',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
background: theme.palette.primary.dark,
|
background: theme.palette.primary.dark,
|
||||||
},
|
},
|
||||||
|
'& > svg': {
|
||||||
|
width: theme.spacing(3),
|
||||||
|
height: theme.spacing(3),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledChat = styled('div')(({ theme }) => ({
|
const StyledChat = styled('div')(({ theme }) => ({
|
||||||
@ -84,6 +102,8 @@ const StyledChatContent = styled('div')(({ theme }) => ({
|
|||||||
|
|
||||||
export const AIChat = () => {
|
export const AIChat = () => {
|
||||||
const unleashAIEnabled = useUiFlag('unleashAI');
|
const unleashAIEnabled = useUiFlag('unleashAI');
|
||||||
|
const demoEnabled = useUiFlag('demo');
|
||||||
|
const isSmallScreen = useMediaQuery(theme.breakpoints.down(768));
|
||||||
const {
|
const {
|
||||||
uiConfig: { unleashAIAvailable },
|
uiConfig: { unleashAIAvailable },
|
||||||
} = useUiConfig();
|
} = useUiConfig();
|
||||||
@ -169,13 +189,15 @@ export const AIChat = () => {
|
|||||||
newChat();
|
newChat();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const demoStepsVisible = demoEnabled && !isSmallScreen;
|
||||||
|
|
||||||
if (!unleashAIEnabled || !unleashAIAvailable) {
|
if (!unleashAIEnabled || !unleashAIAvailable) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!open) {
|
if (!open) {
|
||||||
return (
|
return (
|
||||||
<StyledAIIconContainer>
|
<StyledAIIconContainer demoStepsVisible={demoStepsVisible}>
|
||||||
<StyledAIIconButton
|
<StyledAIIconButton
|
||||||
size='large'
|
size='large'
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -187,18 +209,18 @@ export const AIChat = () => {
|
|||||||
setOpen(true);
|
setOpen(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SmartToyIcon />
|
<AIIcon />
|
||||||
</StyledAIIconButton>
|
</StyledAIIconButton>
|
||||||
</StyledAIIconContainer>
|
</StyledAIIconContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledAIChatContainer>
|
<StyledAIChatContainer demoStepsVisible={demoStepsVisible}>
|
||||||
<StyledResizable
|
<StyledResizable
|
||||||
handlers={['top-left', 'top', 'left']}
|
handlers={['top-left', 'top', 'left']}
|
||||||
minSize={{ width: '270px', height: '250px' }}
|
minSize={{ width: '270px', height: '250px' }}
|
||||||
maxSize={{ width: '90vw', height: '90vh' }}
|
maxSize={{ width: '80vw', height: '90vh' }}
|
||||||
defaultSize={{ width: '320px', height: '500px' }}
|
defaultSize={{ width: '320px', height: '500px' }}
|
||||||
onResize={() => scrollToEnd({ onlyIfAtEnd: true })}
|
onResize={() => scrollToEnd({ onlyIfAtEnd: true })}
|
||||||
>
|
>
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { IconButton, styled, Tooltip, Typography } from '@mui/material';
|
import { IconButton, styled, Tooltip, Typography } from '@mui/material';
|
||||||
import SmartToyIcon from '@mui/icons-material/SmartToy';
|
import { ReactComponent as AIIcon } from 'assets/icons/AI.svg';
|
||||||
import EditNoteIcon from '@mui/icons-material/EditNote';
|
import EditNoteIcon from '@mui/icons-material/EditNote';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
|
|
||||||
const StyledHeader = styled('div')(({ theme }) => ({
|
const StyledHeader = styled('div')(({ theme }) => ({
|
||||||
background: theme.palette.primary.light,
|
background:
|
||||||
|
theme.mode === 'light'
|
||||||
|
? theme.palette.primary.main
|
||||||
|
: theme.palette.primary.light,
|
||||||
color: theme.palette.primary.contrastText,
|
color: theme.palette.primary.contrastText,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@ -41,7 +44,7 @@ export const AIChatHeader = ({ onNew, onClose }: IAIChatHeaderProps) => {
|
|||||||
return (
|
return (
|
||||||
<StyledHeader>
|
<StyledHeader>
|
||||||
<StyledTitleContainer>
|
<StyledTitleContainer>
|
||||||
<SmartToyIcon />
|
<AIIcon />
|
||||||
<StyledTitle>Unleash AI</StyledTitle>
|
<StyledTitle>Unleash AI</StyledTitle>
|
||||||
</StyledTitleContainer>
|
</StyledTitleContainer>
|
||||||
<StyledActionsContainer>
|
<StyledActionsContainer>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Avatar, styled } from '@mui/material';
|
import { Avatar, styled } from '@mui/material';
|
||||||
import SmartToyIcon from '@mui/icons-material/SmartToy';
|
import { ReactComponent as AIIcon } from 'assets/icons/AI.svg';
|
||||||
import { Markdown } from 'component/common/Markdown/Markdown';
|
import { Markdown } from 'component/common/Markdown/Markdown';
|
||||||
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
|
import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
|
||||||
import type { ChatMessage } from 'hooks/api/actions/useAIApi/useAIApi';
|
import type { ChatMessage } from 'hooks/api/actions/useAIApi/useAIApi';
|
||||||
@ -62,7 +62,10 @@ const StyledUserMessage = styled(StyledAIMessage)(({ theme }) => ({
|
|||||||
const StyledAvatar = styled(Avatar)(({ theme }) => ({
|
const StyledAvatar = styled(Avatar)(({ theme }) => ({
|
||||||
width: theme.spacing(4.5),
|
width: theme.spacing(4.5),
|
||||||
height: theme.spacing(4.5),
|
height: theme.spacing(4.5),
|
||||||
backgroundColor: theme.palette.primary.light,
|
backgroundColor:
|
||||||
|
theme.mode === 'light'
|
||||||
|
? theme.palette.primary.main
|
||||||
|
: theme.palette.primary.light,
|
||||||
color: theme.palette.primary.contrastText,
|
color: theme.palette.primary.contrastText,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -89,7 +92,7 @@ export const AIChatMessage = ({ from, children }: IAIChatMessageProps) => {
|
|||||||
return (
|
return (
|
||||||
<StyledMessageContainer>
|
<StyledMessageContainer>
|
||||||
<StyledAvatar>
|
<StyledAvatar>
|
||||||
<SmartToyIcon />
|
<AIIcon />
|
||||||
</StyledAvatar>
|
</StyledAvatar>
|
||||||
<StyledAIMessage>
|
<StyledAIMessage>
|
||||||
<Markdown>{children}</Markdown>
|
<Markdown>{children}</Markdown>
|
||||||
|
@ -12,6 +12,7 @@ const actionColors = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const theme = {
|
const theme = {
|
||||||
|
mode: 'dark',
|
||||||
breakpoints: {
|
breakpoints: {
|
||||||
values: {
|
values: {
|
||||||
xs: 0,
|
xs: 0,
|
||||||
@ -309,7 +310,7 @@ const theme = {
|
|||||||
series: colors.chartSeries,
|
series: colors.chartSeries,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
} as const;
|
||||||
|
|
||||||
export default createTheme({
|
export default createTheme({
|
||||||
...theme,
|
...theme,
|
||||||
|
@ -4,6 +4,7 @@ import { alpha } from '@mui/material';
|
|||||||
import { focusable } from 'themes/themeStyles';
|
import { focusable } from 'themes/themeStyles';
|
||||||
|
|
||||||
export const theme = {
|
export const theme = {
|
||||||
|
mode: 'light',
|
||||||
breakpoints: {
|
breakpoints: {
|
||||||
values: {
|
values: {
|
||||||
xs: 0,
|
xs: 0,
|
||||||
@ -294,7 +295,7 @@ export const theme = {
|
|||||||
series: colors.chartSeries,
|
series: colors.chartSeries,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
} as const;
|
||||||
|
|
||||||
export default createTheme({
|
export default createTheme({
|
||||||
...theme,
|
...theme,
|
||||||
|
@ -3,6 +3,7 @@ import { FormHelperTextOwnProps } from '@mui/material/FormHelperText';
|
|||||||
|
|
||||||
declare module '@mui/material/styles' {
|
declare module '@mui/material/styles' {
|
||||||
interface CustomTheme {
|
interface CustomTheme {
|
||||||
|
mode: 'light' | 'dark';
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user