Newest

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
export const pyodideExample = () => {
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My Pyodide App</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/pyodide/v0.25.1/full/pyodide.js"></script>
</head>
<body>
<h1>Calculate the square of a number and visualize powers using Python in WebAssembly</h1>
<input type="number" id="number" placeholder="Enter a number">
<button id="calculate">Calculate</button>
<p id="result"></p>
<div id="plot"></div>
<script>
async function main() {
let pyodide = await loadPyodide();
await pyodide.loadPackage('matplotlib');
console.log('Pyodide is ready to use!');
pyodide.runPython(\`
import matplotlib.pyplot as plt
import io
import base64
def square(x):
return x * x
def plot_powers(n):
x = list(range(1, n+1))
y_square = [i**2 for i in x]
y_cube = [i**3 for i in x]
plt.figure()
plt.plot(x, y_square, label='Square')
plt.plot(x, y_cube, label='Cube')
plt.xlabel('Number')
plt.ylabel('Value')
plt.title('Powers of Numbers')
plt.legend()
plt.grid(True)
buf = io.BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)
img = base64.b64encode(buf.read()).decode('utf-8')
return img
\`);
return pyodide;
}
const pyodide = main();
const calculateButton = document.getElementById('calculate');
const numberInput = document.getElementById('number');
const resultElement = document.getElementById('result');
const plotContainer = document.getElementById('plot');
calculateButton.addEventListener('click', async () => {
const number = parseInt(numberInput.value);
const result = await pyodide.then(pyodide => pyodide.runPython(\`square(\${number})\`));
resultElement.textContent = \`The square of \${number} is \${result}.\`;
const img = await pyodide.then(pyodide => pyodide.runPython(\`plot_powers(\${number})\`));
plotContainer.innerHTML = \`<img src="data:image/png;base64,\${img}" />\`;
});
</script>
</body>
</html>
`;
return new Response(html, {
headers: {
"Content-Type": "text/html",
},
});
};

codemirror-ts

You can import this a val like this in browser to run codemirror-ts. Bundling done with esm.sh.

Working demo: https://maxm-whitenewt.web.val.run/

Run an editor like so:

Create val/** @jsxImportSource https://esm.sh/react */ import { renderToString } from "npm:react-dom/server"; export default async function(req: Request): Promise<Response> { return new Response( renderToString( <> <script type="module" src="https://esm.town/v/maxm/codemirrorTsBrowser" /> <div id="editor"></div> </>, ), { headers: { "content-type": "text/html" } }, ); }
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
import build from "https://esm.sh/build";
const ret = await build({
dependencies: {
"@codemirror/view": "^6",
"@codemirror/autocomplete": "^6",
"@codemirror/lang-javascript": "^6",
"@codemirror/lint": "^6",
"@codemirror/state": "^6",
"@typescript/vfs": "^1.5.0",
"codemirror": "^6",
"typescript": "^5.2.2",
"@valtown/codemirror-ts": "1.0.0",
},
source: `
export { EditorView, basicSetup } from "codemirror";
export { javascript } from "@codemirror/lang-javascript";
export { autocompletion } from "@codemirror/autocomplete";
export {
createDefaultMapFromCDN,
createSystem,
createVirtualTypeScriptEnvironment,
} from "@typescript/vfs";
export { ScriptElementKind } from "typescript";
export {
tsLinter,
tsHover,
tsAutocomplete,
tsSync,
} from "@valtown/codemirror-ts";
`,
});
const {
autocompletion,
basicSetup,
createDefaultMapFromCDN,
createSystem,
createVirtualTypeScriptEnvironment,
EditorView,
javascript,
tsAutocomplete,
tsHover,
tsLinter,
tsSync,
} = await import(ret.url + "?bundle-deps");
import ts from "https://esm.sh/typescript@5.4.5";
const fsMap = await createDefaultMapFromCDN(
{ target: ts.ScriptTarget.ES2022 },
"3.7.3",
true,
ts,
);
const system = createSystem(fsMap);
const compilerOpts = {};
const env = createVirtualTypeScriptEnvironment(system, [], ts, compilerOpts);
const path = "index.ts";
console.log(env.getSourceFile);
let editor = new EditorView({
doc: `let hasAnError: string = 10;
function increment(num: number) {
return num + 1;
}
increment('not a number');`,
extensions: [
basicSetup,
javascript({
typescript: true,
jsx: true,
}),
tsSync({ env, path }),
tsLinter({ env, path }),
autocompletion({
override: [tsAutocomplete({ env, path })],
}),
tsHover({
env,
path,
}),
],
parent: document.querySelector("#editor")!,
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** @jsxImportSource https://esm.sh/react */
import { renderToString } from "npm:react-dom/server";
export default async function(req: Request): Promise<Response> {
return new Response(
renderToString(
<>
<script type="module" src="https://esm.town/v/maxm/codemirrorTsBrowser" />
<div id="editor"></div>
</>,
),
{ headers: { "content-type": "text/html" } },
);
}
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
export const pyodideExample = () => {
const html = `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My Pyodide App</title>
<script type="text/javascript" src="https://cdn.jsdelivr.net/pyodide/v0.25.1/full/pyodide.js"></script>
</head>
<body>
<h1>Calculate the square of a number using Python in WebAssembly</h1>
<input type="number" id="number" placeholder="Enter a number">
<button id="calculate">Calculate</button>
<p id="result"></p>
<script>
async function main() {
let pyodide = await loadPyodide();
console.log('Pyodide is ready to use!');
pyodide.globals.set("square", x => x * x);
return pyodide;
}
const pyodide = main();
const calculateButton = document.getElementById('calculate');
const numberInput = document.getElementById('number');
const resultElement = document.getElementById('result');
calculateButton.addEventListener('click', async () => {
const number = parseInt(numberInput.value);
const result = await pyodide.then(pyodide => pyodide.runPython(\`square(\${number})\`));
resultElement.textContent = \`The square of \${number} is \${result}.\`;
});
</script>
</body>
</html>
`;
return new Response(html, {
headers: {
"Content-Type": "text/html",
},
});
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export default async function(req: Request): Promise<Response> {
const url = new URL(req.url);
const path = url.searchParams.get("path");
const apiUrl = new URL(path, "https://api.company-information.service.gov.uk/");
const apiKey = Deno.env.get("companiesHouseApiKey");
const basicAuth = btoa(apiKey + ":");
const response = await fetch(apiUrl, {
headers: { Authorization: `Basic ${basicAuth}` },
});
if (!response.ok) {
return new Response(await response.text(), {
status: response.status,
statusText: response.statusText,
});
}
const data = await response.json();
return Response.json(data);
}

Calorie Count via Photo

Uploads your photo to ChatGPT's new vision model to automatically categorize the food and estimate the calories.

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
/** @jsxImportSource npm:hono@3/jsx */
import { getGPT4oEmoji } from "https://esm.town/v/jdan/gpt4o_emoji";
import { blob } from "https://esm.town/v/std/blob?v=12";
import { fileToDataURL } from "https://esm.town/v/stevekrouse/fileToDataURL";
import { modifyImage } from "https://esm.town/v/stevekrouse/modifyImage";
import { chat } from "https://esm.town/v/stevekrouse/openai";
import { Hono } from "npm:hono@3";
function esmTown(url) {
return fetch(url, {
headers: {
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.142.86 Safari/537.36",
},
}).then(r => r.text());
}
const app = new Hono();
export default app.fetch;
app.get("/", async (c) =>
c.html(
<html>
<head>
<title>Calorie Estimator</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.tailwindcss.com" />
</head>
<body class="h-dvh bg-blue-100">
<div class="flex flex-col h-full items-center p-20">
<a href="/">
<h1 class="text-5xl font-extrabold text-blue-900 hover:underline">What emoji is this?</h1>
</a>
<div class="flex flex-col flex-grow justify-center items-center">
<form
action="/"
target="results"
class="flex flex-col"
method="post"
enctype="multipart/form-data"
>
<div></div>
<label
for="file"
id="upload"
class="px-6 py-4 m-4 text-3xl text-center cursor-pointer border-4 border-blue-400 rounded-xl transition-all bg-blue-300 hover:bg-blue-400 hover:text-blue-900 text-blue-900 shadow-2xl shadow-blue-500/50"
>
Upload photo
</label>
<input
class="hidden"
type="file"
id="file"
accept="image/*"
name="file"
/>
<img class="rounded-md shadow-2xl shadow-blue-500/50" id="preview" />
</form>
<iframe width="300px" height="600px" name="results" class="hidden" id="results" srcdoc="Loading...">
</iframe>
</div>
<a href="https://val.town/v/stevekrouse/calories" class="hover:underline text-blue-700">
view code
</a>
</div>
</body>
<script
dangerouslySetInnerHTML={{
__html: await esmTown("https://esm.town/v/stevekrouse/calories_script"),
}}
type="module"
/>
</html>,
));
app.post("/", async (c) => {
const formData = await c.req.formData();
const file = formData.get("file") as File;
console.log(file);
if (!file || !file.size) {
return c.text("No file uploaded", 400);
}
const key = `file-${file.type}-${Math.floor(Math.random() * 100000)}`;
const ident = await blob.set(key, file);
return c.html(
<div style={{ fontSize: 80 }}>
<a href={`/${encodeURIComponent(key)}`} target="results">
{key}
</a>
</div>,
);
});
/* on /:id, return the blob with the given key */
app.get("/:key", async (c) => {
const key = c.req.param("key");
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
import { chat } from "https://esm.town/v/stevekrouse/openai?v=19";
export async function getGPT4oEmoji(url) {
const response = await chat([
{
role: "system",
// Respond ONLY in a JSON array with values conforming to: {emoji: string}
content: `
You are a helpful assistant.
I will provide you with a photo, and I would like an emoji that best captures the essence of the photo.
Reply with 1 to 5 emojis that the image evokes. Prefer fewer emojis.
`,
},
// {
// role: "user",
// // content: "A chocolate donut with sprinkles",
// content: [{
// type: "image_url",
// image_url: {
// url: "https://brokenovenbaking.com/wp-content/uploads/2022/04/chocolate-donuts-with-sprinkles-11-1.jpg",
// },
// }],
// },
// {
// role: "assistant",
// content: `🍩`,
// },
{
role: "user",
content: [{
type: "image_url",
image_url: {
url,
},
}],
},
], {
model: "gpt-4o",
max_tokens: 100,
});
return response.content;
}
// console.log(
// await getGPT4oEmoji(
// // "https://images.squarespace-cdn.com/content/v1/62015f66f840ef671da14ae7/4161edfa-ebba-4c13-90a4-90383f26260e/Photographing-NYC-skyline.jpg",
// "https://i.imgur.com/FaNnVND.png",
// ),
// );

Calorie Count via Photo

Uploads your photo to ChatGPT's new vision model to automatically categorize the food and estimate the calories.

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
/** @jsxImportSource npm:hono@3/jsx */
import { getGPT4oEmoji } from "https://esm.town/v/jdan/gpt4o_emoji";
import { fileToDataURL } from "https://esm.town/v/stevekrouse/fileToDataURL";
import { modifyImage } from "https://esm.town/v/stevekrouse/modifyImage";
import { chat } from "https://esm.town/v/stevekrouse/openai";
import { Hono } from "npm:hono@3";
function esmTown(url) {
return fetch(url, {
headers: {
"User-Agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.142.86 Safari/537.36",
},
}).then(r => r.text());
}
const app = new Hono();
export default app.fetch;
app.get("/", async (c) =>
c.html(
<html>
<head>
<title>Calorie Estimator</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.tailwindcss.com" />
</head>
<body class="h-dvh bg-blue-100">
<div class="flex flex-col h-full items-center p-20">
<a href="/">
<h1 class="text-5xl font-extrabold text-blue-900 hover:underline">What emoji is this?</h1>
</a>
<div class="flex flex-col flex-grow justify-center items-center">
<form
action="/"
target="results"
class="flex flex-col"
method="post"
enctype="multipart/form-data"
>
<div></div>
<label
for="file"
id="upload"
class="px-6 py-4 m-4 text-3xl text-center cursor-pointer border-4 border-blue-400 rounded-xl transition-all bg-blue-300 hover:bg-blue-400 hover:text-blue-900 text-blue-900 shadow-2xl shadow-blue-500/50"
>
Upload photo
</label>
<input
class="hidden"
type="file"
id="file"
accept="image/*"
name="file"
/>
<img class="rounded-md shadow-2xl shadow-blue-500/50" id="preview" />
</form>
<iframe width="300px" height="600px" name="results" class="hidden" id="results" srcdoc="Loading...">
</iframe>
</div>
<a href="https://val.town/v/stevekrouse/calories" class="hover:underline text-blue-700">
view code
</a>
</div>
</body>
<script
dangerouslySetInnerHTML={{
__html: await esmTown("https://esm.town/v/stevekrouse/calories_script"),
}}
type="module"
/>
</html>,
));
app.post("/", async (c) => {
const formData = await c.req.formData();
const file = formData.get("file") as File;
console.log(file);
if (!file || !file.size) {
return c.text("No file uploaded", 400);
}
const dataURL = await fileToDataURL(file);
const emoji = getGPT4oEmoji(dataURL);
return c.html(
<div style={{ fontSize: 80 }}>
Emoji: {emoji}
</div>,
);
});
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
/** @jsxImportSource https://esm.sh/hono@latest/jsx **/
import { Hono } from "npm:hono@3";
const app = new Hono();
app.get("/", (c) =>
c.html(
<html>
<head>
<title>Will Krouse</title>
<style
dangerouslySetInnerHTML={{
__html:
`:root{--slate1: hsl(200, 7%, 8.8%);--slate2: hsl(195, 7.1%, 11%);--slate3: hsl(197, 6.8%, 13.6%);--slate4: hsl(198, 6.6%, 15.8%);--slate5: hsl(199, 6.4%, 17.9%);--slate6: hsl(201, 6.2%, 20.5%);--slate7: hsl(203, 6%, 24.3%);--slate8: hsl(207,
}}
>
</style>
</head>
<body>
<h1>Will Krouse</h1>
<a href="https://twitter.com/Willthereader" target="_blank">Twitter</a>{" "}
<a href="https://github.com/wkrouse" target="_blank">Github</a>
<p>
Hi, I'm Will, a 24-year-old autistic individual in Florida. I love reading. I enjoy playing board games,
tennis, and pickleball.
</p>
<p>
I'm learning to code. My favorite part are the meta-skills. For example, the debugging mindset is always
useful for fixing problems.
</p>
<p>
I find the complexity of current events and politics really interesting. I enjoy articles that significantly
change my understanding. I really like{" "}
<a className="text-blue-500 hover:underline" href="https://www.readtangle.com">Tangle</a>{" "}
because it offers perspectives from all sides. I dislike when people assume the worst of their opponents and
when I can't tell who's right because no one addresses the best arguments from their opponents.
</p>
<p>
I am on the autism spectrum, so I sometimes misunderstand people's tones and vibes or send the wrong ones.
Your patience and feedback are appreciated!
</p>
<p>
I built{" "}
<a className="text-blue-500 hover:underline" href="https://www.val.town/v/willthereader/personalWebsite">
this site
</a>{" "}
on Val Town.
</p>
<h2>Projects</h2>
<div>
<div>
3/6/2024 -{" "}
<a href="https://news.ycombinator.com/item?id=39618062">
Bringing My OCD Online: Userscripts for Immersive Forum Browsing
</a>
</div>
</div>
<h2>Credit</h2>
<div>
<div>
<p>
I copied the formatting from{" "}
<a className="text-blue-500 hover:underline" href="https://vbud.dev/">Valjean Clark</a>{" "}
and was inspired by his design.
</p>
</div>
</div>
</body>
</html>,
));
app.get("/yeah", (c) => c.html("Routing!"));
export default app.fetch;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export default async function(req: Request): Promise<Response> {
return new Response(
JSON.stringify(
[
{
asset: "eth",
links: ["https://www.infura.io/faucet/sepolia"],
},
{
asset: "usdc",
links: ["https://faucet.circle.com"],
},
{
asset: "link",
links: ["https://faucets.chain.link"],
},
],
undefined,
2,
),
{ status: 200 },
);
}