No description
  • TypeScript 71%
  • HTML 19.6%
  • Python 7.4%
  • JavaScript 1.5%
  • CSS 0.5%
Find a file
Nostress767 30183acd94
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m48s
Added favicon, kanji quizzes and reworked UI/UX
2026-06-05 22:32:54 -03:00
.forgejo/workflows Added favicon, kanji quizzes and reworked UI/UX 2026-06-05 22:32:54 -03:00
kanji-sets kaku: uma aplicação para treinar a escrita de japonês 2026-06-01 22:45:17 -03:00
scripts kaku: uma aplicação para treinar a escrita de japonês 2026-06-01 22:45:17 -03:00
src Added favicon, kanji quizzes and reworked UI/UX 2026-06-05 22:32:54 -03:00
.gitignore kaku: uma aplicação para treinar a escrita de japonês 2026-06-01 22:45:17 -03:00
.kanjivg.url kaku: uma aplicação para treinar a escrita de japonês 2026-06-01 22:45:17 -03:00
.sqlite-wasm.url kaku: uma aplicação para treinar a escrita de japonês 2026-06-01 22:45:17 -03:00
apple-touch-icon.png Added favicon, kanji quizzes and reworked UI/UX 2026-06-05 22:32:54 -03:00
build_db.ts kaku: uma aplicação para treinar a escrita de japonês 2026-06-01 22:45:17 -03:00
favicon.ico Added favicon, kanji quizzes and reworked UI/UX 2026-06-05 22:32:54 -03:00
favicon.svg Added favicon, kanji quizzes and reworked UI/UX 2026-06-05 22:32:54 -03:00
index.html Added favicon, kanji quizzes and reworked UI/UX 2026-06-05 22:32:54 -03:00
input.css Added favicon, kanji quizzes and reworked UI/UX 2026-06-05 22:32:54 -03:00
package-lock.json kaku: uma aplicação para treinar a escrita de japonês 2026-06-01 22:45:17 -03:00
package.json Added favicon, kanji quizzes and reworked UI/UX 2026-06-05 22:32:54 -03:00
README.md Added favicon, kanji quizzes and reworked UI/UX 2026-06-05 22:32:54 -03:00
tailwind.config.js Added favicon, kanji quizzes and reworked UI/UX 2026-06-05 22:32:54 -03:00
tsconfig.json kaku: uma aplicação para treinar a escrita de japonês 2026-06-01 22:45:17 -03:00

Kanji Grader (FSRS)

Um aplicativo de prática manuscrita de japonês e revisão por repetição espaçada, totalmente offline e rodando no navegador. O usuário desenha kanji (ou kana) numa canvas HTML e é pontuado em ordem dos traços, direção dos traços e precisão da forma. O progresso de aprendizado é gerenciado pelo algoritmo FSRS, com todos os dados persistidos localmente no navegador via SQLite compilado para WebAssembly e armazenado no Origin Private File System (OPFS).

Em produção: https://kaku.ggjz.org

Funcionalidades

Modos de operação

Modo Descrição
Modo Revisão (padrão) Fila diária guiada pelo FSRS. Os caracteres são apresentados automaticamente conforme o agendamento de repetição espaçada. Uma nota de aprovação avança o card; reprovação reagenda o card para a mesma sessão.
Modo Prática Seleção livre. Escolha qualquer hiragana, katakana ou kanji a partir de uma grade de caracteres e desenhe livremente. As pontuações são exibidas mas não são registradas.
Modo Quiz Testa recall ativo: o app apresenta uma dica e você desenha o caractere correspondente de memória. Cada quiz concluído é salvo no Histórico de Quizzes.

O Modo Prática suporta os seguintes conjuntos de caracteres:

  • Hiragana / Katakana com botões de modificador Dakuten / Handakuten (Normal, Dakuten, Handakuten). Selecionar um modificador substitui o kana base no lugar (ex.: は → ば ou ぱ) em vez de mostrar caracteres separados.
  • Kanji por série Jouyou (16 e ginásio), com uma visão unificada "Jouyou (Todos)" ordenada por contagem de traços.
  • Ensino médio (kanji Jinmeiyō).
  • Níveis Kanken 4, 3, 2.5 e 2.

O Modo Quiz oferece dois tipos de teste:

  • Kana — a dica é o romaji (ou o kana equivalente) e você desenha o hiragana/katakana. Avança automaticamente.
  • Kanji (Jōyō) — agrupado como no Modo Prática (1º6º ano, Chūgakkō, Jōyō=todos os 2136). A dica é uma combinação alternável de significado / on'yomi / kun'yomi (ligue/desligue cada uma ao vivo durante o quiz) que você usa para desenhar o kanji de memória. Após avaliar, o kanji correto e sua ordem de traços são revelados para conferência, e um botão "Próximo →" avança. As dicas vêm de src/quiz-data.json.

Desenho e pontuação

  • Canvas 300 × 300 com suporte a mouse e toque (compatível com iPad).
  • Sobreposição de guia de referência (alternável) renderiza os traços corretos com indicadores numerados de ordem por trás da camada de desenho, com 30 % de opacidade.
  • Sobreposição de grade com cruz central e linhas tracejadas opcionais nos quadrantes, alternável independentemente.
  • Desfazer (por traço, também via Ctrl+Z / Cmd+Z) e Limpar.
  • A pontuação avalia três métricas independentes, cada uma com nota 0100 %:
    • Ordem dos Traços — Se cada traço desenhado corresponde geometricamente ao traço de referência sequencial correto. Usa centroide + comparação ponto a ponto normalizada com um portão de distância.
    • Direção dos Traços — Se cada traço foi desenhado na direção correta (início-para-fim vs fim-para-início).
    • Precisão da Forma — Erro ponto a ponto do traço do usuário reamostrado e normalizado contra a referência, com penalidade por razão de comprimento para traços muito curtos ou muito longos.
  • Uma pontuação total ponderada combina as três métricas. Os pesos são ajustáveis via um slider de dois polegares personalizado no modal de Configurações (padrão: Ordem 30 %, Direção 30 %, Forma 40 %).
  • Cada métrica tem também um slider de tolerância independente (01) que suaviza ou endurece a curva de penalidade.
  • Uma nota de aprovação configurável (padrão 90 %) determina se o card FSRS recebe Bom ou De Novo.

Presets de pontuação e regras avançadas

Três presets de pontuação dão perfis rápidos de dificuldade:

Preset Pesos Tolerâncias Regras
Mais Fácil Forma 100 % Todas 1.0 Casamento 1-para-1, sem reforço de primeiro/último, direção relaxada
Normal (padrão) Ord 30 / Dir 30 / Forma 40 Todas 1.0 Casamento 1-para-1, reforço do primeiro e último traço
Mais Difícil Ord 30 / Dir 30 / Forma 40 Todas 0.5 Casamento 1-para-1, primeiro e último, direção estrita

Regras avançadas de pontuação (checkboxes em Configurações):

  • Forçar Casamento 1-para-1 de Traços — Cada traço do usuário só pode casar com um traço de referência; previne que um único traço bem desenhado infle as pontuações.
  • Forçar Ordem do Primeiro e Último Traço — Penaliza fortemente ordem/forma se o primeiro ou último traço não casar. Crítico para forma adequada do kanji.
  • Direção Estrita dos Traços — A pontuação de direção fica binária (0 ou 100) em vez de crédito parcial (50/100).

Repetição espaçada (FSRS)

  • Usa ts-fsrs (v3.5+) com fuzzing habilitado e retenção desejada configurável (padrão 90 %).
  • Fila diária construída a partir de três níveis de prioridade:
    1. Cards em Aprendizado / Reaprendizado vencidos agora.
    2. Cards de Revisão vencidos a qualquer hora hoje.
    3. Cards Novos (até um limite diário configurável, padrão 20).
  • Cards avaliados como "De Novo" que ainda estão vencidos hoje são reenfileirados no final da sessão atual.
  • O contador de cards novos persiste através de recarregamentos da página dentro do mesmo dia.
  • Navegador de Cards lista todos os cards com rótulos de tempo de vencimento relativos e ações por card: Resetar (apaga o histórico FSRS) e Mover para o Topo (força um card para a frente da fila de cards novos).

Estatísticas

  • Painel de estatísticas da sessão (coluna esquerda) mostra contagens restantes de Novos / Aprendendo / Revisando para a sessão atual.
  • Gráfico de pizza total (gradiente cônico) com legenda mostra a distribuição global em todos os caracteres do banco.

Configurações e ferramentas de depuração

  • Modal de Configurações: cards novos/dia, retenção desejada do FSRS, nota de aprovação, slider de pesos, sliders de tolerância por métrica, presets de pontuação, regras avançadas.
  • Modal de Debug: viagem no tempo (+1 dia para testar FSRS), resetar progresso, resetar banco e configurações, apagar o arquivo OPFS do banco.
  • Alternância Tema Escuro / Claro (padrão escuro), persistida em localStorage.

Arquitetura

100 % cliente, offline-first

A aplicação inteira roda no navegador, com zero backend. Não há lógica do lado servidor, chamadas de API ou contas de usuário. Uma vez cacheado o banco SQLite no OPFS, o aplicativo funciona totalmente offline.

Arquitetura TypeScript modular

O código-fonte está dividido em módulos focados:

Arquivo Função
src/app.ts Classe principal KanjiGrader: lógica de UI, desenho no canvas, eventos, fluxo de revisão FSRS, configurações, gerenciamento de modos, lógica de modificadores de kana
src/scoring.ts Classe estática GeometryUtil (amostragem de path, reamostragem, normalização, centroide, comprimento de arco) e função calculateGeometricScore()
src/grading.ts Integração com FSRS: gradeKanji() calcula o próximo estado do card, updateFsrsParams() configura a retenção
src/types.ts Interfaces TypeScript compartilhadas: KanjiRow, FsrsCardRow, WorkerMessage, WorkerResponse
src/worker.ts Web Worker dedicado: gerenciamento de SQLite/OPFS, download do banco, montagem da fila diária, CRUD de cards, estatísticas
src/kanji-data.json Dados de caracteres agrupados por série e ordenados por traços (gerado pelos scripts do pipeline)

Web Worker + SQLite WASM + OPFS

Todas as operações de banco rodam num Web Worker dedicado (worker.ts) para manter a thread de UI responsiva. O worker:

  1. No primeiro lançamento, baixa o banco pré-compilado kanjivg.sqlite3.gz via fetch(), descompacta e salva no OPFS.
  2. Em lançamentos seguintes, detecta o arquivo OPFS existente e pula o download.
  3. Lida com todas as leituras/escritas: buscar dados SVG, salvar estado do card FSRS, montar a fila diária, computar estatísticas e executar resets.

O SQLite é carregado via importScripts('sqlite3.js') (o build oficial em WASM) usando o VFS OpfsDb para armazenamento durável. As escritas OPFS usam createSyncAccessHandle() quando disponível (Safari/iOS), com createWritable() como fallback.

Por que HTTPS é obrigatório (cabeçalhos COOP / COEP)

createSyncAccessHandle() do OPFS e SharedArrayBuffer exigem isolamento de origem cruzada. Em produção, o CloudFront serve cada resposta com:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

Esses cabeçalhos são injetados por uma política customizada de Response Headers anexada à distribuição CloudFront — sem eles a página carrega, mas o OPFS falha silenciosamente e o worker não consegue persistir o banco.

Algoritmo de pontuação geométrica

A pontuação é puramente geométrica (sem modelo ML), implementada em scoring.ts:

  1. Cada traço do usuário é reamostrado para 50 pontos igualmente espaçados.
  2. Os pontos são normalizados pela bounding box num espaço de coordenadas centrado de 100 unidades.
  3. Cada traço do usuário é casado com seu melhor traço de referência minimizando uma métrica combinada de erro de ponto normalizado + distância de centroide.
  4. Reforço 1-para-1 (quando habilitado) impede que múltiplos traços do usuário reivindiquem o mesmo traço de referência.
  5. Ordem dos traços está correta apenas se o traço do usuário i casa melhor com o traço de referência i e passa por um portão de forma/distância. O reforço de primeiro/último traço (quando habilitado) aplica penalidade de 50 % na ordem e 80 % na forma em caso de erro.
  6. Direção é avaliada por traço: o ponto inicial do usuário é comparado com ambos os extremos do traço de referência. Em modo estrito a direção é binária (0/100); em modo leniente, traços invertidos recebem 50 %.
  7. Forma é o erro médio normalizado de ponto, escalado por uma penalidade quadrática de razão de comprimento e uma penalidade por diferença na quantidade de traços (25 % por traço extra/faltando, limitada a 100 %).
  8. As curvas de tolerância por métrica suavizam as pontuações: nota = 100 (100 bruta) / tolerância.
  9. Total ponderado final: total = (ordem × pOrdem) + (direção × pDir) + (forma × pForma).

Estrutura do repositório

.forgejo/workflows/deploy.yml   # CI: build + deploy para S3 + CloudFront
.kanjivg.url                    # URL do release do KanjiVG (editar para atualizar)
.sqlite-wasm.url                # URL do build oficial do SQLite WASM
src/                            # Código-fonte TypeScript
scripts/                        # Pipeline de dados (Python + test_scoring.ts)
kanji-sets/                     # Listas de caracteres (com atribuição por subpasta)
  app.kanjialive.com/             # japanese-radicals.csv + LICENSE
  kanji.jitenon.jp/               # shougakkou, jouyou_new, jinmeiyou, kanken*
index.html                      # Página única servida pela CDN
input.css                       # Entrada do Tailwind
tailwind.config.js
tsconfig.json
package.json / package-lock.json
build_db.ts                     # Constrói kanjivg.sqlite3.gz a partir do zip do KanjiVG
README.md

Desenvolvimento local

Pré-requisitos: Node 20+, Python 3.10+ (apenas para os scripts em scripts/), python3/make/g++ (para a compilação nativa do better-sqlite3).

# Instalar dependências
npm install

# Baixar os arquivos vendored uma única vez (o CI faz isso automaticamente)
curl -fsSL "$(cat .kanjivg.url)" -o "$(basename $(cat .kanjivg.url))"
curl -fsSL "$(cat .sqlite-wasm.url)" -o /tmp/sqlite-wasm.zip
DIR=$(basename $(cat .sqlite-wasm.url) .zip)
unzip -j /tmp/sqlite-wasm.zip "${DIR}/jswasm/sqlite3.js" "${DIR}/jswasm/sqlite3.wasm" "${DIR}/jswasm/sqlite3-opfs-async-proxy.js" -d .

# Construir o banco SQLite a partir do zip do KanjiVG
npx tsx build_db.ts

# Build da aplicação (esbuild + Tailwind)
npm run build

# Rodar testes da pontuação
npm test

Testar localmente (com OPFS funcionando)

npm run build      # gera dist/bundle.js, dist/worker.js, dist/output.css
npm run serve      # → http://localhost:8000

npm run serve roda scripts/serve_local.py (só usa a biblioteca padrão do Python). Abra http://localhost:8000 — não use 127.0.0.1 por outro hostname nem outra porta sem ajustar.

Após o build, todos os arquivos servidos em produção ficam no diretório raiz (index.html, sqlite3.js, sqlite3.wasm, sqlite3-opfs-async-proxy.js, kanjivg.sqlite3.gz) mais dist/ (bundle.js, worker.js, output.css).

Pipeline de dados

src/kanji-data.json (a lista mestre de caracteres agrupada por série e ordenada por traços) é mantida pelos scripts em scripts/. Esses scripts só precisam rodar quando você quer regenerar o JSON a partir das listas em kanji-sets/.

# Reconstruir séries 16 a partir de shougakkou.txt
python scripts/rebuild_grades_1to6.py

# Reconstruir kanji do ginásio (Jouyou menos séries 16)
python scripts/rebuild_middle_school.py

# Adicionar kanji Jinmeiyou (ensino médio)
python scripts/add_high_school_kanji.py

# Adicionar níveis Kanken
python scripts/add_kanken_kanji.py

# Cross-referência da cobertura de SVG vs kanji-data.json
python scripts/analyze_kanji.py

O analyze_kanji.py e o recover_missing_kanji.py dependem dos diretórios kanji/ e others/ (ambos não versionados — disponíveis localmente). Para baixar os SVGs originais do KanjiVG, basta extrair o zip apontado por .kanjivg.url.

src/quiz-data.json alimenta o quiz de kanji (desenhar de memória a partir de significados / on'yomi / kun'yomi, agrupado por série como no Modo Prática). Ele é versionado e empacotado pelo esbuild dentro de dist/bundle.js (assim como src/kanji-data.json), então só precisa ser regerado quando os dados de origem mudam.

Atribuição dos conjuntos de caracteres

  • shougakkou.txt — Kanji do ensino fundamental por série (学年別漢字配当表), definidos pelo Ministério da Educação do Japão (MEXT).
  • jouyou_new.txt — Lista Jouyou (常用漢字表), fixada por diretriz do Gabinete japonês (内閣告示).
  • jinmeiyou.txt — Kanji Jinmeiyō (人名用漢字), fixados pelo Ministério da Justiça do Japão.
  • kanken1.txt kanken4.txt, kanken1.5.txt, kanken2.5.txt — Kanji por nível Kanken (漢検), definidos pela Fundação Kanken (日本漢字能力検定協会).

A página kanji.jitenon.jp foi a fonte de conveniência da qual essas listas foram copiadas com Ctrl+c/Ctrl+v

kanji-sets/app.kanjialive.com/japanese-radicals.csv vem da tabela de radicais original do projeto Kanji Alive redistribuído sob a licença Creative Commons indicada em kanji-sets/app.kanjialive.com/LICENSE.md.

Os SVGs de ordem dos traços (não versionados; baixados pelo CI) vêm do projeto KanjiVG.

Dependências externas

Dependência Função
ts-fsrs Algoritmo de agendamento por repetição espaçada
KanjiVG Dados SVG de ordem dos traços para kanji, hiragana, katakana
SQLite WASM SQLite no cliente via sqlite3.js + sqlite3.wasm + proxy assíncrono OPFS
esbuild Bundler TypeScript
Tailwind CSS v3 Framework CSS utility-first
tsx Execução de TypeScript (usada pelo npm test e build_db.ts)