New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

@seolhun/firstage-editor

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@seolhun/firstage-editor

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

latest
Source
npmnpm
Version
0.1.9
Version published
Maintainers
1
Created
Source

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:

// 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 ✨

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

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

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

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

Image Feature

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

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:

// 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

  • Create feature directory: features/{feature-name}/
  • Follow naming convention: {feature}.{type}.{ext}
  • 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

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:

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

📦 Benefits of Colocation

  • High Cohesion: Related functionality is grouped together
  • Low Coupling: Features are independent and reusable
  • Easy Maintenance: Changes are localized to one directory
  • Clear Responsibility: File names indicate exact purpose
  • Better DX: Developers can focus on one feature at a time
  • 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:

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

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

Keywords

react

FAQs

Package last updated on 14 Aug 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts