33- [x] fix wonky sidebar separator height problem (thanks to @stevekrouse)
34- [x] make result tables scrollable
35- [x] add export to CSV, and JSON (CSV and JSON helper functions written in [this val](https://www.val.town/v/nbbaier/sqliteExportHelpers). Thanks to @pomdtr for merging the initial version!)
36- [x] add listener for cmd+enter to submit query
2import { easyAQI } from "https://esm.town/v/stevekrouse/easyAQI?v=5";
3
4export async function aqi(interval: Interval) {
5 const location = "downtown brooklyn"; // <-- change to place, city, or zip code
6 const data = await easyAQI({ location });
2import { render } from "npm:preact-render-to-string";
3
4export function it(name, fn) {
5 const ok = "✅ [OK]: ";
6 const fail = "❌ [FAIL]: ";
15}
16
17export function describe(name, run) {
18 console.log(name);
19 return async function(req: Request): Promise<Response> {
20 await run();
21 return Response.json({ ok: true, status: 200 });
34};
35
36function Badge({ status }: BadgeProps) {
37 const color = colors[status];
38 const label = status ? status.toUpperCase() : "N/A";
68}
69
70export default async function(req: Request): Promise<Response> {
71 const params = new URL(req.url).searchParams;
72 const valURL = params.get("url");
35}
36
37function App() {
38 const [texts, setTexts] = useState<TextEntry[]>([]);
39 const [modalOpen, setModalOpen] = useState(false);
388}
389
390function client() {
391 createRoot(document.getElementById("root")!).render(<App />);
392}
396}
397
398async function server(request: Request): Promise<Response> {
399 const { sqlite } = await import("https://esm.town/v/stevekrouse/sqlite");
400 const SCHEMA_VERSION = 3;
8import _ from "npm:lodash@4";
9
10async function main(req: Request): Promise<Response> {
11 const { readable, writable } = new TransformStream();
12 const writer = writable.getWriter();
20 <script>
21 // Scroll to the bottom of the page when the page changes.
22 (new MutationObserver(function (mutationsList, observer) {
23 window.scrollTo({ left: 0, top: document.body.scrollHeight, behavior: "instant" });
24 })).observe(document, { childList: true, characterData: true, subtree: true });
47 Your response should start with \`\`\`ts and end with \`\`\`, so full code fences.
48 There should be no comments like "more content here", it should be complete and directly runnable.
49 The val should have an "export default async function main". The val should return a valid HTML website.
50 Prefer using Tailwind. Put frontend functions in a <script> tag, using dangerouslySetInnerHTML. Don't use Hono. Don't use Response.redirect.
51 `.replace("\n", " "),
52 },
31// // Write Data
32// // ---------
33// function addTodo(text) {
34// db.transact([tx.messages[id()].update({ text: 'what' })]);
35// // db.transact(
42// }
43
44// function deleteTodoItem(message) {
45// db.transact(tx.messages[message.id].delete());
46// }
47
48// function toggleDone(message) {
49// db.transact(tx.messages[message.id].update({ done: !message.done }));
50// }
51
52// function deleteCompleted(messages) {
53// const completed = messages.filter((message) => message.done);
54// const txs = completed.map((message) => tx.messages[message.id].delete());
56// }
57
58// function toggleAllTodos(messages) {
59// const newVal = !messages.every((message) => message.done);
60// db.transact(messages.map((message) => tx.messages[message.id].update({ done: newVal })));
62
63
64// function query(q = { messages: {} }) {
65// const { isLoading, error, data } = db.subscribeQuery({ messages: {} });
66// console.log('data', data);
68
69
70// function renderError(error) {
71// console.error(error);
72// }
73
74// function render(data) {
75// console.log(data);
76// }
21}
22
23function App() {
24 const [templates, setTemplates] = useState<MemeTemplate[]>([]);
25 const [selectedTemplate, setSelectedTemplate] = useState<string>("");
94 const [topPosition, bottomPosition] = getTextPositions(image.width, image.height, selectedTemplate);
95
96 // Function to draw text with word wrap
97 const drawText = (text: string, position: TextPosition) => {
98 ctx.fillStyle = position.color;
172}
173
174function client() {
175 createRoot(document.getElementById("root")).render(<App />);
176}
178if (typeof document !== "undefined") { client(); }
179
180async function server(request: Request): Promise<Response> {
181 const url = new URL(request.url);
182
3import { newRSSItems } from "https://esm.town/v/stevekrouse/newRSSItems";
4
5export async function pollRSSFeeds({ lastRunAt }: Interval) {
6 return Promise.all(
7 Object.entries(rssFeeds).map(async ([name, url]) => {
2import { easyAQI } from "https://esm.town/v/stevekrouse/easyAQI?v=5";
3
4export async function aqi(interval: Interval) {
5 const location = "Camden, London"; // <-- change to place, city, or zip code
6 const data = await easyAQI({ location });
2import { renderToString } from "npm:react-dom/server";
3
4export default async function(req: Request) {
5 return new Response(
6 renderToString(