From ec15d3f3d61ae8d4899105b5da7776b5ce2c6617 Mon Sep 17 00:00:00 2001 From: Bryan Date: Sun, 1 Nov 2026 19:15:26 -0600 Subject: [PATCH 1/3] docs(readme): translate to English and document accessibility Also lists the supported peer ranges and the accessibilityLabels prop, and links the contributing guide. Co-Authored-By: Claude Sonnet 5.5 --- README.md | 537 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 281 insertions(+), 256 deletions(-) diff --git a/README.md b/README.md index 72f96b8..bca475a 100644 --- a/README.md +++ b/README.md @@ -2,54 +2,55 @@ > npm: [`@acertijosapps/react-native-md-document`](https://www.npmjs.com/package/@acertijosapps/react-native-md-document) -Visor de documentos **Markdown-como-PDF** para React Native: documento inmutable, con paso -de hoja animado, zoom, subrayado persistente, text-to-speech con resaltado sincronizado y -tema claro/oscuro. Sin librerías de terceros para el contenido (parser y render propios); el -storage, el origen del documento y el motor de voz son inyectables. +**Markdown-as-PDF** document viewer for React Native: an immutable document with animated +page-turning, zoom, persistent highlights, text-to-speech with synchronized word highlighting +and a light/dark theme. No third-party libraries for the content (own parser and renderer); the +storage, the document source and the speech engine are injectable. -## Vista previa +## Preview -Documento de demostración (`example/`) en tema claro y oscuro: banner con tokens de marca, -estilos en línea, resaltado y adaptación de color por tema. +Demo document (`example/`) in light and dark themes: banner with brand tokens, inline styles, +highlights and per-theme color adaptation. -| Claro | Oscuro | +| Light | Dark | |---|---| -| Tema claro | Tema oscuro | - -## Características - -- **Render Markdown** + **HTML embebido**: encabezados, párrafos, listas, blockquote, code, - `hr`, estilos en línea (negrita, cursiva, código, tachado, enlaces), **tablas** y HTML con - estilos inline (color, fondo, centrado, tamaños, anchos de columna). -- **Paso de hoja** animado (Reanimated + Gesture Handler), un gesto = una página. -- **Zoom** por pinch + arrastre, compuesto con el flip y el tap. -- **Subrayado** por palabra, **persistente entre sesiones** (storage inyectable). -- **Text-to-speech** con resaltado de la palabra hablada (incluso en tablas), pausa/reanudar, - velocidad, y auto-avance de página siguiendo la lectura. -- **Tema claro/oscuro** (`auto` sigue el SO) con tokens de color adaptables por tema. -- **Orígenes**: URL remota, ruta local del dispositivo, ruta del proyecto. -- **Orientación** vertical y horizontal (re-pagina al rotar). -- **Modo libro**: en tablets y plegables en horizontal muestra dos páginas lado a lado, con - el volteo de hoja desde el lomo. Activo por defecto; se desactiva con `dualPage={false}`. - -## Requisitos - -| Plataforma | Mínimo | Nota | +| Light theme | Dark theme | + +## Features + +- **Markdown** + **embedded HTML**: headings, paragraphs, lists, blockquotes, code, `hr`, + inline styles (bold, italic, code, strikethrough, links), **tables** and HTML with inline + styles (color, background, centering, sizes, column widths). +- Animated **page turning** (Reanimated + Gesture Handler), one gesture = one page. +- **Zoom** with pinch + drag, composed with the flip and the tap. +- **Per-word highlights**, **persistent across sessions** (injectable storage). +- **Text-to-speech** with the spoken word highlighted (even inside tables), pause/resume, + speed, and automatic page advance that follows the reading. +- **Light/dark theme** (`auto` follows the OS) with color tokens that adapt per theme. +- **Sources**: remote URL, local device path, bundled project file. +- **Portrait and landscape** orientation (re-paginates on rotation). +- **Book mode**: on tablets and foldables in landscape it shows two pages side by side, with + the page turning from the spine. On by default; turn it off with `dualPage={false}`. +- **Accessible**: screen readers (VoiceOver, TalkBack) hear the current page, can turn pages + with a swipe up/down, and skip the pages hidden behind the flip animation. + +## Requirements + +| Platform | Minimum | Note | |---|---|---| -| **iOS** | 13.0 | Deployment target del módulo nativo (`react-native-md-document.podspec`). TTS con resaltado por palabra incluido. | -| **Android** | 7.0 (API 24) | `minSdkVersion` del módulo. El TTS habla en API 24+, pero el **resaltado por palabra** durante la lectura usa `onRangeStart`, que requiere **Android 8.0 (API 26)**. | -| **React Native** | 0.76 | Probado con RN 0.76.5 / Expo SDK 52. | -| **Peer deps** | `react-native-reanimated` ≥ 3.16 · `react-native-gesture-handler` ≥ 2.20 | Únicas dependencias visuales. | +| **iOS** | 13.0 | Deployment target of the native module (`react-native-md-document.podspec`). TTS with per-word highlighting included. | +| **Android** | 7.0 (API 24) | The module's `minSdkVersion`. TTS speaks on API 24+, but the **per-word highlighting** while reading uses `onRangeStart`, which requires **Android 8.0 (API 26)**. | +| **React Native** | 0.76 | Tested with RN 0.76.5 / Expo SDK 52. | +| **Peer deps** | `react-native-reanimated` ≥ 3.16 and < 4 · `react-native-gesture-handler` ≥ 2.20 and < 3 | The only visual dependencies. Reanimated 4 and Gesture Handler 3 are not supported yet. | ### Android 16 KB (page size) -**Compatible.** `react-native-md-document` **no incluye código nativo compilado** -(`.so`): el módulo `DocumentSpeech` es Kotlin puro y se compila al dex de la -app. Al no aportar binarios, no hay librerías desalineadas de nuestra parte — -la librería cumple el requisito de 16 KB de Android 15 (Google Play, `targetSdk -35`) por construcción, en cualquier tamaño de página. +**Compatible.** `react-native-md-document` **ships no compiled native code** (`.so`): the +`DocumentSpeech` module is plain Kotlin and is compiled into the app's dex. Since it adds no +binaries, none of our libraries can be misaligned — the library meets Android 15's 16 KB +requirement (Google Play, `targetSdk 35`) by construction, on any page size. -## Instalación +## Installation ```sh # npm @@ -59,7 +60,7 @@ npm install @acertijosapps/react-native-md-document react-native-gesture-handler yarn add @acertijosapps/react-native-md-document react-native-gesture-handler react-native-reanimated ``` -`react-native-reanimated` requiere su plugin de Babel (último en la lista): +`react-native-reanimated` requires its Babel plugin (last in the list): ```js // babel.config.js @@ -69,7 +70,7 @@ module.exports = { }; ``` -La app debe envolver la raíz en `GestureHandlerRootView`: +The app must wrap its root in `GestureHandlerRootView`: ```tsx import { GestureHandlerRootView } from 'react-native-gesture-handler'; @@ -79,92 +80,91 @@ export default function App() { } ``` -Para el **text-to-speech nativo**, el módulo `DocumentSpeech` se autoenlaza como cualquier -paquete RN: +For **native text-to-speech**, the `DocumentSpeech` module is autolinked like any RN package: ```sh -npx pod-install # iOS (o: cd ios && pod install) -# Android: autolinking por gradle, sin pasos extra +npx pod-install # iOS (or: cd ios && pod install) +# Android: Gradle autolinking, no extra steps ``` -### Dependencias opcionales +### Optional dependencies -- **Almacenamiento del subrayado**: la librería **no** depende de - `@react-native-async-storage/async-storage`. El store es inyectable - (`getItem`/`setItem`, sync o async): sirve AsyncStorage, MMKV, SQLite o el - tuyo. Instálalo **solo si** quieres persistir subrayados con esa opción; para - subrayado en memoria usa `createMemoryHighlightStore()`, y sin subrayado no - necesitas nada. Ver [Subrayado persistente](#subrayado-persistente). +- **Highlight storage**: the library does **not** depend on + `@react-native-async-storage/async-storage`. The store is injectable + (`getItem`/`setItem`, sync or async): AsyncStorage, MMKV, SQLite or your own will do. Install + it **only if** you want to persist highlights with that option; for in-memory highlights use + `createMemoryHighlightStore()`, and if you don't highlight you need nothing. See + [Persistent highlights](#persistent-highlights). -Únicas dependencias obligatorias: `react-native-reanimated` y -`react-native-gesture-handler` (los peers). +The only required dependencies are `react-native-reanimated` and +`react-native-gesture-handler` (the peers). -## Inicio rápido +## Quick start ```tsx import { DocumentViewer, parse } from '@acertijosapps/react-native-md-document'; -const doc = parse('# Hola\n\nUn documento **Markdown**.'); +const doc = parse('# Hello\n\nA **Markdown** document.'); export function Viewer() { return ; } ``` -`DocumentViewer` mide el contenido, lo pagina al tamaño del viewport y habilita flip + zoom. -`parse(source)` devuelve un `MDDocument` inmutable con `plainText` (la proyección de texto que -usan highlight y TTS). +`DocumentViewer` measures the content, paginates it to the viewport size and enables flip + +zoom. `parse(source)` returns an immutable `MDDocument` with `plainText` (the text projection +that highlights and TTS rely on). -## Orígenes del documento +## Document sources -`useDocumentSource` cubre los tres orígenes: +`useDocumentSource` covers the three sources: ```tsx import { DocumentViewer, useDocumentSource } from '@acertijosapps/react-native-md-document'; function Screen() { - // Proyecto (bundled): string o { markdown } + // Project (bundled): string or { markdown } const { document, isLoading, error, reload } = useDocumentSource({ markdown: MD_STRING }); - // URL remota: fetch integrado + // Remote URL: built-in fetch // useDocumentSource({ uri: 'https://example.com/doc.md' }) - // Ruta local del dispositivo + // Local device path // useDocumentSource({ uri: 'file:///…/doc.md' }) if (!document) { - return null; // isLoading / error / reload disponibles + return null; // isLoading / error / reload are available } return ; } ``` -Detección de string: con `http(s)://`, `file:`, `content:`, `asset:` o que empiece con `/` se -trata como URI; cualquier otro string es Markdown crudo. +String detection: a string starting with `http(s)://`, `file:`, `content:`, `asset:` or `/` is +treated as a URI; any other string is raw Markdown. -### Carga gestionada por el visor +### Viewer-managed loading -Si no quieres cablear el hook a mano, pasa `source` directo a `DocumentViewer`: carga solo y -expone los estados de carga. +If you don't want to wire the hook by hand, pass `source` straight to `DocumentViewer`: it +loads on its own and exposes the loading states. ```tsx console.log('listo', doc.length)} + onLoadComplete={(doc) => console.log('ready', doc.length)} onLoadError={(err) => console.warn(err.message)} renderLoading={() => } renderError={(err, retry) => ( {err.message} -