xxxxxxxxxx
440
//File: sketch.js
//Credit: DragonFireGames
// ----------- API Keys -----------
const HUGGING_FACE_API_KEYS = [
"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",
"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",
"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",
"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",
"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"
];
var SELECTED_KEY = 0;
// --------- Model Options ---------
//const model = "gpt2";
//const model = "bigcode/starcoder2-15b";
//const model = "mistralai/Mixtral-8x7B-Instruct-v0.1";
//const model = "codellama/CodeLlama-13b-hf";
const model = "mistralai/Mistral-7B-Instruct-v0.2";
//const model = "google/gemma-7b";
//const model = "stabilityai/stable-code-3b";
//const model = "bigscience/bloom";
//const model = "stabilityai/stablecode-completion-alpha-3b-4k";
//const model = "HuggingFaceH4/starchat-beta";
//const modelType = 'question';
const modelType = 'generate';
// ------ Image Model Options ------
//const imageModel = "runwayml/stable-diffusion-v1-5";
const imageModel = "stabilityai/stable-diffusion-xl-base-1.0";
//const imageModel = "prompthero/openjourney";
//const imageModel = "nerijs/pixel-art-xl";
// ------------ Settings ------------
const useCache = false;
// https://huggingface.co/docs/api-inference/detailed_parameters#text-generation-task
var modelParameters = {
//top_k: 0,
//top_p: 0,
temperature: 1.0,
repetition_penalty: 1.2,//1.3,
//max_new_tokens: 20,
//max_time: 0,
return_full_text: true, // Keep true
//num_return_sequences: 2,
//do_sampling: 0,
};
var imageModelParameters = {
// Could not find api documentation, left blank for now.
};
var oneImageAtATime = false;
// --------------------------
// Get Width, Height, Elements
var width = window.innerWidth;
var height = window.innerHeight;
const prompt = document.getElementById("prompt");
const imageprompt = document.getElementById("imageprompt");
const enter = document.getElementById("enter");
const frame = document.getElementById("frame");
const loading = document.getElementById("loading");
var InvalidImage = LoadFile("assets/invalid.txt");
var ErrorImage = LoadFile("assets/error.txt");
const downHTML = document.getElementById("downloadHTML");
const downZip = document.getElementById("downloadZip");
const reloadBtn = document.getElementById("reloadBtn");
async function send(prehtml) {
enter.style.visibility = "hidden";
loading.style.visibility = "visible";
if (modelType == 'generate') var html = "This is an HTML webpage containing "+prompt.value+":\n<!DOCTYPE html>\n<html>";
else if (modelType == 'question') var html = "Write an HTML webpage containing "+prompt.value+"\n<|end|><|assistant|>\n<!DOCTYPE html>\n<html>";
if (prehtml) html = prehtml;
while (true) {
var before = html;
var generated = await generateHTML(html);
generated = generated.replace('<|endoftext|>',"</html>");
generated = generated.replace('<|end|>',"</html>");
generated = generated.replace('<<END>>',"</html>");
generated = generated.replace('"""',"</html>");
console.log(generated);
frame.src = "";
frame.srcdoc = generated;
if (modelParameters.return_full_text) html = generated;
else html += generated;
if (html == before) html += "</html>";
if (!html.match(/<html>([^]*?)<\/html>/gm)) continue;
break;
}
console.log("Done, creating web page");
await createWebPage(html);
loading.style.visibility = "hidden";
enter.style.visibility = "visible";
downHTML.style.visibility = "visible";
downZip.style.visibility = "visible";
reloadBtn.style.visibility = "visible";
}
//
function changePrompt(p1,p2) {
p1.style.height = 0;
var h = Math.min(p1.scrollHeight - 3, 115);
p1.style.height = h+"px";
p1.h = h;
frame.style.height = (height-h-p2.h-130)+"px";
}
changePrompt(prompt,imageprompt);
changePrompt(imageprompt,prompt);
window.onresize = function() {
changePrompt(prompt,imageprompt);
changePrompt(imageprompt,prompt);
}
// Send to ChatGPT and get html
async function generateHTML(sQuestion) {
//var sQuestion = "Write a webpage using html css js about "+prompt.value;
//var sQuestion = "Write a webpage using html css js which contains "+prompt.value;
if (sQuestion == "") {
alert("Type in your question!");
prompt.focus();
return;
}
const sUrl = 'https://api-inference.huggingface.co/models/'+model;
try {
var response = await fetch(sUrl, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"Authorization": 'Bearer '+HUGGING_FACE_API_KEYS[SELECTED_KEY]
},
body: JSON.stringify({
inputs: sQuestion,
parameters: modelParameters,
options: {
use_cache: useCache,
wait_for_model: true
}
})
});
var data = await response.json();
if (data.error) {
if (data.error.includes("Rate limit")) {
SELECTED_KEY++;
SELECTED_KEY = SELECTED_KEY % HUGGING_FACE_API_KEYS.length;
var text = await generateHTML(sQuestion);
return text;
}
console.error(data.error);
alert(data.error);
if (modelParameters.return_full_text) return sQuestion+"</html>";
else return "</html>";
}
return data[0].generated_text;
} catch (error) {
alert("Error generating HTML");
console.error("Error generating HTML:");
throw error;
}
}
// Add images and display recieved html while storing it in a zip file
var zipfile = {};
var originalHTML;
async function createWebPage(text) {
console.log(text);
var html;
try {
html = "<!DOCTYPE html>"+text.match(/<html>([^]*?)<\/html>/gm)[0];
} catch (e) {
console.log(text);
html = text;
throw Error("Invalid");
}
var js = text.match(/```(js|javascript)[^]*?```/g);
if (js) {
html = html.match(/^([^]+)<\/body>/)[1]+`
<script>
${js[0].match(/```(js|javascript)([^]*?)```/)[2]}
</script>
`+html.match(/<\/body>([^]+?)$/)[1];
console.log(html)
}
originalHTML = html;
frame.src = "";
frame.srcdoc = html;
zipfile = new JSZip();
var assetFolder = zipfile.folder("assets");
var htmlFile = html;
var modifiedText = html; // separate variable to store modified text
// Use a list to hold promises
// This allows each promise to run in parallel rather than in sequence
// Promise.all() will wait for them all to finish
var imgPromises = [];
const imgRegex1 = /<img[^>]+src\s*=\s*"([^"]+)"[^>]*>/gm;
const imgRegex1a = /<img[^>]+src\s*=\s*'([^']+)'[^>]*>/gm;
const imgRegex1b = /<img[^>]+src\s*=\s*([^"\s]+)[^>]*>/gm;
var matches1 = html.match(imgRegex1) || [];
matches1 = matches1.concat(html.match(imgRegex1a) || []);
matches1 = matches1.concat(html.match(imgRegex1b) || []);
if (matches1) {
// Generate promises
imgPromises = imgPromises.concat(matches1.map((elem) => async () => {
console.log(elem);
var src = (elem.match(/src\s*=\s*"([^"]+)"/) || elem.match(/src\s*=\s*'([^']+)'/) || elem.match(/src\s*=\s*([^"\s]+)/))[1];
var alt = elem.match(/alt\s*=\s*"([^"]+)"/);
var caption = html.match(new RegExp(elem+"[^<]*<figcaption>([^<]*)<\/figcaption>"));
var p; if (alt) p = alt[1];
if (caption) p = caption[1];
var { data, url, name } = await loadImage(src,p);
modifiedText = modifiedText.replace(src,url);
assetFolder.file(name, data, {base64: true});
htmlFile = htmlFile.replace(src,"assets/"+name);
frame.srcdoc = modifiedText;
return;
}));
}
const imgRegex2 = /background-image:\s*url\(('|")([^'"]+)('|")\)/g;
var matches2 = html.match(imgRegex2);
if (matches2) {
// Generate promises
imgPromises = imgPromises.concat(matches2.map((elem) => async () => {
var src = elem.match(/background-image:\s*url\(('|")([^'"]+)('|")\)/)[2];
var { data, url, name } = await loadImage(src);
modifiedText = modifiedText.replace(src,url);
assetFolder.file(name, data, {base64: true});
htmlFile = htmlFile.replace(src,"assets/"+name);
frame.srcdoc = modifiedText;
return;
}));
}
const imgRegex3 = /[\d\w_]+\.src\s*=\s*('|")([^'"]+)('|")/g;
var matches3 = html.match(imgRegex3);
if (matches3) {
// Generate promises
imgPromises = imgPromises.concat(matches3.map((elem) => async () => {
var src = elem.match(/\.src\s*=\s*('|")([^'"]+)('|")/)[2];
var p = elem.match(/^[^\.]+/)[0]+" from "+prompt.value;
var { data, url, name } = await loadImage(src);
modifiedText = modifiedText.replace(src,url);
assetFolder.file(name, data, {base64: true});
htmlFile = htmlFile.replace(src,"assets/"+name);
frame.srcdoc = modifiedText;
return;
}));
}
// Wait for all promises to resolve
if (oneImageAtATime) {
// Run in sequence
for (var i = 0; i < imgPromises.length; i++) {
console.log("Loading "+i);
await imgPromises[i]();
frame.src = "";
frame.srcdoc = modifiedText;
}
} else {
// Run in parallel
await Promise.all(imgPromises.map(e=>e()));
}
console.log("Images done!");
zipfile.file("index.html",htmlFile);
frame.src = "";
frame.srcdoc = modifiedText;
}
async function loadImage(src,p) {
var name = src.match(/([^/]+?)$/g)[0].match(/^[^\?#]+/g)[0];
var dataRegex = /data:image\/(jpeg|png|gif);base64,(.+)/;
try {
var res = await fetch(src);
var blob = await res.blob();
var base64 = await blobToData(blob);
var match = base64.match(dataRegex);
if (!match) throw Error();
} catch(e) {
if (!p) p = decodeURIComponent(name.match(/^[^\.]+/)[0]);
p = p.replace(/[-_]+/g," ");
p += " "+imageprompt.value;
base64 = await generateImage(p);
match = base64.match(dataRegex);
}
return {
data: match[2],
url: base64,
name: name,
type: match[1]
};
}
// Generate image from prompt
async function generateImage(imgPrompt) {
// Heavily modified from the example in the huggingface documentation
console.log("Generate "+imgPrompt);
const url = `https://api-inference.huggingface.co/models/`+imageModel;
const options = {
method: "POST",
headers: {
//"Content-Type": "application/json",
"Authorization": "Bearer " + HUGGING_FACE_API_KEYS[SELECTED_KEY],
},
body: JSON.stringify({
inputs: imgPrompt,
parameters: imageModelParameters,
options: {
use_cache: useCache,
wait_for_model: true
}
})
};
try {
var response = await fetch(url, options);
var blob = await response.blob();
if (blob.type == "application/json") {
var txt = await blob.text();
var json = JSON.parse(txt);
if (json.estimated_time) {
console.log(json.error+"\nWaiting "+json.estimated_time);
await wait(json.estimated_time * 1000);
return await generateImage(imgPrompt);
} else {
console.log(json);
return ErrorImage;
}
}
console.log(blob);
if (blob.size <= 4723) {
return await generateImage(imgPrompt);
}
var data = await blobToData(blob);
console.log(data.substring(0,100)+"...");
return data;
} catch (err) {
console.error(err);
return ErrorImage;
}
}
// Download file
async function downloadHTML() {
var url = 'data:text/plain;charset=utf-8,' + encodeURIComponent(frame.srcdoc);
var fileName = prompt.value.replaceAll(' ','_').replaceAll('.','')+'.html';
downloadFile(fileName,url);
}
async function downloadZip() {
var blob = await zipfile.generateAsync({type: 'blob'});
var fileName = prompt.value.replaceAll(' ','_').replaceAll('.','')+'.zip';
var url = URL.createObjectURL(blob);
downloadFile(fileName,url);
}
function downloadFile(name,dataUrl) {
var element = document.createElement('a');
element.href = dataUrl;
element.download = name;
element.click();
}
// Useful Misc Functions
async function LoadFile(url,call) {
var response = await fetch(url);
var data = await response.text();
if (call) {call(data);}
return data;
}
function blobToData(blob) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => resolve(e.target.result);
reader.readAsDataURL(blob);
})
}
async function wait(time) {
return new Promise((resolve) => {
setTimeout(resolve,time);
});
}