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
/** @jsxImportSource https://esm.sh/react */
const tabs = { "/home": "Home", "/browse": "Browse", "/faq": "FAQ" };
export default function({ activeTab, children }) {
return (
<html>
<head>
<title>Date Me Directory</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://cdn.tailwindcss.com" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/quicklink/2.3.0/quicklink.umd.js"></script>
<script
dangerouslySetInnerHTML={{
__html: `window.addEventListener('load', () => {
quicklink.listen();
});`,
}}
>
</script>
</head>
<body>
<div className="p-4 gap-x-4 flex items-center">
{Object.entries(tabs).map(([link, title]) => (
<a
href={link}
key={link}
className={`${activeTab === link ? "" : "text-gray-400 hover:text-gray-500"}`}
>
{title === "Home" ? <div className="font-bold text-black">Date Me Directory</div> : title}
</a>
))}
<a
href="https://noteforms.com/forms/submit-your-date-me-profile"
className="px-2 py-1 border-blue-500 text-blue-500 border border-2 hover:bg-blue-100 rounded-md font-bold rounded ml-4"
>
Submit <span className="hidden md:inline">your doc</span>
</a>
</div>
{children}
</body>
</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
/** @jsxImportSource https://esm.sh/react */
import Layout from "https://esm.town/v/stevekrouse/dateme_layout";
import { Action, handler } from "https://esm.town/v/pomdtr/ssr";
import { sqlite } from "https://esm.town/v/std/sqlite?v=4";
import { zip } from "npm:lodash-es";
export const action: Action = async (req: Request, ctx) => {
const url = new URL(req.url);
const search = url.searchParams;
let { columns, rows } = await sqlite.execute({
sql: `select * from datemedocs
where
Location like ?
and Age >= ?
and Age <= ?
and Gender like ?
and InterestedIn like ?
`,
args: [
"%" + (search.get("location") ?? "") + "%",
search.get("min-age") ?? 0,
search.get("max-age") ?? 100,
"%" + (search.get("desired-gender") ?? "") + "%",
"%" + (search.get("gender") ?? "") + "%",
],
});
const profiles = rows.map(row =>
Object.fromEntries(zip(
columns,
row.map(d => {
try {
return JSON.parse(d);
} catch (_) {
return d;
}
}),
))
);
return ctx.render({
search: {
gender: search.get("gender"),
desiredGender: search.get("desired-gender"),
minAge: search.get("min-age") ?? 0,
maxAge: search.get("max-age") ?? 100,
location: search.get("location"),
},
profiles,
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
/** @jsxImportSource https://esm.sh/react */
import { handler } from "https://esm.town/v/pomdtr/ssr";
import date_me_doc_locations from "https://esm.town/v/stevekrouse/date_me_doc_locations";
import Layout from "https://esm.town/v/stevekrouse/dateme_layout";
export function Home(props) {
return (
<Layout activeTab={props.route}>
<div className="max-w-lg mx-auto p-10">
<div className="mb-10">
<div className="text-4xl font-bold mb-4">
Date thoughtfully
</div>
<div className="text-lg ">
Date Me Docs are long-form, earnest dating profiles for romantic partners.
</div>
</div>
<form className="space-y-4" action="/browse">
<div className="flex text-xl space-x-2">
<div>I'm</div>
<select name="gender" className="border border-gray-300">
<option value="" className="text-gray-500">...</option>
<option value="m">male</option>
<option value="f">female</option>
<option value="nb">non-binary</option>
</select>
</div>
<div className="flex text-xl space-x-2">
<div>Looking for</div>
<select name="desired-gender" className="border border-gray-300">
<option value="">...</option>
<option value="m">male</option>
<option value="f">female</option>
<option value="nb">non-binary</option>
</select>
</div>
<div className="flex text-xl space-x-2">
<div>Between</div>
<input defaultValue={18} type="number" name="min-age" className="w-12 h-min border border-gray-300"></input>
<div>and</div>
<input defaultValue={70} type="number" name="max-age" className="w-12 border border-gray-300"></input>
<div>years old</div>
</div>
<div className="flex text-xl space-x-2">
<div>In</div>
<select name="location" className="border border-gray-300">
<option value="">...</option>
{date_me_doc_locations.map(
(location) => <option key={location} value={location}>{location}</option>,

"Code on Val Town" Response modifier

Appends the "Code on Val Town" ribbon to an HTTP Response.

Usage

  • new modifyResponse(res, { handle: "andre", name: "foo" }) - define which val to link to;
  • new modifyResponse(res) - infer the val from the call stack.

Example: @andreterron/openable_res

import { modifyResponse } from "https://esm.town/v/andreterron/codeOnVT_modifyResponse";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";

export default async (req: Request): Promise<Response> => {
  return modifyResponse(html(`
    <h2>Hello world!</h2>
    <style>* { font-family: sans-serif }</style>
  `));
};
Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { InjectCodeOnValTownStream } from "https://esm.town/v/andreterron/InjectCodeOnValTownStream";
import { ValRef } from "https://esm.town/v/andreterron/ValRef";
/**
* @param res Response
* @param val Define which val should open. Defaults to the root reference.
*/
export function modifyResponse(
res: Response,
val?: ValRef,
) {
if (res.headers.get("Content-Type") !== "text/html") {
return res;
}
return new Response(
res.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(new InjectCodeOnValTownStream(val))
.pipeThrough(new TextEncoderStream()),
res,
);
}

Injects the "Code on Val Town" ribbon on an HTML string stream

Usage

  • new InjectCodeOnValTownStream({ handle: "andre", name: "foo" }) - define which val to link to;
  • new InjectCodeOnValTownStream() - infer the val from the call stack.

Example: @andreterron/openable_stream

import { InjectCodeOnValTownStream } from "https://esm.town/v/andreterron/InjectCodeOnValTownStream";
import { blob } from "https://esm.town/v/std/blob?v=11";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";

export default async (req: Request): Promise<Response> => {
  await blob.set(
    "openable_test",
    `<h2>Hello world!</h2>
    <style>* { font-family: sans-serif }</style>`,
  );

  const value = await blob.get("openable_test");

  return html(
    value.body
      .pipeThrough(new TextDecoderStream())
      .pipeThrough(new InjectCodeOnValTownStream())
      .pipeThrough(new TextEncoderStream()),
  );
};
Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { ribbonElement } from "https://esm.town/v/andreterron/codeOnVT_ribbonElement";
import { InjectHTMLElementStream } from "https://esm.town/v/andreterron/InjectHTMLElementStream";
import { rootValRef } from "https://esm.town/v/andreterron/rootValRef";
import { ValRef } from "https://esm.town/v/andreterron/ValRef";
/**
* @param val Define which val should open. Defaults to the root reference.
*/
export class InjectCodeOnValTownStream extends InjectHTMLElementStream {
constructor(val?: ValRef) {
const valRef = val?.handle && val?.name ? val : rootValRef();
const element = valRef ? ribbonElement(valRef) : "";
super(element);
}
}

"Code on Val Town" Ribbon HTML Element

Ribbon element used by @andreterron/codeOnValTown

Usage

  • ribbonElement({ handle: "andre", name: "foo" }) - define which val to link to;
  • ribbonElement() - infer the val from the call stack.

Example: @andreterron/openable_element

import { ribbonElement } from "https://esm.town/v/andreterron/codeOnVT_ribbonElement";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";

export default async (req: Request): Promise<Response> => {
  return html(`
    <h2>Hello world!</h2>
    <style>* { font-family: sans-serif }</style>
    ${ribbonElement()}
  `);
};
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
import { rootValRef } from "https://esm.town/v/andreterron/rootValRef";
import { ValRef } from "https://esm.town/v/andreterron/ValRef";
export function ribbonElement(val?: ValRef) {
const valRef = val?.handle && val?.name ? val : rootValRef();
if (!valRef) {
console.error("Failed to infer val. Please set the val parameter to the desired `{ handle, name }`");
return "";
}
const valSlug = `${valRef.handle}/${valRef.name}`;
return `
<div id="code-on-vt-host">
<template shadowrootmode="open">
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.2.3/gh-fork-ribbon.min.css"
/>
<a
href="https://www.val.town/v/${valSlug}"
target="_blank"
class="github-fork-ribbon"
data-ribbon="Code on Val Town"
title="Code on Val Town"
>
Code on Val Town
</a>
</template>
</div>`;
}
1
2
3
4
5
6
7
8
9
10
11
12
import { rootRef } from "https://esm.town/v/andreterron/rootRef";
import { ValRef } from "https://esm.town/v/andreterron/ValRef";
export function rootValRef(): ValRef | undefined {
const val = rootRef();
return val
? {
handle: val.userHandle,
name: val.valName,
}
: undefined;
}
1
2
3
4
export interface ValRef {
handle: string;
name: string;
}

Code on Val Town

306790319-d5b13560-7b3a-458a-98c9-1696ef40d972.png

Tell your website visitors about your val! This val is a fetch handler middleware. It inserts a "Code on Val Town" ribbon that links to the val itself. Your website visitors will be able to view your code, fork the val, and suggest changes with pull requests!

Usage

There are 5 different ways to add the "Code on Val Town" ribbon!

1. Use the element string

import { ribbonElement } from "https://esm.town/v/andreterron/codeOnValTown";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";

export default async (req: Request): Promise<Response> => {
  return html(`
    <h2>Hello world!</h2>
    <style>* { font-family: sans-serif }</style>
    ${ribbonElement()}
  `);
};

2. Wrap your HTML string

import { htmlString } from "https://esm.town/v/andreterron/codeOnValTown";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";

export default async (req: Request): Promise<Response> => {
  return html(htmlString(`
    <h2>Hello world!</h2>
    <style>* { font-family: sans-serif }</style>
  `));
};

3. Pipe your HTML stream through our injector

// TODO

4. Wrap your response

import { modifyResponse } from "https://esm.town/v/andreterron/codeOnValTown";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";

export default async (req: Request): Promise<Response> => {
  return modifyResponse(html(`
    <h2>Hello world!</h2>
    <style>* { font-family: sans-serif }</style>
  `));
};

5. Wrap your fetch handler

Check out @andreterron/openable for an example!

import { modifyFetchHandler } from "https://esm.town/v/andreterron/codeOnValTown";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";

export default modifyFetchHandler(async (req: Request): Promise<Response> => {
  return html(`
    <h2>Hello world!</h2>
    <style>* { font-family: sans-serif }</style>
  `);
});
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
import { inferRequestVal } from "https://esm.town/v/andreterron/inferRequestVal";
import { InjectHTMLElementStream } from "https://esm.town/v/andreterron/InjectHTMLElementStream";
import { rootRef } from "https://esm.town/v/andreterron/rootRef";
export function ribbonElement(val?: ValRef) {
const valRef = val?.handle && val?.name ? val : rootValRef();
if (!valRef) {
console.error("Failed to infer val. Please set the val parameter to the desired `{ handle, name }`");
return "";
}
const valSlug = `${valRef.handle}/${valRef.name}`;
return `
<div id="code-on-vt-host">
<template shadowrootmode="open">
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.2.3/gh-fork-ribbon.min.css"
/>
<a
href="https://www.val.town/v/${valSlug}"
target="_blank"
class="github-fork-ribbon"
data-ribbon="Code on Val Town"
title="Code on Val Town"
>
Code on Val Town
</a>
</template>
</div>`;
}
/**
* @param bodyText HTML string that will be used to inject the element.
* @param options.val Define which val should open. Defaults to the root reference.
*/
export function htmlString(
bodyText: string,
options: {
val?: {
handle: string;
name: string;
};
} = {},
) {
const val = options.val?.handle && options.val?.name ? options.val : rootValRef();
if (!val) {
console.error("Failed to infer val. Please set the options.val parameter to the desired `{ handle, name }`");
1
2
3
4
5
6
7
8
9
10
11
12
import { hydrateRoot } from "https://esm.sh/react-dom@18.2.0/client";
import React from "https://esm.sh/react@18.2.0";
import { jsx as _jsx } from "https://esm.sh/react@18.2.0/jsx-runtime";
const islands = Array.from(document.querySelectorAll("[data-hydration-val]"));
for (const island of islands) {
const slug = island.getAttribute("data-hydration-val");
console.log(`hydrating ${slug}`);
const { default: Component } = await import(`https://esm.town/v/${slug}`);
const props = JSON.parse(island.getAttribute("data-hydration-props"));
hydrateRoot(island, _jsx(Component, props));
}