{"id":49492652,"url":"https://github.com/sohei-t/text-diff-editor","last_synced_at":"2026-05-01T07:06:09.934Z","repository":{"id":339754337,"uuid":"1163227872","full_name":"sohei-t/text-diff-editor","owner":"sohei-t","description":"React + TypeScript 製マルチパネルテキスト差分エディタ。Myers差分アルゴリズム、3テーマ対応、検索・置換、ズーム・パン、File System Access API","archived":false,"fork":false,"pushed_at":"2026-03-12T07:39:09.000Z","size":24528,"stargazers_count":0,"open_issues_count":6,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-12T11:38:08.259Z","etag":null,"topics":["dark-mode","diff-engine","multi-panel","myers-algorithm","react","text-editor","typescript","vite"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sohei-t.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-21T09:50:34.000Z","updated_at":"2026-03-12T07:39:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sohei-t/text-diff-editor","commit_stats":null,"previous_names":["sohei-t/text-diff-editor"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sohei-t/text-diff-editor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sohei-t%2Ftext-diff-editor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sohei-t%2Ftext-diff-editor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sohei-t%2Ftext-diff-editor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sohei-t%2Ftext-diff-editor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sohei-t","download_url":"https://codeload.github.com/sohei-t/text-diff-editor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sohei-t%2Ftext-diff-editor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32487751,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dark-mode","diff-engine","multi-panel","myers-algorithm","react","text-editor","typescript","vite"],"created_at":"2026-05-01T07:06:08.967Z","updated_at":"2026-05-01T07:06:09.919Z","avatar_url":"https://github.com/sohei-t.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Text Diff Editor\n\n[![CI](https://github.com/sohei-t/text-diff-editor/actions/workflows/ci.yml/badge.svg)](https://github.com/sohei-t/text-diff-editor/actions/workflows/ci.yml)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.7-3178C6?logo=typescript\u0026logoColor=white)](https://www.typescriptlang.org/)\n[![React](https://img.shields.io/badge/React-18.3-61DAFB?logo=react\u0026logoColor=white)](https://react.dev/)\n[![Vite](https://img.shields.io/badge/Vite-6.0-646CFF?logo=vite\u0026logoColor=white)](https://vite.dev/)\n[![Tests](https://img.shields.io/badge/tests-66%20passing-brightgreen)](https://github.com/sohei-t/text-diff-editor/actions)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nA multi-panel text diff viewer and editor built with React 18 and TypeScript. Features a custom Myers diff algorithm implementation with character-level precision, three professional themes, and a complete editing toolkit -- all without external diff libraries.\n\n**[Live Demo](https://sohei-t.github.io/text-diff-editor/)**\n\n---\n\n## Table of Contents\n\n- [Overview](#overview)\n- [Features](#features)\n- [Architecture](#architecture)\n- [Tech Stack](#tech-stack)\n- [Getting Started](#getting-started)\n- [Keyboard Shortcuts](#keyboard-shortcuts)\n- [Testing](#testing)\n- [Diff Algorithm](#diff-algorithm)\n- [Contributing](#contributing)\n- [License](#license)\n\n---\n\n## Overview\n\nText Diff Editor is a browser-based application for comparing and editing text side by side. It supports 1-, 2-, and 3-panel split views with real-time diff computation, character-level inline highlighting, and a full-featured editor with undo/redo, search and replace, zoom, and pan.\n\nThe diff engine is written entirely in TypeScript with no external dependencies, implementing the Myers shortest edit script algorithm for line-level diffs and LCS (Longest Common Subsequence) for character-level inline diffs.\n\n---\n\n## Features\n\n### Diff and Comparison\n\n- **Myers diff algorithm** -- Line-level diff with O(D(N+M)) time complexity\n- **Character-level inline diffs** -- LCS-based highlighting within modified lines\n- **Real-time computation** -- Diffs update as you type\n- **Async diff support** -- Non-blocking computation for large files\n- **Result caching** -- 20-item LRU cache for repeated comparisons\n\n### Editor\n\n- **Multi-panel views** -- Switch between 1, 2, or 3 pane split layouts\n- **Resizable panels** -- Drag splitters to adjust panel widths\n- **1000-level undo/redo** -- Deep operation history per panel\n- **Line numbers** -- Toggleable gutter display\n- **Word wrap** -- Configurable text wrapping\n- **Current line highlighting** -- Visual indicator for active line\n\n### Search and Replace\n\n- **Full-text search** -- Find across editor content with match navigation\n- **Regular expression support** -- Regex pattern matching with case sensitivity toggle\n- **Replace and replace all** -- Single or bulk replacements\n\n### Navigation and Viewport\n\n- **Zoom** -- Cmd/Ctrl + scroll wheel for scaling\n- **Pan** -- Alt + drag with momentum-based inertia scrolling\n- **Synchronized scrolling** -- Lock scroll position across panels\n- **Diff navigation** -- Jump to next/previous change with F7/Shift+F7\n\n### File Operations\n\n- **File System Access API** -- Native file open/save on supported browsers (Chrome/Edge)\n- **Fallback file handling** -- Download-based save for unsupported browsers\n- **Auto-save** -- Configurable timer-based automatic saving\n- **Recent files** -- Track recently opened files\n\n### Themes and Accessibility\n\n- **Light theme** -- Clean parchment-toned design\n- **Dark theme** -- Low-glare ink-themed interface\n- **High Contrast theme** -- WCAG AA compliant with enhanced color ratios\n- **CSS Custom Properties** -- Theme switching via CSS variables with zero re-renders\n- **Keyboard-accessible** -- Full operation via keyboard shortcuts\n\n---\n\n## Architecture\n\n```\nsrc/\n|\n|-- engine/                     # Pure TypeScript diff engine\n|   |-- DiffEngine.ts           #   Myers algorithm + LCS inline diffs\n|   +-- README.md               #   Algorithm documentation\n|\n|-- types/\n|   +-- index.ts                # All type definitions (185 lines)\n|\n|-- context/                    # 7 React Context providers\n|   |-- ThemeContext.tsx         #   Theme switching (light/dark/high-contrast)\n|   |-- SettingsContext.tsx      #   Persisted user preferences\n|   |-- EditorContext.tsx        #   Panel content and cursor state\n|   |-- SplitContext.tsx         #   Split view configuration\n|   |-- DiffContext.tsx          #   Diff computation results\n|   |-- FileContext.tsx          #   File operations\n|   |-- ToastContext.tsx         #   Notification system\n|   +-- index.ts                #   Barrel export\n|\n|-- hooks/                      # 12 custom hooks\n|   |-- useEditor.ts            #   Core editor state management\n|   |-- useUndoRedo.ts          #   1000-level undo/redo history\n|   |-- useSearch.ts            #   Search and replace logic\n|   |-- useDiffComputation.ts   #   Debounced diff computation\n|   |-- useZoom.ts              #   Cmd+scroll zoom handler\n|   |-- usePan.ts               #   Alt+drag with momentum inertia\n|   |-- useScrollSync.ts        #   Synchronized panel scrolling\n|   |-- useSplitter.ts          #   Drag-to-resize panels\n|   |-- useAutoSave.ts          #   Timer-based auto-save\n|   |-- useKeyboardShortcuts.ts #   Global shortcut registration\n|   |-- useDebounce.ts          #   Debounce utility hook\n|   +-- useWelcomeMessage.ts    #   First-run welcome message\n|\n|-- components/\n|   |-- EditorApp.tsx           # Root application component\n|   |-- toolbar/\n|   |   +-- Toolbar.tsx         #   File, view, font, and zoom controls\n|   |-- editor/\n|   |   |-- EditorContainer.tsx #   Multi-panel layout orchestrator\n|   |   |-- EditorPanel.tsx     #   Individual editor panel\n|   |   |-- TextArea.tsx        #   Controlled textarea with diff overlay\n|   |   |-- LineNumbers.tsx     #   Gutter line number display\n|   |   |-- PanelHeader.tsx     #   Panel title and file info\n|   |   +-- Splitter.tsx        #   Draggable panel divider\n|   |-- search/\n|   |   +-- SearchBar.tsx       #   Search/replace floating bar\n|   |-- status/\n|   |   +-- StatusBar.tsx       #   Cursor position, diff stats, theme\n|   +-- ui/\n|       |-- Toast.tsx           #   Individual toast notification\n|       |-- ToastContainer.tsx  #   Toast stack manager\n|       +-- ErrorBoundary.tsx   #   React error boundary\n|\n|-- utils/                      # Helper functions\n|-- styles/\n|   +-- index.css               # All styles with CSS Custom Properties\n|\n+-- __tests__/                  # 66 tests across 4 test suites\n    |-- DiffEngine.test.ts      #   Diff algorithm unit tests\n    |-- hooks.test.ts           #   Custom hook tests\n    |-- utils.test.ts           #   Utility function tests\n    +-- components.test.tsx     #   Component rendering tests\n```\n\n---\n\n## Tech Stack\n\n| Category         | Technology                                  |\n| ---------------- | ------------------------------------------- |\n| Framework        | React 18.3.1                                |\n| Language         | TypeScript 5.7.2 (strict mode)              |\n| Build Tool       | Vite 6.0.5                                  |\n| Test Framework   | Vitest 2.1.8                                |\n| Test Utilities   | React Testing Library 16.1, jsdom 25        |\n| Styling          | CSS Custom Properties (zero-runtime themes) |\n| CI/CD            | GitHub Actions (test, build, deploy)        |\n| Hosting          | GitHub Pages                                |\n| Diff Engine      | Custom Myers algorithm (pure TypeScript)     |\n| File Handling    | File System Access API + fallback           |\n\n**Zero external diff dependencies.** The entire diff engine is implemented from scratch in TypeScript.\n\n---\n\n## Getting Started\n\n### Prerequisites\n\n- Node.js 20 or later\n- npm 9 or later\n\n### Installation\n\n```bash\ngit clone https://github.com/sohei-t/text-diff-editor.git\ncd text-diff-editor\nnpm install\n```\n\n### Development\n\n```bash\nnpm run dev          # Start development server (Vite HMR)\n```\n\nOpen [http://localhost:5173/text-diff-editor/](http://localhost:5173/text-diff-editor/) in your browser.\n\n### Build\n\n```bash\nnpm run build        # Type-check and build for production\nnpm run preview      # Preview the production build locally\n```\n\n### Test\n\n```bash\nnpm test             # Run all 66 tests\nnpm run test:watch   # Run tests in watch mode\n```\n\n---\n\n## Keyboard Shortcuts\n\n| Shortcut           | Action                    |\n| ------------------ | ------------------------- |\n| `Cmd/Ctrl + N`     | New file                  |\n| `Cmd/Ctrl + O`     | Open file                 |\n| `Cmd/Ctrl + S`     | Save file                 |\n| `Cmd/Ctrl + W`     | Close panel               |\n| `Cmd/Ctrl + F`     | Find                      |\n| `Cmd/Ctrl + H`     | Find and replace          |\n| `Cmd/Ctrl + \\`     | Toggle split view         |\n| `Cmd/Ctrl + Z`     | Undo                      |\n| `Cmd/Ctrl + Shift + Z` | Redo                  |\n| `F7`               | Jump to next diff         |\n| `Shift + F7`       | Jump to previous diff     |\n| `Cmd/Ctrl + Scroll`| Zoom in/out               |\n| `Alt + Drag`       | Pan viewport (with momentum) |\n\n---\n\n## Testing\n\nThe test suite contains **66 tests** organized into four test files covering the core layers of the application:\n\n| Test File               | Scope                                      | Tests |\n| ----------------------- | ------------------------------------------ | ----- |\n| `DiffEngine.test.ts`    | Myers algorithm, inline diffs, caching     | Core  |\n| `hooks.test.ts`         | Custom hook behavior and state management  | Hooks |\n| `utils.test.ts`         | Utility and helper function correctness    | Utils |\n| `components.test.tsx`   | Component rendering and user interactions  | UI    |\n\nRun the full suite:\n\n```bash\nnpm test\n```\n\nThe CI pipeline runs on every push and pull request to `main`, executing type checking, all tests, and a production build before deploying to GitHub Pages.\n\n---\n\n## Diff Algorithm\n\nThe diff engine implements two complementary algorithms:\n\n### Line-Level Diff: Myers Shortest Edit Script\n\nThe [Myers diff algorithm](http://www.xmailserver.org/diff2.pdf) (1986) finds the shortest edit script between two sequences. It operates in O(D(N+M)) time where D is the edit distance, making it efficient for texts with few changes.\n\n**Implementation details:**\n\n1. **Forward pass** -- Explores the edit graph along diagonals, tracking the furthest-reaching point for each diagonal at each edit distance `d`\n2. **Trace recording** -- Stores the state vector at each step for backtracking\n3. **Backtracking** -- Reconstructs the optimal edit path from the trace, producing a sequence of equal/add/delete operations\n4. **Change building** -- Converts raw edit operations into structured `DiffChange` objects, pairing adjacent delete+add operations as modifications\n\n### Character-Level Inline Diff: LCS\n\nFor lines classified as \"modified\" (a paired delete and add), the engine computes character-level diffs using a dynamic programming LCS (Longest Common Subsequence) approach:\n\n1. Build an (m+1) x (n+1) DP table for the two character arrays\n2. Backtrack through the table to extract the LCS\n3. Walk both strings against the LCS to emit equal/add/delete segments\n\nThis produces precise, character-by-character highlighting of what changed within a line.\n\n### Caching\n\nResults are stored in a 20-item LRU cache keyed by text length and a 64-character prefix. Full-text equality is verified on cache hits to prevent false matches.\n\n---\n\n## Contributing\n\nContributions are welcome. Please follow these steps:\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feat/your-feature`)\n3. Make your changes with tests\n4. Ensure all 66 tests pass (`npm test`)\n5. Ensure TypeScript compiles without errors (`npx tsc --noEmit`)\n6. Submit a pull request\n\n### Code Style\n\n- TypeScript strict mode is enforced\n- All types are defined in `src/types/index.ts`\n- Context providers follow the pattern in `src/context/`\n- Custom hooks are in `src/hooks/` with barrel exports\n\n---\n\n## License\n\n[MIT](./LICENSE) -- Copyright (c) 2026 sohei-t\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsohei-t%2Ftext-diff-editor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsohei-t%2Ftext-diff-editor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsohei-t%2Ftext-diff-editor/lists"}