Ir al contenido

Utilidades nativas de medios + sistema

Este documento es una inmersión profunda en el subsistema de la capa de primitivas de sistema/medios/conversión descrita en docs/natives-architecture.md: image, html, clipboard y perfilado de work.

  • crates/pi-natives/src/image.rs
  • crates/pi-natives/src/html.rs
  • crates/pi-natives/src/clipboard.rs
  • crates/pi-natives/src/prof.rs
  • crates/pi-natives/src/task.rs
  • packages/natives/src/image/index.ts
  • packages/natives/src/image/types.ts
  • packages/natives/src/html/index.ts
  • packages/natives/src/html/types.ts
  • packages/natives/src/clipboard/index.ts
  • packages/natives/src/clipboard/types.ts
  • packages/natives/src/work/index.ts
  • packages/natives/src/work/types.ts

Nota: no existe crates/pi-natives/src/work.rs; el perfilado de trabajo está implementado en prof.rs y alimentado por la instrumentación en task.rs.

Mapeo de API TS ↔ exportaciones/módulos Rust

Sección titulada «Mapeo de API TS ↔ exportaciones/módulos Rust»
Exportación TS (packages/natives)Exportación N-API de RustMódulo Rust
PhotonImage.parse(bytes)PhotonImage::parseimage.rs
PhotonImage#resize(width, height, filter)PhotonImage::resizeimage.rs
PhotonImage#encode(format, quality)PhotonImage::encodeimage.rs
htmlToMarkdown(html, options)html_to_markdownhtml.rs
copyToClipboard(text)copy_to_clipboard + lógica de respaldo en TSclipboard.rs + clipboard/index.ts
readImageFromClipboard()read_image_from_clipboardclipboard.rs
getWorkProfile(lastSeconds)get_work_profileprof.rs
  • Límite de entrada JS: Uint8Array con bytes de imagen codificada.
  • Límite de decodificación Rust: los bytes se copian a Vec<u8>, el formato se detecta con ImageReader::with_guessed_format(), luego se decodifica a DynamicImage.
  • Estado en memoria: PhotonImage almacena Arc<DynamicImage>.
  • Límite de salida: encode(format, quality) retorna Promise<Uint8Array> (Vec<u8> en Rust).

Los IDs de formato son numéricos:

  • 0: PNG
  • 1: JPEG
  • 2: WebP (codificador sin pérdida)
  • 3: GIF

Restricciones:

  • quality solo se utiliza para JPEG.
  • PNG/WebP/GIF ignoran quality.
  • Los IDs de formato no soportados fallan (Invalid image format: <id>).
  • Límite de entrada JS: string HTML + objeto opcional { cleanContent?: boolean; skipImages?: boolean }.
  • Límite de conversión Rust: la entrada String es convertida por html_to_markdown_rs::convert.
  • Límite de salida: string Markdown.

Comportamiento de conversión:

  • cleanContent tiene valor predeterminado false.
  • Cuando cleanContent=true, se habilita el preprocesamiento con PreprocessingPreset::Aggressive y flags de eliminación forzada para navegación/formularios.
  • skipImages tiene valor predeterminado false.
  • Ruta de texto:
    • TS primero emite OSC 52 (\x1b]52;c;<base64>\x07) cuando stdout es un TTY.
    • El mismo texto se intenta luego a través de la API nativa del portapapeles (native.copyToClipboard) como mejor esfuerzo.
    • En Termux, TS intenta termux-clipboard-set primero.
  • Ruta de lectura de imagen:
    • Rust lee la imagen cruda desde arboard.
    • Rust la recodifica a bytes PNG (crate image), retorna { data: Uint8Array, mimeType: "image/png" }.
    • TS retorna null anticipadamente en Termux o sesiones Linux sin servidor de pantalla (DISPLAY/WAYLAND_DISPLAY ausentes).
  • Límite de recolección: las muestras de perfilado son producidas por guardas profile_region(tag) en task::blocking y task::future.
  • Formato de almacenamiento: buffer circular de tamaño fijo (MAX_SAMPLES = 10_000) que almacena ruta de pila + duración (μs) + marca de tiempo (μs desde el inicio del proceso).
  • Límite de salida: getWorkProfile(lastSeconds) retorna un objeto:
    • folded: texto de pila plegada (entrada para flamegraph)
    • summary: tabla resumen en markdown
    • svg: SVG de flamegraph opcional
    • totalMs, sampleCount
  1. PhotonImage.parse(bytes) programa una tarea de decodificación bloqueante (image.decode).
  2. En caso de éxito, existe un handle nativo PhotonImage en JS.
  3. resize(...) crea un nuevo handle nativo (image.resize), los handles antiguo y nuevo pueden coexistir.
  4. encode(...) materializa bytes (image.encode) sin mutar las dimensiones de la imagen.

Transiciones de fallo:

  • Fallo en la detección de formato/decodificación rechaza la promesa de parse.
  • Fallo en la codificación rechaza la promesa de encode.
  • ID de formato inválido rechaza la promesa de encode.
  1. htmlToMarkdown(html, options) programa una tarea de conversión bloqueante.
  2. La conversión se ejecuta con opciones predeterminadas (cleanContent=false, skipImages=false) a menos que se especifiquen.
  3. Retorna un string markdown o rechaza.

Transiciones de fallo:

  • Fallo del convertidor retorna una promesa rechazada (Conversion error: ...).

copyToClipboard(text) es intencionalmente de mejor esfuerzo y multi-ruta:

  1. Si es TTY: intenta escritura OSC 52 (payload en base64).
  2. Intenta comando Termux cuando TERMUX_VERSION está establecido.
  3. Intenta copia de texto nativa con arboard.
  4. Suprime errores en la capa TS.

readImageFromClipboard() difiere en rigurosidad según la etapa:

  1. TS bloquea estrictamente contextos de ejecución no soportados (Termux/Linux sin interfaz gráfica) retornando null.
  2. La lectura de arboard en Rust se ejecuta solo cuando TS lo permite.
  3. ContentNotAvailable se mapea a null.
  4. Otros errores de Rust rechazan.
  1. Sin inicio explícito: el perfilado está siempre activo cuando se ejecutan los helpers de tareas.
  2. Cada ámbito de tarea instrumentado registra una muestra al destruir la guarda.
  3. Las muestras sobrescriben las entradas más antiguas después de alcanzar la capacidad del buffer.
  4. getWorkProfile(lastSeconds) lee una ventana de tiempo y deriva artefactos plegados/resumen/svg.

Transiciones de fallo:

  • Fallo en la generación de SVG es un fallo suave (svg: null), mientras que folded y summary aún se retornan.
  • Una ventana de muestras vacía retorna datos plegados vacíos y svg: null, no un error.

Operaciones no soportadas y propagación de errores

Sección titulada «Operaciones no soportadas y propagación de errores»
  • Entrada de decodificación no soportada o bytes corruptos: fallo estricto (rechazo de promesa).
  • ID de formato de codificación no soportado: fallo estricto.
  • Sin ruta de respaldo de mejor esfuerzo en el wrapper de TS.
  • Los errores de conversión son fallos estrictos (rechazo).
  • La omisión de opciones se maneja con valores predeterminados de mejor esfuerzo, no como fallo.
  • La copia de texto es de mejor esfuerzo en la capa TS: los fallos operacionales se suprimen.
  • La lectura de imagen distingue “sin imagen” (null) de fallo operacional (rechazo).
  • Termux/Linux sin interfaz gráfica se tratan como contextos no soportados para lectura de imagen (null).
  • La recuperación es estricta para la llamada a la función en sí, pero la generación de artefactos es parcialmente de mejor esfuerzo (svg nullable).
  • El truncamiento del buffer es comportamiento esperado (buffer circular), no un error de pérdida de datos.
  • Texto del portapapeles: OSC 52 depende del soporte del terminal; el acceso nativo al portapapeles depende del entorno de escritorio/sesión.
  • Lectura de imagen del portapapeles: bloqueada en TS para Termux y Linux sin servidor de pantalla.