A **zero-dependency**, lightweight React text editor with intelligent entity recognition and real-time formatting using an innovative bitmap-based architecture.
npm install @connectedxm/entity-editorA zero-dependency, lightweight React text editor with intelligent entity recognition and real-time formatting using an innovative bitmap-based architecture.
- ๐ Zero Dependencies: Pure React implementation with no external libraries
- ๐ฏ Smart Entity Recognition: Automatically detects and styles:
- @mentions (@username)
- #interests (#hashtag)
- ๐ Links (https://example.com)
- ๐ Rich Text Formatting: Bold, italic, underline, strikethrough with keyboard shortcuts
- โก Real-time Processing: Instant styling as you type with optimized performance
- ๐๏ธ Bitmap Architecture: Ultra-efficient character-level state tracking using bit operations
- ๐จ Customizable Styling: Configurable colors and styles for all entity types
- ๐ Search & Autocomplete: Built-in search functionality for mentions and interests
- โจ๏ธ Keyboard Shortcuts: Cmd/Ctrl+B/I/U/Shift+S for formatting
- ๐ฑ Accessible: Built on standard contentEditable with proper cursor management
The editor's core innovation is its bitmap-based character tracking system. Instead of storing complex objects for each formatting state, every character position in the text is represented by a single number where each bit represents a different property:
``typescript`
// Each character's state encoded in 8 bits (1 byte)
const BOLD_MASK = 0b00000010; // Bit 1: Bold formatting
const ITALIC_MASK = 0b00000100; // Bit 2: Italic formatting
const UNDERLINE_MASK = 0b00001000; // Bit 3: Underline formatting
const STRIKE_MASK = 0b00010000; // Bit 4: Strikethrough formatting
const MENTION_MASK = 0b00100000; // Bit 5: Part of @mention
const INTEREST_MASK = 0b01000000; // Bit 6: Part of #hashtag
const LINK_MASK = 0b10000000; // Bit 7: Part of URL link
1. Character Mapping: Each character position has a corresponding number in the bitmap array
2. Bit Operations: Properties are set/unset using bitwise OR (|) and AND (&) operations
3. Entity Building: Consecutive characters with the same bitmap value are grouped into entities
4. Efficient Updates: Only modified bitmap sections trigger re-rendering
Example: The text "Hello @john" might have a bitmap like:
``
Text: H e l l o @ j o h n
Bitmap: 0 0 0 0 0 0 32 32 32 32 32
Where 32 is 0b00100000 (MENTION_MASK), indicating those characters are part of a mention.
`bash`
npm install @connectedxm/entity-editor
`tsx
import React, { useState, useRef } from "react";
import {
Editor,
EditorRef,
Entity,
MarkState,
SearchEntity,
} from "@connectedxm/entity-editor";
function App() {
const editorRef = useRef
const [plainText, setPlainText] = useState("");
const [entities, setEntities] = useState
const [markState, setMarkState] = useState
bold: false,
italic: false,
underline: false,
strike: false,
});
const [search, setSearch] = useState
return (
plainText={plainText}
setPlainText={setPlainText}
entities={entities}
setEntities={setEntities}
markState={markState}
setMarkState={setMarkState}
search={search}
setSearch={setSearch}
options={{
mentions: true,
interests: true,
links: true,
}}
style={{
border: "1px solid #ccc",
minHeight: "100px",
padding: "10px",
}}
/>
);
}
`
`tsx`
plainText={plainText}
setPlainText={setPlainText}
entities={entities}
setEntities={setEntities}
markState={markState}
setMarkState={setMarkState}
search={search}
setSearch={setSearch}
options={{
mentions: true,
interests: true,
links: true,
}}
entityStyles={{
mentionColor: "#1da1f2",
interestColor: "#ff6b35",
linkColor: "#9c27b0",
}}
className="custom-editor"
debug={false}
/>
The editor exposes imperative methods through a ref:
`tsx
const editorRef = useRef
// Programmatically select/replace entities
const handleMentionSelect = (username: string) => {
if (search?.type === "mention") {
editorRef.current?.selectEntity(
"mention",
search.startIndex,
search.endIndex,
username
);
}
};
// Toggle formatting programmatically
const makeBold = () => {
editorRef.current?.toggleMark("bold");
};
`
`tsx
// Handle search results for mentions
{
search?.type === "mention" && (
Select a user to mention:
{searchResults.map((user) => (
handleMentionSelect(user.username)}>
{user.name} (@{user.username})
))}
);
}
// Handle search results for interests
{
search?.type === "interest" && (
$3
Add CSS to style the recognized entities:
`css
.activity-mention {
color: #1da1f2;
background-color: rgba(29, 161, 242, 0.1);
border-radius: 3px;
padding: 0 2px;
}.activity-interest {
color: #ff6b35;
font-weight: 500;
}
.activity-link {
color: #9c27b0;
text-decoration: underline;
}
.activity-bold {
font-weight: bold;
}
.activity-italic {
font-style: italic;
}
.activity-underline {
text-decoration: underline;
}
.activity-strike {
text-decoration: line-through;
}
`๐ Entity Structure
The editor converts bitmap data into structured entities for easy consumption:
`typescript
interface Entity {
type: "mention" | "interest" | "link" | "segment";
startIndex: number;
endIndex: number;
marks: ("bold" | "italic" | "underline" | "strike")[];
href?: string; // For links
username?: string; // For mentions
interest?: string; // For interests
}interface SearchEntity {
type: "mention" | "interest" | "link";
search: string;
startIndex: number;
endIndex: number;
}
`$3
1. Bitmap Scanning: The system scans through the bitmap array
2. Grouping: Consecutive characters with identical bit values are grouped together
3. Entity Creation: Each group becomes an entity with its type determined by the bitmap value
4. Mark Extraction: Formatting bits are converted to a
marks array๐ฏ Recognition Patterns
- Mentions:
@username (alphanumeric, underscores, hyphens, apostrophes)
- Interests: #hashtag (alphanumeric, underscores, hyphens)
- Links: https:// or http:// URLs
- Formatting: Applied via keyboard shortcuts or toolbarโก Performance Benefits
$3
The bitmap approach provides significant performance advantages:
Memory Efficiency:
- Each character: 1 number (4-8 bytes) vs complex objects (50+ bytes)
- 10,000 characters: ~40KB vs ~500KB+ in traditional approaches
- 90%+ memory reduction for large documents
Processing Speed:
- Bit operations are CPU-native and extremely fast
- O(1) property checks vs O(n) object property lookups
- 5-10x faster entity processing
Update Performance:
- Only modified bitmap regions trigger re-rendering
- Optimized React re-renders through efficient state management
๐ API Reference
$3
| Prop | Type | Required | Description |
| -------------- | ---------------------------------------- | -------- | ------------------------------------- |
|
ref | React.RefObject | Yes | Ref to access editor methods |
| plainText | string | Yes | The current plain text content |
| setPlainText | (text: string) => void | Yes | Callback to update plain text |
| entities | Entity[] | Yes | Array of recognized entities |
| setEntities | (entities: Entity[]) => void | Yes | Callback to update entities |
| markState | MarkState | Yes | Current formatting state |
| setMarkState | (state: MarkState) => void | Yes | Callback to update formatting state |
| search | SearchEntity \| null | Yes | Current search state for autocomplete |
| setSearch | (search: SearchEntity \| null) => void | Yes | Callback to update search state |
| options | EntityOptions | No | Configure which entities to detect |
| entityStyles | StyleOptions | No | Custom colors for entity types |
| style | React.CSSProperties | No | Inline styles for the editor |
| className | string | No | CSS class name for the editor |
| debug | boolean | No | Enable debug mode (default: false) |$3
`typescript
interface EntityOptions {
mentions?: boolean; // Enable @mention detection
interests?: boolean; // Enable #hashtag detection
links?: boolean; // Enable URL detection
}interface StyleOptions {
mentionColor?: string; // Custom color for mentions
interestColor?: string; // Custom color for interests
linkColor?: string; // Custom color for links
}
interface MarkState {
bold: boolean;
italic: boolean;
underline: boolean;
strike: boolean;
}
interface EditorRef {
selectEntity: (
entityType: EntityType,
startIndex: number,
endIndex: number,
newText: string
) => void;
toggleMark: (markType: MarkType) => void;
}
`$3
- Ctrl/Cmd + B: Toggle bold
- Ctrl/Cmd + I: Toggle italic
- Ctrl/Cmd + U: Toggle underline
- Ctrl/Cmd + Shift + S: Toggle strikethrough
๐งช Testing
Run the test suite:
`bash
npm test # Run tests in watch mode
npm run test:run # Run tests once
`๐๏ธ Development
`bash
Install dependencies
npm installStart development server
npm run devBuild for production
npm run buildCreate local package
npm run local
`๐ Why Zero Dependencies?
This editor proves that powerful text editing doesn't require heavy libraries:
- Performance: No bundle bloat, faster load times
- Security: No third-party vulnerabilities
- Control: Full understanding and control over every feature
- Maintenance: Easier updates and customization
- Reliability: No breaking changes from external dependencies
๐ค Contributing
This project maintains its zero-dependency philosophy and bitmap-based architecture. When contributing:
1. No External Dependencies: Keep the runtime dependency-free
2. Bitmap First: All new features should leverage the bitmap system
3. Performance Focused: Optimize for memory usage and processing speed
4. TypeScript: Maintain strict typing throughout
5. Test Coverage: Add comprehensive tests for new features
6. API Stability: Keep the component interface simple and focused
$3
-
src/Editor.tsx - Main editor component
- src/interfaces.ts - TypeScript interfaces and types
- src/helpers/bitmap.ts - Core bitmap manipulation functions
- src/helpers/entities.ts - Bitmap-to-entity conversion logic
- src/helpers/marks/ - Individual formatting bit operations
- src/helpers/entities/ - Entity type detection and processing
- src/helpers/dom.ts - DOM manipulation and cursor management
- src/helpers/keyboard.ts - Keyboard shortcut handling
- src/hooks/useDebounce.ts` - Debounce hook for performanceMIT License - feel free to use in your projects!
---
Built with โค๏ธ and zero dependencies by ConnectedXM