import { generateImageFrame } from "./openrouter.js"; import { buildGifFromFrames } from "./gif.js"; import { firstPrompt, nextFramePrompt, clampForm } from "./ui.js"; const GEMINI_MODEL = "google/gemini-3.1-flash-image-preview"; window.vibeGifApp = function () { return { settingsOpen: false, loading: false, error: "", apiKeyInput: "", apiKey: "", progressLabel: "", progressPct: 0, frames: [], gifUrl: "", form: { model: GEMINI_MODEL, userPrompt: "", frameCount: 4, fps: 6, imageSize: "1K", aspectRatio: "1:1" }, isGeminiModel(model) { return model === GEMINI_MODEL; }, refreshIcons() { const L = window.lucide; if (!L?.createIcons) return; try { // Newer lucide builds can require icons object L.createIcons({ icons: L.icons }); } catch (_) { // Backward compatibility fallback try { L.createIcons(); } catch {} } }, init() { this.apiKey = localStorage.getItem("openrouter_api_key") || ""; this.apiKeyInput = this.apiKey || ""; this.$nextTick(() => this.refreshIcons()); window.addEventListener("load", () => this.refreshIcons()); document.addEventListener("alpine:initialized", () => this.refreshIcons()); this.$watch("settingsOpen", () => { this.$nextTick(() => this.refreshIcons()); }); this.$watch("form.model", (model) => { if (!this.isGeminiModel(model) && this.form.imageSize !== "1K") { this.form.imageSize = "1K"; } if (this.isGeminiModel(model) && !["1K", "0.5K"].includes(this.form.imageSize)) { this.form.imageSize = "1K"; } }); }, saveApiKey() { this.apiKey = this.apiKeyInput.trim(); localStorage.setItem("openrouter_api_key", this.apiKey); this.settingsOpen = false; }, clearApiKey() { this.apiKey = ""; this.apiKeyInput = ""; localStorage.removeItem("openrouter_api_key"); }, async generate() { this.error = ""; this.gifUrl = ""; this.frames = []; this.progressLabel = ""; this.progressPct = 0; clampForm(this.form); if (!this.apiKey) { this.error = "Add your OpenRouter API key first (panel-left icon)."; this.settingsOpen = true; return; } if (!this.form.userPrompt) { this.error = "Please enter a simple prompt (e.g. rolling cat)."; return; } this.loading = true; try { const total = this.form.frameCount; const p1 = firstPrompt(this.form.userPrompt); this.progressLabel = `Generating frame 1/${total}...`; const frame1 = await generateImageFrame({ apiKey: this.apiKey, model: this.form.model, textPrompt: p1, previousFrames: [], imageSize: this.form.imageSize, aspectRatio: this.form.aspectRatio }); this.frames.push(frame1); this.progressPct = Math.round((1 / total) * 100); for (let i = 2; i <= total; i++) { this.progressLabel = `Generating frame ${i}/${total}...`; const next = await generateImageFrame({ apiKey: this.apiKey, model: this.form.model, textPrompt: nextFramePrompt(total), previousFrames: this.frames.slice(-2), imageSize: this.form.imageSize, aspectRatio: this.form.aspectRatio }); this.frames.push(next); this.progressPct = Math.round((i / total) * 100); } this.progressLabel = "Building GIF..."; this.gifUrl = await buildGifFromFrames(this.frames, this.form.fps); this.progressLabel = "Done."; this.progressPct = 100; } catch (e) { this.error = e?.message || "Failed to generate."; } finally { this.loading = false; } } }; };