Skip to content

UN-EOSG-Analytics/undifferent

Repository files navigation

undifferent

A diff algorithm and viewer for comparing documents, with special support for UN resolutions.

Installation

npm install github:un-eosg-analytics/undifferent

Usage

Core Diff Algorithm

The core module provides a pure TypeScript diff algorithm with no React dependency:

import { diff, similarity, highlight } from 'undifferent/core'

// Compare two arrays of lines
const result = diff(linesA, linesB, { threshold: 0.8 })

console.log(result.score)  // Overall similarity (0-1)
console.log(result.items)  // Array of diff items with highlighting

// Calculate similarity between two strings
const score = similarity('hello world', 'hello there')

// Get highlighted diff markup
const { left, right } = highlight('old text', 'new text')
// left: "~~old~~ text"
// right: "**new** text"

React Components

The react module provides components for displaying diffs:

import { DiffViewer, Comparison, DiffItem } from 'undifferent/react'
import type { DiffResult } from 'undifferent/core'
import type { UNDocumentMetadata } from 'undifferent/un-fetcher'

// Full viewer with document headers (symbol, date, PDF link, vote)
<DiffViewer 
  data={diffResult}
  left={{
    symbol: 'A/RES/77/16',
    metadata: leftMetadata,  // UNDocumentMetadata from fetchDocumentMetadata
    format: 'doc',           // 'doc' | 'pdf'
  }}
  right={{
    symbol: 'A/RES/79/326',
    metadata: rightMetadata,
    format: 'doc',
  }}
/>

// Or build your own UI with individual components
{diffResult.items.map((item, i) => (
  <Comparison key={i} item={item} />
))}

UN Document Fetching

The un-fetcher module provides utilities for fetching UN documents (server-side only):

import { fetchUNDocument, fetchDocumentMetadata } from 'undifferent/un-fetcher'

// Fetch a UN document by symbol
const doc = await fetchUNDocument('A/RES/77/16')
console.log(doc.lines)    // Array of text lines
console.log(doc.format)   // 'doc' or 'pdf'

// Fetch document metadata from UN Digital Library
const meta = await fetchDocumentMetadata('A/HRC/RES/50/13')
console.log(meta.title)     // "Access to medicines, vaccines..."
console.log(meta.date)      // "2022-07-14"
console.log(meta.year)      // 2022
console.log(meta.subjects)  // ["RIGHT TO HEALTH", "VACCINES", ...]
console.log(meta.vote)      // { inFavour: 29, against: 15, abstaining: 3 }

Styling

The React components use CSS variables for theming:

:root {
  --diff-item-bg: #ffffff;
  --diff-added-bg: #bbf7d0;
  --diff-removed-bg: #fef2f2;
  --diff-moved-bg: #fefce8;
  --diff-aligned-bg: #eff6ff;
}

API Reference

Core

  • diff(linesA, linesB, options?) - Compute structured diff
  • similarity(a, b) - Calculate Levenshtein similarity ratio
  • highlight(a, b) - Generate diff markup

React

  • <DiffViewer> - Full diff viewer with document headers and diff items
  • <DocumentHeader> - Document metadata display (symbol, date, PDF link, vote)
  • <Comparison> - Single diff row (left + right)
  • <DiffItem> - Single side content
  • parseHighlightedText(text) - Parse diff markup to React elements

UN Fetcher

  • fetchUNDocument(symbol) - Fetch UN document content by symbol from ODS
  • fetchDocumentMetadata(symbol) - Fetch metadata (title, date, year) from UN Digital Library

Project Structure

undifferent/
├── src/                    # LIBRARY CODE - edit here for features
│   ├── core/               # Pure TypeScript diff algorithm (no React)
│   │   ├── diff.ts         # Main diff algorithm
│   │   ├── similarity.ts   # Levenshtein similarity
│   │   └── highlight.ts    # Diff markup generation
│   ├── react/              # React components for displaying diffs
│   │   ├── DiffViewer.tsx  # Main viewer component
│   │   ├── DocumentHeader.tsx # Document metadata display
│   │   ├── Comparison.tsx  # Single diff row (left + right)
│   │   └── DiffItem.tsx    # Single side content
│   └── un-fetcher/         # UN document fetching (server-side)
│       ├── fetcher.ts      # Fetch documents from ODS
│       └── parser.ts       # Parse UN symbols
├── app/                    # DEMO APP ONLY - just consumes the library
│   ├── page.tsx            # Demo page with example comparisons
│   └── api/diff/route.ts   # API endpoint using un-fetcher
└── dist/                   # Built library output

IMPORTANT: The app/ directory is only a demo. All features, UI components, and logic should be implemented in src/ (the library). The app should only:

  • Provide example comparisons
  • Call the API and pass data to library components
  • Handle routing/navigation

License

MIT

About

Diff viewer for UN resolutions

Resources

Stars

Watchers

Forks