Spaces:
Running
Running
let isDarkMode = false; | |
let fontData = []; | |
// Load available fonts when page loads | |
async function loadAvailableFonts() { | |
try { | |
const response = await fetch("/api/fonts"); | |
fontData = await response.json(); | |
// Populate both quote and author font family selects | |
const quoteFontFamily = document.getElementById("quoteFontFamily"); | |
const authorFontFamily = document.getElementById("authorFontFamily"); | |
const fontOptions = fontData | |
.map((font) => `<option value="${font.family}">${font.family}</option>`) | |
.join(""); | |
quoteFontFamily.innerHTML = fontOptions; | |
authorFontFamily.innerHTML = fontOptions; | |
// Initialize variations for both | |
if (fontData.length > 0) { | |
updateFontVariations("quote", fontData[0].family); | |
updateFontVariations("author", fontData[0].family); | |
generateQuote(); | |
} | |
} catch (error) { | |
console.error("Error loading fonts:", error); | |
showStatus("Error loading fonts", "error"); | |
} | |
} | |
// Update font variations based on selected font family | |
function updateFontVariations(type, family) { | |
const font = fontData.find((f) => f.family === family); | |
if (!font) return; | |
const styleSelect = document.getElementById(`${type}FontStyle`); | |
// Get unique styles | |
const styles = new Set(font.variations.map((v) => v.style)); | |
// Update style options | |
styleSelect.innerHTML = Array.from(styles) | |
.map( | |
(style) => `<option value="${style}">${formatFontStyle(style)}</option>` | |
) | |
.join(""); | |
} | |
// Format font weight for display | |
function formatFontWeight(weight) { | |
switch (weight) { | |
case "normal": | |
return "Regular"; | |
case "bold": | |
return "Bold"; | |
case "light": | |
return "Light"; | |
case "medium": | |
return "Medium"; | |
default: | |
return weight.charAt(0).toUpperCase() + weight.slice(1); | |
} | |
} | |
// Format font style for display | |
function formatFontStyle(style) { | |
switch (style) { | |
case "normal": | |
return "Normal"; | |
case "italic": | |
return "Italic"; | |
default: | |
return style.charAt(0).toUpperCase() + style.slice(1); | |
} | |
} | |
// Setup color pickers with hex input sync | |
function setupColorPickers() { | |
const colorInputs = document.querySelectorAll('input[type="color"]'); | |
colorInputs.forEach((input) => { | |
const textInput = document.getElementById(input.id + "Text"); | |
// Update text input when color changes | |
input.addEventListener("input", () => { | |
textInput.value = input.value.toUpperCase(); | |
}); | |
// Update color input when valid hex is entered | |
textInput.addEventListener("input", () => { | |
let hex = textInput.value.trim(); | |
if (!hex.startsWith("#")) { | |
hex = "#" + hex; | |
} | |
if (/^#[0-9A-F]{6}$/i.test(hex)) { | |
input.value = hex; | |
} | |
}); | |
// Format hex on blur | |
textInput.addEventListener("blur", () => { | |
let hex = textInput.value.trim(); | |
if (!hex.startsWith("#")) { | |
hex = "#" + hex; | |
} | |
if (/^#[0-9A-F]{6}$/i.test(hex)) { | |
textInput.value = hex.toUpperCase(); | |
input.value = hex; | |
} else { | |
textInput.value = input.value.toUpperCase(); | |
} | |
}); | |
}); | |
} | |
async function generateQuote() { | |
const generateBtn = document.querySelector(".btn-primary"); | |
const btnText = generateBtn.querySelector(".btn-text"); | |
const btnLoader = generateBtn.querySelector(".btn-loader"); | |
try { | |
// Show loading state | |
generateBtn.disabled = true; | |
btnText.style.opacity = "0"; | |
btnLoader.style.display = "block"; | |
document.getElementById("loading").style.display = "flex"; | |
document.getElementById("quoteImage").style.opacity = "0.5"; | |
const data = { | |
text: document.getElementById("quoteText").value, | |
author: document.getElementById("author").value, | |
bgColor: document.getElementById("bgColor").value, | |
barColor: document.getElementById("barColor").value, | |
textColor: document.getElementById("textColor").value, | |
authorColor: document.getElementById("authorColor").value, | |
quoteFontFamily: document.getElementById("quoteFontFamily").value, | |
quoteFontWeight: "bold", // Force bold for quote | |
quoteFontStyle: document.getElementById("quoteFontStyle").value, | |
authorFontFamily: document.getElementById("authorFontFamily").value, | |
authorFontWeight: "normal", // Force normal for author | |
authorFontStyle: document.getElementById("authorFontStyle").value, | |
barWidth: parseInt(document.getElementById("barWidth").value), | |
}; | |
const response = await fetch("/api/generate-quote", { | |
method: "POST", | |
headers: { | |
"Content-Type": "application/json", | |
}, | |
body: JSON.stringify(data), | |
}); | |
const result = await response.json(); | |
if (result.success) { | |
const img = document.getElementById("quoteImage"); | |
img.src = result.imageUrl; | |
img.style.opacity = "1"; | |
showStatus("Quote generated successfully!", "success"); | |
} else { | |
throw new Error(result.error); | |
} | |
} catch (error) { | |
showStatus(error.message || "Error generating quote", "error"); | |
console.error(error); | |
} finally { | |
// Reset loading state | |
generateBtn.disabled = false; | |
btnText.style.opacity = "1"; | |
btnLoader.style.display = "none"; | |
document.getElementById("loading").style.display = "none"; | |
} | |
} | |
function downloadQuote() { | |
const img = document.getElementById("quoteImage"); | |
if (!img.src || img.src === window.location.href) { | |
showStatus("Generate a quote first!", "error"); | |
return; | |
} | |
const link = document.createElement("a"); | |
const timestamp = new Date().toISOString().split("T")[0]; | |
link.download = `quote-${timestamp}.png`; | |
link.href = img.src; | |
link.click(); | |
} | |
async function viewRequests() { | |
try { | |
const response = await fetch("/api/requests-history"); | |
const requests = await response.json(); | |
const requestsList = document.getElementById("requestsList"); | |
requestsList.innerHTML = requests | |
.map( | |
(req) => ` | |
<div class="request-item"> | |
<div class="request-time">${new Date( | |
req.timestamp | |
).toLocaleString()}</div> | |
<pre class="request-data">${JSON.stringify( | |
req.request, | |
null, | |
2 | |
)}</pre> | |
</div> | |
` | |
) | |
.join(""); | |
document.getElementById("requestsModal").style.display = "block"; | |
} catch (error) { | |
showStatus("Error fetching requests", "error"); | |
console.error(error); | |
} | |
} | |
function closeModal() { | |
document.getElementById("requestsModal").style.display = "none"; | |
} | |
function toggleTheme() { | |
isDarkMode = !isDarkMode; | |
document.documentElement.setAttribute( | |
"data-theme", | |
isDarkMode ? "dark" : "light" | |
); | |
document.querySelector(".theme-toggle").textContent = isDarkMode | |
? "☀️" | |
: "🌙"; | |
} | |
function showStatus(message, type) { | |
const status = document.getElementById("status"); | |
status.textContent = message; | |
status.style.color = type === "success" ? "var(--success)" : "#ef4444"; | |
status.style.display = "block"; | |
setTimeout(() => { | |
status.style.display = "none"; | |
}, 3000); | |
} | |
// Event Listeners | |
document.addEventListener("DOMContentLoaded", () => { | |
loadAvailableFonts(); | |
setupColorPickers(); | |
// Font family change listeners | |
document.getElementById("quoteFontFamily").addEventListener("change", (e) => { | |
updateFontVariations("quote", e.target.value); | |
}); | |
document | |
.getElementById("authorFontFamily") | |
.addEventListener("change", (e) => { | |
updateFontVariations("author", e.target.value); | |
}); | |
// Check system theme preference | |
if ( | |
window.matchMedia && | |
window.matchMedia("(prefers-color-scheme: dark)").matches | |
) { | |
toggleTheme(); | |
} | |
// Setup bar width value display | |
const barWidthSlider = document.getElementById("barWidth"); | |
const barWidthValue = document.getElementById("barWidthValue"); | |
barWidthSlider.addEventListener("input", () => { | |
barWidthValue.textContent = `${barWidthSlider.value}px`; | |
}); | |
}); | |
// Close modal when clicking outside | |
window.onclick = function (event) { | |
const modal = document.getElementById("requestsModal"); | |
if (event.target === modal) { | |
closeModal(); | |
} | |
}; | |