Merge pull request #526 from Sunwood-ai-labs/codex/allow-png-background-upload

[codex] Allow PNG custom background uploads
This commit is contained in:
Sid
2026-05-22 20:37:25 -07:00
committed by GitHub
11 changed files with 51 additions and 12 deletions
@@ -52,6 +52,7 @@ import { getTestId } from "@/utils/getTestId";
import ColorPicker from "../ui/color-picker";
import { AnnotationSettingsPanel } from "./AnnotationSettingsPanel";
import { BlurSettingsPanel } from "./BlurSettingsPanel";
import { BACKGROUND_IMAGE_ACCEPT, isSupportedBackgroundImageType } from "./backgroundImageUpload";
import { CropControl } from "./CropControl";
import { parseCustomPlaybackSpeedInput } from "./customPlaybackSpeed";
import {
@@ -644,9 +645,7 @@ export function SettingsPanel({
const file = files[0];
// Validate file type - only allow JPG/JPEG
const validTypes = ["image/jpeg", "image/jpg"];
if (!validTypes.includes(file.type)) {
if (!isSupportedBackgroundImageType(file.type, file.name)) {
toast.error(t("imageUpload.invalidFileType"), {
description: t("imageUpload.jpgOnly"),
});
@@ -1586,7 +1585,7 @@ export function SettingsPanel({
type="file"
ref={fileInputRef}
onChange={handleImageUpload}
accept=".jpg,.jpeg,image/jpeg"
accept={BACKGROUND_IMAGE_ACCEPT}
className="hidden"
/>
<Button
@@ -0,0 +1,20 @@
import { describe, expect, it } from "vitest";
import { isSupportedBackgroundImageType } from "./backgroundImageUpload";
describe("background image upload validation", () => {
it("accepts PNG images for custom backgrounds", () => {
expect(isSupportedBackgroundImageType("image/png", "生成画像1.png")).toBe(true);
});
it("accepts PNG images by extension when the browser does not provide a MIME type", () => {
expect(isSupportedBackgroundImageType("", "生成画像1.png")).toBe(true);
});
it("keeps rejecting non-image uploads", () => {
expect(isSupportedBackgroundImageType("text/plain", "notes.txt")).toBe(false);
});
it("does not allow extension fallback for explicit unsupported MIME types", () => {
expect(isSupportedBackgroundImageType("text/plain", "notes.png")).toBe(false);
});
});
@@ -0,0 +1,20 @@
const SUPPORTED_BACKGROUND_IMAGE_TYPES = new Set(["image/jpeg", "image/jpg", "image/png"]);
const SUPPORTED_BACKGROUND_IMAGE_EXTENSIONS = new Set([".jpg", ".jpeg", ".png"]);
export const BACKGROUND_IMAGE_ACCEPT = ".jpg,.jpeg,.png,image/jpeg,image/png";
export function isSupportedBackgroundImageType(type: string, fileName: string): boolean {
const normalizedType = type.trim().toLowerCase();
if (SUPPORTED_BACKGROUND_IMAGE_TYPES.has(normalizedType)) {
return true;
}
if (normalizedType) {
return false;
}
const lowerName = fileName.trim().toLowerCase();
return [...SUPPORTED_BACKGROUND_IMAGE_EXTENSIONS].some((extension) =>
lowerName.endsWith(extension),
);
}
+1 -1
View File
@@ -112,7 +112,7 @@
},
"imageUpload": {
"invalidFileType": "Invalid file type",
"jpgOnly": "Please upload a JPG or JPEG image file.",
"jpgOnly": "Please upload a JPG, JPEG, or PNG image file.",
"uploadSuccess": "Custom image uploaded successfully!",
"failedToUpload": "Failed to upload image",
"errorReading": "There was an error reading the file."
+1 -1
View File
@@ -112,7 +112,7 @@
},
"imageUpload": {
"invalidFileType": "Tipo de archivo no válido",
"jpgOnly": "Por favor sube un archivo de imagen JPG o JPEG.",
"jpgOnly": "Por favor sube un archivo de imagen JPG, JPEG o PNG.",
"uploadSuccess": "¡Imagen personalizada subida exitosamente!",
"failedToUpload": "Error al subir la imagen",
"errorReading": "Hubo un error al leer el archivo."
+1 -1
View File
@@ -113,7 +113,7 @@
},
"imageUpload": {
"invalidFileType": "Type de fichier invalide",
"jpgOnly": "Veuillez téléverser un fichier image JPG ou JPEG.",
"jpgOnly": "Veuillez téléverser un fichier image JPG, JPEG ou PNG.",
"uploadSuccess": "Image personnalisée téléversée avec succès !",
"failedToUpload": "Échec du téléversement de l'image",
"errorReading": "Une erreur s'est produite lors de la lecture du fichier."
+1 -1
View File
@@ -112,7 +112,7 @@
},
"imageUpload": {
"invalidFileType": "無効なファイル形式",
"jpgOnly": "JPG または JPEG 画像ファイルを選択してください。",
"jpgOnly": "JPG、PNG、GIF、またはWebP画像ファイルを選択してください。",
"uploadSuccess": "カスタム画像を読み込みました。",
"failedToUpload": "画像の読み込みに失敗しました",
"errorReading": "ファイルの読み取り中にエラーが発生しました。"
+1 -1
View File
@@ -112,7 +112,7 @@
},
"imageUpload": {
"invalidFileType": "지원하지 않는 파일 형식입니다",
"jpgOnly": "JPG 또는 JPEG 이미지 파일을 업로드해 주세요.",
"jpgOnly": "JPG, JPEG 또는 PNG 이미지 파일을 업로드해 주세요.",
"uploadSuccess": "커스텀 이미지가 성공적으로 업로드되었습니다!",
"failedToUpload": "이미지 업로드에 실패했습니다",
"errorReading": "파일을 읽는 중 오류가 발생했습니다."
+1 -1
View File
@@ -112,7 +112,7 @@
},
"imageUpload": {
"invalidFileType": "Geçersiz dosya türü",
"jpgOnly": "Lütfen bir JPG veya JPEG görüntü dosyası yükleyin.",
"jpgOnly": "Lütfen JPG, JPEG veya PNG görüntü dosyası yükleyin.",
"uploadSuccess": "Özel görüntü başarıyla yüklendi!",
"failedToUpload": "Görüntü yüklenemedi",
"errorReading": "Dosya okunurken bir hata oluştu."
+1 -1
View File
@@ -112,7 +112,7 @@
},
"imageUpload": {
"invalidFileType": "无效的文件类型",
"jpgOnly": "请上传 JPG 或 JPEG 格式的图片文件。",
"jpgOnly": "请上传 JPG、JPEG 或 PNG 格式的图片文件。",
"uploadSuccess": "自定义图片上传成功!",
"failedToUpload": "上传图片失败",
"errorReading": "读取文件时出错。"
+1 -1
View File
@@ -113,7 +113,7 @@
},
"imageUpload": {
"invalidFileType": "無效的檔案類型",
"jpgOnly": "請上傳 JPG 或 JPEG 格式的圖片檔案。",
"jpgOnly": "請上傳 JPG、JPEG 或 PNG 格式的圖片檔案。",
"uploadSuccess": "自訂圖片上傳成功!",
"failedToUpload": "上傳圖片失敗",
"errorReading": "讀取檔案時出錯。"