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

@sobree/core

v0.1.17

Published

Embeddable, print-view-first WYSIWYG editor for .docx — framework-free core, plugin architecture, native OOXML round-trip.

Downloads

2,796

Readme

@sobree/core

Embeddable, print-view-first WYSIWYG editor for .docx. Framework-free core, plugin architecture, native OOXML round-trip.

→ Docs: docs.sobree.dev → Live editor: sobree.dev/try

Install

@sobree/core is the minimal editor kernel — AST + paginator + DOCX I/O + history + fonts. It ships with zero plugin packages. Install the plugins you want and pass them to createSobree().

For an interactive editor with toolbar, keyboard shortcuts, and zoom dock:

pnpm add @sobree/core @sobree/keyboard @sobree/block-tools @sobree/zoom-controls

For a headless / API-driven peer (LLM agent, automation):

pnpm add @sobree/core yjs

Use HeadlessSobree — the no-DOM counterpart of the browser editor. For multi-user collab, add @sobree/collab-providers (client glue) and run @sobree/collab-server somewhere.

Hello world

import { createSobree } from "@sobree/core";
import { keyboard } from "@sobree/keyboard";
import { blockTools } from "@sobree/block-tools";
import { zoomControls } from "@sobree/zoom-controls";
import "@sobree/core/tokens.css";

const editor = createSobree("#editor", {
  content: "# Hello\n\nStart typing.",
  plugins: [
    keyboard(),       // Cmd+B / Cmd+Z / …
    blockTools(),     // floating toolbar + gutter indicator
    zoomControls(),   // bottom-right zoom dock
  ],
});

editor.on("change", ({ doc }) => {
  console.log("body has", doc.body.length, "blocks");
});

createSobree() mounts the viewport, the paginated paper stack, runs each plugin's setup() against a shared context, and returns a single handle. Plugins are torn down in reverse-of-mount order on editor.destroy().

What's in the box

  • Native OOXML AST. Every node maps 1:1 to a <w:…> element.
  • Y.Doc backed. Every editor is backed by a Yjs Y.Doc; mutations mirror in via Y.Doc.transact. Embedders attach y-websocket / y-indexeddb / y-webrtc providers for persistence + collaboration. editor.ydoc is the escape hatch.
  • Pure paginator. TeX-style break selection, widow / orphan, keep-with-next, multi-section. No DOM, no I/O.
  • Per-peer undo / redo. Thin wrapper around Y.UndoManager — each peer's Cmd+Z reverses only its own edits via tracked Y origins; remote edits flow through but stay off the local stack. Selection restored from stackItem.meta.
  • OOXML font embedding. word/fontTable.xml round-trip + ODTTF obfuscation + OS/2 fsType licence check + runtime @font-face registration. Note: fsType is an advisory gate — the embedder is responsible for ensuring they have rights to ship any font they pass to editor.embedFont(...). Pass { allowRestricted: true } only when you do.
  • Minimal core, opt-in plugins. Toolbar (@sobree/block-tools), keyboard (@sobree/keyboard), zoom dock (@sobree/zoom-controls) ship as siblings. For multi-user collab: @sobree/collab-providers (client) + @sobree/collab-server (Node relay). @sobree/core has no plugin dependencies — only fflate for ZIP and yjs for the CRDT.
  • Y-protocol IS the wire. No separate RPC plugin. Headless callers (LLMs, automation) participate via HeadlessSobree — same commands, same Y.Doc.
  • Wire-ready surface. editor.commands.execute(name, args) is the single dispatch path used by keyboard, toolbar, and external transports.
  • JSON-clean projection. editor.getDocument() returns a JSON-clean SobreeDocument snapshot of the underlying Y.Doc — survives structuredClone and any wire.

Polymorphic content

createSobree("#editor", { content: "# Markdown" });          // seed string
createSobree("#editor", { content: docxBlob });              // .docx bytes
createSobree("#editor", { content: astLiteral });            // built with the doc builders
createSobree("#editor");                                      // empty

For the .docx path, await editor.ready resolves once the import lands.

Save back to .docx

const { blob } = editor.toDocx();

Or load a different file at runtime:

const { warnings } = await editor.loadDocx(file);

Documentation

License

MIT.