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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@psray/markdown-editor

v0.2.1

Published

A self-contained Markdown editor and renderer for Nuxt 4 — CodeMirror 6 authoring, markdown-it rendering with callout/checklist/spoiler blocks, and built-in translations.

Readme

@psray/markdown-editor

A Markdown editor and renderer for Nuxt 4, packaged as a module. CodeMirror 6 for authoring, markdown-it for rendering, and a set of site-safe content blocks — callouts, collapsible details, checklists with a live completion count, block and inline spoilers, colored text, colored underlines and sized images.

Translations for Japanese, English, Korean, Simplified and Traditional Chinese ship with the package, so it works with or without @nuxtjs/i18n.

Install

pnpm add @psray/markdown-editor
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@psray/markdown-editor'],
})

Then point Tailwind at the package. Tailwind v4 skips node_modules, so without this line the editor's own utility classes are never generated and the toolbar renders unstyled:

/* app/assets/css/main.css */
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@source "../../../node_modules/@psray/markdown-editor/dist";

Adjust the relative path to reach your project root's node_modules. Tailwind v4 and @tailwindcss/typography are peer dependencies — the package styles itself from your theme rather than shipping a competing one.

Use

<script setup lang="ts">
const content = ref('')
</script>

<template>
  <MarkdownEditor v-model="content" :disabled="saving" :error="error" />
  <RichContent :content="content" format="markdown" />
</template>

MarkdownEditor is the split-pane authoring surface; RichContent renders stored Markdown (or trusted HTML) for readers. MarkdownGuide is the syntax guide the editor's help banner links to.

The editor is heavy — CodeMirror and its Markdown grammar — so mount it as <LazyMarkdownEditor> when it lives behind a dialog or a tab.

Components

| Component | Props | | --- | --- | | MarkdownEditor | v-model (required), disabled, error, placeholder, guideUrl | | RichContent | content, format: 'markdown' \| 'html' | | MarkdownGuide | brand, imageSrc, imageAlt |

Auto-imported helpers

renderMarkdown, markdownHeadings, markdownContainers, markdownContainerSnippet, applyMarkdownEdit, applyMarkdownTextStyle, MARKDOWN_TEXT_COLORS, MARKDOWN_TEXT_SIZES, MARKDOWN_UNDERLINE_COLORS.

renderMarkdown runs the same markdown-it instance the preview uses, so server-rendered content and the live preview can never drift.

Headings and tables of contents

Every heading gets an id slugged from its text, numbered slug-1, slug-2 on repeats. Slugs keep letters from every script, so Japanese, Korean and Chinese headings stay linkable. They are computed from the source alone, so a #fragment link works on the server-rendered HTML rather than only after hydration.

markdownHeadings(source) returns those same headings as { id, text, level }[] — build a contents list from it rather than from a second Markdown parser, which is how a contents list ends up linking to anchors that do not exist:

<script setup lang="ts">
const toc = computed(() => markdownHeadings(article.value.content))
</script>

<template>
  <a v-for="item in toc" :key="item.id" :href="`#${item.id}`">{{ item.text }}</a>
  <RichContent :content="article.content" format="markdown" />
</template>

Options

export default defineNuxtConfig({
  modules: ['@psray/markdown-editor'],
  markdownEditor: {
    components: true,        // register the three components globally
    prefix: '',              // e.g. 'Psray' → <PsrayMarkdownEditor>
    css: true,               // inject the package stylesheet
    defaultLocale: 'ja',     // used when there is no i18n module, and as the last fallback
    guideUrl: '/docs/markdown', // the editor's help banner target; false hides it
    guidePage: false,        // set a path to mount MarkdownGuide as a route
    fallbackMessages: 'auto', // bundle defaultLocale's messages as a safety net
    i18n: {
      enabled: true,
      localeMap: { 'zh-CN': 'zh-Hans' },
    },
  },
})

Translations

With @nuxtjs/i18n installed, the package merges its catalogue into yours under the markdown.* namespace, for every locale code you have configured that it has a translation for. Your own catalogue takes precedence, so redefining a key rewords the editor:

{ "markdown": { "editor": { "help": "How to write a guide" } } }

If you spell a language differently, map it:

markdownEditor: { i18n: { localeMap: { 'zh-CN': 'zh-Hans', 'zh-TW': 'zh-Hant' } } }

Locale codes the package has no translation for — and hosts with no i18n module at all — fall back to the bundled defaultLocale messages rather than rendering raw keys. Those messages are only shipped when they could actually be reached: a host running @nuxtjs/i18n whose locales this package fully covers pays nothing for them. Force the decision either way with fallbackMessages.

Syntax

Everything CommonMark supports, plus:

::: info Before you begin
Requires chapter 3.
:::

::: checklist Collectibles
- [x] Found
- [ ] Missing
:::

::: details Hidden by default
Body text.
:::

::: spoiler Ending
Body text.
:::

::: steps
1. First
2. Second
:::

Inline spoiler: ||hidden text||
Highlight: ==marked==
Color, size, underline: [text]{color=red size=large underline=blue}
Image sizing: ![alt](/image.png){width=320}

Containers also nest — open the outer one with ::::.

html: false is set on the renderer and every attribute goes through a fixed allow-list, so community-authored Markdown cannot inject elements or styles. Links are rendered target="_blank" rel="noopener noreferrer".

Development

pnpm install
pnpm dev          # playground at localhost:3000
pnpm test
pnpm build

The playground deliberately ships no locale files of its own, so every string on screen comes from the module's catalogue — which is the only honest test of the i18n:registerModule wiring. It also configures zh-CN (exercising localeMap) and fr (exercising the bundled fallback).