/** * Button Generator - Main Application File * * This demonstrates how to use the modular button generator system. * Effects are imported and registered with the generator. */ import { ButtonGenerator } from "./button-generator-core.js"; import { UIBuilder } from "./ui-builder.js"; // Import effects (each effect file exports a register() function) import * as solidBg from "./effects/background-solid.js"; import * as gradientBg from "./effects/background-gradient.js"; import * as textureBg from "./effects/background-texture.js"; import * as emojiWallpaper from "./effects/background-emoji-wallpaper.js"; import * as externalImage from "./effects/background-external-image.js"; import * as rainbowBg from "./effects/background-rainbow.js"; import * as rain from "./effects/background-rain.js"; import * as starfield from "./effects/background-starfield.js"; //import * as bubbles from "./effects/background-bubbles.js"; import * as aurora from "./effects/background-aurora.js"; import * as fire from "./effects/background-fire.js"; import * as border from "./effects/border.js"; import * as standardText from "./effects/text-standard.js"; import * as textShadow from "./effects/text-shadow.js"; import * as waveText from "./effects/wave-text.js"; import * as rainbowText from "./effects/rainbow-text.js"; import * as flashText from "./effects/flash-text.js"; import * as spinText from "./effects/spin-text.js"; import * as tickerText from "./effects/ticker-text.js"; import * as glitch from "./effects/glitch.js"; import * as pulse from "./effects/pulse.js"; import * as shimmer from "./effects/shimmer.js"; import * as scanline from "./effects/scanline.js"; import * as rgbSplit from "./effects/rgb-split.js"; import * as noise from "./effects/noise.js"; import * as rotate from "./effects/rotate.js"; import * as hologram from "./effects/hologram.js"; import * as spotlight from "./effects/spotlight.js"; /** * Initialize the button generator application */ export function init() { // Wait for DOM to be ready if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", setupApp); } else { setupApp(); } } /** * Setup the application */ async function setupApp() { console.log("Initializing Button Generator..."); // Setup collapsible sections setupCollapsible(); // Get canvas const canvas = document.getElementById("button-canvas"); if (!canvas) { console.error("Canvas element not found!"); return; } // Create button generator const generator = new ButtonGenerator(canvas, { fps: 20, duration: 2, fonts: [ "Arial", "Lato", "Roboto", "Open Sans", "Montserrat", "Oswald", "Bebas Neue", "Roboto Mono", "VT323", "Press Start 2P", "DSEG7-Classic", ], }); // Register all effects console.log("Registering effects..."); solidBg.register(generator); gradientBg.register(generator); textureBg.register(generator); emojiWallpaper.register(generator); externalImage.register(generator); rainbowBg.register(generator); rain.register(generator); starfield.register(generator); //bubbles.register(generator); aurora.register(generator); fire.register(generator); border.register(generator); standardText.register(generator); textShadow.register(generator); waveText.register(generator); rainbowText.register(generator); flashText.register(generator); spinText.register(generator); tickerText.register(generator); glitch.register(generator); pulse.register(generator); shimmer.register(generator); scanline.register(generator); rgbSplit.register(generator); noise.register(generator); rotate.register(generator); hologram.register(generator); spotlight.register(generator); console.log(`Registered ${generator.getAllEffects().length} effects`); // Build UI from effects console.log("Building UI..."); const controlsContainer = document.querySelector(".controls-section"); if (!controlsContainer) { console.error("Controls container not found!"); return; } const uiBuilder = new UIBuilder(controlsContainer); uiBuilder.buildUI(generator.getAllEffects()); uiBuilder.setupConditionalVisibility(); // Add GIF export settings at the bottom addGifExportSettings(controlsContainer, generator); // Preload fonts console.log("Loading fonts..."); await generator.preloadFonts(); // Bind controls (after UI is built) generator.bindControls(); // Setup additional UI handlers setupUIHandlers(generator); // Setup download button setupDownloadButton(generator); // Setup presets setupPresets(generator); // Initial draw generator.updatePreview(); console.log("Button Generator ready!"); } /** * Add GIF export settings controls */ function addGifExportSettings(container, generator) { const groupDiv = document.createElement('div'); groupDiv.className = 'control-group collapsed'; // Header const header = document.createElement('h3'); header.className = 'control-group-header'; header.textContent = 'Advanced Settings'; groupDiv.appendChild(header); const controlsDiv = document.createElement('div'); controlsDiv.className = 'control-group-controls'; // Color count control const colorsWrapper = document.createElement('div'); colorsWrapper.className = 'control-wrapper'; colorsWrapper.innerHTML = `
Note: This only affects frame-by-frame settings, i.e. 8 colours would be 8 colours per frame. I am working on a solution for this.
256
`; controlsDiv.appendChild(colorsWrapper); // Quality control const qualityWrapper = document.createElement('div'); qualityWrapper.className = 'control-wrapper'; qualityWrapper.innerHTML = `
1
`; controlsDiv.appendChild(qualityWrapper); // Dither control const ditherWrapper = document.createElement('div'); ditherWrapper.className = 'control-wrapper'; ditherWrapper.innerHTML = ` `; controlsDiv.appendChild(ditherWrapper); groupDiv.appendChild(controlsDiv); container.appendChild(groupDiv); // Setup event listeners for value display const colorsInput = document.getElementById('gif-colors'); const colorsValue = document.getElementById('gif-colors-value'); colorsInput.addEventListener('input', () => { colorsValue.textContent = colorsInput.value; }); const qualityInput = document.getElementById('gif-quality'); const qualityValue = document.getElementById('gif-quality-value'); qualityInput.addEventListener('input', () => { qualityValue.textContent = qualityInput.value; }); // Update generator config when changed colorsInput.addEventListener('change', () => { generator.gifConfig.colorCount = parseInt(colorsInput.value); generator.updatePreview(); // Update preview to show color quantization }); // Also update preview on slider input for real-time feedback colorsInput.addEventListener('input', () => { generator.gifConfig.colorCount = parseInt(colorsInput.value); generator.updatePreview(); // Real-time preview update }); qualityInput.addEventListener('change', () => { generator.gifConfig.quality = parseInt(qualityInput.value); }); const ditherSelect = document.getElementById('gif-dither'); ditherSelect.addEventListener('change', () => { const value = ditherSelect.value; generator.gifConfig.dither = value === 'false' ? false : value; }); } /** * Setup collapsible section functionality */ function setupCollapsible() { const headers = document.querySelectorAll(".control-group-header"); console.log("Found", headers.length, "collapsible headers"); headers.forEach((header) => { header.addEventListener("click", () => { const controlGroup = header.closest(".control-group"); if (controlGroup) { controlGroup.classList.toggle("collapsed"); } }); }); } /** * Setup UI handlers for conditional visibility */ function setupUIHandlers(generator) { // Note: Conditional visibility is now handled automatically by UIBuilder.setupConditionalVisibility() // This function is kept for any additional custom handlers if needed in the future } /** * Setup download button */ function setupDownloadButton(generator) { const downloadBtn = document.getElementById("download-button"); if (!downloadBtn) return; downloadBtn.addEventListener("click", async () => { const originalText = downloadBtn.textContent; downloadBtn.disabled = true; downloadBtn.textContent = "Generating GIF..."; try { const blob = await generator.exportAsGif((progress, stage) => { if (stage === "generating") { const percent = Math.round(progress * 100); downloadBtn.textContent = `Generating: ${percent}%`; } else if (stage === "encoding") { const percent = Math.round(progress * 100); downloadBtn.textContent = `Encoding: ${percent}%`; } }); // Download the blob const link = document.createElement("a"); link.download = "button-88x31.gif"; link.href = URL.createObjectURL(blob); link.click(); URL.revokeObjectURL(link.href); downloadBtn.textContent = originalText; } catch (error) { console.error("Error generating GIF:", error); alert("Error generating GIF. Please try again."); downloadBtn.textContent = originalText; } finally { downloadBtn.disabled = false; } }); } /** * Setup preset buttons */ function setupPresets(generator) { // Random preset const randomBtn = document.getElementById("preset-random"); if (randomBtn) { randomBtn.addEventListener("click", () => { applyRandomPreset(); generator.updatePreview(); }); } // Classic preset const classicBtn = document.getElementById("preset-classic"); if (classicBtn) { classicBtn.addEventListener("click", () => { applyClassicPreset(); generator.updatePreview(); }); } // Modern preset const modernBtn = document.getElementById("preset-modern"); if (modernBtn) { modernBtn.addEventListener("click", () => { applyModernPreset(); generator.updatePreview(); }); } } /** * Apply random preset */ function applyRandomPreset() { const randomColor = () => "#" + Math.floor(Math.random() * 16777215) .toString(16) .padStart(6, "0"); // Random background const bgTypeEl = document.getElementById("bg-type"); if (bgTypeEl) { bgTypeEl.value = ["solid", "gradient", "texture"][ Math.floor(Math.random() * 3) ]; bgTypeEl.dispatchEvent(new Event("change")); } setControlValue("bg-color", randomColor()); setControlValue("gradient-color1", randomColor()); setControlValue("gradient-color2", randomColor()); setControlValue("gradient-angle", Math.floor(Math.random() * 360)); // Random text colors setControlValue("text-color", randomColor()); setControlValue("text-gradient-color1", randomColor()); setControlValue("text-gradient-color2", randomColor()); setControlValue("text2-color", randomColor()); setControlValue("text2-gradient-color1", randomColor()); setControlValue("text2-gradient-color2", randomColor()); // Random border setControlValue("border-color", randomColor()); setControlValue("border-width", Math.floor(Math.random() * 6)); } /** * Apply classic 90s preset */ function applyClassicPreset() { setControlValue("bg-type", "gradient"); setControlValue("gradient-color1", "#6e6e6eff"); setControlValue("gradient-color2", "#979797"); setControlValue("gradient-angle", 90); setControlValue("text-color-type", "solid"); setControlValue("text-color", "#000000"); setControlValue("text2-color-type", "solid"); setControlValue("text2-color", "#000000"); setControlValue("border-width", 2); setControlValue("border-color", "#000000"); setControlValue("border-style", "outset"); setControlValue("font-family", "VT323"); setControlValue("font-family2", "VT323"); document.getElementById("bg-type")?.dispatchEvent(new Event("change")); } /** * Apply modern preset */ function applyModernPreset() { setControlValue("bg-type", "gradient"); setControlValue("gradient-color1", "#0a0a0a"); setControlValue("gradient-color2", "#1a0a2e"); setControlValue("gradient-angle", 135); setControlValue("text-color-type", "gradient"); setControlValue("text-gradient-color1", "#00ffaa"); setControlValue("text-gradient-color2", "#00ffff"); setControlValue("text-gradient-angle", 90); setControlValue("text2-color-type", "gradient"); setControlValue("text2-gradient-color1", "#ff00ff"); setControlValue("text2-gradient-color2", "#ff6600"); setControlValue("border-width", 1); setControlValue("border-color", "#00ffaa"); setControlValue("border-style", "solid"); setControlValue("font-family", "Roboto Mono"); setControlValue("font-family2", "Roboto Mono"); document.getElementById("bg-type")?.dispatchEvent(new Event("change")); document .getElementById("text-color-type") ?.dispatchEvent(new Event("change")); document .getElementById("text2-color-type") ?.dispatchEvent(new Event("change")); } /** * Helper to set control value */ function setControlValue(id, value) { const el = document.getElementById(id); if (el) { if (el.type === "checkbox") { el.checked = value; } else { el.value = value; } // Update value display if it exists const valueDisplay = document.getElementById(id + "-value"); if (valueDisplay) { valueDisplay.textContent = value; } } } // Auto-initialize when imported init();