diff --git a/templates/upload.html b/templates/upload.html
index 5906975..54dc5ab 100644
--- a/templates/upload.html
+++ b/templates/upload.html
@@ -62,7 +62,23 @@
width: 100%;
height: 100%;
object-fit: cover;
+ pointer-events: none;
}
+ .change-hint {
+ display: none;
+ position: absolute;
+ inset: auto 0 0 0;
+ padding: 6px 10px;
+ background: rgba(29, 32, 33, 0.75);
+ color: var(--fg2);
+ font-family: monospace;
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ text-align: center;
+ pointer-events: none;
+ }
+ .drop-zone.has-image .change-hint { display: block; }
.field { margin-bottom: 18px; }
.field label {
display: block;
@@ -129,6 +145,7 @@
jpg or png
+
Click to change
@@ -156,7 +173,54 @@
dateInput.value = new Date().toISOString().slice(0, 10);
- fileInput.addEventListener('change', () => {
+ // Read DateTimeOriginal from a JPEG's EXIF APP1 segment. Returns "YYYY-MM-DD"
+ // or null if the file isn't a JPEG, has no EXIF, or lacks the tag.
+ async function readExifDate(file) {
+ if (!/jpe?g$/i.test(file.type) && !/\.jpe?g$/i.test(file.name)) return null;
+ const buf = await file.slice(0, 65536).arrayBuffer();
+ const view = new DataView(buf);
+ if (view.getUint16(0) !== 0xFFD8) return null;
+
+ let offset = 2;
+ while (offset + 4 < view.byteLength) {
+ const marker = view.getUint16(offset);
+ if (marker < 0xFF00) return null;
+ const size = view.getUint16(offset + 2);
+ if (marker === 0xFFE1 && view.getUint32(offset + 4) === 0x45786966) {
+ const tiff = offset + 10;
+ const little = view.getUint16(tiff) === 0x4949;
+ const get16 = (o) => view.getUint16(o, little);
+ const get32 = (o) => view.getUint32(o, little);
+ const ifd0 = tiff + get32(tiff + 4);
+
+ let exifIfd = 0;
+ const n = get16(ifd0);
+ for (let i = 0; i < n; i++) {
+ const e = ifd0 + 2 + i * 12;
+ if (get16(e) === 0x8769) { exifIfd = tiff + get32(e + 8); break; }
+ }
+ if (!exifIfd) return null;
+
+ const en = get16(exifIfd);
+ for (let i = 0; i < en; i++) {
+ const e = exifIfd + 2 + i * 12;
+ if (get16(e) === 0x9003) {
+ const count = get32(e + 4);
+ const dataOff = count > 4 ? tiff + get32(e + 8) : e + 8;
+ let s = '';
+ for (let k = 0; k < count - 1; k++) s += String.fromCharCode(view.getUint8(dataOff + k));
+ const m = s.match(/^(\d{4}):(\d{2}):(\d{2})/);
+ return m ? `${m[1]}-${m[2]}-${m[3]}` : null;
+ }
+ }
+ return null;
+ }
+ offset += 2 + size;
+ }
+ return null;
+ }
+
+ fileInput.addEventListener('change', async () => {
const file = fileInput.files[0];
if (!file) return;
const url = URL.createObjectURL(file);
@@ -168,6 +232,13 @@
submitBtn.disabled = false;
feedback.className = '';
feedback.style.display = 'none';
+
+ let d = null;
+ try { d = await readExifDate(file); } catch (_) {}
+ if (!d && file.lastModified) {
+ d = new Date(file.lastModified).toISOString().slice(0, 10);
+ }
+ if (d) dateInput.value = d;
});
submitBtn.addEventListener('click', async () => {