Pular para o conteúdo

Utilitários nativos de mídia + sistema

Este documento é um aprofundamento de subsistema para a camada de primitivos de sistema/mídia/conversão descrita em docs/natives-architecture.md: image, html, clipboard e perfilamento 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: não existe crates/pi-natives/src/work.rs; o perfilamento de trabalho é implementado em prof.rs e alimentado pela instrumentação em task.rs.

Mapeamento de API TS ↔ exportação/módulo Rust

Seção intitulada “Mapeamento de API TS ↔ exportação/módulo Rust”
Exportação TS (packages/natives)Exportação N-API 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 fallback TSclipboard.rs + clipboard/index.ts
readImageFromClipboard()read_image_from_clipboardclipboard.rs
getWorkProfile(lastSeconds)get_work_profileprof.rs
  • Limite de entrada JS: Uint8Array com bytes de imagem codificada.
  • Limite de decodificação Rust: os bytes são copiados para Vec<u8>, o formato é inferido com ImageReader::with_guessed_format(), então decodificado para DynamicImage.
  • Estado em memória: PhotonImage armazena Arc<DynamicImage>.
  • Limite de saída: encode(format, quality) retorna Promise<Uint8Array> (Vec<u8> no Rust).

IDs de formato são numéricos:

  • 0: PNG
  • 1: JPEG
  • 2: WebP (codificador sem perdas)
  • 3: GIF

Restrições:

  • quality é utilizado apenas para JPEG.
  • PNG/WebP/GIF ignoram quality.
  • IDs de formato não suportados falham (Invalid image format: <id>).
  • Limite de entrada JS: string HTML + objeto opcional { cleanContent?: boolean; skipImages?: boolean }.
  • Limite de conversão Rust: a entrada String é convertida por html_to_markdown_rs::convert.
  • Limite de saída: string Markdown.

Comportamento da conversão:

  • cleanContent tem valor padrão false.
  • Quando cleanContent=true, o pré-processamento é habilitado com PreprocessingPreset::Aggressive e flags de remoção forçada para navegação/formulários.
  • skipImages tem valor padrão false.
  • Caminho de texto:
    • O TS primeiro emite OSC 52 (\x1b]52;c;<base64>\x07) quando stdout é um TTY.
    • O mesmo texto é então tentado via API nativa de área de transferência (native.copyToClipboard) como melhor esforço.
    • No Termux, o TS tenta termux-clipboard-set primeiro.
  • Caminho de leitura de imagem:
    • O Rust lê a imagem bruta do arboard.
    • O Rust recodifica para bytes PNG (crate image), retorna { data: Uint8Array, mimeType: "image/png" }.
    • O TS retorna null antecipadamente no Termux ou em sessões Linux sem servidor de exibição (DISPLAY/WAYLAND_DISPLAY ausentes).
  • Limite de coleta: amostras de perfilamento são produzidas por guardas profile_region(tag) em task::blocking e task::future.
  • Formato de armazenamento: buffer circular de tamanho fixo (MAX_SAMPLES = 10_000) armazenando caminho de pilha + duração (μs) + timestamp (μs desde início do processo).
  • Limite de saída: getWorkProfile(lastSeconds) retorna objeto:
    • folded: texto de pilha empilhada (entrada para flamegraph)
    • summary: tabela resumo em markdown
    • svg: SVG de flamegraph opcional
    • totalMs, sampleCount
  1. PhotonImage.parse(bytes) agenda uma tarefa de decodificação bloqueante (image.decode).
  2. Em caso de sucesso, um handle nativo PhotonImage existe no JS.
  3. resize(...) cria um novo handle nativo (image.resize), handles antigos e novos podem coexistir.
  4. encode(...) materializa bytes (image.encode) sem alterar as dimensões da imagem.

Transições de falha:

  • Falha na detecção de formato/decodificação rejeita a promise de parse.
  • Falha na codificação rejeita a promise de encode.
  • ID de formato inválido rejeita a promise de encode.
  1. htmlToMarkdown(html, options) agenda uma tarefa de conversão bloqueante.
  2. A conversão executa com opções padrão (cleanContent=false, skipImages=false) a menos que especificado.
  3. Retorna string markdown ou rejeita.

Transições de falha:

  • Falha no conversor retorna promise rejeitada (Conversion error: ...).

copyToClipboard(text) é intencionalmente melhor esforço e multi-caminho:

  1. Se TTY: tenta escrita OSC 52 (payload base64).
  2. Tenta comando Termux quando TERMUX_VERSION está definido.
  3. Tenta cópia de texto nativa via arboard.
  4. Suprime erros na camada TS.

readImageFromClipboard() possui rigor diferente por estágio:

  1. O TS bloqueia fortemente contextos de runtime não suportados (Termux/Linux headless) para null.
  2. A leitura arboard do Rust executa apenas quando o TS permite.
  3. ContentNotAvailable é mapeado para null.
  4. Outros erros do Rust rejeitam.
  1. Sem início explícito: o perfilamento está sempre ativo quando os helpers de tarefa executam.
  2. Cada escopo de tarefa instrumentado registra uma amostra no drop do guard.
  3. Amostras sobrescrevem as entradas mais antigas após a capacidade do buffer ser atingida.
  4. getWorkProfile(lastSeconds) lê uma janela de tempo e deriva artefatos folded/summary/svg.

Transições de falha:

  • Falha na geração de SVG é falha suave (svg: null), enquanto folded e summary ainda retornam.
  • Janela de amostra vazia retorna dados folded vazios e svg: null, não um erro.

Operações não suportadas e propagação de erros

Seção intitulada “Operações não suportadas e propagação de erros”
  • Entrada de decodificação não suportada ou bytes corrompidos: falha estrita (rejeição de promise).
  • ID de formato de codificação não suportado: falha estrita.
  • Sem caminho de fallback de melhor esforço no wrapper TS.
  • Erros de conversão são falhas estritas (rejeição).
  • Omissão de opções é padronização de melhor esforço, não falha.
  • Cópia de texto é melhor esforço na camada TS: falhas operacionais são suprimidas.
  • Leitura de imagem distingue “sem imagem” (null) de falha operacional (rejeição).
  • Termux/Linux headless são tratados como contextos não suportados para leitura de imagem (null).
  • A recuperação é estrita para a chamada de função em si, mas a geração de artefatos é parcialmente melhor esforço (svg anulável).
  • Truncamento do buffer é comportamento esperado (buffer circular), não um bug de perda de dados.
  • Texto da área de transferência: OSC 52 depende do suporte do terminal; o acesso nativo à área de transferência depende do ambiente de desktop/sessão.
  • Leitura de imagem da área de transferência: bloqueada no TS para Termux e Linux sem servidor de exibição.