whiskers-chooser
The character chooser: the main screen in an edit mode. She taps the character in the side panel and sees the character with an arrow on each side (previous and next, wrapping around), under it the name of its voice with an arrow on each side (previous and next voice, wrapping), and the bottom buttons are a check (save) and a cross (throw the change away and leave). Choosing a voice speaks "Hi Ada, this voice is called Jennifer" in that voice; changing the character says "Hi Ada" in that character's saved voice. Nothing is saved until the check, and the check saves exactly the shown character and the voice shown for it.
The rules
What each thing she does means is not code in the shell. It is src/domain.rs: twenty rules on the
estate's shared Rete engine (rete, in the submodule third-party/rustcrates, the same engine lmjtfy runs). A rule is a
list of tests on facts (what she did, whether the chooser is open, whether anything is unsaved, whether voices are listed,
and what an earlier effect already did) and what to do when they hold; order is priority. The picture of the network that
runs is docs/chooser-rules.svg, drawn by rete-draw and held to the network by a test
(cargo run -p whiskers-chooser --example draw writes it again).
| Input | Rule | What happens |
|---|---|---|
| open | open | the chooser opens on the saved character and its saved voice (says nothing) |
| right, left | next / previous character | the next or previous character shows, with the voice saved for it, then it says hi in that voice |
| voice right, left | next / previous voice | the next or previous listed voice shows, then it says its name in that voice |
| check | check saves | with something unsaved, the shown character and its voice are kept, then the chooser closes |
| check | check with no change closes | nothing to keep: it closes |
| cross | cross throws away | what shows is put back to what is saved, then the chooser closes |
| anything that means nothing now | closed: ..., open already, no voices to step | a note says so; nothing changes |
What it is
Chooser holds the state the rules act on, because a character and a voice are too wide to be facts: which
are shown, which are saved. Chooser::input(Input) runs the rules and returns an Output: the View to draw, the
ShellEffects to do in order (speak an Utterance, keep a Save, leave), and the trace. The engine (Engine::chooser_input)
keeps one between inputs, rebuilds it each time the chooser opens, and keeps the choice on a Save; whiskers-ffi exports
it, so the Kotlin shell only sends an input, draws the view and does the effects.
Every step leaves one JSON line per event (which rule fired, which facts it stood on, counts; names and ids only, never the
child's name or a voice's name or what was said) and the chooser logs them at info level as chooser {...}.
Files
| File | What |
|---|---|
src/domain.rs | The vocabulary (facts, values, effects, notes) and the rules. |
src/lib.rs | Chooser: the state and the effects the rules decide. |
src/tests.rs | The laws: wrap-around both ways, a preview does not save, the check saves exactly one character and its voice, the cross changes nothing, a character shows its own saved voice, the lines said. |
tests/drawing.rs, tests/common/mod.rs, examples/draw.rs | The picture, and the test that holds it to the rules. |
docs/chooser-rules.svg | The network, drawn. |