Skip to content

Commit d59c058

Browse files
committed
check filesize/info before reading full contents
1 parent 8b3edf6 commit d59c058

File tree

3 files changed

+65
-31
lines changed

3 files changed

+65
-31
lines changed

frontend/app/aipanel/ai-utils.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,11 @@ export interface FileSizeError {
181181
fileType: "text" | "pdf" | "image";
182182
}
183183

184-
export const validateFileSize = (file: File): FileSizeError | null => {
185-
const TEXT_FILE_LIMIT = 200 * 1024; // 200KB
186-
const PDF_LIMIT = 5 * 1024 * 1024; // 5MB
187-
const IMAGE_LIMIT = 10 * 1024 * 1024; // 10MB
184+
const TEXT_FILE_LIMIT = 200 * 1024; // 200KB
185+
const PDF_LIMIT = 5 * 1024 * 1024; // 5MB
186+
const IMAGE_LIMIT = 10 * 1024 * 1024; // 10MB
188187

188+
export const validateFileSize = (file: File): FileSizeError | null => {
189189
if (file.type.startsWith("image/")) {
190190
if (file.size > IMAGE_LIMIT) {
191191
return {
@@ -218,6 +218,33 @@ export const validateFileSize = (file: File): FileSizeError | null => {
218218
return null;
219219
};
220220

221+
export const validateFileSizeFromInfo = (fileName: string, fileSize: number, mimeType: string): FileSizeError | null => {
222+
let maxSize: number;
223+
let fileType: "text" | "pdf" | "image";
224+
225+
if (mimeType.startsWith("image/")) {
226+
maxSize = IMAGE_LIMIT;
227+
fileType = "image";
228+
} else if (mimeType === "application/pdf") {
229+
maxSize = PDF_LIMIT;
230+
fileType = "pdf";
231+
} else {
232+
maxSize = TEXT_FILE_LIMIT;
233+
fileType = "text";
234+
}
235+
236+
if (fileSize > maxSize) {
237+
return {
238+
fileName,
239+
fileSize,
240+
maxSize,
241+
fileType,
242+
};
243+
}
244+
245+
return null;
246+
};
247+
221248
export const formatFileSizeError = (error: FileSizeError): string => {
222249
const typeLabel = error.fileType === "image" ? "Image" : error.fileType === "pdf" ? "PDF" : "Text file";
223250
return `${typeLabel} "${error.fileName}" is too large (${formatFileSize(error.fileSize)}). Maximum size is ${formatFileSize(error.maxSize)}.`;

frontend/app/aipanel/aipanel.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -323,12 +323,12 @@ const AIPanelComponentInner = memo(({ className, onClose }: AIPanelProps) => {
323323

324324
const handleDragOver = (e: React.DragEvent) => {
325325
const hasFiles = hasFilesDragged(e.dataTransfer);
326-
326+
327327
// Only handle native file drags here, let react-dnd handle FILE_ITEM drags
328328
if (!hasFiles) {
329329
return;
330330
}
331-
331+
332332
e.preventDefault();
333333
e.stopPropagation();
334334

@@ -339,12 +339,12 @@ const AIPanelComponentInner = memo(({ className, onClose }: AIPanelProps) => {
339339

340340
const handleDragEnter = (e: React.DragEvent) => {
341341
const hasFiles = hasFilesDragged(e.dataTransfer);
342-
342+
343343
// Only handle native file drags here, let react-dnd handle FILE_ITEM drags
344344
if (!hasFiles) {
345345
return;
346346
}
347-
347+
348348
e.preventDefault();
349349
e.stopPropagation();
350350

@@ -353,12 +353,12 @@ const AIPanelComponentInner = memo(({ className, onClose }: AIPanelProps) => {
353353

354354
const handleDragLeave = (e: React.DragEvent) => {
355355
const hasFiles = hasFilesDragged(e.dataTransfer);
356-
356+
357357
// Only handle native file drags here, let react-dnd handle FILE_ITEM drags
358358
if (!hasFiles) {
359359
return;
360360
}
361-
361+
362362
e.preventDefault();
363363
e.stopPropagation();
364364

@@ -424,7 +424,6 @@ const AIPanelComponentInner = memo(({ className, onClose }: AIPanelProps) => {
424424

425425
// Update drag over state for FILE_ITEM drags
426426
useEffect(() => {
427-
console.log("FILE_ITEM drag state:", { isOver, canDrop });
428427
if (isOver && canDrop) {
429428
setIsReactDndDragOver(true);
430429
} else {

frontend/app/aipanel/waveai-model.tsx

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,19 @@ import { TabRpcClient } from "@/app/store/wshrpcutil";
1616
import { WorkspaceLayoutModel } from "@/app/workspace/workspace-layout-model";
1717
import { BuilderFocusManager } from "@/builder/store/builder-focusmanager";
1818
import { getWebServerEndpoint } from "@/util/endpoints";
19+
import { base64ToArrayBuffer } from "@/util/util";
1920
import { ChatStatus } from "ai";
2021
import * as jotai from "jotai";
2122
import type React from "react";
22-
import { base64ToArrayBuffer } from "@/util/util";
23-
import { createDataUrl, createImagePreview, isAcceptableFile, normalizeMimeType, resizeImage, validateFileSize, formatFileSizeError } from "./ai-utils";
23+
import {
24+
createDataUrl,
25+
createImagePreview,
26+
formatFileSizeError,
27+
isAcceptableFile,
28+
normalizeMimeType,
29+
resizeImage,
30+
validateFileSizeFromInfo,
31+
} from "./ai-utils";
2432
import type { AIPanelInputRef } from "./aipanelinput";
2533

2634
export interface DroppedFile {
@@ -159,39 +167,39 @@ export class WaveAIModel {
159167
}
160168

161169
try {
162-
const fileData = await RpcApi.FileReadCommand(
163-
TabRpcClient,
164-
{
165-
info: {
166-
path: draggedFile.uri,
167-
},
168-
},
169-
null
170-
);
170+
const fileInfo = await RpcApi.FileInfoCommand(TabRpcClient, { info: { path: draggedFile.uri } }, null);
171+
if (fileInfo.notfound) {
172+
this.setError(`File not found: ${draggedFile.relName}`);
173+
return;
174+
}
175+
if (fileInfo.isdir) {
176+
this.setError("Cannot add directories to Wave AI. Please select a file.");
177+
return;
178+
}
179+
180+
const mimeType = fileInfo.mimetype || "application/octet-stream";
181+
const fileSize = fileInfo.size || 0;
182+
const sizeError = validateFileSizeFromInfo(draggedFile.relName, fileSize, mimeType);
183+
if (sizeError) {
184+
this.setError(formatFileSizeError(sizeError));
185+
return;
186+
}
171187

188+
const fileData = await RpcApi.FileReadCommand(TabRpcClient, { info: { path: draggedFile.uri } }, null);
172189
if (!fileData.data64) {
173190
this.setError(`Failed to read file: ${draggedFile.relName}`);
174191
return;
175192
}
176193

177194
const buffer = base64ToArrayBuffer(fileData.data64);
178-
179-
const mimeType = fileData.info?.mimetype || "application/octet-stream";
180195
const file = new File([buffer], draggedFile.relName, { type: mimeType });
181-
182196
if (!isAcceptableFile(file)) {
183197
this.setError(
184198
`File type not supported: ${draggedFile.relName}. Supported: images, PDFs, and text/code files.`
185199
);
186200
return;
187201
}
188202

189-
const sizeError = validateFileSize(file);
190-
if (sizeError) {
191-
this.setError(formatFileSizeError(sizeError));
192-
return;
193-
}
194-
195203
await this.addFile(file);
196204
} catch (error) {
197205
console.error("Error handling FILE_ITEM drop:", error);

0 commit comments

Comments
 (0)