Back to APIs list

Rime API examples & templates

Use these vals as a playground to view and fork Rime API examples and templates on Val Town. Run any example below or find templates that can be used as a pre-built solution.

stevekrouse.com - my personal website

This val hosts my personal website. The view data is stored in Val Town SQLite - @std/sqlite.

It used to live on Github Pages, which is why I proxy over requests to certain blog posts over to the Github Pages site still.

Todos

  • Speed up page load by loading sqlite data later like in @healeycodes/steve_web
  • Store more (legally storable) analytics data, and maybe make a sparkline!
  • Add some sort of way to contact me
  • Move over all my blog posts from Github Pages (maybe into @std/blob as a CMS?)
Readme
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 https://esm.sh/react */
import { email } from "https://esm.town/v/std/email?v=11";
import { sqlite } from "https://esm.town/v/std/sqlite?v=5";
import { ReloadScriptReactElement } from "https://esm.town/v/stevekrouse/ReloadScript";
import tailwindURL from "https://esm.town/v/stevekrouse/tailwindURL";
import { renderToString } from "npm:react-dom/server";
const linkClass = "text-blue-500 hover:underline";
const Link = (
{ children, href }: {
children?: React.ReactNode;
href: string;
},
) => <a className={linkClass} href={href}>{children}</a>;
const dateClass = "text-xs text-gray-400 font-mono mr-1 hidden sm:inline-block";
async function getHits() {
const [, , { rows: [[allHits]] }, { rows: [[todayHits]] }] = await sqlite.batch([
"CREATE TABLE IF NOT EXISTS stevekrouse_com_hits (timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)",
"INSERT INTO stevekrouse_com_hits DEFAULT VALUES",
"SELECT COUNT(*) FROM stevekrouse_com_hits where timestamp > datetime('now', '-28 day')",
"SELECT COUNT(*) from stevekrouse_com_hits where timestamp > datetime('now', '-1 day')",
]);
if (allHits % 100 === 0) email({ subject: `You got ${todayHits} hits today! (${allHits} total)` });
return { allHits, todayHits };
}
export default async (request: Request) => {
const url = new URL(request.url);
if (url.pathname === "/favicon.ico") return new Response(null, { status: 404 });
if (url.pathname !== "/")
return fetch(
`https://stevekrouse.github.io/${url.pathname}${url.search}`,
request as any as RequestInit,
);
const { allHits, todayHits } = await getHits();
return new Response(
renderToString(
<html>
<head>
<title>Steve Krouse</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<script src={tailwindURL} />
{url.searchParams.get("dev")
? <ReloadScriptReactElement vals={[{ valName: "dot_com", userHandle: "stevekrouse" }]} />
: null}
</head>
<body>
<div className="max-w-3xl p-10 space-y-4 mx-auto">
<h1 className="text-orange-600 text-2xl font-medium">Steve Krouse</h1>
<div>
πŸ‘‹ Hi, I'm Steve. I live in Prospect Heights, Brooklyn.
</div>
<div>
I build <Link href="https://val.town">Val Town</Link>, a social website to code in the cloud.
</div>
<div>
This site was{" "}
<Link href="https://www.val.town/v/stevekrouse/dot_com">built in Val Town</Link>. It was viewed{" "}
{todayHits} times today, and {allHits} times this month.
</div>
<div>
<h2 className="text-xl mb-1">Projects</h2>
<ul>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Jul -</span>
<Link href="https://val.town">Val Town</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Sep -</span>
<Link href="https://dateme.directory">Date Me Directory</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Jan -</span>
<Link href="https://twitter.com/stevekrouse/status/1520162279899078657">Zaplib</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2021 Mar -</span>
<Link href="http://updates.compose.run">Compose</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2017 Jul -</span>
<Link href="https://futureofcoding.org/">Future of Coding</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2016 May -</span>
<Link href="https://github.com/stevekrouse/woofjs">WoofJS</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2015 Sep -</span>
<Link href="http://coding.space">The Coding Space Curriculum</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2015 Jul -</span>
<Link href="http://thecodingspace.com">The Coding Space</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2014 Jan -</span>Software Engineer @{" "}
<Link href="https://looker.com/">Looker</Link>
</li>

stevekrouse.com - my personal website

This val hosts my personal website. The view data is stored in Val Town SQLite - @std/sqlite.

It used to live on Github Pages, which is why I proxy over requests to certain blog posts over to the Github Pages site still.

Todos

  • Speed up page load by loading sqlite data later like in @healeycodes/steve_web
  • Store more (legally storable) analytics data, and maybe make a sparkline!
  • Add some sort of way to contact me
  • Move over all my blog posts from Github Pages (maybe into @std/blob as a CMS?)
Readme
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 https://esm.sh/react */
import { email } from "https://esm.town/v/std/email?v=11";
import { sqlite } from "https://esm.town/v/std/sqlite?v=5";
import { ReloadScriptReactElement } from "https://esm.town/v/stevekrouse/ReloadScript";
import tailwindURL from "https://esm.town/v/stevekrouse/tailwindURL";
import React from "npm:react";
import { renderToString } from "npm:react-dom/server";
const linkClass = "text-blue-500 hover:underline";
const Link = (
{ children, href }: {
children?: React.ReactNode;
href: string;
},
) => <a className={linkClass} href={href}>{children}</a>;
const dateClass = "text-xs text-gray-400 font-mono mr-1 hidden sm:inline-block";
async function getHits() {
const [, , { rows: [[allHits]] }, { rows: [[todayHits]] }] = await sqlite.batch([
"CREATE TABLE IF NOT EXISTS stevekrouse_com_hits (timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)",
"INSERT INTO stevekrouse_com_hits DEFAULT VALUES",
"SELECT COUNT(*) FROM stevekrouse_com_hits where timestamp > datetime('now', '-28 day')",
"SELECT COUNT(*) from stevekrouse_com_hits where timestamp > datetime('now', '-1 day')",
]);
if (allHits % 100 === 0) email({ subject: `You got ${todayHits} hits today! (${allHits} total)` });
return { allHits, todayHits };
}
export default async (request: Request) => {
const url = new URL(request.url);
console.log(url);
if (url.pathname === "/favicon.ico") return new Response(null, { status: 404 });
if (url.pathname !== "/")
return fetch(
`https://stevekrouse.github.io/${url.pathname}${url.search}`,
request as any as RequestInit,
);
const { allHits, todayHits } = await getHits();
return new Response(
renderToString(
<html>
<head>
<title>Steve Krouse</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<script src={tailwindURL} />
{url.searchParams.get("dev")
? <ReloadScriptReactElement vals={[{ valName: "dot_com", userHandle: "stevekrouse" }]} />
: null}
</head>
<body>
<div className="max-w-3xl p-10 space-y-4 mx-auto">
<h1 className="text-orange-600 text-2xl font-medium">Steve Krouse</h1>
<div>
πŸ‘‹ Hi, I'm Steve. I live in Prospect Heights, Brooklyn.
</div>
<div>
I build <Link href="https://val.town">Val Town</Link>, a social website to code in the cloud.
</div>
<div>
This site was{" "}
<Link href="https://www.val.town/v/stevekrouse/dot_com">built in Val Town</Link>. It was viewed{" "}
{todayHits} times today, and {allHits} times this month.
</div>
<div>
<h2 className="text-xl mb-1">Projects</h2>
<ul>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Jul -</span>
<Link href="https://val.town">Val Town</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Sep -</span>
<Link href="https://dateme.directory">Date Me Directory</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Jan -</span>
<Link href="https://twitter.com/stevekrouse/status/1520162279899078657">Zaplib</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2021 Mar -</span>
<Link href="http://updates.compose.run">Compose</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2017 Jul -</span>
<Link href="https://futureofcoding.org/">Future of Coding</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2016 May -</span>
<Link href="https://github.com/stevekrouse/woofjs">WoofJS</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2015 Sep -</span>
<Link href="http://coding.space">The Coding Space Curriculum</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2015 Jul -</span>
<Link href="http://thecodingspace.com">The Coding Space</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2014 Jan -</span>Software Engineer @{" "}
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
import { api } from "https://esm.town/v/pomdtr/api";
export type Example = {
title: string;
description: string;
href: string;
resources: ExampleResource[];
preview?: string;
vals?: ExampleVal[];
};
export type ExampleResource = {
title: string;
link: string;
};
export interface ExampleVal {
author: string;
name: string;
snippets: ExampleSnippet[];
}
export interface ExampleSnippet {
text: string;
code: string;
}
export function extractSnippets(content: string): ExampleSnippet[] {
// Separate the code into snippets.
const snippets: ExampleSnippet[] = [];
let parseMode = "code";
let text = "";
let code = "";
for (const line of content.split("\n")) {
const trimmedLine = line.trim();
if (parseMode == "code") {
if (
trimmedLine.startsWith("// deno-lint-ignore")
|| trimmedLine.startsWith("//deno-lint-ignore")
|| trimmedLine.startsWith("// deno-fmt-ignore")
|| trimmedLine.startsWith("//deno-fmt-ignore")
) {
// skip deno directives
} else if (trimmedLine.startsWith("//-")) {
code += line.replace("//-", "//") + "\n";
} else if (trimmedLine.startsWith("//")) {
if (text || code.trimEnd()) {
code = code.trimEnd();
snippets.push({ text, code });
}
text = trimmedLine.slice(2).trim();
code = "";
parseMode = "comment";
} else {
code += line + "\n";
}
} else if (parseMode == "comment") {
if (
trimmedLine.startsWith("// deno-lint-ignore")
|| trimmedLine.startsWith("//deno-lint-ignore")
|| trimmedLine.startsWith("// deno-fmt-ignore")
|| trimmedLine.startsWith("//deno-fmt-ignore")
) {
// skip deno directives
} else if (trimmedLine.startsWith("//")) {
text += " " + trimmedLine.slice(2).trim();
} else {
code += line + "\n";
parseMode = "code";
}
} else if (parseMode == "file") {
if (line == "*/") {
parseMode = "code";
} else {
code += line + "\n";
}
}
}
if (text || code.trimEnd()) {
code = code.trimEnd();
snippets.push({ text, code });
}
return snippets;
}
export async function parseExample(author: string, name: string): Promise<Example> {
let { code } = await api<{ code: string }>(`/v1/alias/${author}/${name}`);
// Substitute $std/ with the full import url
code = code.replaceAll("$std/", "https://deno.land/std@0.207.0/");
// Extract the multi line JS doc comment at the top of the file
const [, jsdoc, rest] = code.match(/^\s*\/\*\*(.*?)\*\/\s*(.*)/s) || [];
// Extract the @key value pairs from the JS doc comment
let description = "";
const kv: Record<string, string> = {};
const vals = [];
const resources: ExampleResource[] = [];

Live reload in new tabs

When you're working on an HTML HTTP val in a new tab, it's annoying to have to manually reload the tab on every save. In the Val Town editor, you can hit cmd+enter, but there's nothing like that for a val in a new tab because Val Town doesn't control that new tab (like we control the iframe in the browser preview). However, you control that HTML via the fetch handler you're writing, so you can add a script that polls the Val Town API for the current version number of your val, and reload itself if it detects a new version. This val has a collection of helpers to help you do just that.

Usage

Create valimport { html } from "https://esm.town/v/stevekrouse/html"; import { reloadOnSaveFetchMiddleware } from "https://esm.town/v/stevekrouse/reloadOnSave"; export default reloadOnSaveFetchMiddleware(async function(req: Request): Promise<Response> { return html(`<h1>Hello!!</h1>`); })
Readme
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
import { createElement } from "https://esm.sh/react";
import { InjectHTMLElementStream } from "https://esm.town/v/andreterron/InjectHTMLElementStream?v=9";
import { rootValRef } from "https://esm.town/v/andreterron/rootValRef?v=3";
import { ValRef } from "https://esm.town/v/andreterron/ValRef?v=1";
import { getCurrentValVersionNumber } from "https://esm.town/v/stevekrouse/getCurrentValVersionNumber";
import { modifyResponse } from "https://esm.town/v/stevekrouse/modifyResponse";
import { parentReference } from "https://esm.town/v/stevekrouse/parentReference";
import type { MiddlewareHandler } from "npm:hono";
// this script runs client-side
export const reloadOnVals = async function(vals: ValRef[]) {
const valVersions = await Promise.all(vals.map(getCurrentValVersionNumber));
// console.log("initialValVersions: ", valVersions);
const interval = setInterval(async () => {
let newValVersions = await Promise.all(vals.map(getCurrentValVersionNumber));
// console.log("newValVersions: ", newValVersions);
if (JSON.stringify(newValVersions) !== JSON.stringify(valVersions)) {
clearInterval(interval);
window.location.reload();
}
}, 1000);
};
// experimental - don't use this
export function reloadOnSaveHonoMiddleware(vals: ValRef[] = [rootValRef()]): MiddlewareHandler {
return async function(c, next) {
await next();
// don't reload on custom domains, only reload on val.run URLs
if (!new URL(c.req.url).hostname.includes("val.run")) {
return;
}
c.res = modifyResponse(
c.res,
new InjectHTMLElementStream(ReloadScriptText(vals)),
);
};
}
/**
* @param handler http val's fetch handler
* @param vals to watch
*/
export function reloadOnSaveFetchMiddleware(
handler: (req: Request) => Response | Promise<Response>,
vals = [rootValRef()],
): (req: Request) => Promise<Response> {
return async (req: Request): Promise<Response> => {
const res = await handler(req);
// don't reload on custom domains, only reload on val.run URLs
if (!new URL(req.url).hostname.includes("val.run")) {
return res;
}
return modifyResponse(
res,
new InjectHTMLElementStream(ReloadScriptText(vals)),
);
};
}
// generates a script element as a string
export const ReloadScriptText = (vals = [rootValRef()]) =>
`<script type="module">
import { reloadOnVals } from "${import.meta.url}"
reloadOnVals(${JSON.stringify(vals)})
</script>`;
// generates a react element
export const ReloadScriptReactElement = ({ vals } = { vals: [rootValRef()] }) =>
createElement("script", {
type: "module",
dangerouslySetInnerHTML: {
__html: `import { reloadOnVals } from "${import.meta.url}";
reloadOnVals(${JSON.stringify(vals)});`,
},
});

stevekrouse.com - my personal website

This val hosts my personal website. The view data is stored in Val Town SQLite - @std/sqlite.

It used to live on Github Pages, which is why I proxy over requests to certain blog posts over to the Github Pages site still.

Todos

  • Speed up page load by loading sqlite data later like in @healeycodes/steve_web
  • Store more (legally storable) analytics data, and maybe make a sparkline!
  • Add some sort of way to contact me
  • Move over all my blog posts from Github Pages (maybe into @std/blob as a CMS?)
Readme
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 https://esm.sh/react */
import { email } from "https://esm.town/v/std/email?v=11";
import { sqlite } from "https://esm.town/v/std/sqlite?v=5";
import { ReloadScriptReactElement } from "https://esm.town/v/stevekrouse/ReloadScript";
import tailwindURL from "https://esm.town/v/stevekrouse/tailwindURL";
import { renderToString } from "npm:react-dom/server";
const linkClass = "text-blue-500 hover:underline";
const Link = (
{ children, href }: {
children?: React.ReactNode;
href: string;
},
) => <a className={linkClass} href={href}>{children}</a>;
const dateClass = "text-xs text-gray-400 font-mono mr-1 hidden sm:inline-block";
async function getHits() {
const [, , { rows: [[allHits]] }, { rows: [[todayHits]] }] = await sqlite.batch([
"CREATE TABLE IF NOT EXISTS stevekrouse_com_hits (timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)",
"INSERT INTO stevekrouse_com_hits DEFAULT VALUES",
"SELECT COUNT(*) FROM stevekrouse_com_hits where timestamp > datetime('now', '-28 day')",
"SELECT COUNT(*) from stevekrouse_com_hits where timestamp > datetime('now', '-1 day')",
]);
if (allHits % 100 === 0) email({ subject: `You got ${todayHits} hits today! (${allHits} total)` });
return { allHits, todayHits };
}
export default async (request: Request) => {
const url = new URL(request.url);
console.log(url);
if (url.pathname === "/favicon.ico") return new Response(null, { status: 404 });
if (url.pathname !== "/")
return fetch(
`https://stevekrouse.github.io/${url.pathname}${url.search}`,
request as any as RequestInit,
);
const { allHits, todayHits } = await getHits();
return new Response(
renderToString(
<html>
<head>
<title>Steve Krouse</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<script src={tailwindURL} />
{url.searchParams.get("dev")
? <ReloadScriptReactElement vals={[{ valName: "dot_com", userHandle: "stevekrouse" }]} />
: null}
</head>
<body>
<div className="max-w-3xl p-10 space-y-4 mx-auto">
<h1 className="text-orange-600 text-2xl font-medium">Steve Krouse</h1>
<div>
πŸ‘‹ Hi, I'm Steve. I live in Prospect Heights, Brooklyn.
</div>
<div>
I build <Link href="https://val.town">Val Town</Link>, a social website to code in the cloud.
</div>
<div>
This site was{" "}
<Link href="https://www.val.town/v/stevekrouse/dot_com">built in Val Town</Link>. It was viewed{" "}
{todayHits} times today, and {allHits} times this month.
</div>
<div>
<h2 className="text-xl mb-1">Projects</h2>
<ul>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Jul -</span>
<Link href="https://val.town">Val Town</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Sep -</span>
<Link href="https://dateme.directory">Date Me Directory</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2022 Jan -</span>
<Link href="https://twitter.com/stevekrouse/status/1520162279899078657">Zaplib</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2021 Mar -</span>
<Link href="http://updates.compose.run">Compose</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2017 Jul -</span>
<Link href="https://futureofcoding.org/">Future of Coding</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2016 May -</span>
<Link href="https://github.com/stevekrouse/woofjs">WoofJS</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2015 Sep -</span>
<Link href="http://coding.space">The Coding Space Curriculum</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2015 Jul -</span>
<Link href="http://thecodingspace.com">The Coding Space</Link>
</li>
<li className="pb-2 sm:pb-1">
<span className={dateClass}>2014 Jan -</span>Software Engineer @{" "}
<Link href="https://looker.com/">Looker</Link>

Server-side Render React Mini Framework

This is very experimental, more of a prototype of an architecture, than a true framework

Example: https://www.val.town/v/stevekrouse/TodoApp

Readme
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
/** @jsxImportSource https://esm.sh/react */
import { renderToString } from "https://esm.sh/react-dom@18.2.0/server";
import { useEffect, useState } from "https://esm.sh/react@18.2.0";
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo?v=25";
import { html } from "https://esm.town/v/stevekrouse/html";
// button that's disabled until client react hydrates
export const Button = (props) => {
const [clientHydrated, setClientHydrated] = useState(false);
useEffect(() => setClientHydrated(true), []);
return <button disabled={!clientHydrated} {...props}></button>;
};
export const Form = ({ onSubmit, ...props }: any) => {
const [clientHydrated, setClientHydrated] = useState(false);
useEffect(() => setClientHydrated(true), []);
return (
<form
disabled={!clientHydrated}
onSubmit={async (e) => {
e.preventDefault();
const onData = onSubmit ? onSubmit(e) : () => 1;
const formData = new FormData(e.target as any);
const resp = await fetch("/", {
method: props.action ?? "POST",
body: formData,
});
await onData(resp);
}}
{...props}
>
</form>
);
};
export const hydrate = (importMetaURL: string) =>
async function(req: Request): Promise<Response> {
const { author, name } = extractValInfo(importMetaURL);
const valURL = `https://www.val.town/v/${author}/${name}`;
const moduleURL = `https://esm.town/v/${author}/${name}`;
const exports = await import(moduleURL);
const action = exports.action ?? (() => ({}));
const loader = exports.loader ?? (() => ({}));
const Component = exports.Component ?? (() => (
<div>
Error: Please ensure you <code>export Component</code>" in{" "}
<a target="_blank" href={valURL}>@{author}/{name}</a>.
</div>
));
if (req.method === "GET") {
const props = await loader(req);
const script = `
import { hydrateRoot } from "https://esm.sh/react-dom@18.2.0/client";
import { jsx as _jsx } from "https://esm.sh/react@18.2.0/jsx-runtime";
import { Component } from "https://esm.town/v/${author}/${name}";
let props = ${JSON.stringify(props)}
try {
hydrateRoot(document, _jsx(Component, props));
} catch (e) {
console.error(e)
}
`;
return html(renderToString(
<>
<Component {...props} />
<script type="module" dangerouslySetInnerHTML={{ __html: script }} />
</>,
));
} else {
return action(req);
}
};
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
/** @jsxImportSource https://esm.sh/react */
import { generateLessonHtml } from "https://esm.town/v/petermillspaugh/lessonTemplate";
import { renderToString } from "npm:react-dom/server";
/*
* Note: this lesson is a work in progress πŸ‘·β€β™‚οΈ
*/
const TITLE = "Generation";
const FILL_BLANK = <>TODO</>;
const CONTENT = (
<>
<h2>Generation</h2>
<p>
Generation is when you attempt to solve a problem before being presented with the solution or relevant knowledge.
Generation produces more durable learning.
</p>
<p>Trying to solve a problem before you know the solution is an effective technique.</p>
<p>Desirable difficulties.</p>
<p>
This course uses generation in the pre-lesson exercise.
</p>
<p>
Learning is more durable when it’s effortful. Josh Comeau uses this tactic in his{" "}
<a href="https://www.joshwcomeau.com/courses/">courses</a>, challenging students to solve a coding exercise before
he introduces the relevant content.
</p>
<h2>The case for writing</h2>
<p>
The act of writing allows you to elaborate and reflect on what you've learned (or know), often forging new
connections between related mental models. It often includes retrieval practice as you recall the information
while translating your thoughts to paper. Writing can also expose gaps and fuzzy spots in your knowledge, forcing
you to refine what you know.
</p>
<p>
Chapter 4 of <em>Make It Stick</em>{" "}
on embracing difficulties includes the story of "The Blundering Gardener" Bonnie Blodgett who became an expert
gardener by getting her hands dirty and learning things one at a time by trial and error. Bonnie also writes
extensively about her gardening in a quarterly called <em>The Garden Letter</em>. The book notes that "in{" "}
<em>writing</em>{" "}
about her experiences, Bonnie is engaging in two potent learning processes beyond the act of gardening itself. She
is retrieving the details and story of what she has discoveredβ€”say, about an experiment in grafting two species of
fruit treesβ€”and then she is elaborating by explainging the experience to her readers, connecting the outcome to
what she already knows about the subject or has learned as a result."
</p>
</>
);
const QUIZ = [
{
question: "",
answer: "",
},
];
export const generation = {
title: TITLE,
fillBlank: FILL_BLANK,
quiz: QUIZ,
fetchHtml: (email: string, lesson: number) =>
generateLessonHtml({
email,
lesson,
title: `Lesson ${lesson + 1}: ${TITLE}`,
fillBlank: FILL_BLANK,
content: CONTENT,
quiz: QUIZ,
}),
};
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
/** @jsxImportSource https://esm.sh/react */
import { generateLessonHtml } from "https://esm.town/v/petermillspaugh/lessonTemplate";
import { renderToString } from "npm:react-dom/server";
/*
* Note: this lesson is a work in progress πŸ‘·β€β™‚οΈ
*/
const TITLE = "Varied practice";
const FILL_BLANK = (
<>
Two basketball players want to improve their 3-point shooting with a weekly routine. The first player takes 100
shots from a different location on the court each day Monday to Friday (e.g. 100 shots from the left corner on the
first day, 100 shots from the wing on the second day, 100 shots from the top of the arc on the third day, and so
on). The second player takes 20 shots from 5 locations{" "}
<em>every day</em>. So both players take 100 shots per day, totaling 500 shots each week. Which routine do you think
would be more effective?
</>
);
const CONTENT = (
<>
<h2>Varied practice versus massed practice</h2>
<p>
Is massed practice–focusing study on one topic for a concentrated time period–effective? No. Learning gains are
transitory.
</p>
<p>
Varied practice is more effective than massed practice and is consolidated in a different part of the brain that
encodes higher-order, more flexible learning.
</p>
<h2>Varied practice in sports</h2>
<p>TODO: beanbag toss experiment</p>
<p>TODO: basketball example</p>
<h2>Flexible memory storage in your brain</h2>
<p>TODO</p>
</>
);
const QUIZ = [
{
question: "",
answer: "",
},
];
export const variedPractice = {
title: TITLE,
fillBlank: FILL_BLANK,
quiz: QUIZ,
fetchHtml: (email: string, lesson: number) =>
generateLessonHtml({
email,
lesson,
title: `Lesson ${lesson + 1}: ${TITLE}`,
fillBlank: FILL_BLANK,
content: CONTENT,
quiz: QUIZ,
}),
};
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
export default async function untitled_jadeCarp(req: Request): Promise<Response> {
return Response.json(
[
{
"id": 1,
"name": "The Shawshank Redemption",
"description":
"Andy Dufresne, a banker convicted of murdering his wife and sentenced to life imprisonment at Shawshank State Penitentiary, maintains his innocence and begins a journey of hope and redemption.",
"rating": 8.9,
"image": "https://m.media-amazon.com/images/M/MV5BMTU4NzAyNTQ1OV5BMl5BanBnXkFtZTcwNTI1OTYzNzM@.jpg",
},
{
"id": 2,
"name": "The Godfather",
"description":
"The story of the Corleone family under patriarch Vito Corleone, focusing on the transformation of his youngest son, Michael, from reluctant family outsider to ruthless mafia boss.",
"rating": 9.2,
"image": "https://m.media-amazon.com/images/M/MV5BMTUyNjQwNzEtZDIzMC00ODc3LWI4MTgtNjQwOTQ5Mjc1M2E@.jpg",
},
{
"id": 3,
"name": "The Dark Knight",
"description":
"Batman raises his stakes in his war on crime when he targets the enigmatic criminal mastermind known as the Joker, a terrorist causing chaos in Gotham City.",
"rating": 9.0,
"image": "https://m.media-amazon.com/images/M/MV5BNDYxMzUxMTctNmRjOC00ZjFhLWFmNTEtNDJkNzZlMGM5MzQ@.jpg",
},
{
"id": 4,
"name": "12 Angry Men",
"description": "A jury in New York City deliberates the fate of a young man accused of murdering his father.",
"rating": 8.9,
"image": "https://m.media-amazon.com/images/M/MV5BMTQ0ODc4MDU4NF5BMl5BanBnXkFtZTgwNTY2NDEyNDE@.jpg",
},
{
"id": 5,
"name": "The Lord of the Rings: The Return of the King",
"description":
"Gandalf and Aragorn lead the Fellowship of the Ring to the slopes of Mount Doom to destroy the One Ring and defeat the Dark Lord Sauron.",
"rating": 8.9,
"image": "https://m.media-amazon.com/images/M/MV5BMjEyOTU3NzgxOTBeQTJeQWpwZ15BbWUuOTk5OTk3ODg2OTk@.jpg",
},
{
"id": 6,
"name": "The Dark Knight Rises",
"description":
"Batman, Bane, and Catwoman form an alliance to save Gotham City from the clutches of the villainous Lex Luthor and his army of mutated creatures.",
"rating": 8.4,
"image": "https://m.media-amazon.com/images/M/MV5BMTQ2NTQ5ODUwNTNeQTJeQWpwZ15BbWUuNTU5OTk2NzQ3OTg@.jpg",
},
{
"id": 7,
"name": "Schindler's List",
"description":
"The true story of Oskar Schindler, a German businessman who saved the lives of over 1,000 Jews during the Holocaust.",
"rating": 8.9,
"image": "https://m.media-amazon.com/images/M/MV5BMDU0OTg2NDcxNTVeQTJeQWpwZ15BbWUuMjM4MDUwNTQ0OTg@.jpg",
},
{
"id": 8,
"name": "Pulp Fiction",
"description":
"A series of interconnected and often violent stories in Los Angeles, told in a non-linear fashion.",
"rating": 8.9,
"image":
"https://m.media-amazon.com/images/M/MV5BMTYwNjAxOTQtNDQ4Mi00NDg3LWE1OWUtODMyYjU4NDZlNzQyXkEyXkFqcGdeQXVyMjMwOTEyMzE0@.jpg",
},
],
);
}

https://adventofcode.com/2023/day/8

the script times out on val.town, so I ran it with deno on my machine for some mintes.

By running

deno run https://esm.town/v/karfau/aoc23_08 | grep z

I observed a repeating pattern in the output...

each "track" was reaching the same position over and over again, but not on the same lines.

I dumped the beginning of the output into a text file, opened it to look at the patterns and confirmed that each "track" had a fixed frequency for each position ending on a Z.

so I took the "step"(+1 because it's zero based) and checked the primes each of them contains, to get the correct input. I calculated that with a calculator and copied in the number.

Readme
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
import { expect } from "https://esm.town/v/karfau/chai";
expect(
firstStar(`RL
AAA = (BBB, CCC)
BBB = (DDD, EEE)
CCC = (ZZZ, GGG)
DDD = (DDD, DDD)
EEE = (EEE, EEE)
GGG = (GGG, GGG)
ZZZ = (ZZZ, ZZZ)`),
"*1 sample 1",
).to.equal(2);
expect(
firstStar(`LLR
AAA = (BBB, BBB)
BBB = (AAA, ZZZ)
ZZZ = (ZZZ, ZZZ)`),
"*1 sample 2",
).to.equal(6);
function firstStar(input: string) {
const [directions, ...mapRaw] = input.split("\n").filter((line) => Boolean(line.trim()));
const map = Object.fromEntries(mapRaw.map(line => {
const [key, L, R] = line.match(/[A-Z]{3}/g);
return [key, { L, R }];
}));
// debug(map, "map");
let step = 0, position = "AAA";
while (position !== "ZZZ") {
const dir = directions[step % directions.length];
position = map[position][dir];
if (!dir || !position) {
return -1;
}
step++;
}
return step;
}
// as as soon as the assertions pass, the solution is calculated
console.log("solution *1:", firstStar(input()));
expect(
secondStar(`LR
11A = (11B, XXX)
11B = (XXX, 11Z)
11Z = (11B, XXX)
22A = (22B, XXX)
22B = (22C, 22C)
22C = (22Z, 22Z)
22Z = (22B, 22B)
XXX = (XXX, XXX)`),
"*2 sample 1",
).to.equal(6);
// expect(secondStar(``), "*2 sample 2").to.equal("?");
function secondStar(input: string) {
const [directions, ...mapRaw] = input.split("\n").filter((line) => Boolean(line.trim()));
const map = Object.fromEntries(mapRaw.map(line => {
const [key, L, R] = line.match(/[0-9A-Z]{3}/g);
return [key, { L, R }];
}));
debug(map, "map");
let step = 0, positions = debug(Object.keys(map).filter(key => key.endsWith("A"))), reached = 6;
const notWithZ = pos => !pos.endsWith("Z");
while (positions.some(notWithZ)) {
const dir = directions[step % directions.length];
const id = `${positions.join(",")}->${dir}`;
positions = positions.map(position => map[position][dir]);
const left = positions.filter(notWithZ).length;
left <= reached && debug(positions, `${step} ${dir} ${left}`);
reached = Math.min(reached, left);
if (!dir) {
return -step;
}
step++;
}
return step;
}
console.log("solution *2:", secondStar(input()));
function input() {
return `LLLRRLRRRLLRRLRRLLRLRRLRRRLRRLRRLRRRLRLRRLRLRRLRRLLRRLRLLRRLLLRRRLRRLRLRLRRRLRLLRRLRRRLRRLRRLRRLRLLRLLRRLRRRLRRLRLRRLRRRLRRLLRLLRRLRRRLLRRRLRLRRRLLRLRRLRRLLRRLRRLLLRRRLRLRRRLRRLLRLRRLRLLRRRLRLRLLRLRRRLRLRRRLRRLRLRLLRLRRRLRRLRRRLRRRLRLRRRLRRRLLLLR
FSH = (CGN, NDK)
LQT = (NSK, XBG)
LCP = (QQB, NTB)
DFG = (KTV, NJR)
MCC = (TRF, NHH)
PHG = (VMX, SHB)
SMP = (MKD, TBS)
MRT = (NJX, HHJ)
LNG = (KRF, VDK)
TKG = (VLM, XFQ)
QCR = (BDL, VKQ)
PQC = (CXK, HKS)
Fork
1
2
// set at Thu Nov 30 2023 14:22:53 GMT+0000 (Coordinated Universal Time)
export let topHNThreadByHour = ["Top thread on Hackernews for 3:00 is: Vespa.ai is spinning out of Yahoo as a separate company","Top thread on Hackernews for 4:00 is: President Speaking: Spoofing Alerts in 4G LTE Networks (2019) [pdf]","Top thread on Hacke
1
export let primesHelper = (primes, current, limit) => current === limit ? primes : primes.some(p => current % p === 0) ? primesHelper(primes, current + 1, limit) : primesHelper([...primes, current], current+1, limit)
1
2
3
4
5
6
import { runVal } from "https://esm.town/v/std/runVal";
export const rimeAPIEx = await runVal("stevekrouse.rime", {
text: "hello friend",
speaker: "young_female_latina-4",
});
1
2
3
4
// set by stevekrouse.rimeRateLimitExceeded at 2023-06-27T15:23:13.552Z
export let rimeUsage = {
"6/27/2023": 29
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
export let largestFact = 0;
const numm = 600851475143;
let newnumm = numm;
let counter = 2;
while (counter * counter <= newnumm) {
if (newnumm % counter == 0) {
newnumm = newnumm / counter;
largestFact = counter;
} else {
counter++;
}
}
if (newnumm > largestFact) { // the remainder is a prime number
largestFact = newnumm;
}