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

edodo-write

v0.9.3

Published

A Notion/Medium-style WYSIWYG editor whose single source of truth is Markdown. Type-to-format, a slash menu, a floating selection toolbar — framework-agnostic core with an optional React wrapper.

Readme

You edit rich text; you read and store Markdown. Type ## , - , [ ] , > , ``` and watch it transform as you go. Select text for a floating toolbar; press / for a grouped block menu; hover a block to drag it or open its menu; ⌘K opens a link popover. Framework-free core, optional React wrapper, and a plugin API for everything above the editing engine.

npm i edodo-write

Why

Most rich editors store bespoke JSON — hard to diff, grep, feed to an LLM, or move between systems. edodo-write keeps Markdown as the value. The rich surface is just a view: Markdown is parsed to HTML on load and every edit is serialised straight back, so the bytes you save are portable, human-readable and version-control friendly. Anything Markdown can't express (underline, toggles) is deliberately not offered — nothing you see can silently vanish from the saved value.

Features

  • Type-to-format — headings 1–6, bullet / numbered / to-do lists, quotes, code blocks, dividers, and inline **bold** *italic* `code` ~~strike~~.
  • Slash menu (Notion-style) — grouped, filterable, works in empty list items, /heading 1 with spaces works.
  • Images — paste a screenshot, drop a file, or upload / paste a URL from the /image popover; hosting is pluggable (uploadImage) with a zero-config data-URL fallback, and the saved Markdown is just ![alt](url).
  • Tables/table inserts a GFM table; type in cells, Tab/Enter walk them (Tab at the end adds a row), and the block menu adds/deletes rows and columns — the saved Markdown is a plain GFM table.
  • Math, diagrams, tags & embeds (plugins) — $x^2$ / $$…$$ TeX (KaTeX when installed), live ```edd/```mermaid diagram widgets via edodo-draw, #tag/@mention chips fed by your suggestion source, and bare-URL video/audio/bookmark embeds — each stored as plain, degradable Markdown.
  • Floating selection toolbar (Medium-style).
  • Link popover — ⌘/Ctrl+K, toolbar, or click a link to edit / open / remove; paste a URL over a selection to link it.
  • Block handles — drag the grip to reorder; click it for a block menu (Turn into, Duplicate, Copy as Markdown, Delete).
  • Markdown clipboard — copy puts Markdown on your clipboard; paste accepts Markdown or rich HTML (converted to Markdown, inserted as real blocks).
  • Undo / redo — a Markdown-snapshot history (⌘/Ctrl+Z, ⌘/Ctrl+Shift+Z, ⌘/Ctrl+Y), consistent across typing, commands, paste and drag.
  • Plugins — commands, input rules, keymaps, menu items, and paired markdown extensions per editor instance; collisions throw, runtime errors are isolated. First-party: highlight(), callout(), math(), diagrams()/edodoDraw(), tags(), embeds() — see First-party plugins.
  • Robust editing — Enter/Backspace/Tab do the Notion-like thing; a document normaliser repairs native contentEditable damage after every input; IME-safe input rules.
  • Clean GFM output — tables, task lists, strikethrough; fence contents preserved byte-for-byte; literal < escaped so prose like a<b>c round-trips. Round-trip stability is enforced by tests.
  • Interactive task lists — tick a checkbox, the Markdown flips [ ][x].
  • Light & dark themes via CSS variables; runtime setReadOnly toggle.
  • Tiny — 3 runtime deps (marked, turndown, the turndown GFM plugin). React is an optional peer.

Use it

Vanilla:

import { EdodoWrite } from "edodo-write";
import "edodo-write/styles.css";
import { strict as assert } from "node:assert";

const host = document.createElement("div");
document.body.appendChild(host);

const editor = new EdodoWrite(host, {
  value: "# Hello\n\nType **markdown** and watch it render.",
  onChange: (md) => console.log(md),
});

assert.equal(editor.getMarkdown(), "# Hello\n\nType **markdown** and watch it render.");
editor.destroy();

React:

import { useState } from "react";
import { EdodoWriteEditor, Markdown } from "edodo-write/react";
import "edodo-write/styles.css";

export function Notes() {
  const [md, setMd] = useState("# Hello");
  return (
    <div>
      <EdodoWriteEditor value={md} onChange={setMd} placeholder="Write…" />
      <Markdown value={md} /> {/* read-only render */}
    </div>
  );
}

Plugins — opt-in features with paired parse/serialise extensions, so plugin syntax round-trips like everything else:

import { EdodoWrite } from "edodo-write";
import { highlight, callout, math, edodoDraw, tags, embeds } from "edodo-write/plugins";
import { strict as assert } from "node:assert";

const host = document.createElement("div");
document.body.appendChild(host);

const editor = new EdodoWrite(host, {
  value: "Ship ==highlighted== prose with $E=mc^2$ inline.",
  plugins: [
    highlight(),                      // ==text== ↔ <mark>, Mod-Shift-H
    callout(),                        // > [!NOTE] callouts (GitHub alerts)
    math(),                           // $tex$ / $$…$$ — KaTeX when installed
    edodoDraw(),                      // ```edd + ```mermaid diagram widgets
    tags({ source: async () => [] }), // #tag menu fed by YOUR source
    embeds(),                         // bare-URL video/audio/bookmark embeds
  ],
});

assert.ok(editor.getHTML().includes("<mark>highlighted</mark>"));
assert.equal(editor.getMarkdown(), "Ship ==highlighted== prose with $E=mc^2$ inline."); // byte-for-byte
editor.destroy();

Writing your own is a plain object via definePlugin({ name, commands, inputRules, keymap, slashItems, markdown, … }) — the highlight() source is the ~50-line canonical example. A pure functional API (toHTML, toMarkdown, renderMarkdown, sanitizeHtml) is exported for SSR and headless use, and edodo-write/testing ships createCodec / assertRoundTrip so you can prove your plugin's round-trip in one line.

Run this repo (playground + docs)

git clone https://github.com/vivmagarwal/edodo-write.git
cd edodo-write && npm install
npm run dev       # http://localhost:5283 — live editor + Markdown output
npm test          # Vitest (unit + round-trip)
npm run test:e2e  # Playwright (real-browser behaviour)

Docs

Live site: https://vivmagarwal.github.io/edodo-write/

For LLMs and AI agents

The complete documentation ships as one deterministic, build-generated file — llms-full.txt (index: llms.txt). It is regenerated from docs/*.md on every build; a guide missing from it fails the build, so it can never drift from the docs. Point your agent at it for full context on embedding, the plugin API, and the Markdown dialect.

License

MIT © vivmagarwal