Skip to content
Draft
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
127 changes: 127 additions & 0 deletions DICIONARIO-RIMAS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# 📖 Dicionário de Rimas - Documentação

## Visão Geral

O **Dicionário de Rimas** é uma página interativa que visualiza e agrupa as palavras finais dos versos de todas as cantorias do acervo Vilanova. É uma ferramenta única para poetas, estudantes e entusiastas do repente descobrirem padrões de rima e explorarem o vocabulário dos cantadores.

## Funcionalidades

### 🎯 Agrupamento Inteligente

As rimas são agrupadas por suas **terminações fonéticas** (últimas 2-4 letras normalizadas), permitindo descobrir:
- Palavras que rimam entre si
- Frequência de uso de cada rima
- Contexto dos versos onde aparecem

**Exemplo:**
- Terminação `-ente`: independente (10x), valente (4x), serpente (3x)
- Terminação `-oano`: alagoano (32x)
- Terminação `-mar`: mar (23x)

### 🔍 Filtros Disponíveis

1. **Por Letra**: Navegue pelas rimas começando com cada letra do alfabeto
2. **Por Estilo**: Filtre rimas de um estilo específico (Martelo Alagoano, Galope à Beira Mar, etc.)
3. **Busca**: Pesquise por palavra ou terminação específica
4. **Ordenação**: Por frequência (mais usadas) ou ordem alfabética

### 📊 Estatísticas em Tempo Real

A página mostra:
- Total de terminações de rimas diferentes
- Total de palavras únicas
- Total de versos catalogados
- Número de cantorias analisadas

### 🎨 Interface Interativa

- **Cards Expansíveis**: Clique em uma terminação para ver todas as palavras e versos
- **Contexto Rico**: Cada verso mostra:
- Texto completo do verso
- Link para a cantoria
- Nome do cantador
- Estilo da cantoria
- **Responsivo**: Funciona perfeitamente em desktop e mobile

## Como Usar

### Para Poetas e Estudantes

1. **Encontrar Rimas**: Digite uma palavra na busca para ver com que ela rima
2. **Inspiração**: Navegue pelas terminações mais frequentes para descobrir vocabulário comum
3. **Estudar Padrões**: Compare como diferentes cantadores usam as mesmas rimas

### Para Pesquisadores

1. **Análise de Vocabulário**: Veja quais palavras são mais usadas em cada estilo
2. **Padrões de Rima**: Identifique preferências de rima por estilo ou cantador
3. **Contexto Histórico**: Veja como palavras são usadas em diferentes contextos

## Exemplo de Uso

```
Buscar: "amor"
Resultado:
- Terminação "-amor"
• amor (15 ocorrências)
- "Sei que acima da morte está o amor." (Pensamentos e Pensadores)
- "O amor é mais nobre, é mais humano" (Ivanildo Vilanova)
```

## Dados Processados

Atualmente o dicionário analisa:
- **12 cantorias** com versos completos
- **104 estrofes** catalogadas
- **980 versos** transcritos
- **436 terminações** de rima diferentes

Os dados são extraídos em tempo real do acervo, então novos repentes adicionados aparecem automaticamente.

## Navegação

Acesse: `/rimas` ou clique em "Rimas" no menu de navegação

## Implementação Técnica

### Arquivos

- **Página**: `/view/src/routes/rimas.tsx`
- **Utilitários**: `/view/src/lib/rhymes.ts`
- **Navegação**: Adicionado em `/view/src/components/site-header.tsx`

### Algoritmo de Agrupamento

1. **Extração**: Remove pontuação e extrai palavra final de cada verso
2. **Normalização**: Remove acentos e converte para minúsculas
3. **Terminação**: Extrai últimas 2-4 letras dependendo do tamanho da palavra
4. **Agrupamento**: Agrupa palavras com mesma terminação

### Performance

- Processamento em memória usando `useMemo` para cache
- Filtros aplicados de forma eficiente com Maps
- Interface responsiva e rápida

## Futuras Melhorias

- [ ] Visualização de esquemas de rima (AABB, ABAB, etc.)
- [ ] Exportar lista de rimas como PDF
- [ ] Sons de rima (fonética completa, não apenas terminação)
- [ ] Estatísticas de evolução temporal das rimas
- [ ] API pública para consulta de rimas

## Contribuindo

Para adicionar novas cantorias que aparecerão no dicionário:
1. Adicione o arquivo JSON em `public/data/cantorias/`
2. Inclua as estrofes completas com todos os versos
3. O dicionário atualizará automaticamente

## Créditos

Desenvolvido para o **Projeto Vilanova** - preservação digital da cantoria nordestina.

---

**Feito com ❤️ para o repente nordestino**
14 changes: 14 additions & 0 deletions view/src/components/site-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ export function SiteHeader({ showBackButton, backTo = "/", backLabel = "Voltar"
>
Cantadores
</Link>
<Link
to="/rimas"
className="text-sm font-semibold text-[#2E5266] hover:text-[#C84B31] transition-colors"
activeProps={{ className: "text-[#C84B31]" }}
>
Rimas
</Link>
<GlobalSearch />
</nav>
</div>
Expand Down Expand Up @@ -94,6 +101,13 @@ export function SiteHeader({ showBackButton, backTo = "/", backLabel = "Voltar"
>
Cantadores
</Link>
<Link
to="/rimas"
className="text-xs font-semibold text-[#2E5266] hover:text-[#C84B31] transition-colors"
activeProps={{ className: "text-[#C84B31]" }}
>
Rimas
</Link>
</nav>
</div>
</header>
Expand Down
200 changes: 200 additions & 0 deletions view/src/lib/rhymes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/**
* Utilitário para extrair e agrupar rimas do acervo
*/

import type { Cantoria, Estrofe } from "./types";

export interface RhymeWord {
word: string; // Palavra final normalizada
original: string; // Palavra original (com acentos, maiúsculas)
verse: string; // Verso completo
cantoriaId: string;
cantoriaTitle: string;
cantadorName: string;
estilo: string;
estrofeNumero: number;
versoNumero: number; // Posição do verso na estrofe (0-indexed)
}

export interface RhymeGroup {
rhyme: string; // Terminação da rima (últimas 2-3 sílabas)
words: Map<string, RhymeWord[]>; // Agrupado por palavra completa
count: number; // Total de ocorrências
}

/**
* Extrai a palavra final de um verso (remove pontuação)
*/
function extractLastWord(verse: string): string {
// Remove pontuação final e espaços
const cleaned = verse.trim().replace(/[.,!?;:"'»«""]$/g, '');
const words = cleaned.split(/\s+/);
return words[words.length - 1] || '';
}

/**
* Normaliza palavra para comparação (remove acentos, lowercase)
*/
function normalizeWord(word: string): string {
return word
.toLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '');
}

/**
* Extrai a terminação da rima (últimas 2-4 letras dependendo do tamanho)
*/
function extractRhymeSuffix(word: string): string {
const normalized = normalizeWord(word);

// Para palavras muito curtas, usar a palavra inteira
if (normalized.length <= 3) {
return normalized;
}

// Para palavras médias (4-6 letras), pegar últimas 3 letras
if (normalized.length <= 6) {
return normalized.slice(-3);
}

// Para palavras longas, pegar últimas 4 letras
return normalized.slice(-4);
}

/**
* Extrai todas as rimas de uma cantoria
*/
export function extractRhymesFromCantoria(cantoria: Cantoria): RhymeWord[] {
const rhymes: RhymeWord[] = [];

cantoria.estrofes.forEach((estrofe: Estrofe) => {
estrofe.versos.forEach((verso: string, index: number) => {
const original = extractLastWord(verso);
if (!original) return;

const word = normalizeWord(original);

rhymes.push({
word,
original,
verse: verso,
cantoriaId: cantoria.id,
cantoriaTitle: cantoria.titulo,
cantadorName: estrofe.cantador,
estilo: cantoria.estilo.nome,
estrofeNumero: estrofe.numero,
versoNumero: index,
});
});
});

return rhymes;
}

/**
* Agrupa rimas por terminação
*/
export function groupRhymesByEnding(rhymeWords: RhymeWord[]): Map<string, RhymeGroup> {
const groups = new Map<string, RhymeGroup>();

rhymeWords.forEach((rhymeWord) => {
const suffix = extractRhymeSuffix(rhymeWord.word);

if (!groups.has(suffix)) {
groups.set(suffix, {
rhyme: suffix,
words: new Map(),
count: 0,
});
}

const group = groups.get(suffix)!;

if (!group.words.has(rhymeWord.word)) {
group.words.set(rhymeWord.word, []);
}

group.words.get(rhymeWord.word)!.push(rhymeWord);
group.count++;
});

return groups;
}

/**
* Extrai todas as rimas de um conjunto de cantorias
*/
export function extractAllRhymes(cantorias: Cantoria[]): RhymeWord[] {
const allRhymes: RhymeWord[] = [];

cantorias.forEach((cantoria) => {
const rhymes = extractRhymesFromCantoria(cantoria);
allRhymes.push(...rhymes);
});

return allRhymes;
}

/**
* Obtém estatísticas das rimas
*/
export function getRhymeStats(rhymeGroups: Map<string, RhymeGroup>) {
let totalRhymes = 0;
let totalWords = 0;
const styleCount = new Map<string, number>();

rhymeGroups.forEach((group) => {
totalRhymes += group.count;
totalWords += group.words.size;

group.words.forEach((words) => {
words.forEach((word) => {
styleCount.set(word.estilo, (styleCount.get(word.estilo) || 0) + 1);
});
});
});

return {
totalRhymeGroups: rhymeGroups.size,
totalRhymes,
totalUniqueWords: totalWords,
styleCount,
};
}

/**
* Filtra grupos de rimas por letra inicial
*/
export function filterByLetter(
rhymeGroups: Map<string, RhymeGroup>,
letter: string
): Map<string, RhymeGroup> {
const filtered = new Map<string, RhymeGroup>();

rhymeGroups.forEach((group, key) => {
if (key.startsWith(letter.toLowerCase())) {
filtered.set(key, group);
}
});

return filtered;
}

/**
* Ordena grupos de rimas por número de ocorrências
*/
export function sortByFrequency(
rhymeGroups: Map<string, RhymeGroup>
): [string, RhymeGroup][] {
return Array.from(rhymeGroups.entries()).sort((a, b) => b[1].count - a[1].count);
}

/**
* Ordena grupos de rimas alfabeticamente
*/
export function sortAlphabetically(
rhymeGroups: Map<string, RhymeGroup>
): [string, RhymeGroup][] {
return Array.from(rhymeGroups.entries()).sort((a, b) => a[0].localeCompare(b[0]));
}
2 changes: 2 additions & 0 deletions view/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import CantadoresRoute from "./routes/cantadores.tsx";
import CantadorRoute from "./routes/cantador.tsx";
import MusicasRoute from "./routes/musicas.tsx";
import MusicaRoute from "./routes/musica.tsx";
import RimasRoute from "./routes/rimas.tsx";
import { Toaster } from "sonner";

import "./styles.css";
Expand All @@ -34,6 +35,7 @@ const routeTree = rootRoute.addChildren([
CantadorRoute(rootRoute),
MusicasRoute(rootRoute),
MusicaRoute(rootRoute),
RimasRoute(rootRoute),
]);

const queryClient = new QueryClient();
Expand Down
Loading