marimo-learn
Formative assessment widgets for marimo notebooks.
Installation
pip install marimo-learn
Usage in marimo
from marimo_learn import FlashcardWidget, MultipleChoiceWidget, OrderingWidget
from marimo_learn import MatchingWidget, LabelingWidget, ConceptMapWidget
Each widget is an anywidget component. Pass the configuration as constructor
arguments; the widget syncs state back to Python via the value traitlet.
Standalone JavaScript bundle
The widgets are also published as an npm package:
npm install marimo-learn
Or load the bundle directly in HTML:
<script type="module" src="marimo-learn.js"></script>
The bundle auto-mounts any .marimo-* divs it finds in the page (see
HTML configuration below). You can also call the render
functions manually:
import { renderFlashcard, renderMultipleChoice, renderOrdering,
renderMatching, renderLabeling, renderConceptMap } from 'marimo-learn';
renderMultipleChoice(document.getElementById('target'), {
question: 'What is 2 + 2?',
options: ['3', '4', '5'],
correct_answer: 1,
lang: 'en',
});
HTML configuration
When the bundle is loaded, autoMount() scans the page for divs with the
class marimo-<widget>, parses the HTML markup inside each div, and replaces
it with a live widget. This lets you author exercises in plain HTML or
Markdown.
Multiple choice
<div class="marimo-multiple-choice" data-correct="2">
<p>Question text</p>
<ol>
<li>Option A</li>
<li>Option B</li>
<li>Option C (correct — zero-based index matches data-correct)</li>
<li>Option D</li>
</ol>
</div>
data-correct— zero-based index of the correct option (required)data-lang— language code, defaulten
Flashcard
<div class="marimo-flashcard">
<p>Deck title (optional)</p>
<dl>
<dt>Front of card 1</dt>
<dd>Back of card 1</dd>
<dt>Front of card 2</dt>
<dd>Back of card 2</dd>
</dl>
</div>
Cards are shuffled automatically. data-lang sets the language (default en).
Ordering
<div class="marimo-ordering">
<p>Question text</p>
<ol>
<li>First step (correct position)</li>
<li>Second step</li>
<li>Third step</li>
</ol>
</div>
The <ol> lists items in the correct order. The widget shuffles them
before presenting them to the student. data-lang sets the language (default
en).
Matching
<div class="marimo-matching">
<p>Question text</p>
<table>
<tr><td>Left item 1</td><td>Right item 1</td></tr>
<tr><td>Left item 2</td><td>Right item 2</td></tr>
<tr><td>Left item 3</td><td>Right item 3</td></tr>
</table>
</div>
Each row defines a matched pair. The right column is shuffled automatically.
data-lang sets the language (default en).
Labeling
<div class="marimo-labeling">
<p>Question text</p>
<table>
<tr><td>Text line 1</td><td>Label name</td></tr>
<tr><td>Text line 2</td><td>Label name</td></tr>
<tr><td>Text line 3</td><td>Label A, Label B</td></tr>
</table>
</div>
Column 1 is the text to label; column 2 is the correct label name. Multiple
correct labels for one line are written as a comma-separated list. The
available label set is derived automatically from the unique names in column 2.
data-lang sets the language (default en).
Concept map
<div class="marimo-concept-map">
<p>Question text</p>
<table>
<tr><td>Source node</td><td>relationship</td><td>Target node</td></tr>
<tr><td>Node A</td> <td>leads to</td> <td>Node B</td></tr>
<tr><td>Node B</td> <td>leads to</td> <td>Node C</td></tr>
</table>
</div>
Each row defines one correct directed edge: source → label → target. The node
list and term list are inferred automatically from the table in
first-appearance order. data-lang sets the language (default en).
Utilities for use in marimo notebooks.
Color
Bases: str, Enum
Standard colors available to Turtle turtles.
Source code in src/marimo_learn/turtle.py
38 39 40 41 42 43 44 45 46 47 | |
ConceptMapWidget
Bases: BaseWidget
A concept mapping widget where students draw labeled directed edges between concepts.
Students select a relationship term then click two concept nodes to connect them. Concept nodes can be dragged to rearrange the layout.
Attributes:
| Name | Type | Description |
|---|---|---|
question |
str
|
The question or prompt shown above the map |
concepts |
list
|
List of concept names (nodes) |
terms |
list
|
List of relationship terms that can label edges |
correct_edges |
list
|
List of dicts with 'from', 'to', 'label' keys |
value |
dict
|
State with 'edges', 'score', 'total', and 'correct' keys |
Source code in src/marimo_learn/concept_map.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
FlashcardWidget
Bases: BaseWidget
A flashcard widget with self-reported spaced repetition.
Students flip cards to reveal the answer, then rate themselves (Got it / Almost / No). Cards rated "Almost" or "No" are re-inserted into the queue; the deck is complete when all cards are rated "Got it".
Attributes:
| Name | Type | Description |
|---|---|---|
question |
str
|
Optional heading shown above the deck |
cards |
list
|
List of dicts with 'front' and 'back' keys |
shuffle |
bool
|
Whether to shuffle the deck initially |
value |
dict
|
State with 'results' (per-card ratings/attempts) and 'complete' |
Source code in src/marimo_learn/flashcard.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | |
LabelingWidget
Bases: BaseWidget
A text labeling widget where students drag numbered labels to text lines.
Attributes:
| Name | Type | Description |
|---|---|---|
question |
str
|
The question text to display |
labels |
list
|
List of label texts (shown on left) |
text_lines |
list
|
List of text lines to be labeled (shown on right) |
correct_labels |
dict
|
Mapping of line indices to lists of correct label indices |
value |
dict
|
Current state with 'placed_labels', 'score', 'total', and 'correct' keys |
Source code in src/marimo_learn/labeling.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | |
MatchingWidget
Bases: BaseWidget
A matching question widget where students pair items from two columns using drag-and-drop.
Attributes:
| Name | Type | Description |
|---|---|---|
question |
str
|
The question text to display |
left |
list
|
Items in the left column |
right |
list
|
Items in the right column |
correct_matches |
dict
|
Mapping of left column indices to right column indices |
value |
dict
|
Current state with 'matches', 'correct', and 'score' keys |
Source code in src/marimo_learn/matching.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | |
MultipleChoiceWidget
Bases: BaseWidget
A multiple choice question widget.
Attributes:
| Name | Type | Description |
|---|---|---|
question |
str
|
The question text to display |
options |
list
|
List of answer options |
correct_answer |
int
|
Index of the correct answer (0-based) |
explanation |
str
|
Optional explanation text shown after answering |
value |
dict
|
Current state with 'selected', 'correct', and 'answered' keys |
Source code in src/marimo_learn/multiple_choice.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | |
NumericEntryWidget
Bases: BaseWidget
A numeric entry question widget.
Attributes:
| Name | Type | Description |
|---|---|---|
question |
str
|
The question text to display |
correct_answer |
float
|
The expected numeric answer |
tolerance |
float
|
Acceptance window; answer is correct if |entered - correct_answer| < tolerance |
explanation |
str
|
Optional explanation shown after answering |
value |
dict
|
Current state with 'entered', 'correct', 'ok', and 'answered' keys |
Source code in src/marimo_learn/numeric_entry.py
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | |
OrderingWidget
Bases: BaseWidget
An ordering question widget where students arrange items in sequence using drag-and-drop.
Attributes:
| Name | Type | Description |
|---|---|---|
question |
str
|
The question text to display |
items |
list
|
Items in the correct order |
shuffle |
bool
|
Whether to shuffle items initially |
value |
dict
|
Current state with 'order' and 'correct' keys |
Source code in src/marimo_learn/ordering.py
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | |
PredictThenCheckWidget
Bases: BaseWidget
A predict-then-check question widget.
The learner reads a code snippet, selects their predicted output from multiple choice options, receives immediate feedback, and can then reveal the actual output to verify by running the code themselves.
Attributes:
| Name | Type | Description |
|---|---|---|
question |
str
|
The question text to display |
code |
str
|
The code block shown to the learner |
output |
str
|
The actual output revealed when the learner clicks "Reveal Output" |
options |
list
|
List of candidate output strings |
correct_answer |
int
|
Index of the correct option (0-based) |
explanations |
list
|
Per-option explanation strings shown after answering |
explanation |
str
|
Fallback explanation if explanations is omitted |
value |
dict
|
Current state with 'selected', 'correct', and 'answered' keys |
Source code in src/marimo_learn/predict_then_check.py
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | |
Turtle
Async turtle that draws into a World.
Create via World.turtle() rather than directly. Each movement
method is a coroutine that yields to the event loop after moving so
that other turtles can run concurrently.
Source code in src/marimo_learn/turtle.py
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | |
World
Bases: AnyWidget
Canvas widget that owns rendering and hosts one or more turtles.
Typical notebook usage::
world = World()
async def my_drawing(world, turtle):
for i in range(60):
await turtle.forward(i * 3)
turtle.right(91)
world.set_coroutine(my_drawing)
mo.ui.anywidget(world) # display via mo.ui.anywidget for live comm
Drawing runs as an asyncio task in Marimo's event loop, so the kernel stays free and Stop works immediately.
For testing, pass output_fn to bypass widget rendering::
world = World(output_fn=lambda _: None)
await world.run(my_drawing())
Source code in src/marimo_learn/turtle.py
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | |
run(*coroutines)
async
Run coroutines in the current event loop (test / direct-use path).
Notebook users should use set_coroutine() + the Start button instead.
Source code in src/marimo_learn/turtle.py
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | |
set_coroutine(*coro_fns)
Register async drawing functions to run when Start is pressed.
Each function must accept (world, turtle) and move that turtle.
One :class:Turtle is created automatically per function.
Pass the function itself (not a coroutine object) so that a fresh coroutine is created on each Start press, enabling clean restarts.
Source code in src/marimo_learn/turtle.py
109 110 111 112 113 114 115 116 117 118 119 | |
turtle()
Create a new turtle that belongs to this world.
Source code in src/marimo_learn/turtle.py
103 104 105 106 107 | |
localize_file(filename, url)
Download a file from the url, returning the local path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
str
|
name for local copy of file |
required |
url
|
str
|
URL of file to download |
required |
Returns:
| Type | Description |
|---|---|
str
|
local file path |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
if remote file not found |
Source code in src/marimo_learn/utilities.py
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | |