Accept a Blob when checking whether a chat attachment is an image.
Unit Tests / test (pull_request) Successful in 14s

Beta deploy typechecks the composer file as Blob, so the File-only helper failed the production build after the vision merge. Closes #130.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
westfarnandCursor committed 2026-10-20 10:05:12 -05:00
1 parent 89228719b5
commit 107acc68a2
3 files changed
+8 -7

No files matched your search

@@ -335,15 +335,15 @@ const AlwaysScrollToBottom = ({ trigger }: { trigger: string | number }): JSX.El
return <div ref={elementRef} />; return <div ref={elementRef} />;
}; };
const AttachmentPreview = ({ file }: { file: File | null }): JSX.Element | null => { const AttachmentPreview = ({ file }: { file: Blob | null }): JSX.Element | null => {
const theme = useContext(ThemeContext); const theme = useContext(ThemeContext);
const [url, setUrl] = useState<string | null>(null); const [url, setUrl] = useState<string | null>(null);
useEffect(() => { useEffect(() => {
if (!isImageFile(file)) { if (!file || !isImageFile(file)) {
setUrl(null); setUrl(null);
return; return;
} }
const next = URL.createObjectURL(file as File); const next = URL.createObjectURL(file);
setUrl(next); setUrl(next);
return () => URL.revokeObjectURL(next); return () => URL.revokeObjectURL(next);
}, [file]); }, [file]);
@@ -370,7 +370,7 @@ const AttachmentPreview = ({ file }: { file: File | null }): JSX.Element | null
color: theme?.darkMode ? "rgba(255,255,255,0.6)" : "rgba(0,0,0,0.6)", color: theme?.darkMode ? "rgba(255,255,255,0.6)" : "rgba(0,0,0,0.6)",
textAlign: "center", textAlign: "center",
}}> }}>
Attached: {file.name} Attached: {file instanceof File ? file.name : "attachment"}
</div> </div>
); );
}; };
@@ -473,7 +473,7 @@ const AsyncDashboardInner = (): JSX.Element => {
} }
try { try {
clearActivity(); clearActivity();
const attachedImage = isImageFile(file) ? URL.createObjectURL(file as File) : null; const attachedImage = file && isImageFile(file) ? URL.createObjectURL(file) : null;
const tempConversations: ConversationPrompt[] = [ const tempConversations: ConversationPrompt[] = [
...conversationDetails, ...conversationDetails,
new ConversationPrompt({ new ConversationPrompt({
@@ -753,7 +753,7 @@ const AsyncDashboardInner = (): JSX.Element => {
</span> </span>
</Tooltip> </Tooltip>
</StyledInputContainer> </StyledInputContainer>
<AttachmentPreview file={formik.values.file as File | null} /> <AttachmentPreview file={formik.values.file} />
</Form> </Form>
)} )}
</Formik> </Formik>
@@ -16,6 +16,7 @@ describe('visionAttachment', () => {
it('detects image files', () => { it('detects image files', () => {
expect(isImageFile(new File(['x'], 'a.png', { type: 'image/png' }))).toBe(true); expect(isImageFile(new File(['x'], 'a.png', { type: 'image/png' }))).toBe(true);
expect(isImageFile(new File(['x'], 'a.pdf', { type: 'application/pdf' }))).toBe(false); expect(isImageFile(new File(['x'], 'a.pdf', { type: 'application/pdf' }))).toBe(false);
expect(isImageFile(new Blob(['x'], { type: 'image/png' }))).toBe(true);
expect(isImageFile(null)).toBe(false); expect(isImageFile(null)).toBe(false);
}); });
}); });
+1 -1
View File
@@ -1,7 +1,7 @@
export const CHAT_FILE_ACCEPT = export const CHAT_FILE_ACCEPT =
'.csv,.xlsx,.txt,.pdf,.PDF,image/png,image/jpeg,image/webp,image/gif'; '.csv,.xlsx,.txt,.pdf,.PDF,image/png,image/jpeg,image/webp,image/gif';
export function isImageFile(file: File | null | undefined): boolean { export function isImageFile(file: Blob | null | undefined): boolean {
return Boolean(file && file.type.startsWith('image/')); return Boolean(file && file.type.startsWith('image/'));
} }