Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ElthonJohan/comunitea/llms.txt

Use this file to discover all available pages before exploring further.

The tablero (board) module provides everything needed to render and operate ComuniTEA’s AAC communication board. It exports two primary hooks — useTablero for board navigation state and useFrase for managing the four-slot sentence strip — plus the buildMergedBoard utilities, the Pictograma and Subcategoria data types, and the pre-built data constants (ALL_SUBCATS, PERSONAS_ROW, PETICIONES_ROW, and the pic() factory). Together these form a complete, self-contained state management layer that keeps board navigation and sentence composition in sync without any external state management library.
// features/tablero/index.ts
export * from './hooks/useTablero';
export * from './hooks/useFrase';
export * from './data/tablero';
export * from './data/voice/frase';

Data structures

Pictograma

The atomic unit of the tablero. Every tile on the board, in the sentence strip, and in the personas/peticiones rows is a Pictograma.
export interface Pictograma {
  id: string;
  emoji: string;
  label: string;
  categoria: string;
  esFavorito: boolean;
  /**
   * Key in getAudioAssets(voice) from constants/AudioAssets.ts.
   * Falls back to `id` if omitted (e.g. "manzana" → manzana.mp3).
   */
  audioAssetKey?: string;
  /** Remote avatar URL or custom image URI for personas / custom pictograms. */
  imageUri?: string;
}

Subcategoria

A named group of pictograms displayed as a horizontal row on the board.
export interface Subcategoria {
  id: string;
  nombre: string;
  emoji: string;
  categoriaId: string;
  pictogramas: Pictograma[];
}

pic() factory function

Use pic() to create Pictograma objects without manually spreading optional fields.
export function pic(
  id: string,
  emoji: string,
  label: string,
  categoria: string,
  esFavorito = false,
  audioAssetKey?: string,
  imageUri?: string,
): Pictograma
id
string
required
Unique pictogram identifier. Also used as the audioAssetKey fallback.
emoji
string
required
Emoji displayed when no imageUri is set.
label
string
required
Human-readable text shown below the pictogram tile and spoken aloud.
categoria
string
required
Category string (e.g. "comida", "personas", "peticiones"). Routes taps in useFrase.agregarPictograma().
esFavorito
boolean
Whether the pictogram appears in the Favorites row. Defaults to false.
audioAssetKey
string
Explicit key to look up in the bundled audio asset map. Omit to use id as the key.
imageUri
string
Remote or local URI for a photo-based pictogram (e.g. child’s avatar, custom photo).

Pre-built data constants

ALL_SUBCATS

The full default board — an array of Subcategoria objects covering fruits, food, toys, vegetables, sweets, hygiene, clothing, emotions, places, animals, actions, and more. Exported from features/tablero/data/tablero.ts.
export const ALL_SUBCATS: Subcategoria[];

PERSONAS_ROW

Default people pictograms shown in the Personas row when the child profile has no custom important_people set.
export const PERSONAS_ROW: Pictograma[] = [
  pic('mama',  '👩',    'Mamá',  'personas'),
  pic('papa',  '👨',    'Papá',  'personas'),
  pic('profe', '👩‍🏫', 'Profe', 'personas'),
  pic('nico',  '👦',    'Nico',  'personas'),
];

PETICIONES_ROW

The fixed row of social/action pictograms shown beneath the board.
export const PETICIONES_ROW: Pictograma[] = [
  pic('quiero',    '🤲', 'Quiero',    'peticiones'),
  pic('no-quiero', '🚫', 'No quiero', 'peticiones'),
  pic('me-gusta',  '❤️', 'Me gusta',  'peticiones'),
  pic('me-duele',  '😣', 'Me duele',  'peticiones'),
  pic('ayuda',     '🙋', 'Ayuda',     'peticiones'),
];

buildMergedBoard

buildMergedBoard is not a single function but a set of merge utilities in features/tablero/buildMergedBoard.ts that combine default board data with per-child personalization.

mergeAllSubcats

export function mergeAllSubcats(
  preferred: string[] | null | undefined,
): Subcategoria[]
Returns ALL_SUBCATS with an optional "Le gusta" subcategory prepended, built from the child’s preferred_activities profile field. If preferred is empty or null, returns ALL_SUBCATS unchanged.
preferred
string[] | null | undefined
The preferred_activities array from the child’s profile. Each entry is a raw activity identifier or a custom:Label string.

mergePersonasRow

export function mergePersonasRow(
  importantPeople: string[] | null | undefined,
  childAvatarUrl?: string | null,
): Pictograma[]
Builds the Personas row. The child’s “Yo” picto is always first. If importantPeople is empty, falls back to PERSONAS_ROW; otherwise parses emoji|Name lines and creates custom pictos.
importantPeople
string[] | null | undefined
Array of "emoji|Name" strings from the child profile (e.g. "👩|Mamá").
childAvatarUrl
string | null | undefined
Remote avatar URL. Used as imageUri on the “Yo” picto.

Helper functions

/** Formats a person entry for storage: "👩|Mamá" */
export function formatImportantPersonLine(emoji: string, name: string): string

/** Parses "👩|Mamá" → { emoji: "👩", name: "Mamá" } */
export function parseImportantPersonLine(raw: string): { emoji: string; name: string }

/** Seeds important_people from default PERSONAS_ROW for first-time setup. */
export function seedImportantPeopleFromPersonasRow(): string[]

useTablero

useTablero is the main board navigation hook. It derives the subcategory list from the child’s profile, manages the active category filter, controls the category overlay drawer, and exposes the FlatList ref for programmatic scroll-to-category.
export function useTablero(): {
  subcategorias: Subcategoria[];
  categorias: CategoriaResumen[];
  categoriaActiva: string | null;
  overlayVisible: boolean;
  openOverlay: () => void;
  closeOverlay: () => void;
  selectCategoria: (categoriaId: string) => void;
  clearCategoriaActiva: () => void;
  categoriaActivaLabel: string | null;
  listRef: React.RefObject<FlatList<Subcategoria>>;
  personas: Pictograma[];
  peticiones: Pictograma[];
  favoritos: Pictograma[];
  state: TableroState;
}

Return shape

subcategorias
Subcategoria[]
The visible subcategories slice. For basic-level users this is capped at 4 (maxSubcategoriasVisibles = 4); for intermediate/advanced it’s unlimited. Computed from mergeAllSubcats(childProfile.preferred_activities).
categorias
CategoriaResumen[]
Deduplicated category summaries derived from all subcategories (including those beyond the visible slice). Used to populate the category overlay drawer.
categoriaActiva
string | null
The categoriaId of the currently highlighted category, or null if no filter is active.
overlayVisible
boolean
Controls the visibility of the category drawer overlay.
openOverlay
() => void
Shows the category selection overlay.
closeOverlay
() => void
Hides the category selection overlay.
selectCategoria
(categoriaId: string) => void
Sets categoriaActiva, closes the overlay, and scrolls the FlatList to the matching subcategory row using scrollToIndex.
clearCategoriaActiva
() => void
Clears the active category filter (shows all subcategories).
categoriaActivaLabel
string | null
Human-readable label for the active category (e.g. "Comida"), or null.
listRef
React.RefObject<FlatList<Subcategoria>>
Pass this directly to your FlatList so selectCategoria can call scrollToIndex.
personas
Pictograma[]
Merged Personas row: [childYoPicto, ...importantPeople] or [childYoPicto, ...PERSONAS_ROW].
peticiones
Pictograma[]
Always equals PETICIONES_ROW (the five social-action pictograms).
favoritos
Pictograma[]
Flat list of all pictograms across all subcategories where esFavorito === true.
state
TableroState
Snapshot object { subcategorias, categoriaActiva, overlayVisible } — useful if you need to pass board state as a prop without destructuring.

CategoriaResumen shape

export interface CategoriaResumen {
  id: string;
  nombre: string;
  emoji: string;
  pictogramCount: number;  // Total pictograms across all subcats for this category
}

useFrase

useFrase manages the four-slot AAC sentence strip. The strip follows a fixed structure: [Persona] [Petición] [Objeto 1] [Objeto 2]. Routing is automatic — pictograms with categoria === "personas" go into slot 0, "peticiones" into slot 1, and everything else into slot 2 then slot 3. Sentence playback uses speakFrasePhrase(), which runs through ElevenLabs TTS with the same fallback chain as useSpeech.
export function useFrase(): {
  slotPersona: Pictograma;
  slotPeticion: Pictograma | null;
  slotsObjeto: [Pictograma | null, Pictograma | null];
  slots: (Pictograma | null)[];
  fraseLista: boolean;
  puedeHablar: boolean;
  agregarPersona: (picItem: Pictograma) => void;
  agregarPeticion: (picItem: Pictograma) => void;
  agregarObjeto: (picItem: Pictograma) => void;
  agregarPictograma: (p: Pictograma) => void;
  quitarPictograma: (index: number) => void;
  limpiarFrase: () => void;
  leerFrase: () => Promise<void>;
}

Sentence strip slot structure

Slot 0  │  Slot 1    │  Slot 2    │  Slot 3
──────────────────────────────────────────────
Persona │  Petición  │  Objeto 1  │  Objeto 2
 "Yo"   │ "Quiero"   │ "Manzana"  │  (vacío)

Return shape

slotPersona
Pictograma
Always occupied. Defaults to the “Yo” picto built from the child’s avatar. Updated when a pictogram with categoria === "personas" is tapped.
slotPeticion
Pictograma | null
The social/action verb (petición). null when empty. Setting this slot enables playback (fraseLista = true).
slotsObjeto
[Pictograma | null, Pictograma | null]
Two optional object slots. The first empty slot is always filled first. If both are occupied, the second is overwritten.
slots
(Pictograma | null)[]
Computed array of all four slots in order: [slotPersona, slotPeticion, slotsObjeto[0], slotsObjeto[1]]. Convenient for rendering a flat list of slot tiles.
fraseLista
boolean
true when slotPeticion !== null. Use this to enable the ▶ play button.
puedeHablar
boolean
Alias for fraseLista. Both reflect the same condition.
agregarPictograma
(p: Pictograma) => void
The primary tap handler. Routes to agregarPersona, agregarPeticion, or agregarObjeto based on p.categoria.
quitarPictograma
(index: number) => void
Removes a pictogram by slot index. Removing slot 0 resets it to the default “Yo” picto. Removing slots 1–3 sets the slot to null.
limpiarFrase
() => void
Resets all slots: petición and objeto slots become null, persona resets to the child’s default “Yo” picto.
leerFrase
() => Promise<void>
Assembles the label text from all occupied slots, optionally runs AI expansion via expandTableroSlots(), stops any concurrent speech, and plays the full sentence using speakFrasePhrase(). Also calls recordSentenceSpoken() on the child profile context.

FraseState type

export interface FraseState {
  slotPersona: Pictograma;
  slotPeticion: Pictograma | null;
  slotsObjeto: [Pictograma | null, Pictograma | null];
  /** true when slotPeticion is set — enables the ▶ button */
  fraseLista: boolean;
}

useTablero + useFrase together

The two hooks compose naturally: useTablero drives board navigation, useFrase manages the sentence strip, and agregarPictograma bridges them.
import { useTablero, useFrase } from '@/features/tablero';
import { useSpeech } from '@/features/vocabulario';

export function TableroScreen() {
  const {
    subcategorias,
    categorias,
    openOverlay,
    selectCategoria,
    personas,
    peticiones,
    listRef,
  } = useTablero();

  const {
    slots,
    fraseLista,
    agregarPictograma,
    quitarPictograma,
    limpiarFrase,
    leerFrase,
  } = useFrase();

  const { speakWithAssetKey } = useSpeech();

  const handlePictogramPress = (p: Pictograma) => {
    // Play audio for immediate auditory feedback
    speakWithAssetKey(p.label, p.audioAssetKey ?? p.id);
    // Route pictogram to the correct sentence slot
    agregarPictograma(p);
  };

  return (
    <View>
      {/* Sentence strip */}
      <SentenceStrip
        slots={slots}
        onRemove={quitarPictograma}
        onClear={limpiarFrase}
        onPlay={fraseLista ? leerFrase : undefined}
      />

      {/* Category drawer button */}
      <CategoryButton onPress={openOverlay} />

      {/* Personas row */}
      <PictogramRow pictograms={personas} onPress={handlePictogramPress} />

      {/* Main board */}
      <FlatList
        ref={listRef}
        data={subcategorias}
        renderItem={({ item }) => (
          <SubcategoriaRow subcat={item} onPress={handlePictogramPress} />
        )}
      />

      {/* Peticiones row */}
      <PictogramRow pictograms={peticiones} onPress={handlePictogramPress} />

      {/* Category overlay */}
      <CategoryOverlay
        categories={categorias}
        onSelect={selectCategoria}
      />
    </View>
  );
}
leerFrase() stops any ongoing speech from the G2/G3 exercise hooks (stopSpeakG2, stopSpeakG3) before playing the sentence, so boards and exercises never overlap audio.
For basic-level children (UserGroupLevel === 'basic'), useTablero automatically limits subcategorias to the first 4 entries and increases pictogram tile sizes. This is driven by useUserGroup() internally — no extra configuration is required.

Build docs developers (and LLMs) love