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

@stxt-lang/editor

v0.4.0

Published

The STXT editor for any web page: highlighting, live errors, grammar-driven completion and hover, on CodeMirror 6.

Readme

STXT Editor

The STXT editor for any web page: a <textarea> becomes an editor with highlighting, errors while typing, and completion and hover driven by the grammar.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stxt-lang/[email protected]/dist/stxt-editor.css">
<script src="https://cdn.jsdelivr.net/npm/@stxt-lang/[email protected]/dist/stxt-editor.js"></script>

<textarea id="doc" name="doc">
Recipe (com.example.cooking): Pancakes
	Serves: four
	Difficulty: Easy
</textarea>

<script>
	const template = `
Template (@stxt.template): com.example.cooking
	Structure >>
		Recipe (com.example.cooking):
			Serves: (1) NATURAL
			Difficulty: (?) ENUM [Easy, Medium, Hard]
`;

	const editor = StxtEditor.fromTextArea(document.getElementById("doc"), {
		grammars: [template],
		onChange: (text, diagnostics) => console.log(diagnostics),
	});
</script>

Serves: four is underlined: the template says NATURAL.

Status: 0.4.0. The API may still change before 1.0.

What it does

  • Highlighting from the parser of @stxt-lang/core. There is no grammar of the language here. Blocks that a grammar declares as MARKDOWN are highlighted as Markdown.
  • Errors while typing: syntax errors, and validation errors against the grammars.
  • Completion (typing, or Ctrl+Space): the nodes the grammar allows at that point, and the ENUM values.
  • Hover over a node shows what the parser and its grammar know about it, or only what the grammar says: its description, its type and its values.
  • Go to definition (Ctrl+Click or Ctrl+B on the name of a node; Cmd on macOS): to the line of the grammar that declares it.
  • Tab inserts indentation: a tab, or four spaces. Switching re-indents the document.
  • The textarea stays in sync: a form that contains it still submits the text.

Install

With a <script>

The package carries its files ready for a page, minified. Any CDN that serves npm packages serves them, under https://cdn.jsdelivr.net/npm/@stxt-lang/[email protected]/:

| File | Global | Size (gzip) | What it has | |---|---|---|---| | dist/stxt-editor.js | StxtEditor | 422 kB (134 kB) | The editor and the static highlighting, with CodeMirror and @stxt-lang/core inside | | dist/stxt-highlight.js | StxtHighlight | 76 kB (21 kB) | Only the static highlighting, without CodeMirror | | dist/stxt-editor.css | — | 6 kB (2 kB) | The styles of both | | dist/stxt-editor-dark.css | — | 7 kB (2 kB) | The same styles with the dark theme, instead of stxt-editor.css |

With a bundler

npm install @stxt-lang/editor
import { fromTextArea } from "@stxt-lang/editor";
import "@stxt-lang/editor/stxt-editor.css";

The package is ESM, with type declarations. CodeMirror and @stxt-lang/core are dependencies, not bundled, so a page that already uses CodeMirror loads a single copy.

API

| Call | What it does | |---|---| | fromTextArea(textarea, options) | Replaces a textarea with an editor. The textarea is hidden and keeps the text | | mount(element, options) | Creates an editor inside an element |

Both return an Editor. Every option is optional:

| Option | Default | | |---|---|---| | value | the textarea's value, or "" | Initial text | | grammars | [] | Texts of @stxt.schema or @stxt.template documents | | validation | true | Validate against the grammars. Syntax errors are always reported | | indent | "tabs" | What Tab inserts: "tabs" or "spaces" | | readOnly | false | The text can be read, not edited | | lineNumbers | true | Show the line numbers. The marks of the lines with problems stay either way | | developerInfo | true | What the hover shows: the technical card, or only what the grammar says | | onChange | — | (text, diagnostics) => void, on creation and after every change | | onGoToDefinition | — | (location) => void, on go to definition into one of grammars |

A grammar written inside the document itself works too. A grammar that lives at a URL is fetched by the page:

const template = await (await fetch("/grammars/cooking.stxt")).text();
editor.setGrammars([template]);

| Method | | |---|---| | getValue() / setValue(text) | The text. setValue is an edit: it can be undone | | setGrammars(texts) | Replaces the grammars and validates again | | setValidation(enabled) | Turns validation on or off | | setIndent(mode) | Changes what Tab inserts and re-indents the document | | setReadOnly(readOnly) | Read-only or editable | | setLineNumbers(show) | Shows or hides the line numbers | | setDeveloperInfo(show) | Changes what the hover shows | | getDiagnostics() | The problems of the document | | getGrammarDiagnostics() | The problems of each grammar, in order | | focus() / destroy() | destroy removes the editor and shows the textarea again | | view | The CodeMirror EditorView |

A diagnostic is { line, code, message, severity, source }:

| Field | | |---|---| | line | 0-based | | code | The stable error code of the ecosystem: INDENTATION_MIXED, INVALID_VALUE… | | severity | "error" or "warning" | | source | "syntax", "grammar" or "validation" |

Hover

| developerInfo | The hover shows | A node no grammar declares | |---|---|---| | true | The technical card: text block or inline node, level, value, canonical name, and from the grammar the type, the values, the children with their cardinality and the description | The card, without the grammar part | | false | What the grammar says, for whoever writes the document: the description, the type and, for an ENUM, the values | No hover |

const editor = StxtEditor.fromTextArea(textarea, {
	grammars: [template],
	developerInfo: false,
});

The description is the Description of the node in a schema, or its entry in the Description of a template.

Go to definition

A grammar written inside the document is on the page: the cursor goes to the line. The grammars of grammars are not, so the editor tells the page, and the page decides what to do:

const editor = StxtEditor.fromTextArea(textarea, {
	grammars: [template],
	onGoToDefinition: ({ grammar, line, namespace }) => showGrammar(grammar, line),
});

| Field | | |---|---| | grammar | Index of the grammar in grammars | | line | Line of that grammar, 0-based: the one that declares the node. Over a namespace, the one of the grammar's root | | namespace | The namespace the grammar defines |

Without onGoToDefinition, nothing happens for those grammars.

The pieces underneath

For more than one document per view, or another layout, the package also exports what the editor is made of:

  • Analyzer: parses and validates a set of documents. No DOM, no CodeMirror.
  • createStxtEditor, createStxtExtensions: the CodeMirror extensions.
  • setTokensEffect, toCmDiagnostics: to push an analysis into a view.

Static highlighting

For pages that show STXT and do not edit it, there is a highlighter without CodeMirror:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stxt-lang/[email protected]/dist/stxt-editor.css">
<script src="https://cdn.jsdelivr.net/npm/@stxt-lang/[email protected]/dist/stxt-highlight.js"></script>

<pre><code class="language-stxt">Recipe (com.example.cooking): Pancakes
	Serves: 4</code></pre>

<script>
	StxtHighlight.highlightAll();
</script>

| Call | What it does | |---|---| | highlightAll(selector, options) | Highlights every element that matches. selector defaults to pre code.language-stxt | | highlight(element, options) | Highlights one element in place | | highlightText(text, options) | Returns the HTML, without touching the DOM: it also works in Node, on a server or in a build |

The only option is grammars: with them, blocks that a grammar declares as MARKDOWN are highlighted as Markdown.

With a bundler: import { highlightAll } from "@stxt-lang/editor/highlight".

Themes

The colours are CSS custom properties on .stxt-editor (the editor) and .stxt-highlight (static highlighting). A page themes both by overriding them:

.stxt-editor,
.stxt-highlight {
	--stxt-node: #0d5cb6;
	--stxt-value: #d05a80;
	--stxt-comment: #008000;
	--stxt-background: #fff;
}

.stxt-editor {
	height: 22rem;
}

Without a height, the editor grows with the text. With one, it scrolls.

| Property | Colours | |---|---| | --stxt-node | Names of the nodes | | --stxt-block | The >> of a text block | | --stxt-namespace | Namespaces | | --stxt-value | Values | | --stxt-comment | Comments | | --stxt-muted | Punctuation, line numbers, secondary text | | --stxt-background, --stxt-text, --stxt-border | The box of the editor | | --stxt-gutter-background, --stxt-active-line, --stxt-active-gutter | The line numbers and the current line | | --stxt-selected, --stxt-code-background, --stxt-separator | The selected completion, inline Markdown code, the rule of the hover | | --stxt-font-mono, --stxt-font-body, --stxt-font-size | Fonts |

The defaults are the palette of https://stxt.dev.

Dark theme

stxt-editor-dark.css is the same stylesheet with a dark palette. A page loads it instead of stxt-editor.css:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@stxt-lang/[email protected]/dist/stxt-editor-dark.css">

With a bundler: import "@stxt-lang/editor/stxt-editor-dark.css".

To follow the colour scheme of the system, a page loads both, each with its media:

<link rel="stylesheet" media="(prefers-color-scheme: light)" href="https://cdn.jsdelivr.net/npm/@stxt-lang/[email protected]/dist/stxt-editor.css">
<link rel="stylesheet" media="(prefers-color-scheme: dark)" href="https://cdn.jsdelivr.net/npm/@stxt-lang/[email protected]/dist/stxt-editor-dark.css">

The dark palette is the same set of properties, so it is overridden the same way. Static highlighting only sets the colours of the text: the dark background of a <pre> is the page's.

Development

npm install
npm test        # lint + mocha
npm run build   # typecheck + lint, then dist/
npm start       # build and open examples/ at http://localhost:8080 (PORT changes it)
                # examples/index.html is the editor, examples/highlight.html the highlighter;
                # index-dark.html and highlight-dark.html are the same pages with the dark theme

License

MIT © stxt-lang