AppChat: Draw: inline enhancements

This commit is contained in:
Enrico Ros
2025-02-23 00:02:43 -08:00
parent 195255ce9a
commit ee45f3cae9
3 changed files with 93 additions and 14 deletions
+15 -10
View File
@@ -2,10 +2,9 @@ import * as React from 'react';
import { useShallow } from 'zustand/react/shallow';
import type { FileWithHandle } from 'browser-fs-access';
import { Box, Button, ButtonGroup, Card, Dropdown, Grid, IconButton, Menu, MenuButton, MenuItem, Textarea, Tooltip, Typography } from '@mui/joy';
import { Box, Button, ButtonGroup, Card, Dropdown, Grid, IconButton, Menu, MenuButton, MenuItem, Textarea, Typography } from '@mui/joy';
import { ColorPaletteProp, SxProps, VariantProp } from '@mui/joy/styles/types';
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline';
import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import FormatPaintTwoToneIcon from '@mui/icons-material/FormatPaintTwoTone';
import PsychologyIcon from '@mui/icons-material/Psychology';
@@ -74,6 +73,7 @@ import { ButtonMicMemo } from './buttons/ButtonMic';
import { ButtonMultiChatMemo } from './buttons/ButtonMultiChat';
import { ButtonOptionsDraw } from './buttons/ButtonOptionsDraw';
import { ComposerTextAreaActions } from './textarea/ComposerTextAreaActions';
import { ComposerTextAreaDrawActions } from './textarea/ComposerTextAreaDrawActions';
import { StatusBarMemo } from '../StatusBar';
import { TokenBadgeMemo } from './tokens/TokenBadge';
import { TokenProgressbarMemo } from './tokens/TokenProgressbar';
@@ -832,7 +832,7 @@ export function Composer(props: {
variant='outlined'
color={isDraw ? 'warning' : isReAct ? 'success' : undefined}
autoFocus
minRows={isMobile ? 4 : agiAttachmentPrompts.hasData ? 3 : showChatInReferenceTo ? 4 : 5}
minRows={isMobile ? 4 : isDraw ? 4 : agiAttachmentPrompts.hasData ? 3 : showChatInReferenceTo ? 4 : 5}
maxRows={isMobile ? 8 : 10}
placeholder={textPlaceholder}
value={composeText}
@@ -841,8 +841,12 @@ export function Composer(props: {
onPasteCapture={handleAttachCtrlV}
// onFocusCapture={handleFocusModeOn}
// onBlurCapture={handleFocusModeOff}
endDecorator={
<ComposerTextAreaActions
endDecorator={isDraw
? <ComposerTextAreaDrawActions
composerText={composeText}
onReplaceText={setComposeText}
/>
: <ComposerTextAreaActions
agiAttachmentPrompts={agiAttachmentPrompts}
inReferenceTo={inReferenceTo}
onAppendAndSend={handleAppendTextAndSend}
@@ -1027,11 +1031,12 @@ export function Composer(props: {
{/*</Tooltip>}*/}
{/* [Draw] Imagine */}
{isDraw && !!composeText && <Tooltip title='Generate an image prompt'>
<IconButton variant='outlined' disabled={noConversation || noLLM} onClick={handleTextImagineClicked}>
<AutoAwesomeIcon />
</IconButton>
</Tooltip>}
{/* NOTE: disabled: as we have prompt enhancement in the TextArea (Draw Mode) already */}
{/*{isDraw && !!composeText && <Tooltip title='Generate an image prompt'>*/}
{/* <IconButton variant='outlined' disabled={noConversation || noLLM} onClick={handleTextImagineClicked}>*/}
{/* <AutoAwesomeIcon />*/}
{/* </IconButton>*/}
{/*</Tooltip>}*/}
{/* Mode expander */}
<IconButton
@@ -15,7 +15,7 @@ export const AGI_SUGGESTIONS_COLOR: ColorPaletteProp = 'success';
// Styles
const textAreaSx: SxProps = {
export const composerTextAreaSx: SxProps = {
flex: 1,
// layout
@@ -29,8 +29,8 @@ const textAreaSx: SxProps = {
'--Button-gap': '1.2rem',
transition: 'background-color 0.2s, color 0.2s',
// minWidth: 160,
},
};
} as const,
} as const;
const promptButtonSx: SxProps = {
@@ -75,7 +75,7 @@ export function ComposerTextAreaActions(props: {
return null;
return (
<Box sx={textAreaSx}>
<Box sx={composerTextAreaSx}>
{/* In-Reference-To bubbles */}
{props.inReferenceTo?.map((item, index) => (
@@ -0,0 +1,74 @@
import * as React from 'react';
import { Box, Button } from '@mui/joy';
import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh';
import { composerTextAreaSx } from './ComposerTextAreaActions';
import { imaginePromptFromTextOrThrow } from '~/modules/aifn/imagine/imaginePromptFromText';
const _style = {
enhance: {
minWidth: 170,
mx: 0.625,
pr: 2,
border: '1px solid',
borderColor: 'warning.outlinedBorder',
boxShadow: '0px 4px 4px -4px rgb(var(--joy-palette-warning-darkChannel) / 20%)',
transition: 'background-color 0.14s',
justifyContent: 'space-between',
} as const,
gone: {
visibility: 'hidden',
} as const,
} as const;
export function ComposerTextAreaDrawActions(props: {
composerText: string,
onReplaceText: (text: string) => void,
}) {
// state
const [isSimpleEnhancing, setIsSimpleEnhancing] = React.useState(false);
// derived
const trimmedPrompt = props.composerText.trim();
const userHasText = !!trimmedPrompt;
const { onReplaceText } = props;
const handleSimpleEnhance = React.useCallback(async () => {
if (!trimmedPrompt || isSimpleEnhancing) return;
setIsSimpleEnhancing(true);
const improvedPrompt = await imaginePromptFromTextOrThrow(trimmedPrompt, 'DEV')
.catch(console.error);
if (improvedPrompt)
onReplaceText(improvedPrompt);
setIsSimpleEnhancing(false);
}, [isSimpleEnhancing, onReplaceText, trimmedPrompt]);
return (
<Box sx={composerTextAreaSx}>
{/* Enhance button */}
<Button
size='sm'
variant={isSimpleEnhancing ? 'soft' : 'soft'}
color='warning'
disabled={!userHasText}
loading={isSimpleEnhancing}
loadingPosition='end'
// className={promptButtonClass}
endDecorator={<AutoFixHighIcon sx={{ fontSize: '20px' }} />}
onClick={handleSimpleEnhance}
sx={!userHasText ? _style.gone : _style.enhance}
>
{isSimpleEnhancing ? 'Enhancing...' : 'Enhance Prompt'}
</Button>
</Box>
);
}