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

wizzywig

v0.1.3

Published

WizzyWig: commercial WYSIWYG rich text editor with a ProseMirror-style document model — the affordable TinyMCE alternative

Readme

WizzyWig

A modern, commercial WYSIWYG rich text editor built on a ProseMirror-style document model. Designed as a drop-in replacement for TinyMCE in business web applications, priced simply per domain — no per-seat or per-editor-load licensing, no external runtime dependencies. Free for development and evaluation; production use requires a license key from wizzy-wig.com.

Features

  • Text formatting — bold, italic, underline, strikethrough, sub/superscript, inline code, text and background color, remove formatting
  • Blocks — paragraphs, headings H1–H6, blockquotes, code blocks, alignment, line height, indentation
  • Lists — ordered, unordered, nested, and task lists
  • Tables — insert, add/remove rows and columns, merge/split cells, column widths, header cells (colspan/rowspan aware)
  • Media — image insert/upload, paste and drag-drop of image files, visual resize handles, alignment, configurable upload handler
  • Links — insert/edit dialog (Ctrl+K), validation, open-in-new-tab with automatic rel="noopener noreferrer"
  • Clipboard — copy/cut/paste with cleanup of Word and Google Docs markup
  • Security — allowlist-based HTML sanitizer applied to all HTML input (initial content, setContent, paste, drop)
  • UI — responsive toolbar, modal dialogs, right-click context menus, special character and emoji pickers, source-code view, dark mode
  • History — full undo/redo with keyboard shortcuts
  • Extensibility — first-class plugin system at both the state level (ProseMirror-style) and the editor level (toolbar items, dialogs, commands, shortcuts)

Quick start

npm install wizzywig
<div id="editor"></div>
import { Editor } from 'wizzywig';
import 'wizzywig/style.css';

const editor = new Editor({
  element: document.getElementById('editor'),
  content: '<p>Hello world</p>',
  toolbar: true,
});

editor.getContent();              // '<p>Hello world</p>'
editor.setContent('<h1>Hi</h1>'); // replace document (undoable)
editor.insertContent('<em>!</em>');
editor.focus();
editor.undo();
editor.redo();
editor.destroy();

Editor options

new Editor({
  element,                  // required: mount point
  content: '<p></p>',       // initial HTML
  toolbar: true,            // true = default layout, array = custom, false = none
  theme: 'light',           // 'light' | 'dark' | 'auto'
  editable: true,           // read-only mode when false
  autofocus: false,
  contextMenu: true,        // right-click menu
  media: {                  // image handling (false disables)
    upload: async (file) => uploadAndReturnURL(file),
    maxSize: 10 * 1024 * 1024,
    accept: ['image/png', 'image/jpeg'],
    onError: (message, file) => toast(message),
  },
  sanitizer: new Sanitizer({ /* custom allowlists */ }), // false disables
  plugins: [],              // extra state-level plugins
  use: [],                  // editor-level plugins (see below)
});

Events

editor.on('change', ({ editor }) => save(editor.getContent()));
editor.on('selectionchange', () => {});
editor.on('transaction', ({ transaction }) => {});
editor.on('focus', () => {});
editor.on('blur', () => {});
editor.on('destroy', () => {});
editor.off('change', handler);

Commands

Commands are functions of (state, dispatch) that return whether they apply. Run them through the editor:

import { toggleMark, setBlockType, wrapInList, insertTable } from 'wizzywig';

editor.exec(toggleMark('bold'));
editor.can(toggleMark('bold'));     // dry-run
editor.exec(setBlockType('heading', { level: 2 }));
editor.exec(wrapInList('bullet_list'));
editor.exec(insertTable(3, 3));

// Named commands (registered by plugins or at runtime):
editor.registerCommand('shout', toggleMark('bold'));
editor.execCommand('shout');
editor.canCommand('shout');

Toolbar

new Editor({ toolbar: ['undo', 'redo', '|', 'bold', 'italic', '|', 'link'] });

Register custom items:

import { registerToolbarItem } from 'wizzywig';

registerToolbarItem('shout', {
  label: '!!',
  tooltip: 'Shout',
  command: toggleMark('bold'),
  isActive: (state) => isMarkActive(state, 'bold'),
});

Built-in names: undo redo blocks bold italic underline strike code subscript superscript align-left align-center align-right align-justify bullet-list ordered-list task-list outdent indent blockquote hr link image table charmap emoji removeformat sourcecode plus table-* operations. '|' inserts a separator.

Dialogs

import { registerDialog } from 'wizzywig';

registerDialog('alert-box', (editor) => ({
  title: 'Insert alert box',
  fields: [
    { type: 'text', name: 'text', label: 'Message', required: true },
    { type: 'select', name: 'kind', label: 'Kind', options: [
      { label: 'Info', value: 'info' },
      { label: 'Warning', value: 'warning' },
    ] },
  ],
  onSubmit: (values, ed) => ed.insertContent(`<blockquote><p>${values.text}</p></blockquote>`),
}));

editor.openDialog('alert-box');

Plugins

Editor-level plugins bundle everything a feature needs:

import { registerEditorPlugin } from 'wizzywig';

registerEditorPlugin({
  name: 'mentions',
  toolbarItems: { mention: { label: '@', tooltip: 'Mention', onClick: (ed) => ed.openDialog('mention') } },
  dialogs: { mention: (ed) => ({ /* DialogSpec */ }) },
  commands: { 'insert-mention': myCommand },
  keymap: { 'Mod-Shift-2': myCommand },
  statePlugins: (ed) => [new Plugin({ props: { handleTextInput: ... } })],
  setup: (ed) => {
    const off = ...;
    return () => off(); // teardown on destroy
  },
});

new Editor({ element, use: ['mentions'] });

Markdown

Opt in with use: ['markdown']. Adds an MD toolbar item (include markdown in your toolbar layout), a Markdown dialog, plain-text Markdown paste, and helpers:

import {
  Editor,
  getMarkdown,
  setMarkdown,
  insertMarkdown,
  markdownToHTML,
  serializeMarkdown,
} from 'wizzywig';

const editor = new Editor({
  element,
  use: ['markdown'],
  toolbar: ['bold', 'italic', '|', 'markdown', 'sourcecode'],
});

getMarkdown(editor);                 // document → Markdown
editor.exec(setMarkdown('# Hi'));    // replace document
editor.exec(insertMarkdown('**x**')); // insert at selection

Supports headings, emphasis, strikethrough, inline code, fenced code, links, images, lists (bullet / ordered / task), blockquotes, horizontal rules, and tables (export).

State-level plugins follow the ProseMirror shape (props.handleKeyDown, handlePaste, handleDrop, handleClick, handleTextInput, state fields, filterTransaction, appendTransaction).

Theming

All colors come from CSS custom properties on .wysiwyg-theme. Switch with editor.setTheme('dark' | 'light' | 'auto') or override variables:

.my-editor.wysiwyg-theme {
  --wysiwyg-accent: #7c3aed;
  --wysiwyg-toolbar-bg: #faf5ff;
}

Security

All HTML entering the document (initial content, setContent, insertContent, paste, drop) passes through an allowlist sanitizer that strips scripts, event handlers, unsafe URLs (including obfuscated javascript: schemes), and unknown tags. Configure with new Sanitizer({ allowedTags, allowedSchemes, allowedStyles, ... }).

Licensing

WizzyWig is commercial software licensed per domain. Keys are purchased through the High Octane Brands portal; one minimal plan covers hosting and continued development of the editor.

Development and evaluation are free: on development domains (localhost, *.test, …) the editor runs fully featured with no network calls and no notices. On production domains, a key — passed as licenseKey or auto-detected from the CDN loader snippet — is checked in once per domain and the verdict cached for 7 days. The check-in sends only the license key, product slug, editor version, and current hostname; without a key the editor makes no network requests and instead shows a small unlicensed notice. Enforcement is deliberately gentle and fail-open: outages fall back to the last known verdict or to valid, and an invalid or missing license only ever shows the notice banner — editing is never disabled. Inspect the status via editor.getLicenseStatus() ('none' | 'dev' | 'unlicensed' | 'pending' | 'valid' | 'grace' | 'invalid').

new Editor({
  element,
  licenseKey: 'HOB-WYSIWYG-XXXX',          // hosted channel only
  licenseServer: 'https://.../check',      // optional override
});

Migrating from TinyMCE

| TinyMCE | wizzywig | | --- | --- | | tinymce.init({ selector }) | new Editor({ element }) | | editor.getContent() | editor.getContent() | | editor.setContent(html) | editor.setContent(html) | | editor.insertContent(html) | editor.insertContent(html) | | editor.execCommand('Bold') | editor.exec(toggleMark('bold')) | | editor.on('change', fn) | editor.on('change', fn) | | editor.mode.set('readonly') | editor.setEditable(false) | | toolbar: 'undo redo | bold' | toolbar: ['undo', 'redo', '|', 'bold'] | | editor.windowManager.open(...) | editor.openDialog({...}) | | editor.ui.registry.addButton | registerToolbarItem(name, spec) | | editor.destroy() | editor.destroy() |

Development

npm install
npm run dev        # demo at http://localhost:5173
npm test           # vitest suite
npm run typecheck
npm run build      # dist/ (ESM + d.ts + css)

Framework wrappers

See examples/react and examples/vue for thin wrapper components, and examples/vanilla.html for a plain-HTML page.

License

Commercial — see LICENSE. Free for development and evaluation; production use requires a per-domain key from wizzy-wig.com.