From a6244848b990cffd021dfd0b59cea78a35b1a2a0 Mon Sep 17 00:00:00 2001 From: you Date: Mon, 23 Mar 2026 04:31:58 +0000 Subject: [PATCH] feat: add theme import from file - Import File button opens file picker for .json theme files - Merges imported theme into current state, applies live preview - Also syncs ROLE_COLORS/TYPE_COLORS globals on import - Moved Copy/Download buttons out of collapsed details - Raw JSON textarea now editable (was readonly) --- public/customize.js | 58 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/public/customize.js b/public/customize.js index 0f0bec0d..cb95e555 100644 --- a/public/customize.js +++ b/public/customize.js @@ -602,13 +602,15 @@ '
' + '' + '' + + '' + + '' + '
' + - '
Export as JSON' + - '' + '
' + '' + '' + '
' + + '
Raw JSON' + + '' + '
' + ''; } @@ -902,6 +904,58 @@ setTimeout(function () { loadServerBtn.textContent = '📥 Load from Server'; }, 3000); }); }); + + // Import from file + var importBtn = document.getElementById('custImportFile'); + var importInput = document.getElementById('custImportInput'); + if (importBtn && importInput) { + importBtn.addEventListener('click', function () { importInput.click(); }); + importInput.addEventListener('change', function () { + var file = importInput.files[0]; + if (!file) return; + var reader = new FileReader(); + reader.onload = function () { + try { + var data = JSON.parse(reader.result); + // Merge imported data into state + if (data.branding) Object.assign(state.branding, data.branding); + if (data.theme) Object.assign(state.theme, data.theme); + if (data.themeDark) Object.assign(state.themeDark, data.themeDark); + if (data.nodeColors) { + Object.assign(state.nodeColors, data.nodeColors); + if (window.ROLE_COLORS) Object.assign(window.ROLE_COLORS, data.nodeColors); + if (window.ROLE_STYLE) { + for (var role in data.nodeColors) { + if (window.ROLE_STYLE[role]) window.ROLE_STYLE[role].color = data.nodeColors[role]; + } + } + } + if (data.typeColors) { + Object.assign(state.typeColors, data.typeColors); + if (window.TYPE_COLORS) Object.assign(window.TYPE_COLORS, data.typeColors); + } + if (data.home) { + if (data.home.heroTitle) state.home.heroTitle = data.home.heroTitle; + if (data.home.heroSubtitle) state.home.heroSubtitle = data.home.heroSubtitle; + if (data.home.steps) state.home.steps = deepClone(data.home.steps); + if (data.home.checklist) state.home.checklist = deepClone(data.home.checklist); + if (data.home.footerLinks) state.home.footerLinks = deepClone(data.home.footerLinks); + } + applyThemePreview(); + autoSave(); + window.dispatchEvent(new CustomEvent('theme-changed')); + render(container); + importBtn.textContent = '✓ Imported!'; + setTimeout(function () { importBtn.textContent = '📂 Import File'; }, 2000); + } catch (e) { + importBtn.textContent = '✕ Invalid JSON'; + setTimeout(function () { importBtn.textContent = '📂 Import File'; }, 3000); + } + }; + reader.readAsText(file); + importInput.value = ''; + }); + } } function toggle() {