andreterron-graffitiwebsite.web.val.run
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
import { runVal } from "https://esm.town/v/std/runVal";
export let graffitiWebsite = async (req: express.Request, res: express.Response) => {
const { default: htm } = await import("npm:htm");
const { default: vhtml } = await import("npm:vhtml");
const html = htm.bind(vhtml);
const head = html`<head>
<title>Graffiti Wall</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/htmx.org@1.9.2"></script>
</head>`;
const formClass =
"p-2 pt-16 group min-h-screen animate-all bg-emerald-50 w-full max-w-2xl mx-auto";
const inputClass = "p-2 border rounded w-full";
const labelClass = "w-full flex flex-col gap-2";
const inputNameClass = "pl-0.5 text-sm font-bold uppercase text-emerald-800";
const inputDescriptionClass = "pl-0.5 text-xs text-emerald-700";
if (req.method === "POST") {
const value = req.body;
if (
!("x" in value) ||
!value.x ||
!("y" in value) ||
!value.y ||
!("color" in value) ||
!value.color
) {
return res.end("Bad input");
}
await runVal(
"andreterron.paintGraffitiPixel",
value.x,
value.y,
value.color,
);
await new Promise<void>((resolve) => setTimeout(() => resolve(), 150));
}
const code = `await api(
@andreterron.paintGraffitiPixel,
${Math.min(Math.floor(Math.random() * 64), 64)}, // x
${Math.min(Math.floor(Math.random() * 32), 32)}, // y
"#ffffff", // color
);`;
const query = new URLSearchParams({ code });
const url = `https://www.val.town/embed/new?${query.toString()}`;
return res.end(html`<html>
${head}
<body class="bg-emerald-50">
<form hx-post="/" class=${formClass}>
<h1 class="text-3xl text-center font-bold text-emerald-800">
Graffiti Wall
</h1>
<img class="mx-auto my-2 bg-black w-[512px] h-[256px]" src="https://andreterron-graffitiwall.express.val.run/" />
<div class="flex flex-col gap-6 items-start p-4 max-w-2xl">
<label class=${labelClass} for="x">
<span class=${inputNameClass}>X</span>
<small class=${inputDescriptionClass}>0 ≤ x ${"<"} 64</small>
<input required class=${inputClass} id="x" name="x" type="number" placeholder="0" autocomplete="off" />
</label>
<label class=${labelClass} for="y">
<span class=${inputNameClass}>Y</span>
<small class=${inputDescriptionClass}>0 ≤ y ${"<"} 32</small>
<input required class=${inputClass} id="y" name="y" type="number" placeholder="0" autocomplete="off" />
</label>
<label class=${labelClass} for="color">
<span class=${inputNameClass}>Color</span>
<small class=${inputDescriptionClass}>"#ff0000" or "rgb(255,0,0)"</small>
<input required class=${inputClass} id="color" name="color" type="text" placeholder="#000000" autocomplete="off" />
</label>
<button class="p-2 bg-emerald-500 text-white rounded" type="submit">Paint!</button>
<div class="flex my-6 w-full items-center gap-4">
<hr class="block flex-1 w-full h-px bg-gray-600 opacity-100" />
<span class="shrink-0 grow-0 text-gray-600 opacity-80">or</span>
<hr class="block flex-1 w-full h-px bg-gray-600 opacity-100" />
</div>
<label class=${labelClass}>
<span class=${inputNameClass}>Create a val</span>
<small class=${inputDescriptionClass}>reload the page after running the code</small>
</label>
<iframe class="w-full h-[480px] bg-emerald-50" src=${url} />
</div>
</form>
</body>
</html>`);
};
// Forked from @andreterron.genval
// Forked from @tmcw.poll
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
October 23, 2023