Preserve PNG transparency for static images and show a checkerboard preview.

Static uploads were forced to JPEG with a white fill, which turned white icons into solid squares; PNG sources now stay PNG and CMS previews get a visible background.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-09-27 14:25:01 +03:30
co-authored by Cursor
parent 6d85a217e1
commit 95390933a7
4 changed files with 34 additions and 2 deletions
@@ -65,6 +65,18 @@
cursor: pointer;
}
/* Checkerboard so white/transparent PNG icons stay visible in the CMS. */
.imageButtonTransparent {
background-color: #64748b;
background-image:
linear-gradient(45deg, #475569 25%, transparent 25%),
linear-gradient(-45deg, #475569 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, #475569 75%),
linear-gradient(-45deg, transparent 75%, #475569 75%);
background-size: 16px 16px;
background-position: 0 0, 0 8px, 8px -8px, -8px 0;
}
.image {
width: 100%;
height: 100%;
@@ -3,6 +3,7 @@ import { useLocale } from '@meshkee/dashboard-ui'
import { useT } from '../i18n/useT'
import type { WebsiteStaticImageItem } from '../types/websiteStaticImage'
import { cssAspectRatio, slotPreviewSize } from '../utils/aspectRatio'
import { isPngImageUrl } from '../utils/imageUpload'
import { Tooltip } from './Tooltip'
import controlStyles from './CategoryRow.module.css'
import styles from './WebsiteStaticImageGallery.module.css'
@@ -106,7 +107,7 @@ export function WebsiteStaticImageGallery({
<article className={styles.card}>
<button
type="button"
className={styles.imageButton}
className={`${styles.imageButton} ${isPngImageUrl(image.url) ? styles.imageButtonTransparent : ''}`}
onClick={() => onAddAt(index)}
aria-label={replaceLabel}
style={{ aspectRatio: cssAspect }}
@@ -20,6 +20,7 @@ import type { BusinessMessageKey } from '../i18n/messages'
import { ApiError } from '../lib/api'
import { getBusinessDomain } from '../lib/config'
import { resolveDataUrlToMediaId } from '../services/mediaService'
import { uploadFilenameForImageDataUrl } from '../utils/imageUpload'
import {
deleteWebsiteStaticImageSlot,
listWebsiteStaticImageSlots,
@@ -230,7 +231,7 @@ export function WebsiteStaticImagesPage() {
const occupying = occupyingImage(slot, index)
const imageMediaId = await resolveDataUrlToMediaId(
data.image,
'static-image.jpg',
uploadFilenameForImageDataUrl(data.image ?? '', 'static-image'),
occupying?.imageMediaId,
)
if (!imageMediaId) {
+18
View File
@@ -98,3 +98,21 @@ export async function ensureJpegUploadFile(
: `${filename.replace(/\.[^.]+$/, '') || 'image'}.jpg`,
)
}
/** Pick .png vs .jpg from a data URL so PNG transparency is not flattened to JPEG. */
export function uploadFilenameForImageDataUrl(
dataUrl: string,
baseName: string,
): string {
const safe = baseName.replace(/\.[^.]+$/, '') || 'image'
return /^data:image\/png/i.test(dataUrl) ? `${safe}.png` : `${safe}.jpg`
}
export function isPngImageUrl(url: string): boolean {
if (/^data:image\/png/i.test(url)) return true
try {
return new URL(url, 'https://local').pathname.toLowerCase().endsWith('.png')
} catch {
return /\.png(\?|#|$)/i.test(url)
}
}