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

@sjawhar/proof-editor

v0.3.12

Published

Open-source collaborative markdown editor with provenance tracking and an agent HTTP bridge

Readme

@sjawhar/proof-editor

A standalone, embeddable build of Proof's collaborative Milkdown/ProseMirror editor — the mark/collab/heatmap plugin stack, without Proof's own app shell, auth, websocket bridge, or analytics. You bring a Yjs document (and, optionally, an Awareness instance) and get back an editor bound to it, plus a hook for turning user actions on comments, suggestions, and an "ask" mark into calls against your own backend instead of local, unconditional document mutations.

A companion @sjawhar/proof-editor/headless entry point exposes the same ProseMirror schema for Node/Bun: parse and serialize markdown without a DOM, for servers that need to read or write documents this editor can open.

Usage

import { createProofEditor } from '@sjawhar/proof-editor';
import '@sjawhar/proof-editor/style.css';

const handle = await createProofEditor(document.getElementById('editor')!, {
  ydoc,
  awareness,
  user: { name: 'Ada', color: '#ef4444' },
  onMarkAction: async (action) => {
    await fetch('/api/mark-actions', { method: 'POST', body: JSON.stringify(action) });
  },
});
import { createHeadlessProof } from '@sjawhar/proof-editor/headless';

const { schema, parseMarkdown, serializeMarkdown } = await createHeadlessProof();
const doc = parseMarkdown('# Hello\n\nSome **bold** text.');
console.log(serializeMarkdown(doc));

API

export interface ProofEditorUser {
  name: string;
  color: string;
}

export type SelectionBarActionKind = 'comment' | 'ask' | 'suggest';
export type PopoverActionKind = 'reply' | 'resolve' | 'unresolve' | 'accept' | 'reject' | 'delete';
export type MarkAction =
  | { kind: SelectionBarActionKind; markId: string; quote: string; from: number; to: number }
  | { kind: 'reply'; markId: string; text: string }
  | { kind: Exclude<PopoverActionKind, 'reply'>; markId: string };

export interface CreateProofEditorOptions {
  ydoc: Y.Doc;
  awareness?: Awareness | null;
  user: ProofEditorUser;
  readOnly?: boolean;
  onMarkAction?: (action: MarkAction) => void | Promise<void>;
  onMarkClick?: (markId: string) => void;
  onMarkHover?: (markId: string | null) => void;
  heatMapMode?: 'hidden' | 'subtle' | 'background' | 'full';
}

export interface ProofEditorHandle {
  view: EditorView;
  getMarkdown(): string;
  setMarkdown(markdown: string): void;
  markOffsets(): Map<string, number>;
  setReadOnly(readOnly: boolean): void;
  applyRemoteMarks(metadata: Record<string, StoredMark>, options?: { hydrateAnchors?: boolean }): void;
  removeMark(markId: string): void;
  focusMark(markId: string): void;
  destroy(): void;
}

export function createProofEditor(root: HTMLElement, opts: CreateProofEditorOptions): Promise<ProofEditorHandle>;

The selection-bar actions (comment | ask | suggest) apply a local mark before onMarkAction runs. If that promise rejects, the mark is removed. In the default popover mode, reply (with its text), resolve, unresolve, accept, reject, and delete report through the hook when it is present; without a hook, they use the editor's local mutation.

On coarse-pointer devices, the selection bar waits 400 ms after selection changes settle, then docks at the bottom of the viewport with safe-area spacing and 44 px touch targets. The browser's native text-selection menu remains above the selected text, while the editor actions stay out of its way.

Providing onMarkClick or onMarkHover enables margin mode. The editor reports interactions on mark spans, does not register the mark popover or arrow-comment composer, and leaves thread UI to the host. markOffsets() returns the top offset of each distinct mark's first span relative to the supplied root, in document order. setMarkdown() parses markdown with the editor schema and replaces the current document.

export interface HeadlessProofEditor {
  schema: Schema;
  parseMarkdown(markdown: string): ProseMirrorNode;
  serializeMarkdown(doc: ProseMirrorNode): string;
}

export function createHeadlessProof(): Promise<HeadlessProofEditor>;

The headless entry point does not construct a browser editor or require a Yjs document.

Typed blocks

Pass the document service's block schema to both createProofEditor and createHeadlessProof. Each schema type becomes a ProseMirror block node and uses the :::name{...} container form of generic directive syntax in Markdown. That is the only directive form the parser knows: inside a line, :name and ::name are ordinary text, so prose such as 16:25Z or a:b parses as those literal characters. A paragraph line that opens with ::name, :name{...}, or a malformed ::: is refused with the document service's own wording, so the editor and the service accept the same documents. Attribute values declared server: true are parsed and serialized unchanged; the editor exposes no attribute controls for them, and setBlockAttributes rejects them. A host renderer receives the typed node and schema definition and should render those values read-only.

ProofEditorHandle extends TypedBlockCommands:

export type TypedBlockAttributeValue = string | boolean | readonly string[];
export type TypedBlockAttributes = Readonly<Record<string, TypedBlockAttributeValue>>;

export interface TypedBlockCommands {
  insertTypedBlock(typeName: string, attrs?: TypedBlockAttributes): boolean;
  retypeBlock(blockId: string, typeName: string, attrs?: TypedBlockAttributes): boolean;
  setBlockAttributes(blockId: string, attrs: TypedBlockAttributes): boolean;
  blockMenuItems(): readonly TypedBlockMenuItem[];
}

insertTypedBlock creates the named typed block with an empty paragraph body and schema defaults at the current selection. retypeBlock preserves the target block's blockId and makes its existing block content the typed body's first child. Undo restores the original plain block and id. setBlockAttributes is for host-driven changes to client-owned attributes such as urgency and multiple.

blockMenuItems() returns one Insert <Name> item per declared type. When the selection is in a paragraph that type can contain, it also returns Turn into <Name>; hosts render these items in their own block menu. It returns no items when no blockSchema was supplied.

The browser entry exports createTypedBlockCommands for hosts that wrap their own editor state. The headless entry re-exports the block-schema types so a server and browser can share one schema definition.

Attribution

Built on Every's Proof SDK, MIT licensed. "Proof" is a product of Every; this package is an independent, unaffiliated build of its open-source editor core and is not the hosted Proof product.