File size: 5,156 Bytes
6348944
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<html>
  <head>
    <title>Webapp Factory 🏭</title>
    <link href="/css/[email protected]" rel="stylesheet" type="text/css">
    <script src="/js/[email protected]" defer></script>
    <script src="/js/[email protected]"></script>
  </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 space to generate web content with WizardCoder, using an Inference Endpoint.</p>
              <p>Feel free to duplicate and create interesting forks πŸ”§</p>
            </div>
            <textarea
              name="draft"
              x-model="draft"
              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 = draft, state = state === 'stopped' ? 'loading' : 'stopped'"
              :class="draft.length < minPromptSize ? 'btn-neutral' : state === 'stopped' ? 'btn-accent' : 'btn-warning'"
              :disabled="draft.length < minPromptSize"
            >
              <span x-show="draft.length < minPromptSize">Prompt too short to generate</span>
              <span x-show="draft.length >= minPromptSize && state !== 'stopped'">Stop now</span>
              <span x-show="draft.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 a while β˜•)</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=${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,
    draft: 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: 10, // 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 ${timeoutInSec} seconds to generate a token.`)
          this.state = 'stopped'
          return
        }
        if (html.includes('</html>')) {
          // console.log('We reached the natural end of the stream, it seems.')
          // this.state === 'stopped'
          return
        }
      }, 100)
    },
  }
}
    </script>
  </body>
</html>