An infinite meme machine for 'They can put a man on the moon but they can't cure the common cold' one-liners, generated live.
---
format: typebulb/v1
name: "They Can But They Can't"
---
**code.tsx**
```tsx
import React, { useState, useEffect, useRef, useCallback } from "react"
import { createRoot } from "react-dom/client"
type Joke = { they?: string; basis?: string; setup: string; punch: string }
const SEED = tb.json<Joke[]>(0)
const BATCH = 8
const TOPICS = [
{
id: "ai",
label: "🤖 AI",
hint: "frontier AI models: superhuman at one thing, useless at something trivial",
who: "ONE AI model (or the lab that ships it). The same model must do both halves: the thing that wins the medal is the thing that fumbles the trivial task. Nothing about printers, wifi, airlines or plumbing — an AI lab is not responsible for those.",
},
{
id: "science",
label: "🔬 Science",
hint: "big science and engineering feats vs. mundane unsolved annoyances",
who: "'they' = science and modern civilisation as a whole — the classic vague 'they' of the original moon/common-cold line. Both halves must sit at that same civilisational scale.",
},
{
id: "gadgets",
label: "📱 Gadgets",
hint: "consumer tech: phones, smart devices, wifi, bluetooth, printers",
who: "'they' = the consumer-tech industry that designs and ships the devices. The feat must be theirs (a phone, a chip, a car, an OS) and so must the failure — not a hospital's or an airline's.",
},
]
const SYSTEM = `You are a deadpan joke machine that writes one-liners in the "they can X but they can't Y" format made famous by "They can put a man on the moon but they can't cure the common cold."
The shape is: an astonishing, verifiable, specific human achievement — followed by a petty, universal, infuriating failure that everyone has personally suffered.
THE REFERENT RULE — the one that decides whether a line works at all:
"They" in the first clause and the subject of the second clause must be THE SAME actor. Before writing each line, name that actor in the "they" field, then check that BOTH halves are genuinely its doing. One of two things must be true:
(a) both halves belong to one concrete actor — the same model, the same company, the same industry; or
(b) both halves are at civilisational scale, where "they" is the vague "they" of "put a man on the moon" — humanity, science, the modern world.
A line that mixes the two is broken and must be discarded, no matter how funny it sounds.
BROKEN: "They can prove a theorem no mathematician could but the printer still won't connect to the wifi." (an AI lab does not make printers — two different 'they')
BROKEN: "They can land a rover on Mars but the self-checkout thinks your hand is an unexpected item." (NASA does not build self-checkouts)
WORKS: "They can put a man on the moon but they can't cure the common cold." (both are civilisation-scale)
WORKS: "They can win 10 Fields Medals but they'll recommend a cafe that closed two years ago." (the same model does both)
THE IRONY RULE — what separates a joke from two unrelated facts:
The failure must be the humble sibling of the achievement: the SAME faculty, domain or promise, at the trivial end. The listener should feel "but that's the easy version of what you just did". A failure from an unrelated department is not ironic, it is just a second sentence.
IRONIC: "They can win a Fields Medal but they'll tell you 9.11 is bigger than 9.9." (both mathematics — one impossible, one a child's exercise)
IRONIC: "They can read a 500-page contract in three seconds but they can't remember what you told them four messages ago." (both reading and holding text)
IRONIC: "They can put a man on the moon but they can't cure the common cold." (both are 'hard problems science was supposed to solve', and the cold is the one in your own nose)
FLAT: "They can win a Fields Medal but the pricing page still has a typo." (unrelated faculties — no irony, just two facts)
THE TRUTH RULE — both halves must be REAL:
The feat must be a genuine, checkable accomplishment that demonstrates competence — not merely an event that happened to occur.
The failure must be a documented, widely-experienced shortcoming: something people actually complain about, that a reader will recognise from their own life or from the news. Never invent a failure because it sounds plausible.
And the failure must be an INABILITY — not an opinion, a preference, a matter of taste, or a whimsical non-question.
INVENTED (banned): "They can score in the 99th percentile on the bar exam but they can't play tic-tac-toe without cheating." (models don't cheat at tic-tac-toe — nobody has ever observed this; it was made up because it sounded plausible)
NOT A FAILURE (banned): "They can elect a real-estate mogul to the highest office but they still can't decide if a hot dog is a sandwich." (that is an opinion, not an inability — and being elected is not a feat of competence)
REAL: "They can maintain a global GPS satellite constellation but they still can't get a passport renewal under six weeks." (both are genuine state capacity, both verifiable)
In the "basis" field, say in a few words what makes the failure real — the actual observed behaviour or the common complaint. If you cannot name one, the line is invented: throw it out and write a different one.
Rules that make these land:
- SPECIFIC beats generic. "detect a gravitational wave from two black holes colliding 1.3 billion years ago" beats "do advanced physics". Numbers, names and distances are funny; abstractions are not.
- The failure must be small and familiar. The comedy is the size gap, not the failure itself.
- Keep the whole line under about 30 words. Punchy. No preamble, no explanation, no "isn't it ironic".
- Vary the grammar of the second half: usually "they can't …" / "they still …", and only where the referent rule allows it, a narrower subject that clearly belongs to the same actor ("the app still can't …", "the model still insists …").
- Never reuse a comparison from the avoid-list.
- Do not be wholesome, do not moralise, do not add emoji.
Return ONLY a JSON array, no markdown fence, of exactly ${BATCH} objects:
[{"they": "<the single actor responsible for BOTH halves>", "basis": "<what makes the failure real — the observed behaviour or common complaint>", "setup": "<the achievement, no leading 'They can'>", "punch": "<the failure, a full clause whose subject is that same actor>"}]
Work like this: draft about twelve candidates, run every one through the referent, irony and truth rules, throw out each one that fails any of them, and return only the ${BATCH} strongest survivors. A weak line is worse than a repeat.
The app renders each as: They can {setup} but {punch}.`
function clean(s: string) {
return String(s || "").trim().replace(/^(they can|but)\s+/i, "").replace(/[.\s]+$/, "")
}
function parseJokes(text: string): Joke[] {
const a = text.indexOf("["), b = text.lastIndexOf("]")
if (a < 0 || b < a) return []
try {
const raw = JSON.parse(text.slice(a, b + 1))
if (!Array.isArray(raw)) return []
return raw
.map((j: any) => ({
they: String(j?.they || "").trim(),
basis: String(j?.basis || "").trim(),
setup: clean(j?.setup),
punch: clean(j?.punch),
}))
.filter((j: Joke) => j.setup.length > 3 && j.punch.length > 3)
} catch { return [] }
}
function line(j: Joke) { return `They can ${j.setup} but ${j.punch}.` }
function CopyIcon({ done }: { done: boolean }) {
return done ? (
<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor"
strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M20 6 9 17l-5-5" />
</svg>
) : (
<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor"
strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<rect x="9" y="9" width="11" height="11" rx="2.5" />
<path d="M6 15H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v1" />
</svg>
)
}
function Card({ joke }: { joke: Joke }) {
return (
<p className="joke" key={joke.setup}>
<span className="feat">They can </span>
<span className="feat">{joke.setup}</span>
<span className="fail"> but </span>
<span className="fail">{joke.punch}</span>
<span className="fail">.</span>
</p>
)
}
function App() {
const [access, setAccess] = useState<AiAccess>("own")
const [topic, setTopic] = useState(TOPICS[0])
const [custom, setCustom] = useState("")
const [current, setCurrent] = useState<Joke>(SEED[0])
const [started, setStarted] = useState(false)
const [history, setHistory] = useState<Joke[]>([])
const [busy, setBusy] = useState(false)
const [error, setError] = useState<string | null>(null)
const [copied, setCopied] = useState(false)
const queue = useRef<Joke[]>([])
const seen = useRef<string[]>([])
const inflight = useRef<Promise<void> | null>(null)
const topicKey = useRef("")
const fill = useCallback(async () => {
if (inflight.current) return inflight.current
const subject = custom.trim() || topic.hint
const who = custom.trim()
? `the user typed this topic themselves, and it may be a person, a company, a country, an institution, a profession or an era. Decide who "they" is before writing anything: for a person or an organisation, both halves must be that same person's or organisation's real capabilities; for a country or a government, "they" is that state and both halves are its state capacity; for a field, "they" is its practitioners. Hold every line to whichever you picked.`
: topic.who
const avoid = seen.current.slice(-24)
const p = (async () => {
tb.log(`[fill] asking for ${BATCH} on "${subject}" (avoiding ${avoid.length})`)
const ask = tb.ai({
system: SYSTEM,
webSearch: false,
effort: 2,
messages: [{
role: "user",
content:
`Topic: ${subject}\n\n` +
`Who "they" is for this topic: ${who}\n\n` +
(avoid.length ? `Avoid-list (already used, do not repeat or paraphrase):\n${avoid.map(s => "- " + s).join("\n")}\n\n` : "") +
`Calibration — this is the quality bar:\n${SEED.slice(0, 5).map(j => "- " + line(j)).join("\n")}\n\n` +
`Write ${BATCH} new ones. Apply the referent rule to every single one before you return it.`,
}],
})
const guard = new Promise<never>((_, rej) =>
setTimeout(() => rej(new Error("the model took too long — try again")), 90000))
const { text } = await Promise.race([ask, guard])
tb.log(`[fill] got ${text.length} chars`)
const jokes = parseJokes(text).filter(j => !seen.current.includes(j.setup))
tb.log(`[fill] parsed ${jokes.length} jokes; referents: ${jokes.map(j => j.they || "?").join(" / ")}`)
if (!jokes.length) throw new Error("the model returned nothing usable")
queue.current = queue.current.concat(jokes)
})()
inflight.current = p
try { await p } catch (e) { tb.log(`[fill] FAILED: ${String((e as any)?.message || e)}`); throw e }
finally { inflight.current = null }
}, [topic, custom])
const next = useCallback(async () => {
setError(null); setCopied(false)
try {
if (!queue.current.length) { setBusy(true); await fill() }
const j = queue.current.shift()!
seen.current.push(j.setup)
setCurrent(j); setStarted(true)
setHistory(h => [j, ...h].slice(0, 40))
} catch (e: any) {
setError(String(e?.message || e))
} finally { setBusy(false) }
}, [fill])
useEffect(() => {
tb.aiAccess().then(setAccess).catch(() => setAccess("none"))
}, [])
// changing the subject only drops the queued batch — it never spends a call;
// nothing here (or on mount) hits the API until Generate is pressed
const key = topic.id + "|" + custom.trim()
useEffect(() => {
if (topicKey.current === key) return
topicKey.current = key
queue.current = []
}, [key])
// probe handlers — selftest / state readback
useEffect(() => tb.onMessage(async (m: any) => {
if (m === "selftest") {
try {
queue.current = []
await fill()
const got = queue.current.slice(0, 3).map(line)
return { verdict: got.length ? "ok" : "empty", count: queue.current.length, sample: got }
} catch (e: any) { return { verdict: "error", error: String(e?.message || e) } }
}
if (m === "state") return { current: line(current), started, queued: queue.current.length, seen: seen.current.length, busy, error }
return undefined
}), [fill, current, started, busy, error])
const copy = () => { tb.copy(line(current)); setCopied(true) }
return (
<div className="wrap">
<header>
<h1><span className="feat">They can…</span> <span className="fail">but they can't…</span></h1>
<p className="sub">Where progress gets a shrug.</p>
</header>
<div className="chips">
{TOPICS.map(t => (
<button
key={t.id}
className={"chip" + (t.id === topic.id && !custom.trim() ? " on" : "")}
onClick={() => { setCustom(""); setTopic(t) }}
>{t.label}</button>
))}
<input
className={"chip custom" + (custom.trim() ? " on" : "")}
value={custom}
placeholder="✏️ your own topic…"
onChange={e => setCustom(e.target.value)}
/>
</div>
<section className={"card" + (busy ? " busy" : "")}>
<Card joke={current} />
<button
className={"copy" + (copied ? " done" : "")}
onClick={copy}
aria-label={copied ? "copied" : "copy"}
title={copied ? "copied" : "copy this one"}
><CopyIcon done={copied} /></button>
{busy && <div className="bar" />}
</section>
<div className="actions">
<button className="big" onClick={() => next()} disabled={busy || access === "none"}>
{busy ? "THINKING…" : started ? "ANOTHER ONE" : "GENERATE"}
</button>
</div>
{access === "none" && <p className="warn">No AI configured — add an API key to <code>.env</code> and run with <code>--trust</code>. Showing the built-in classics.</p>}
{error && <p className="warn">Couldn't reach the model: {error}</p>}
{history.length > 1 && (
<details className="past">
<summary>Previous ({history.length - 1})</summary>
<ul>
{history.slice(1).map((j, i) => (
<li key={i} onClick={() => tb.copy(line(j))} title="click to copy">{line(j)}</li>
))}
</ul>
</details>
)}
</div>
)
}
createRoot(document.getElementById("root")!).render(<App />)
```
**styles.css**
```css
:root {
--ink: #16181d;
--dim: #6b7280;
--edge: rgba(0, 0, 0, 0.12);
--card: rgba(255, 255, 255, 0.75);
--glow: 0 10px 40px -18px rgba(0, 0, 0, 0.55);
--feat: #15803d;
--fail: #cc2222;
--chip: rgba(0, 0, 0, 0.04);
--btn: #6b7280;
--btn-hi: #8d95a3;
--btn-ink: #ffffff;
}
html[data-theme="dark"] {
--ink: #e8eaee;
--dim: #8b93a1;
--edge: rgba(255, 255, 255, 0.16);
--card: rgba(255, 255, 255, 0.05);
--glow: 0 10px 40px -18px rgba(0, 0, 0, 0.9);
--feat: #4ade80;
--fail: #fa6363;
--chip: rgba(255, 255, 255, 0.07);
}
@media (prefers-color-scheme: dark) {
html:not([data-theme="light"]) {
--ink: #e8eaee;
--dim: #8b93a1;
--edge: rgba(255, 255, 255, 0.16);
--card: rgba(255, 255, 255, 0.05);
--glow: 0 10px 40px -18px rgba(0, 0, 0, 0.9);
--feat: #4ade80;
--fail: #fa6363;
--chip: rgba(255, 255, 255, 0.07);
}
}
html[data-theme="dark"] { --btn: #3a3f4a; --btn-hi: #565d6b; --btn-ink: #f3f5f8; }
@media (prefers-color-scheme: dark) {
html:not([data-theme="light"]) { --btn: #3a3f4a; --btn-hi: #565d6b; --btn-ink: #f3f5f8; }
}
.wrap {
max-width: 680px;
margin: 0 auto;
box-sizing: border-box;
min-height: 100dvh;
padding: 28px 18px 32px;
font: 15px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
color: var(--ink);
display: grid;
align-content: center;
gap: 18px;
}
header { text-align: center; }
h1 {
font-size: clamp(27px, 5.2vw, 38px);
margin: 0;
letter-spacing: -0.03em;
line-height: 1.1;
}
.sub { margin: 10px 0 0; color: var(--dim); font-size: clamp(17px, 2.6vw, 22px); }
.chips { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
.chip {
font: inherit; font-size: 15px;
padding: 7px 15px;
border: 1px solid transparent;
border-radius: 999px;
background: var(--chip);
color: var(--ink);
cursor: pointer;
transition: border-color .15s, background .15s;
}
.chip:hover { border-color: var(--edge); }
.chip.on { border-color: currentColor; background: transparent; }
.chip.custom { cursor: text; width: 190px; }
.chip.custom::placeholder { color: var(--dim); }
.card {
position: relative;
overflow: hidden;
min-height: 190px;
display: grid;
place-items: center;
gap: 14px;
padding: 32px 30px 42px;
border: 1px solid var(--edge);
border-radius: 18px;
background: var(--card);
box-shadow: var(--glow);
}
.card.busy { opacity: 0.75; }
.joke {
margin: 0;
text-align: center;
font-size: clamp(19px, 3.4vw, 25px);
line-height: 1.34;
font-weight: 650;
letter-spacing: -0.015em;
text-wrap: balance;
animation: rise .35s ease both;
}
@keyframes rise { from { opacity: 0; transform: translateY(7px) } to { opacity: 1; transform: none } }
.dim { color: var(--dim); }
.feat { color: var(--feat); }
.fail { color: var(--fail); }
.bar {
position: absolute; left: 0; bottom: 0; height: 2px; width: 40%;
background: var(--feat);
animation: slide 1.1s ease-in-out infinite;
}
@keyframes slide { 0% { transform: translateX(-100%) } 100% { transform: translateX(350%) } }
.actions { display: flex; gap: 10px; justify-content: center; align-items: stretch; }
button.big {
font: inherit;
cursor: pointer;
border-radius: 12px;
line-height: 1;
transition: transform .12s ease, box-shadow .18s ease, background .18s, color .18s, border-color .18s;
}
button.big {
position: relative;
overflow: hidden;
isolation: isolate;
padding: 13px 28px;
font-size: 16px;
font-weight: 700;
letter-spacing: 0.06em;
border: none;
background: linear-gradient(180deg, var(--btn-hi), var(--btn));
color: var(--btn-ink);
box-shadow: 0 6px 18px -8px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.18);
}
button.big::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(105deg, transparent 34%, rgba(255, 255, 255, 0.3) 50%, transparent 66%);
transform: translateX(-130%);
transition: transform .7s cubic-bezier(.22, .8, .3, 1);
pointer-events: none;
}
button.big:hover:not(:disabled)::after { transform: translateX(130%); }
button.big:hover:not(:disabled) { transform: translateY(-1px); box-shadow: 0 10px 24px -9px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.18); }
button.big:active:not(:disabled) { transform: translateY(1px); box-shadow: 0 3px 10px -6px rgba(0, 0, 0, 0.5); }
button.copy {
position: absolute;
right: 10px;
bottom: 10px;
display: grid;
place-items: center;
width: 32px;
height: 32px;
padding: 0;
border: 1px solid transparent;
border-radius: 9px;
background: transparent;
color: var(--dim);
opacity: 0.55;
cursor: pointer;
transition: opacity .15s, color .15s, background .15s, border-color .15s;
}
.card:hover button.copy { opacity: 1; }
button.copy:hover { color: var(--ink); background: var(--chip); border-color: var(--edge); }
button.copy.done { color: var(--feat); opacity: 1; }
button:disabled { opacity: 0.45; cursor: default; box-shadow: none; }
button:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.warn { margin: 0; text-align: center; font-size: 12.5px; color: var(--dim); }
code { font-size: 12px; background: var(--chip); padding: 1px 4px; border-radius: 4px; }
.past summary { cursor: pointer; color: var(--dim); font-size: 13px; }
.past ul { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 8px; }
.past li {
font-size: 13.5px; color: var(--dim); cursor: pointer;
padding: 7px 10px; border-radius: 8px; background: var(--chip);
}
.past li:hover { color: var(--ink); }
footer { text-align: center; font-size: 12px; color: var(--dim); }
footer label { display: inline-flex; gap: 6px; align-items: center; }
select {
font: inherit; font-size: 12px; padding: 3px 6px; border-radius: 6px;
border: 1px solid var(--edge); background: Canvas; color: CanvasText;
}
select, option { background: Canvas; color: CanvasText; }
```
**index.html**
```html
<div id="root"></div>
```
**data.txt**
```txt
[
{ "setup": "win 10 Fields Medals", "punch": "they'll recommend a cafe that closed two years ago" },
{ "setup": "put a man on the moon", "punch": "they can't cure the common cold" },
{ "setup": "land a rocket upright on a barge in the middle of the ocean", "punch": "they can't make an airport wifi login page work" },
{ "setup": "detect a gravitational wave from two black holes colliding 1.3 billion years ago", "punch": "the smoke alarm can't tell a house fire from toast" },
{ "setup": "move a billion dollars of crypto in nine seconds", "punch": "a bank transfer takes 3-5 business days" },
{ "setup": "win a Fields Medal", "punch": "they can't tell you what today's date is" },
{ "setup": "sequence a human genome for two hundred dollars", "punch": "nobody in the office can connect to the printer" }
]
```
**config.json**
```json
{
"description": "An infinite meme machine for 'They can put a man on the moon but they can't cure the common cold' one-liners, generated live.",
"dependencies": {
"react": "^19.2.7",
"react-dom": "^19.2.7"
}
}
```
**notes.md**
```md
Meme-app genre: one big joke on a card, tap for another.
Design notes:
- Jokes are fetched in batches of 8 and served from an in-memory queue, so
"Another one" is instant after the first call; a background refill kicks in
when the queue drops to 2.
- `seen` accumulates every setup used this session and is sent to the model as
an avoid-list, which is what keeps a long session from looping.
- `data.txt` does double duty: few-shot calibration inside the prompt, and the
offline fallback when tb.aiAccess() is 'none'.
- Probe handlers: `typebulb send <file> selftest --wait` (forces one live batch,
returns verdict/count/sample) and `send <file> state --wait`.
```