diff --git a/client/src/components/HeroElement.jsx b/client/src/components/HeroElement.jsx
index 7d12d8d..fd3efb0 100644
--- a/client/src/components/HeroElement.jsx
+++ b/client/src/components/HeroElement.jsx
@@ -107,12 +107,15 @@ function content(element) {
case 'buttons':
return Math.round(v * 100) / 100
const genId = () => (crypto.randomUUID ? crypto.randomUUID() : `el-${Date.now()}-${Math.random()}`)
const hexOf = (v) => (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(v || '') ? v : '#ffffff')
+// Soft page-weight warning shown before uploading a large hero image. This is
+// only a nudge (hero images render on the public landing page); the server hard-
+// limits uploads at 8 MB. Returns true when the caller should abort the upload.
+const WARN_UPLOAD_MB = 5
+function tooLargeToUpload(size) {
+ return (
+ size > WARN_UPLOAD_MB * 1024 * 1024 &&
+ !confirm(`This image is over ${WARN_UPLOAD_MB} MB and may slow the page. Upload anyway?`)
+ )
+}
+
function newElement(type, z) {
const base = { id: genId(), type, x: 50, y: 50, z, anchor: 'center' }
if (type === 'text_block') {
@@ -206,7 +217,7 @@ export default function HeroEditor() {
const file = e.target.files?.[0]
e.target.value = ''
if (!file) return
- if (file.size > 1024 * 1024 && !confirm('This image is over 1 MB and may slow the page. Upload anyway?')) return
+ if (tooLargeToUpload(file.size)) return
setUploading(true)
try {
const { url } = await api.admin.upload(file)
@@ -499,9 +510,45 @@ const swatch = { width: '100%', height: 38, padding: 2, border: '1px solid var(-
function MoonPanel({ element, onProps }) {
const p = element.props || {}
+ const [up, setUp] = useState(false)
+ // Reuses the shared admin upload endpoint (same as the image/background panels);
+ // a successful upload just points props.src at the returned URL.
+ async function onFile(e) {
+ const f = e.target.files?.[0]
+ e.target.value = ''
+ if (!f) return
+ if (tooLargeToUpload(f.size)) return
+ setUp(true)
+ try {
+ const { url } = await api.admin.upload(f)
+ onProps({ src: url })
+ } catch {
+ /* ignore */
+ } finally {
+ setUp(false)
+ }
+ }
return (
Uses the moon from the hero artwork.
+Using the default moon from the hero artwork.
+ )} + + {p.src && ( + onProps({ src: '' })}>Use default + )} +