termi-kids 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,17 +10,15 @@
10
10
 
11
11
  # Termi
12
12
 
13
- Termi is a friendly robot that helps you build things on a real computer.
13
+ Termi is a friendly robot that helps you build browser games on a real computer.
14
14
 
15
- You type what you want. Termi writes the code with you. Your game opens in the browser right away.
15
+ You pick a game idea (or invent your own). You write a prompt, or ask Termi to help write one. Termi builds the game with you. Your game opens in the browser and updates after every prompt.
16
16
 
17
- You can make dodge games, platform games, pixel art, dance music, virtual pets, adventure stories, quizzes, your own web page, and talking characters. If you change your mind, type `undo` and the last change is gone. When something works, you earn badges.
17
+ There are 30 ready game ideas plus "Build my own idea". When you finish, Termi can run a short test pass and suggest one last improvement. Games save in your local library on this computer.
18
18
 
19
- Termi is a computer program, an AI tool. It is not a person. A grown-up sets it up first.
20
-
21
- Not sure what to say first? Type `/quest` inside any project. Termi walks you through a real build, step by step, with a ready idea at every step. Press Enter to use it, or type your own. Finish a quest and you earn the Quest Hero badge.
19
+ Want to get better at prompting? Open **Learn AI**. Six short lessons teach you how to boss your AI helper. Each one earns a badge, and you can replay them any time.
22
20
 
23
- Want to get good at building with AI? Type `termi learn`. Six short lessons teach you how to be the boss of your AI helper. Each one earns a badge, and you can replay them any time.
21
+ Termi is a computer program, an AI tool. It is not a person. A grown-up sets it up first.
24
22
 
25
23
  Ready? Ask a grown-up to read the next part.
26
24
 
@@ -37,7 +35,7 @@ Please read [SAFETY.md](SAFETY.md) before handing the keyboard to your kid. It e
37
35
  - Node.js 20.19 or newer: https://nodejs.org
38
36
  - macOS, Windows, or Linux
39
37
  - An AI account you own (one of: a ChatGPT account, a Claude API key, an OpenAI API key, or a Grok API key)
40
- - About 700 MB of free disk space for the on-device safety checker (recommended, on by default)
38
+ - About 700 MB of free disk space for the on-device safety checker (installed during setup)
41
39
 
42
40
  If Node is too old, Termi prints a plain message with the download link instead of starting.
43
41
 
@@ -51,21 +49,21 @@ npm install -g termi-kids
51
49
 
52
50
  Then run `termi`. If npm reports a permission error, prefix the install with `sudo` on macOS and Linux, or run the terminal as administrator on Windows. To remove it later, run `npm rm -g termi-kids` (full cleanup steps are in SAFETY.md).
53
51
 
52
+ To update later, run `termi update` (or `npm install -g termi-kids@latest`). On each new session, Termi checks for a newer version and asks whether to update (y/n). Set `TERMI_SKIP_UPDATE=1` to silence the check.
53
+
54
54
  ### First run: the setup wizard
55
55
 
56
56
  Run `termi`. The first run starts a setup wizard for a parent or guardian. It takes about five minutes:
57
57
 
58
58
  1. **Create a grown-up PIN** (at least 4 characters). It guards the grown-up zone. Your kid should not know it.
59
- 2. **Age band and consent.** You pick "Under 13" or "13 or older" and confirm that Termi uses an AI account you own and that you agree to watch how your kid uses it. This consent is recorded in the local safety log.
59
+ 2. **Consent.** You confirm that Termi uses an AI account you own and that you agree to watch how your kid uses it. One safety bar applies for every age (no under-13 / over-13 split). Consent is recorded in the local safety log.
60
60
  3. **Pick an AI provider.** The default is "ChatGPT sign-in": your browser opens, you sign in to your own ChatGPT account, and no API key is needed. The other options are a Claude API key, an OpenAI API key, or a Grok API key. Grok requires an extra confirmation because the xAI API terms are adults-only (details in SAFETY.md). You can add several providers and choose which one is active.
61
- 4. **Safety checker download.** Termi offers a small safety model (623 MB) that screens every message right on your computer, even with no internet. It is on by default and worth keeping. While it downloads, Termi tells you plainly that basic safety (the local filter plus the online checks) is already running, and asks whether you want to start building now or wait and watch the progress bar. Either way, the checker turns itself on the moment the verified file lands and Termi says so, even mid-session. The download is anonymous (no account or token needed) and resumes if interrupted. Details are in [SAFETY.md](SAFETY.md).
61
+ 4. **Safety checker install.** Setup always starts the on-device safety model download (about 623 MB). It screens every message on this computer, even offline. You can wait for the progress bar or keep setting up while it finishes in the background. Basic safety is already on during the download. The download is anonymous and resumes if interrupted. Details are in [SAFETY.md](SAFETY.md).
62
62
  5. **Hand the keyboard to your kid.** The kid picks a made-up nickname (Termi asks them not to use their real name) and sees a clear disclosure: "Termi is a computer program, an AI. It is a tool a grown-up set up for you. It is not a person."
63
63
  6. **Desktop shortcut.** Termi offers to write a double-clickable launcher on the Desktop (`Termi.command` on macOS, `Termi.bat` on Windows, `Termi.desktop` on Linux) so your kid can come back tomorrow without typing commands.
64
- 7. **Optional first game.** Two keypresses later there is a running game in the browser.
65
-
66
- If you skip the provider step, Termi runs in offline mode: your kid can still create projects, play them in the preview, undo changes, and browse ideas. The chat shows a kind "ask a grown-up to set up the AI helper" screen.
64
+ 7. **Optional first game.** The kid can jump straight into **Build a game** (pick an idea or invent one).
67
65
 
68
- **If your kid does not know where to start:** every project type has a Build Quest (`/quest` in the chat), a five-step guided build with a ready prompt at each step, and a large idea deck (`/ideas`). Quest steps go through the same safety pipeline as any other message.
66
+ If you skip the provider step, Termi runs in offline mode: your kid can still open the library and Learn AI. Building with the AI helper needs a provider.
69
67
 
70
68
  ### What keeps your kid safe
71
69
 
@@ -85,13 +83,13 @@ Parents get a PIN-gated grown-up zone, a tamper-evident log of every block and s
85
83
 
86
84
  | Command | What it does |
87
85
  |---|---|
88
- | `termi` | First run: setup wizard. After that: the home menu. |
89
- | `termi new` | Start a new project. |
90
- | `termi go [name]` | Open a project and build. With no name, pick from a list. |
91
- | `termi preview [name]` | Open a project in the browser without the chat. |
92
- | `termi ideas` | Print fun project ideas. |
93
- | `termi learn` | Six short, replayable lessons on building with AI. Fully offline, no AI calls. |
86
+ | `termi` | First run: setup wizard. After that: home (Build a game, My games, Learn AI). |
87
+ | `termi new` | Build a game (idea list + prompt loop). |
88
+ | `termi go [name]` | Open a game from your library. With no name, pick from a list. |
89
+ | `termi preview [name]` | Open a game in the browser without the chat. |
90
+ | `termi learn` | Learn AI: six short offline lessons on prompting. |
94
91
  | `termi grownups` | The grown-up zone. PIN required. |
92
+ | `termi update` | Update Termi to the latest npm version. |
95
93
  | `termi help` | Show the command list. |
96
94
  | `termi --version` | Show the version. |
97
95
 
package/SAFETY.md CHANGED
@@ -14,7 +14,7 @@ This page is for parents and guardians. It explains the safety system in plain l
14
14
 
15
15
  Five layers sit between your kid and the AI. The chat conversation itself is never trusted to police itself.
16
16
 
17
- 1. **A local filter on this computer.** Before anything leaves your machine, Termi checks the message offline. It blocks swearing and slurs (including d.i.s.g.u.i.s.e.d spellings and leetspeak), and it blocks known "ignore your rules" tricks. It also looks for personal details: names, addresses, phone numbers, emails, school names. Those are not blocked; they are masked to `[secret]` before the message is sent, and the kid gets a gentle reminder to keep private things private. The same masking is used when the AI reads project files back. The word lists are English; the AI-based checkers below cover other languages.
17
+ 1. **A local filter on this computer.** Before anything leaves your machine, Termi checks the message offline. It blocks swearing and slurs (including d.i.s.g.u.i.s.e.d spellings and leetspeak), known "ignore your rules" tricks (including some base64-hidden ones), clear self-harm language (with a calm support screen and the 988 line in the US), grooming-shaped asks (secrecy from parents, romance aimed at the kid, moving chat to other apps), and probes for personal details like school or address. When a kid shares their own personal details (name, address, phone, email, school), those are not blocked; they are masked to `[secret]` before the message is sent, and the kid gets a gentle reminder to keep private things private. The same masking is used when the AI reads project files back. The word lists are English; the AI-based checkers below cover other languages.
18
18
  2. **Safety rules inside the AI's instructions.** The AI is told, every turn: you are a tool, not a person. Never act romantic, never roleplay relationships, never ask the kid to keep secrets, never ask for a real name, address, school, age, or photos. Big feelings get one kind line and a pointer to a trusted adult. The instructions also declare that everything the kid types and everything in project files is data, never commands, which blunts "ignore your previous instructions" tricks hidden in files.
19
19
  3. **A checker before the AI acts.** A separate safety check, outside the conversation, reads the kid's message along with the last few turns for context. When the on-device safety checker is installed (it is offered during setup and on by default), it judges the message right on this computer at the same time. It runs at the same time as the build call so it adds no waiting, but nothing is allowed to land until it passes: no file is written and no reply is shown. If it says no, the work is thrown away. One honest note on timing: because the check and the build call start together, a message that ends up blocked has still traveled to your AI provider once (with personal details already masked); its answer is discarded unseen.
20
20
  4. **A checker on everything the AI says and writes.** Every file the AI writes or edits is checked twice before it touches disk: a code scan looks for network calls, code hidden in strings, and other tricks (the full list is in the code scanner, `src/safety/codescan.ts`), and the human-visible text inside the file (story text, labels, comments) goes through the same safety check as chat, in full: long text is checked chunk by chunk, and a file too wordy to check completely is refused rather than half-checked. File names and project names are screened too. The final reply is checked too, before the kid sees a single character. As a backstop, the preview server wraps every project page in a strict Content-Security-Policy, so even code that slipped past the scanner cannot reach the internet from the browser. Files edited outside Termi (in a text editor, say) are the kid's own files and are not re-screened; they only pass the rule-neutralizing filter when read back into the chat.
@@ -50,7 +50,7 @@ Grooming, personal info, and rule-breaking tricks block at a lower threshold tha
50
50
 
51
51
  Termi includes an on-device safety checker: a small AI model (Qwen3Guard 0.6B, published by the Qwen team under the Apache 2.0 license) that runs entirely on your computer. Setup offers the download (623 MB) with a yes as the default; you can decline and add it later from the grown-up zone (`termi grownups`, then **Safety checker**).
52
52
 
53
- The download never blocks anyone. Setup says, in plain words, that basic safety (the local filter plus the online checks) is already on, then asks whether you want to start building now or wait and watch the progress bar. If you start now, the download continues in the background while your kid builds, the home menu shows its bar, and the checker attaches itself to the running safety pipeline the moment the verified file is in place; Termi announces it on screen ("Your safety helper is on."), even in the middle of a build session. If the download is interrupted (a closed laptop, a dropped connection), it resumes from where it stopped the next time Termi starts. Until the file lands, the online checks carry the load exactly as they would with the checker turned off.
53
+ Setup always starts the download (it is part of install, not an easy skip). Setup says, in plain words, that basic safety (the local filter plus the online checks) is already on, then asks whether you want to wait and watch the progress bar or keep setting up while it finishes. Either way the download continues, the home menu can show its bar, and the checker attaches itself to the running safety pipeline the moment the verified file is in place; Termi announces it on screen ("Your safety helper is on."), even in the middle of a build session. If the download is interrupted (a closed laptop, a dropped connection), it resumes from where it stopped the next time Termi starts. Until the file lands, the online checks carry the load.
54
54
 
55
55
  The download itself is anonymous. The model file comes over HTTPS from a public model repository, with no account, sign-in, or token of any kind; nothing about you or your kid is sent with the request, and the file is checked against a pinned cryptographic fingerprint before it is used.
56
56
 
package/dist/cli.js CHANGED
@@ -19,7 +19,6 @@ import { installGlobalHandlers, renderCrash } from './ui/errors.js';
19
19
  import { mascot } from './ui/mascot.js';
20
20
  import { style } from './ui/theme.js';
21
21
  import { T } from './ui/text.js';
22
- import { executeIdeas } from './surfaces/commands.js';
23
22
  /** Pure boot branch logic: settings state in, actions out. */
24
23
  export function decideBoot(state) {
25
24
  return {
@@ -32,12 +31,12 @@ export function cliHelp() {
32
31
  return [
33
32
  'How to use Termi:',
34
33
  ' termi open the home menu',
35
- ' termi new start a new project',
36
- ' termi go [name] open a project and build',
37
- ' termi preview watch a project run',
38
- ' termi ideas get fun ideas',
39
- ' termi learn play six short lessons about AI',
34
+ ' termi new build a game',
35
+ ' termi go [name] open a game from your library',
36
+ ' termi preview watch a game run',
37
+ ' termi learn Learn AI (prompting lessons)',
40
38
  ' termi grownups grown-up zone (PIN needed)',
39
+ ' termi update update Termi to the latest version',
41
40
  ' termi help show this help',
42
41
  ' termi --version show the version',
43
42
  ].join('\n');
@@ -123,24 +122,6 @@ async function routePreview(nameArg, settings) {
123
122
  // Held open on purpose; Ctrl+C ends it through the global handler.
124
123
  });
125
124
  }
126
- async function routeIdeas(settings) {
127
- let scaffoldId = 'games';
128
- if (settings.lastProjectSlug !== null) {
129
- try {
130
- const store = await import('./projects/store.js');
131
- const last = store.openProject(settings.lastProjectSlug);
132
- if (last !== null) {
133
- scaffoldId = last.meta.scaffoldId;
134
- }
135
- }
136
- catch {
137
- // Fall back to game ideas.
138
- }
139
- }
140
- await executeIdeas(scaffoldId, (text) => {
141
- console.log(text);
142
- });
143
- }
144
125
  async function route(command, rest, settings) {
145
126
  const home = await import('./surfaces/home.js');
146
127
  switch (command) {
@@ -149,10 +130,8 @@ async function route(command, rest, settings) {
149
130
  return;
150
131
  }
151
132
  case 'new': {
152
- const project = await home.runNewProject(settings);
153
- if (project !== null) {
154
- await home.openChatLoop(project, settings);
155
- }
133
+ const build = await import('./surfaces/buildGame.js');
134
+ await build.runBuildGame(settings);
156
135
  return;
157
136
  }
158
137
  case 'go': {
@@ -167,7 +146,9 @@ async function route(command, rest, settings) {
167
146
  return;
168
147
  }
169
148
  case 'ideas': {
170
- await routeIdeas(settings);
149
+ // Ideas live inside Build a game now.
150
+ const build = await import('./surfaces/buildGame.js');
151
+ await build.runBuildGame(settings);
171
152
  return;
172
153
  }
173
154
  case 'learn': {
@@ -180,6 +161,11 @@ async function route(command, rest, settings) {
180
161
  await panel.runPanel();
181
162
  return;
182
163
  }
164
+ case 'update': {
165
+ const update = await import('./update/prompt.js');
166
+ await update.runUpdateCommand();
167
+ return;
168
+ }
183
169
  default: {
184
170
  console.log(`Hmm, "${command}" is not a Termi command.`);
185
171
  console.log(cliHelp());
@@ -198,6 +184,13 @@ export async function main(argv = process.argv.slice(2)) {
198
184
  console.log(cliHelp());
199
185
  return;
200
186
  }
187
+ // Update works without the wizard so a parent can refresh a broken install.
188
+ if (command === 'update') {
189
+ ensureDirs();
190
+ const update = await import('./update/prompt.js');
191
+ await update.runUpdateCommand();
192
+ return;
193
+ }
201
194
  ensureDirs();
202
195
  const loaded = loadSettings();
203
196
  let settings = loaded.settings;
@@ -243,6 +236,17 @@ export async function main(argv = process.argv.slice(2)) {
243
236
  const { ensureGuardFetch } = await import('./safety/guarddownload.js');
244
237
  void ensureGuardFetch();
245
238
  }
239
+ // Returning sessions: offer an update when npm has a newer version.
240
+ // Skipped during setup, tests, and TERMI_SKIP_UPDATE=1.
241
+ if (!decision.runWizard) {
242
+ try {
243
+ const update = await import('./update/prompt.js');
244
+ await update.maybePromptForUpdate();
245
+ }
246
+ catch {
247
+ // Never block boot on the update check.
248
+ }
249
+ }
246
250
  await route(command, argv.slice(1), settings);
247
251
  }
248
252
  const underTest = process.env.VITEST !== undefined || process.env.NODE_ENV === 'test';
@@ -164,7 +164,7 @@ export async function runLesson(lesson, say = defaultSay) {
164
164
  */
165
165
  export async function runLearnMenu() {
166
166
  console.log(mascot('happy'));
167
- console.log('Welcome to Termi school! Six tiny lessons, zero homework.');
167
+ console.log('Learn AI: six tiny lessons on how to prompt. Zero homework.');
168
168
  for (;;) {
169
169
  const done = new Set(loadProgress().completed);
170
170
  const options = [
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Minimal blank game project: no stock playable game, just a local HTML
3
+ * shell the model fills in. Used by the Build a game flow.
4
+ */
5
+ import fs from 'node:fs';
6
+ import path from 'node:path';
7
+ import { projectsDir } from '../config/paths.js';
8
+ import { slugifyName } from './create.js';
9
+ import { notesFileName, openProject, renderTermiMd, saveProjectMeta, } from './store.js';
10
+ const BLANK_HTML = `<!DOCTYPE html>
11
+ <html lang="en">
12
+ <head>
13
+ <meta charset="UTF-8" />
14
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
15
+ <title>__TITLE__</title>
16
+ <link rel="stylesheet" href="style.css" />
17
+ </head>
18
+ <body>
19
+ <main>
20
+ <h1 id="title">__TITLE__</h1>
21
+ <p id="hint">Your game will show up here after you build with Termi.</p>
22
+ <canvas id="game" width="480" height="320" aria-label="game"></canvas>
23
+ </main>
24
+ <script src="game.js"></script>
25
+ </body>
26
+ </html>
27
+ `;
28
+ const BLANK_CSS = `* { box-sizing: border-box; }
29
+ body {
30
+ margin: 0;
31
+ font-family: system-ui, sans-serif;
32
+ background: #0f172a;
33
+ color: #e2e8f0;
34
+ min-height: 100vh;
35
+ display: grid;
36
+ place-items: center;
37
+ }
38
+ main { text-align: center; padding: 1rem; }
39
+ h1 { font-size: 1.4rem; margin: 0 0 0.5rem; }
40
+ #hint { opacity: 0.8; margin-bottom: 1rem; }
41
+ canvas {
42
+ background: #1e293b;
43
+ border: 2px solid #38bdf8;
44
+ border-radius: 12px;
45
+ max-width: 100%;
46
+ }
47
+ `;
48
+ const BLANK_JS = `// Blank starter. Termi and the kid fill this in.
49
+ const canvas = document.getElementById('game');
50
+ const ctx = canvas.getContext('2d');
51
+ function frame() {
52
+ ctx.fillStyle = '#1e293b';
53
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
54
+ ctx.fillStyle = '#94a3b8';
55
+ ctx.font = '16px system-ui, sans-serif';
56
+ ctx.textAlign = 'center';
57
+ ctx.fillText('Ready to build!', canvas.width / 2, canvas.height / 2);
58
+ }
59
+ frame();
60
+ `;
61
+ function writeRel(dir, relPath, content) {
62
+ const target = path.join(dir, ...relPath.split('/'));
63
+ fs.mkdirSync(path.dirname(target), { recursive: true });
64
+ fs.writeFileSync(target, content, 'utf8');
65
+ }
66
+ /**
67
+ * Creates an empty local-browser game shell under the projects library.
68
+ * scaffoldId stays "games" so existing badges and quests still recognize it.
69
+ */
70
+ export function createBlankGameProject(prettyNameInput, ideaLabel = 'My game') {
71
+ const trimmed = prettyNameInput.trim();
72
+ const prettyName = trimmed.length > 0 ? trimmed : 'My Game';
73
+ const { slug } = slugifyName(prettyName);
74
+ const dir = path.join(projectsDir(), slug);
75
+ fs.mkdirSync(dir, { recursive: true });
76
+ const html = BLANK_HTML.replaceAll('__TITLE__', prettyName);
77
+ writeRel(dir, 'index.html', html);
78
+ writeRel(dir, 'style.css', BLANK_CSS);
79
+ writeRel(dir, 'game.js', BLANK_JS);
80
+ writeRel(dir, notesFileName, renderTermiMd(prettyName, {
81
+ whatThisIs: `A browser game: ${ideaLabel}. Built with Termi.`,
82
+ files: ['index.html', 'style.css', 'game.js'],
83
+ builtSoFar: ['Blank game shell ready for the first prompt'],
84
+ recapLine: `We started ${prettyName}.`,
85
+ }));
86
+ const now = new Date().toISOString();
87
+ saveProjectMeta({
88
+ slug,
89
+ prettyName,
90
+ scaffoldId: 'games',
91
+ themeId: 'blank',
92
+ createdAt: now,
93
+ lastOpenedAt: now,
94
+ });
95
+ const project = openProject(slug);
96
+ if (project === null) {
97
+ throw new Error('Something went wrong while making the project. Try again.');
98
+ }
99
+ return project;
100
+ }
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Kid-facing Build-a-game idea bank.
3
+ *
4
+ * Exactly 31 entries: "Build my own idea" first, then 30 browser HTML games.
5
+ * Every idea is local-preview only (HTML/CSS/JS), no image generation, no
6
+ * native installs. Pure data for tests and the Build a game menu.
7
+ */
8
+ /** True for the custom entry that asks the kid for their own concept. */
9
+ export function isOwnIdea(idea) {
10
+ return idea.id === 'own';
11
+ }
12
+ /**
13
+ * Menu order is fixed. First entry is always Build my own idea.
14
+ * The remaining 30 are distinct local-browser game concepts.
15
+ */
16
+ export const GAME_IDEAS = [
17
+ {
18
+ id: 'own',
19
+ label: 'Build my own idea',
20
+ blurb: 'You invent the game. Termi helps you build it.',
21
+ seedPrompt: '',
22
+ },
23
+ {
24
+ id: 'dodge-rain',
25
+ label: 'Dodge the rain',
26
+ blurb: 'Move left and right. Catch drops or dodge them.',
27
+ seedPrompt: 'Make a simple canvas game where I move left and right to dodge falling rain drops. Add a score and a restart key.',
28
+ },
29
+ {
30
+ id: 'catch-stars',
31
+ label: 'Catch the stars',
32
+ blurb: 'Catch falling stars before they hit the ground.',
33
+ seedPrompt: 'Make a canvas catch game. Stars fall from the top. I move a basket to catch them. Show the score and lives.',
34
+ },
35
+ {
36
+ id: 'memory-cards',
37
+ label: 'Memory cards',
38
+ blurb: 'Flip cards and match pairs.',
39
+ seedPrompt: 'Make a memory match card game with 12 cards (6 pairs) using emoji. Click to flip. Count moves. Show a win message.',
40
+ },
41
+ {
42
+ id: 'click-targets',
43
+ label: 'Click the targets',
44
+ blurb: 'Click circles before they vanish.',
45
+ seedPrompt: 'Make a click target game. Circles appear for a short time. Click them for points. Add a 30 second timer and a final score.',
46
+ },
47
+ {
48
+ id: 'maze-escape',
49
+ label: 'Maze escape',
50
+ blurb: 'Find the exit in a simple maze.',
51
+ seedPrompt: 'Make a keyboard maze game on a canvas grid. Arrow keys move a square. Reach the green exit. Keep walls solid.',
52
+ },
53
+ {
54
+ id: 'snake-lite',
55
+ label: 'Snake lite',
56
+ blurb: 'Grow a snake by eating dots.',
57
+ seedPrompt: 'Make a simple snake game on a grid. Arrow keys move. Eat food to grow. Hitting a wall or yourself restarts.',
58
+ },
59
+ {
60
+ id: 'pong-solo',
61
+ label: 'Pong solo',
62
+ blurb: 'Bounce a ball with one paddle.',
63
+ seedPrompt: 'Make a one player pong game. Move the paddle with the mouse or arrows. Ball bounces. Score points. Restart on miss.',
64
+ },
65
+ {
66
+ id: 'whack-moles',
67
+ label: 'Whack a mole',
68
+ blurb: 'Tap moles as they pop up.',
69
+ seedPrompt: 'Make a whack a mole game with a 3 by 3 grid of holes. Moles pop up briefly. Click them for points. Add a timer.',
70
+ },
71
+ {
72
+ id: 'color-match',
73
+ label: 'Color match',
74
+ blurb: 'Match the word color to the right button.',
75
+ seedPrompt: 'Make a color word game. Show a color name in a random ink color. Kid clicks the button that matches the WORD meaning. Score streaks.',
76
+ },
77
+ {
78
+ id: 'typing-race',
79
+ label: 'Typing race',
80
+ blurb: 'Type the word before time runs out.',
81
+ seedPrompt: 'Make a typing race. Show a simple word. Kid types it and presses Enter. Track speed and accuracy for 10 words.',
82
+ },
83
+ {
84
+ id: 'platform-jump',
85
+ label: 'Platform jumper',
86
+ blurb: 'Jump across platforms to the flag.',
87
+ seedPrompt: 'Make a side view platformer on canvas. Arrow keys move and jump. Reach a flag on the right. Simple gravity and platforms.',
88
+ },
89
+ {
90
+ id: 'balloon-pop',
91
+ label: 'Balloon pop',
92
+ blurb: 'Pop balloons before they float away.',
93
+ seedPrompt: 'Make a balloon pop game. Balloons float up. Click to pop them. Missed balloons cost a life. Show score.',
94
+ },
95
+ {
96
+ id: 'quiz-sprint',
97
+ label: 'Quiz sprint',
98
+ blurb: 'Answer fun multiple choice questions.',
99
+ seedPrompt: 'Make a 5 question multiple choice quiz about animals. Show score at the end. Use big clear buttons.',
100
+ },
101
+ {
102
+ id: 'simon-says',
103
+ label: 'Simon says lights',
104
+ blurb: 'Repeat the light pattern.',
105
+ seedPrompt: 'Make a Simon style memory game with 4 colored buttons. Play a growing pattern. Kid repeats it. Score the longest streak.',
106
+ },
107
+ {
108
+ id: 'rock-paper',
109
+ label: 'Rock paper scissors',
110
+ blurb: 'Play against the computer. Pick rock, paper, or scissors.',
111
+ seedPrompt: 'Make rock paper scissors with three big buttons. Computer picks at random. Track wins, losses, and ties.',
112
+ },
113
+ {
114
+ id: 'number-guess',
115
+ label: 'Number guess',
116
+ blurb: 'Guess the secret number.',
117
+ seedPrompt: 'Make a number guess game from 1 to 50. Kid types a guess. Say higher or lower. Count tries until they win.',
118
+ },
119
+ {
120
+ id: 'breakout-lite',
121
+ label: 'Breakout lite',
122
+ blurb: 'Bounce a ball to break bricks.',
123
+ seedPrompt: 'Make a simple breakout game. Paddle at the bottom, ball, and one row of bricks. Clear the bricks to win.',
124
+ },
125
+ {
126
+ id: 'flappy-lite',
127
+ label: 'Flappy lite',
128
+ blurb: 'Tap to flap between gaps.',
129
+ seedPrompt: 'Make a flappy style game. Space or click flaps up. Gravity pulls down. Fly through gaps in pipes. Score each pipe.',
130
+ },
131
+ {
132
+ id: 'tic-tac-toe',
133
+ label: 'Tic tac toe',
134
+ blurb: 'Play X and O on a 3 by 3 board.',
135
+ seedPrompt: 'Make tic tac toe for two players on one keyboard. Click cells. Detect win and draw. Add a new game button.',
136
+ },
137
+ {
138
+ id: 'collect-coins',
139
+ label: 'Collect coins',
140
+ blurb: 'Run around and grab all the coins.',
141
+ seedPrompt: 'Make a top down collect game. Arrow keys move a player. Coins sit on a small map. Collect all coins to win.',
142
+ },
143
+ {
144
+ id: 'avoid-cars',
145
+ label: 'Avoid the cars',
146
+ blurb: 'Cross the road safely.',
147
+ seedPrompt: 'Make a cross the road game. Player moves up. Cars move left and right. Reach the top without hitting a car.',
148
+ },
149
+ {
150
+ id: 'reaction-test',
151
+ label: 'Reaction test',
152
+ blurb: 'Click when the screen turns green.',
153
+ seedPrompt: 'Make a reaction time test. Screen starts red, then turns green after a random wait. Click as fast as you can. Show ms time.',
154
+ },
155
+ {
156
+ id: 'word-scramble',
157
+ label: 'Word scramble',
158
+ blurb: 'Unscramble the letters.',
159
+ seedPrompt: 'Make a word scramble game with 8 easy words. Show mixed letters. Kid types the word. Track score out of 8.',
160
+ },
161
+ {
162
+ id: 'tower-stack',
163
+ label: 'Tower stack',
164
+ blurb: 'Stack blocks as high as you can.',
165
+ seedPrompt: 'Make a stack game. A block slides left and right. Click or press space to drop it. Stack as high as you can without missing.',
166
+ },
167
+ {
168
+ id: 'balloon-math',
169
+ label: 'Balloon math',
170
+ blurb: 'Pop the balloon with the right answer.',
171
+ seedPrompt: 'Make a math balloon game. Show a simple add or subtract problem. Three balloons show answers. Click the correct one.',
172
+ },
173
+ {
174
+ id: 'day-night',
175
+ label: 'Day and night runner',
176
+ blurb: 'Run while the world switches day and night.',
177
+ seedPrompt: 'Make a runner where the player auto runs right. Jump over rocks. Every 10 seconds switch day and night colors. Track distance.',
178
+ },
179
+ {
180
+ id: 'fruit-slice',
181
+ label: 'Fruit slice',
182
+ blurb: 'Swipe or click fruit, avoid bombs.',
183
+ seedPrompt: 'Make a fruit slice style game. Fruit flies up. Click fruit for points. Clicking a bomb ends the round. Show score.',
184
+ },
185
+ {
186
+ id: 'hide-seek',
187
+ label: 'Hide and seek dots',
188
+ blurb: 'Find the odd color in the crowd.',
189
+ seedPrompt: 'Make a find the odd one out game. Show many colored dots. One is a slightly different color. Click it. Levels get harder.',
190
+ },
191
+ {
192
+ id: 'space-lander',
193
+ label: 'Soft lander',
194
+ blurb: 'Land the ship gently on the pad.',
195
+ seedPrompt: 'Make a lander game. Ship falls with gravity. Arrow keys thrust and steer. Land softly on a pad. Crash if too fast.',
196
+ },
197
+ {
198
+ id: 'story-choice',
199
+ label: 'Choice adventure',
200
+ blurb: 'A short story with choices you click.',
201
+ seedPrompt: 'Make a short click adventure with 4 scenes. Each scene has text and 2 choice buttons. End with a happy finish screen.',
202
+ },
203
+ ];
204
+ /** Exactly one own-idea entry plus 30 game ideas. */
205
+ export const GAME_IDEA_COUNT = GAME_IDEAS.length;
206
+ /** Menu options for selects: value is idea id. */
207
+ export function gameIdeaMenuOptions() {
208
+ return GAME_IDEAS.map((idea) => ({
209
+ value: idea.id,
210
+ label: idea.label,
211
+ hint: idea.blurb,
212
+ }));
213
+ }
214
+ export function gameIdeaById(id) {
215
+ return GAME_IDEAS.find((idea) => idea.id === id);
216
+ }