4 steps OK

This commit is contained in:
Djeex
2025-08-20 20:19:24 +02:00
parent f6e6a11fc1
commit cb91b92555
6 changed files with 206 additions and 32 deletions

View File

@ -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")

View File

@ -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();
}
});
}