chore: remove pdf attachement

This commit is contained in:
Faisal Amir 2025-08-14 15:01:30 +07:00
parent ec9925ed5a
commit f70449fd98
5 changed files with 11 additions and 99 deletions

View File

@ -1,7 +1,7 @@
'use client' 'use client'
import TextareaAutosize from 'react-textarea-autosize' import TextareaAutosize from 'react-textarea-autosize'
import { cn, toGigabytes } from '@/lib/utils' import { cn } from '@/lib/utils'
import { usePrompt } from '@/hooks/usePrompt' import { usePrompt } from '@/hooks/usePrompt'
import { useThreads } from '@/hooks/useThreads' import { useThreads } from '@/hooks/useThreads'
import { useCallback, useEffect, useRef, useState } from 'react' import { useCallback, useEffect, useRef, useState } from 'react'
@ -191,8 +191,6 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
return 'image/jpeg' return 'image/jpeg'
case 'png': case 'png':
return 'image/png' return 'image/png'
case 'pdf':
return 'application/pdf'
default: default:
return '' return ''
} }
@ -226,21 +224,16 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
const detectedType = file.type || getFileTypeFromExtension(file.name) const detectedType = file.type || getFileTypeFromExtension(file.name)
const actualType = getFileTypeFromExtension(file.name) || detectedType const actualType = getFileTypeFromExtension(file.name) || detectedType
// Check file type - exclude PDF for local models (llamacpp) // Check file type - images only
const allowedTypes = [ const allowedTypes = [
'image/jpg', 'image/jpg',
'image/jpeg', 'image/jpeg',
'image/png', 'image/png',
...(model?.provider !== 'llamacpp' ? ['application/pdf'] : []),
] ]
if (!allowedTypes.includes(actualType)) { if (!allowedTypes.includes(actualType)) {
const supportedFormats =
model?.provider === 'llamacpp'
? 'JPEG, JPG, and PNG'
: 'JPEG, JPG, PNG, and PDF'
setMessage( setMessage(
`File attachments not supported currently. Only ${supportedFormats} files are allowed.` `File attachments not supported currently. Only JPEG, JPG, and PNG files are allowed.`
) )
// Reset file input to allow re-uploading // Reset file input to allow re-uploading
if (fileInputRef.current) { if (fileInputRef.current) {
@ -389,25 +382,6 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
alt={`${file.name} - ${index}`} alt={`${file.name} - ${index}`}
/> />
)} )}
{file.type === 'application/pdf' && (
<div className="bg-main-view-fg/4 h-full rounded-lg p-2 max-w-[400px] pr-4">
<div className="flex gap-2 items-center justify-center h-full">
<div className="size-10 rounded-md bg-main-view shrink-0 flex items-center justify-center">
<span className="uppercase font-bold">
{file.name.split('.').pop()}
</span>
</div>
<div className="truncate">
<h6 className="truncate mb-0.5 text-main-view-fg/80">
{file.name}
</h6>
<p className="text-xs text-main-view-fg/70">
{toGigabytes(file.size)}
</p>
</div>
</div>
</div>
)}
<div <div
className="absolute -top-1 -right-2.5 bg-destructive size-5 flex rounded-full items-center justify-center cursor-pointer" className="absolute -top-1 -right-2.5 bg-destructive size-5 flex rounded-full items-center justify-center cursor-pointer"
onClick={() => handleRemoveFile(index)} onClick={() => handleRemoveFile(index)}

View File

@ -224,11 +224,7 @@ export const ThreadContent = memo(
toSendMessage.content?.find((c) => c.type === 'text')?.text?.value || toSendMessage.content?.find((c) => c.type === 'text')?.text?.value ||
'' ''
const attachments = toSendMessage.content const attachments = toSendMessage.content
?.filter( ?.filter((c) => (c.type === 'image_url' && c.image_url?.url) || false)
(c) =>
(c.type === 'image_url' && c.image_url?.url) ||
((c as any).type === 'file' && (c as any).file?.data)
)
.map((c) => { .map((c) => {
if (c.type === 'image_url' && c.image_url?.url) { if (c.type === 'image_url' && c.image_url?.url) {
const url = c.image_url.url const url = c.image_url.url
@ -242,15 +238,6 @@ export const ThreadContent = memo(
base64: base64, base64: base64,
dataUrl: url, dataUrl: url,
} }
} else if ((c as any).type === 'file' && (c as any).file?.data) {
const fileContent = (c as any).file
return {
name: fileContent.filename || 'file',
type: fileContent.media_type,
size: 0, // We don't have the original size
base64: fileContent.data,
dataUrl: `data:${fileContent.media_type};base64,${fileContent.data}`,
}
} }
return null return null
}) })
@ -307,17 +294,14 @@ export const ThreadContent = memo(
<div className="w-full"> <div className="w-full">
{/* Render attachments above the message bubble */} {/* Render attachments above the message bubble */}
{item.content?.some( {item.content?.some(
(c) => (c) => (c.type === 'image_url' && c.image_url?.url) || false
(c.type === 'image_url' && c.image_url?.url) ||
((c as any).type === 'file' && (c as any).file?.data)
) && ( ) && (
<div className="flex justify-end w-full mb-2"> <div className="flex justify-end w-full mb-2">
<div className="flex flex-wrap gap-2 max-w-[80%] justify-end"> <div className="flex flex-wrap gap-2 max-w-[80%] justify-end">
{item.content {item.content
?.filter( ?.filter(
(c) => (c) =>
(c.type === 'image_url' && c.image_url?.url) || (c.type === 'image_url' && c.image_url?.url) || false
((c as any).type === 'file' && (c as any).file?.data)
) )
.map((contentPart, index) => { .map((contentPart, index) => {
// Handle images // Handle images
@ -335,27 +319,6 @@ export const ThreadContent = memo(
</div> </div>
) )
} }
// Handle PDF files
else if (
(contentPart as any).type === 'file' &&
(contentPart as any).file?.media_type ===
'application/pdf'
) {
const fileContent = (contentPart as any).file
return (
<div key={index} className="relative">
<div className="w-40 h-40 bg-main-view-fg/5 border border-main-view-fg/10 rounded-md flex flex-col items-center justify-center p-4">
<div className="text-2xl mb-2">📄</div>
<div className="text-xs text-center text-main-view-fg/70 truncate w-full">
{fileContent.filename || 'PDF Document'}
</div>
<div className="text-xs text-main-view-fg/50 mt-1">
PDF
</div>
</div>
</div>
)
}
return null return null
})} })}
</div> </div>

View File

@ -231,7 +231,7 @@ describe('ChatInput', () => {
const sendButton = document.querySelector('[data-test-id="send-message-button"]') const sendButton = document.querySelector('[data-test-id="send-message-button"]')
await user.click(sendButton) await user.click(sendButton)
expect(mockSendMessage).toHaveBeenCalledWith('Hello world') expect(mockSendMessage).toHaveBeenCalledWith('Hello world', true, undefined)
}) })
it('sends message when Enter key is pressed', async () => { it('sends message when Enter key is pressed', async () => {
@ -248,7 +248,7 @@ describe('ChatInput', () => {
const textarea = screen.getByRole('textbox') const textarea = screen.getByRole('textbox')
await user.type(textarea, '{Enter}') await user.type(textarea, '{Enter}')
expect(mockSendMessage).toHaveBeenCalledWith('Hello world') expect(mockSendMessage).toHaveBeenCalledWith('Hello world', true, undefined)
}) })
it('does not send message when Shift+Enter is pressed', async () => { it('does not send message when Shift+Enter is pressed', async () => {

View File

@ -81,17 +81,6 @@ export const newUserThreadContent = (
detail: 'auto', detail: 'auto',
}, },
} as any) } as any)
} else if (attachment.type === 'application/pdf') {
contentParts.push({
type: 'file' as any,
file: {
filename: attachment.name,
file_data: `data:${attachment.type};base64,${attachment.base64}`,
// Keep original data for local display purposes
data: attachment.base64,
media_type: attachment.type,
},
} as any)
} }
}) })
} }

View File

@ -32,6 +32,7 @@ export class CompletionMessagesBuilder {
// For user messages, handle multimodal content // For user messages, handle multimodal content
if (msg.content.length > 1) { if (msg.content.length > 1) {
// Multiple content parts (text + images + files) // Multiple content parts (text + images + files)
const content = msg.content.map((contentPart) => { const content = msg.content.map((contentPart) => {
if (contentPart.type === 'text') { if (contentPart.type === 'text') {
return { return {
@ -46,16 +47,9 @@ export class CompletionMessagesBuilder {
detail: contentPart.image_url?.detail || 'auto', detail: contentPart.image_url?.detail || 'auto',
}, },
} }
} else if ((contentPart as any).type === 'file') { } else {
return {
type: 'file',
file: {
filename: (contentPart as any).file?.filename || 'document.pdf',
file_data: (contentPart as any).file?.file_data || (contentPart as any).file?.data ? `data:application/pdf;base64,${(contentPart as any).file.data}` : '',
},
}
}
return contentPart return contentPart
}
}) })
return { return {
role: msg.role, role: msg.role,
@ -112,14 +106,6 @@ export class CompletionMessagesBuilder {
detail: 'auto', detail: 'auto',
}, },
}) })
} else if (attachment.type === 'application/pdf') {
messageContent.push({
type: 'file',
file: {
filename: attachment.name,
file_data: `data:${attachment.type};base64,${attachment.base64}`,
},
})
} }
}) })