jbilcke-hf's picture
jbilcke-hf HF staff
fix
d40d743
<html>
<head>
<title>Webapp Factory 🏭</title>
<link href="/css/daisyui@2.6.0.css" rel="stylesheet" type="text/css">
</head>
<body>
<div class="flex flex-row" x-data="app()" x-init="init()">
<div
class="hero min-h-screen bg-stone-100 transition-[width] delay-150 ease-in-out"
:class="open ? 'w-2/6' : 'w-6/6'">
<div class="hero-content text-center">
<div class="flex flex-col max-w-xl space-y-6">
<h1
class="font-bold text-stone-600 mb-4 transition-all delay-150 ease-in-out"
:class="open ? 'md:text-3xl lg:text-4xl' : 'text-6xl'"
>Webapp Factory 🏭</h1>
<div
class="py-2 space-y-4 text-stone-600 transition-all delay-150 ease-in-out"
:class="open ? 'md:text-lg lg:text-xl' : 'text-2xl'">
<p>A Hugging Face space to generate web applications using a local LLM (Airoboros-13B).</p>
<p>No 3rd party service or token needed: feel free to duplicate and create interesting forks of this space πŸ”§</p>
</div>
<textarea
name="promptDraft"
x-model="promptDraft"
rows="10"
placeholder="a pong clone made using the canvas.."
class="input input-bordered w-full rounded text-lg text-stone-500 bg-stone-300 font-mono h-48"
></textarea>
<button
class="btn disabled:text-stone-400"
@click="open = true, prompt = promptDraft, state = state === 'stopped' ? 'loading' : 'stopped'"
:class="promptDraft.length < minPromptSize ? 'btn-neutral' : state === 'stopped' ? 'btn-accent' : 'btn-warning'"
:disabled="promptDraft.length < minPromptSize"
>
<span x-show="promptDraft.length < minPromptSize">Prompt too short to generate</span>
<span x-show="promptDraft.length >= minPromptSize && state !== 'stopped'">Stop now</span>
<span x-show="promptDraft.length >= minPromptSize && state === 'stopped'">Generate!</span>
</button>
<span class="py-3" x-show="state === 'loading'">Waiting for the stream to begin (might take a few minutes)..</span>
<span class="py-3" x-show="state === 'streaming'">
Streamed <span x-text="humanFileSize(size, true, 2)"></span> so far<br/> (hang on, this may take 5-15 minutes β˜•)</span>
</div>
</div>
</div>
<div
class="flex transition-[width] delay-150 ease-in-out"
:class="open ? 'w-4/6' : 'w-0'">
<iframe
id="iframe"
class="border-none w-full h-screen"
:src="!open
? '/placeholder.html'
: `/app?prompt=${encodeURIComponent(prompt)}`
"></iframe>
</div>
</div>
<script>
/**
* Format bytes as human-readable text.
*
* @param bytes Number of bytes.
* @param si True to use metric (SI) units, aka powers of 1000. False to use
* binary (IEC), aka powers of 1024.
* @param dp Number of decimal places to display.
*
* @return Formatted string.
*/
function humanFileSize(bytes, si=false, dp=1) {
const thresh = si ? 1000 : 1024;
if (Math.abs(bytes) < thresh) {
return bytes + ' B';
}
const units = si
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
let u = -1;
const r = 10**dp;
do {
bytes /= thresh;
++u;
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
return bytes.toFixed(dp) + ' ' + units[u];
}
function app() {
return {
open: false,
promptDraft: new URLSearchParams(window.location.search).get('prompt') || '',
prompt: '',
size: 0,
minPromptSize: 16, // if you change this, you will need to also change in src/index.mts
timeoutInSec: 3 * 60, // time before we determine something went wrong
state: 'stopped',
lastTokenAt: + new Date(),
init() {
setInterval(() => {
if (this.state === 'stopped') {
this.lastTokenAt = +new Date()
return
}
const html = document?.getElementById('iframe')?.contentWindow?.document?.documentElement?.outerHTML
const size = Number(html?.length) // count how many characters we have generated
if (isNaN(size) || !isFinite(size)) {
this.size = 0
this.state = 'loading'
return
}
this.size = new Blob([html]).size
this.state = 'streaming'
const lastTokenAt = + new Date()
const elapsed = (lastTokenAt - this.lastTokenAt) / 1000
this.lastTokenAt = lastTokenAt
if (elapsed > this.timeoutInSec) {
console.log(`Something went wrong, it too more than ${this.timeoutInSec} seconds to generate a token.`)
this.state = 'stopped'
return
}
if (html.endsWith('</html>')) {
// console.log('We reached the natural end of the stream, it seems.')
// this.state === 'stopped'
return
}
}, 100)
},
}
}
</script>
<script src="/js/alpinejs@3.12.2.js"></script>
<script src="/js/tailwindcss@3.3.2.js"></script>
</body>
</html>