]> git.cworth.org Git - zombocom-ai/blob - index.html
Set bus timer to 30 seconds
[zombocom-ai] / index.html
1 <!DOCTYPE html>
2 <html>
3
4 <head>
5   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
6   <title>ZOMBO</title>
7   <link href="/zombo.css" rel="stylesheet" type="text/css">
8   <meta name="viewport" content="width=device-width,initial-scale=1">
9   <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
10   <meta name="HandheldFriendly" content="true">
11 </head>
12
13 <body>
14   <div id="content">
15
16     <div id="header" align="center">
17       <p>
18         <br>
19       </p>
20
21       <p>
22         <img src="/zombocom.png" alt="Zombocom" longdesc="http://zombo.com" width="1199" height="217">
23       </p>
24     </div>
25
26     <p>
27       Welcome to Zombocom. You can do anything at Zombocom, anything at
28       all. The only limit is yourself!
29     </p>
30
31     <dialog id="name-dialog">
32       <form method="dialog">
33         <p>
34           <label>Your name:
35             <input id="name" type="text" autocomplete="off">
36           </label>
37         </p>
38         <div>
39           <button value="default">OK</button>
40         </div>
41       </form>
42     </dialog>
43
44     <p>
45       <div id="spinner" align="center">
46         <div class="animate-flicker">
47           <p>
48             <img src="/pngwheel.png" class="rotate thefade">
49           </p>
50         </div>
51       </div>
52
53       <form action="" id="zombo-form">
54         <div class="form-row large">
55           <label for="prompt">
56             What can you imagine?
57           </label>
58           <button id="safety" type="button">Safety prompt</button>
59           <textarea id="prompt" rows="4" width="100%" autocomplete="off" required></textarea>
60         </div>
61
62         <div class="form-row small left">
63           <div class="labeled-row">
64             <label for="code">
65               Numeric code:
66             </label>
67             <input id="code" type="number" min="0" max="4294967295" autocomplete="off" placeholder="(Leave blank for random)" />
68           </div>
69         </div>
70
71         <div class="form-row large">
72           <button id="generate" type="submit">Make the infinite possible</button>
73         </div>
74       </form>
75     </p>
76
77     <div id="images">
78     </div>
79
80     <audio loop="" src="/zombo_words.mp3" type="audio/mpeg"></audio>
81     <button id="mute" class="menu-button fade volume">
82       <div>🔊</div>
83       <script>
84 const mute = document.querySelector("#mute");
85 const icon = document.querySelector("#mute > div");
86 const audio = document.querySelector("audio");
87
88 mute.addEventListener("click", () => {
89   if (audio.paused) {
90       audio.volume = 0.2;
91       audio.play();
92       icon.innerHTML = "🔈";
93   } else {
94       audio.pause();
95       icon.innerHTML = "🔊";
96   }
97   mute.classList.add("fade");
98 });
99       </script>
100
101     </button>
102
103     <button id="profile" class="menu-button">
104       <div>👤</div>
105     </button>
106
107   </div>
108
109   <script src="/socket.io/socket.io.js"></script>
110   <script>
111     var socket = io();
112
113     const name_input = document.querySelector("#name");
114     const name_dialog = document.querySelector("#name-dialog");
115     var informed_name;
116     const images = document.querySelector("#images");
117     const zombo_form = document.querySelector("#zombo-form");
118     const prompt = document.querySelector("#prompt");
119     const code = document.querySelector("#code");
120     const safety= document.querySelector("#safety");
121     const spinner = document.querySelector("#spinner");
122     const profile = document.querySelector("#profile");
123     var spinner_timeout;
124
125     profile.addEventListener('click', () => {
126         name_dialog.showModal();
127     });
128
129     name_dialog.addEventListener('close', () => {
130         socket.emit('set-name', name_input.value);
131     });
132
133     function add_comment(comments, comment) {
134         const dt = document.createElement('dt');
135         const dd = document.createElement('dd');
136         dt.textContent = comment.name + ':';
137         dd.textContent = comment.text;
138         comments.appendChild(dt);
139         comments.appendChild(dd);
140     }
141
142     socket.on('comment', function(comment) {
143         const comments = document.querySelector("#comments_" + comment.image_id);
144         add_comment(comments, comment);
145     });
146
147     socket.on('inform-name', (name) => {
148         console.log("Received inform-name event: " + name);
149
150         /* When we receive a name we store it in 3 places:
151          *
152          *   * The informed_name variable (confirming what the server knows)
153          *   * The name_input field (so the user will see that in their profile)
154          *   * In localStorage (for use in a future session)
155          */
156         informed_name = name;
157         name_input.value = name;
158         localStorage.setItem('name', name);
159     });
160
161     socket.on('reset', () => {
162         images.replaceChildren();
163
164         /* Since the server is seeing us for the first time, let it
165            know our name (if we have one). */
166         const name = localStorage.getItem('name');
167         if (name) {
168             socket.emit('set-name', name);
169             return;
170         }
171     });
172
173     socket.on('image', (image) => {
174         const figure = document.createElement('figure');
175         figure.id = "image_" + image.id;
176
177         const img = document.createElement('img');
178         img.src = image.filename;
179
180         if (image.link) {
181             const a = document.createElement('a');
182             a.href = image.link;
183             a.appendChild(img);
184             figure.appendChild(a);
185         } else {
186             figure.appendChild(img);
187         }
188
189         if (image.censored) {
190             img.style.display = "none";
191         }
192
193         if (image.prompt && image.code) {
194             const figcaption = document.createElement('figcaption');
195             const caption_text = document.createTextNode(`${image.prompt} (${image.code}) `);
196             figcaption.appendChild(caption_text);
197
198             const reuse_button = document.createElement('button');
199             reuse_button.appendChild(document.createTextNode("Reuse"));
200             figcaption.appendChild(reuse_button);
201
202             reuse_button.addEventListener('click', () => {
203                 prompt.value = image.prompt;
204                 window.scrollTo(0,0);
205             });
206
207             if (image.censored) {
208                 figcaption.style.display = "none";
209             }
210
211             figure.appendChild(figcaption);
212         }
213
214         const dl_comments = document.createElement('dl');
215         dl_comments.className = "comments";
216         dl_comments.id = "comments_" + image.id;
217
218         image.comments.forEach((comment) => {
219             add_comment(dl_comments, comment);
220         });
221
222         figure.appendChild(dl_comments);
223
224         const comment_form = document.createElement('form');
225         const comment_input = document.createElement('input')
226         comment_input.type = "text";
227         comment_input.className = "comment";
228         comment_input.placeholder = "Add a comment";
229         comment_form.appendChild(comment_input);
230         figure.appendChild(comment_form);
231
232         comment_input.addEventListener('focus', () => {
233             /* If the server has informed us it has our name, we are done. */
234             if (informed_name)
235                 return;
236
237             /* If server has no name, check local storage. */
238             const name = localStorage.getItem('name');
239             if (name) {
240                 socket.emit('set-name', name);
241                 return;
242             }
243
244             /* Failing both, bring up the modal dialog to set the name. */
245             name_dialog.showModal();
246         });
247
248         comment_form.addEventListener('submit', function(e) {
249             e.preventDefault();
250             if (comment_input.value) {
251                 socket.emit('comment', {image_id: image.id,
252                                         text: comment_input.value});
253                 comment_input.value = '';
254             }
255         });
256
257         images.prepend(figure);
258     });
259
260     function hide_spinner() {
261         zombo_form.style.display = "grid";
262         spinner.style.display = "none";
263     }
264
265     zombo_form.addEventListener('submit', function(e) {
266         e.preventDefault();
267         /* Hide the form and show spinner while generation is happening. */
268         zombo_form.style.display = "none";
269         spinner.style.display = "block";
270         spinner_timeout = setTimeout(hide_spinner, 60000);
271         socket.emit('generate', {"prompt": prompt.value, "code": code.value});
272         prompt.value = '';
273     });
274
275     socket.on('generation-done', () => {
276         /* Re-display the form and hide spinner now that generation is over. */
277         clearTimeout(spinner_timeout);
278         hide_spinner();
279     });
280
281     function random_from(items) {
282         return items[Math.floor(Math.random()*items.length)];
283     }
284
285     safety.addEventListener("click", () => {
286         const art_type = [
287             "A portrait of",
288             "A hyperrealistic photograph of",
289             "A matte painting of",
290             "Epic fantasy card art of",
291             "Professional oil painting of",
292             "An image of",
293             "A pencil sketch of",
294             "A watercolor of",
295             "A 3D rendering of",
296             "A marble statue of",
297         ];
298         const subject = [
299             " a modern home",
300             " crashing waves",
301             " a Porsche 911 Carrera",
302             " a Halo spartan",
303             " a cute cat",
304             " a mad scientist",
305             " an elfin princess",
306             " sci-fi buildings",
307             " pastel purple clouds",
308             " a Teenage Mutant Ninja Turtle",
309             " Pikachu",
310         ];
311         const scenery = [
312             " in tropical surroundings",
313             " in space",
314             " in an ocean storm",
315             " in a snowy forest",
316             " in a cardboard box",
317             " holding an axe",
318             " in an epic landscape",
319             " in a haunted forest",
320             " riding a motorbike",
321             " in a futuristic city at night",
322             " in a dystopian landscape, foggy",
323             " in the land of snow",
324             " that looks like a watermelon",
325             " on the streets of London at night with neon lights flashing",
326             " that is part octopus",
327             " melting into a puddle",
328             " on a birthday cake",
329         ];
330         const artist = [
331             ", unreal engine 5",
332             ", concept art",
333             ", graphic novel",
334             " by Vincent Van Gogh",
335             " by Claude Monet",
336             " by Johannes Vermeer",
337             ", fantasy art, neon fog",
338             ", epic lighting from above",
339             ", trending on artstation",
340             " by Gustav Klimt",
341             " by Leonardo da Vinci",
342         ];
343
344         prompt.value = random_from(art_type) + random_from(subject) +
345             random_from(scenery) + random_from(artist);
346         return false;
347     });
348
349   </script>
350 </body>
351 </html>