From ceb0de897b35c663697e06ed2f9620cff5b81a64 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Thu, 14 May 2026 08:39:34 -0400 Subject: [PATCH] Add metadata, dark mode, Nostr export/import, and split JSON project format --- src/index.html | 71 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/src/index.html b/src/index.html index 33b4551..ded5f5c 100644 --- a/src/index.html +++ b/src/index.html @@ -6,10 +6,14 @@ DVD / Blu-ray Cover Template Maker @@ -829,6 +836,14 @@
+
+ Project metadata + + +
+

IMDb import uses OMDb-compatible endpoint + API key.

+
+
View / export @@ -843,10 +858,11 @@ +
- + @@ -1148,6 +1164,8 @@ }; const state = { + meta: { title: "", imdbId: "", omdbApiKey: "" }, + darkMode: false, templateMode: 'cover', preset: 'dvd', panelW: presets.dvd.panelW, @@ -1243,6 +1261,9 @@ $('zoomOut').textContent = `${state.zoom}%`; $('artworkMode').value = state.artworkMode; $('showGuides').checked = state.showGuides; + $('darkMode').checked = !!state.darkMode; + $('titleField').value = state.meta?.title || ""; + $('imdbField').value = state.meta?.imdbId || ""; $('showBleed').checked = state.showBleed; $('showCrops').checked = state.showCrops; $('printGuides').checked = state.printGuides; @@ -1257,6 +1278,7 @@ document.body.classList.toggle('hide-crops', !state.showCrops); document.body.classList.toggle('print-guides', state.printGuides); document.body.classList.toggle('print-crops', state.printCrops); + document.body.classList.toggle('dark', !!state.darkMode); } function renderDimensions() { @@ -1918,8 +1940,19 @@ render(); } + function exportPayload() { + return { + version: 2, + meta: state.meta, + ui: { zoom: state.zoom, darkMode: state.darkMode, showGuides: state.showGuides, showBleed: state.showBleed, showCrops: state.showCrops, printGuides: state.printGuides, printCrops: state.printCrops }, + coverTemplate: { preset: state.preset, panelW: state.panelW, trimH: state.trimH, spineW: state.spineW, bleed: state.bleed, safe: state.safe, artworkMode: state.artworkMode, slots: state.slots, combined: state.combined }, + discTemplate: { config: discTemplate, discs: state.discs }, + layers: state.layers + }; + } + function downloadProject() { - const blob = new Blob([JSON.stringify(state, null, 2)], { type: 'application/json' }); + const blob = new Blob([JSON.stringify(exportPayload(), null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); const date = new Date().toISOString().slice(0, 10); @@ -1933,7 +1966,8 @@ function loadProjectFromText(text) { const parsed = JSON.parse(text); - const next = { ...state, ...parsed }; + const source = parsed.version ? { ...parsed.ui, ...parsed.coverTemplate, discs: parsed.discTemplate?.discs, meta: parsed.meta, layers: parsed.layers } : parsed; + const next = { ...state, ...source }; next.slots = { ...state.slots, ...(parsed.slots || {}) }; next.combined = { ...state.combined, ...(parsed.combined || {}) }; next.discs = { ...state.discs, ...(parsed.discs || {}) }; @@ -1971,13 +2005,40 @@ render(); }); - for (const id of ['showGuides', 'showBleed', 'showCrops', 'printGuides', 'printCrops']) { + for (const id of ['showGuides', 'showBleed', 'showCrops', 'printGuides', 'printCrops','darkMode']) { $(id).addEventListener('change', (event) => { - state[id] = event.target.checked; + if (id === "darkMode") state.darkMode = event.target.checked; else state[id] = event.target.checked; render(); }); } + $("titleField").addEventListener("input", (e)=>{state.meta.title=e.target.value; render();}); + $("imdbField").addEventListener("input", (e)=>{state.meta.imdbId=e.target.value; render();}); + + $("imdbImportBtn").addEventListener("click", async ()=>{ + const imdb = state.meta.imdbId.trim(); + const key = prompt("OMDb API key:", state.meta.omdbApiKey || ""); + if (!imdb || !key) return; + state.meta.omdbApiKey = key; + const res = await fetch(`https://www.omdbapi.com/?i=${encodeURIComponent(imdb)}&apikey=${encodeURIComponent(key)}`); + const data = await res.json(); + if (data && data.Title) { state.meta.title = data.Title; $("titleField").value = data.Title; render(); } else alert("IMDb import failed"); + }); + + $("nostrExportBtn").addEventListener("click", async ()=>{ + if (!window.nostr) return alert("No Nostr extension found"); + const evt={kind:30078,created_at:Math.floor(Date.now()/1000),tags:[["d", state.meta.imdbId||state.meta.title||"template"]],content:JSON.stringify(state)}; + evt.pubkey = await window.nostr.getPublicKey(); + const signed = await window.nostr.signEvent(evt); + await navigator.clipboard.writeText(JSON.stringify(signed)); + alert("Signed Nostr event copied to clipboard"); + }); + + $("nostrImportBtn").addEventListener("click", ()=>{ + const raw = prompt("Paste Nostr event JSON"); if (!raw) return; + try { const evt = JSON.parse(raw); if (evt.content) loadProjectFromText(evt.content); } catch { alert("Bad event JSON"); } + }); + $('printBtn').addEventListener('click', () => { render(); window.print();