Skip to content

Commit

Permalink
Added ability to specify images via URL instead of base64
Browse files Browse the repository at this point in the history
  • Loading branch information
Dmitri Nasonov committed Jul 2, 2024
1 parent 0e79fe4 commit f222084
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 50 deletions.
2 changes: 2 additions & 0 deletions public/locales/en/main.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"save": "Save",
"generate": "Generate",
"add_image_url": "Add image URL",
"enter_image_url_placeholder": "Enter image URL",
"cancel": "Cancel",
"confirm": "Confirm",
"warning": "Warning",
Expand Down
23 changes: 17 additions & 6 deletions src/components/Chat/ChatContent/Message/View/ContentView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ import CrossIcon from '@icon/CrossIcon';

import useSubmit from '@hooks/useSubmit';

import { ChatInterface, ContentInterface, ImageContentInterface, TextContentInterface } from '@type/chat';
import {
ChatInterface,
ContentInterface,
ImageContentInterface,
TextContentInterface,
} from '@type/chat';

import { codeLanguageSubset } from '@constants/chat';

Expand All @@ -41,7 +46,7 @@ const ContentView = memo(
messageIndex,
}: {
role: string;
content: ContentInterface[],
content: ContentInterface[];
setIsEdit: React.Dispatch<React.SetStateAction<boolean>>;
messageIndex: number;
}) => {
Expand Down Expand Up @@ -132,13 +137,19 @@ const ContentView = memo(
{(content[0] as TextContentInterface).text}
</ReactMarkdown>
) : (
<span className='whitespace-pre-wrap'>{(content[0] as TextContentInterface).text}</span>
<span className='whitespace-pre-wrap'>
{(content[0] as TextContentInterface).text}
</span>
)}
</div>
<div className="flex gap-4">
<div className='flex gap-4'>
{(content.slice(1) as ImageContentInterface[]).map((image, index) => (
<div key={index} className="image-container">
<img src={image.image_url.url} alt={`uploaded-${index}`} className="h-20" />
<div key={index} className='image-container'>
<img
src={image.image_url.url}
alt={`uploaded-${index}`}
className='h-20'
/>
</div>
))}
</div>
Expand Down
131 changes: 87 additions & 44 deletions src/components/Chat/ChatContent/Message/View/EditView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const EditView = ({

const [_content, _setContent] = useState<ContentInterface[]>(content);
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
const [imageUrl, setImageUrl] = useState<string>('');
const textareaRef = React.createRef<HTMLTextAreaElement>();

const { t } = useTranslation();
Expand Down Expand Up @@ -104,6 +105,22 @@ const EditView = ({
_setContent(updatedContent);
};

const handleImageUrlChange = () => {
if (imageUrl.trim() === '') return;

const newImage: ImageContentInterface = {
type: 'image_url',
image_url: {
detail: 'auto',
url: imageUrl,
},
};

const updatedContent = [..._content, newImage];
_setContent(updatedContent);
setImageUrl('');
};

const handleImageDetailChange = (index: number, detail: string) => {
const updatedImages = [..._content];
updatedImages[index + 1].image_url.detail = detail;
Expand Down Expand Up @@ -229,6 +246,9 @@ const EditView = ({
setIsEdit={setIsEdit}
_setContent={_setContent}
_content={_content}
imageUrl={imageUrl}
setImageUrl={setImageUrl}
handleImageUrlChange={handleImageUrlChange}
/>
{isModalOpen && (
<PopupModal
Expand All @@ -254,6 +274,9 @@ const EditViewButtons = memo(
setIsEdit,
_setContent,
_content,
imageUrl,
setImageUrl,
handleImageUrlChange,
}: {
sticky?: boolean;
handleFileChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
Expand All @@ -265,6 +288,9 @@ const EditViewButtons = memo(
setIsEdit: React.Dispatch<React.SetStateAction<boolean>>;
_setContent: React.Dispatch<React.SetStateAction<ContentInterface[]>>;
_content: ContentInterface[];
imageUrl: string;
setImageUrl: React.Dispatch<React.SetStateAction<string>>;
handleImageUrlChange: () => void;
}) => {
const { t } = useTranslation();
const generating = useStore.getState().generating;
Expand All @@ -286,65 +312,82 @@ const EditViewButtons = memo(
return (
<div>
{modelTypes[model] == 'image' && (
<div className='flex justify-center'>
<div className='flex gap-5'>
{_content.slice(1).map((image, index) => (
<div
key={index}
className='image-container flex flex-col gap-2'
>
<img
src={image.image_url.url}
alt={`uploaded-${index}`}
className='h-10'
/>
<div className='flex flex-row gap-3'>
<select
onChange={(event) =>
handleImageDetailChange(index, event.target.value)
}
title='Select image resolution'
aria-label='Select image resolution'
defaultValue={image.image_url.detail}
style={{ color: 'black' }}
>
<option value='auto'>Auto</option>
<option value='high'>High</option>
<option value='low'>Low</option>
</select>
<button
className='close-button'
onClick={() => handleRemoveImage(index)}
aria-label='Remove Image'
>
&times;
</button>
<>
<div className='flex justify-center'>
<div className='flex gap-5'>
{_content.slice(1).map((image, index) => (
<div
key={index}
className='image-container flex flex-col gap-2'
>
<img
src={image.image_url.url}
alt={`uploaded-${index}`}
className='h-10'
/>
<div className='flex flex-row gap-3'>
<select
onChange={(event) =>
handleImageDetailChange(index, event.target.value)
}
title='Select image resolution'
aria-label='Select image resolution'
defaultValue={image.image_url.detail}
style={{ color: 'black' }}
>
<option value='auto'>Auto</option>
<option value='high'>High</option>
<option value='low'>Low</option>
</select>
<button
className='close-button'
onClick={() => handleRemoveImage(index)}
aria-label='Remove Image'
>
&times;
</button>
</div>
</div>
</div>
))}
))}

<button
className='btn relative btn-neutral h-10'
onClick={handleUploadButtonClick}
aria-label={'Upload Images'}
>
<div className='flex items-center justify-center gap-2'>
<FolderIcon />
</div>
</button>
</div>
</div>
<div className='flex justify-center mt-4'>
<input
type='text'
value={imageUrl}
onChange={(e) => setImageUrl(e.target.value)}
placeholder={t('enter_image_url_placeholder') as string}
className='input input-bordered w-full max-w-xs text-gray-800 dark:text-white p-3 border-none bg-gray-200 dark:bg-gray-600 rounded-md m-0 w-full mr-0 h-10 focus:outline-none'
/>
<button
className='btn relative btn-neutral h-10'
onClick={handleUploadButtonClick}
aria-label={'Upload Images'}
className='btn btn-primary ml-2'
onClick={handleImageUrlChange}
aria-label={t('add_image_url') as string}
>
<div className='flex items-center justify-center gap-2'>
<FolderIcon />
</div>
{t('add_image_url')}
</button>
</div>

{/* Hidden file input */}
<input
type='file'
ref={fileInputRef}
style={{ display: 'none' }}
onChange={handleFileChange}
accept='image/*'
multiple
/>
</div>
</>
)}

<div className='flex'>
<div className='flex-1 text-center mt-2 flex justify-center'>
{sticky && (
Expand Down

0 comments on commit f222084

Please sign in to comment.