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
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
1export default async function (interval: Interval) {
2
3}
1export default async function server(request: Request): Promise<Response> {
2 const { email } = await import("https://esm.town/v/std/email");
3
60 }
61
62 // Rest of the existing server function remains the same
63}
34};
35
36function ProductCard({ product }) {
37 return (
38 <div className="product-card">
45}
46
47function App() {
48 const [gender, setGender] = useState('male');
49
90}
91
92function client() {
93 createRoot(document.getElementById("root")).render(<App />);
94}
95if (typeof document !== "undefined") { client(); }
96
97export default async function server(request: Request): Promise<Response> {
98 return new Response(`
99 <html>
46- Key: `mentionsDiscord`
47- Value: Your Discord webhook URL.
48Notifications will be sent using this function:
49
50```ts
63
64- **Proxies via Val Town's [SocialDataProxy](https://www.val.town/v/stevekrouse/socialDataProxy)**: Limited to 10 cents per day for [**Val Town Pro users**](https://www.val.town/pricing). This API is *only* for Pro users.
65- **Need more calls?** Sign up for your own [SocialData API token](https://socialdata.tools) and configure the [`socialDataSearch`](https://www.val.town/v/stevekrouse/socialDataSearch) function.
12const isProd = false;
13
14export async function twitterAlert({ lastRunAt }: Interval) {
15 // If isProd, search for tweets since that last time this interval ran
16 // if not, search for tweets since 48 hours ago for testing
3import React, { useEffect, useState } from "https://esm.sh/react@18.2.0";
4
5function App() {
6 const [works, setWorks] = useState([]);
7 const [newWork, setNewWork] = useState({
141}
142
143function client() {
144 createRoot(document.getElementById("root")).render(<App />);
145}
146if (typeof document !== "undefined") { client(); }
147
148export default async function server(request: Request): Promise<Response> {
149 const { sqlite } = await import("https://esm.town/v/stevekrouse/sqlite");
150 const { blob } = await import("https://esm.town/v/std/blob");
106];
107
108function App() {
109 const [currentQuestion, setCurrentQuestion] = useState(0);
110 const [selectedAnswers, setSelectedAnswers] = useState(new Array(QUIZ_QUESTIONS.length).fill(null));
217}
218
219function client() {
220 createRoot(document.getElementById("root")).render(<App />);
221}
222if (typeof document !== "undefined") { client(); }
223
224export default function server(request: Request): Response {
225 return new Response(`
226 <html>
15];
16
17function App() {
18 const [noClicks, setNoClicks] = useState(0);
19 const [isValentine, setIsValentine] = useState(false);
121}
122
123function client() {
124 createRoot(document.getElementById("root")).render(<App />);
125}
126if (typeof document !== "undefined") { client(); }
127
128export default async function server(request: Request): Promise<Response> {
129 return new Response(
130 `