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

@arrisa/command

v1.0.29

Published

Arrisa — command

Readme

@arrisa/command

Editing commands, menu model, and structure helpers for Arrisa — pure transaction specs, keymaps, and toolbars without owning the view.

Install

pnpm add @arrisa/command

Depends on @arrisa/doc, @arrisa/state, @arrisa/phrases, and @arrisa/types. Pair with @arrisa/editor for a live view and @arrisa/history for real undo/redo.

Quick start

import {Command, toggleStrong, enter, undo, redo} from "@arrisa/command"
import {history, undo as histUndo, redo as histRedo} from "@arrisa/history"
import {Arrisa, KeyBinding} from "@arrisa/editor"
import {EditorState} from "@arrisa/state"

// Pure command → Transaction.Spec | false
let result = toggleStrong({state: editor.state}, null)
if (result) editor.dispatch(result)

// Or dispatch through the command protocol (handlers + view commands)
Command.dispatch(editor, enter)

// Wire history stubs so default keymap Mod-z / Mod-y work
let config: EditorState.Extension = [
  history(),
  Command.handler(undo, (ed) => histUndo({state: ed.state})),
  Command.handler(redo, (ed) => histRedo({state: ed.state})),
  KeyBinding.of({key: "Mod-b", run: toggleStrong}),
]

Features / concepts

Pure vs view commands

| Kind | Signature | When to use | |------|-----------|-------------| | Command.Pure | ({state}, param) => Transaction.Spec \| false | Unit-testable edits; no DOM geometry | | Command | (target: Arrisa, param) => boolean \| Transaction.Spec | Needs line/page geometry (moveToLineBoundary, moveVertically) |

Most high-level commands are pure. View-backed ones: deleteToLineEnd, deleteLine, moveByLine, moveByPage, moveToLineSide.

Dispatch protocol

  • Command.dispatch(editor, cmd) / Command.dispatch(editor, cmd, param) — runs registered handlers first (first truthy wins), then the command body. Specs are applied via editor.dispatch.
  • Command.bind(cmd, param) — fix a parameter for menus/keymaps.
  • Command.handler(cmd, override) — extension that overrides a command identity.

Menu model

Menu is a declarative toolbar model: items register via Menu.Item.source, templates expand "..." into children by parent + rank, and Menu.resolve produces a flat list of buttons, custom controls, submenus, and | separators. View packages (menuBar, floatingMenu) render the resolved tree.

Undo/redo stubs

undo / redo from this package always return false. They exist as command identities for keymaps and menus. Install real history with @arrisa/history and Command.handler (see playground wiring above).

Main public API

Protocol

| Export | Description | |--------|-------------| | Command | Command type + handler, bind, dispatch, Pure, Bound | | Arrisa | Minimal view surface (state, dispatch, line/vertical motion, scrollDOM, dom). Not the full @arrisa/editor class — that class implements this interface. | | Arrisa.coveredMargins | Facet: chrome margins for page-step motion (shared identity with the editor) |

Insert

| Export | Description | |--------|-------------| | insertText | Replace range with text + active marks (userEvent required in param) | | insertLineBreak | Schema line-break node or \n in preserve-whitespace parents | | enter | Enter key: new textblock / lift empty / split | | transposeChars | Swap grapheme clusters around a cursor | | replaceDoc | Replace the whole document with another Plot.Doc; cursor near start; userEvent: "set.doc"; does not record history |

Delete (high-level)

| Export | Description | |--------|-------------| | deleteUnit | Selection, or join + unit delete in "forward" | "backward" | | deleteWord | Same, preferring word delete at a cursor | | deleteToLineEnd | View: delete to visual line boundary | | deleteLine | View: delete the visual line |

Structure

| Export | Description | |--------|-------------| | setTextblockType | Change selected textblocks to a Plot.Tag | | wrapBlock / unwrapBlock | Wrap/unwrap selection in a block tag | | toggleBlock | Unwrap if present, else wrap | | toggleList | Put selected textblocks into / out of a list tag | | listIsActive | Predicate factory for list toolbar active state |

Marks

| Export | Description | |--------|-------------| | toggleMark | Toggle a mark on selection or stored marks. Param is Mark or Mark.Type (Type is resolved to .default) | | toggleEmphasis / toggleStrong / toggleUnderline | Convenience for @arrisa/types marks | | setAlignment | Text-align on textblocks (null / "start" clears default) | | setDirection | dir on textblocks (null clears) | | clearFormatting | Strip stored marks at an empty cursor, or every mark on nodes in the selection (does not unwrap blocks) | | applyLink | Add a sanitized Link mark on a non-empty range (sanitizeLinkHref; false if empty or unsafe) | | removeLinks | Remove Link marks in the selection (false when none) |

Mark exclusivity

| Export | Description | |--------|-------------| | markExclusivity | Extension: isolating mark types + Command.handler for toggleMark | | markExclusivityPolicy | Read combined policy from state | | markAllowedByExclusivity | Whether a mark may be applied under policy | | toggleMarkExclusive | Toggle with conflict stripping | | MarkExclusivityPolicy | {isolating: Mark.Type[]} |

import {markExclusivity} from "@arrisa/command"
import {Code, Strikethrough} from "@arrisa/types"

// Monospace / strike cannot stack with other formats
markExclusivity({isolating: [Code, Strikethrough]})

Motion

| Export | Description | |--------|-------------| | moveByUnit | Character/node step (dir, optional extend) | | moveByWord | Word step (visual left/right) | | moveByLine / moveByPage | View-backed vertical motion | | moveToLineSide | Visual line start/end | | moveToTextblockSide | Textblock start/end | | moveToDocSide | Document start/end | | selectAll | Select entire document | | collapseSelection | Collapse a non-empty selection to a cursor at head (false if already empty) |

History placeholders

| Export | Description | |--------|-------------| | undo / redo | No-op commands (false); override or use @arrisa/history |

Menu

| Export | Description | |--------|-------------| | Menu.Button.define / Menu.Button.toggleMark | Toolbar buttons | | Menu.CustomControl.define | Custom DOM controls | | Menu.Group.define / Menu.Group.top / .commands / .inline / .block / .insert | Groups + standard ranks | | Menu.Submenu.define / Menu.Submenu.textblockStyle | Submenus | | Menu.Template / group.template(...) | Template trees | | Menu.resolve(items, template?, suppress?) | Resolve to actionable list | | Menu.Item.source | Facet that collects registered items |

import {Menu, toggleStrong, Command} from "@arrisa/command"
import {Strong} from "@arrisa/types"

// Strong is the default Mark instance; callers may pass Strong or Strong.type
let boldBtn = Menu.Button.toggleMark({
  mark: Strong,
  parent: Menu.Group.inline,
  rank: 10,
  label: "B",
})

// boldBtn.extension installs the Menu.Item.source facet
// Custom button:
Menu.Button.define({
  run: Command.bind(toggleStrong, null),
  label: "Bold",
  parent: Menu.Group.inline,
  rank: 10,
  active: (state) => !!Strong.isInSet(state.sel.activeMarks),
})

Utils (building blocks)

Used by higher-level commands; also available for custom commands:

| Export | Module | |--------|--------| | deleteSelection, deleteEmptyTextblock, deleteBackward, deleteForward | delete | | joinBackward, joinForward, joinListItems | join | | liftEmptyBlock, splitTextblock | split | | joinBlocks, clearNonFitting, autoJoinBlocks, textblockChild | blocks | | findWrappable, wrapBlockRange, findUnwrappable, doUnwrapBlock | wrap | | selectedTextblocks, canAddMarkInRange | selection |

Layering / related packages

| Package | Role | |---------|------| | @arrisa/doc | Document model, changes, schema | | @arrisa/state | EditorState, transactions, selection | | @arrisa/types | Standard marks (Strong, Emphasis, …) used by mark helpers | | @arrisa/phrases | Phrase refs for menu descriptions | | @arrisa/history | Real undo/redo (wire via Command.handler) | | @arrisa/editor | View that implements the command Arrisa interface; menus, keymaps | | @arrisa/schema | Ready-made extensions that register Menu items and key bindings |

Layer: depends only on doc/state/types/phrases (layer 2). Does not import @arrisa/editor.

License

MIT