Skip to content

Commit

Permalink
Added image upload
Browse files Browse the repository at this point in the history
Added image upload
  • Loading branch information
vgcman16 committed Oct 24, 2024
1 parent 8e7220e commit a2fe484
Show file tree
Hide file tree
Showing 4 changed files with 25,156 additions and 29 deletions.
11 changes: 11 additions & 0 deletions app/components/chat/BaseChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { MODEL_LIST, DEFAULT_PROVIDER } from '~/utils/constants';
import { Messages } from './Messages.client';
import { SendButton } from './SendButton.client';
import { useState } from 'react';
import { ImageUpload } from './ImageUpload';

import styles from './BaseChat.module.scss';

Expand Down Expand Up @@ -83,6 +84,8 @@ interface BaseChatProps {
sendMessage?: (event: React.UIEvent, messageInput?: string) => void;
handleInputChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
enhancePrompt?: () => void;
imageFile?: File | null;
onImageUpload?: (file: File) => void;
}

export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
Expand All @@ -104,6 +107,8 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
handleInputChange,
enhancePrompt,
handleStop,
imageFile,
onImageUpload,
},
ref,
) => {
Expand Down Expand Up @@ -229,6 +234,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
</>
)}
</IconButton>
<ImageUpload onImageUpload={onImageUpload} />
</div>
{input.length > 3 ? (
<div className="text-xs text-bolt-elements-textTertiary">
Expand All @@ -237,6 +243,11 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
) : null}
</div>
</div>
{imageFile && (
<div className="px-4 pb-2 text-sm text-bolt-elements-textSecondary">
Image attached: {imageFile.name}
</div>
)}
<div className="bg-bolt-elements-background-depth-1 pb-6">{/* Ghost Element */}</div>
</div>
</div>
Expand Down
59 changes: 30 additions & 29 deletions app/components/chat/Chat.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { DEFAULT_MODEL } from '~/utils/constants';
import { cubicEasingFn } from '~/utils/easings';
import { createScopedLogger, renderLogger } from '~/utils/logger';
import { BaseChat } from './BaseChat';
import { ImageUpload } from './ImageUpload';

const toastAnimation = cssTransition({
enter: 'animated fadeInRight',
Expand All @@ -40,9 +41,6 @@ export function Chat() {
);
}}
icon={({ type }) => {
/**
* @todo Handle more types if we need them. This may require extra color palettes.
*/
switch (type) {
case 'success': {
return <div className="i-ph:check-bold text-bolt-elements-icon-success text-2xl" />;
Expand All @@ -51,7 +49,6 @@ export function Chat() {
return <div className="i-ph:warning-circle-bold text-bolt-elements-icon-error text-2xl" />;
}
}

return undefined;
}}
position="bottom-right"
Expand All @@ -74,6 +71,7 @@ export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProp

const [chatStarted, setChatStarted] = useState(initialMessages.length > 0);
const [model, setModel] = useState(DEFAULT_MODEL);
const [imageFile, setImageFile] = useState<File | null>(null);

const { showChat } = useStore(chatStore);

Expand Down Expand Up @@ -153,17 +151,10 @@ export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProp
const sendMessage = async (_event: React.UIEvent, messageInput?: string) => {
const _input = messageInput || input;

if (_input.length === 0 || isLoading) {
if ((_input.length === 0 && !imageFile) || isLoading) {
return;
}

/**
* @note (delm) Usually saving files shouldn't take long but it may take longer if there
* many unsaved files. In that case we need to block user input and show an indicator
* of some kind so the user is aware that something is happening. But I consider the
* happy case to be no unsaved files and I would expect users to save their changes
* before they send another message.
*/
await workbenchStore.saveAllFiles();

const fileModifications = workbenchStore.getFileModifcations();
Expand All @@ -172,36 +163,44 @@ export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProp

runAnimation();

let messageContent = `[Model: ${model}]\n\n`;

if (fileModifications !== undefined) {
const diff = fileModificationsToHTML(fileModifications);
messageContent += `${diff}\n\n`;
}

/**
* If we have file modifications we append a new user message manually since we have to prefix
* the user input with the file modifications and we don't want the new user input to appear
* in the prompt. Using `append` is almost the same as `handleSubmit` except that we have to
* manually reset the input and we'd have to manually pass in file attachments. However, those
* aren't relevant here.
*/
append({ role: 'user', content: `[Model: ${model}]\n\n${diff}\n\n${_input}` });

/**
* After sending a new message we reset all modifications since the model
* should now be aware of all the changes.
*/
workbenchStore.resetAllFileModifications();
messageContent += _input;

if (imageFile) {
const reader = new FileReader();
reader.onload = async (e) => {
const base64Image = e.target?.result as string;
messageContent += `\n\n[Attached Image: ${imageFile.name}]\n${base64Image}`;

append({ role: 'user', content: messageContent });
setImageFile(null);
};
reader.readAsDataURL(imageFile);
} else {
append({ role: 'user', content: `[Model: ${model}]\n\n${_input}` });
append({ role: 'user', content: messageContent });
}

setInput('');
if (fileModifications !== undefined) {
workbenchStore.resetAllFileModifications();
}

setInput('');
resetEnhancer();

textareaRef.current?.blur();
};

const [messageRef, scrollRef] = useSnapScroll();

const handleImageUpload = (file: File) => {
setImageFile(file);
};

return (
<BaseChat
ref={animationScope}
Expand Down Expand Up @@ -235,6 +234,8 @@ export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProp
scrollTextArea();
});
}}
imageFile={imageFile}
onImageUpload={handleImageUpload}
/>
);
});
39 changes: 39 additions & 0 deletions app/components/chat/ImageUpload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useRef } from 'react';

interface ImageUploadProps {
onImageUpload: (file: File) => void;
}

export const ImageUpload: React.FC<ImageUploadProps> = ({ onImageUpload }) => {
const fileInputRef = useRef<HTMLInputElement>(null);

const handleClick = () => {
fileInputRef.current?.click();
};

const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
onImageUpload(file);
}
};

return (
<div>
<button onClick={handleClick} type="button" className="p-2 rounded-full hover:bg-gray-200">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
<circle cx="8.5" cy="8.5" r="1.5"/>
<polyline points="21 15 16 10 5 21"/>
</svg>
</button>
<input
type="file"
ref={fileInputRef}
onChange={handleFileChange}
accept="image/*"
style={{ display: 'none' }}
/>
</div>
);
};
Loading

0 comments on commit a2fe484

Please sign in to comment.