Support image/file attachments (#80)

This commit is contained in:
Will Chen
2025-05-05 12:38:09 -07:00
committed by GitHub
parent 0108ff1a82
commit ac8ef73bee
10 changed files with 620 additions and 34 deletions

View File

@@ -1,4 +1,4 @@
import { SendIcon, StopCircleIcon, X } from "lucide-react";
import { SendIcon, StopCircleIcon, X, Paperclip, Loader2 } from "lucide-react";
import type React from "react";
import { useEffect, useRef, useState } from "react";
import { ModelPicker } from "@/components/ModelPicker";
@@ -6,14 +6,39 @@ import { useSettings } from "@/hooks/useSettings";
import { homeChatInputValueAtom } from "@/atoms/chatAtoms"; // Use a different atom for home input
import { useAtom } from "jotai";
import { useStreamChat } from "@/hooks/useStreamChat";
import { useAttachments } from "@/hooks/useAttachments";
import { AttachmentsList } from "./AttachmentsList";
import { DragDropOverlay } from "./DragDropOverlay";
import { usePostHog } from "posthog-js/react";
import { HomeSubmitOptions } from "@/pages/home";
export function HomeChatInput({ onSubmit }: { onSubmit: () => void }) {
export function HomeChatInput({
onSubmit,
}: {
onSubmit: (options?: HomeSubmitOptions) => void;
}) {
const posthog = usePostHog();
const [inputValue, setInputValue] = useAtom(homeChatInputValueAtom);
const textareaRef = useRef<HTMLTextAreaElement>(null);
const { settings, updateSettings, isAnyProviderSetup } = useSettings();
const { streamMessage, isStreaming, setIsStreaming } = useStreamChat({
hasChatId: false,
}); // eslint-disable-line @typescript-eslint/no-unused-vars
// Use the attachments hook
const {
attachments,
fileInputRef,
isDraggingOver,
handleAttachmentClick,
handleFileChange,
removeAttachment,
handleDragOver,
handleDragLeave,
handleDrop,
clearAttachments,
} = useAttachments();
const adjustHeight = () => {
const textarea = textareaRef.current;
if (textarea) {
@@ -30,10 +55,24 @@ export function HomeChatInput({ onSubmit }: { onSubmit: () => void }) {
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
onSubmit();
handleCustomSubmit();
}
};
// Custom submit function that wraps the provided onSubmit
const handleCustomSubmit = () => {
if ((!inputValue.trim() && attachments.length === 0) || isStreaming) {
return;
}
// Call the parent's onSubmit handler with attachments
onSubmit({ attachments });
// Clear attachments as part of submission process
clearAttachments();
posthog.capture("chat:home_submit");
};
if (!settings) {
return null; // Or loading state
}
@@ -41,7 +80,23 @@ export function HomeChatInput({ onSubmit }: { onSubmit: () => void }) {
return (
<>
<div className="p-4">
<div className="flex flex-col space-y-2 border border-border rounded-lg bg-(--background-lighter) shadow-sm">
<div
className={`relative flex flex-col space-y-2 border border-border rounded-lg bg-(--background-lighter) shadow-sm ${
isDraggingOver ? "ring-2 ring-blue-500 border-blue-500" : ""
}`}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
>
{/* Attachments list */}
<AttachmentsList
attachments={attachments}
onRemove={removeAttachment}
/>
{/* Drag and drop overlay */}
<DragDropOverlay isDraggingOver={isDraggingOver} />
<div className="flex items-start space-x-2 ">
<textarea
ref={textareaRef}
@@ -53,6 +108,25 @@ export function HomeChatInput({ onSubmit }: { onSubmit: () => void }) {
style={{ resize: "none" }}
disabled={isStreaming} // Should ideally reflect if *any* stream is happening
/>
{/* File attachment button */}
<button
onClick={handleAttachmentClick}
className="px-2 py-2 mt-1 mr-1 hover:bg-(--background-darkest) text-(--sidebar-accent-fg) rounded-lg disabled:opacity-50"
disabled={isStreaming}
title="Attach files"
>
<Paperclip size={20} />
</button>
<input
type="file"
ref={fileInputRef}
onChange={handleFileChange}
className="hidden"
multiple
accept=".jpg,.jpeg,.png,.gif,.webp,.txt,.md,.js,.ts,.html,.css,.json,.csv"
/>
{isStreaming ? (
<button
className="px-2 py-2 mt-1 mr-2 text-(--sidebar-accent-fg) rounded-lg opacity-50 cursor-not-allowed" // Indicate disabled state
@@ -62,8 +136,11 @@ export function HomeChatInput({ onSubmit }: { onSubmit: () => void }) {
</button>
) : (
<button
onClick={onSubmit}
disabled={!inputValue.trim() || !isAnyProviderSetup()}
onClick={handleCustomSubmit}
disabled={
(!inputValue.trim() && attachments.length === 0) ||
!isAnyProviderSetup()
}
className="px-2 py-2 mt-1 mr-2 hover:bg-(--background-darkest) text-(--sidebar-accent-fg) rounded-lg disabled:opacity-50"
title="Start new chat"
>