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:
co-authored by
Cursor
parent
6d85a217e1
commit
95390933a7
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user