npm package: @seolhun/firstage-editor
Description: A modern, extensible rich text editor built with Lexical and React, featuring a **colocation architecture** for maximum maintainability and developer experience.
Author: Seolhun
Latest version: 0.1.9

# Editor Package

A modern, extensible rich text editor built with Lexical and React, featuring a **colocation architecture** for maximum maintainability and developer experience.

## 🎯 PlainText vs RichText Architecture

This editor supports two distinct modes optimized for different use cases:

### PlainText Editor (소셜 미디어 간단 글쓰기)

- **Purpose**: Quick social media posts, comments, simple messages
- **Features**: Mentions (@user), Emojis (😀), Images, Hashtags (#tag), Auto-links
- **UI**: Clean, minimal interface focused on speed
- **Nodes**: Limited to essential formatting only

### RichText Editor (블로그 고도화 에디터)  

- **Purpose**: Blog posts, articles, documentation, long-form content
- **Features**: All PlainText features plus tables, code blocks, headings, lists, embeds
- **UI**: Full toolbar with advanced formatting options
- **Nodes**: Complete feature set for professional content creation

### Dynamic Configuration

The editor automatically loads appropriate nodes and plugins based on the `isRichText` setting:

```tsx
// PlainText mode - minimal features
<RootEditor initialSettings={{ isRichText: false }} />

// RichText mode - full features  
<RootEditor initialSettings={{ isRichText: true }} />
```

## 🎯 Colocation Architecture

This editor follows a **feature-first colocation** approach where all related files for a feature are grouped together in a single directory. This improves code organization, reduces cognitive load, and makes features more maintainable.

### Perfect Colocation Example: Emoji System ✨

```sh
packages/editor/src/features/emoji/
├── components/            # Components used by Node/Renderer/Plugin
├── emoji.node.ts          # Lexical node definition
├── emoji.plugin.tsx       # Main plugin with FloatingPortal
├── emoji.typeahead.tsx    # Typeahead functionality
├── emoji.renderer.tsx     # EmojiPicker renderer (@emoji-mart/react)
├── emoji.store.ts         # Zustand state management
├── emoji.types.ts         # TypeScript type definitions
├── emoji.utils.ts         # Search and filtering utilities
├── emoji.item.tsx         # Individual emoji item component
├── emoji.{feature-name}.tsx # Additional feature-specific files
└── index.ts               # Exports (export * from pattern)
```

### Naming Convention

**Pattern**: `{feature}.{type}.{ext}`

- **Node**: `{feature}.node.ts` (e.g., `emoji.node.ts`)
- **Plugin**: `{feature}.plugin.tsx` (e.g., `emoji.plugin.tsx`)
- **Typeahead**: `{feature}.typeahead.tsx` (e.g., `emoji.typeahead.tsx`)
- **Renderer**: `{feature}.renderer.tsx` (e.g., `emoji.renderer.tsx`)
- **Store**: `{feature}.store.ts` (e.g., `emoji.store.ts`)
- **Types**: `{feature}.types.ts` (e.g., `emoji.types.ts`)
- **Utils**: `{feature}.utils.ts` (e.g., `emoji.utils.ts`)
- **Components**: `{feature}.{component}.tsx` (e.g., `emoji.item.tsx`)

## 📁 Project Structure

```sh
packages/editor/src/
├── features/                   # Feature-first colocation
│   ├── emoji/                 # ✅ Perfect colocation example
│   │   ├── emoji.node.ts      # EmojiNode definition
│   │   ├── emoji.plugin.tsx   # Main plugin
│   │   ├── emoji.typeahead.tsx # Typeahead functionality
│   │   ├── emoji.renderer.tsx # EmojiPicker renderer
│   │   ├── emoji.store.ts     # State management
│   │   ├── emoji.types.ts     # Type definitions
│   │   ├── emoji.utils.ts     # Utilities
│   │   ├── emoji.item.tsx     # Item component
│   │   ├── emoji.{feature-name}.ts   # What you need for the feature
│   │   └── index.ts           # Exports
│   ├── image/                 # 🔄 Improved colocation
│   │   ├── image.node.tsx     # ImageNode definition
│   │   ├── image.plugin.tsx   # Main plugin
│   │   ├── image.component.tsx # Image component
│   │   ├── image.types.ts     # Type definitions
│   │   ├── image.{feature-name}.ts   # What you need for the feature
│   │   ├── index.ts           # Exports
│   │   └── [legacy folders]   # Backward compatibility
│   ├── mention/               # 🔄 New colocation structure
│   │   ├── mention.node.ts    # MentionNode definition
│   │   ├── mention.plugin.tsx # Main plugin
│   │   ├── mention.item.tsx   # Mention item component
│   │   ├── mention.types.ts   # Type definitions
│   │   ├── mention.{feature-name}.ts   # What you need for the feature
│   │   └── index.ts           # Exports
│   ├── .../                   # 🔄 New colocation structure
│   │   ├── ...node.ts         # ...Node definition
│   │   ├── ...plugin.tsx      # Main plugin
│   │   ├── ...types.ts        # Type definitions
│   │   ├── ....{feature-name}.ts   # What you need for the feature
│   │   └── index.ts           # Exports
│   └── editors/               # Core editor components
├── plugins/                   # Legacy plugins (migrating to features/)
├── nodes/                     # Legacy nodes (migrating to features/)
├── ui/                        # Shared UI components
├── context/                   # React contexts
├── hooks/                     # Custom hooks
└── utils/                     # Utility functions
```

## 🚀 Usage

### Emoji Feature

```tsx
import { EmojiPlugin, EmojiTypeaheadPlugin } from '@/features/emoji';

function MyEditor() {
  return (
    <LexicalComposer>
      <EmojiPlugin floatingAnchor={anchorElement} />
      <EmojiTypeaheadPlugin floatingAnchor={anchorElement} />
    </LexicalComposer>
  );
}
```

### Image Feature

```tsx
import { ImagePlugin, ImageNode, INSERT_IMAGE_COMMAND } from '@/features/image';

function MyEditor() {
  const [editor] = useLexicalComposerContext();
  
  const insertImage = (payload: ImagePayload) => {
    editor.dispatchCommand(INSERT_IMAGE_COMMAND, payload);
  };

  return (
    <LexicalComposer initialConfig={{ nodes: [ImageNode] }}>
      <ImagePlugin activeEditor={editor} />
    </LexicalComposer>
  );
}
```

### Mention Feature

```tsx
import { MentionPlugin, MentionNode } from '@/features/mention';

function MyEditor() {
  const fetchMentionOptions = async (query: string) => {
    // Fetch users, teams, channels, etc.
    return await api.searchMentions(query);
  };

  return (
    <LexicalComposer initialConfig={{ nodes: [MentionNode] }}>
      <MentionPlugin 
        floatingAnchor={anchorElement}
        fetchMentionOptions={fetchMentionOptions}
        minLength={1}
        maxResults={10}
      />
    </LexicalComposer>
  );
}
```

## 🎨 Styling

Each feature includes its own SCSS file with scoped styles:

```scss
// features/emoji/emoji.scss
.__RootEditor__ {
  .emoji-container {
    // Emoji-specific styles
  }
}

// features/image/image.scss  
.__RootEditor__ {
  .image-container {
    // Image-specific styles
  }
}

// features/mention/mention.scss
.__RootEditor__ {
  .mention {
    // Mention-specific styles
  }
}
```

## 🔧 Development Guidelines

### Adding a New Feature

1. **Create feature directory**: `features/{feature-name}/`
2. **Follow naming convention**: `{feature}.{type}.{ext}`
3. **Include all related files**:
   - Node definition (`{feature}.node.ts`)
   - Plugin (`{feature}.plugin.tsx`)
   - Types (`{feature}.types.ts`)
   - Styles (`{feature}.scss`)
   - Components as needed
   - Barrel export (`index.ts`)

### Example: Creating a Table Feature

```sh
features/table/
├── table.node.ts          # TableNode, TableRowNode, TableCellNode
├── table.plugin.tsx       # TablePlugin with commands
├── table.toolbar.tsx      # Table toolbar component
├── table.types.ts         # Table-related types
├── table.utils.ts         # Table utilities
├── table.scss             # Table styling
└── index.ts               # Exports
```

## 🧪 Testing

Each feature should include its own test files:

```sh
features/emoji/
├── emoji.node.test.ts
├── emoji.plugin.test.tsx
└── emoji.utils.test.ts
```

## 📦 Benefits of Colocation

1. **High Cohesion**: Related functionality is grouped together
2. **Low Coupling**: Features are independent and reusable
3. **Easy Maintenance**: Changes are localized to one directory
4. **Clear Responsibility**: File names indicate exact purpose
5. **Better DX**: Developers can focus on one feature at a time
6. **Scalability**: Easy to add, remove, or modify features

## 🔄 Migration Status

### ✅ Core Text Formatting (Internalized)

- ✅ **Paragraph**: Custom paragraph node with text alignment and indentation support
- ✅ **Heading**: H1-H6 headings with markdown syntax (## heading) and styling
- ✅ **Quote**: Blockquotes, pullquotes, and citations with author/source metadata

### ✅ Feature Nodes (Colocation Pattern)

- ✅ **Emoji**: Complete colocation implementation
- ✅ **Image**: Improved with colocation (legacy support maintained)  
- ✅ **Mention**: Migrated from plugins/ to features/
- ✅ **Hashtag**: Auto-detection and styling with colocation pattern
- ✅ **Link**: Auto-link detection with URL validation and colocation pattern

### 📋 Remaining Migrations

- 📋 **Tables**: Migrate to features/ with colocation
- 📋 **Code blocks**: Migrate to features/ with colocation
- 📋 **Lists**: Migrate to features/ with colocation
- 📋 **Legacy plugins**: Complete migration to features/

## 🤝 Contributing

When contributing to this editor:

1. Follow the colocation pattern for new features
2. Use the established naming conventions
3. Include comprehensive TypeScript types
4. Add feature-specific styling in SCSS files
5. Create exports using `export * from` pattern in `index.ts`
6. Maintain backward compatibility when refactoring

---

This architecture ensures our editor remains maintainable, scalable, and developer-friendly as it grows in complexity and features.