playable-lessons 0.2.1 → 0.6.2
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 +198 -6
- package/out/cli/index.js +415 -37
- package/out/shared/aiTaskExport.js +161 -0
- package/out/shared/caseStudyExport.js +115 -0
- package/out/shared/flashcardExport.js +161 -0
- package/out/shared/generate.js +490 -11
- package/out/shared/h5pExporter.js +286 -0
- package/out/shared/imageClient.js +227 -0
- package/out/shared/inkParser.js +187 -0
- package/out/shared/inkRuntime.generated.js +9 -0
- package/out/shared/planExport.js +110 -0
- package/out/shared/project.js +12 -0
- package/out/shared/prompts.js +433 -1
- package/out/shared/quizExport.js +182 -0
- package/out/shared/storyExport.js +72 -20
- package/out/shared/summaryExport.js +144 -0
- package/out/shared/version.generated.js +7 -0
- package/package.json +9 -5
package/README.md
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
# Playable Lessons
|
|
2
2
|
|
|
3
|
-
Turn educational source material into **playable interactive fiction**. Paste in a case study, lecture notes, a methodology, or just a topic, and Playable Lessons uses AI to weave it into a branching [Ink](https://www.inklestudios.com/ink/) story that learners can play — then export it as a self-contained HTML file, an Ink/Twee source file, or a printable walkthrough.
|
|
3
|
+
Turn educational source material into **playable interactive fiction**. Paste in a case study, lecture notes, a methodology, or just a topic, and Playable Lessons uses AI to weave it into a branching [Ink](https://www.inklestudios.com/ink/) story that learners can play — then export it as a self-contained HTML file, an Ink/Twee source file, or a printable walkthrough. It can also turn the same material into **study flashcards**, a **multiple-choice quiz**, a **study summary**, **AI-collaboration tasks**, or a **teaching case study**. A **Plan** mode recommends which outputs suit your material and can generate them all at once.
|
|
4
4
|
|
|
5
5
|
It ships in two forms:
|
|
6
6
|
|
|
7
|
-
- **Desktop app** (Electron + React) — the full authoring experience: AI generation with an interactive clarification step, a node-graph editor, live player, and exports. Aimed at non-technical authors (e.g. educators).
|
|
8
|
-
- **CLI** (`playable-lessons`, on npm) — the same engine, headless: **generate**, **validate**, and **export**. Handy for scripts, CI, batch runs, and local/offline generation against Ollama.
|
|
7
|
+
- **Desktop app** (Electron + React) — the full authoring experience: AI generation with an interactive clarification step, a node-graph editor, live player, a **Projects dashboard** (save / reopen / delete lessons as ownable folders on disk), **inline editing** with AI refinement (✨), and exports. Stories can be illustrated with an **AI-generated image per scene** (cartoon or photorealistic) using your own image provider — OpenAI, Google Imagen, or any OpenAI-compatible endpoint. Aimed at non-technical authors (e.g. educators).
|
|
8
|
+
- **CLI** (`playable-lessons`, on npm) — the same engine, headless: **generate** an Ink story, **flashcards**, a **quiz**, a **summary**, **AI-collaboration tasks**, or a **case study**; **plan** a recommended set; **validate**, and **export**. Handy for scripts, CI, batch runs, and local/offline generation against Ollama.
|
|
9
|
+
- **Self-hosted web** (Docker) — a browser-based version for institutions (e.g. university staff). The server holds the API key; users just enter an access code. No per-user keys, no accounts — work is stored per-browser.
|
|
9
10
|
|
|
10
|
-
The desktop app and the CLI share one generation pipeline; the app adds the interactive clarification step
|
|
11
|
+
The desktop app and the CLI share one generation pipeline; the app adds the interactive clarification step, visual editor, and inline editing on top.
|
|
12
|
+
|
|
13
|
+
A public landing page with a **try-it demo** (bring your own key) is at **[michael-borck.github.io/playable-lessons](https://michael-borck.github.io/playable-lessons/)**.
|
|
11
14
|
|
|
12
15
|
---
|
|
13
16
|
|
|
@@ -33,8 +36,13 @@ playable-lessons generate --topic "Phishing awareness for staff" -p openai -o le
|
|
|
33
36
|
|
|
34
37
|
# Pipe source material via stdin:
|
|
35
38
|
cat lecture.txt | playable-lessons generate -o lesson/ -p ollama -m llama3.1:8b
|
|
39
|
+
|
|
40
|
+
# With a generated illustration per scene (cartoon or photorealistic):
|
|
41
|
+
OPENAI_API_KEY=sk-... playable-lessons generate -i notes.md -p ollama -o lesson/ --images --image-style cartoon
|
|
36
42
|
```
|
|
37
43
|
|
|
44
|
+
`--images` adds a `# IMAGE_PROMPT:` tag to every scene, then generates each image with an image provider (`--image-provider openai|gemini|custom`, inferred from `OPENAI_API_KEY`/`GEMINI_API_KEY`/`--image-base-url`; `--image-model` to override the default model). Images are embedded in the HTML export and written to an `images/` folder.
|
|
45
|
+
|
|
38
46
|
Provider selection and API keys:
|
|
39
47
|
|
|
40
48
|
| Provider | `--provider` | Key (env var) | Notes |
|
|
@@ -44,7 +52,120 @@ Provider selection and API keys:
|
|
|
44
52
|
| Ollama | `ollama` | none (or `OLLAMA_API_KEY`) | local by default; `--ollama-url` for remote |
|
|
45
53
|
| Custom | `custom` | `PLAYABLE_LESSONS_API_KEY` / `OPENAI_API_KEY` | OpenAI-compatible; requires `--base-url` + `--model` |
|
|
46
54
|
|
|
47
|
-
If `--provider` is omitted it's inferred from the environment (Claude → OpenAI → Ollama). Keys are read from env vars only — the CLI never touches the OS keychain (that's the desktop app). Other flags: `--mode`, `--length` (short/medium/long), `--tone`, `--protagonist`, `--answers`, `--title
|
|
55
|
+
If `--provider` is omitted it's inferred from the environment (Claude → OpenAI → Ollama). Keys are read from env vars only — the CLI never touches the OS keychain (that's the desktop app). Other flags: `--mode`, `--length` (short/medium/long), `--tone`, `--protagonist`, `--answers`, `--title`, and `--target` (story | flashcards | quiz | summary | ai-task | case-study — see the per-target sections below). Run `playable-lessons generate --help` for all options.
|
|
56
|
+
|
|
57
|
+
### Generate flashcards
|
|
58
|
+
|
|
59
|
+
Add `--target flashcards` to turn the same source material into a deck of study flashcards instead of an Ink story. Each card has a front, back, optional hint, and optional tag.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Flashcards from a topic, offline via Ollama (no API key needed):
|
|
63
|
+
playable-lessons generate --topic "Supply chain resilience" --target flashcards -p ollama -m llama3.1:8b -o deck/
|
|
64
|
+
|
|
65
|
+
# From a file, exporting every format and setting the card count:
|
|
66
|
+
playable-lessons generate -i notes.md --target flashcards --cards 20 -o deck/ -f csv,html,anki,json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| Format | Output |
|
|
70
|
+
| ------ | ------ |
|
|
71
|
+
| `csv` | `front,back,hint,tag` — imports into Anki, Quizlet, Excel (`.flashcards.csv`) |
|
|
72
|
+
| `html` | Self-contained flip-card deck (`.flashcards.html`) — click or Space to flip, arrow keys to navigate; works offline |
|
|
73
|
+
| `anki` | Tab-separated import for Anki: front, back, tag (`.flashcards.txt`) |
|
|
74
|
+
| `json` | The raw structured deck (`.flashcards.json`) |
|
|
75
|
+
|
|
76
|
+
The default format for flashcards is `csv,html`; use `--cards N` to set the count (default 12). The same source can yield both a story and a deck — run `generate` twice with different `--target`.
|
|
77
|
+
|
|
78
|
+
### Generate a quiz
|
|
79
|
+
|
|
80
|
+
Add `--target quiz` to produce a self-marking multiple-choice quiz from the same source material. Each question has a stem, 2–6 options, the correct option's index, and an optional explanation.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
playable-lessons generate --topic "Supply chain resilience" --target quiz -p ollama -m llama3.1:8b -o quiz/
|
|
84
|
+
|
|
85
|
+
playable-lessons generate -i notes.md --target quiz --cards 20 -o quiz/ -f html,json,txt
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
| Format | Output |
|
|
89
|
+
| ------ | ------ |
|
|
90
|
+
| `html` | Self-contained interactive quiz (`.quiz.html`) — pick answers, submit for an instant score, with correct/wrong highlighting and explanations; works offline |
|
|
91
|
+
| `json` | The raw structured quiz (`.quiz.json`) |
|
|
92
|
+
| `txt` | A printable quiz with an answer key (`.quiz.txt`) |
|
|
93
|
+
|
|
94
|
+
The default format for quiz is `html,json`; `--cards N` sets the number of questions.
|
|
95
|
+
|
|
96
|
+
### Generate a summary
|
|
97
|
+
|
|
98
|
+
Add `--target summary` to distill the source material into a study summary: a short overview, the key points, and a glossary of essential terms.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
playable-lessons generate --topic "Supply chain resilience" --target summary -p ollama -m llama3.1:8b -o summary/
|
|
102
|
+
|
|
103
|
+
playable-lessons generate -i notes.md --target summary --cards 10 -o summary/ -f html,txt,json
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Format | Output |
|
|
107
|
+
| ------ | ------ |
|
|
108
|
+
| `html` | Self-contained study sheet (`.summary.html`) — overview, numbered key points, glossary; works offline |
|
|
109
|
+
| `txt` | A printable summary with key points and a glossary (`.summary.txt`) |
|
|
110
|
+
| `json` | The raw structured summary (`.summary.json`) |
|
|
111
|
+
|
|
112
|
+
The default format for summary is `html,txt`; `--cards N` sets the target number of key points.
|
|
113
|
+
|
|
114
|
+
### Generate AI-collaboration tasks
|
|
115
|
+
|
|
116
|
+
Add `--target ai-task` to re-engineer the material into **AI-collaboration tasks** — tasks a learner completes by *iteratively interrogating* an LLM chatbot. Each task is built so the chatbot's first answer is wrong or incomplete until the learner probes it: a concrete scenario with 2–4 **load-bearing specifics**, a student-facing brief, a deliverable, an **engagement-anchored rubric** (not a correctness rubric), and a note on why a delegating learner loses. (This output never produces quizzes/flashcards/summaries — pick those targets for those.)
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
playable-lessons generate --topic "clinic outreach on a budget cut" --target ai-task -p ollama -m llama3.1:8b -o tasks/ --cards 3
|
|
120
|
+
|
|
121
|
+
playable-lessons generate -i worksheet.md --target ai-task -o tasks/ -f html,txt,json
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
| Format | Output |
|
|
125
|
+
| ------ | ------ |
|
|
126
|
+
| `html` | Self-contained task sheet (`.ai-task.html`) — scenario, brief, deliverable, load-bearing specifics, rubric; works offline |
|
|
127
|
+
| `txt` | A printable version of the tasks (`.ai-task.txt`) |
|
|
128
|
+
| `json` | The raw structured task set (`.ai-task.json`) |
|
|
129
|
+
|
|
130
|
+
The default format for ai-task is `html,txt`; `--cards N` sets the number of tasks (try `--cards 3` for richer tasks).
|
|
131
|
+
|
|
132
|
+
### Generate a case study
|
|
133
|
+
|
|
134
|
+
Add `--target case-study` to turn the material into a **teaching case study** — a realistic situation (protagonist, situation, key facts, conflict, decision points, discussion questions) a learner analyzes. Use `--depth` to control how much is produced:
|
|
135
|
+
|
|
136
|
+
| depth | produces |
|
|
137
|
+
| --- | --- |
|
|
138
|
+
| `idea` | a brief premise (protagonist, situation, conflict) |
|
|
139
|
+
| `outline` | the full structured skeleton, no prose |
|
|
140
|
+
| `complete` *(default)* | the full skeleton plus a written narrative |
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
playable-lessons generate --topic "clinic scheduling under staff shortages" --target case-study --depth complete -p ollama -m llama3.1:8b -o case/
|
|
144
|
+
|
|
145
|
+
playable-lessons generate -i notes.md --target case-study --depth outline -o case/ -f html,txt,json
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
| Format | Output |
|
|
149
|
+
| ------ | ------ |
|
|
150
|
+
| `html` | Self-contained case-study sheet (`.case-study.html`) — narrative + structured sections; works offline |
|
|
151
|
+
| `txt` | A printable version of the case study (`.case-study.txt`) |
|
|
152
|
+
| `json` | The raw structured case study (`.case-study.json`) |
|
|
153
|
+
|
|
154
|
+
The default format for case-study is `html,txt`; `--depth` selects idea | outline | complete (default complete).
|
|
155
|
+
|
|
156
|
+
### Plan a set of outputs (recommender)
|
|
157
|
+
|
|
158
|
+
Not sure which outputs to make? `plan` analyzes the material and recommends a complementary set (2–4 of the targets above), then optionally generates them all into the output folder.
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# Just recommend a set:
|
|
162
|
+
playable-lessons plan --topic "supply chain resilience" -p ollama -m llama3.1:8b -o plan/
|
|
163
|
+
|
|
164
|
+
# Recommend AND generate every recommended output:
|
|
165
|
+
playable-lessons plan -i notes.md --apply -o plan/
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`plan` writes `plan.json` + `plan.txt` + `plan.html` (the recommendation). With `--apply` it also generates each recommended target (`.html` + `.json`). In the desktop app, the **Plan** target shows the recommendation with a “Generate all → new project” action that writes the whole set to a project folder.
|
|
48
169
|
|
|
49
170
|
### Validate an Ink file
|
|
50
171
|
|
|
@@ -90,11 +211,82 @@ npm test
|
|
|
90
211
|
|
|
91
212
|
The desktop app supports **Claude**, **OpenAI**, a local **Ollama**, and any **OpenAI-compatible endpoint** (remote Ollama, OpenRouter, LiteLLM, vLLM, …) via a base URL + bearer token. Model IDs are editable, with live model listing and a connection test. API keys are stored in your OS keychain (via Electron `safeStorage`), not in plain config.
|
|
92
213
|
|
|
214
|
+
### Inline editing + AI refinement
|
|
215
|
+
|
|
216
|
+
After generating any output (flashcards, quiz, summary, AI-collaboration tasks, case study), switch to **Study → Edit** to tweak the content inline — edit text fields, add/delete items, and use the **✨** button to AI-refine individual fields. Click **💬** before ✨ to give a custom direction (e.g. "simplify for beginners"). Changes flow through to exports.
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Self-hosted web (Docker)
|
|
221
|
+
|
|
222
|
+
For institutions or teams that want a browser-based version without per-user API keys. The server holds the key; users enter a shared access code.
|
|
223
|
+
|
|
224
|
+
### Quick start
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# 1. Grab the files
|
|
228
|
+
curl -sL https://raw.githubusercontent.com/michael-borck/playable-lessons/main/docker-compose.yml -o docker-compose.yml
|
|
229
|
+
mkdir -p server
|
|
230
|
+
curl -sL https://raw.githubusercontent.com/michael-borck/playable-lessons/main/server/.env.example -o server/.env
|
|
231
|
+
|
|
232
|
+
# 2. Edit server/.env — set your API key + optional access code
|
|
233
|
+
# ANTHROPIC_API_KEY=sk-ant-...
|
|
234
|
+
# ACCESS_CODE=staff-2024 (comma-separated for multiple groups; blank = no gate)
|
|
235
|
+
|
|
236
|
+
# 3. Swap build for the prebuilt image in docker-compose.yml
|
|
237
|
+
# image: ghcr.io/michael-borck/playable-lessons:latest
|
|
238
|
+
|
|
239
|
+
# 4. Deploy
|
|
240
|
+
docker compose pull && docker compose up -d
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Staff access the server at `http://your-server:3000`, enter the access code, and generate. Work is stored per-browser (localStorage); the AI key stays server-side. Rate limiting is configurable (`RATE_LIMIT_PER_HOUR`, default 30) and counts only generation submissions, not status polling.
|
|
244
|
+
|
|
245
|
+
Generations run as an **in-process job queue** (one at a time by default; `GENERATE_CONCURRENCY` to raise). Visitors see their queue position and live pipeline progress, can **cancel while waiting** (a job already running plays out — the pipeline has no abort switch), and can **close the page and come back** — results stay fetchable for an hour via a job id in localStorage. Jobs live in memory: a server restart clears the queue, and the UI says so honestly.
|
|
246
|
+
|
|
247
|
+
To revoke a group: remove their code from `.env` → `docker compose restart`. To update: change the image tag → `docker compose pull && docker compose up -d`.
|
|
248
|
+
|
|
249
|
+
### Scene images (optional)
|
|
250
|
+
|
|
251
|
+
The hosted app can offer "illustrate every scene" (cartoon or photorealistic) on stories. It's off by default; enable it in `.env` with `SCENE_IMAGES=true` plus one image provider:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
# Option A — Google Imagen (cheap, fast, ~$0.02/image; good default for a public server):
|
|
255
|
+
SCENE_IMAGES=true
|
|
256
|
+
GEMINI_API_KEY=AIza...
|
|
257
|
+
|
|
258
|
+
# Option B — OpenAI (gpt-image-1; reuses OPENAI_API_KEY if IMAGE_API_KEY is blank):
|
|
259
|
+
# SCENE_IMAGES=true
|
|
260
|
+
# IMAGE_PROVIDER=openai
|
|
261
|
+
|
|
262
|
+
# Option C — self-hosted generator on the same VPS (LocalAI, included in the compose file):
|
|
263
|
+
# docker compose --profile images up -d
|
|
264
|
+
# SCENE_IMAGES=true
|
|
265
|
+
# IMAGE_PROVIDER=custom
|
|
266
|
+
# IMAGE_BASE_URL=http://localai:8080/v1
|
|
267
|
+
# IMAGE_MODEL=stablediffusion
|
|
268
|
+
|
|
269
|
+
# Option D — your own SwarmUI server (GPU, free per image, native API):
|
|
270
|
+
# SCENE_IMAGES=true
|
|
271
|
+
# IMAGE_PROVIDER=swarmui
|
|
272
|
+
# IMAGE_BASE_URL=https://swarmui.example.org
|
|
273
|
+
# IMAGE_MODEL=juggernautXL_v9
|
|
274
|
+
# IMAGE_API_KEY=... # only if your SwarmUI is behind auth
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Notes for operators:
|
|
278
|
+
|
|
279
|
+
- Each illustrated story triggers up to `IMAGE_MAX_SCENES` image generations (default 8) — combined with the per-IP `RATE_LIMIT_PER_HOUR`, that bounds your image spend.
|
|
280
|
+
- The LocalAI service runs **CPU-only by default and is slow** (a minute or more per scene on a small VPS). It's internal-only (no published port — LocalAI has no auth). For usable self-hosting, use a GPU host and the `localai/localai:*-gpu-*` images; for a CPU-only VPS, prefer Gemini/OpenAI.
|
|
281
|
+
- First LocalAI start downloads a multi-GB model into the `localai-models` volume.
|
|
282
|
+
- If the app sits behind nginx/Caddy, raise `proxy_read_timeout` (e.g. `300s`) — illustrated stories take minutes — and set `TRUST_PROXY=1` so the per-IP rate limiter sees real visitor IPs instead of one shared proxy bucket.
|
|
283
|
+
|
|
93
284
|
---
|
|
94
285
|
|
|
95
286
|
## Tech stack
|
|
96
287
|
|
|
97
|
-
Electron + electron-vite · React 19 + TypeScript · Zustand · [inkjs](https://github.com/y-lohse/inkjs) (Ink compiler + runtime) · @xyflow/react (graph editor) · yargs (CLI).
|
|
288
|
+
Electron + electron-vite · React 19 + TypeScript · Zustand · [inkjs](https://github.com/y-lohse/inkjs) (Ink compiler + runtime) · @xyflow/react (graph editor) · yargs (CLI) · Express (self-hosted server).
|
|
289
|
+
|
|
98
290
|
|
|
99
291
|
## License
|
|
100
292
|
|