npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

siga-printer

v1.6.1

Published

Full-featured ESC/POS thermal printer library for React Native. Supports Epson TM-T20X II and compatibles via USB, Serial, Bluetooth and TCP/IP.

Readme

siga-printer

npm version license

React Native library for ESC/POS thermal printer printing. Full support for Epson TM-T20X II and compatible printers.

English | Português


What's new in v1.5.4

  • printerManager.getPrinter() — retrieve the active ConnectedThermalPrinter from any screen without opening a second connection
  • Android 14 fixPendingIntent now uses explicit intent + RECEIVER_NOT_EXPORTED (required for API 34+)
  • USB discovery filter — only shows actual printers (USB class 7 + known vendor IDs); chargers and hubs no longer appear
  • Expo config plugin — auto-configures AndroidManifest, Info.plist and JitPack in android/build.gradle
  • TemplateDesigner — visual drag-and-drop canvas to build print templates and export typed TypeScript code
  • PrintPreview / PrintPreviewBuilder — realistic thermal paper preview with landscape mode
  • ConnectionManager — global singleton for connection state across screens with auto-reconnect
  • useConnectionManager hook + PrinterSelector component

English

Supported connections

| Type | Android | iOS | Notes | |--------------|---------|------|------------------------------------------| | USB (OTG) | ✅ | ❌ | USB OTG cable required | | Serial | ✅ | ❌ | Via USB-Serial adapter (RS-232) | | Bluetooth | ✅ | ✅ | Classic SPP (Android) / MFi (iOS) | | TCP/IP | ✅ | ✅ | Port 9100 (Epson default) |


Installation

npm install siga-printer
# or
yarn add siga-printer

Expo (bare workflow / EAS Build)

Managed workflow is not supported — this library has native modules. Use bare workflow (expo prebuild) or Expo Dev Client.

Add the config plugin to app.json:

{
  "expo": {
    "plugins": ["siga-printer"]
  }
}

Then run:

expo prebuild
# or (EAS)
eas build

The plugin automatically adds all required Android permissions, iOS plist entries, and the JitPack repository — no manual setup needed.

Bare React Native

Add to android/app/src/main/AndroidManifest.xml:

<!-- USB -->
<uses-feature android:name="android.hardware.usb.host" android:required="false" />

<!-- Bluetooth (Android 12+) -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

Add to android/build.gradle:

allprojects {
  repositories {
    maven { url 'https://jitpack.io' }
  }
}

iOS

cd ios && pod install

Add to Info.plist (for Bluetooth MFi):

<key>UISupportedExternalAccessoryProtocols</key>
<array>
  <string>com.epson.escpos</string>
</array>

Basic usage

import { ThermalPrinter } from 'siga-printer';

const printer = await ThermalPrinter.connect({
  type: 'usb',
  vendorId:  0x04b8,  // Epson
  productId: 0x0202,  // TM-T20X II
});

await printer
  .init()
  .text('Hello, world!', { bold: true, align: 'center', size: 2 })
  .divider()
  .qrCode('https://example.com', { size: 6 })
  .cut()
  .print();

await printer.disconnect();

API Reference

ThermalPrinter.connect(config, options?)

Connects to a printer and returns a ConnectedThermalPrinter.

const printer = await ThermalPrinter.connect(
  {
    type:      'usb',
    vendorId:  0x04b8,
    productId: 0x0202,
    timeout:   3000,
  },
  {
    profile:        PRINTER_PROFILES.EPSON_TM_T20X,
    charset:        'CP860',
    orientation:    'portrait',
    autoCut:        false,
    feedAfterPrint: 0,
  }
);

ThermalPrinter.create(options?)

Creates a builder without connecting (generate bytes and send externally).

const builder = ThermalPrinter.create({ charset: 'CP860' });
const bytes   = await builder.init().text('Test').cut().build();
// bytes: Uint8Array

Content methods (chainable)

.init()

Initializes the printer (ESC @). Always call at the start of each job.

.text(content, options?)

printer.text('Normal text')
printer.text('Bold centered', { bold: true, align: 'center' })
printer.text('Large', { size: 2 })
printer.text('Inverted', { invert: true })
printer.text('Double underline', { underline: 'double' })

| Prop | Type | Default | |--------------------|---------------------------------|----------| | bold | boolean | false | | underline | boolean \| 'double' | false | | size | 1–8 | 1 | | widthMultiplier | 1–8 | 1 | | heightMultiplier | 1–8 | 1 | | align | 'left' \| 'center' \| 'right' | 'left' | | invert | boolean | false | | charset | CharsetEncoding | inherited|

.feed(lines?) / .feedDots(dots)

Advance paper by lines or dots.

.divider(options?)

printer.divider()                    // ─────────────────────
printer.divider({ style: 'double' }) // =====================
printer.divider({ style: 'dashed' }) // ---------------------
printer.divider({ style: 'dotted' }) // .....................
printer.divider({ char: '*' })       // *********************

.section(title, options?)

Section header with automatic dividers:

─────────────────────────────────────────
           CUSTOMER DATA
─────────────────────────────────────────

.row(cells)

Column layout. Widths must sum to 100:

printer.row([
  { text: 'Product',  width: 60, align: 'left'  },
  { text: 'Qty',      width: 15, align: 'center' },
  { text: '$ 99.90', width: 25, align: 'right'  },
])

.barcode(data, options?)

printer.barcode('34191090080000008214800082194207197960000125000', {
  type:        'ITF',
  height:      60,
  width:       2,
  hriPosition: 'below',
  align:       'center',
})

Supported types: CODE39, CODE93, CODE128, EAN8, EAN13, ITF / ITF25, CODABAR, UPC_A, UPC_E

.qrCode(data, options?)

printer.qrCode('https://example.com', {
  size:       5,    // module size 1–16
  errorLevel: 'M',  // L | M | Q | H
  align:      'center',
})

.image(options)

printer.image({ source: require('./assets/logo.png'), width: 200, align: 'center' })
printer.image({ source: 'data:image/png;base64,...', dither: 'floyd-steinberg' })

| Dither mode | Best for | Speed | |-------------------|-------------------------|--------| | threshold | Monochrome logos | Fast | | floyd-steinberg | Photos and gradients | Medium | | atkinson | Logos with halftones | Medium | | bayer | Balanced | Medium |

.cut(mode?, options?)

printer.cut()                     // full cut + 3 feed lines
printer.cut('partial')            // partial cut
printer.cut('full', { feed: 5 }) // custom feed

.cashDrawer(options?)

printer.cashDrawer()                         // pin 2, 100ms
printer.cashDrawer({ pin: 5, duration: 200 })

.raw(bytes)

Inject raw ESC/POS bytes for commands not covered by the API.


Print orientation

// Portrait (default)
const printer = await ThermalPrinter.connect(config, { orientation: 'portrait' });

// Landscape — ALL content rotated 90° (text, barcodes, QR, dividers, rows)
const printer = await ThermalPrinter.connect(config, { orientation: 'landscape' });

// Switch during a job
printer.setOrientation('landscape');

// Or via printerManager.getPrinter()
const printer = printerManager.getPrinter({ orientation: 'landscape' });

How it works — two paths, chosen automatically by printer profile:

| Printer | Method | Quality | Data sent | |---|---|---|---| | Epson TM series | ESC/POS Page Mode (ESC L / ESC T 1 / FF) | Native 203 DPI font + barcode | ~500 bytes | | Generic (no Page Mode) | Android Canvas bitmap → rotate 90° → GS v 0 | Rasterized | 50–400 KB |

For Epson TM-T20X II (default profile), the printer firmware handles all rotation natively — no bitmap generation, no extra processing, same speed as portrait.

Printer profiles control which path is used via supportsPageMode. All EPSON_TM_* profiles have supportsPageMode: true. GENERIC_58MM / GENERIC_80MM default to false (bitmap fallback). Override with a custom profile if your generic printer supports page mode.


Connection manager

printerManager is a global singleton that holds connection state across components. Connect once, print from any screen.

import { printerManager, useConnectionManager, PrinterSelector } from 'siga-printer';

// ── Imperative (outside components) ──────────────────────────

await printerManager.connectDevice(device); // device from discoverAll()
await printerManager.connect({ type: 'usb' });
await printerManager.disconnect();
await printerManager.reconnect();

const unsub = printerManager.subscribe(state => {
  console.log(state.status);          // 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'error' | 'disconnected'
  console.log(state.connectedDevice); // DiscoveredDevice | null
});
unsub();

printerManager.configure({
  autoReconnect:       true,
  maxReconnectAttempts: 3,
  reconnectDelay:      1000, // ms, doubles each attempt
});
// ── Reactive hook (inside components) ────────────────────────

import { useConnectionManager } from 'siga-printer';

function MyScreen() {
  const {
    status,
    connectedDevice,
    isConnected,
    error,
    connectDevice,
    connect,
    disconnect,
    reconnect,
  } = useConnectionManager();

  return <Text>{isConnected ? connectedDevice?.name : status}</Text>;
}
// ── Ready-made UI component ───────────────────────────────────

import { PrinterSelector } from 'siga-printer';

function SetupScreen() {
  return (
    <PrinterSelector
      onConnect={(device) => console.log('Connected to', device.name)}
      onError={(msg) => console.error(msg)}
      scanType="all"    // 'all' | 'usb' | 'bluetooth' | 'tcp'
      showStatus={true}
    />
  );
}

PrinterSelector includes: scan by type, device list with icons, per-device connect/disconnect buttons, real-time status banner.

// ── Print from any screen after connecting ────────────────────

import { printerManager, BoletoTemplate } from 'siga-printer';

async function handlePrint() {
  // Reuses the connection made by PrinterSelector — no second connect()
  const printer = printerManager.getPrinter();
  if (!printer) {
    console.error('Not connected');
    return;
  }
  await BoletoTemplate.print(printer, { ... });
}

getPrinter() returns a ConnectedThermalPrinter backed by the already-open connection. Returns null if not connected. Use useConnectionManager() reactively to guard the UI, and printerManager.getPrinter() imperatively when printing.


Print preview

Render a realistic thermal paper preview — no printer required. Useful for testing templates, building template editors, and showing print previews to users.

import { PrintPreviewBuilder, PrintPreview } from 'siga-printer';

function MyPreview() {
  const builder = new PrintPreviewBuilder({
    profile:     PRINTER_PROFILES.EPSON_TM_T20X,
    orientation: 'portrait',  // or 'landscape'
  });

  builder
    .init()
    .text('RECEIPT', { bold: true, align: 'center', size: 2 })
    .divider()
    .row([
      { text: 'Coffee',  width: 60 },
      { text: '$ 3.50', width: 40, align: 'right' },
    ])
    .divider()
    .qrCode('https://example.com', { size: 4, align: 'center' })
    .cut();

  return (
    <PrintPreview
      builder={builder}
      showRuler={true}
      showMetrics={true}
      showCutMark={true}
    />
  );
}

PrintPreview props:

| Prop | Type | Default | Description | |--------------------|-----------|---------|-------------------------------------| | builder | PrintPreviewBuilder | required | Builder with content | | showRuler | boolean | true | mm ruler at top of paper | | showMetrics | boolean | true | metrics bar (length, overflow count)| | showCutMark | boolean | true | scissors cut mark line | | highlightOverflow| boolean | true | highlight overflow elements in red | | scale | number | auto | paper width scale factor | | style | ViewStyle| — | ScrollView style override |

Landscape preview: when orientation: 'landscape', the preview shows a portrait paper strip with content rotated 90° — matching the physical paper as it exits the printer.


Template designer

Visual drag-and-drop canvas for building print templates. Generates typed TypeScript code ready to use with PrintPreviewBuilder.

import { TemplateDesigner } from 'siga-printer';

function DesignerScreen() {
  return (
    <TemplateDesigner
      onExport={(code, template) => {
        console.log('Generated TypeScript:', code);
        // paste into your project
      }}
      onSave={(template) => {
        // save template JSON to your backend or AsyncStorage
        saveTemplate(template);
      }}
    />
  );
}

TemplateDesigner props:

| Prop | Type | Description | |--------------------|-------------------------------------------------|----------------------------------| | initialTemplate | Partial<TemplateDefinition> | Pre-load an existing template | | onExport | (code: string, template: TemplateDefinition) => void | Called when user taps Export | | onSave | (template: TemplateDefinition) => void | Called when user taps Save |

Features:

  • Drag-and-drop blocks on a scaled paper canvas
  • 2mm snap-to-grid
  • Block types: text, barcode, QR code, image, divider, row
  • Per-block properties panel (alignment, font, size, etc.)
  • Variables panel — define dynamic placeholders ({{customer_name}}, etc.)
  • Printer profile and orientation selector
  • Export to TypeScript code snippet
  • Phone portrait: tabbed layout (Palette / Canvas / Properties)
  • Tablet / landscape: three-column layout

Device discovery

import { PrinterDiscovery } from 'siga-printer';

const discovery = new PrinterDiscovery();

const all       = await discovery.discoverAll();
const epsonUsb  = await discovery.discoverEpsonUSB();
const network   = await discovery.discoverNetwork();
const bluetooth = await discovery.discoverBluetooth();

// [
//   { type: 'usb',       name: '/dev/bus/usb/001/002', vendorId: 0x04b8, productId: 0x0202 },
//   { type: 'bluetooth', name: 'TM-T20X',              address: 'AA:BB:CC:DD:EE:FF' },
//   { type: 'tcp',       name: '192.168.1.100',        address: '192.168.1.100' },
// ]

Printer profiles

import { PRINTER_PROFILES, ThermalPrinter } from 'siga-printer';

const printer = await ThermalPrinter.connect(config, {
  profile: PRINTER_PROFILES.EPSON_TM_T20X, // default
});

// Available: EPSON_TM_T20, EPSON_TM_T20II, EPSON_TM_T20X
//            EPSON_TM_T88, EPSON_TM_T82
//            GENERIC_58MM, GENERIC_80MM

Boleto template

import { ThermalPrinter, BoletoTemplate } from 'siga-printer';

const printer = await ThermalPrinter.connect({ type: 'usb' });

await BoletoTemplate.print(printer, {
  bank: { name: 'Example Bank', cnpj: '00.000.000/0001-00' },
  beneficiary: { name: 'Company XYZ', cnpj: '11.111.111/0001-11' },
  payer: { name: 'John Doe', document: '123.456.789-00', address: '...' },
  boleto: {
    nossoNumero:    '00012345',
    dueDate:        '08/10/2025',
    amount:         'R$ 1,250.00',
    barcode:        '34191090080000008214800082194207197960000125000',
    digitableLine:  '34191.09008 00000.082148 00082.194207 1 97960000125000',
  },
  instructions: ['Do not accept after due date.'],
});

License

MIT



Português

Novidades na v1.5.4

  • printerManager.getPrinter() — recupera o ConnectedThermalPrinter ativo de qualquer tela sem abrir uma segunda conexão
  • Fix Android 14PendingIntent agora usa intent explícito + RECEIVER_NOT_EXPORTED (exigido no API 34+)
  • Filtro de discovery USB — exibe apenas impressoras (classe USB 7 + vendor IDs conhecidos); carregadores e hubs não aparecem mais
  • Expo config plugin — configura automaticamente AndroidManifest, Info.plist e JitPack no android/build.gradle
  • TemplateDesigner — canvas drag-and-drop visual para criar templates de impressão e exportar código TypeScript tipado
  • PrintPreview / PrintPreviewBuilder — preview realista em papel térmico com modo paisagem
  • ConnectionManager — singleton global de estado de conexão entre telas com reconexão automática
  • Hook useConnectionManager + componente PrinterSelector

Conexões suportadas

| Tipo | Android | iOS | Observação | |--------------|---------|------|------------------------------------------| | USB (OTG) | ✅ | ❌ | Cabo OTG necessário | | Serial | ✅ | ❌ | Via adaptador USB-Serial (RS-232) | | Bluetooth | ✅ | ✅ | Classic SPP (Android) / MFi (iOS) | | TCP/IP | ✅ | ✅ | Porta 9100 (padrão Epson) |


Instalação

npm install siga-printer
# ou
yarn add siga-printer

Expo (bare workflow / EAS Build)

Managed workflow não é suportado — a lib tem módulos nativos. Use bare workflow (expo prebuild) ou Expo Dev Client.

Adicione o config plugin no app.json:

{
  "expo": {
    "plugins": ["siga-printer"]
  }
}

Depois rode:

expo prebuild
# ou (EAS)
eas build

O plugin adiciona automaticamente todas as permissões Android, entradas do Info.plist iOS e o repositório JitPack — sem configuração manual.

React Native puro

Adicione ao android/app/src/main/AndroidManifest.xml:

<!-- USB -->
<uses-feature android:name="android.hardware.usb.host" android:required="false" />

<!-- Bluetooth (Android 12+) -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

Adicione ao android/build.gradle:

allprojects {
  repositories {
    maven { url 'https://jitpack.io' }
  }
}

iOS

cd ios && pod install

Adicione ao Info.plist (para Bluetooth MFi):

<key>UISupportedExternalAccessoryProtocols</key>
<array>
  <string>com.epson.escpos</string>
</array>

Uso básico

import { ThermalPrinter } from 'siga-printer';

const printer = await ThermalPrinter.connect({
  type:      'usb',
  vendorId:  0x04b8,  // Epson
  productId: 0x0202,  // TM-T20X II
});

await printer
  .init()
  .text('Olá, mundo!', { bold: true, align: 'center', size: 2 })
  .divider()
  .qrCode('https://exemplo.com', { size: 6 })
  .cut()
  .print();

await printer.disconnect();

API Completa

ThermalPrinter.connect(config, options?)

Conecta a uma impressora e retorna um ConnectedThermalPrinter.

const printer = await ThermalPrinter.connect(
  {
    type:      'usb',
    vendorId:  0x04b8,
    productId: 0x0202,
    timeout:   3000,
  },
  {
    profile:        PRINTER_PROFILES.EPSON_TM_T20X,
    charset:        'CP860',
    orientation:    'portrait',
    autoCut:        false,
    feedAfterPrint: 0,
  }
);

ThermalPrinter.create(options?)

Cria um builder sem conectar (para gerar bytes e enviar por fora).

const builder = ThermalPrinter.create({ charset: 'CP860' });
const bytes   = await builder.init().text('Teste').cut().build();
// bytes: Uint8Array

Métodos de conteúdo (encadeáveis)

.init()

Inicializa a impressora (ESC @). Sempre chame no início de cada job.

.text(content, options?)

printer.text('Texto normal')
printer.text('Negrito centralizado', { bold: true, align: 'center' })
printer.text('Grande', { size: 2 })
printer.text('Invertido', { invert: true })
printer.text('Sublinhado duplo', { underline: 'double' })

| Prop | Tipo | Padrão | |--------------------|---------------------------------|----------| | bold | boolean | false | | underline | boolean \| 'double' | false | | size | 1–8 | 1 | | widthMultiplier | 1–8 | 1 | | heightMultiplier | 1–8 | 1 | | align | 'left' \| 'center' \| 'right' | 'left' | | invert | boolean | false | | charset | CharsetEncoding | herdado |

.feed(lines?) / .feedDots(dots)

Avança papel por linhas ou pontos.

.divider(options?)

printer.divider()                    // ─────────────────────
printer.divider({ style: 'double' }) // =====================
printer.divider({ style: 'dashed' }) // ---------------------
printer.divider({ style: 'dotted' }) // .....................
printer.divider({ char: '*' })       // *********************

.section(title, options?)

Cabeçalho de seção com divisores automáticos:

─────────────────────────────────────────
         DADOS DO CLIENTE
─────────────────────────────────────────

.row(cells)

Layout de colunas. Widths devem somar 100:

printer.row([
  { text: 'Produto',   width: 60, align: 'left'  },
  { text: 'Qtd',       width: 15, align: 'center' },
  { text: 'R$ 99,90', width: 25, align: 'right'  },
])

.barcode(data, options?)

printer.barcode('34191090080000008214800082194207197960000125000', {
  type:        'ITF',
  height:      60,
  width:       2,
  hriPosition: 'below',
  align:       'center',
})

Tipos suportados: CODE39, CODE93, CODE128, EAN8, EAN13, ITF / ITF25, CODABAR, UPC_A, UPC_E

.qrCode(data, options?)

printer.qrCode('https://exemplo.com.br', {
  size:       5,    // tamanho do módulo 1–16
  errorLevel: 'M',  // L | M | Q | H
  align:      'center',
})

.image(options)

printer.image({ source: require('./assets/logo.png'), width: 200, align: 'center' })
printer.image({ source: 'data:image/png;base64,...', dither: 'floyd-steinberg' })

| Modo de dithering | Uso ideal | Velocidade | |-------------------|--------------------------|------------| | threshold | Logotipos monocromáticos | Rápido | | floyd-steinberg | Fotos e gradientes | Médio | | atkinson | Logotipos com meias-tintas| Médio | | bayer | Balanceado | Médio |

.cut(mode?, options?)

printer.cut()                     // corte completo + 3 linhas de feed
printer.cut('partial')            // corte parcial
printer.cut('full', { feed: 5 }) // feed customizado

.cashDrawer(options?)

printer.cashDrawer()
printer.cashDrawer({ pin: 5, duration: 200 })

.raw(bytes)

Injeta bytes ESC/POS arbitrários para comandos não cobertos pela API.


Orientação de impressão

// Retrato (padrão)
const printer = await ThermalPrinter.connect(config, { orientation: 'portrait' });

// Paisagem — TODO conteúdo rotacionado 90° (texto, código de barras, QR, divisores, linhas)
const printer = await ThermalPrinter.connect(config, { orientation: 'landscape' });

// Trocar durante o job
printer.setOrientation('landscape');

// Ou via printerManager.getPrinter()
const printer = printerManager.getPrinter({ orientation: 'landscape' });

Como funciona — dois caminhos, escolhidos automaticamente pelo perfil:

| Impressora | Método | Qualidade | Dados enviados | |---|---|---|---| | Epson TM series | ESC/POS Page Mode (ESC L / ESC T 1 / FF) | Fonte nativa 203 DPI + barcode | ~500 bytes | | Genérica (sem Page Mode) | Android Canvas bitmap → rotaciona 90° → GS v 0 | Rasterizado | 50–400 KB |

Para a Epson TM-T20X II (perfil padrão), o firmware da impressora rotaciona tudo nativamente — sem geração de bitmap, sem processamento extra, mesma velocidade do modo retrato.

Os perfis de impressora controlam qual caminho é usado via supportsPageMode. Todos os perfis EPSON_TM_* têm supportsPageMode: true. GENERIC_58MM / GENERIC_80MM usam false por padrão (fallback bitmap). Crie um perfil customizado se sua impressora genérica suportar page mode.


Gerenciamento de conexão

printerManager é um singleton global que mantém o estado da conexão entre componentes. Conecte uma vez, imprima de qualquer tela.

import { printerManager, useConnectionManager, PrinterSelector } from 'siga-printer';

// ── Imperativo (fora de componentes) ──────────────────────────

await printerManager.connectDevice(device); // device vem do discoverAll()
await printerManager.connect({ type: 'usb' });
await printerManager.disconnect();
await printerManager.reconnect();

const unsub = printerManager.subscribe(state => {
  console.log(state.status);          // 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'error' | 'disconnected'
  console.log(state.connectedDevice); // DiscoveredDevice | null
});
unsub();

printerManager.configure({
  autoReconnect:        true,
  maxReconnectAttempts: 3,
  reconnectDelay:       1000, // ms, dobra a cada tentativa
});
// ── Hook reativo (dentro de componentes) ──────────────────────

import { useConnectionManager } from 'siga-printer';

function MinhaScreen() {
  const {
    status,
    connectedDevice,
    isConnected,
    error,
    connectDevice,
    connect,
    disconnect,
    reconnect,
  } = useConnectionManager();

  return <Text>{isConnected ? connectedDevice?.name : status}</Text>;
}
// ── Componente pronto (UI completa) ───────────────────────────

import { PrinterSelector } from 'siga-printer';

function TelaConexao() {
  return (
    <PrinterSelector
      onConnect={(device) => console.log('Conectado a', device.name)}
      onError={(msg) => console.error(msg)}
      scanType="all"    // 'all' | 'usb' | 'bluetooth' | 'tcp'
      showStatus={true}
    />
  );
}

PrinterSelector inclui: busca por tipo, lista de dispositivos com ícones, botão conectar/desconectar por item, banner de status em tempo real.

// ── Imprimir de qualquer tela após conectar ───────────────────

import { printerManager, BoletoTemplate } from 'siga-printer';

async function handleImprimir() {
  // Reutiliza a conexão feita pelo PrinterSelector — sem segundo connect()
  const printer = printerManager.getPrinter();
  if (!printer) {
    console.error('Não conectado');
    return;
  }
  await BoletoTemplate.print(printer, { ... });
}

getPrinter() retorna um ConnectedThermalPrinter usando a conexão já aberta. Retorna null se não conectado. Use useConnectionManager() reativamente para guardar a UI, e printerManager.getPrinter() imperativamente na hora de imprimir.


Preview de impressão

Renderize um preview realista em papel térmico — sem impressora. Útil para testar templates, construir editores e mostrar preview ao usuário antes de imprimir.

import { PrintPreviewBuilder, PrintPreview } from 'siga-printer';

function MeuPreview() {
  const builder = new PrintPreviewBuilder({
    profile:     PRINTER_PROFILES.EPSON_TM_T20X,
    orientation: 'portrait',  // ou 'landscape'
  });

  builder
    .init()
    .text('CUPOM', { bold: true, align: 'center', size: 2 })
    .divider()
    .row([
      { text: 'Café',    width: 60 },
      { text: 'R$ 5,00', width: 40, align: 'right' },
    ])
    .divider()
    .qrCode('https://exemplo.com', { size: 4, align: 'center' })
    .cut();

  return (
    <PrintPreview
      builder={builder}
      showRuler={true}
      showMetrics={true}
      showCutMark={true}
    />
  );
}

Props do PrintPreview:

| Prop | Tipo | Padrão | Descrição | |--------------------|-----------|---------|----------------------------------------| | builder | PrintPreviewBuilder | obrigatório | Builder com conteúdo | | showRuler | boolean | true | Régua em mm no topo do papel | | showMetrics | boolean | true | Barra de métricas (comprimento, overflow)| | showCutMark | boolean | true | Linha de corte com tesoura | | highlightOverflow| boolean | true | Destaca elementos em overflow em vermelho| | scale | number | auto | Fator de escala da largura do papel | | style | ViewStyle| — | Override de estilo do ScrollView |

Preview paisagem: com orientation: 'landscape', o preview exibe uma tira de papel vertical com o conteúdo girado 90° — igual ao papel físico saindo da impressora.


Designer de templates

Canvas drag-and-drop visual para criar templates de impressão. Gera código TypeScript tipado pronto para uso com PrintPreviewBuilder.

import { TemplateDesigner } from 'siga-printer';

function TelaDesigner() {
  return (
    <TemplateDesigner
      onExport={(code, template) => {
        console.log('Código TypeScript gerado:', code);
        // cole no seu projeto
      }}
      onSave={(template) => {
        // salve o JSON do template no seu backend ou AsyncStorage
        salvarTemplate(template);
      }}
    />
  );
}

Props do TemplateDesigner:

| Prop | Tipo | Descrição | |--------------------|--------------------------------------------------------|-----------------------------------| | initialTemplate | Partial<TemplateDefinition> | Template pré-carregado | | onExport | (code: string, template: TemplateDefinition) => void | Chamado ao tocar em Exportar | | onSave | (template: TemplateDefinition) => void | Chamado ao tocar em Salvar |

Funcionalidades:

  • Blocos arrastáveis em canvas de papel em escala real
  • Snap em grade de 2mm
  • Tipos de bloco: texto, código de barras, QR code, imagem, divisor, linha de colunas
  • Painel de propriedades por bloco (alinhamento, fonte, tamanho, etc.)
  • Painel de variáveis — defina placeholders dinâmicos ({{nome_cliente}}, etc.)
  • Seletor de perfil de impressora e orientação
  • Exportação para snippet TypeScript
  • Celular retrato: layout em abas (Paleta / Canvas / Propriedades)
  • Tablet / paisagem: layout em três colunas

Descoberta de dispositivos

import { PrinterDiscovery } from 'siga-printer';

const discovery = new PrinterDiscovery();

const todos      = await discovery.discoverAll();
const epsonUsb   = await discovery.discoverEpsonUSB();
const rede       = await discovery.discoverNetwork();
const bluetooth  = await discovery.discoverBluetooth();

// [
//   { type: 'usb',       name: '/dev/bus/usb/001/002', vendorId: 0x04b8, productId: 0x0202 },
//   { type: 'bluetooth', name: 'TM-T20X',              address: 'AA:BB:CC:DD:EE:FF' },
//   { type: 'tcp',       name: '192.168.1.100',        address: '192.168.1.100' },
// ]

Perfis de impressora

import { PRINTER_PROFILES, ThermalPrinter } from 'siga-printer';

const printer = await ThermalPrinter.connect(config, {
  profile: PRINTER_PROFILES.EPSON_TM_T20X, // padrão
});

// Disponíveis: EPSON_TM_T20, EPSON_TM_T20II, EPSON_TM_T20X
//              EPSON_TM_T88, EPSON_TM_T82
//              GENERIC_58MM, GENERIC_80MM

Template de boleto

import { ThermalPrinter, BoletoTemplate } from 'siga-printer';

const printer = await ThermalPrinter.connect({ type: 'usb' });

await BoletoTemplate.print(printer, {
  bank: { name: 'Banco Exemplo S.A.', cnpj: '00.000.000/0001-00' },
  beneficiary: { name: 'Empresa XYZ LTDA', cnpj: '11.111.111/0001-11' },
  payer: { name: 'João da Silva', document: '123.456.789-00', address: 'Rua Exemplo, 100' },
  boleto: {
    nossoNumero:   '00012345',
    dueDate:       '10/08/2025',
    amount:        'R$ 1.250,00',
    barcode:       '34191090080000008214800082194207197960000125000',
    digitableLine: '34191.09008 00000.082148 00082.194207 1 97960000125000',
  },
  instructions: ['Não receber após o vencimento.'],
});

Licença

MIT