npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@seolhun/firstage-editor

v0.1.9

Published

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

Readme

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

  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

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

  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.