4 steps OK
This commit is contained in:
		@@ -98,6 +98,14 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
			
		||||
  const deleteModalConfirm = document.getElementById("delete-modal-confirm");
 | 
			
		||||
  const deleteModalCancel = document.getElementById("delete-modal-cancel");
 | 
			
		||||
 | 
			
		||||
  // Modal elements for theme deletion
 | 
			
		||||
  const deleteThemeModal = document.getElementById("delete-theme-modal");
 | 
			
		||||
  const deleteThemeModalClose = document.getElementById("delete-theme-modal-close");
 | 
			
		||||
  const deleteThemeModalConfirm = document.getElementById("delete-theme-modal-confirm");
 | 
			
		||||
  const deleteThemeModalCancel = document.getElementById("delete-theme-modal-cancel");
 | 
			
		||||
  const deleteThemeModalText = document.getElementById("delete-theme-modal-text");
 | 
			
		||||
  let themeToDelete = null;
 | 
			
		||||
 | 
			
		||||
  // Show/hide thumbnail preview, remove button, and choose button
 | 
			
		||||
  function updateThumbnailPreview(src) {
 | 
			
		||||
    if (thumbnailPreview) {
 | 
			
		||||
@@ -201,6 +209,64 @@ document.addEventListener("DOMContentLoaded", () => {
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Remove theme button triggers modal
 | 
			
		||||
  const removeThemeBtn = document.getElementById("remove-theme-btn");
 | 
			
		||||
  if (removeThemeBtn && themeSelect) {
 | 
			
		||||
    removeThemeBtn.addEventListener("click", () => {
 | 
			
		||||
      const theme = themeSelect.value;
 | 
			
		||||
      if (!theme) return showToast("❌ No theme selected", "error");
 | 
			
		||||
      if (["modern", "classic"].includes(theme)) {
 | 
			
		||||
        showToast("❌ Cannot remove default theme", "error");
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      themeToDelete = theme;
 | 
			
		||||
      deleteThemeModalText.textContent = `Are you sure you want to remove theme "${theme}"?`;
 | 
			
		||||
      deleteThemeModal.style.display = "flex";
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Modal logic for theme deletion
 | 
			
		||||
  if (deleteThemeModal && deleteThemeModalClose && deleteThemeModalConfirm && deleteThemeModalCancel) {
 | 
			
		||||
    deleteThemeModalClose.onclick = deleteThemeModalCancel.onclick = () => {
 | 
			
		||||
      deleteThemeModal.style.display = "none";
 | 
			
		||||
      themeToDelete = null;
 | 
			
		||||
    };
 | 
			
		||||
    window.onclick = function(event) {
 | 
			
		||||
      if (event.target === deleteThemeModal) {
 | 
			
		||||
        deleteThemeModal.style.display = "none";
 | 
			
		||||
        themeToDelete = null;
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
    deleteThemeModalConfirm.onclick = async () => {
 | 
			
		||||
      if (!themeToDelete) return;
 | 
			
		||||
      const res = await fetch("/api/theme/remove", {
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        headers: { "Content-Type": "application/json" },
 | 
			
		||||
        body: JSON.stringify({ theme: themeToDelete })
 | 
			
		||||
      });
 | 
			
		||||
      const result = await res.json();
 | 
			
		||||
      if (result.status === "ok") {
 | 
			
		||||
        showToast("✅ Theme removed!", "success");
 | 
			
		||||
        // Refresh theme select
 | 
			
		||||
        fetch("/api/themes")
 | 
			
		||||
          .then(res => res.json())
 | 
			
		||||
          .then(themes => {
 | 
			
		||||
            themeSelect.innerHTML = "";
 | 
			
		||||
            themes.forEach(theme => {
 | 
			
		||||
              const option = document.createElement("option");
 | 
			
		||||
              option.value = theme;
 | 
			
		||||
              option.textContent = theme;
 | 
			
		||||
              themeSelect.appendChild(option);
 | 
			
		||||
            });
 | 
			
		||||
          });
 | 
			
		||||
      } else {
 | 
			
		||||
        showToast(result.error || "❌ Error removing theme", "error");
 | 
			
		||||
      }
 | 
			
		||||
      deleteThemeModal.style.display = "none";
 | 
			
		||||
      themeToDelete = null;
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Fetch theme list and populate select
 | 
			
		||||
  if (themeSelect) {
 | 
			
		||||
    fetch("/api/themes")
 | 
			
		||||
 
 | 
			
		||||
@@ -170,28 +170,28 @@ document.addEventListener("DOMContentLoaded", async () => {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (fontUploadInput) {
 | 
			
		||||
  fontUploadInput.addEventListener("change", async (e) => {
 | 
			
		||||
    const file = e.target.files[0];
 | 
			
		||||
    if (!file) return;
 | 
			
		||||
    const ext = file.name.split('.').pop().toLowerCase();
 | 
			
		||||
    if (!["woff", "woff2"].includes(ext)) {
 | 
			
		||||
      showToast("Only .woff and .woff2 fonts are allowed.", "error");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const formData = new FormData();
 | 
			
		||||
    formData.append("file", file);
 | 
			
		||||
    formData.append("theme", themeInfo.theme_name);
 | 
			
		||||
    const res = await fetch("/api/font/upload", { method: "POST", body: formData });
 | 
			
		||||
    const result = await res.json();
 | 
			
		||||
    if (result.status === "ok") {
 | 
			
		||||
      showToast("✅ Font uploaded!", "success");
 | 
			
		||||
      localFonts = await fetchLocalFonts(themeInfo.theme_name);
 | 
			
		||||
      refreshLocalFonts();
 | 
			
		||||
    } else {
 | 
			
		||||
      showToast("Error uploading font.", "error");
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
    fontUploadInput.addEventListener("change", async (e) => {
 | 
			
		||||
      const file = e.target.files[0];
 | 
			
		||||
      if (!file) return;
 | 
			
		||||
      const ext = file.name.split('.').pop().toLowerCase();
 | 
			
		||||
      if (!["woff", "woff2"].includes(ext)) {
 | 
			
		||||
        showToast("Only .woff and .woff2 fonts are allowed.", "error");
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      const formData = new FormData();
 | 
			
		||||
      formData.append("file", file);
 | 
			
		||||
      formData.append("theme", themeInfo.theme_name);
 | 
			
		||||
      const res = await fetch("/api/font/upload", { method: "POST", body: formData });
 | 
			
		||||
      const result = await res.json();
 | 
			
		||||
      if (result.status === "ok") {
 | 
			
		||||
        showToast("✅ Font uploaded!", "success");
 | 
			
		||||
        localFonts = await fetchLocalFonts(themeInfo.theme_name);
 | 
			
		||||
        refreshLocalFonts();
 | 
			
		||||
      } else {
 | 
			
		||||
        showToast("Error uploading font.", "error");
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Remove font button triggers modal
 | 
			
		||||
  if (localFontsList) {
 | 
			
		||||
@@ -333,20 +333,75 @@ document.addEventListener("DOMContentLoaded", async () => {
 | 
			
		||||
  // Add Google Font
 | 
			
		||||
  const addGoogleFontBtn = document.getElementById("add-google-font");
 | 
			
		||||
  if (addGoogleFontBtn) {
 | 
			
		||||
    addGoogleFontBtn.addEventListener("click", () => {
 | 
			
		||||
    addGoogleFontBtn.addEventListener("click", async () => {
 | 
			
		||||
      googleFonts.push({ family: "", weights: [] });
 | 
			
		||||
      // Save immediately to backend
 | 
			
		||||
      await fetch("/api/theme-google-fonts", {
 | 
			
		||||
        method: "POST",
 | 
			
		||||
        headers: { "Content-Type": "application/json" },
 | 
			
		||||
        body: JSON.stringify({ theme_name: themeInfo.theme_name, google_fonts: googleFonts })
 | 
			
		||||
      });
 | 
			
		||||
      // Fetch updated theme info and refresh dropdowns
 | 
			
		||||
      const updatedThemeInfo = await fetchThemeInfo();
 | 
			
		||||
      const updatedGoogleFonts = updatedThemeInfo.theme_yaml.google_fonts || [];
 | 
			
		||||
      googleFonts.length = 0;
 | 
			
		||||
      googleFonts.push(...updatedGoogleFonts);
 | 
			
		||||
      renderGoogleFonts(googleFonts);
 | 
			
		||||
      refreshFontDropdowns();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Remove Google Font
 | 
			
		||||
  const googleFontsFields = document.getElementById("google-fonts-fields");
 | 
			
		||||
  if (googleFontsFields) {
 | 
			
		||||
    googleFontsFields.addEventListener("click", (e) => {
 | 
			
		||||
      if (e.target.classList.contains("remove-google-font")) {
 | 
			
		||||
        const idx = parseInt(e.target.dataset.idx, 10);
 | 
			
		||||
        googleFonts.splice(idx, 1);
 | 
			
		||||
    // Save on blur for family/weights fields
 | 
			
		||||
    googleFontsFields.addEventListener("blur", async (e) => {
 | 
			
		||||
      if (
 | 
			
		||||
        e.target.name &&
 | 
			
		||||
        (e.target.name.endsWith("[family]") || e.target.name.endsWith("[weights]"))
 | 
			
		||||
      ) {
 | 
			
		||||
        // Update googleFonts array from the form fields
 | 
			
		||||
        const fontFields = googleFontsFields.querySelectorAll(".input-field");
 | 
			
		||||
        googleFonts.length = 0;
 | 
			
		||||
        fontFields.forEach(field => {
 | 
			
		||||
          const family = field.querySelector('input[name^="google_fonts"][name$="[family]"]').value.trim();
 | 
			
		||||
          const weights = field.querySelector('input[name^="google_fonts"][name$="[weights]"]').value
 | 
			
		||||
            .split(",").map(w => w.trim()).filter(Boolean);
 | 
			
		||||
          googleFonts.push({ family, weights });
 | 
			
		||||
        });
 | 
			
		||||
        // Save immediately to backend
 | 
			
		||||
        await fetch("/api/theme-google-fonts", {
 | 
			
		||||
          method: "POST",
 | 
			
		||||
          headers: { "Content-Type": "application/json" },
 | 
			
		||||
          body: JSON.stringify({ theme_name: themeInfo.theme_name, google_fonts: googleFonts })
 | 
			
		||||
        });
 | 
			
		||||
        // Fetch updated theme info and refresh dropdowns
 | 
			
		||||
        const updatedThemeInfo = await fetchThemeInfo();
 | 
			
		||||
        const updatedGoogleFonts = updatedThemeInfo.theme_yaml.google_fonts || [];
 | 
			
		||||
        googleFonts.length = 0;
 | 
			
		||||
        googleFonts.push(...updatedGoogleFonts);
 | 
			
		||||
        renderGoogleFonts(googleFonts);
 | 
			
		||||
        refreshFontDropdowns();
 | 
			
		||||
      }
 | 
			
		||||
    }, true); // Use capture phase to catch blur from children
 | 
			
		||||
 | 
			
		||||
    // Delegate remove button click for Google Fonts
 | 
			
		||||
    googleFontsFields.addEventListener("click", async (e) => {
 | 
			
		||||
      if (e.target.classList.contains("remove-google-font")) {
 | 
			
		||||
        const idx = Number(e.target.dataset.idx);
 | 
			
		||||
        googleFonts.splice(idx, 1);
 | 
			
		||||
        // Save immediately to backend
 | 
			
		||||
        await fetch("/api/theme-google-fonts", {
 | 
			
		||||
          method: "POST",
 | 
			
		||||
          headers: { "Content-Type": "application/json" },
 | 
			
		||||
          body: JSON.stringify({ theme_name: themeInfo.theme_name, google_fonts: googleFonts })
 | 
			
		||||
        });
 | 
			
		||||
        // Fetch updated theme info and refresh dropdowns
 | 
			
		||||
        const updatedThemeInfo = await fetchThemeInfo();
 | 
			
		||||
        const updatedGoogleFonts = updatedThemeInfo.theme_yaml.google_fonts || [];
 | 
			
		||||
        googleFonts.length = 0;
 | 
			
		||||
        googleFonts.push(...updatedGoogleFonts);
 | 
			
		||||
        renderGoogleFonts(googleFonts);
 | 
			
		||||
        refreshFontDropdowns();
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user