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

@hx-cd/screenshot

v0.1.2

Published

A pure JavaScript browser screenshot library with zero dependencies

Readme

@hx-cd/screenshot

A pure JavaScript browser screenshot library with zero external dependencies. Uses SVG foreignObject for high-fidelity captures.

Features

  • Zero Dependencies - Pure vanilla JavaScript, no external libraries
  • High Fidelity - SVG foreignObject preserves CSS/layout precision
  • Multiple Capture Modes - Viewport, element, region, full-page screenshots
  • Interactive Selection - Drag-to-select with resizable selection box
  • Advanced Editor - 7 annotation tools (arrow, rect, circle, pen, text, blur, mosaic)
  • Undo/Redo - Full history support with Ctrl+Z / Ctrl+Y
  • Popover Toolbar - Context-aware toolbar below selection
  • Magnifier - 2x-10x zoom for pixel-level precision
  • Watermark - Text or image watermarks with positioning and tiling
  • Compression - Quality control and format conversion (PNG/JPEG/WebP)
  • Custom Actions - Extensible editor with custom buttons
  • High-DPI Support - Scale factor for retina displays

Installation

pnpm add @hx-cd/screenshot
# or
npm install @hx-cd/screenshot
# or
yarn add @hx-cd/screenshot

Quick Start

Basic Usage

import { Screenshot } from '@hx-cd/screenshot';

const screenshot = new Screenshot();

// Capture viewport
const result = await screenshot.captureViewport();
console.log(`Captured: ${result.width} x ${result.height}`);

// Download the result
screenshot.download(result.canvas, 'screenshot.png');

// Or get as blob
const blob = await result.toBlob();

// Or as data URL
const dataUrl = result.toDataURL();

Interactive Selection (框选编辑模式)

import { Screenshot } from '@hx-cd/screenshot';

const screenshot = new Screenshot();

// Capture with user selection - shows editor after selection
const result = await screenshot.captureSelection();

// User flow:
// 1. Drag to select region → Screenshot captured immediately
// 2. Adjust selection using 8 drag handles (corners + edges)
// 3. Move selection by dragging the selection box
// 4. Select annotation tools from popover toolbar
// 5. Draw on the canvas (arrow, rect, circle, pen, text, blur, mosaic)
// 6. Click Confirm or press Enter to complete
// 7. Click Cancel or press ESC to abort

API Reference

Screenshot Class

const screenshot = new Screenshot(options?: ScreenshotOptions);

Options

interface ScreenshotOptions {
  scale?: number;              // Scale factor for high-DPI (default: 1)
  backgroundColor?: string;    // Background color (default: '#ffffff')
  useCORS?: boolean;           // Use CORS for images (default: true)
  includeShadow?: boolean;     // Include shadow DOM (default: true)
  format?: 'image/png' | 'image/jpeg' | 'image/webp'; // Output format
  quality?: number;            // Image quality 0-1 (default: 0.95)
  maxWidth?: number;           // Maximum output width
  maxHeight?: number;          // Maximum output height
  customActions?: CustomAction[]; // Custom actions for editor
  captureRoot?: Element;       // Root element to limit capture scope
}

Capture Methods

// Capture viewport (visible area)
const result = await screenshot.captureViewport();

// Capture specific element (by selector or element reference)
const result = await screenshot.captureElement('.selector');
const result = await screenshot.captureElement(document.getElementById('my-element'));

// Capture specific region (coordinates relative to viewport)
const result = await screenshot.captureRegion(x, y, width, height);

// Capture full page (including scrollable area)
const result = await screenshot.captureFullPage(useTiling?: boolean);

// Capture with user selection (opens interactive editor)
const result = await screenshot.captureSelection(captureRoot?: Element);

Image Processing

// Add watermark
const watermarked = await screenshot.addWatermark(canvas, {
  text: '© 2025 My Company',
  position: 'bottom-right',
  opacity: 0.5,
  fontSize: 24,
  color: '#ffffff',
});

// Apply annotations programmatically
const annotated = screenshot.applyAnnotations(canvas, [
  {
    type: 'rect',
    x: 50,
    y: 50,
    width: 100,
    height: 60,
    color: '#ff0000',
    lineWidth: 3,
  },
]);

// Create interactive annotation editor
const editor = screenshot.createAnnotationEditor(canvas);

// Compress canvas
const blob = await screenshot.compress(canvas, { quality: 0.8, format: 'image/jpeg' });

// Compress image blob
const compressedBlob = await screenshot.compressImage(originalBlob, { quality: 0.7 });

Utility Methods

// Download canvas
screenshot.download(canvas, 'filename.png');

// Download blob
screenshot.downloadBlob(blob, 'filename.png');

// Copy to clipboard
await screenshot.copyToClipboard(canvas);

// Convert canvas to blob
const blob = await screenshot.toBlob(canvas);

// Convert canvas to data URL
const dataUrl = screenshot.toDataURL(canvas);

// Update options
screenshot.setOptions({ quality: 0.8 });

// Get current options (with defaults)
const options = screenshot.getOptions();

CaptureResult Interface

All capture methods return a CaptureResult:

interface CaptureResult {
  canvas: HTMLCanvasElement;
  width: number;
  height: number;
  toBlob: (type?: string, quality?: number) => Promise<Blob>;
  toDataURL: (type?: string, quality?: number) => string;
}

Annotation Tools

| Tool | Type | Description | |------|------|-------------| | Arrow | arrow | Draw arrows with configurable color/width | | Rectangle | rect | Draw rectangles | | Circle | circle | Draw circles/ellipses | | Pen | pen | Freehand drawing | | Text | text | Add text labels with font size control | | Mosaic | mosaic | Pixelate region | | Blur | blur | Gaussian blur region |

Keyboard Shortcuts

| Key | Action | |-----|--------| | Enter | Confirm selection | | Escape | Cancel selection | | Ctrl+Z | Undo | | Ctrl+Y or Ctrl+Shift+Z | Redo |

Custom Actions

Extend the editor with custom buttons:

const screenshot = new Screenshot({
  customActions: [
    {
      label: 'Export',
      icon: '📥',
      title: 'Export as PNG',
      onClick: async (canvas, ctx) => {
        const blob = await ctx.getBlob();
        // Your custom logic here
      },
      enabled: () => true, // Optional: controls button disabled state
    },
  ],
});

Architecture

Core Rendering Pipeline

1. Clone DOM tree (deep clone)
       ↓
2. Inline styles (200+ CSS properties via getComputedStyle)
       ↓
3. Inline resources (images, backgrounds, canvases)
       ↓
4. Serialize to SVG foreignObject
       ↓
5. Render to Canvas via Image + drawImage
       ↓
6. Export (Blob/DataURL/download)

Key Modules

| Module | Purpose | |--------|---------| | svgRenderer.ts | Core buildSvgDataUrl() and renderSvgToCanvas() | | utils/style.ts | Recursive style inlining with scroll compensation | | utils/dom.ts | DOM cloning and render utilities | | utils/resource.ts | External resource inlining (CORS support) | | features/annotation.ts | 7 annotation tools | | features/editor/ | Selection, toolbar, magnifier, editor | | core/history/ | Undo/redo with Command + Memento patterns |

Browser Compatibility

| Browser | Minimum Version | Support | |---------|-----------------|---------| | Chrome | 70+ | ✅ Full | | Firefox | 65+ | ✅ Full | | Safari | 13+ | ✅ Full | | Edge | 79+ | ✅ Full | | Chrome (Android) | 70+ | ✅ Full | | Safari (iOS) | 13+ | ✅ Full |

Not supported:

  • IE 11 and below (no foreignObject support)
  • Old Android WebView (<70)

Known Limitations

  1. Cross-origin images - Require CORS headers or will fail to inline
  2. WebGL content - Not captured (canvas appears blank)
  3. Video content - Only poster image is captured
  4. iframes - Content inside iframes is not captured
  5. CSS filters - Limited support for some complex filters
  6. Box shadows - May be clipped in some cases

Scroll Handling

The library correctly handles scroll offsets in three scenarios:

| Case | Detection | Handling | |------|-----------|----------| | Table-only container | overflow: auto + single <table> child | Apply transform to <tbody> only | | Standard container | overflow: auto + multiple/non-table children | Apply transform to direct children | | Non-scroll container | overflow: visible or no overflow | No transform applied |

Table Header Fix

Problem: Browser natively fixes <thead> at container top. Applying transform to <table> breaks this.

Solution: Apply scroll compensation only to <tbody> elements:

<div style="overflow: auto;" data-scroll-x="50" data-scroll-y="100">
  <table>                          ← No transform (table header stays fixed)
    <thead>...</thead>             ← Browser native fixed positioning
    <tbody>...</tbody>             ← transform: translate(-50px, -100px)
  </table>
</div>

Performance Tips

  1. Use appropriate scale - Don't use high scale for large captures
  2. Inline images - Large images increase memory usage
  3. Tile large captures - Use captureFullPage(true) for very long pages
  4. Clean up - Call destroy() for selection tools when done

Package Structure

src/
├── index.ts                      # Main entry
├── Screenshot.ts                 # Screenshot class
├── types.ts                      # TypeScript types
│
├── capture/                      # Capture mechanisms
│   ├── svgRenderer.ts            # SVG → Canvas core
│   ├── baseCapture.ts            # Unified capture pipeline
│   ├── element.ts                # Element capture
│   ├── viewport.ts               # Viewport capture
│   ├── fullPage.ts               # Full-page/tiled capture
│   └── region.ts                 # Region capture/crop
│
├── features/                     # Feature modules
│   ├── annotation.ts             # 7 annotation tools
│   ├── watermark.ts              # Watermark functionality
│   ├── compress.ts               # Image compression
│   └── editor/
│       ├── AdvancedSelection.ts  # Selection tool with editor
│       ├── Editor.ts             # Standalone image editor
│       ├── Toolbar.ts            # Unified toolbar
│       ├── SelectionCanvas.ts    # Canvas display manager
│       ├── SelectionResizer.ts   # 8-handle resizer
│       ├── EditorRenderer.ts     # Canvas rendering
│       ├── EditorStateManager.ts # State management
│       └── magnifier.ts          # Magnifier tool
│
├── utils/                        # Utilities
│   ├── style.ts                  # Style inlining (200+ props)
│   ├── dom.ts                    # DOM utilities
│   ├── resource.ts               # Resource inlining
│   ├── canvas.ts                 # Canvas utilities
│   └── download.ts               # Download/clipboard
│
├── core/history/                 # Undo/redo
│   ├── HistoryManager.ts         # Command + Memento pattern
│   └── commands.ts               # Command implementations
│
└── styles/                       # CSS styles
    ├── selection.css
    └── editor.css

License

MIT