feat: submit file changes to the llm (#11)
This commit is contained in:
@@ -18,7 +18,7 @@ interface BaseChatProps {
|
||||
promptEnhanced?: boolean;
|
||||
input?: string;
|
||||
handleStop?: () => void;
|
||||
sendMessage?: () => void;
|
||||
sendMessage?: (event: React.UIEvent) => void;
|
||||
handleInputChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
|
||||
enhancePrompt?: () => void;
|
||||
}
|
||||
@@ -103,7 +103,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
sendMessage?.();
|
||||
sendMessage?.(event);
|
||||
}
|
||||
}}
|
||||
value={input}
|
||||
@@ -122,13 +122,13 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
|
||||
<SendButton
|
||||
show={input.length > 0 || isStreaming}
|
||||
isStreaming={isStreaming}
|
||||
onClick={() => {
|
||||
onClick={(event) => {
|
||||
if (isStreaming) {
|
||||
handleStop?.();
|
||||
return;
|
||||
}
|
||||
|
||||
sendMessage?.();
|
||||
sendMessage?.(event);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -2,10 +2,11 @@ import type { Message } from 'ai';
|
||||
import { useChat } from 'ai/react';
|
||||
import { useAnimate } from 'framer-motion';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { toast, ToastContainer, cssTransition } from 'react-toastify';
|
||||
import { cssTransition, toast, ToastContainer } from 'react-toastify';
|
||||
import { useMessageParser, usePromptEnhancer, useSnapScroll } from '~/lib/hooks';
|
||||
import { chatStore } from '~/lib/stores/chat';
|
||||
import { workbenchStore } from '~/lib/stores/workbench';
|
||||
import { fileModificationsToHTML } from '~/utils/diff';
|
||||
import { cubicEasingFn } from '~/utils/easings';
|
||||
import { createScopedLogger } from '~/utils/logger';
|
||||
import { BaseChat } from './BaseChat';
|
||||
@@ -41,7 +42,7 @@ export function ChatImpl({ initialMessages, storeMessageHistory }: ChatProps) {
|
||||
|
||||
const [animationScope, animate] = useAnimate();
|
||||
|
||||
const { messages, isLoading, input, handleInputChange, setInput, handleSubmit, stop } = useChat({
|
||||
const { messages, isLoading, input, handleInputChange, setInput, handleSubmit, stop, append } = useChat({
|
||||
api: '/api/chat',
|
||||
onError: (error) => {
|
||||
logger.error(error);
|
||||
@@ -100,15 +101,49 @@ export function ChatImpl({ initialMessages, storeMessageHistory }: ChatProps) {
|
||||
setChatStarted(true);
|
||||
};
|
||||
|
||||
const sendMessage = () => {
|
||||
if (input.length === 0) {
|
||||
const sendMessage = async (event: React.UIEvent) => {
|
||||
if (input.length === 0 || 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();
|
||||
|
||||
chatStore.setKey('aborted', false);
|
||||
|
||||
runAnimation();
|
||||
handleSubmit();
|
||||
|
||||
if (fileModifications !== undefined) {
|
||||
const diff = fileModificationsToHTML(fileModifications);
|
||||
|
||||
/**
|
||||
* 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: `${diff}\n\n${input}` });
|
||||
|
||||
setInput('');
|
||||
|
||||
/**
|
||||
* After sending a new message we reset all modifications since the model
|
||||
* should now be aware of all the changes.
|
||||
*/
|
||||
workbenchStore.resetAllFileModifications();
|
||||
} else {
|
||||
handleSubmit(event);
|
||||
}
|
||||
|
||||
resetEnhancer();
|
||||
|
||||
textareaRef.current?.blur();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { AnimatePresence, cubicBezier, motion } from 'framer-motion';
|
||||
interface SendButtonProps {
|
||||
show: boolean;
|
||||
isStreaming?: boolean;
|
||||
onClick?: VoidFunction;
|
||||
onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
}
|
||||
|
||||
const customEasingFn = cubicBezier(0.4, 0, 0.2, 1);
|
||||
@@ -20,7 +20,7 @@ export function SendButton({ show, isStreaming, onClick }: SendButtonProps) {
|
||||
exit={{ opacity: 0, y: 10 }}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
onClick?.();
|
||||
onClick?.(event);
|
||||
}}
|
||||
>
|
||||
<div className="text-lg">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { modificationsRegex } from '~/utils/diff';
|
||||
import { Markdown } from './Markdown';
|
||||
|
||||
interface UserMessageProps {
|
||||
@@ -7,7 +8,11 @@ interface UserMessageProps {
|
||||
export function UserMessage({ content }: UserMessageProps) {
|
||||
return (
|
||||
<div className="overflow-hidden">
|
||||
<Markdown>{content}</Markdown>
|
||||
<Markdown>{sanitizeUserMessage(content)}</Markdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function sanitizeUserMessage(content: string) {
|
||||
return content.replace(modificationsRegex, '').trim();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user