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

@depart-os/text-diff

v0.2.0

Published

Inline-tag (1-round) text diff utilities for React (Korean/English) — used by depart-os proposal review flow.

Readme

@depart-os/text-diff

인라인 태그 기반 1라운드 텍스트 diff 유틸리티. 한국어·영어 텍스트의 변경 부분을 <del>/<ins> 태그로 단일 string 에 마킹하고, React 컴포넌트로 모드별 렌더한다.

Install

pnpm add @depart-os/text-diff
# or: npm install @depart-os/text-diff / yarn add @depart-os/text-diff

저장 포맷

원본:    "신호일 수 있어요"
수정후:  "신호<del>일 수 있어요</del><ins>입니다</ins>"
  • <del> : 원본에 있다가 삭제
  • <ins> : 새로 추가
  • 태그 밖 : 양쪽 공통

1라운드 전용. 같은 문서를 두 번 이상 누적 수정하는 워크플로우는 지원하지 않는다. 매 수정마다 항상 최초 원본 vs 새 수정본 으로 새 태그 string 을 만든다.

Usage

빌더 측 — 수정 전송 시 태그 string 생성

import { generateTagged } from '@depart-os/text-diff'

async function submitEdit(originalContent: string, newContent: string) {
  const tagged = generateTagged(originalContent, newContent)
  // tagged === "신호<del>일 수 있어요</del><ins>입니다</ins>"
  await api.put('/captions/123', { content: tagged })
}

받는 쪽 — 모드별 렌더

import { WordDiff } from '@depart-os/text-diff'

function ReviewPanel({ content }: { content: string }) {
  return (
    <>
      <section>
        <h3>변경사항</h3>
        <WordDiff content={content} mode="diff" />
      </section>
      <section>
        <h3>수정본</h3>
        <WordDiff content={content} mode="after" />
      </section>
      <section>
        <h3>원본</h3>
        <WordDiff content={content} mode="before" />
      </section>
    </>
  )
}

추가된 단어는 파란 톤(bg-blue-100), 삭제된 단어는 분홍 톤(bg-rose-50)에 취소선으로 표시된다.

양방향 협업 (편집 ↔ 컨펌)

A 측이 수정해서 B 측에 보내고, B 가 다시 수정해서 A 에 보내는 ping-pong 흐름:

import { extractAfter, generateTagged } from '@depart-os/text-diff'

// 받았을 때 — textarea 초기값은 직전 상대 시안의 plain
function startEditing(serverContent: string) {
  return extractAfter(serverContent)
}

// 보낼 때 — 새 tagged = 직전 상대 시안 vs 내 새 시안
async function submitMyEdit(serverContent: string, myDraft: string) {
  const baseline = extractAfter(serverContent)
  const newTagged = generateTagged(baseline, myDraft)
  await api.put('/...', { content: newTagged })
}

서버는 매번 새 tagged 로 덮어씀. DB 컬럼 1개로 무한 라운드 가능. extractAfter 가 plain 도 그대로 통과시키므로 기존 plain 데이터와도 자동 호환.

Plain text 호환

태그 없는 plain text 를 넘기면 모든 모드에서 그대로 렌더된다 (변경 표시 없음). 기존 데이터와 신규 데이터를 한 컬럼에 섞어 보관해도 안전하다.

Tailwind v4 설정

이 패키지는 Tailwind v3/v4 클래스 문자열을 그대로 출력한다. 소비측 프로젝트의 Tailwind 가 패키지 dist 도 스캔해야 색이 입혀진다.

Tailwind v4 (CSS-first)globals.css 에서:

@import 'tailwindcss';
@source '../../node_modules/@depart-os/text-diff/dist';

@source 의 상대 경로는 그 CSS 파일의 위치 기준으로 조정한다.

Tailwind v3tailwind.config.jscontent 에 추가:

export default {
  content: [
    './src/**/*.{ts,tsx}',
    './node_modules/@depart-os/text-diff/dist/**/*.{js,mjs,cjs}',
  ],
}

API

generateTagged(before: string, after: string): string

LCS 기반 워드 단위 diff 를 계산하고 <del>/<ins> 태그로 직렬화한 단일 string 을 반환. 한글/영문/공백/구두점을 각각의 토큰으로 분리.

extractBefore(content: string): string

tagged string 에서 "직전 baseline" plain text 를 추출. <ins> 블록은 제거하고 <del> 내용만 살림. plain text 입력은 no-op 으로 그대로 반환.

extractBefore('신호<del>일 수 있어요</del><ins>입니다</ins>')
// → '신호일 수 있어요'

extractAfter(content: string): string

tagged string 에서 "최신 시안" plain text 를 추출. <del> 블록은 제거하고 <ins> 내용만 살림. plain text 입력은 no-op 으로 그대로 반환.

extractAfter('신호<del>일 수 있어요</del><ins>입니다</ins>')
// → '신호입니다'

라운드트립 보장: extractBefore(generateTagged(a, b)) === a && extractAfter(generateTagged(a, b)) === b.

<WordDiff content mode className? />

태그 문자열을 파싱해서 mode 별로 렌더.

| mode | 동작 | |------|------| | 'diff' | <del> 분홍+취소선, <ins> 파란 강조, eq 일반 | | 'after' | <del> 제외, <ins> 내용만 살림 → 수정 후 평문처럼 보임 | | 'before' | <ins> 제외, <del> 내용만 살림 → 수정 전 평문처럼 보임 |

className 은 컨테이너 <p> 에 추가된다.

Sanitization

<del>/<ins> 는 HTML5 표준 시맨틱 태그라 DOMPurify 등 대부분의 sanitizer 기본 whitelist 에 포함된다. 통상 별도 작업 없이 통과한다. 보수적인 환경이면 운영 적용 전 PUT → GET 라운드트립을 한 번 확인.

License

MIT