playable-lessons 0.2.2 → 0.6.3

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
@@ -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 and visual editor on top.
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`. Run `playable-lessons generate --help` for all options.
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