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

lispgram

v0.6.2

Published

A lispy surface language compiler and SVG visualizer for NCF diagrams.

Readme

lispgram

lispgram is a small JavaScript package that gives you:

  • a user-facing lispy surface language
  • a compiler from that surface language into NCF
  • a lightweight SVG visualizer that can render either Lispgram source or raw NCF

NCF stays the internal representation. Author-facing source compiles into:

(cy
  (nodes ...)
  (edges ...))

Supported surface forms

; initialize a node
(P)

; initialize a node with a label
(P{hi})

; parent/child relationships
(X Y Z)
(X Y (Z P))
(X (Y (Z P)))

; arrows
(-> e A B)
(-> e{hi} A B)

; fan-out
(-> e A [B C D])

; fan-in
(-> e [A B C] D)

What this starter implements

  • nodes with optional {label} suffixes
  • parent/child trees compiled to (@parent X)
  • arrows compiled as NCF arrow carrier nodes with:
    • (@class "arrow")
    • (@class "arrowFrom")
    • (@class "arrowTo")
  • fan-in / fan-out expansion to edgeId_1, edgeId_2, ...
  • raw NCF passthrough
  • browser auto-initialization for <pre class="lispgram">...</pre> blocks
  • heuristic SVG routing for parallel, anti-parallel, and higher-order arrows
  • compound/container labels rendered only in the header band

Install

npm install lispgram

Browser usage

<pre class="lispgram">
(Y X)
(-> f A B)
(-> g B A)
(-> h X Y)
</pre>

<script type="module">
  import lispgram from "lispgram";
  lispgram.initialize({
  startOnLoad: true,
  showArrowGlyphs: true,
  clickToFocus: true
});
</script>

Library usage

import lispgram from "lispgram";

const source = `
  (Y X)
  (-> f A B)
  (-> g B A)
  (-> h X Y)
`;

const ncfString = lispgram.toNCF(source);
const ncfDoc = lispgram.toNCFDoc(source);

Render into a DOM node:

import { render } from "lispgram";

render(document.querySelector("#app"), `
  (P A B)
  (-> left A B)
  (-> right B A)
`);

API

parseDocument(source)

Parses Lispgram surface syntax into an AST.

compileLispgram(source)

Returns:

{
  ast,
  doc
}

where doc is the normalized NCF JS object.

toNCFDoc(source)

Compiles Lispgram source to an NCF document object. If the input is already raw NCF, it is parsed and returned.

toNCF(source)

Compiles Lispgram source into an NCF string.

parseNCF(source)

Parses raw NCF.

emitNCFDoc(doc)

Serializes an NCF doc object back to text.

render(target, source, options?)

Renders Lispgram or NCF into an SVG inside target.

initialize(options?)

Finds matching elements and auto-renders them.

Options:

{
  selector: ".lispgram",
  startOnLoad: true,
  showNCF: false,
  showArrowGlyphs: false,
  clickToFocus: true,
  arrowGlyphRadius: 5.75,
  width: "100%",
  height: 480
}

Per-diagram overrides are also available with data attributes:

<pre class="lispgram"
  data-lispgram-arrow-glyphs="true"
  data-lispgram-click-focus="true"
  data-lispgram-arrow-glyph-radius="6.5">
  (-> f A B)
</pre>

Notes

This package is intentionally small and readable. It is a good base for:

  • improving the layout engine
  • extending the surface language
  • adding richer styling directives
  • expanding support for higher-order arrows and editor UX

Dev visualizer

The package now includes a browser-based dev tool with Lispgram + raw NCF input, compiled/normalized NCF output, orthogonal routing, wiring overlay, arrow-node glyphs, click focus, zoom, and fit-to-view.

Run it from the repo with:

npm run dev:visualizer

Then open:

http://localhost:4173/examples/devtool.html

The dev tool accepts either Lispgram surface syntax or raw NCF. When the input is Lispgram, it compiles it to NCF first and shows the normalized NCF in the side panel.

It can also run as a standalone static page. examples/devtool.html now auto-loads the local ../src/index.js module when it is available and otherwise falls back to:

https://unpkg.com/lispgram/src/index.js

That means you can open the file directly, host it from any static site, or copy examples/devtool-standalone.html by itself and still use the package from unpkg.

Optional query param overrides:

  • ?module=local forces the local repo module
  • ?module=unpkg forces the unpkg module

Development

npm test

Focus behavior

When clickToFocus is enabled, clicking a node, compound, edge path, edge label, or arrow glyph focuses that element within its own diagram. clickToFocus defaults to true; set data-lispgram-click-focus="false" or pass clickToFocus: false to disable it.

Demo

go to repo root directory

python -m http.server

navigate to examples/basic.html in browser

Browser script Drop-in use


<pre class="lispgram">
  (lispgram
    (A)
    (-> e X Y)
  )
</pre>
<script type="module">
  import lispgram from "https://unpkg.com/lispgram/src/index.js";
    lispgram.initialize({
    startOnLoad: true,
    showArrowGlyphs: true,
    clickToFocus: true,
    arrowGlyphRadius: 5.75
  });
</script>