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

picoprint

v1.0.6

Published

A tiny, fast, and feature-rich pretty printer for the terminal with zero dependencies

Readme

picoprint

A tiny, fast, and feature-rich pretty printer for the terminal with zero dependencies.

Installation

bun add picoprint
# or
pnpm add picoprint
# or
npm install picoprint

Quick Start

import p from 'picoprint';

// Pretty print any value
p({ name: 'Alice', age: 30, hobbies: ['reading', 'coding'] });

// Use colors
p.green.log('Success!');
p.bgRed.white.log('ERROR');

// Quick logging (indent-aware)
p.log('server', 'listening on', 3000);

// Control global indentation
p.indent();
p.log('within a block'); // printed with 2-space indent
p.indent(3);
p.log('deeper');         // printed with 5-space indent
p.dedent(); // pops one prior indent level
p.log('back to 2');
p.dedent();
p.log('back to 0');

// Draw boxes
p.box('Hello World', {
  style: 'rounded',
  color: p.yellow,
  padding: 1
});

Features

  • 🎨 Rich formatting - Colors, backgrounds, styles, gradients
  • 📦 Box drawing - Multiple border styles with padding and titles
  • 📊 Tables - Auto-formatted tables from arrays and objects
  • 🌳 Trees - Hierarchical tree structures with search
  • 🔍 Diffs - Visual object and text diffs
  • 💻 Code highlighting - Syntax highlighting with optional bat integration
  • 📅 Calendars - ASCII calendars with event markers
  • 🔄 Streaming - Streaming output for progressive rendering
  • 📝 Logging - p.log and chainable .log on colors (e.g., p.yellow.log())

🎨 Colors & Styling

// Basic colors
p.red('Error'), p.green('Success'), p.yellow('Warning')
p.bgBlue.white('Highlighted')

// Modifiers
p.bold('Bold'), p.dim('Dimmed'), p.italic('Italic')
p.underline('Underlined'), p.strikethrough('Strikethrough')

// 256 colors
p.color256(196)('Red from 256 palette')
p.bgColor256(226)('Yellow background')

// RGB & Hex
p.rgb(255, 128, 0)('Orange')
p.hex('#FF5733')('Coral')
p.bgRgb(0, 0, 255)('Blue background')
p.bgHex('#2E86AB')('Ocean background')

// Gradients
p.gradient('Smooth gradient text', p.red, p.blue)
p.gradientRgb('RGB gradient', {r:255,g:0,b:0}, {r:0,g:0,b:255})
p.gradientHex('Hex gradient', '#FF0000', '#0000FF')

// Rainbow & color palettes
p.rainbow('Rainbow text 🌈')
p.palette('#FF0000', 7) // Generate 7 shades

// Logging with styles
p.yellow.log('warn:', 'disk', 95, '%')
p.bold.yellow.log('ready')
// plain log
p.log('status', 200)

📦 Boxes

// Basic box
p.box('Content here');

// Styled boxes
p.box('Double border', { style: 'double', color: p.green });
p.box('Rounded box', { style: 'rounded', title: 'Info', color: p.cyan });

// Box styles: single, double, rounded, thick, ascii

// Box variants
p.box.frame('No padding frame', { color: p.red });
p.box.panel('Title', 'Panel content with rounded corners');

// Capture console output
p.box(() => {
  console.log('This output is captured');
  console.log(p.green('With colors!'));
}, { title: 'Captured', style: 'rounded' });

// Box returns callback's return value
const result = p.box(() => {
  console.log('Computing...');
  return 42;
}, { title: 'Process' });
// result === 42

// Background colors
p.box('Blue background', {
  background: p.bgBlue,
  padding: 1,
  color: p.white
});

➖ Lines

// Simple line
p.line();

// Line with label
p.line('Section Title');

// Line styles
p.line({ style: 'double' });
p.line({ style: 'thick' });
p.line({ style: 'dashed' });

// Shortcuts
p.line.double('Double Line');
p.line.thick('Thick Line');
p.line.dashed('Dashed');
p.line.section('Section');

// Gradient line
p.line.gradient({ start: p.magenta, end: p.yellow });

// Custom alignment
p.line({ label: 'Left', align: 'left' });
p.line({ label: 'Right', align: 'right' });

📊 Tables

// Array of objects
p.table([
  { name: 'Alice', age: 30, city: 'New York' },
  { name: 'Bob', age: 25, city: 'London' }
]);

// Object as key-value pairs
p.table({ host: 'localhost', port: 3000, secure: true });

// Map
p.table(new Map([['key1', 'value1'], ['key2', 'value2']]));

// Table options
p.table(data, {
  style: 'double',      // single, double, rounded, thick, ascii
  showIndex: true,      // Show row numbers
  columns: ['name', 'age'], // Select columns
  align: { price: 'right', name: 'left' },
  compact: true,
  maxWidth: 20
});

// Compare two objects side-by-side
p.table.compare(
  { port: 3000, host: 'localhost' },
  { port: 8080, host: '0.0.0.0' }
);

🌳 Trees

// Basic tree
const tree = {
  name: 'root',
  children: [
    { name: 'branch1' },
    {
      name: 'branch2',
      children: [
        { name: 'leaf1' },
        { name: 'leaf2' }
      ]
    }
  ]
};
p.tree(tree);

// Tree styles: unicode, ascii, rounded, double, bold

// Tree with values & metadata
p.tree(tree, {
  showValues: true,
  showMetadata: true,
  maxDepth: 3,
  style: 'rounded'
});

// Convert object to tree
p.tree.fromObject({
  user: { name: 'Alice', settings: { theme: 'dark' } }
}, 'Config');

// Search in tree
p.tree.search(tree, 'leaf');

// Tree statistics
p.tree.stats(tree);

// Multiple trees
p.tree.multi([tree1, tree2, tree3]);

// Directory structure
const dir = {
  name: 'src',
  type: 'directory',
  children: [
    { name: 'index.ts', type: 'file', size: 2048 },
    {
      name: 'components',
      type: 'directory',
      children: [
        { name: 'Button.tsx', type: 'file', size: 1024 }
      ]
    }
  ]
};
p.tree.directory(dir, {
  fileIcons: true,
  showSizes: true,
  sortBy: 'type' // or 'name', 'size'
});

💻 Code Highlighting

// Basic syntax highlighting (uses bat if available)
p.code('const x = 42;', 'javascript');

// Code with options
p.code(sourceCode, {
  language: 'typescript',
  lineNumbers: true,
  window: 'rounded',      // Window border style
  title: 'Example',
  titleAlign: 'center',
  padding: 1,
  background: p.bgBlue,
  borderColor: p.yellow,
  titleColor: p.cyan
});

// Configure bat integration
p.configure({
  code: {
    useBat: true,
    batTheme: 'TwoDark'
  }
});

🔍 Diffs

// Object diff
p.diff(
  { name: 'Alice', age: 30 },
  { name: 'Alice', age: 31, city: 'NYC' }
);

// Options
p.diff(obj1, obj2, {
  showUnchanged: true,
  compact: false,
  maxDepth: 3
});

// Word diff
p.diffWords('Hello world', 'Hello beautiful world');
p.diffWords(text1, text2, {
  ignoreCase: true,
  ignoreWhitespace: true
});

// Side-by-side comparison
p.compare(leftData, rightData);
p.compare(data1, data2, { labels: ['Dev', 'Prod'] });

// Deep diff (returns diff nodes)
const changes = p.deepDiff(obj1, obj2);

📅 Calendar

// Current month
p.calendar();

// Specific date
p.calendar(new Date(2024, 2)); // March 2024

// With options
p.calendar(date, {
  showHeader: true,
  showFooter: true,
  showWeekNumbers: true,
  firstDayOfWeek: 0 // 0=Sunday, 1=Monday
});

// Calendar with events
p.calendarWithEvents(new Date(), [
  {
    date: new Date(2024, 2, 15),
    label: 'Meeting',
    color: p.red,
    priority: 'high'
  }
]);

🔄 Streaming

// Stream table data
const stream = p.stream.table();
stream.write({ id: 1, status: 'pending' });
stream.write({ id: 2, status: 'complete' });
stream.close();

// Stream tree nodes
const treeStream = p.stream.tree();
treeStream.add({ name: 'node1', path: [] });
treeStream.add({ name: 'child', path: ['node1'] });
treeStream.close();

// Stream boxes
const boxStream = p.stream.box({ title: 'Live Data' });
boxStream.writeln('Line 1');
boxStream.writeln('Line 2');
boxStream.close();

// Stream pretty printing
const ppStream = p.stream.pp();
ppStream.write({ data: 'value' });
ppStream.close();

🐛 Stack Traces

// Enhanced error display
try {
  someFunction();
} catch (err) {
  p.error(err);
}

// Current stack trace
p.trace();

// Call stack
p.callStack();

// Custom stack display
p.stack();

⚙️ Configuration

p.configure({
  // Pretty print options
  maxDepth: 5,
  maxItems: 100,
  compact: false,
  sorted: true,
  showHidden: false,
  colors: true,

  // Code highlighting
  code: {
    useBat: true,        // Use bat for syntax highlighting
    batTheme: 'TwoDark', // Bat theme
    batOptions: []       // Additional bat arguments
  }
});

// Get current config
const config = p.getConfig();

// Reset to defaults
p.resetConfig();

API

The default export p is both a function and an object:

  • p(value, options?) - Pretty print any value
  • p.{color}(text) - All color functions
  • p.log(...args) - Print any args (indent-aware) and return string
  • p.{color}.log(...args) - Chain colors then print args
  • p.box(content, options?) - Draw boxes
  • p.line(options?) - Draw lines
  • p.table(data, options?) - Display tables
  • p.tree(node, options?) - Display trees
  • p.code(code, options?) - Syntax highlighting
  • p.diff(a, b, options?) - Show differences
  • p.calendar(date?, options?) - Display calendar
  • p.stream.* - Streaming variants
  • p.indent(amount?) - Increase global indent (default 2)
  • p.dedent() - Decrease by one prior p.indent level
  • And more...

Note on indentation levels: each call to p.indent(amount?) pushes one level by the specified space amount (default 2). p.dedent() removes exactly one prior level per call; call it repeatedly to pop multiple levels.

Requirements

  • Node.js 22+
  • Optional: bat for enhanced syntax highlighting