Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,19 @@ src/
user-facing string, both languages, plus topic/language/voice names)
config/ settings.rs, level.rs (commitment), paths.rs
store/ history.rs (SQLite)
decks/<lang>.toml hand-curated built-in decks (embedded with include_str!)
decks/<lang>/ hand-curated built-in decks (en, es for pt-BR speakers; pt for English
speakers): deck.toml = header + general tips, then one file of phrases
per theme, named in English (work.toml, health.toml…; the `topic` keys
inside stay pt-BR). `themed!` in learn/deck.rs joins them; a new theme
file must be listed there
decks/<lang>.basics.toml hand-curated first words + short chunks (where the path starts)
decks/<lang>.lexicaster.toml GENERATED by scripts/port-lexicaster.mjs — never edit by hand
decks/<lang>.lexicaster.tiers.toml hand-curated short/polished answers for the generated deck,
keyed by `say` and merged at load (a stale `say` fails the deck tests)
decks/pt.basics.toml first Brazilian Portuguese words + chunks, written for English speakers
decks/i18n/<deck>.<native>.toml translation overlay keyed by `say` (meaning, situation, tip,
cue) bringing a deck to another native; e.g. es.en.toml, es.basics.en.toml
decks/i18n/<deck>.<native> translation overlay keyed by `say` (meaning, situation, tip, cue)
bringing a deck to another native: es.en/ mirrors decks/es/ theme by theme,
es.basics.en.toml is one file
tests/cli.rs drives the built binary (CARGO_BIN_EXE_snowlearner) with SNOWLEARNER_HOME
tests/install.rs install.sh end to end against a local fake release (real script, real binary)
tests/speech_e2e.rs real whisper run on synthesized speech (#[ignore], needs a model)
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "snowlearner"
version = "0.1.6"
version = "0.1.7"
edition = "2024"
rust-version = "1.85"
description = "A pixel-art frost mage that freezes your screen until you practice speaking a new language."
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,18 @@ none (hotkey confirms your answer, CPU-only drawing). `SNOWLEARNER_RENDERER=cpu|
| Action | Hotkey / mouse | Command |
|---|---|---|
| Practice a phrase · "I'm done talking" · next | `Ctrl+Alt+M` · click the orb | `snowlearner say` |
| Control panel (game, audio, progress tabs) | `Ctrl+Alt+K` · Ctrl+click the orb | `snowlearner menu` |
| Control panel (game, audio, keys, progress tabs) | `Ctrl+Alt+K` · Ctrl+click the orb | `snowlearner menu` |
| Your progress: known · learning · next | `Ctrl+Alt+P` | `snowlearner progress` (`--print` in the terminal) |
| Magic hand (15 s) | hold `Ctrl+Alt` · `Ctrl+Alt+G` | `snowlearner grab` |
| Pause (black hole) / resume | right-click the orb · `P` | `snowlearner pause` |
| Today's recap | `Ctrl+Alt+J` · `S` | `snowlearner summary` |
| Shortcut cheat sheet | hover the orb · hold `Ctrl+Alt` | — |
| Quit | — | `snowlearner quit` |

A hotkey another program already uses (NVIDIA's overlay takes `Ctrl+Alt+M`, for one) does nothing:
the app says so at startup and marks it `(in use)` in the panel's **keys** tab, where each hotkey
can be moved to a free key. The change applies at once, and every on-screen hint follows it.

Window mode extras: `H` help, `1`–`4` commitment, `L` switch language, click characters to poke them.

**Commitment level (*compromisso*)**: `chill | steady | committed | relentless` — how often the
Expand Down
39 changes: 39 additions & 0 deletions decks/en/deck.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
language = "en"
native = "pt-BR"
language_name = "inglês"
title = "Inglês pra vida real"

# General tips the warrior drops between challenges.

[[tip]]
text = "Falso amigo: 'actually' é 'na verdade', não 'atualmente' (currently)."
[[tip]]
text = "Falso amigo: 'pretend' é 'fingir'. 'Pretender' é 'intend' ou 'plan to'."
[[tip]]
text = "Falso amigo: 'push' é EMPURRAR. Puxar é 'pull'. Olhe a porta!"
[[tip]]
text = "Falso amigo: 'library' é biblioteca. Livraria é 'bookstore'."
[[tip]]
text = "Falso amigo: 'exquisite' é refinado, elogio! Não é 'esquisito' (weird)."
[[tip]]
text = "Falso amigo: 'parents' são pai e mãe. Parentes são 'relatives'."
[[tip]]
text = "Falso amigo: 'lunch' é almoço. Lanche é 'snack'."
[[tip]]
text = "Pronúncia: final -ed quase não soa. 'worked' = 'uórkt', não 'uórqued'."
[[tip]]
text = "Pronúncia: o 'th' é língua entre os dentes. 'think' não é 'fink' nem 'tink'."
[[tip]]
text = "Pronúncia: não coloque 'i' no fim: 'Facebook' não é 'feicebuki'."
[[tip]]
text = "Pronúncia: 'beach' tem 'i' longo; 'bitch' tem 'i' curto. Cuidado na praia!"
[[tip]]
text = "Educação: 'Could you...?' soa bem mais gentil que 'Can you...?'."
[[tip]]
text = "Pra ganhar tempo pensando: 'Let me think...' ou 'That's a good question'."
[[tip]]
text = "Não entendeu? Tudo bem pedir: 'Sorry, could you say that again?'."
[[tip]]
text = "'How are you?' muitas vezes é só um oi. Responda curto: 'Good, you?'."
[[tip]]
text = "Em email e reunião, 'I'm afraid...' suaviza uma notícia ruim."
156 changes: 156 additions & 0 deletions decks/en/emergency.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# decks/en: emergência. The header and tips are in deck.toml; src/learn/deck.rs joins the files.

[[phrase]]
topic = "emergência"
situation = "Aconteceu um acidente."
say = "Please call an ambulance"
short = "Call an ambulance!"
polished = "Please, someone call an ambulance right now!"
meaning = "Por favor, chame uma ambulância"
accept = ["Call an ambulance"]

[[phrase]]
topic = "emergência"
situation = "Perdeu o passaporte na viagem."
say = "I lost my passport"
polished = "Excuse me, I've lost my passport"
meaning = "Perdi meu passaporte"

[[phrase]]
topic = "emergência"
situation = "Precisa de ajuda urgente na rua."
say = "Can you help me, please?"
short = "Help, please!"
polished = "Excuse me, could you help me, please?"
meaning = "Você pode me ajudar, por favor?"
accept = ["Could you help me, please?", "Can you help me?"]

[[phrase]]
topic = "emergência"
level = "A1"
situation = "Você vê fumaça saindo da cozinha do restaurante."
say = "There's a fire!"
short = "Fire!"
polished = "There's a fire, everyone get out!"
meaning = "Está pegando fogo!"
accept = ["There is a fire"]

[[phrase]]
topic = "emergência"
level = "A1"
situation = "Você saiu do metrô numa cidade que não conhece e não sabe voltar."
say = "I can't find my hotel."
polished = "Excuse me, I can't find my hotel."
meaning = "Não consigo achar meu hotel."
accept = ["I cannot find my hotel"]

[[phrase]]
topic = "emergência"
level = "A1"
situation = "Você volta para a mesa do café e sua mochila sumiu."
say = "Someone stole my backpack."
short = "My backpack!"
polished = "Excuse me, someone just stole my backpack."
meaning = "Roubaram minha mochila."
accept = ["Somebody stole my backpack", "My backpack was stolen"]

[[phrase]]
topic = "emergência"
level = "A1"
situation = "Uma pessoa passa mal e cai ao seu lado na rua."
say = "We need a doctor."
short = "A doctor!"
polished = "We need a doctor here, please hurry!"
meaning = "Precisamos de um médico."

[[phrase]]
topic = "emergência"
level = "A1"
situation = "Seu amigo se machucou e você para um táxi na rua."
say = "I need to get to a hospital."
short = "A hospital, quick!"
polished = "Please, I need to get to a hospital right now."
meaning = "Preciso ir a um hospital."
accept = ["I need to go to a hospital", "I need to go to the hospital"]

[[phrase]]
topic = "emergência"
level = "A2"
situation = "Seu amigo caiu da bicicleta e não consegue levantar."
say = "My friend is hurt."
short = "He's hurt!"
polished = "Please help, my friend is badly hurt."
meaning = "Meu amigo está machucado."
accept = ["My friend is injured"]

[[phrase]]
topic = "emergência"
level = "A2"
situation = "Você liga para a emergência e perguntam onde você está."
say = "I don't know the address."
short = "I don't know."
polished = "I'm sorry, I don't know the exact address."
meaning = "Não sei o endereço."
accept = ["I do not know the address"]

[[phrase]]
topic = "emergência"
level = "A2"
situation = "Seu celular descarregou e você precisa avisar a família."
say = "Can I use your phone?"
short = "Your phone, please?"
polished = "Sorry, could I use your phone for a minute?"
meaning = "Posso usar seu celular?"
accept = ["Could I use your phone?", "May I use your phone?"]

[[phrase]]
topic = "emergência"
level = "A2"
situation = "A porta bateu e a chave ficou dentro do apartamento."
say = "I'm locked out."
short = "Locked out!"
polished = "I'm locked out of my apartment."
meaning = "Fiquei trancado pra fora."
accept = ["I locked myself out"]
tip = "'Locked out' = trancado do lado de fora, sem a chave."

[[phrase]]
topic = "emergência"
level = "A2"
situation = "Depois de uma batida leve de carro, você desce pra ver as pessoas."
say = "Is everyone okay?"
short = "Everyone okay?"
polished = "Is everyone okay? Is anybody hurt?"
meaning = "Está todo mundo bem?"
accept = ["Is everybody okay?", "Is everyone all right?"]

[[phrase]]
topic = "emergência"
level = "B1"
situation = "Na delegacia, depois que levaram sua mochila."
say = "I need to report a theft."
short = "I was robbed."
polished = "Hello, I'd like to report a theft, please."
meaning = "Preciso registrar um furto."
accept = ["I want to report a theft"]
tip = "'Theft' é furto; 'robbery' é roubo com ameaça."

[[phrase]]
topic = "emergência"
level = "B1"
situation = "Seu cartão sumiu e você liga para o banco."
say = "I need to cancel my card."
short = "Cancel my card, please."
polished = "I've lost my card and I need to cancel it."
meaning = "Preciso cancelar meu cartão."
accept = ["I need to block my card"]

[[phrase]]
topic = "emergência"
level = "B1"
situation = "Um homem desmaiou e você fala com a emergência ao telefone."
say = "He's not breathing."
short = "Not breathing!"
polished = "Please hurry, he's not breathing!"
meaning = "Ele não está respirando."
accept = ["He is not breathing", "He isn't breathing"]
80 changes: 80 additions & 0 deletions decks/en/first-contact.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# decks/en: primeiros contatos. The header and tips are in deck.toml; src/learn/deck.rs joins the files.

[[phrase]]
topic = "primeiros contatos"
level = "A2"
situation = "Você trava no meio da frase conversando com um desconhecido."
say = "I'm learning English."
short = "Still learning."
polished = "I'm still learning English, so please be patient."
meaning = "Estou aprendendo inglês."
accept = ["I am learning English", "I'm still learning English"]

[[phrase]]
topic = "primeiros contatos"
level = "A2"
situation = "Um vizinho pergunta há quanto tempo você está na cidade."
say = "I moved here last year."
short = "Last year."
polished = "I moved here last year for work."
meaning = "Me mudei pra cá no ano passado."
accept = ["I came here last year"]

[[phrase]]
topic = "primeiros contatos"
level = "A2"
situation = "Você conversa com alguém na fila da padaria do bairro."
say = "Do you live around here?"
polished = "So, do you live around here?"
meaning = "Você mora por aqui?"
accept = ["Do you live nearby?", "Do you live near here?"]

[[phrase]]
topic = "primeiros contatos"
level = "A2"
situation = "Você se despede de alguém que conheceu hoje num jantar."
say = "It was nice meeting you."
short = "Nice meeting you."
polished = "It was really nice meeting you today."
meaning = "Foi um prazer te conhecer."
accept = ["It was nice to meet you", "It was great meeting you"]
tip = "Na chegada é 'Nice to meet you'; na despedida, 'Nice meeting you'."

[[phrase]]
topic = "primeiros contatos"
level = "B1"
situation = "Um colega novo conta que veio de outro país."
say = "What brought you here?"
polished = "So, what brought you to this city?"
meaning = "O que te trouxe pra cá?"
accept = ["What brings you here?"]

[[phrase]]
topic = "primeiros contatos"
level = "B1"
situation = "Você conhece alguém do bairro numa festa de vizinhos."
say = "How long have you lived here?"
short = "How long?"
polished = "How long have you been living here?"
meaning = "Há quanto tempo você mora aqui?"
tip = "Pra algo que começou no passado e continua: 'have lived', não 'live'."

[[phrase]]
topic = "primeiros contatos"
level = "B1"
situation = "Perguntam como está sendo a vida na cidade nova."
say = "I'm still getting used to it."
short = "Still getting used to it."
polished = "Honestly, I'm still getting used to it."
meaning = "Ainda estou me acostumando."
tip = "'Get used to' = se acostumar com."

[[phrase]]
topic = "primeiros contatos"
level = "B1"
situation = "No último dia do curso, você se despede de um colega."
say = "We should keep in touch."
short = "Let's keep in touch."
polished = "We should definitely keep in touch."
meaning = "A gente devia manter contato."
accept = ["We should stay in touch", "Let's stay in touch"]
42 changes: 42 additions & 0 deletions decks/en/health-and-social.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# decks/en: saúde e social. The header and tips are in deck.toml; src/learn/deck.rs joins the files.

[[phrase]]
topic = "saúde e social"
level = "B1"
situation = "Um amigo nota que você anda quieto e pergunta se está tudo bem."
say = "I've been feeling stressed lately."
short = "I've been stressed."
polished = "To be honest, I've been feeling stressed lately."
meaning = "Ando estressado ultimamente."
accept = ["I've been stressed lately", "I've been really stressed lately"]

[[phrase]]
topic = "saúde e social"
level = "B1"
situation = "Um colega te chama pra sair tarde numa terça-feira."
say = "I'm trying to get more sleep."
short = "I need more sleep."
polished = "I'm really trying to get more sleep these days."
meaning = "Estou tentando dormir mais."
accept = ["I'm trying to sleep more"]

[[phrase]]
topic = "saúde e social"
level = "B1"
situation = "Seu amigo está exausto e não para de trabalhar."
say = "You should take a day off."
short = "Take a day off."
polished = "I really think you should take a day off."
meaning = "Você devia tirar um dia de folga."
accept = ["You should take the day off"]
tip = "'A day off' = um dia de folga."

[[phrase]]
topic = "saúde e social"
level = "B1"
situation = "Uma amiga está doente em casa, sozinha."
say = "Text me if you need anything."
short = "Text me."
polished = "Please text me if you need anything at all."
meaning = "Me manda mensagem se precisar de alguma coisa."
accept = ["Message me if you need anything", "Call me if you need anything"]
Loading
Loading