mirror of
https://github.com/vibegif/vibegif.lol.git
synced 2026-04-07 02:12:12 +00:00
Feat: Alpine app orchestration and generation loop
This commit is contained in:
115
assets/js/app.js
Normal file
115
assets/js/app.js
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import { generateImageFrame } from "./openrouter.js";
|
||||||
|
import { buildGifFromFrames } from "./gif.js";
|
||||||
|
import { firstPrompt, nextFramePrompt, clampForm } from "./ui.js";
|
||||||
|
|
||||||
|
window.vibeGifApp = function () {
|
||||||
|
return {
|
||||||
|
settingsOpen: false,
|
||||||
|
loading: false,
|
||||||
|
error: "",
|
||||||
|
apiKeyInput: "",
|
||||||
|
apiKey: "",
|
||||||
|
progressLabel: "",
|
||||||
|
progressPct: 0,
|
||||||
|
frames: [],
|
||||||
|
gifUrl: "",
|
||||||
|
|
||||||
|
form: {
|
||||||
|
model: "google/gemini-3.1-flash-image-preview",
|
||||||
|
userPrompt: "",
|
||||||
|
frameCount: 4,
|
||||||
|
fps: 6,
|
||||||
|
imageSize: "1K",
|
||||||
|
aspectRatio: "1:1"
|
||||||
|
},
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.apiKey = localStorage.getItem("openrouter_api_key") || "";
|
||||||
|
this.apiKeyInput = this.apiKey || "";
|
||||||
|
window.lucide?.createIcons();
|
||||||
|
this.$watch("settingsOpen", () => setTimeout(() => window.lucide?.createIcons(), 0));
|
||||||
|
this.$watch("form.model", () => {
|
||||||
|
if (this.form.model !== "google/gemini-3.1-flash-image-preview" && this.form.imageSize === "0.5K") {
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Frame 1 prompt must be exactly this template + userPrompt
|
||||||
|
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 p = nextFramePrompt(total);
|
||||||
|
const next = await generateImageFrame({
|
||||||
|
apiKey: this.apiKey,
|
||||||
|
model: this.form.model,
|
||||||
|
textPrompt: p,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user