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

@rossshannon/deep-diffs

v1.0.0

Published

Visualise cumulative changes across multiple text revisions with intensity-based highlighting

Downloads

8

Readme

Deep Diffs Header

Deep Diffs

Visualise cumulative changes across multiple text revisions. Regions, words or characters that have been edited multiple times are highlighted with increasing intensity.

Deep diff example

Unlike normal diff tools that compare two versions, deep-diffs can visualise where changes have been made in a document throughout multiple revisions over time — giving you a “heatmap” of editorial activity, particularly where details are being worked onor revised repeatedly.

Use Cases

  • Legal document review — see which clauses have been contentious across redlining rounds
  • Collaborative writing — identify paragraphs that have already been finessed
  • Wikipedia-style editing — spot edit-war hotspots
  • Code review — find code blocks that have been tweaked repeatedly

Installation

npm install @rossshannon/deep-diffs

Quick Start

import { deepDiffHtml, getDefaultStyles } from '@rossshannon/deep-diffs';

// A contract clause being refined over multiple revisions
const revisions = [
  'The client shall pay the invoice.',
  'The client shall pay the invoice within 30 days.',
  'The client shall pay the invoice in full within 30 days.',
  'The client shall pay the invoice in full within 30 business days.',
];

const html = deepDiffHtml(revisions);
const css = getDefaultStyles();

Output:

The client shall pay the invoice<ins class="deep-diff"> <ins class="deep-diff">in full
</ins>within 30 <ins class="deep-diff">business </ins>days</ins>.

Notice the nested <ins> tags — “within 30 days” was added first, then “in full” was inserted inside that region, then “business” was added. The nesting depth indicates how many times a region has been edited. The default CSS styles these with increasing background intensity, creating a visual heatmap of editorial activity. After multiple rounds of editing, a richer picture of the editorial process emerges.

API

computeDeepDiff(revisions, options?)

Computes diff markers without rendering.

import { computeDeepDiff } from '@rossshannon/deep-diffs';

const { text, markers } = computeDeepDiff(revisions);

// text: the final revision text
// markers: array of { start, end, enabled } marker objects

Options:

  • skipEmpty (boolean, default true) — skip empty revisions (useful for filtering vandalism)
  • timeout (number, default 1) — diff computation timeout in seconds

renderWithMarkers(text, markers, options?)

Renders text with markers as HTML.

import { computeDeepDiff, renderWithMarkers } from '@rossshannon/deep-diffs';

const { text, markers } = computeDeepDiff(revisions);
const html = renderWithMarkers(text, markers, {
  tagName: 'mark',
  className: 'changed'
});

Options:

  • tagName (string, default 'ins') — HTML tag for markers
  • className (string, default 'deep-diff') — CSS class for tags

deepDiffHtml(revisions, options?)

Convenience function combining computeDeepDiff and renderWithMarkers.

import { deepDiffHtml } from '@rossshannon/deep-diffs';

const html = deepDiffHtml(revisions, { skipEmpty: true });

getDefaultStyles(maxDepth?)

Generates CSS for nested marker intensity.

import { getDefaultStyles } from '@rossshannon/deep-diffs';

const css = getDefaultStyles(5);
// Returns CSS with increasingly intense backgrounds for nested .deep-diff elements

How It Works

  1. Chain diffs — compute diffs between each consecutive revision pair
  2. Track markers — maintain a list of changed regions with [start, end] positions
  3. Transform markers — as new edits occur, existing markers shift, expand, or contract:
    • Insert before marker → shift right
    • Insert within marker → expand
    • Delete before marker → shift left
    • Delete within marker → contract (disable if fully subsumed)
  4. Accumulate — new insertions add new markers; nesting depth = change frequency
  5. Render — interleave tags at marker boundaries

This is essentially a simplified form of operational transformation — the same conceptual framework that powers real-time collaboration in Google Docs.

Browser Usage

<script type="module">
  import { deepDiffHtml, getDefaultStyles } from 'https://unpkg.com/@rossshannon/deep-diffs';

  const style = document.createElement('style');
  style.textContent = getDefaultStyles();
  document.head.appendChild(style);

  document.getElementById('output').innerHTML = deepDiffHtml(myRevisions);
</script>

TypeScript

Full TypeScript support with bundled type definitions:

import {
  deepDiffHtml,
  computeDeepDiff,
  type Marker,
  type DeepDiffResult
} from '@rossshannon/deep-diffs';

const result: DeepDiffResult = computeDeepDiff(['v1', 'v2', 'v3']);
console.log(result.text);           // string
console.log(result.markers);        // Marker[]

const html: string = deepDiffHtml(['v1', 'v2'], {
  tagName: 'mark',
  className: 'highlight'
});

Limitations

  • Character-indexed markers — This techique is tuned for tracking how details change in text over time. Large structural refactors (e.g., replacing or moving paragraphs) will lose some of the necessary context.
  • No move detection — if text is cut and pasted elsewhere, it's treated as delete + insert, not a move.

Prior Art & Inspiration

History

Original algorithm by Ross Shannon (2010). Modernised and published 2026.

License

MIT