import snarkdown from "https://cdn.skypack.dev/snarkdown"; import hljs from "https://cdn.skypack.dev/highlight.js"; // models base url const MODELS = { moondream2_q4k: { base_url: "https://huggingface.co/santiagomed/candle-moondream/resolve/main/", model: "model-q4_0.gguf", tokenizer: "tokenizer.json", quantized: true, size: "1.51 GB", }, }; const moodreamWorker = new Worker("./moondreamWorker.js", { type: "module", }); async function generateSequence(controller) { const getValue = (id) => document.querySelector(`#${id}`).value; const modelID = getValue("model"); const model = MODELS[modelID]; const weightsURL = model.model instanceof Array ? model.model.map((m) => model.base_url + m) : model.base_url + model.model; const tokenizerURL = model.base_url + model.tokenizer; const prompt = getValue("prompt").trim(); const temperature = getValue("temperature"); const topP = getValue("top-p"); const repeatPenalty = getValue("repeat_penalty"); const seed = getValue("seed"); const maxSeqLen = getValue("max-seq"); if (prompt?.value?.trim() === "") { return; } function updateStatus(data) { const outStatus = document.querySelector("#output-status"); const outGen = document.querySelector("#output-generation"); const outCounter = document.querySelector("#output-counter"); switch (data.status) { case "loading": outStatus.hidden = false; outStatus.textContent = data.message; outGen.hidden = true; outCounter.hidden = true; break; case "generating": const { message, prompt, sentence, tokensSec, totalTime } = data; outStatus.hidden = true; outCounter.hidden = false; outGen.hidden = false; outGen.innerHTML = snarkdown(prompt + sentence); outCounter.innerHTML = `${(totalTime / 1000).toFixed( 2 )}s (${tokensSec.toFixed(2)} tok/s)`; hljs.highlightAll(); break; case "complete": outStatus.hidden = true; outGen.hidden = false; break; } } return new Promise((resolve, reject) => { moodreamWorker.postMessage({ weightsURL, modelID, tokenizerURL, quantized: model.quantized, imageURL: currentImageURL, prompt, temp: temperature, top_p: topP, repeatPenalty, seed: seed, maxSeqLen, verbose_prompt: false, command: "start", }); const handleAbort = () => { moodreamWorker.postMessage({ command: "abort" }); }; const handleMessage = (event) => { const { status, error, message, prompt, sentence } = event.data; if (status) updateStatus(event.data); if (error) { moodreamWorker.removeEventListener("message", handleMessage); reject(new Error(error)); } if (status === "aborted") { moodreamWorker.removeEventListener("message", handleMessage); resolve(event.data); } if (status === "complete") { moodreamWorker.removeEventListener("message", handleMessage); resolve(event.data); } }; controller.signal.addEventListener("abort", handleAbort); moodreamWorker.addEventListener("message", handleMessage); }); } const form = document.querySelector("#form"); const prompt = document.querySelector("#prompt"); const runBtn = document.querySelector("#run"); const modelSelect = document.querySelector("#model"); const dropArea = document.querySelector("#drop-area"); const canvas = document.querySelector("#canvas"); const ctxCanvas = canvas.getContext("2d"); const fileUpload = document.querySelector("#file-upload"); const clearImgBtn = document.querySelector("#clear-img-btn"); const imagesExamples = document.querySelector("#image-select"); let currentImageURL = null; let runController = new AbortController(); let isRunning = false; document.addEventListener("DOMContentLoaded", () => { for (const [id, model] of Object.entries(MODELS)) { const option = document.createElement("option"); option.value = id; option.innerText = `${id} (${model.size})`; modelSelect.appendChild(option); } const query = new URLSearchParams(window.location.search); const modelID = query.get("model"); if (modelID) { modelSelect.value = modelID; } else { modelSelect.value = "moondream2_q4k"; } }); imagesExamples.addEventListener("click", (e) => { // if (isEmbedding || isSegmenting) { // return; // } const target = e.target; if (target.nodeName === "IMG") { const href = target.src; clearImageCanvas(); currentImageURL = href; drawImageCanvas(href); } }); modelSelect.addEventListener("change", (e) => { const query = new URLSearchParams(window.location.search); query.set("model", e.target.value); window.history.replaceState({}, "", `${window.location.pathname}?${query}`); window.parent.postMessage({ queryString: "?" + query }, "*"); const model = MODELS[e.target.value]; document.querySelector("#max-seq").max = model.seq_len; document.querySelector("#max-seq").nextElementSibling.value = 200; }); clearImgBtn.addEventListener("click", () => { clearImageCanvas(); }); //add event listener to file input fileUpload.addEventListener("input", async (e) => { const target = e.target; if (target.files.length > 0 && !target.files[0].type.includes("svg")) { const href = URL.createObjectURL(target.files[0]); clearImageCanvas(); await drawImageCanvas(href); } }); // add event listener to drop-area dropArea.addEventListener("dragenter", (e) => { e.preventDefault(); dropArea.classList.add("border-blue-700"); }); dropArea.addEventListener("dragleave", (e) => { e.preventDefault(); dropArea.classList.remove("border-blue-700"); }); dropArea.addEventListener("dragover", (e) => { e.preventDefault(); }); dropArea.addEventListener("drop", async (e) => { e.preventDefault(); dropArea.classList.remove("border-blue-700"); const url = e.dataTransfer.getData("text/uri-list"); const files = e.dataTransfer.files; if (files.length > 0) { const href = URL.createObjectURL(files[0]); clearImageCanvas(); await drawImageCanvas(href); } else if (url) { clearImageCanvas(); await drawImageCanvas(url); } }); form.addEventListener("submit", async (e) => { e.preventDefault(); if (isRunning) { stopRunning(); } else { startRunning(); await generateSequence(runController); stopRunning(); } }); async function drawImageCanvas(imgURL) { if (!imgURL) { throw new Error("No image URL provided"); } return new Promise((resolve, reject) => { ctxCanvas.clearRect(0, 0, canvas.width, canvas.height); ctxCanvas.clearRect(0, 0, canvas.width, canvas.height); const img = new Image(); img.crossOrigin = "anonymous"; img.onload = () => { canvas.width = img.width; canvas.height = img.height; ctxCanvas.drawImage(img, 0, 0); clearImgBtn.disabled = false; resolve(img); }; img.src = imgURL; currentImageURL = imgURL; }); } function clearImageCanvas() { ctxCanvas.clearRect(0, 0, canvas.width, canvas.height); clearImgBtn.disabled = true; canvas.parentElement.style.height = "auto"; currentImageURL = null; canvas.width = 0; canvas.height = 0; } function startRunning() { isRunning = true; runBtn.textContent = "Stop"; prompt.disabled = true; } function stopRunning() { runController.abort(); runController = new AbortController(); runBtn.textContent = "Run"; isRunning = false; prompt.disabled = false; } prompt.addEventListener("input", (e) => { runBtn.disabled = false; });