diff --git a/src/integrations/misc/__tests__/imageDataUrl.spec.ts b/src/integrations/misc/__tests__/imageDataUrl.spec.ts index 4993303034..01b100afa8 100644 --- a/src/integrations/misc/__tests__/imageDataUrl.spec.ts +++ b/src/integrations/misc/__tests__/imageDataUrl.spec.ts @@ -18,40 +18,13 @@ describe("normalizeImageRefsToDataUrls", () => { expect(result).toEqual([dataUrl]) }) - it("should convert webview URIs to data URLs", async () => { - const webviewUri = "file:///path/to/test.png" - const mockBuffer = Buffer.from("test image data") - - vi.mocked(fs.readFile).mockResolvedValue(mockBuffer) - - const result = await normalizeImageRefsToDataUrls([webviewUri]) - - expect(result).toHaveLength(1) - expect(result[0]).toMatch(/^data:image\/png;base64,/) - expect(fs.readFile).toHaveBeenCalledWith("/path/to/test.png") - }) - - it("should handle mixed arrays of data URLs and webview URIs", async () => { - const dataUrl = "data:image/jpeg;base64,test123" - const webviewUri = "file:///path/to/test.png" - const mockBuffer = Buffer.from("test image data") - - vi.mocked(fs.readFile).mockResolvedValue(mockBuffer) - - const result = await normalizeImageRefsToDataUrls([dataUrl, webviewUri]) - - expect(result).toHaveLength(2) - expect(result[0]).toBe(dataUrl) // Data URL unchanged - expect(result[1]).toMatch(/^data:image\/png;base64,/) // Webview URI converted - }) - it("should handle errors gracefully by skipping problematic images", async () => { const validDataUrl = "data:image/png;base64,valid" - const invalidWebviewUri = "file:///nonexistent/test.png" + const invalidCdnUri = "vscode-file://vscode-app/nonexistent/test.png" vi.mocked(fs.readFile).mockRejectedValue(new Error("File not found")) - const result = await normalizeImageRefsToDataUrls([validDataUrl, invalidWebviewUri]) + const result = await normalizeImageRefsToDataUrls([validDataUrl, invalidCdnUri]) expect(result).toEqual([validDataUrl]) // Only valid ones returned }) diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index 63e1fd4a0a..4e8b1570b3 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -11,6 +11,7 @@ import { ExtensionMessage } from "@roo/ExtensionMessage" import { vscode } from "@src/utils/vscode" import { useExtensionState } from "@src/context/ExtensionStateContext" import { useAppTranslation } from "@src/i18n/TranslationContext" +import { MAX_IMAGE_BYTES, estimateBytesFromBase64DataUrl } from "@src/utils/image" import { ContextMenuOptionType, getContextMenuOptions, @@ -732,18 +733,11 @@ export const ChatTextArea = forwardRef( if (dataUrls.length > 0) { // Process each image: enforce 10MB limit and send to backend to save as temp file for (const dataUrl of dataUrls) { - // Approximate bytes from base64 (ignore header and padding) - const commaIdx = dataUrl.indexOf(",") - let tooLarge = false - if (commaIdx !== -1) { - const base64 = dataUrl.slice(commaIdx + 1).replace(/=+$/, "") - const approxBytes = Math.floor((base64.length * 3) / 4) - if (approxBytes > 10 * 1024 * 1024) { - console.warn("Pasted image exceeds 10MB; skipping") - tooLarge = true - } + const approxBytes = estimateBytesFromBase64DataUrl(dataUrl) + if (approxBytes > MAX_IMAGE_BYTES) { + console.warn("Pasted image exceeds 10MB; skipping") + continue } - if (tooLarge) continue const requestId = Math.random().toString(36).substring(2, 9) @@ -910,18 +904,11 @@ export const ChatTextArea = forwardRef( if (dataUrls.length > 0) { // Process each dropped image: enforce 10MB limit and send to backend to save as temp file for (const dataUrl of dataUrls) { - // Approximate bytes from base64 (ignore header and padding) - const commaIdx = dataUrl.indexOf(",") - let tooLarge = false - if (commaIdx !== -1) { - const base64 = dataUrl.slice(commaIdx + 1).replace(/=+$/, "") - const approxBytes = Math.floor((base64.length * 3) / 4) - if (approxBytes > 10 * 1024 * 1024) { - console.warn("Dropped image exceeds 10MB; skipping") - tooLarge = true - } + const approxBytes = estimateBytesFromBase64DataUrl(dataUrl) + if (approxBytes > MAX_IMAGE_BYTES) { + console.warn("Dropped image exceeds 10MB; skipping") + continue } - if (tooLarge) continue const requestId = Math.random().toString(36).substring(2, 9)