CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-ace

A React component for Ace Editor providing comprehensive code editing capabilities with syntax highlighting, themes, and customization options

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

diff-editor.mddocs/

Diff Editor

The Diff Editor component provides specialized functionality for comparing two pieces of text with visual highlighting of differences. It's built on top of the Split Editor and uses diff-match-patch for intelligent difference detection and highlighting.

Capabilities

DiffComponent

Diff editor component that compares two text sources and highlights their differences with visual indicators.

/**
 * Diff editor component for comparing and highlighting differences between two text sources
 * Automatically detects changes and provides visual indicators for additions, deletions, and modifications
 */
declare class DiffComponent extends React.Component<IDiffEditorProps, IDiffEditorState> {}

interface IDiffEditorProps {
  /** Starting cursor position */
  cursorStart?: number;
  /** Additional editor properties */
  editorProps?: object;
  /** Enable basic autocompletion */
  enableBasicAutocompletion?: boolean | string[];
  /** Enable live autocompletion */
  enableLiveAutocompletion?: boolean | string[];
  /** Auto-focus the diff editor on mount */
  focus?: boolean;
  /** Font size for both editor panes */
  fontSize?: number;
  /** Total height of the diff editor */
  height?: string;
  /** Highlight active line in both panes */
  highlightActiveLine?: boolean;
  /** Maximum lines for both editor panes */
  maxLines?: number;
  /** Minimum lines for both editor panes */
  minLines?: number;
  /** Syntax highlighting mode for both panes */
  mode?: string;
  /** Unique identifier for the diff editor instance */
  name?: string;
  /** CSS class name for the diff editor container */
  className?: string;
  /** Called when diff editor is fully loaded */
  onLoad?: (editor: IEditorProps) => void;
  /** Called when content changes in either pane */
  onChange?: (value: string[], event?: any) => void;
  /** Called when text is pasted into either pane */
  onPaste?: (value: string) => void;
  /** Called when either pane is scrolled */
  onScroll?: (editor: IEditorProps) => void;
  /** Split orientation ("beside" or "below") */
  orientation?: string;
  /** Make both editor panes read-only */
  readOnly?: boolean;
  /** Scroll margin for both editor panes */
  scrollMargin?: number[];
  /** Ace editor configuration options */
  setOptions?: object;
  /** Show line numbers in both panes */
  showGutter?: boolean;
  /** Show print margin in both panes */
  showPrintMargin?: boolean;
  /** Number of splits (typically 2 for diff view) */
  splits?: number;
  /** CSS styles for the diff editor container */
  style?: object;
  /** Tab size for both editor panes */
  tabSize?: number;
  /** Editor theme for both panes */
  theme?: string;
  /** Content for comparison [original, modified] */
  value?: string[];
  /** Total width of the diff editor */
  width?: string;
  /** Enable line wrapping in both panes */
  wrapEnabled?: boolean;
}

interface IDiffEditorState {
  /** Current content values [original, modified] */
  value: string[];
}

Usage Examples

Basic Diff Comparison:

import React, { useState } from "react";
import { diff } from "react-ace";

import "ace-builds/src-noconflict/mode-javascript";
import "ace-builds/src-noconflict/theme-github";

const DiffEditor = diff;

function CodeComparison() {
  const [values, setValues] = useState([
    // Original code
    `function calculateTotal(items) {
  let total = 0;
  for (let i = 0; i < items.length; i++) {
    total += items[i].price;
  }
  return total;
}`,
    // Modified code
    `function calculateTotal(items) {
  return items.reduce((total, item) => total + item.price, 0);
}`
  ]);

  return (
    <DiffEditor
      mode="javascript"
      theme="github"
      value={values}
      onChange={setValues}
      name="code-diff"
      width="100%"
      height="400px"
      fontSize={14}
      showGutter={true}
      highlightActiveLine={true}
    />
  );
}

Document Comparison with Headers:

import React, { useState } from "react";
import { diff } from "react-ace";

import "ace-builds/src-noconflict/mode-markdown";
import "ace-builds/src-noconflict/theme-github";

const DiffEditor = diff;

function DocumentDiff() {
  const [originalDoc, setOriginalDoc] = useState(`# Project Documentation

## Overview
This project provides basic functionality.

## Features
- Feature A
- Feature B

## Installation
Run npm install to get started.`);

  const [modifiedDoc, setModifiedDoc] = useState(`# Project Documentation

## Overview
This project provides comprehensive functionality with advanced features.

## Features
- Feature A (Enhanced)
- Feature B
- Feature C (New)

## Installation
Run npm install to get started.

## Configuration
Set environment variables before running.`);

  const handleChange = (values) => {
    setOriginalDoc(values[0]);
    setModifiedDoc(values[1]);
  };

  return (
    <div>
      <div style={{ display: 'flex', marginBottom: '10px' }}>
        <div style={{ flex: 1, textAlign: 'center', fontWeight: 'bold' }}>
          Original
        </div>
        <div style={{ flex: 1, textAlign: 'center', fontWeight: 'bold' }}>
          Modified
        </div>
      </div>
      <DiffEditor
        mode="markdown"
        theme="github"
        value={[originalDoc, modifiedDoc]}
        onChange={handleChange}
        name="document-diff"
        width="100%"
        height="500px"
        fontSize={14}
        showGutter={true}
        orientation="beside"
      />
    </div>
  );
}

Read-Only Diff Viewer:

import React from "react";
import { diff } from "react-ace";

import "ace-builds/src-noconflict/mode-json";
import "ace-builds/src-noconflict/theme-monokai";

const DiffEditor = diff;

interface DiffViewerProps {
  beforeData: object;
  afterData: object;
}

function DiffViewer({ beforeData, afterData }: DiffViewerProps) {
  const values = [
    JSON.stringify(beforeData, null, 2),
    JSON.stringify(afterData, null, 2)
  ];

  return (
    <DiffEditor
      mode="json"
      theme="monokai"
      value={values}
      name="json-diff-viewer"
      readOnly={true}
      width="100%"
      height="600px"
      fontSize={14}
      showGutter={true}
      highlightActiveLine={false}
      setOptions={{
        showLineNumbers: true,
        showFoldWidgets: true
      }}
    />
  );
}

Diff with Custom Styling:

import React, { useState } from "react";
import { diff } from "react-ace";

const DiffEditor = diff;

function StyledDiffEditor() {
  const [values, setValues] = useState([
    "Original text content",
    "Modified text content with changes"
  ]);

  const customStyle = {
    border: '2px solid #ccc',
    borderRadius: '8px',
    overflow: 'hidden'
  };

  return (
    <div>
      <style>{`
        .diff-editor .ace_gutter {
          background: #f5f5f5;
        }
        .diff-editor .ace_gutter-active-line {
          background: #e8f4f8;
        }
        .diff-added {
          background-color: #d4edda;
        }
        .diff-removed {
          background-color: #f8d7da;
        }
      `}</style>
      
      <DiffEditor
        mode="text"
        theme="github"
        value={values}
        onChange={setValues}
        name="styled-diff"
        className="diff-editor"
        style={customStyle}
        width="100%"
        height="400px"
        fontSize={14}
      />
    </div>
  );
}

Vertical Diff Layout:

import React, { useState } from "react";
import { diff } from "react-ace";

import "ace-builds/src-noconflict/mode-html";
import "ace-builds/src-noconflict/theme-github";

const DiffEditor = diff;

function VerticalDiff() {
  const [values, setValues] = useState([
    '<div class="old-version">Old HTML</div>',
    '<div class="new-version">New HTML with improvements</div>'
  ]);

  return (
    <DiffEditor
      mode="html"
      theme="github"
      value={values}
      onChange={setValues}
      name="vertical-diff"
      orientation="below"
      width="100%"
      height="600px"
      fontSize={14}
      showGutter={true}
      splits={2}
    />
  );
}

Diff Features

Automatic Difference Detection:

The Diff Editor automatically detects and highlights:

  • Added lines: New content in the modified version
  • Removed lines: Content present in original but not modified
  • Changed lines: Lines with modifications
  • Unchanged lines: Content that remains the same

Visual Indicators:

  • Different background colors for added/removed/changed content
  • Line-by-line comparison with clear visual separation
  • Gutter indicators showing change types
  • Synchronized scrolling between panes

Configuration Options

Layout Configuration:

interface DiffLayoutConfig {
  /** Split orientation */
  orientation?: "beside" | "below";
  /** Number of comparison panes */
  splits?: number;
  /** Synchronized scrolling */
  syncScroll?: boolean;
}

Diff-Specific Options:

The Diff Editor inherits all standard Ace Editor options and adds diff-specific behavior:

  • Automatic line-by-line comparison
  • Visual difference highlighting
  • Synchronized cursor movement (optional)
  • Read-only mode for pure viewing
  • Custom styling for different change types

Event Handling

Change Detection:

/** Called when content changes in either pane */
onChange?: (values: string[], event?: {
  paneIndex: number;
  changeType: 'addition' | 'deletion' | 'modification';
  affectedLines: number[];
}) => void;

Load Event:

/** Called when diff editor is fully loaded */
onLoad?: (editor: {
  leftPane: Ace.Editor;
  rightPane: Ace.Editor;
  getDiffs: () => DiffResult[];
}) => void;

Integration with diff-match-patch

The Diff Editor uses the diff-match-patch library internally for:

  • Intelligent text comparison algorithms
  • Efficient difference calculation
  • Line-level and character-level diff detection
  • Optimal highlighting placement

Performance Considerations

  • Large Files: Consider pagination or virtualization for very large files
  • Real-time Updates: Use debouncing for frequently changing content
  • Memory Usage: Diff calculations can be memory-intensive for large texts
  • Rendering: Complex diffs may impact rendering performance

Accessibility

The Diff Editor includes accessibility features:

  • Keyboard navigation between panes
  • Screen reader support for change descriptions
  • High contrast mode compatibility
  • Focus management for diff navigation

Install with Tessl CLI

npx tessl i tessl/npm-react-ace

docs

ace-editor.md

diff-editor.md

index.md

split-editor.md

tile.json