Reference Manual

This is the reference manual for Wordgard. It is a complete list of every type and value in the library's public interface.

Wordgard is published as a package ("wordgard") that exports multiple separate modules with names like "wordgard/doc" or "wordgard/editor". The modules are split in such a way that each defines a self-contained part of the system. In some situations, such as manipulating documents without an editor, you'll only need a few of them. To set up a full-featured editor, you'll need most.

These are the modules:

Setting up a simple editor might look like this:

import {Wordgard, defaultKeymap, menuBar} from "wordgard/editor"
import {fullSchema} from "wordgard/schema"
import {history} from "wordgard/history"

const editor = Wordgard.create({
  parent: document.body,
  doc: `<p>The initial document</p>`,
  config: [fullSchema(), defaultKeymap, history(), menuBar()]
})

wordgard/doc

This module defines the library's document data structure, change model, and some helper functionality for working with those.

Document

A document is a tree of nodes. It is made up of these elements:

  • Nodes can be plots (nodes with content nodes) or leaves. The top document node is a plot.
  • Each node has a type.
  • Marks are pieces of information attached to nodes. They are used for things like text style, image alt text, or block alignment.
  • Tags describes a node's nature, it combines a node type, an optional parameter value, and a set of marks. Leaf nodes are tags, plot nodes have tags.
type Node = Plot | Leaf

A node in the document is either a plot (which may have content) or a leaf node.

interface Node.Shared {

The interface shared by both Leaf and Plot.

  name: string

The name of this node's type.

  tag: Node.Tag

The node's tag. For leaves, this is the leaf itself, for plots, the plot tag.

  length: number

The length of this node. For a plot, this is its contentLength plus 2 (for the open and close tokens), for leaves this is 1, except for text leaves, where it is the length of the text.

  marks: Mark.Set

The set of marks for this node.

  mark<Value>(mark: Mark.Type<Value>): Value | undefined

Get the value of the given mark for this node, if any.

  eq(other: Node): boolean

Compare this node to another node.

  withMarks(marks: Mark.Set): Node

Create a copy of this node with the given set of marks instead of its current mark set.

  isLeaf: boolean

True when this is a leaf node. TypeScript will automatically narrow from Node to Leaf after you check this.

  isText: boolean

Tests whether this is a text leaf.

  isPlot: boolean

True when this is a Plot.

  toJSON(): Node.JSON

Convert this node to its JSON-serializeable representation.

}
type Node.Type<T = unknown> = Leaf.Type<T> | Plot.Type<T>

A node type can be either a leaf type or a plot type.

type Node.Type.Ref<T> = Plot.Type<T> | Leaf.Type<T> |
  Plot.Tag<T> | Leaf<T>

Used as input type by some functions acting on node types, so that you can pass either a bare type or a singleton leaf or plot tag.

Node.Type.get<T>(ref: Node.Type.Ref<T>): Node.Type<T>

Get the type referred to by a reference.

type Node.Tag = Leaf | Plot.Tag

A tag is a node type with a parameter and a set of marks. For leaves, the entire node is the tag. For plots, it is a separate object in the tag property.

interface Node.Tag.Shared {

The interface shared by leaves and plot tags.

  type: Node.Type<Param>

The type of the tag.

  param: Param

The tag parameter. Will be null for parameter-less types.

  marks: Mark.Set

The set of marks for this tag.

  name: string

The name of the tag's type.

  mark<Value>(mark: Mark.Type<Value>): Value | undefined

Find the value of the given given make type in this tag's set of marks, or return undefined if it isn't present.

  eq(other: Node.Tag): boolean

Compare this tag to another tag.

  isLeaf: boolean

Test whether this is a leaf.

  isPlot: boolean

Test whether this is a plot tag.

  is<T>(type: Leaf.Type<T>): this is Leaf<T>
  is<T>(type: Plot.Type<T>): this is Plot.Tag<T>

Test whether this tag is of the given type.

  isText: boolean

Holds true when this is a text leaf.

  toJSON(): Node.JSON

Convert this tag to a JSON-serializeable object.

}
type Node.Tag.For<
  Type extends Node.Type.Ref<any>
> = Type extends Leaf.Type<infer T> ? Leaf<T> : Type
  extends Plot.Type<infer T> ? Plot.Tag<T> : Type

Deduce a tag type for a given node type or tag.

interface Node.Spec {

Shared fields between Leaf.Spec and Plot.Spec.

  inline?: boolean

Whether this node is an inline or a block node. Defaults to block.

  defaultParam?: Param

The default parameter value for the node type. Only meaningful when the type is being defined directly, rather than as a singleton tag.

  validate?: string | ((param: Param) => void)

A function or type name used to validate this tag's parameter value. This will be used when deserializing the attribute from JSON. When a string, it should be a |-separated string of primitive types ("number", "string", "boolean", "null", and "undefined"). The library will raise an error when the value is not one of those types. When a function, it should raise an error if the value doesn't have the expected type or shape.

  group?: Node.Group | readonly Node.Group[]

Assign one or more groups to this node type. Groups are used when specifying allowed content for a plot. Schema overrides can change a node's set of groups.

  role?: Node.Role | readonly Node.Role[]

Roles to add to this node type, which mark it as having a certain semantic role, such as being a list.

  shape: Shape.Element<Param> | Shape.Structure<Param>

The default DOM/HTML shape of this node. This will determine what the node looks like, both in an editor an in serialized HTML form. In most cases, this also specifies the way the node is parsed when reading HTML content.

  parseRules?: readonly parse.Rule.Element<Param>[]

Extra parse rules to associate with this node type.

  selectable?: boolean

When set to true, nodes of this type, if they are a leaf or atom, can be selected by clicking them or moving the selection into them with the keyboard.

}
interface Node.JSON {

The JSON representation for a node or tag.

  type: string
  param?: any
  marks?: {[string]: any}
  content?: readonly Node.JSON[]
}
class Node.Group {

Groups are used to specify parent-child relationships between nodes, and valid targets for marks. You can use predefined groups provided as static properties on the class, or define your own for custom categories.

  parent: Node.Group | undefined

Groups may have a parent group. Membership of a group implies membership of its parent groups.

  static define(parent?: Node.Group): Node.Group

Define a custom node group.

  static All: Node.Group

A group that contains every node type.

  static Inline: Node.Group

All inline nodes are automatically assigned to this group.

  static Block: Node.Group

Block elements automatically get assigned to this group.

  static Leaf: Node.Group

The group of all leaf nodes.

  static Plot: Node.Group

The group of all non-leaf nodes.

  static Textblock: Node.Group

Block plots with inline content are tagged as textblocks.

  static Content: Node.Group

A group used for generic block content, such as paragraphs and lists. The basic schema uses this as the content type for the top level document, blockquotes, and list items.

  static TableCell: Node.Group

Group for the cell nodes in tables.

  static ListItem: Node.Group

Generic list item group.

}
type Node.Query = Node.Tag |
  Node.Group |
  Node.Type |
  readonly Node.Query[] |
  {and: readonly Node.Query[]}

Describes a set of node types. Can be either a single tag or type, which matches exactly that type (tags are assumed to be singleton tags—only their type is used), a reference to a node group, or a combination of multiple of those. An array indicates the union of all the groups in the array (matches types that match any of the queries). An object with an and property indicates an intersection (must match all the queries).

class Node.Role {

Roles are used to add some semantic information to node types. You can define your own, and use the hasRole method to check whether a given node has the role attached.

  static define(): Node.Role

Define a new role.

  static Code: Node.Role

This role indicates that a plot contains code, and makes some commands behave differently inside such a plot.

  static List: Node.Role

Identifies a plot as a list container. This makes some commands treat the plot specially.

  static LineBreak: Node.Role

A single leaf type in a schema may have the LineBreak role, which identifies it as the canonical node that represents a line break. Nodes marked as line breaks will be parsed from and serialized to newline characters inside whitespace-preserving nodes.

}
class Leaf<Param = unknown> implements Node.Shared,
  Node.Tag.Shared<Param> {

A leaf node, which is a node with no content nodes. Used for things like text, images, line breaks, and so on. Counts as a Node.Tag.

  type: Leaf.Type<Param>

This leaf's type.

  static define(name: string, spec: Leaf.Spec<null>): Leaf<null>

Define a singleton leaf type, without parameter. If you need to store a parameter value in each leaf of the type, use Leaf.Type.define instead.

  tokenType: Token.Type.Node

In slices, leaf nodes count as node tokens.

  static text(
    text: string,
    marks: Mark.Set = Mark.none
  ): Leaf<string>

Create a text node with the given text and mark set.

}
class Leaf.Type<Param = unknown> {

Node type for leaves.

  name: string

The name of this node type.

  hasRole(role: Node.Role): boolean

Test whether this node has the given role.

  isInline: boolean

True when this is an inline node type.

  isBlock: boolean

True when this is a block node type.

  isSelectable: boolean

Whether this node is selectable.

  default: Leaf<Param> | null

A default leaf for this type. Available if the leaf was defined with Leaf.define, or a Node.Spec.defaultParam was given.

  spec: Leaf.Spec<any>

The spec used to define this type. Its type parameter is cleared to avoid this field making the class invariant (in the type system sense), which would prevent Leaf.Type<unknown> from being a supertype of specific leaf types.

  static define<T>(
    name: string,
    spec: Leaf.Spec<T>
  ): Leaf.Type<T>

Define a new leaf type.

  of(param: Param, marks: Mark.Set = Mark.none): Leaf<Param>

Create a leaf with this type.

  isLeaf: true

Used to narrow Node.Type values to Leaf.

  isPlot: false

Leaves are not plots.

}
interface Leaf.Spec extends Node.Spec<Param> {
  toText?: (node: Leaf) => string

Can be used to make leaves of this type show up in the output of textContent.

}
Leaf.Text: Leaf.Type<string>

The type of text leaves. Represents a series of characters with a given set of marks. The only leaf with a length that isn't always 1. Adjacent text leaves with the same marks are merged automatically.

class Plot implements Node.Shared {

Plots delimit parts of the document, giving a special meaning to the nodes inside them. They are defined by a tag and an array of content.

  tag: Plot.Tag

The tag that identifies this plot.

  content: readonly Node[]

The nodes in this plot.

  contentLength: number

The sum of the length of this plot's content nodes.

  type: Plot.Type

The type of this plot's tag.

  contentEq(other: Plot): boolean

Compare the content of this plot to the content of the given plot.

  inlineContent: boolean

Tells you whether the content of this plot is inline.

  isTextblock: boolean

True if this is a block node with inline content.

  isDoc: boolean

True if this is a document node.

  firstChild: Node | null

Get the plot's first child, if any.

  lastChild: Node | null

Get the plot's first child.

  iterate(
    from: number,
    to: number,
    f: (
      node: Node,
      pos: number,
      parent: Plot | null,
      index: number
    ) => boolean | undefined
  )
  iterate(f: (
    node: Node,
    pos: number,
    parent: Plot | null,
    index: number
  ) => boolean | undefined)

Iterate though the given range (or the entire document, when given only one argument), and call the given function on every node that overlaps the given range, outer nodes before inner nodes. When the function returns false for a node, descendents of that node are not iterated.

  nodeAt(pos: number): Node | null

Return the node that starts at the given offset from this node's content start, if any. Will not return text nodes.

  plotAt(pos: number): Plot | null

Return the plot at the given offset, if any.

  textContent(options: {
    from?: number

An optional start position, as an offset from the plot's content start.

    to?: number

An optional end position.

    blockSeparator?: string

Text to separate blocks with. Defaults to a single newline character.

    leafText?: string | ((node: Leaf) => string)

Override the way non-text leaves are converted to string.

  } = {}): string

Return the text content of this plot.

  tokenType: Token.Type.Node

Plot nodes count as node tokens in a Slice.

  static define(
    name: string,
    spec: Plot.Spec<null>
  ): Plot.Tag<null>

Define a singleton plot type. If the plot needs a parameter value, use Plot.Type.define instead.

  static defineDoc(spec: {
    inlineContent?: true | Node.Query,
    blockContent?: Node.Query,
    canBeEmpty?: boolean
  }): Plot.Type<null>

Define a document plot type. Exactly one of these must occur in a schema.

}
Plot.End: {

The end token for a plot. Used in slices.

  tokenType: Token.Type.Close
}
class Plot.Tag<Param = unknown>
  implements Node.Tag.Shared<Param> {

A plot tag holds the type of the plot, its parameter (if any), and a set of marks.

  create(content?: readonly Node[]): Plot

Create a plot with this tag and the given content.

  withMarks(marks: Mark.Set): Plot.Tag<Param>

Create a copy of this tag with the given marks.

  split(atEnd: boolean): Plot.Tag<Param>

Return a tag that represents content split off from the plot with this tag. Will respect the Mark.Spec.keepOnSplit mark property. atEnd should be set to true if the split happens at the end of the plot's content.

  tokenType: Token.Type.Open

A plot tag counts as an open token in a slice.

  inlineContent: boolean

True when this plot type contains inline content.

  isTextblock: boolean

True when this is a block plot with inline content.

  isDoc: boolean

Test whether this is a document plot.

}
class Plot.Type<Param = unknown> {

A type of plot.

  name: string

The name of this node type.

  hasRole(role: Node.Role): boolean

Test whether this node has the given role.

  isInline: boolean

True when this is an inline node type.

  isBlock: boolean

True when this is a block node type.

  isSelectable: boolean

Whether this node is selectable.

  default: Plot.Tag<Param> | null

A default tag for this plot type.

  isolating: boolean

Whether the Plot.Spec.isolating flag is set on this plot type.

  defining: boolean

The plot's Plot.Spec.defining flag.

  neutral: boolean

The plot's Plot.Spec.neutral flag.

  preserveWhitespace: boolean

Whether whitespace should be preserved inside this plot.

  orientation: "row" | "column"

The orientation of the content of the plot. Will be "row" for plots with inline content, and defaults to "column" for plots with block content unless explicitly set.

  spec: Plot.Spec<any>

The spec used to define this plot type.

  static define<T>(
    name: string,
    spec: Plot.Spec<T>
  ): Plot.Type<T>

Define a plot type.

  of(param: Param, marks: Mark.Set = Mark.none): Plot.Tag<Param>

Create a plot tag of this type with the given parameter and mark set.

  inlineContent: boolean

Tells you whether this plot type has inline content.

  isTextblock: boolean

True if this is a block plot with inline content.

  isDoc: boolean

True if this is a document plot type.

  isLeaf: false

Tells you that this is not a leaf type.

  isPlot: true

This is a plot type. Can be used to narrow Node.Type to Plot.Type.

  canBeEmpty: boolean

Tells you whether this plot type is allowed to be empty.

}
interface Plot.Spec extends Node.Spec<Param> {

Object used to define a plot type.

  blockContent?: Node.Query

When this node has block-level content, provide a query matching the nodes it may contain here. You generally don't want to use Node.Group.Block here, since specialized block types (like table cells or list items) should probably only be allowed in their designated parent plots.

  inlineContent?: true | Node.Query

When this node has inline content, provide a query specifying valid content. If set to true, any inline node may appear in this node.

  canBeEmpty?: boolean

Plots with block content, by default, require at least one child. You can set this to true to allow them to be empty. Plots with inlne content can always be empty.

  cursorBarrier?: boolean

Whether the sides of this plot act as a 'barrier' when normalizing a cursor position, which means that a separate cursor position exists at its boundary. By default, nodes that are isolating, whitespace-preserving, or both a leaf and a block count as barriers.

  defaultBlock?: boolean

Indicates that this type of block is the default generic block type in parent nodes where it may occur (which is appropriate for, for example, paragraphs tags). Default blocks should not have a required param. When not specified, the configuration precedence order determines which child type is the default.

  preserveWhitespace?: boolean

Controls whether whitespace inside this type of node should be preserved. Disables whitespace collapsing and the replacement of newlines with line break nodes in the parser and serializer. Defaults to false, unless the node has the Node.Role.Code role.

  isolating?: boolean

Isolating plots disallow some kinds of editing across their borders (such as backspacing or unwrapping). A table cell is an example of a node that you'd use this for.

  orientation?: "row" | "column"

Block containers are, by default, assumed to arrange their children vertically below each other ("column"). You can set this to "row" to tell the editor that this container's children are horizontally next to each other.

  defining?: boolean

Defining nodes are preserved (when possible) when their content is duplicated (dragged, pasted, etc) into a new position. Defaults to false.

  neutral?: boolean

Neutral nodes may be completely replaced when their entire content gets replaced. Defaults to !Plot.Spec.defining.

  autoJoin?: boolean | ((
    before: Plot.Tag,
    after: Plot.Tag
  ) => boolean)

Whether block nodes of this type should be automatically joined when they become adjacent through an edit. Defaults to false. Note that editing commands need to explicitly call autoJoinBlocks for joining to happen.

  preserveOnSplitAtEnd?: boolean

By default, splitting a textblock at the end will revert the new block to the default type of textblock at that position. Setting this to true on a textblock type will prevent that behavior.

  cursorInsideBounds?: boolean

For inline nodes with inline content, this determines whether there are normalized cursor positions directly inside the node. The default is to only have cursor positions right outside the node.

}
class Plot.Doc extends Plot {

Document plots are used as the top level plot in a document. They offer some additional methods and, unlike normal plots, their length property reports only the length of their content, without counting open/close tokens (because those are not part of the document).

  schema: Schema

The document's schema.

  length: number

The length of the document's content.

  resolve(pos: number): Pos

Resolve the given position in the document, returning an object describing its context.

  resolveNode(pos: number): Pos.Node | null

Resolve the node at the given position, providing information about its context.

  resolvePlot(pos: number): Pos.Plot | null

Like resolveNode, but only resolves plot nodes.

  contextAt(pos: number, maxDepth?: number): readonly Plot.Tag[]

Get the context stack (the array of wrapping plot tags, inner-to-outer) at the given position.

  slice(from: number, to: number = this.length): Slice

Create a slice of the content between from and to.

}
class Mark<Value = unknown> {

A mark has a type and a value. Some mark types without a meaningful parameter value (such as Emphasis), will only use a single mark object.

  type: Mark.Type<Value>

The type of the mark.

  value: Value

The parameter value. This may be something like the link target for a link mark, or the alignment side for a text alignment mark.

  eq(other: Mark): boolean

Compare this mark to another one. Parameter values are compared by structure.

  name: string

The name of this mark's type.

  static define(name: string, spec: Mark.Spec<null>): Mark<null>

Define a singleton mark, without parameter.

  addToSet(set: Mark.Set): Mark.Set

Add this mark to the given set. Will overwrite existing instances of the mark in the set, unless this is a set-valued mark.

  removeFromSet(set: Mark.Set): Mark.Set

Remove this mark from the given set.

  isInSet(set: Mark.Set): Mark<Value> | null

Test whether this mark is in the given set.

  static sameSet(a: Mark.Set, b: Mark.Set): boolean

Compare two sets of marks.

  static none: Mark.Set

The empty mark set.

}
class Mark.Type<Param = unknown> {
  default: Mark<Param> | null

The default mark for this type, if any.

  inclusive: boolean

Whether this is an inclusive mark.

  spanning: boolean

Whether this is a spanning mark.

  spec: Mark.Spec<any>

The spec used to define this mark. (Its type parameter is set to any to circumvent a typing issue where Mark<T> isn't a subtype of Mark<unknown>.)

  name: string

The name of the mark's type.

  of(value: Param): Mark<Param>

Create a mark of this type.

  removeFromSet(set: Mark.Set): Mark.Set

Remove the mark of this type from the given set, if present.

  isInSet(set: Mark.Set): Mark<Param> | null

Test whether there is a mark of this type in the given set. If so, return it.

  isElement: boolean

Whether this mark is rendered with an element.

  static define<Param>(
    name: string,
    spec: Mark.Spec<Param>
  ): Mark.Type<Param>

Define a mark type with the given parameter type.

}
interface Mark.Spec {

Configuration for marks.

  target?: Node.Query

Which node tags this mark may apply to, as node query. The default is {and: [Node.Group.Inline, Node.Group.Leaf]}.

  rank?: number

Determines the position of this mark relative to other marks. Should be a number between 0 and 100. Marks with lower rank appear first in mark set arrays, and are rendered around higher rank marks when rendered as an element. Ties are broken by name. Defaults to 100.

  inclusive?: boolean

Whether this mark should be active when the cursor is positioned at its end (or at its start when that is also the start of the parent node). Defaults to true.

  spanning?: boolean

Whether this mark can span across multiple nodes, or refers to an individual node. Only spanning marks can be added to text. Spanning marks with an element representation can be drawn as elements containing multiple nodes, unless another, lower-ranked mark requires the nodes to be wrapped separately. Defaults to true for specs with an element representation, false for specs with an attribute representation.

  keepOnSplit?: boolean | ((
    tag: Plot.Tag,
    atEnd: boolean
  ) => boolean)

Used by Plot.Tag.split to determine whether to keep this mark in the split-off tag. atEnd will be true if the split happens at the end of the node's content.

  keepOnTypeChange?: boolean | ((
    from: Node.Tag,
    to: Node.Tag
  ) => boolean)

Used by withMarksFrom to decide whether marks of this type are preserved after the type change.

  defaultParam?: Param

A default value for the parameter. If given, a mark with this parameter will be stored in Mark.Type.default.

  validate?: string | ((value: Param) => void)

A function or type name used to validate parameters of this mark. See Node.Spec.validate.

  set?: Param extends readonly (infer Content)[] ? {compare: (
    a: Content,
    b: Content
  ) => number} : never

A mark parameter can be set-valued, which changes how adding and removing marks of that type works. This requires the mark parameter to be an array type. When adding a set-valued mark to a mark set, the value of the mark in the new set is the union of the values of its original value and the added mark. Similarly, when removing such a mark, only the values in the parameter of the removed mark are removed from the mark in the set (except when there are none left, in which case the mark is removed entirely).

compare should be a function that compares two values and returns 0 if they are the same, or an ordering number otherwise. This is used to sort and compare the values.

  shape: Shape.Element<Param> | Shape.Attribute<Param> |
    Shape.Attributes<Param>

A mark can be either represented with a wrapping element, or with one or more attributes added to the affected nodes.

  parseRules?: readonly parse.Rule<Param>[]

A set of parse rules for this mark. The mark field for these will automatically be defaulted to the mark type itself.

}
type Mark.Set = readonly Mark[]

A set of marks is a sorted array in which a given mark type can occur at most once.

Shapes

Node and mark shapes (the way they are converted to and from DOM/HTML structure) can be defined with these data structures.

type Shape.Element<Param> = {

Declares the shape of a node or mark to be a simple element. A parse rule can automatically be derived for it if the node or mark has a default parameter or a read function is defined to determine the parameter.

  element: string

The element name to use.

  selector?: string

A selector to use in the parse rule. Defaults to the element name.

  attributes?: Record<string, string> |
    ((param: Param) => Record<string, string>)

Attributes to add to the element.

  readElement?: (element: Element) => Param | typeof parse.Reject

A helper to read a parameter value from the element. If this returns parse.Reject, the parse rule will not apply.

  atom?: boolean

When specifying the shape of a plot, this indicates whether this node is an atom, meaning its content isn't editable through the editor.

}
type Shape.Structure<Param> = {

Declares the shape of a node in a way that allows a more complicated shape than Shape.Element. This will not automatically create a parse rule, so you'll want to define those yourself.

  structure: Elt | ((param: Param) => Elt)

The structure as a tree of Elts. If this is for a plot that is not to be rendered as an atom, the structure should contain a hole for the content.

  atom?: boolean

If structure is a function, and the target node is a plot, use this to specify whether the plot should be rendered as an atom or with content.

}
type Shape.Attribute<Param> = {

Declares that a mark is represented with a specific DOM attribute. This allows a matching parse rule to be derived automatically in most situations.

  attribute: string

The name of the attribute.

  value: string |
    (Param extends string ? 0 : never) |
    ((param: Param) => string | null)

Its value. When given as 0, which is ony valid when the Param type is string, the value of the mark's parameter is used directly.

  readAttribute?: (value: string) => typeof parse.Reject | Param

An optional function that converts the value of the attribute back into a parameter value. Used in the parse rule.

  preferTarget?: string

If the target node may be a composite shape (rather than a single DOM element), you can provide a limited form of selector here to target a specific element in that shape. Node names and class names are supported, as in "img", "img.my-class", or ".class1.class2". If no matching element is found, the attributes will be added to the node's outer element, as normal.

}
type Shape.Attributes<Param> = {

Declares that a dynamic attribute or set of attributes should be added to nodes with this mark. Will not produce an implicit parse rule.

  attributes: Record<string, string> |
    ((param: Param) => Record<string, string>)

The attributes to add, either directly or as a function of the mark's parameter.

  preferTarget?: string

A selector for the preferred target element.

}
class Elt<T = string> {

This class describes a DOM element, its attributes, and its children. It is used in describing the structure of nodes and decorations.

Elements can provide a wrapping structure by having a content hole somewhere in their children, which indicates where the structure they wrap (typically a node's content) goes.

The type parameter indicates the set of additional leaf types. By default, it holds only string (as a shorthand for text nodes), but elements used in decorations also support custom widgets.

  tagName: string

The element's tag name. May be prefixed with "svg:" or "math:" to indicate an SVG or MathML element.

  attrs: Attributes

The set of attributes, as an array of strings.

  children: readonly (0 | Elt<T> | T)[]

The element's children.

  static create<T = string>(
    tagName: string,
    attrs: Attributes,
    children: readonly (0 | T | Elt<T>)[]
  ): Elt<Exclude<T, 0 | Elt<any>>>

Create an element. See also Elt.mk for a more ergonomic creation function.

  static mk<T = string>(
    name: string,
    children?: (0 | T | Elt<T>)[]
  ): Elt<Exclude<T, 0 | Elt<any>>>
  static mk<T = string>(
    name: string,
    attrs: Record<string, string>,
    children?: readonly (0 | T | Elt<T>)[]
  ): Elt<Exclude<T, 0 | Elt<any>>>

Create an element, specifying the attributes as an object. Both the set of attributesand the array of children are optional. The literal number 0 is used to indicate a content hole in the array of children.

  hasContent: boolean

True if this element or one of its children has a content hole.

  eqTag(elt: Elt<any>): boolean

Compare this element's tag name and attributes (not its children) to another element.

  eq(other: any): boolean

Compare this element (including its children) to another element.

  toHTML(): string

Convert an element with string content to an HTML string.

  toDOM(doc?: Document): Element | Text

Convert an element (with only string content) to a DOM tree.

}
class Elt.Fragment<T = string> {

A collection of elements or other content values.

  content: readonly (T | Elt<T>)[]
  static create<T = string>(
    content: readonly (T | Elt<T>)[]
  ): Elt.Fragment<T>

Create a fragment.

  toHTML(): string

Convert this fragment to an HTML string.

  toDOM(doc?: Document): DocumentFragment

Convert this fragment to a DOM fragment.

}
type Attributes = readonly string[]

Sets of attributes are stored in arrays of strings, with the even indices holding attribute names, the odd ones attribute values. The attributes are sorted by name.

Attributes.none: Attributes

The empty set of attributes.

Attributes.eq(a: Attributes, b: Attributes): boolean

Compare two attribute sets.

Attributes.merge(a: Attributes, b: Attributes): Attributes

Combine two attribute sets, with b having higher precedence when they set the same attribute.

Attributes.read(obj: Record<string, string | null>): Attributes

Convert an attribute object into a set.

Attributes.get(attrs: Attributes, name: string): string | null

Get the value of the given attribute.

Schema

Each document has a schema associated with it that configures the set of nodes and marks that can occur in it, and their relations.

class Schema {

A schema is a collection of node and mark types, including exactly one document type, plus an optional set of overrides that modify the relations between those elements. It determines what kind of elements may occur in documents that follow this schema, and where they can show up.

  elements: readonly Schema.Element[]

All the schema elements that make up this schema. Useful if you want to base another schema on this one.

  nodes: readonly Node.Type[]

The node types that are part of this schema.

  marks: readonly Mark.Type[]

Mark types used in this schema.

  docTag: Plot.Tag<null>

The plot tag used by documents in this schema.

  lineBreak: Leaf | null

The line break node defined in this schema, if any.

  doc(children: readonly Node[]): Plot.Doc

Create a document in this schema.

  validate(node: Node)

Validate that a node and its content conform to this schema. Will run automatically when creating a document.

  has(elt: Node.Type.Ref<any> | Mark.Type | Mark<any>): boolean

Test whether the given mark or tag type is included in this schema.

  matchNode(node: Node.Type, q: Node.Query): boolean

Test whether a node type matches the given node query.

  markAllowed(mark: Mark.Type, node: Node.Type): boolean

Test whether the given mark is allowed on the given node type.

  sharesContent(a: Plot.Type, b: Plot.Type): boolean

Returns true if there's at least one node type in the schema that may occur in both a and b.

  withMarksFrom<T extends Node.Tag>(from: Node.Tag, to: T): T

Returns a copy of to with all the marks from from that it doesn't already have, and that aren't dropped by the mark's Mark.Spec.keepOnTypeChange configuration.

  canContain(parent: Plot.Type, child: Node.Type): boolean

Check whether a given plot type can contain a given node type.

  defaultContentTag(parent: Plot.Type): Node.Tag | null

Return the first defaultable node tag that can occur as a child of parent.

  defaultContentPlot(parent: Plot.Type): Plot.Tag | null

Return the first defaultable plot tag that can be a child of parent.

  createAndFill(parent: Node.Tag): Node

Create a node from a tag, optionally adding a default child if this is a plot that cannot be empty.

  findWrapping(
    parent: Plot.Type,
    child: Node.Type
  ): readonly Plot.Tag[] | null

Find a set of tags that child must be wrapped in to be able to occur in parent. Will return the empty array if it fits directly, and null if it cannot occur at all.

  getMark(name: string): Mark.Type | undefined

Get the mark type with the given name in this schema.

  getNode(name: string): Node.Type | undefined

Get the node type with the given name.

  static define(spec: readonly Schema.Element[]): Schema

Define a schema from a set of schema elements. The set must contain precisely one document type, and no conflicting node or mark names.

  nodeFromJSON(json: Node.JSON): Node

Deserialize a node from its JSON representation.

  tagFromJSON(json: Node.JSON): Leaf | Plot.Tag

Deserialize a tag from its JSON representation.

  marksFromJSON(json: Record<string, any>): Mark.Set

Read a set of marks from their JSON representation.

  docFromJSON(json: Node.JSON): Plot.Doc

Read a document from JSON.

}
type Schema.Element = Node.Tag | Node.Type | Mark | Mark.Type |
  Schema.Override

A schema element is any node tag or type, mark or mark type, or override.

class Schema.Override {

Though nodes and marks are mostly self-contained, a few of their aspects can be overridden per schema.

  static markTarget(
    mark: Mark.Type | Mark,
    target: Node.Query | ((target: Node.Query) => Node.Query)
  ): Schema.Override

Create a schema override that changes the target nodes for a mark.

  static plotContent(
    plot: Plot.Type | Plot.Tag,
    content: Node.Query | ((content: Node.Query) => Node.Query)
  ): Schema.Override

Create a schema override that changes the content specification for a given node. Note that this can not change a node with inline content to block content or vice versa.

  static nodeGroup(
    node: Node.Tag | Node.Type,
    group: Node.Group | readonly Node.Group[]
  ): Schema.Override

Override the set of groups that a node may be part of.

}
class SchemaError extends Error {

Exception type used for errors related to schema definition.

}
class ValidationError extends Error {

Exception type used when validating content against a schema, when checking JSON input, or when validating change sets.

}

Changes

Change sets provide a precise description of how a document changed. Any modification to the editor's document goes through a transaction, which describes it using a change set.

class ChangeSet {

A change set contains a series of changes to a given document that produce a new document. They divide the document in a number of sections that are either kept as-is, have marks added or removed, or are replaced entirely by a Slice of new tokens.

Change sets store the length of their start document and will raise an error if you try to apply them to a document with a different length.

  sections: ChangeSet.Sections

Pairs of integers, with the first one representing the length of the section in the start document, the second either -1 for a preserved, -2 for a marked range, or a non-negative insertion length for a replacement.

  length: number

The length of the start document.

  newLength: number

The length of the updated document.

  empty: boolean

Returns true if this set makes no changes.

  eq(other: ChangeSet): boolean

Compare this change set to another one.

  apply(doc: Plot.Doc): Plot.Doc

Apply the changes to the given document, producing a new document. Will raise an error if the document length doesn't match or the change is not well-formed for this document.

The result of this method is cached, so applying the same change set to the same document multiple times is cheap.

  toJSON(): ChangeSet.JSON

Convert this change set to a JSON-serializeable representation.

  static fromJSON(
    schema: Schema,
    json: ChangeSet.JSON
  ): ChangeSet

Parse a JSON representation into a change set.

  transform(
    doc: Plot.Doc,
    other: ChangeSet,
    before: boolean = false
  ): ChangeSet

Perform an operational transformation on this change and the given other change. Both changes should start with the given document doc. Returns a modified version of the change that can be applied after the other change has been applied to doc.

By default, the semantics of conflicting changes are resolved as if this came after other. That means content inserted in the same position by both will put the content inserted by this last. You can set before to true to invert this, making this come before other. Setting this correctly is necessary to make the result of independently applied transformed changes converge.

  compose(other: ChangeSet): ChangeSet

Compose two change sets, where other starts from the document produced by this, into a single change set.

  invert(doc: Plot.Doc): ChangeSet

Compute the inverse of this change set. doc is the document that the change starts from. For a given change A, doc.apply(A).apply(A.invert(doc)) equals doc.

  correct(doc: Plot.Doc, local: boolean = false): ChangeSet

Returns the change itself if it can be applied to this document and produce a valid new document, or a modified version of the change that is correct.

  mapPos(pos: number, assoc?: 1 | -1): number
  mapPos(
    pos: number,
    assoc: 1 | -1,
    track?: ChangeSet.TrackMode
  ): number | null

Map a document position through this change, returning either the adjusted position, or null if a the tracked position is deleted.

The assoc parameter, which defaults to -1, decides to which side the position sticks. When content is inserted precisely at the mapped position, it will stay before it when assoc == -1, and move after it when assoc == 1.

By default, mapping will always return a new position, even if all the content around the position was deleted. You can pass a tracking mode to make it return null when either the token before, the token after, or both tokens around the position were deleted.

  findInserted(pred: (tag: Node.Tag) => boolean): number | null

Scan through the content inserted by this change until a tag that matches the predicate is found. If successful, return the position (in the new document) of the tag. This can be useful for when creating a new selection after a fitted change.

  touchesRange(from: number, to: number): boolean | "cover"

Returns true if any of the replaced ranges in this change set overlaps or is adjacent to the given range.

  iterChanges(
    replaced: (
      fromA: number,
      toA: number,
      fromB: number,
      toB: number,
      inserted: Slice
    ) => void,
    preserved?: (
      fromA: number,
      toA: number,
      fromB: number,
      toB: number,
      modifications: readonly ({add: Mark} |
        {remove: Mark})[] | null
    ) => void
  )

Iterate over the ranges in this changeset, calling replaced for ranges that have been replaced, and preserved for ranges that are either preserved as-is (when modifications is null) or only have marks modified.

  iterGaps(
    gap: (
      fromA: number,
      toA: number,
      fromB: number,
      toB: number
    ) => void,
    change?: (
      fromA: number,
      toA: number,
      fromB: number,
      toB: number
    ) => void
  )

Iterate over the sections of the document this change leaves unchanged or which have only mark changes. posA provides the position of the range in the original document, posB the position in the changed document.

  iterChangedRanges(range: (
    fromA: number,
    toA: number,
    fromB: number,
    toB: number
  ) => void)

Iterate over the ranges changed (either replaced or modified) by this change desc. Joins adjacent changed ranges together.

  pad(before: number, after: number): ChangeSet

Add skipped sections before and after this change set, so that it can apply to a larger document. Mostly useful when propagating changes from an editor displaying a smaller part of a document into the full document.

  clip(from: number, to: number): ChangeSet | null

Clip the set to only a sub-region. This can fail, if there are replacements across the region's sides, in which case the method returns null

  static create(doc: Plot.Doc, spec: ChangeSet.Spec): ChangeSet

Create a change set. All positions in the given change description refer to positions in the starting document.

  static empty(length: number): ChangeSet

Returns an empty change set for a document of the given length.

  static transform(
    doc: Plot.Doc,
    a: ChangeSet,
    b: ChangeSet
  ): {a: ChangeSet, b: ChangeSet}

Transform two change set starting from the same document over each other, returning two transformed change sets. The returned a can be applied after the b passed in, and the returned b can be applied after the a passed in, resulting the same final document on both sides. a is taken to happen before b when insertions at the same position need to be merged.

This method is slightly more efficient than transforming both steps separately.

}
type ChangeSet.Change = {

Representation of a single document change, as used in ChangeSet.Spec. Changes can either affect marks (when add or remove is present), or replace a part of the document (otherwise).

  from: number

The start position of the change.

  to?: number

The end position. When not given, this defaults to from for replacement changes, and from + 1 for changes that add or remove marks.

  insert?: Slice | readonly Token[]

Replace the given range with this slice.

  fit?: boolean | readonly Plot.Tag[]

For deletions or insertions where it isn't obvious that the replacement will produce a valid document, set this to true or a stack of context tags to make the library process the replacement to make sure it fits. Context tags (passed with the innermost tag first, as in contextAt may be used as wrappers when fitting the slice.

  add?: Mark

Add the given mark to this change's range. Cannot be combined with insert.

  remove?: Mark

Remove the given mark from this range.

}
type ChangeSet.Spec = ChangeSet | ChangeSet.Change | {
  correct: ChangeSet.Spec,
  local?: boolean
} | readonly ChangeSet.Spec[]

This type is used to describe a change set. A spec can be a single change, an existing change set, a set of changes wrapped in a correction scope, or an array of the same.

The from and to positions in the changes in a set spec all refer to the origin document. It is not necessary to 'compensate' for earlier changes in those specified later. If, for some reason, you have changes that should be applied after each other, create multiple change sets and compose them.

By default, the provider of changes vouches for their correctness. It is possible to create change sets that will error when you try to apply them, because applying them does not create a well formed document.

When making changes where you cannot guarantee that they fit, you should either use ChangeSet.Change.fit, which will try to change the range of a change to make it fit, or the {correct} form, which will combine the changes it is given, and then process them as a whole to make sure they produce a valid document. The local flag indicates that the effect of changes should be kept as narrow as possible—for example, that nodes opened but not closed by them should not extend to cover content after the change.

type ChangeSet.Sections = readonly number[]

The sections in a change set are represented as an array, with each pair of two numbers describing a changed section. The first number is the length of the section in the old document. The second number is -1 for unchanged sections, -2 for updated sections, and a non-negative number (the length of the inserted content) for replacements.

type ChangeSet.JSON = readonly (number | [
  number,
  Slice.JSON | readonly ({
    add: string,
    value: any
  } | {remove: string, value: any})[]
])[]

The JSON representation of a change set.

type ChangeSet.TrackMode = "before" | "after" | "around"

Modes available in mapPos to control whether null is returned on nearby deletions.

class Slice {

A slice represents a part of a document. It is used to represent inserted content in change sets, or things like clipboard content.

  length: number

The length of the slice's content.

  content: readonly Token[]
  static of(content: readonly Token[]): Slice

Create a slice.

  eq(other: Slice): boolean

Compare a slice to another one.

  slice(from: number, to: number = this.length): Slice

Create a sub-slice of this slice.

  concat(other: Slice): Slice

Concatenate this slice to another slice.

  textContent(options: {
    blockSeparator?: string,
    leafText?: string | ((node: Leaf) => string)
  } = {}): string

Get the text content of the slice's tokens.

  static empty: Slice

The empty slice.

  toJSON(): Slice.JSON

Convert this slice to a JSON-serializeable representation.

  static fromJSON(schema: Schema, json: Slice.JSON): Slice

Build a slice from its JSON representation.

}
type Slice.JSON = readonly (Node.JSON | ".")[]

A slice's JSON representation.

type Token = Node | Plot.Tag | {tokenType: Token.Type.Close}

The type of tokens in a slice. A plot tag represents the point where a plot is opened, Plot.End the point where a plot is closed, and nodes just represent the insertion of that node.

enum Token.Type {Open, Close, Node}

Tokens have a tokenType property holding oneof these values.

Resolved Positions

Positions in a wordgard document are expressed with offset numbers. On its own, such a number doesn't tell you much about its context. Resolved positions are data structures that describe a node or a position in a node, plus that node's parents (if any).

class Pos {

This class represents a resolved position.

  parent: Pos.Plot

The plot that the position points into.

  pos: number

The position itself.

  index: number

The index into its parent's content array. Note that if inText is non-zero, this is the index of the text node.

  inText: number

Text nodes don't count as parent plots. Rather, positions that fall inside a text node have a non-zero value here that provides the offset into the text node.

  matchingParent(pred: (plot: Plot) => boolean): Pos.Plot | null

Find the innermost parent plot for which the given predicate returns true.

  advance(distance: number, walk?: Pos.Walker): Pos

Move ahead through the document the given number of positions. Don't descend into nodes that fall entirely within the skipped range. If walk is given, call methods on it for each node entered, skipped, or left. Return a new position at the end of the range.

  walk(distance: number, walk: Pos.Walker): Pos

Move ahead through the document, always entering every plot. If walk is given, call methods on it for each node or node boundary passed. Return a new position.

  nodeAfter: Node | null

Get the node directly after this position. If inText is non-zero, return only the part of the text node that's after the position.

  nodeBefore: Node | null

Get the node directly before this position. If inText is non-zero, return only the part of the text node before the position.

  textblockParent: Pos.Plot | null

Get the nearest parent that is a textblock.

  depth: number

Get the depth of this position (the amount of plots that wrap it, not counting the document).

  parentAt(depth: number): Pos.Plot

Get the parent plot at the given depth.

  isAtStart(parent: Pos.Plot): boolean

Returns true if this position is right at the start of the given parent plot, or transitively at the start of its first child.

  isAtEnd(parent: Pos.Plot): boolean

Returns true if this position is right at the end of the given parent plot, or transitively at the end of its last child.

  doc: Plot.Doc

Get the document that this position points into.

  marks(across?: Pos): Mark.Set

Get the set of active inline marks at this position or, if across is given, the marks that would apply to content replacing the range between that this and across.

}
interface Pos.Walker {

Interface for the walker object that can be passed to Pos.advance and walk.

  skip(node: Node, pos: number, parent: Pos.Plot, index: number)

Called when a node is skipped over. Will only be called for leaves when using walk.

  enterPlot(
    node: Plot,
    pos: number,
    parent: Pos.Plot,
    index: number
  ): boolean | undefined

Called when a plot is entered.

  leavePlot(
    tag: Plot.Tag,
    pos: number,
    parent: Pos.Plot,
    index: number
  )

Called when leaving a plot.

}
class Pos.Node {

Represents the position of a node, with information about its parent plots.

  parent: Pos.Plot | null

The node's direct parent.

  node: Node

The node object.

  index: number

The node's index in its parent plot.

  before: number

The position before the node. Will raise an error if this is the document top node.

  after: number

The position after the node. Throws if this is a document.

  depth: number

The depth of this node (the number of parent nodes, not counting the document).

  doc: Plot.Doc

The document that this position points into.

  isFirst: boolean

Returns true if this is either a document node or the first node in its parent.

  isLast: boolean

Returns true if this is a document node or the last node in its parent.

  nextSibling: Node | null

The node before this one, if any.

  previousSibling: Node | null

The node after this node, if any.

}
class Pos.Plot extends Pos.Node {

Subclass of Pos.Node that points at a plot node.

  start: number

The position at the start of this plot's content.

  end: number

The position at end of this plot's content.

}

Parsing and Serialization

These functions allow conversion between HTML and Wordgard document formats. Note that, because not all of HTML is expressible in a given Wordgard schema, parsing is lossy.

parse(
  schema: Schema,
  doc: Element | DocumentFragment,
  options: parse.Options = {}
): Plot.Doc

Parse the given DOM structure as a document, using the given schema. By default the set of parse rules will be derived from the schema, but it is possible to pass in a custom set.

type parse.Options = {

Options that can be passed to parsing functions.

  collapseWhiteSpace?: boolean

Controls whether HTML-style whitespace collapsing is used (outside nodes that don't enable preserveWhitespace). Defaults to true.

  isOpen?: (elt: Element) => "start" | "end" | "start end" | null

Function used in in parse.slice to determine whether a given element is open (on either side).

  ruleSet?: parse.Rule.Set

The rule set to use. Defaults to the rule set derived from the schema.

}
parse.slice(
  schema: Schema,
  doc: Element | DocumentFragment,
  options: parse.Options = {}
): {slice: Slice, context: Plot.Tag[]}

Parse the given DOM structure as a slice.

type parse.Rule<Param = any> = parse.Rule.Element<Param> |
  parse.Rule.Attribute<Param>

Parse rules describe how DOM constructs should map to Wordgard document nodes. Many can be automatically derived from node and mark shapes, but it is also often useful to provide them directly.

interface parse.Rule.Element {

Describes a rule that matches elements by selector.

  selector: string

The CSS selector that should match the element.

  tag?: Leaf<Param> | Plot.Tag<Param> | Node.Type<Param>

If this is a node-creating rule, this holds the type of node to create when this rule matches. If this is a node type without default parameter, you must also define parse.Rule.Element.param or parse.Rule.Element.readElement.

  mark?: Mark.Type<Param> | Mark<Param>

Mark-creating rules should provide a mark or mark type here. The mark will be applied to the content of the element. Again, if the type lacks a parameter, parse.Rule.Element.param or parse.Rule.Element.readElement will be used to find it.

  ignore?: boolean | "skip"

Instead of creating a node or mark, rules may tell the parser to ignore a given element. true means discard it entirely, "skip" means ignore the element itself, but do parse its child nodes.

  param?: Param

A parameter for the tag or mark type.

  readElement?: (element: Element) => Param | typeof parse.Reject

A function that reads the parameter from the matched element. May return parse.Reject to indicate that the rule should not be applied to this element.

  marksFrom?: string

An optional CSS selector for finding an additional inner element to read marks from.

  contentElement?: string | ((elt: Element) => Element)

By default, when applying a rule for a plot or mark type, parsing continues with the element's direct children. You can pass a selector or function here to select another content element.

  ignoreContent?: string | ((elt: Element) => boolean)

Ignore DOM nodes matching this selector or predicate, when they appear in this plot's content element.

  precedence?: number

A number between -10 and 10 (inclusive) that specifies the relative precedence of this rule. Defaults to 0.

}
interface parse.Rule.Attribute {

An attribute parse rule matches an attribute, instead of an entire element. Such a rule can only create marks, not tags.

  attribute: string

The attribute to look for. May have the form style/color to look at a style property instead.

  value?: string

When given, this rule only matches when the attribute has this value.

  mark?: Mark.Type<Param> | Mark<Param>

The mark to create for this attribute, if any.

  clearMark?: (mark: Mark) => boolean

Remove a mark from the surrounding set of marks when this rule matches.

  ignore?: boolean

Can be set to true to cause the parser to ignore this attribute.

  param?: Param

A parameter to give to the mark type in parse.Rule.Attribute.mark.

  readAttribute?: (value: string) => typeof parse.Reject | Param

Read a parameter value from the attribute value. May return parse.Reject to prevent the rule from matching.

  consuming?: boolean

Controls whether other rules may match this attribute after this rule matches. Defaults to true.

  precedence?: number

A number between -10 and 10 (inclusive) that specifies the relative precedence of this rule. Defaults to 0.

}
class parse.Rule.Set {

A collection of parse rules. Usually derived from a schema.

  rules: readonly parse.Rule[]

The rules in this set.

  static of(rules: readonly parse.Rule[]): parse.Rule.Set

Create a rule set with the given parse rules.

  static fromSchema(schema: Schema): parse.Rule.Set

Create a rule set containing all the parse rules attached to nodes and marks in the given schema, as well as the rules that can be derived from the node and mark shape declarations.

}
parse.Reject: symbol

A special value that parse rule functions can return to block the rule from matching.

serialize(
  doc: Plot.Doc,
  options: serialize.Options = {}
): Elt.Fragment

Serialize a document to an array of elements and strings. These can be converted to a DOM structure with toDOM or an HTML string with toHTML. Will use the shapes specified in the node specs, unless overridden.

interface serialize.Options {

The options passed to serializer functions.

  emitNewlines?: boolean

Set this to true to replace nodes with the LineBreak role with newline characters.

  override?: (tag: Node.Tag) => Elt | null

Override the shape used for some tags. Return null to fall back to the node's default shape.

}
serialize.node(
  node: Node,
  options: serialize.Options
): string | Elt

Serialize a single node.

serialize.slice(
  slice: Slice,
  options: serialize.slice.Options
): Elt.Fragment

Serialize a slice.

interface serialize.slice.Options extends serialize.Options {

Options passed to serialize.slice.

  openAttr?: string

If given, the serializer will set this attribute to "start", "end", or "start end" for nodes that are open at the start and/or end of the slice.

  context?: readonly Plot.Tag[]

The slice's context. Will be used to determine the type of open nodes at the start of the slice.

  includeContext?: number

The amount of context nodes to include in the output. Defaults to 0, meaning only use those that are open at the start of the slice.

}

wordgard/state

This module defines an editor state data structure, a transaction abstraction for updating such a state, and a selection model.

Editor State

The editor state captures the situation in a Wordgard editor at any given point in time. The editor starts with a given state when created. On every state-affecting user action, it applies a transaction, which produces the new editor state.

class GardState {

The editor state tracks things like the current document, the selection, the configuration of the editor, and any extra state defined by extensions.

The state is a persistent (immutable) data structure. To update a state, you create a transaction, which produces a new state instance, without modifying the original object.

  static create(spec: GardState.Spec): GardState

Create a new state. You'll usually only need this when initializing an editor or loading a new document—updated states are created by applying transactions.

The schema of the state can be provided either via the configuration or by passing in an initialized document (which will have its own schema). If the configuration contains a document plot type, the schema from the configuration will be used, even if a document was provided.

  config: GardState.Configuration

The configuration for this state.

  doc: Plot.Doc

The current document.

  schema: Schema

The document's schema.

  selection: GardSelection

The current selection.

  sel: GardSelection.Resolved

A resolved form of the state's selection. Instead of raw positions, this object holds document position objects for head, anchor, from, and to.

  field<T>(field: GardState.Field<T>): T
  field<T>(
    field: GardState.Field<T>,
    require: false
  ): T | undefined

Retrieve the value of a state field. Throws an error when the state doesn't have that field, unless you pass false as second parameter.

  facet<Output>(facet: GardState.Facet.Reader<Output>): Output

Get the value of a state facet.

  update(spec: Transaction.Spec): Transaction

Create a transaction that updates this state.

  textblockMap(node: Pos.Plot): TextblockMap

Compute the textblock map for the given plot (which should be a textblock).

  toJSON(fields?: {[string]: GardState.Field<any>}): any

Convert this state to a JSON-serializable object. When custom fields should be serialized, you can pass them in as an object mapping property names (in the resulting object, which should not use doc or selection) to fields.

  static fromJSON(
    json: any,
    extensions: GardState.Extension,
    fields?: {[string]: GardState.Field<any>}
  ): GardState

Deserialize a state from its JSON representation. When custom fields should be deserialized, pass the same object you passed to toJSON when serializing as third argument.

  readOnly: boolean

Returns true when the editor is readOnly to be read-only.

  textLTR: boolean

Get the global text direction (true when left-to-right, false when right-to-left) for the document. Note that the direction of individual blocks can be overridden with textblockLTR.

  textblockLTR(plot: Plot): boolean

Return the text direction in a given textblock (by tag).

  isAtom(type: Node.Type): boolean

Tells you whether a node type is an atom (a leaf or a plot with an atomic shape).

  wordAt(pos: number, bias: 1 | -1 = 1): GardSelection.Text

Return the extent of the word around the given position, as a text selection.

  static reconfigure: Transaction.Effect.Type<
    GardState.Extension
  >

This effect can be used to reconfigure the root extensions of the editor. Doing this will discard any extensions appended, but does not reset the content of reconfigured compartments.

  static appendConfig: Transaction.Effect.Type<
    GardState.Extension
  >

Append extensions to the top-level configuration of the editor.

}
interface GardState.Spec {

Options passed when creating an editor state.

  doc?: string |
    Node.JSON |
    Plot.Doc |
    HTMLElement |
    DocumentFragment |
    ((schema: Schema) => Plot.Doc)

The initial document. When passing in a document node here, it is not necessary to include a schema in your configuration (though it is allowed, and the document content will be moved into that schema if it differs from the one on the given document).

All other forms require a schema from the configuration. A string or DOM structure will be parsed as HTML. Passing a string only works in the browser, where the library can use the browser's HTML parser. In other environments, you'll need to do the parsing yourself (for example with jsdom). A JSON node deserialized, and a function called to produce the document.

  selection?: GardSelection |
    GardSelection.Text.Spec |
    ((cx: GardSelection.Context) => GardSelection)

The starting selection. Defaults to a cursor at the start of the document.

  config?: GardState.Configuration | GardState.Extension

The configuration for this state, either as a resolved GardState.Configuration or as a set of extensions.

}
class GardState.Field<Value> {

Fields can store additional information in an editor state, and keep it in sync with the rest of the state. The type parameter indicates the type of value stored in the field.

  static define<Value>(
    config: GardState.Field.Spec<Value>
  ): GardState.Field<Value>

Define a state field.

  extension: GardState.Extension

State field instances can be used as Extension values to enable the field in a given state.

  init(create: (state: GardState) => Value): GardState.Extension

Returns an extension that enables this field and overrides the way it is initialized. Can be useful when you need to provide a non-default starting value for the field.

}
type GardState.Field.Spec<Value> = {

The options passed when defining a state field.

  create: (state: GardState) => Value

Creates the initial value for the field when a state is created.

  update: (value: Value, transaction: Transaction) => Value

Compute a new value from the field's previous value and a transaction. Should not mutate the old value (since that will change the existing state), but create a fresh one or return the old value unchanged.

  compare?: (a: Value, b: Value) => boolean

Compare two values of the field, returning true when they are the same. This is used to avoid recomputing facets that depend on the field when its value did not change. Defaults to using ===.

  provide?: (
    field: GardState.Field<Value>
  ) => GardState.Extension

Provide extensions based on this field. The given function will be called once with the initialized field. It is typically used with a facet's from method to create facet inputs from this field, but can also return other extensions that should be enabled when the field is present in a configuration.

  toJSON?: (value: Value, state: GardState) => any

A function used to serialize this field's content to JSON. Only necessary when this field is included in the argument to toJSON.

  fromJSON?: (json: any, state: GardState) => Value

A function that deserializes the JSON representation of this field's content.

}
class GardState.Facet<
  Input,
  Output = readonly Input[]
> implements GardState.Facet.Reader<Output> {

A facet is a labeled value that is associated with an editor state. It takes inputs from any number of extensions, and combines those into a single output value.

Examples of uses of facets are the read-only configuration, editor attributes, and update listeners.

Note that Facet instances can be used anywhere where GardState.Facet.Reader is expected.

Facets have an input type (the type of values provided for it), and an output type (the type you get when you read the facet) that defaults to an array of input values, but can be anything if a GardState.Facet.Spec.combine option is provided.

  default: Output

The output of the facet when it has no inputs.

  isStatic: boolean

True when this is a static facet.

  reader: GardState.Facet.Reader<Output>

A facet reader for this facet, which can be used to read it but not to define values for it.

  static define<
    Input,
    Output = readonly Input[]
  >(config: GardState.Facet.Spec<
    Input,
    Output
  > = {}): GardState.Facet<Input, Output>

Defines a facet with the given input an output types.

  of(value: Input): GardState.Extension

Returns an extension that provides the given value to this facet.

  compute(get: (state: GardState) => Input): GardState.Extension

Create an extension that computes a value for the facet from a state. The given function should only depend on the state, not any external non-constant inputs. Its return value will be kept on state update, unless any of the fields or facets (including document and selection) that it read are changed by the update, in which case it is called again.

In cases where your value depends only on a single field, you can use the from method instead.

  computeN(get: (
    state: GardState
  ) => readonly Input[]): GardState.Extension

Create an extension that computes zero or more values for this facet from a state.

  from<T extends Input>(
    field: GardState.Field<T>
  ): GardState.Extension
  from<T>(
    field: GardState.Field<T>,
    get: (value: T) => Input
  ): GardState.Extension

Shorthand method for registering a facet source with a state field as input. If the field's type corresponds to this facet's input type, the getter function can be omitted. If given, it will be used to produce the input from the field value.

}
type GardState.Facet.Spec<Input, Output> = {

Options passed when defining a facet.

  combine?: (value: readonly Input[]) => Output

How to combine the input values into a single output value. When not given, the array of input values becomes the output. This function will immediately be called on creating the facet, with an empty array, to compute the facet's default value when no inputs are present.

  compare?: (a: Output, b: Output) => boolean

How to compare output values to determine whether the value of the facet changed. When a new value for the facet is computed that that compares as equal to the old value, the old value is kept. So in most circumstances, facet values can be cheaply compared by identity to check for changes. Defaults to comparing by === or, if no combine function was given, comparing each element of the array with ===.

  compareInput?: (a: Input, b: Input) => boolean

How to compare input values to avoid recomputing the output value when no inputs changed. Defaults to comparing with ===.

  static?: boolean

Forbids dynamic inputs to this facet. Allows the facet to be read from a configuration.

  enables?: GardState.Extension |
    ((self: GardState.Facet<
      Input,
      Output
    >) => GardState.Extension)

If given, these extensions (or the result of calling the given function with the facet) will be added to any state where this facet is provided. (Note that, while a facet's default value can be read from a state even if the facet wasn't present in the state at all, the extensions won't be added in that situation.)

}
type GardState.Facet.Reader<Output> = {}

A facet reader can be used to fetch the value of a facet, through facet or as a dependency in Facet.compute, but not to define new values for the facet.

GardState.Facet.combineConfig<Config extends {}>(
  configs: readonly Partial<Config>[],
  defaults: Partial<Config>,
  combine: {[P in keyof Config]: (
    first: Config[P],
    second: Config[P]
  ) => Config[P]} = {}
): Config

Utility function for combining multiple configuration objects. defaults should hold default values for all optional fields in Config.

The function will, by default, raise an error when a field gets two values that aren't ===-equal, but you can provide combine functions per field to do something else.

class GardState.Configuration {

A state configuration stores a set of extensions, structured so that state updates can be performed efficiently.

  base: GardState.Extension

The set of extensions that this configuration is based on.

  staticFacet<Output>(facet: GardState.Facet<
    any,
    Output
  >): Output

Read the value of a static facet.

  static create(
    extensions: GardState.Extension
  ): GardState.Configuration

Create a configuration from the given set of extensions.

  schema: Schema | null

Get the schema defined by this configuration. Will be null if the schema does not contain a document plot type.

}
type GardState.Extension = {extension: GardState.Extension} |
  readonly GardState.Extension[]

Extension values can be provided when creating a state to attach various kinds of configuration and behavior information. They can either be built-in extension-providing objects, such as state fields or facet providers, or objects with an extension in its extension property. Extensions can be nested in arrays arbitrarily deep—they will be flattened when resolved into a configuration.

GardState.prec: {

By default extensions are registered in the order they are found in the flattened form of the configuration's extension tree. Individual extension values can be assigned a precedence to override this. Extensions that do not have a precedence set get the precedence of the nearest parent with a precedence, or default if there is no such parent. The final ordering of extensions is determined by first sorting by precedence and then by order within each precedence.

  highest: (ext: GardState.Extension) => GardState.Extension

The highest precedence level, for extensions that should end up near the start of the precedence ordering.

  high: (ext: GardState.Extension) => GardState.Extension

A higher-than-default precedence, for extensions that should come before those with default precedence.

  default: (ext: GardState.Extension) => GardState.Extension

The default precedence, which is also used for extensions without an explicit precedence.

  low: (ext: GardState.Extension) => GardState.Extension

A lower-than-default precedence.

  lowest: (ext: GardState.Extension) => GardState.Extension

The lowest precedence level. Meant for things that should end up near the end of the extension order.

}
class GardState.Compartment {

Extension compartments can be used to make a configuration dynamic. By wrapping part of your configuration in a compartment, you can later replace that part through a transaction.

  static define(): GardState.Compartment

Define a new compartment.

  of(ext: GardState.Extension): GardState.Extension

Create an instance of this compartment to add to your state configuration.

  reconfigure(
    content: GardState.Extension
  ): Transaction.Effect<unknown>

Create an effect that reconfigures this compartment.

  get(state: GardState): GardState.Extension | undefined

Get the current content of the compartment in the state, or undefined if it isn't present.

}
GardState.schemaElement: GardState.Facet<
  Schema.Element | readonly Schema.Element[],
  readonly Schema.Element[]
>

Facet used to register schema elements. If a configuration contains a document type, the editor's document schema will be derived from the content of this facet. (Otherwise, the state will try to use the schema provided via the GardState.Spec.doc option, or raise an error is none is provided.)

GardState.readOnly: GardState.Facet<boolean, boolean>

This facet controls the value of the readOnly getter, which is consulted by commands and extensions that implement editing functionality to determine whether they should apply. It defaults to false, but when its highest-precedence value is true, the state is considered read-only, and such functions won't change the document.

Not to be confused with editable, which controls whether the editor's DOM is set to be editable (and thus focusable).

GardState.textLTR: GardState.Facet<boolean, boolean>

Facet that indicates the document's default text direction. Note that this will not affect the editor CSS, and when the state's value disagrees with the direction set in the editor, the editor component will automatically inject an instance of this with a high precedence to align the state to the DOM. Still, if you know the direction in advance, it can be useful to set this, so that the direction is already accurate during initialization. Defaults to true.

GardState.textblockLTR: GardState.Facet<
  (plot: Plot) => boolean | null
>

Configure the text direction per textblock. All values given for this will be consulted in order of precedence, until one returns a non-null value. If none set a direction, the editor's base direction is used.

Schema elements like direction register an instance of this to make the editor aware of the meaning of the Direction mark.

GardState.visualCursorMotion: GardState.Facet<boolean, boolean>

Configure whether to use visual or logical cursor motion in bidirectional text. The default is visual, where pressing left/right arrow keys moves the cursor in the direction that corresponds to the arrow on key. When disabled, the motion uses the string index order instead.

class Transaction {

Changes to the editor state are grouped into transactions. Typically, a user action creates a single transaction, which may contain any number of document changes, may change the selection, or have other effects. Create a transaction by calling update, or immediately dispatch one by calling Wordgard.dispatch.

  startState: GardState

The state from which this transaction starts.

  changes: ChangeSet

The document changes made by this transaction.

  selection: GardSelection | undefined

The selection set by this transaction, or undefined if it doesn't explicitly set a selection.

  effects: readonly Transaction.Effect<any>[]

The effects contained in this transaction.

  scrollIntoView: boolean

Whether the selection should be scrolled into view after this transaction is dispatched.

  newSelection: GardSelection

The new selection produced by the transaction. If this.selection is undefined, this will map the start state's current selection through the changes made by the transaction.

  newDoc: Plot.Doc

The new document produced by the transaction. Contrary to .state.doc, accessing this won't force the entire new state to be computed right away, so it is recommended that transaction extenders use this property when they need to look at the new document.

  state: GardState

The new state created by the transaction. Lazily computed so that the state is resolved the first time this property is accessed.

  annotation<T>(
    type: Transaction.Annotation.Type<T>
  ): T | undefined

Get the value of the given transaction annotation type, if any.

  docChanged: boolean

Indicates whether the transaction changed the document.

  reconfigured: boolean

Indicates whether this transaction reconfigures the state (through a configuration compartment, reconfiguration, or appended configuration).

  isUserEvent(event: string): boolean

Returns true if the transaction has a user event annotation that is equal to or more specific than event. For example, if the transaction has "select.pointer" as user event, "select" and "select.pointer" will match it.

}
interface Transaction.Spec {

Describes a transaction when calling GardState.update or Wordgard.dispatch.

  changes?: ChangeSet.Spec

The changes to the document made by this transaction.

  selection?: GardSelection | GardSelection.Text.Spec | ((
    cx: GardSelection.Context,
    changes: ChangeSet
  ) => GardSelection | null)

When set, this transaction explicitly updates the selection. Offsets in this selection should refer to the document as it is after the transaction. If a selection can only be computed after the new document is available, you can pass a function here.

  effects?: Transaction.Effect<any> |
    readonly Transaction.Effect<any>[]

Attach effects to this transaction. Again, when they contain positions and this same spec makes changes, those positions should refer to positions in the updated document.

  annotations?: Transaction.Annotation<any> |
    readonly Transaction.Annotation<any>[]

Set annotations for this transaction.

  userEvent?: string

Shorthand for annotations: Transaction.userEvent.of(...).

  scrollIntoView?: boolean

When set to true, the transaction is marked as needing to scroll the current selection into view.

  sequential?: boolean

Only meaningful for specs that are combined with another transaction spec (via or by being returned from an {@link Transaction.extender extender. Normally, when specs are combined, the positions in changes are taken to refer to the document positions in the initial document. When a spec has sequental set to true, its positions will be taken to refer to the document created by the changes in the spec before it.

}
Transaction.merge(
  state: GardState,
  a: Transaction.Spec,
  b: Transaction.Spec
): Transaction.Spec

Merge two transaction specs into a single one, combining the effect of both.

Transaction.extender: GardState.Facet<
  (tr: Transaction) => Transaction.Spec | null
>

Facet used to register a hook that gets a chance to add to transactions before they are applied. If such a function returns a transaction spec, it will be combined with the original transaction (in the same way as the arguments to update).

When possible, it is recommended to avoid accessing state in an extender, since it will force creation of a state that will then be discarded again, if the transaction is actually extended.

This functionality should be used with care. Indiscriminately modifying transaction is likely to break something or degrade the user experience.

Extenders that may add document changes should generally not do anything for remote transactions, because doing so risks causing endlessly cascading changes or other confusion. It is possible to define extenders that are safe when activated on multiple peer (for example, duplicate deletions of the same content tend to converge), but it requires a lot of care.

Transaction.appender: GardState.Facet<(
  trs: readonly Transaction[],
  state: GardState
) => Transaction.Spec | null>

A transaction appender can create more transactions in response to a transaction. Transaction.append, which is called by the editor when dispatching a transaction, will call appenders on sets of transactions, allowing them to add another transaction. When another appender adds a transaction, extenders that already ran will be called again, but only with the transactions that were added after they ran.

Transaction.append(tr: Transaction): readonly Transaction[]

Apply transaction appenders, return an array of the original transaction plus any that were appended.

class Transaction.Annotation<T> {

Annotations are tagged values that are used to add metadata to transactions in an extensible way. They should be used to model things that effect the entire transaction (such as its time stamp or information about its origin). For effects that happen alongside the other changes made by the transaction, effects are more appropriate.

  type: Transaction.Annotation.Type<T>

The annotation type.

  value: T

The value of this annotation.

  static define<T>(): Transaction.Annotation.Type<T>

Define a new type of annotation.

}
class Transaction.Annotation.Type<T> {

Marker that identifies a type of annotation.

  of(value: T): Transaction.Annotation<T>

Create an instance of this annotation.

}
Transaction.foo: number
Transaction.time: Transaction.Annotation.Type<number>

Annotation used to store transaction timestamps. Automatically added to every transaction, holding Date.now().

Transaction.userEvent: Transaction.Annotation.Type<string>

Annotation used to associate a transaction with a user interface event. Holds a string identifying the event, using a dot-separated format to support attaching more specific information. The events used by the core libraries are:

  • "input" when content is entered
    • "input.type" for typed input
      • "input.type.compose" for composition
    • "input.paste" for pasted input
    • "input.drop" when adding content with drag-and-drop
  • "delete" when the user deletes content
    • "delete.selection" when deleting the selection
    • "delete.forward" when deleting forward from the selection
    • "delete.backward" when deleting backward from the selection
    • "delete.cut" when cutting to the clipboard
  • "move" when content is moved
    • "move.drop" when content is moved within the editor through drag-and-drop
  • "select" when explicitly changing the selection
    • "select.pointer" when selecting with a mouse or other pointing device
    • "select.all" when selecting the entire document
  • "undo" and "redo" for history actions
  • "insert" for actions that insert nodes
  • "mark" for actions that manipulate marks
    • "mark.add" when a command adds a mark
    • "mark.remove" when a command removes one
  • "split", "wrap", "settype", "wrap", "unwrap" for block manipulation actions

Use isUserEvent to check whether the annotation matches a given event.

Transaction.addToHistory: Transaction.Annotation.Type<boolean>

Annotation indicating whether a transaction should be added to the undo history or not.

Transaction.remote: Transaction.Annotation.Type<boolean>

Annotation indicating (when present and true) that a transaction represents a change made by some other actor, not the user. This is used, for example, to tag other people's changes in collaborative editing.

Transaction.appended: Transaction.Annotation.Type<boolean>

A flag set on transactions created by a transaction appender.

class Transaction.Effect<Value> {

Transaction effects can be used to represent additional effects associated with a transaction. They are often useful to model changes to custom state fields, when those changes aren't implicit in document or selection changes.

  value: Value

The value of this effect.

  map(mapping: ChangeSet): Transaction.Effect<Value> | undefined

Map this effect through a position mapping. Will return undefined when the changes deleted the effect.

  is<T>(
    type: Transaction.Effect.Type<T>
  ): this is Transaction.Effect<T>

Tells you whether this effect object is of a given type.

  static define<Value = null>(
    spec: Transaction.Effect.Spec<Value> = {}
  ): Transaction.Effect.Type<Value>

Define a new effect type. The type parameter indicates the type of values that his effect holds. It should be a type that doesn't include undefined, since that is used in mapping to indicate that an effect is removed.

}
Transaction.Effect.mapEffects(
  effects: readonly Transaction.Effect<any>[],
  mapping: ChangeSet
): readonly Transaction.Effect<any>[]

Map an array of effects through a change set.

class Transaction.Effect.Type<Value> {

A type of state effect. Defined with Transaction.Effect.define.

  of(value: Value): Transaction.Effect<Value>

Create an effect instance of this type.

}
interface Transaction.Effect.Spec {

Options passed when defining an effect.

  map?: (value: Value, mapping: ChangeSet) => Value | undefined

Provides a way to map an effect like this through a position mapping. When not given, the effects will simply not be mapped. When the function returns undefined, that means the mapping deletes the effect.

}

Selection

Editor selections are objects that derive from GardSelection. This module defines text and node selection types. It is possible for extension code to define custom selection types.

abstract class GardSelection {

The base class for editor selections. Actual selections will be a subclass of this—usually GardSelection.Text or GardSelection.Node.

  protected new GardSelection(
    anchor: number,
    head: number,
    goalColumn?: number
  )
  anchor: number

The anchor of the selection—the side that doesn't move when you extend it.

  head: number

The head of the selection, which is moved when it is extended (for example by moving the cursor while holding Shift).

  goalColumn?: number

The goal column (stored vertical offset) associated with a selection. This is used to preserve the vertical position when moving across lines of different length.

  from: number

The lower boundary of the selected range.

  to: number

The upper boundary of the range.

  empty: boolean

True when anchor and head are at the same position.

  isCursor: boolean

Returns true when this is an empty text selection.

  ranges: readonly {from: number, to: number}[]

The set of ranges covered by this selection, sorted. By default, this is just the selection's main from to to, but custom selection implementations can override it.

  replacementRange: {from: number, to: number}

The range that should be used when replacing this selection with other content (for example when typing or pasting over it) or deleting it. The default implementation returns this.from to this.to.

  domSelection: {
    head: number,
    headSide: 1 | -1,
    anchor: number,
    anchorSide: 1 | -1
  }

This can be overridden to control the DOM selection created for a selection. The default is to just return the selection's own head and anchor.

  headSide: 1 | -1

The side that the selection head is associated with. -1 means it is after the element before its position, 1 means it is before the element after its position. This influences where the cursor is drawn (for example when on a line wrapping boundary or in bidirectional text) and where further motion takes it. It is valid for it to point in a direction where the is no element (say, -1 when at the start of its parent node).

By default, this points in the direction of the anchor or forward if that is equal to the head, but selection types like GardSelection.Text can override it.

  anchorSide: 1 | -1

The side associated with the selection anchor. Also by default points towards the head, or if that is the same position, forward.

  abstract eq(other: GardSelection): boolean

Compare this selection to another selection.

  eqPos(other: GardSelection): boolean

Returns true if this selection has the same head and anchor as the given selection.

  abstract map(
    change: ChangeSet,
    cx: GardSelection.Context,
    assoc?: 1 | -1
  ): GardSelection

Map a selection through a change. Used to adjust the selection position for changes.

  toJSON(state: GardState): unknown

Convert this selection to an object that can be serialized to JSON. Each selection type may define its own JSON representation format.

  static fromJSON(
    cx: GardSelection.Context,
    json: unknown
  ): GardSelection

Deserialize a selection. The configuration is used to associate custom selection types with their implementation.

  static cursor(
    pos: number,
    side?: 1 | -1,
    goalColumn?: number
  ): GardSelection.Text

Create a cursor text selection at the given position.

  static range(
    anchor: number,
    head?: number,
    headSide?: 1 | -1,
    goalColumn?: number
  ): GardSelection.Text

Create a text selection.

  static node(
    pos: number,
    node: Node,
    goalColumn?: number
  ): GardSelection.Node

Create a node selection.

  nextNormalCursor(
    cx: GardSelection.Context,
    forward: boolean = true
  ): GardSelection.Text | null

Find the next normal cursor position after or before this selection's head. Normal cursor positions are:

  • Any inline position, except one directly inside of an inline plot that doesn't have cursorInsideBounds set.

  • Positions between two cursor barriers, if not already an inline position. Cursor barriers are the sides of the document, any block leaves, or plots that are isolating, whitespace-preserving, or explicitly defined as a cursor barrier.

  normalCursorAtBound(
    cx: GardSelection.Context,
    forward: boolean = true
  ): GardSelection.Text | null

Get a normal cursor at the start or end of this selection.

  skipWord(
    cx: GardSelection.Context,
    forward: boolean = true
  ): GardSelection.Text | null

Move across one word starting from this selection's head.

  static near(
    cx: GardSelection.Context,
    pos: number,
    bias: 1 | -1 = 1
  ): GardSelection.Text

Find a normal selection near the given position.

  static atStart(
    cx: GardSelection.Context,
    block?: Pos.Plot
  ): GardSelection.Text

Find a normal selection at the start of the document or the given textblock.

  static atEnd(
    cx: GardSelection.Context,
    block?: Pos.Plot
  ): GardSelection.Text

Find a normal selection at the end of the document or the given textblock.

}
GardSelection.define<T extends GardSelection, JSON extends {}>(
  tag: string,
  cls: {new(...args: any[]): T},
  toJSON: (sel: T) => JSON,
  fromJSON: (doc: Plot.Doc, json: JSON) => T
): any

Create an extension that registers a custom selection type using the given class. Such a selection is only valid in a state that has the extension active. The JSON representation of a selection will be tagged with the tag string, and created and read via the functions passed here.

class GardSelection.Text extends GardSelection {

Text selections hold a single arbitrary range in the document. They represent a cursor when their anchor and head are the same position.

  marks: Mark.Set | undefined

A set of active marks that should be applied to content inserted at this selection (replacing the contextual marks). Used mostly for making the effect of toggling inline styles stick until something is inserted. Marks that aren't valid for the inserted content will be ignored.

  static create(
    spec: GardSelection.Text.Spec
  ): GardSelection.Text

Create a text selection.

}
type GardSelection.Text.Spec = {

Description of a text selection.

  anchor: number

The anchor point of the selection. This is the side that doesn't move when extending the selection (for example by moving the cursor while holding shift).

  head?: number

The moving side of the selection. This will default to anchor when not given.

  headSide?: 1 | -1

The side of the head position that the selection is associated with, if any. -1 points at the element before the position, 1 at the element after. This is only meaningful for empty/cursor selections. It will influence where the cursor is drawn.

  goalColumn?: number

Associates a horizontal position with this selection for use during vertical cursor motion.

  marks?: Mark.Set

Marks associated with a cursor selection, which will determine the marks of inline content inserted at that selection. This is used for things like toggling emphasis on a cursor selection.

}
type GardSelection.Text.JSON = {
  anchor: number,
  head?: number,
  side?: 1 | -1,
  marks?: Record<string, any>
}

The representation of a text selection when serialized to JSON.

class GardSelection.Node extends GardSelection {

Node selections select a single node. They are created, for example, when clicking on or moving into a selectable leaf node. Use GardSelection.node to create one.

  node: Node

The selected node.

}
type GardSelection.Node.JSON = {pos: number}

The representation of a node selection when serialized to JSON.

class GardSelection.Resolved {

A selection object where the selection positions have been resolved. For convenience, an editor state's sel property provides an instance of this, derived from the state's regular selection.

  anchor: Pos

The selection anchor.

  head: Pos

The head of the selection.

  selection: GardSelection

The original selection.

  from: Pos

The lower bound of the selection.

  to: Pos

The upper bound of the selection.

  ranges: readonly {from: Pos, to: Pos}[]

The selection ranges.

  replacementRange: {from: Pos, to: Pos}

The resolved replacement range.

  activeMarks: Mark.Set

The active marks for this selection. If this is a cursor selection with explicitly stored marks, those are returned. Otherwise, this computes the marks that should be applied to content inserted in the selection's position, based on spanning marks on the surrounding nodes.

}
type GardSelection.Context = {
  doc: Plot.Doc,
  config: GardState.Configuration
}

Many selection related functions need access to a configuration (to determine text direction and visual motion behavior) and a document. Note that GardState is a subtype of this.

Textblocks

It is often helpful to look at a textblock (a block plot containing inline content) as a flat line of text. A textblock map is a data structure intended to make that easy.

class TextblockMap {

A textblock map contains the text in a textblock as a string, and can help convert between string offsets and document positions.

Note that this is not the way to convert a piece of document to a string. Use textContent for that.

  start: number

The start position of the textblock content.

  node: Plot

The textblock's document node.

  ltr: boolean

Whether the base direction of this block is left-to-right.

  text: string

The text in the block. Non-text leaf nodes and nodes with block content will be replaced by a single 0xfffc character in this string.

  order: readonly BidiSpan[]

The text order of the text in this block. Will generally be a single span, but if the block mixes left-to-right and right-to-left text, this describes the individual sections, ordered from the block's start to its end.

  static get(
    cx: GardSelection.Context,
    start: number,
    node: Plot
  ): TextblockMap

Get the map for the given textblock. Will use a cache to reuse results for unchanged blocks.

  toIndex(pos: number): number

Get the string index for a document position. Positions outside of the textblock will be clipped to its start or end.

  fromIndex(index: number): number

Get the document position for a given string index.

  visualSide(start: boolean): {pos: number, side: 1 | -1}

Get the position at the start or end of the textblock. Note that in bidirectional text this may not be the actual start or end position of the node.

}
class BidiSpan {

Represents a contiguous range of text that has a single direction (as in left-to-right or right-to-left).

  ltr: boolean

The direction of this span.

  from: number

The start of the span (relative to the start of the line).

  to: number

The end of the span.

  level: number

The "bidi level" of the span. 0 means left-to-right, 1 means right-to-left, 2 means left-to-right embedded inside right-to-left, and so on, with even numbers being left-to-right, odd numbers right-to-left..

  static strongDir(ch: number): boolean | null

Query whether the given character has a strong direction. Returns null when not, true when left-to-right, and false when right-to-left.

}

Corrections

Corrections provide an abstraction for enforcing certain types of invariants about a document's shape. They watch for some kinds of document changes on specific types of nodes and, when they occur, run a function to verify that some condition holds and optionally adjust the content by making more changes. The correcting function will be passed a Pos.Node object pointing at the matched node, as well as the editor state before the transaction. Changes it produces will be interpreted as relative to the document after the transaction (so node.doc). Corrections are wrappers around transaction extenders. This means that their effect will be included in transactions as they are applied.

class Correction {

The class representing a correction. Counts as an editor extension.

Corrections install themselves as transaction extenders that check modified nodes and, if necessary, apply fixes to enforce constraints. In normal operation, this means that they guarantee the constraints implemented in the transaction are enforced.

They do not activate for remote transactions, because acting on those can cause collaborative editing setups to malfunction (for example, causing all peers to repeatedly try to correct the same issue, causing an endless loop of updates or other chaos). The collab module has special provisions for integrating corrections in the step transformation in a safe way, but that requires you to explicitly tell it to use them, both on the client and the server.

  extension: GardState.Extension

To take effect, corrections must be included in an editor configuration.

  scan(state: GardState): Transaction | null

This method can be used to run a correction agains all matching nodes in an existing document. If the correction makes any changes, the method returns a transaction with those changes.

  static onChildList(
    query: Node.Query,
    correct: (node: Pos.Plot) => ChangeSet.Spec | null
  ): Correction

Create a correction that runs whenever the child list of a node that matches the given query changes, or such a node is inserted into the document.

  static onContent(
    query: Node.Query,
    correct: (node: Pos.Plot) => ChangeSet.Spec | null
  ): Correction

Create a correction that runs whenever any content inside a node that matches the given query changes, or such a node is inserted into the document.

  static onMarks(
    query: Node.Query,
    correct: (node: Pos.Node) => ChangeSet.Spec | null
  ): Correction

Define a correction that runs whenever the set of marks on a matching tag changes.

  static check(
    changes: ChangeSet,
    doc: Plot.Doc,
    corrections: readonly Correction[]
  ): ChangeSet | null

Check the ranges touched by the given change set against the given list of corrections. Return a change set if any changes need to be made. (This isn't how you normally use corrections, but can be useful in a situation where you aren't working with an editor state transaction.)

}

wordgard/editor

This module defines an editor component for use in the browser, on top of the editing model defined in the other modules. It can be loaded outside the browser, but won't do anything useful there.

Editor Component

The Wordgard class is responsible for displaying an editor interface and the document inside of it. It captures basic user events, such as typing and pointer clicks, and converts them into editing actions.

class Wordgard {

This class implements the editor's user interface. It wraps the editable DOM surface and possibly other elements such as panels.

  static create(spec: Wordgard.Spec): Wordgard

Construct a new editor. You'll want to either provide a parent option, or put the editor's DOM element into your document after creating an editor, so that the user can see it.

  state: GardState

The current editor state.

  composing: boolean

Indicates whether the user is currently composing text via IME, and at least one change has been made in the current composition.

  compositionStarted: boolean | null

Indicates whether the user is currently in composing state. Note that on some platforms, like Android, this will be the case a lot, since just putting the cursor on a word starts a composition there.

  editable: boolean

Queries whether the editor's DOM is editable.

  focusable: boolean

Returns true if the editor can be focused (is editable or has a tabindex).

  root: DocumentOrShadowRoot

The document or shadow root that the editor lives in.

  dom: HTMLElement

The outer DOM element that represents the editor.

  scrollDOM: HTMLElement

The DOM element that can be styled to scroll. (Note that it may not have been, so you can't assume this is scrollable.)

  contentDOM: HTMLElement

The editable DOM element holding the editor content. You should not, usually, interact with this content directly though the DOM, since the editor will immediately undo most of the changes you make. Instead, dispatch transactions to modify content, and decorations to style it.

  dispatch(tr: Transaction.Spec | Transaction)

All editor state updates go through this. It takes a transaction or transaction spec and updates the editor to show the new state produced by that transaction. This function is bound to the editor instance, so it does not have to be called as a method.

Will apply transaction appenders and include any extra transactions they produce in the editor's state.

Updates will be immediately be reflected in the object's state property, but updating the DOM will be deferred to the next display update.

  flush()

Force a flush on the editor content, updating its DOM representation for any pending changes.

  scheduleDOMRead(read: (wg: Wordgard) => void)

Schedule a function that needs to read from the (flushed) DOM. During an editor update, when doing anything that needs to access the DOM layout, it is important to schedule it with this method, to avoid forcing unnecessary DOM layouts.

  scheduleDOMWrite(write: (wg: Wordgard) => void)

Schedule a function that needs to modify the DOM. When doing any kind of DOM mutation that depends on a DOM read, use this method, so that read and write phases remain separate.

  plugin<
    T extends Wordgard.Plugin.Value
  >(plugin: Wordgard.Plugin<T>): T | null

Get the value of a specific plugin, if present. Note that plugins that crash can be dropped from an editor, so even when you know you registered a given plugin, it is recommended to check the return value of this method.

  moveToLineBoundary(
    start: GardSelection,
    forward: boolean
  ): GardSelection.Text | null

Find the position at the end or start of the (wrapped) line. If the given position isn't in a textblock, this will return null.

  moveVertically(
    start: GardSelection,
    forward: boolean,
    distance?: number,
    allowNode?: boolean
  ): GardSelection | null

Move a cursor position vertically. When distance isn't given, it defaults to moving to the vertical element below or above the start position. Otherwise, distance should provide a positive distance in pixels.

When start has a goalColumn, the vertical motion will use that as a target horizontal position. Otherwise, the cursor's own horizontal position is used. The returned cursor will have its goal column set to whichever column was used. If allowNode is true, this may return a node selection on a block node.

  domAtPos(pos: number, assoc: 1 | -1 = -1): {
    node: Node,
    offset: number
  }

Find the DOM parent node and offset (child offset if node is an element, character offset when it is a text node) at the given document position.

  nodeDOM(pos: number): Element | null

Get the DOM element for the node at the given position, if any.

  posAtDOM(node: Node, offset: number = 0): number

Find the document position at the given DOM node. Can be useful for associating positions with DOM events. Will raise an error when node isn't part of the editor content.

  nodeFromDOM(node: Element): {pos: number, node: Node} | null

Find the Wordgard node represented by the given DOM node, or one of its parent nodes, if any. Will not return the outer document node.

  posAtCoords(coords: {
    x: number,
    y: number
  }): {pos: number, side: 1 | -1, target: number | null}

Get the document position at the given screen coordinates.

  coordsAtPos(pos: number, assoc: 1 | -1 = -1): DOMRect

Get the screen coordinates at the given document position. side determines whether the coordinates are based on the element before (-1) or after (1) the position (if no element is available on the given side, the method will transparently use another strategy to get reasonable coordinates).

  coordsForElement(pos: number): DOMRect | null

Return the rectangle around a given node or character. If there is no element directly after pos, this will return null. For space characters that are a line wrap point, this will return the position before the line break.

  hasFocus: boolean

Check whether the editor has focus.

  focus()

Put focus on the editor.

  themeClasses: string

Get the CSS classes for the currently active editor themes.

  static scrollIntoView(
    pos: number | GardSelection,
    options: Wordgard.ScrollSpec = {}
  ): Transaction.Effect<unknown>

Returns an effect that can be added to a transaction to cause it to scroll the given position or range into view.

  static label(
    label: string | PhraseSet.Ref
  ): GardState.Extension

Add an aria-label attribute to the editable element holding the given string or phrase.

  static clipboardOutputFilter: GardState.Facet<(
    content: Slice,
    state: GardState
  ) => Slice>

Filter functions provided through this facet will be run on a slice before it is serialized to the clipboard.

  static clipboardOutputHTMLFilter: GardState.Facet<(
    html: string,
    state: GardState
  ) => string>

Filter functions provided through this facet will be run on an HTML string before it put onto the clipboard.

  static clipboardTextSerializer: GardState.Facet<(
    slice: Slice,
    context: readonly Plot.Tag[],
    state: GardState
  ) => string | null>

This can be used to provide a function that converts a document slice to a string that is put onto the plain-text clipboard. Serializers are tried in order of precedence until one returns a string.

  static clipboardOutputTextFilter: GardState.Facet<(
    html: string,
    state: GardState
  ) => string>

Filter to run on the plain text representation of content put onto the clipboard.

  static clipboardInputFilter: GardState.Facet<(
    content: Slice,
    state: GardState
  ) => Slice>

Filter functions provided through this facet will be run on a slice after it is read from the clipboard.

  static clipboardInputHTMLFilter: GardState.Facet<(
    html: string,
    state: GardState
  ) => string>

Filter functions to run on HTML text that is read from the clipboard.

  static clipboardTextParser: GardState.Facet<(
    text: string,
    state: GardState
  ) => Slice | null>

When the editor reads plain text from the clipboard, this facet can be used to provide a custom parser. Each provided function is tried in order of precedence, until one returns a slice.

  static clipboardInputTextFilter: GardState.Facet<(
    html: string,
    state: GardState
  ) => string>

Filter to run on plain text read from the clipboard.

  static pasteHandler: GardState.Facet<(
    wg: Wordgard,
    event: ClipboardEvent,
    slice: Slice,
    context: readonly Plot.Tag[]
  ) => boolean>

Facet that allows you to register handlers to override paste behavior.

  static dropHandler: GardState.Facet<(
    wg: Wordgard,
    event: DragEvent,
    pos: number,
    move: {from: number, to: number} | null,
    slice: Slice,
    context: readonly Plot.Tag[]
  ) => boolean>

Facet for custom drop handlers. When the drop is done inside the editor and should move an existing range, the move parameter will hold the origin range.

  static isFocusChange: Transaction.Annotation.Type<boolean>

This annotation is added to transactions created because the editor's focused status changed. It holds true when the editor gained focus, false when it lost focus.

  static styleModule: GardState.Facet<StyleModule>

Facet to add a style module to an editor. The editor will ensure that the module is mounted in its document root.

  static domEventHandler<
    Event extends keyof HTMLElementEventMap
  >(
    event: Event,
    handler: (
      event: HTMLElementEventMap[Event],
      wg: Wordgard
    ) => boolean | undefined
  ): GardState.Extension

Returns an extension that can be used to add a DOM event handler to the editor. For any given event, such functions are ordered by extension precedence, and the first handler to return true will be assumed to have handled that event, and no other handlers or built-in behavior will be activated for it. These are registered on the content element, except for scroll handlers, which will be called any time the editor's scroll element or one of its parent nodes is scrolled.

  static domEventObserver<
    Event extends keyof HTMLElementEventMap
  >(
    event: Event,
    observer: (
      event: HTMLElementEventMap[Event],
      wg: Wordgard
    ) => void
  ): GardState.Extension

Create an extension that registers a DOM event observers. Contrary to event handlers, observers can't be prevented from running by a higher-precedence handler returning true. They also don't prevent other handlers and observers from running when they return true, and should not call preventDefault.

  static scrollHandler: GardState.Facet<(
    wg: Wordgard,
    target: {from: number, to: number} & Wordgard.ScrollSpec
  ) => boolean>

Scroll handlers can override how editor content is scrolled into view. If they return true, no further handling happens for the scrolling. If they return false, the default scroll behavior is applied. Scroll handlers should never initiate editor updates.

  static exceptionSink: GardState.Facet<(exception: any) => void>

Allows you to provide a function that should be called when the library catches an exception from an extension (mostly from plugins, but may be used by other extensions to route exceptions from user-code-provided callbacks). This is mostly useful for debugging and logging. See Wordgard.logException.

  static transactionListener: GardState.Facet<(
    trs: readonly Transaction[],
    wg: Wordgard
  ) => void>

Registers a listener function to be called whenever a set of transactions is applied to the editor. This function may dispatch additional transactions, if needed.

  static updateListener: GardState.Facet<(
    update: Wordgard.Update
  ) => void>

A facet that can be used to register a function to be called after the editor flushes updates to the DOM. Dispatching transactions from such a function is allowed, but will cause a new, separate update to happen.

  static editable: GardState.Facet<boolean, boolean>

Facet that controls whether the editor content DOM is editable. When its highest-precedence value is false, the element will not have its contenteditable attribute set. (Note that this doesn't affect API calls that change the editor content, even when those are bound to keys or buttons. See the readOnly facet for that.)

A non-editable editor will, by default, not be focusable. You can set a content attribute of tabindex: 0 to make an uneditable Wordgard focusable.

  static cursorBlinkRate: GardState.Facet<number, number>

Controls the length of a full cursor blink cycle, in milliseconds. Defaults to 1200. Can be set to 0 to disable blinking.

  static mouseSelectionStyle: GardState.Facet<(
    wg: Wordgard,
    event: MouseEvent
  ) => Wordgard.MouseSelectionStyle | null>

Allows you to influence the way mouse selection happens. The functions in this facet will be called for a mousedown event on the editor, and can return an object that overrides the way a selection is computed from that mouse click or drag.

  static dragMovesSelection: GardState.Facet<(
    event: MouseEvent
  ) => boolean>

Facet used to configure whether a given selection drag event should move or copy the selection. The given predicate will be called with the mousedown event, and can return true when the drag should move the content. The default behavior is to copy when holding Alt on Mac and Control on other platforms, and move otherwise.

  static theme(spec: Record<
    string,
    StyleSpec
  >): GardState.Extension

Create a theme extension. The first argument can be a style-mod style spec providing the styles for the theme. These will be prefixed with a generated scope class.

Because the selectors are prefixed, rules that directly match the editor's wrapper element (to which the scope class will be added) need to be explicitly differentiated by adding an & to the selector for that element—for example &:has(wg-content:focus).

  static colorScheme: GardState.Facet<
    "dark" | "light" | "auto",
    "dark" | "light" | "auto"
  >

This facet controls whether a dark or light color scheme is active, which determines whether style rules with a &dark or &light selector are applied. Defaults to "light". If set to "auto", the editor uses a CSS prefers-color-scheme: dark query to determine whether to enable light or dark mode.

Note that setting this to dark will not automatically make the editor look dark. The default styling does not override the inherited background and color of the editor. In case of a page-wide prefers-color-scheme selection, those might already be dark. But when setting an editor on a light background to explicitly to use a dark theme, you'll need to make sure you also load styles for that.

  static styles(spec: Record<
    string,
    StyleSpec
  >): GardState.Extension

Create an extension that loads a set of style rules. Like with theme, use & to indicate the place of the editor wrapper element when directly targeting that. You can also use &dark or &light instead to only target editors with a dark or light color scheme.

  static scrolling(height: string | number): GardState.Extension

Creates a simple theme that sets a height (given in pixels or, if a string, a CSS number + unit) and automatic overflow scrolling on the editor. (The default styling makes the editor height fit its content.)

  static cspNonce: GardState.Facet<string, string>

Provides a Content Security Policy nonce to use when creating the style sheets for the editor. Holds the empty string when no nonce has been provided.

  static contentAttributes: GardState.Facet<Record<
    string,
    string | null
  > | ((wg: Wordgard) => Record<string, string | null>)>

Facet that provides additional DOM attributes for the editor's editable DOM element, either directly, or as a function from the editor state.

  static editorAttributes: GardState.Facet<Record<
    string,
    string | null
  > | ((wg: Wordgard) => Record<string, string | null>)>

Facet that provides DOM attributes for the editor's outer element.

  static announce: Transaction.Effect.Type<string>

State effect used to include screen reader announcements in a transaction. These will be added to the DOM in a visually hidden element with aria-live="polite" set, and should be used to describe effects that are visually obvious but may not be noticed by screen reader users (such as moving to the next search match).

  static coveredMargins: GardState.Facet<
    (wg: Wordgard) => Partial<DOMRect> | null
  >

Facet that allows extensions to indicate that some amount of space around the sides of the scrolling element should be considered blocked from view when scrolling something into view. This is only used by plugins that introduce elements that cover part of the editor (for example a gutter).

}
interface Wordgard.Spec extends Partial<GardState.Spec> {

The type of object given to Wordgard.create.

  state?: GardState

The editor's initial state. If not given, a new state is created by passing this configuration object to GardState.create, using its doc, selection, and config fields (if provided).

  parent?: Element | DocumentFragment

When present, the editor is immediately appended to the given element on creation. (Otherwise, you'll have to place the editor element in the document yourself.)

  scrollTo?: Transaction.Effect<any>

Pass an effect created with Wordgard.scrollIntoView here to set an initial scroll position.

}
type Wordgard.ScrollSpec = {

Options passed to Wordgard.scrollIntoView.

  y?: "center" | "start" | "end" | "nearest"

By default ("nearest") the position will be vertically scrolled only the minimal amount required to move the given position into view. You can set this to "start" to move it to the top of the editor, "end" to move it to the bottom, or "center" to move it to the center.

  x?: "center" | "start" | "end" | "nearest"

Effect similar to y, but for the horizontal scroll position.

  yMargin?: number

Extra vertical distance to add when moving something into view. Not used with the "center" strategy. Defaults to 5. Must be less than the height of the editor.

  xMargin?: number

Extra horizontal distance to add. Not used with the "center" strategy. Defaults to 5. Must be less than the width of the editor.

}
interface Wordgard.MouseSelectionStyle {

The interface that objects registered with Wordgard.mouseSelectionStyle must conform to.

  get: (curEvent: MouseEvent, extend: boolean) => GardSelection

Return a new selection for the mouse gesture that starts with the event that was originally given to the constructor, and ends with the event passed here. In case of a plain click, those may both be the mousedown event, in case of a drag gesture, the latest mousemove event will be passed.

When extend is true, that means the new selection should, if possible, extend the start selection.

  update: (update: Wordgard.Update) => boolean | undefined

Called when the editor is updated while the gesture is in progress. When the document changes, it may be necessary to map some data (like the original selection or start position) through the changes.

This may return true to indicate that the get method should get queried again after the update, because something in the update could change its result. Be wary of infinite loops when using this (where get returns a new selection, which will trigger update, which schedules another get in response).

}
Wordgard.logException(
  state: GardState,
  exception: any,
  context?: string
): void

Log or report an unhandled exception in client code. Should probably only be used by extension code that allows client code to provide functions, and calls those functions in a context where an exception can't be propagated to calling code in a reasonable way (for example when in an event handler).

Either calls a handler registered with Wordgard.exceptionSink, window.onerror, if defined, or console.error (in which case it'll pass context, when given, as first argument).

class Wordgard.Plugin<V extends Wordgard.Plugin.Value> {

Plugins associate stateful values with an editor. They can be useful for displaying interface elements, or keeping ephemeral interface state.

  extension: GardState.Extension

Instances of this class act as extensions.

  static define<V extends Wordgard.Plugin.Value>(
    create: (wg: Wordgard) => V,
    provide?: (plugin: Wordgard.Plugin<V>) => GardState.Extension
  ): Wordgard.Plugin<V>

Define a plugin from a constructor function that creates the plugin's value, given an editor.

  static fromClass<V extends Wordgard.Plugin.Value>(
    cls: {new(wg: Wordgard): V},
    provide?: (plugin: Wordgard.Plugin<V>) => GardState.Extension
  ): Wordgard.Plugin<V>

Create a plugin for a class whose constructor takes an editor as only argument.

  eventHandler<Event extends keyof HTMLElementEventMap>(
    event: Event,
    handler: (
      event: HTMLElementEventMap[Event],
      wg: Wordgard,
      value: V
    ) => boolean | undefined
  ): GardState.Extension

Create an event handler for this plugin. Usually called from the plugin's provide function.

  eventObserver<Event extends keyof HTMLElementEventMap>(
    event: Event,
    observer: (
      event: HTMLElementEventMap[Event],
      wg: Wordgard,
      value: V
    ) => void
  ): GardState.Extension

Create an event observer for this plugin.

}
interface Wordgard.Plugin.Value {

This is the interface plugin objects must expose.

  update(update: Wordgard.Update)

Notifies the plugin of an update that happened in the editor. This is called before the editor updates its own DOM. It is responsible for updating the plugin's internal state (including any state that may be read by plugin fields) and writing to the DOM for the changes in the update. To avoid unnecessary layout recomputations, it should not read the DOM layout—use scheduleDOMRead to schedule your code in a DOM reading phase if you need to.

  docUpdate(wg: Wordgard)

When present, this will be called when an update causes any changes in the DOM representation of the document.

  connect(wg: Wordgard)

Called when the editor is attached to the DOM. If the plugin needs to allocate any resource that must be released, or modify something outside the editor, it should do it in this method, and make sure to release/undo it in its disconnect method.

  disconnect(wg: Wordgard)

Called when the editor is removed from the DOM, or the plugin is removed from the editor.

  remove(wg: Wordgard)

Called when the plugin is removed from an editor. This should clean up any changes it made to the editor itself. If the editor was connected to a document, disconnect will be called before this.

}
class Wordgard.Update {

Editor plugins and update listeners are given instances of this class whenever the editor is updated.

  changes: ChangeSet

The changes made to the document by this update.

  editor: Wordgard

The editor that the update is associated with.

  startState: GardState

The previous editor state.

  state: GardState

The new editor state.

  transactions: readonly Transaction[]

The transactions involved in the update. May be empty.

  geometryChanged: boolean

Returns true when the document was modified or when the size of the editor, or elements within the editor, changed.

  focusChanged: boolean

True when this update indicates a focus change.

  docChanged: boolean

Whether the document changed in this update.

  selectionSet: boolean

Whether the selection was explicitly set in this update.

}

Key Bindings

Functionality used for binding keys to editing commands.

class KeyBinding {

Key bindings associate keys with functions that should be run when a matching keyboard event happens.

A key binding can either specify a specific character to match on, which will be compared against the actual character produced by a key event, or describe a key combination.

Bindings for a given key event are evaluated in order of precedence, with each getting a chance to handle the event, stopping when the first handler returns true.

Key combinations are described by strings like "Shift-Ctrl-Enter"—a key identifier prefixed with zero or more modifiers. Key identifiers are based on the strings that can appear in KeyEvent.key. Use lowercase letters to refer to letter keys. You can use "Space" as an alias for the " " name.

Modifiers can be given in any order. Shift- (or s-), Alt- (or a-), Ctrl- (or c- or Control-) and Cmd- (or m- or Meta-) are recognized.

You can use Mod- as a shorthand for Cmd- on Mac and Ctrl- on other platforms. So Mod-b is Ctrl-b on Linux but Cmd-b on macOS.

Unlike character bindings, key combination bindings should refer to the unmodified base key that is being pressed, not the character produced by combining that key with Shift or AltGraph. Keyboard mappings that rearrange the positions of Latin characters are taken into account for this (the mapped position is used), but the library tries to 'see through' keyboard mappings that assign non-Latin characters to keys (so that both the Latin and the non-Latin name can be used).

  extension: GardState.Extension

Bindings count as extensions and can be included in an editor configuration.

  spec: KeyBinding.Spec

The configuration object used to define this binding.

  static of(spec: KeyBinding.Spec): KeyBinding

Define a binding.

}
interface KeyBinding.Spec {

A description of a key binding.

  char?: string

A textual character that this binding should trigger for.

  key?: string

A key combination to use for this binding. If the platform-specific property (mac, win, or linux) for the current platform is used as well in the binding, that one takes precedence. If key isn't defined and the platform-specific binding isn't either, a binding is ignored.

  mac?: string

Key to use specifically on macOS.

  win?: string

Key to use specifically on Windows.

  linux?: string

Key to use specifically on Linux.

  run: Command.Bound | Command

The command to execute when this binding is triggered.

  shift?: Command.Bound | Command

When given, this defines a second binding, using the (possibly platform-specific) key name, prefixed with Shift-, to activate this command.

  any?: (wg: Wordgard, event: KeyboardEvent) => boolean

When this property is present, the function is called for every key, and may return true to indicate the key was handled.

  scope?: string

By default, key bindings apply when focus is on the editor content (the "editor" scope). Some extensions, mostly those that define their own panels, might want to allow registering bindings local to that panel. Such bindings should use a custom scope name. You may also assign multiple scope names to a binding, separating them by spaces.

  allowDefault?: boolean

By default, all keys events for which a handler exists have their preventDefault called, even if no handler returns true. You can set this to true to disable that behavior.

}
KeyBinding.runScopeHandlers(
  wg: Wordgard,
  event: KeyboardEvent,
  scope: string
): boolean

Run the key handlers registered for a given scope. The event object should be a "keydown" event. Returns true if any of the handlers handled it.

KeyBinding.source: GardState.Facet<KeyBinding>

Facet used for registering key bindings. Extension precedence determines the order in which bindings that match the same key are called. When a handler has returned true for a given key, no further handlers are called.

KeyBinding.useDefaultKeymap: GardState.Facet<boolean, boolean>

By default, the default keymap is automatically active. You can configure this to false if you want to completely replace it.

KeyBinding.defaultKeymap: readonly KeyBinding[]

The editor's set of default key bindings. Binds the following keys. Most cursor motion bindings include a Shift- variant that passes the extend flag to the command. Enabled by default unless KeyBinding.useDefaultKeymap is disabled.

On MacOS, the following Emacs-style bindings are available:

Decoration

Decorations provide a way to influence the way the document is displayed without changing the document itself, via editor extensions. They are useful for showing out-of-band information or editing controls directly in the editable content.

type Decoration.Shape = Widget | Elt<string | Widget>

Node shapes can be either a widget or an element which may contain widgets.

Decoration.Tag.shape<T extends Node.Type.Ref<any>>(
  type: T,
  shape: Decoration.Shape |
    ((tag: Node.Tag.For<T>) => Decoration.Shape),
  config?: {atom?: boolean}
): GardState.Extension

Override the way a given node type is drawn in the editor. By default, the shape field in the type's definition will be used, but extensions created with this function can provide an alternative shape for a given type.

When providing a function for the shape, keep in mind that the result will be cached by tag, and you should make sure your function is pure.

When providing a function that returns a shape that changes whether the node is rendered as an atom, you need to provide the atom.

Decoration.Tag.shape.dynamic<T extends Node.Type<any>>(
  type: T,
  shape: (state: GardState) => Decoration.Shape |
    ((tag: Node.Tag.For<T>) => Decoration.Shape),
  config?: {atom?: boolean}
): GardState.Extension

This function allows you to define a custom node shape that depends on the editor state. It will automatically track what slots (see compute) you use, and make sure the nodes are redrawn when those change.

If your shape function returns a function from a tag, you must be careful do any state access you need in the outer function, not the returned function, or it won't be tracked.

You generally don't want to make your shapes depend on constantly-changing slots like the document or selection, because when the document is big, there's a non-trivial amount of work involved when a node shape changes (or may have changed).

When providing a shape for a plot that changes whether it is rendered as an atom, provide the atom option.

Decoration.Tag.wrapper(
  type: Node.Type.Ref<any>,
  wrapper: Elt<string | Widget>,
  options?: {target?: string}
): GardState.Extension

Define a wrapper to be added around a given node type, or some part of it. The given elt should include a hole (0) to indicate where the original shape goes.

If a target option is given, and matching some element in the node's existing shape, only that element will be wrapped. Uses a subset of CSS selectors that supports only tag name and class names (img.x.y).

Decoration.Tag.widget<T extends Node.Type.Ref<any>>(
  type: T,
  place: "before" | "after" | "start" | "end",
  widget: Widget | ((tag: Node.Tag.For<T>) => Widget)
): GardState.Extension

Add a widget to every instance of the given node type. Such widgets can appear before or after the node, and for plots that aren't rendered as atoms, at its start or end.

When a function, widget will be cached by tag, and should be pure.

Decoration.Tag.widget.dynamic<T extends Node.Type.Ref<any>>(
  type: T,
  place: "before" | "after" | "start" | "end",
  widget: (state: GardState) => Widget |
    ((tag: Node.Tag.For<T>) => Widget)
): GardState.Extension

Define a node widget decoration that depends on some aspect of the editor state. See the notes for Decoration.Tag.shape.dynamic.

Decoration.Tag.attribute<T extends Node.Type.Ref<any>>(
  type: T,
  attr: string,
  value: string | ((tag: Node.Tag.For<T>) => string),
  options?: {target?: string}
): GardState.Extension

Add an attribute to the representation of a given node type.

By default, the attribute is added to the outer element (or a wrapper element if the node is rendered as a widget). If the target option is given, and matches an element in the representation, it will be added to that element instead.

abstract class Decoration.Point implements PointSet.Value {

A point decoration is a decoration that targets a given position in the document, or the node after a given position. Sets of point decorations can be provided as point sets through Decoration.Point.source.

  static widget(widget: Widget, options?: {
    side?: number

Determines where this widget appears relative to the cursor (negative means before, positive after, zero means to make it depend on the cursor's own side) and other widgets in the same position. Defaults to zero.

    trackMode?: ChangeSet.TrackMode

What side to track when changes happen around the widget. The default is to keep the widget around unless the content on both sides is deleted. You can pass undefined to indicate the widget should not be deleted by changes, or "before"/"after" to use one specific side.

  }): Decoration.Point

Display a widget at this point.

  static attributes(
    attrs: Record<string, string>,
    options?: {target?: string}
  ): Decoration.Point

Add a set of attributes to the node after this decoration's position.

You can target a specific element in the node's representation with the target option.

  static shape(shape: Decoration.Shape): Decoration.Point

Override the shape of the node after the decoration's point with the given one.

  static wrapper(
    wrapper: Elt<string | Widget>,
    spec?: {target?: string}
  ): Decoration.Point

Wrap the node, or inner node selected with target, at the given position with a wrapper.

  static source: GardState.Facet<(
    state: GardState
  ) => PointSet<Decoration.Point>>

The facet used to register a point decoration source. Functions provided in this way will be called on every editor update, so computing the set on the fly will only perform well for very simple decoration sets, and you'll usually want to keep your set in a state field and update it incrementally.

}
abstract class Decoration.Range implements RangeSet.Value {

Range decorations apply to a document range. They are stored in RangeSets and registered in an editor configuration with Decoration.Range.source.

  static wrapper(
    tagName: string,
    spec: Decoration.Range.WrapperSpec
  ): Decoration.Range

Create a range decoration that wraps nodes in a range with an element, using the given tag name.

  static attribute(
    attr: string,
    value: string,
    options: Decoration.Range.Spec = {}
  ): Decoration.Range

Create a range decoration that adds an attribute to nodes in a range.

  static source: GardState.Facet<(
    state: GardState
  ) => RangeSet<Decoration.Range>>

The facet used to register range decoration sources. The source function will be called on every update. Generating big range sets on the fly will not perform well, so you'll often want to store these in a state field.

}
interface Decoration.Range.Spec {

Configuration object for range decorations.

  inclusive?: boolean | "start" | "end"

Determines whether content inserted next to the range is included when mapping the range through a change. Defaults to false.

  query?: Node.Query

If given, apply this decoration only to matching nodes.

  scope?: "atom" | "inlineatom" | "all"

The type of nodes in the range to apply the decoration to. Defaults to "atom".

}
interface Decoration.Range.WrapperSpec
  extends Decoration.Range.Spec {

Configuration object for wrapper range decorations.

  attributes?: Record<string, string>

Attributes to add to the wrapper element.

  rank?: number

A wrapper's rank determines the nesting order between it and other wrappers created by range decorations or marks. Should be a number between 0 and 100, if given.

  spanning?: boolean

Whether this wrapper may span multiple sibling nodes. Non-spanning wrappers will be created separately for each node. Defaults to true.

}
class Widget<Param = unknown> {

A widget describes a piece of DOM content that can be used to render a node, a part of a node, or an extra element added via a decoration. The Widget object is separate from its DOM representation. It describes how the DOM widget is to be rendered and how it behaves, but it itself is an immutable value.

  value: Param

The parameter for this widget.

  eq(other: any): boolean

Compare this widget to another widget object.

  static define<Param>(
    spec: Widget.Spec<Param>
  ): Widget.Type<Param>

Define a widget type.

  static create(spec: Widget.Spec<null>): Widget<null>

Create a singleton widget.

  type: Widget.Type<unknown extends Param ? any : Param>

This widget's type. The type mangling is a kludge to make sure Widget<Param> is a subtype of Widget<unknown>.

}
type Widget.Spec<Param> = {

Specifies a widget type.

  render: (value: Param) => Element | Text

How to render the widget as DOM content.

  eq?: (a: Param, b: Param) => boolean

Compare the widget value for equality. Will default to ===.

  connect?: (value: Param, dom: Element | Text) => void

Called when a widget of this type is added to an editor that is connected to a DOM document, or an editor with the widget in it is connected.

  disconnect?: (value: Param, dom: Element | Text) => void

Called when a widget of this type is removed from an editor that is connected to a document, or when the editor containing the widget is disconnected.

  handleEvent?: (event: Event, wg: Wordgard) => boolean

Called before the editor handles a DOM event that comes from inside the widget. May return true to indicate that no further handling of the event should happen.

}
class Widget.Type<Param> {

Each widget has an associated type that describes how it behaves.

  of(value: Param): Widget<Param>

Create an instance of this widget type.

}
class PointSet<T extends PointSet.Value = PointSet.Value> {

Data structure used to store sets of points and then track them across document changes. Mostly used for point decorations, but can also track your own types, if you make sure they implement the PointSet.Value interface.

  values: readonly T[]

The values in this set.

  positions: readonly number[]

The positions of the values in this set.

  length: number

The number of points in this set.

  map(changes: ChangeSet): PointSet<T>

Adjust the points for a set of document changes. Returns a new set with the adjusted points. May delete points when the content around them was deleted.

  merge(other: PointSet<T>): PointSet<T>

Returns the union of this set and the given set.

  at(pos: number): T | undefined

Get the value at the given position, if any. If there's multiple values at that position, the one with the lowest side is returned.

  static create<
    T extends PointSet.Value
  >(source: Iterable<[number, T]> |
    ((add: (
    pos: number,
    value: T
  ) => void) => void)): PointSet<T>

Create a point set from an iterable of [position, value] tuples, or a function that calls its argument for every point to add.

  static empty: PointSet<any>

The empty point set.

}
interface PointSet.Value {

Objects stored in a point set must conform to this interface.

  side: number

The side of the point. Used to provide a sorting of points at the same position

  trackMode: ChangeSet.TrackMode | undefined

Specifies whether the point should be deleted when content next to it is deleted. See mapPos.

  eq(other: PointSet.Value): boolean

Method to compare this value to another.

}
class RangeSet<T extends RangeSet.Value = RangeSet.Value> {

Data structure that stores sets of ranges, for use with range decorations or other data types implementing RangeSet.Value.

  values: readonly T[]

The value associated with the ranges in the set.

  from: readonly number[]

The start positions of the ranges in this set.

  to: readonly number[]

The end positions of the ranges.

  length: number

The number of ranges stored in this set.

  map(changes: ChangeSet): RangeSet<T>

Adjust the positions of the ranges for the given change set. Returns a set with the updated ranges.

  static create<T extends RangeSet.Value>(source: Iterable<[
    number,
    number,
    T
  ]> | ((add: (
    from: number,
    to: number,
    value: T
  ) => void) => void)): RangeSet<T>

Create a range set from an iterable of [from, to, value] tuples, or a function that calls its argument for every range to add.

  static empty: RangeSet<any>

The empty range set.

}
interface RangeSet.Value {

Values stored in a range set must conform to this interface.

  inclusiveStart: boolean

Whether content inserted at the start of this value's range is included in the range.

  inclusiveEnd: boolean

Whether content inserted at the end is included.

  eq(other: RangeSet.Value): boolean

Compare this value to another.

}

Panels

Panels are interface elements displayed above or below the editor. They use sticky positioning to stay in view even when the editor is partially scrolled out of view. They are a good way to display things like dialogs, status bars, and menu bars.

interface Panel {

Object that describes an active panel.

  dom: HTMLElement

The element representing this panel. The library will add the "wg-panel" DOM class to this.

  top?: boolean

Controls whether the panel should be at the top or bottom of the editor. Defaults to false.

  update(update: Wordgard.Update)

Update the panel DOM for a given editor update.

  connect(wg: Wordgard)

Called, when present, when the panel has been added the DOM.

  disconnect(wg: Wordgard)

Called when the editor with the panel is disconnected from the DOM, or the panel is removed from an editor.

  remove(wg: Wordgard)

Called when the panel is removed from the editor.

}
type Panel.Constructor = (wg: Wordgard) => Panel

A function that initializes a panel. Used in Panel.show.

Panel.show: GardState.Facet<Panel.Constructor | null>

Opening a panel is done by providing a constructor function for the panel through this facet. (The panel is closed again when its constructor is no longer provided.) Values of null are ignored.

Panel.get<T extends Panel>(
  wg: Wordgard,
  constructor: (wg: Wordgard) => T
): T | null

Get the active panel created by the given constructor, if any. This can be useful when you need access to your panels' DOM structure.

Panel.configure(config?: {
  topContainer?: HTMLElement

By default, panels will be placed inside the editor's DOM structure. You can use this option to override where panels with top: true are placed.

  bottomContainer?: HTMLElement

Override where panels with top: false are placed.

}): GardState.Extension

Configures the panel-managing extension.

menuBar(config: {
  template?: Menu.Template | readonly Menu.Template[]
} = {}): GardState.Extension

Provides a menu bar that displays menu items defined via the menu system in a button bar at the top of the editor. The same menu items can be used by custom menu implementations, but this extension provides a solid default menu style.

interface Dialog {

Dialogs are panels opened as a side-effect, and closed by user action. This interface is used to describe them.

  content?: (wg: Wordgard, close: () => void) => Element

A function to render the content of the dialog. The result should contain at least one <form> element. Submit handlers and a handler for the Escape key will be added to the form.

If this is not given, the label, input, and submitLabel fields will be used to create a simple form for you.

  label?: string

When content isn't given, this provides the text shown in the dialog.

  input?: {[string]: string}

The attributes for an input element shown next to the label. If not given, no input element is added.

  submitLabel?: string

The label for the button that submits the form. Defaults to "OK".

  class?: string

Extra classes to add to the panel.

  focus?: boolean | string

A query selector to find the field that should be focused when the dialog is opened. When set to true, this picks the first <input> or <button> element in the form. When set to false, focus is not moved into the dialog.

  top?: boolean

By default, dialogs are shown above the editor. Set this to false to have it show up at the bottom.

}
Dialog.show(wg: Wordgard, config: Dialog): {
  close: Transaction.Effect<unknown>,
  result: Promise<HTMLFormElement | null>
}

Show a dialog to display a message or prompt the user for input. Returns an effect that can be dispatched to close the dialog, and a promise that resolves when the dialog is closed or a form inside of it is submitted.

You are encouraged, if your handling of the result of the promise dispatches a transaction, to include the close effect in it. If you don't, this function will automatically dispatch a separate transaction right after.

Dialog.get(wg: Wordgard, className: string): Panel | null

Find the Panel for an open dialog, using a class name as identifier.

Dialog.close(wg: Wordgard, className: string): boolean

Close the Panel for an open dialog, by class name.

Tooltips

Tooltips are small elements displayed over the editor near a specific document position.

interface Tooltip {

Describes a tooltip. Values of this type, when provided through the Tooltip.show facet, provide the active tooltips on an editor.

  pos: number

The document position at which to show the tooltip.

  end?: number

The end of the range annotated by this tooltip, if different from pos.

  create(wg: Wordgard): Tooltip.View

A constructor function that creates the tooltip's DOM representation.

  above?: boolean

Whether the tooltip should be shown above or below the target position. Not guaranteed to be respected for hover tooltips since all hover tooltips for the same range are always positioned together. Defaults to false.

  strictSide?: boolean

Whether the above option should be honored when there isn't enough space on that side to show the tooltip inside the viewport. Defaults to false.

  arrow?: boolean

When set to true, show a triangle connecting the tooltip element to position pos.

  clip?: boolean

By default, tooltips are hidden when their position is outside of the visible editor content. Set this to false to turn that off.

}
Tooltip.configure(config: {
  position?: "fixed" | "absolute"

By default, tooltips use "fixed" positioning, which has the advantage that tooltips don't get cut off by scrollable parent elements. However, CSS rules like contain: layout can break fixed positioning in child nodes, which can be worked about by using "absolute" here.

On iOS, which at the time of writing still doesn't properly support fixed positioning, the library always uses absolute positioning.

If the tooltip parent element sits in a transformed element, the library also falls back to absolute positioning.

  parent?: HTMLElement

The element to put the tooltips into. By default, they are put in the editor (<wordgard-editor>) element, and that is usually what you want. But in some layouts that can lead to positioning issues, and you need to use a different parent to work around those.

  tooltipSpace?: (wg: Wordgard) => DOMRect

By default, when figuring out whether there is room for a tooltip at a given position, the extension considers the entire space between 0,0 and documentElement.clientWidth/clientHeight to be available for showing tooltips. You can provide a function here that returns an alternative rectangle.

} = {}): GardState.Extension

Creates an extension that configures tooltip behavior.

interface Tooltip.View {

Describes the way a tooltip is displayed.

  dom: HTMLElement

The DOM element to position over the editor.

  offset?: {x: number, y: number}

Adjust the position of the tooltip relative to its anchor position. A positive x value will move the tooltip horizontally along with the text direction (so right in left-to-right context, left in right-to-left). A positive y will move the tooltip up when it is above its anchor, and down otherwise.

  getCoords?: (pos: number) => DOMRect

By default, a tooltip's screen position will be based on the document position of its pos property. This method can be provided to make the tooltip view itself responsible for finding its screen position.

  overlap?: boolean

By default, tooltips are moved when they overlap with other tooltips. Set this to true to disable that behavior for this tooltip.

  update(update: Wordgard.Update)

Update the DOM element for a change in the view's state.

  connect(wg: Wordgard)

Called when the tooltip is added to a DOM-connected editor.

  disconnect(wg: Wordgard)

Called when the editor containing the tooltip is disconnected, or before the tooltip is removed.

  remove(wg: Wordgard)

Called when the tooltip is removed from the editor.

  positioned(space: DOMRect)

Called when the tooltip has been (re)positioned. The argument is the space available to the tooltip.

  resize?: boolean

By default, the library will restrict the size of tooltips so that they don't stick out of the available space. Set this to false to disable that.

}
Tooltip.show: GardState.Facet<Tooltip | null>

Facet to which an extension can add a value to show a tooltip.

Tooltip.get<T extends Tooltip>(
  wg: Wordgard,
  tooltip: T
): ReturnType<T["create"]> | null
Tooltip.get<T extends Tooltip.View>(
  wg: Wordgard,
  create: (wg: Wordgard) => T
): T | null

Get the active tooltip view for a given tooltip or tooltip constructor, if available.

Tooltip.reposition(wg: Wordgard): void

Tell the tooltip extension to recompute the position of the active tooltips. This can be useful when something happens (such as a re-positioning or CSS change affecting the editor) that could invalidate the existing tooltip positions but isn't detected by the extension.

Tooltip.hover(
  source: (
    wg: Wordgard,
    pos: number,
    side: 1 | -1
  ) => Tooltip | readonly Tooltip[] | Promise<
    Tooltip | readonly Tooltip[] | null
  > | null,
  options: Tooltip.hover.Spec = {}
): {
  extension: GardState.Extension,
  active: GardState.Field<readonly Tooltip[]>
}

Set up a hover tooltip, which shows up when the pointer hovers over ranges of text. The callback is called when the mouse hovers over the document text. It should, if there is a tooltip associated with position pos, return the tooltip description (either directly or in a promise). The side argument indicates on which side of the position the pointer is—it will be -1 if the pointer is before the position, 1 if after the position.

Note that all hover tooltips are hosted within a single tooltip container element. This allows multiple tooltips over the same range to be "merged" together without overlapping.

Returns an editor extension that installs the hover behavior and a state field that can be used to read the currently active tooltips produced by this extension.

type Tooltip.hover.Spec = {

Options given to Tooltip.hover.

  hideOn?: (tr: Transaction, tooltip: Tooltip) => boolean

Controls whether a transaction hides the tooltip. The default is to not hide.

  hideOnChange?: boolean | "touch"

When enabled (this defaults to false), close the tooltip whenever the document changes or the selection is set.

  hoverTime?: number

Hover time after which the tooltip should appear, in milliseconds. Defaults to 300ms.

}
Tooltip.hover.has(state: GardState): boolean

Returns true if any hover tooltips are currently active.

Tooltip.hover.closeAll: Transaction.Effect<null>

Transaction effect that closes all hover tooltips.

Utilities

Input rules are a way to respond to certain patterns of text input with an action, such as replacing sequences of dashes with emdash characters or automatically creating lists when a textblock is started with a number and a period.

class InputRule {

Objects of this type represent input rules.

  extension: GardState.Extension

Rules can be added to a configuration as extension values.

  static define(spec: InputRule.Spec): InputRule

Define an input rule.

  static wrapping(
    expr: RegExp,
    tag: Plot.Tag | ((match: InputRule.MatchArray) => Plot.Tag),
    empty: boolean = false
  ): InputRule

Build an input rule for automatically wrapping a textblock when a given string is typed. You'll probably want the regexp to start with ^, so that the pattern can only occur at the start of a textblock. tag gives the type of plot to wrap in.

When empty is given as true, the rule only applies when the expression matches the textblock's entire content.

  static textblockType(
    expr: RegExp,
    tag: Plot.Tag | ((match: InputRule.MatchArray) => Plot.Tag),
    empty: boolean = false
  ): InputRule

Build an input rule that changes the type of a textblock when the matched text is typed into it. You'll usually want to start your regexp with ^ so that it is only matched at the start of a textblock. The optional getAttrs parameter can be used to compute the new node's attributes, and works the same as in the InputRule.wrapping function.

}
interface InputRule.Spec {

Configuration given to InputRule.define.

  expr: RegExp

The regular expression to match against the text before the input. This expression should end in a $ marker.

  apply: string | ((
    state: GardState,
    match: InputRule.MatchArray
  ) => Transaction.Spec | null)

Handler to call when this rule matches. match will contain the document positions of the full match and all matched groups in expr. Should return true when it has taken an action, false when it didn't. You probably want to include history.isolate.of(true) in any transactions you dispatch from a rule handler, so that users can undo the adjustment if it wasn't what they wanted.

When given as a string, the full match will be replaced by that string.

  lookahead?: RegExp

Because the regular expression given in expr must end at the cursor, it is matched against a string that stops at the cursor, and cannot look beyond it. You can provide an additional expression here (which should start with ^) to enforce a lookahead condition.

  inCode?: boolean

By default, input rules don't apply inside nodes with the Code role. Set this to true to allow matches in code.

}
type InputRule.Match = {from: Pos, to: Pos, text: string}

An object representing a matched group for an input rule. Holds the start and end positions of the group in the document, along with the matched text content.

type InputRule.MatchArray = readonly (InputRule.Match | null)[] &
  {0: InputRule.Match}

An array of matches.

InputRule.emDash: InputRule

Input rule that converts double dashes to an emdash.

InputRule.ellipsis: InputRule

Rule that converts three dots to an ellipsis character.

InputRule.openDoubleQuote: InputRule

“Smart” opening double quotes.

InputRule.closeDoubleQuote: InputRule

“Smart” closing double quotes.

InputRule.openSingleQuote: InputRule

‘Smart’ opening single quotes.

InputRule.closeSingleQuote: InputRule

‘Smart’ closing single quotes.

InputRule.smartQuotes: readonly InputRule[]

Smart-quote related input rules.

placeholder(content: string |
  (() => Element)): GardState.Extension

Extension that enables a placeholder—a piece of example content to show when the editor is empty.

dropCursor(): GardState.Extension

Draws a cursor at the current drop position when something is being dragged over the editor.


wordgard/command

This module exports abstractions to represent editor commands, which represent user actions, as well as a collection of commands and supporting functions.

Command Abstraction

Commands functions are used by things like key bindings, menus, and event handlers to dispatch a specific type of user action. They have a parameter type (which may be null and ignored).

type Command<Param = null> = (
  target: Wordgard,
  param: Param
) => boolean | Transaction.Spec

A command is a function that takes an editor and an additional parameter, and either...

  • returns false to indicate that it does not apply to the current editor state

  • performs its action as a side effect and returns true

  • returns a transaction spec that should be dispatched as its effect

This formulation is chosen to cover both side-effecting commands (whose effect may not even directly affect the editor—a command may just open a dialog or change some editor-external state) and commands implemented as pure functions from state to transaction.

Extensions can register additional handlers for a command, which will be called in order of precedence (until one returns true) when the command is dispatched. Commands are recognized by function identity. So, for example, the enter command is both the tag used to indicate invocation of an enter press and the function that implements the default behavior for this action.

type Command.Pure<Param = null> = (
  target: {state: GardState},
  param: Param
) => false | Transaction.Spec

Command.Pure is a subtype of Command that relies only on the editor state, and performs no imperative effects. When implementing such a command function, it can be useful to tag it with this type, so that it can be invoked without a full editor component for testing or for use in a context where there is no editor.

Note that invoking a command function directly will not activate custom handlers.

Command.handler<Param>(
  command: Command<Param>,
  handler: Command<Param>
): GardState.Extension

Create an extension that adds a handler for the given command.

Command.bind<Param>(
  command: Command<Param>,
  param: Param
): Command.Bound

Bind a command with a parameter. The only thing you can do with a bound command is to dispatch it.

type Command.Bound = {tag: symbol}

Opaque type used for bound commands.

Command.dispatch(
  wg: Wordgard,
  command: Command.Bound | Command
): boolean
Command.dispatch<Param>(
  wg: Wordgard,
  command: Command<Param>,
  param: Param
): boolean

Apply a command to the given editor view. When passing a non-bound command with a parameter, the parameter has to be passed as second argument.

Menu System

A set of abstractions used to define generic menu items and structure. This does not include an actual implementation of a menu component. For that, see menuBar.

type Menu.Item = Menu.Group | Menu.Submenu | Menu.Button |
  Menu.CustomControl

Editor menus are structured as trees, with item groups and submenus as internal nodes, and buttons and custom controls as leaf nodes.

interface Menu.Item.Spec {

Generic configuration fields supported by all menu items.

  select?: (state: GardState) => boolean

When given and returning false, this item should be hidden from the menu. Should be used sparingly, to avoid the menu constantly flickering and changing size as the user is editing.

  enable?: (state: GardState) => boolean

When given and returning false, this item is disabled, which means it looks faded and cannot be interacted with.

  updateFor?: (tr: Transaction) => boolean

By default, state predicates (select, enable, and active) are re-checked whenever the document or selection changes. If an item is sensitive to other aspects of the state, provide a test here that returns true for transactions that might affect the item state.

  parent?: Menu.Group | Menu.Submenu

The item's parent. See Menu.resolve for information on how menus are linked up.

  rank?: number

Determines the order of elements in the parent. Should be a number between 0 and 100. Defaults to 100.

  description?: string | PhraseSet.Ref

A description to associate with the item, used for hover tooltips and screen-reader text. If the item has a textual label, this will default to that label when not given.

}
class Menu.Item.Base {

Base class for menu items, storing the fields specified in Menu.Item.Spec.

  select: ((state: GardState) => boolean) | undefined
  enable: ((state: GardState) => boolean) | undefined
  updateFor: ((tr: Transaction) => boolean) | undefined
  parent: Menu.Group | Menu.Submenu | undefined
  rank: number
  description: string | PhraseSet.Ref | undefined
  extension: GardState.Extension

Menu items can be used as editor extensions to include them in a configuration.

}
Menu.Item.source: GardState.Facet<Menu.Item>

The facet used to add menu items to a configuration. Used by the items' extensions to register them, and by menu implementations to find available items.

type Menu.Item.Resolved = Menu.Button | Menu.CustomControl |
  "|" | Menu.Submenu.Resolved

A resolved menu consists of buttons, custom controls, submenus, and spacers, which are represented by the string literal "|".

type Menu.Label = string | PhraseSet.Ref | {
  icon: string,
  directional?: boolean
}

Labels are used by buttons and submenus to determine what they look like. They may either be textual (a string or reference to a phrase), or an icon, which is expressed an SVG path string that draws the icon inside a 100-by-100 space. The directional flag indicates that the icon should be mirrored vertically in a right-to-left editor.

class Menu.Button extends Menu.Item.Base {

A menu button runs a command when activated. See the spec type for the meaning of the fields.

  label: Menu.Label
  run: Command.Bound | Command
  active: ((state: GardState) => boolean) | undefined
  spec: Menu.Button.Spec

The configuration object used to create this button.

  static define(spec: Menu.Button.Spec): Menu.Button

Define a menu button.

}
interface Menu.Button.Spec extends Menu.Item.Spec {
  run: Command.Bound | Command

The command to run when the user activates the button.

  active?: (state: GardState) => boolean

When this returns true, the button is highlighted as active. This can be used to show, for example, that a mark is active at the cursor, or that a block type matches the block around the current selection. Also used to automatically select a label for a submenu.

  label: Menu.Label

The label to show on this button.

}
Menu.Button.toggleMark(config: {
  mark: Mark,
  parent?: Menu.Group | Menu.Submenu,
  rank?: number,
  description?: PhraseSet.Ref,
  label: Menu.Label
}): Menu.Button

Creates a menu button that toggles an inline mark via Menu.Button.toggleMark, and is shown as active when either that mark is part of the marks associated with the current cursor, or the selection covers only content with that mark.

class Menu.CustomControl extends Menu.Item.Base {

Custom controls are similar to buttons, in that they can be part of the menu and receive focus through menu navigation, but they manage their own DOM. This can be used for elements like color pickers that should be displayed inside of the menu but support user interaction more complex than a button.

  render: (wg: Wordgard, done: () => void) => {
    dom: HTMLElement,
    focus?: HTMLElement
  }
  setEnabled: ((
    dom: Element,
    enabled: boolean
  ) => void) | undefined
  spec: Menu.CustomControl.Spec

The configuration object used to create this control.

  static define(
    spec: Menu.CustomControl.Spec
  ): Menu.CustomControl

Define a custom menu item.

}
interface Menu.CustomControl.Spec extends Menu.Item.Spec {
  render: (wg: Wordgard, done: () => void) => {
    dom: HTMLElement,
    focus?: HTMLElement
  }

The function that renders the actual control. The dom property on the returned object will be displayed in the menu. If focus is provided, that is used as the element to put focus on. If not, dom is used.

The control should call the done function when it decides it is closed or activated, so that any submenu above it knows to close, and focus can be moved back to the editor if appropriate.

  setEnabled?: (focus: Element, enabled: boolean) => void

If the control supports disabling, this function will be called when the enabled state changes, and should update the control to show this.

}
class Menu.Group {

Groups are used to organize sets of menu items together. The top-level menu is a group, but groups may appear at any level, so that items with similar roles can attach themselves to them in order to appear next to each other.

See the spec type for the meaning of the class's fields.

  margin: boolean
  parent: Menu.Group | Menu.Submenu | undefined
  rank: number
  content: readonly (Menu.Item | "...")[] | undefined
  overflow: {at: number, wrap?: Menu.Submenu} | undefined
  extension: GardState.Extension

Menu groups count as extensions.

  spec: Menu.Group.Spec

The configuration object used to create this group.

  static define(spec: Menu.Group.Spec = {}): Menu.Group

Define a menu group.

  template(
    ...content: (Menu.Template | Menu.Item | "...")[]
  ): Menu.Template

Create a template for this group.

}
interface Menu.Group.Spec {

Options used to configure a menu group.

  margin?: boolean

When set to true, leave a bit of space between this group and adjacent items.

  parent?: Menu.Group | Menu.Submenu

The group's parent item, if any.

  rank?: number

The group's rank within its parent.

  content?: readonly (Menu.Item | "...")[]

Default content for this group. Usually you don't need this, as you let parent links from the content items determine what goes in the group. See the menu resolution system.

  overflow?: {at: number, wrap?: Menu.Submenu}

If given when, during resolution, the group contains more than at items, wrap items at - 1 and up in a submenu. You may optionally provide submenu object to specify the look of the submenu, or let it default to showing three vertical dots.

}
Menu.Group.top: Menu.Group

The top-level menu. When you don't provide a custom menu template, this is the starting point from which the menu will be resolved. Parent of most other groups.

Menu.Group.commands: Menu.Group

Editing commands. Holds items like the history undo/redo buttons.

Menu.Group.inline: Menu.Group

Inline style items. Will, by default, contain buttons to create emphasized text, links, and so on.

Menu.Group.block: Menu.Group

Group for block-related items. Holds things like list toggles and text alignment.

Menu.Group.insert: Menu.Group

Group for inserting elements into the document, such as images or tables.

class Menu.Submenu extends Menu.Item.Base {

A submenu is a menu item that, when activated, shows the menu items that are nested under it. See the spec type for the meaning of the class fields.

  label: Menu.Label | undefined
  defaultLabel: Menu.Label | undefined
  arrow: boolean
  width: number | undefined
  content: readonly (Menu.Item | "...")[] | undefined
  spec: Menu.Submenu.Spec

The configuration object used to define this submenu.

  static define(spec: Menu.Submenu.Spec): Menu.Submenu

Define a submenu.

  template(
    ...content: (Menu.Template | Menu.Item | "...")[]
  ): Menu.Template

Create a template item for this submenu.

}
interface Menu.Submenu.Spec extends Menu.Item.Spec {

The options that can be passed to a submenu.

  label?: Menu.Label

The label to show for the submenu. When not given, the submenu will look for the first active item in its children, and use that child's label, or fall back to defaultLabel.

  defaultLabel?: Menu.Label

Fallback label when no regular label is given and there are no active children.

  arrow?: boolean

Whether to show an arrow on the submenu button to indicate that it can be expanded. Defaults to true.

  width?: number

A base with for the submenu button, in CSS ch units. Can be useful when the menu uses a dynamic textual label, and you want to prevent it from changing size as its label changes.

  content?: readonly (Menu.Item | "...")[]

An optional default content. See the resolution system.

}
class Menu.Submenu.Resolved {

A resolved submenu, part of the output of Menu.resolve.

  item: Menu.Submenu

The submenu item.

  content: readonly Menu.Item.Resolved[]

The items inside the submenu.

}
Menu.Submenu.textblockStyle: Menu.Submenu

The submenu to select textblock type. Used to switch between, for example, regular paragraphs and headings

class Menu.Template {

Templates are used to explicitly choose (part of) your menu structure, rather than letting the resolution algorithm build one from your configuration. See Menu.resolve, Group.template, and Submenu.template.

}
Menu.resolve(
  items: readonly Menu.Item[],
  template: Menu.Template | readonly Menu.Template[] =
    Group.top.template(),
  suppress?: readonly Menu.Item[]
): readonly Menu.Item.Resolved[]

Given a set of menu items and optionally a template, this function will resolve a concrete menu tree. To do this, it goes through the template (which defaults to just the top group), filling in open spaces (represented as the string literal "...") with any items provided that have the group or submenu as parent.

The idea is to combine a top-down (the template) and bottom-up (the items, which typically come from an editor configuration) in a way that allows the user to figure out a balance between manually specifying their menu and just using whatever is in the configuration.

Items that are used explicitly in a template will not be used again implicitly. Items included in the suppress parameter will be ignored.

When a submenu or group specifies default content, this will only be used when the template does not specify its own content for the item.

Basic Editing Commands

Commands for fundamental editing actions.

insertText: Command.Pure<{
  from: number,
  to: number,
  insert: string,
  userEvent: string
}>

This command handles text input. To selectively override the behavior of text input, provide a handler that, when the conditions that it requires apply, handles the input and returns true. userEvent will generally be one of "input.type", "input.type.compose" (text inserted as part as a composition), or "input.type.compose.start" (initial text created by a started composition).

insertLineBreak: Command.Pure

Command to insert a line break. The default handler will, if the schema defines a line break node and the selection's parent node allows that, insert such a node. Otherwise, in nodes marked as whitespace-preserving, this will insert a line break.

enter: Command.Pure

The command that handles enter presses. The default handler will, if the selection is not in an inline context, insert an empty default textblock in its position. Otherwise it first tries liftEmptyTextblock, then splitTextblock.

transposeChars: Command.Pure

Swap the characters before and after the cursor.

undo: Command

Undo an edit. Does not have a default handler, but a handler is added by the history extension.

redo: Command

Redo an edit. Does not have a default handler.

Selection Commands

Commands that move the selection. Most take an extend flag that controls whether they move or extend the selection.

moveByUnit: Command.Pure<{
  dir: "left" | "right" | "forward" | "backward",
  extend?: boolean
}>

Move the selection head one unit (text cluster, atomic node, or node boundary) in the indicated direction. When extend is true, keep the selection anchor in place. The default handler moves visually through bidirectional text, so when going left, the motion will go back in left-to-right text, and forward in right-to-left text.

moveByWord: Command.Pure<{
  dir: "left" | "right",
  extend?: boolean
}>

Move the selection head one word in the indicated direction. Keep the anchor in place if extend is true. The default handler moves visually.

moveByLine: Command<{dir: "up" | "down", extend?: boolean}>

Move the selection head one line up or down. When extend is true, keep the anchor in place.

moveByPage: Command<{dir: "up" | "down", extend?: boolean}>

Move the selection head one page up or down. Extend the selection when the extend flag is true.

moveToLineSide: Command<{
  dir: "left" | "right" | "forward" | "backward",
  extend?: boolean
}>

Move to the indicated side of the current line. Will stop at line wrap points. "left" and "right" will be interpreted based on the editor's text direction.

moveToTextblockSide: Command<{
  dir: "left" | "right" | "forward" | "backward",
  extend?: boolean
}>

Move to the start or end of the textblock that has the selection head.

moveToDocSide: Command.Pure<{
  side: "start" | "end",
  extend?: boolean
}>

Move to the start or end of the document.

selectAll: Command.Pure

Select the entire document.

Deletion Commands

Commands that delete content.

deleteUnit: Command.Pure<"forward" | "backward">

Delete the selection, or the unit after or before the selection. If that unit is the start or end of a textblock, this will try to join that textblock to the next one. Otherwise, if it is a character or leaf node, that is deleted. If none of that is possible and the cursor is in an empty textblock, this will delete the textblock.

When deleting backward at the start of a list item that has a sibling before it, this command will try to join those list items.

deleteWord: Command.Pure<"forward" | "backward">

Delete the selection, or the word next to it. Will behave like deleteUnit, except that, when deleting text, it will delete an entire word.

deleteToLineEnd: Command<"forward" | "backward">

Delete to the end or start of the line. Stops at line wrapping points.

deleteLine: Command

Delete the selection, or if that is empty, the line around the cursor.

Block Manipulation Commands

Commands that act on the document's block structure.

setTextblockType: Command.Pure<Plot.Tag>

Set the type of the textblock(s) around the selection to the given tag.

unwrapBlock: Command.Pure<Node.Query | null>

Try to unwrap blocks around the selection. The second argument, if given, indicates what kind of wrapping plots may be removed. Returns null when no unwrapping is possible.

wrapBlock: Command.Pure<Plot.Tag>

Try to wrap selected textblocks in the given wrapper. Will return null if no wrapping is possible.

toggleBlock: Command.Pure<Plot.Tag>

If the selection is in a block of the given type, unwap it. Otherwise, try to wrap the selected blocks in such a tag.

setAlignment: Command.Pure<
  "center" | "start" | "end" | "left" | "right" | null
>

Set the selected textblocks to the given alignment. "left" and "right" will be normalized to "start" or "end" depending on the editor's text direction. The default implementation uses the Alignment mark.

setDirection: Command.Pure<"auto" | "ltr" | "rtl" | null>

Set the text direction for the selected textblocks. null will remove an explicit direction mark, defaulting the blocks back to the editor's base direction. The default implementation uses the Direction mark.

toggleList: Command.Pure<Plot.Tag>

Toggle list wrapping with the given list tag for the selected blocks.

Inline Mark Commands

Commands that toggle inline marks.

toggleMark: Command.Pure<Mark>

Toggle the given mark. If there is no selection, it is added to the cursor's active marks, or removed if it is already in there. Otherwise, if any selected content allows for the mark to be added, it is added. If not, remove the mark from the selection.

toggleEmphasis: Command.Pure

Toggle emphasis. The default implementation uses the Emphasis mark.

toggleStrong: Command.Pure

Toggle strong emphasis. The default implementation uses the Strong mark.

toggleUnderline: Command.Pure

Toggle underlining. The default implementation uses the Underline mark.

Utility Functions

Functions that may be useful when implementing your own commands.

listIsActive: (
  listTag: Plot.Tag
) => (state: GardState) => boolean

Returns true when all selected textblocks are wrapped in a list of the given type.

liftEmptyBlock(state: GardState): false | Transaction.Spec

If the cursor is in an empty textblock that can be lifted out of a parent, return a transaction that does this.

splitTextblock(
  state: GardState,
  splitListItem: boolean = true
): false | Transaction.Spec

Split the textblock at the cursor position, if any. If the textblock is the first child of a list item, also split that item, unless splitListItem is false.

deleteSelection(state: GardState): false | Transaction.Spec

Returns a transaction that deletes the selection, or false if the selection is empty.

deleteBackward(
  state: GardState,
  word: boolean = false
): false | Transaction.Spec

Create a transaction that deletes the atomic node or text cluster/word in front of the cursor, if possible.

deleteForward(
  state: GardState,
  word: boolean = false
): false | Transaction.Spec

Return a transaction that deletes the element (text cluster or leaf node) after the cursor, if any.

deleteEmptyTextblock(
  state: GardState,
  dir: 1 | -1 = -1
): false | Transaction.Spec

If the cursor is inside an empty textblock, return a transaction that deletes the entire block. dir determines which way the cursor moves after the deletion.

joinBackward(state: GardState): false | Transaction.Spec

If the cursor is at the start of a textblock that can be joined to a textblock before it, return a transaction to performs this join.

joinListItems(state: GardState): false | Transaction.Spec

If the cursor is at the start of a list item that has another item before it, return a transaction that joins those two items.

joinForward(state: GardState): false | Transaction.Spec

If the cursor is at the end of a textblock that can be joined to the textblock after it, return a transaction that performs this join.

joinBlocks(before: Pos.Plot, after: Pos.Plot): ChangeSet.Spec

Join two adjacent (only separated by first a sequence of block end tokens and then a sequence of block open tokens) block plots.

selectedTextblocks(state: GardState): Pos.Plot[]

Get an array of all textblocks that contain part of the selection.

clearNonFitting(
  schema: Schema,
  node: Pos.Plot,
  type: Plot.Type
): ChangeSet.Spec

Remove all content in node that is not allowed to appear in type.

findWrappable(
  from: Pos,
  to: Pos,
  wrapper: Plot.Tag
): {from: Pos, to: Pos} | null

Find a way to wrap the blocks betwen from and to in a node with the given tag. Returns precise start and end positions where a wrap is possible, or null if none is possible.

wrapBlockRange(
  range: {from: Pos, to: Pos},
  wrapper: Plot.Tag
): ChangeSet.Spec[]

Wrap the given range in the given wrapper tag. The caller is responsible for verifying that this is actually a valid wrapping. It is recommended to use findWrappable for finding wrap positions in non-trivial situations.

findUnwrappable(
  schema: Schema,
  from: Pos,
  to: Pos,
  query?: Node.Query
): Pos.Plot[] | null

Find the set of block nodes around the given range that match the predicate (if any) and can be unwrapped, meaning their content gets moved out to a parent node.

doUnwrapBlock(
  block: Pos.Plot,
  from?: number,
  to?: number
): ChangeSet.Spec

Unwrap the given block node, or the node's children between from and to.

canAddMarkInRange(
  doc: Plot.Doc,
  from: number,
  to: number,
  mark: Mark.Type | Mark
): boolean

Query whether the given range has any content to which the given mark or mark type could be added.

autoJoinBlocks(
  state: GardState,
  tr: Transaction.Spec
): Transaction.Spec

Post-process the given transaction spec to check for any block boundaries touched by the changes in it that can be auto-joined. If any are found, the spec is updated to perform those joins.


wordgard/history

This module exports an undo history system. You enable it by simply including history() in your configuration.

history(config: {
  minDepth?: number

The minimum depth (amount of events) to store. Defaults to 100.

  newGroupDelay?: number

The maximum time (in milliseconds) that adjacent events can be apart and still be grouped together. Defaults to 500.

  joinToEvent?: (tr: Transaction, isAdjacent: boolean) => boolean

By default, when close enough together in time, changes are joined into an existing undo event if they touch any of the changed ranges from that event. You can pass a custom predicate here to influence that logic.

} = {}): GardState.Extension

Create a history extension with the given configuration. Will include the state field that tracks history, handlers for the undo and redo commands that make use of it, and the undo/redo menu buttons.

history.field: GardState.Field<unknown>

The state field used to store the history data. Should probably only be used when you want to serialize or deserialize state objects in a way that preserves history.

history.isolate: Transaction.Annotation.Type<
  true | "before" | "after"
>

Transaction annotation that will prevent that transaction from being combined with other transactions in the undo history. Given "before", it'll prevent merging with previous transactions. With "after", subsequent transactions won't be combined with this one. With true, the transaction is isolated on both sides.

history.invertedEffects: GardState.Facet<
  (tr: Transaction) => readonly Transaction.Effect<any>[]
>

This facet provides a way to register functions that, given a transaction, provide a set of effects that the history should store when inverting the transaction. This can be used to integrate specific effects in the history, so that they can be undone (and redone again).

type history.EventJSON = {
  changes: ChangeSet.JSON,
  selection: unknown
}
type history.JSON = {
  done: readonly history.EventJSON[],
  undone: readonly history.EventJSON[]
}
undo: Command.Pure

Undo a single group of history events. Returns false if no group is available.

redo: Command.Pure

Redo a single group of undone history events. Returns false if no group is available.

undoDepth: (state: GardState) => number

The amount of undoable change events available in a given state.

redoDepth: (state: GardState) => number

The amount of redoable change events available in a given state.

undoButton: Menu.Button

A menu button that undoes a change.

redoButton: Menu.Button

A menu button that redoes an undone change.


wordgard/types

This module provides a collection of basic schema elements. It is possible to define your own custom elements, but often convenient to use the ones provided here, which come with support extensions in the wordgard/schema module. This module depends on only on wordgard/doc and is kept separate from wordgard/schema so that you can access the types without loading the editor code, when appropriate.

Block Structure

Nodes and marks for structuring documents on the block level.

Paragraph: Plot.Tag<null>

A paragraph. Part of the Content group, allowing inline content, rendered as <p>.

Heading: Plot.Type<number>

Heading plot. Its parameter indicates the heading level. In the Content group, allows inline content, rendered as <h1> to <h6>.

CodeBlock: Plot.Tag<null>

A code block. Part of the Content group, with the Code role. Rendered as <pre>.

CodeBlockLanguage: Mark.Type<string>

A mark that assigns a language identifier (any string) to a code block. Stored in the data-language attribute.

Blockquote: Plot.Tag<null>

Blockquote plot. Allows any Content blocks inside of it, and is itself part of that group. Rendered as <blockquote>.

ListItem: Plot.Tag<null>

Block list item. Allows any Content blocks as content. Rendered as <li>.

InlineListItem: Plot.Tag<null>

List item with inline content. You'll want to have either this plot type or ListItem in your schema, not both. Rendered as <li>.

OrderedList: Plot.Type<number>

Ordered list plot. Part of the Content group, allows either ListItem or InlineListItem as content. Rendered as <ol>, with the parameter providing the start attribute.

BulletList: Plot.Tag<null>

An unordered list. Defaults to the Content group. Allows ListItem or InlineListItem as content. Rendered as <ul>.

HorizontalRule: Leaf<null>

A horizontal separator. Part of the Content group. Renders as <hr>.

Alignment: Mark.Type<"center" | "end">

Text alignment. Applies to any textblock or figure. Not having this mark implies aligning to the start of the block. Renders as a text-align style.

Direction: Mark.Type<"auto" | "ltr" | "rtl">

The text direction for a textblock. Not having this mark means the document's base direction is used. "auto" means the direction is derived from the first character in the block that has a strong direction. Rendered using the dir attribute.

Doc: Plot.Type<null>

A document type that allows any Content group nodes as content.

InlineDoc: Plot.Type<null>

A document type that has inline content, so the entire document is a single textblock.

Inline Structure

Marks and nodes for inline content.

LineBreak: Leaf<null>

A hard line break. Renders as <br> and has the LineBreak role.

Emphasis: Mark<null>

Emphasis mark. Rendered using the <em> element.

Strong: Mark<null>

Strong emphasis mark. Rendered using the <strong> element.

Underline: Mark<null>

Underline mark. Rendered using <u>.

Strikethrough: Mark<null>

Strikethrough mark. Rendered as an <s> element.

Superscript: Mark<null>

Superscript text. Rendered using <sup>.

Subscript: Mark<null>

Subscript text. Rendered using <sub>.

Link: Mark.Type<string>

Link markup. The parameter is the link's target. Rendered as an <a> element with the target in href.

Code: Mark<null>

Code font text. Rendered as <code>.

Color: Mark.Type<string>

Text color mark. Rendered with the color style.

BackgroundColor: Mark.Type<string>

Text background color. Rendered with the background-color style.

Images

Image types and their marks.

Image: Leaf.Type<string>

An inline image leaf. The string parameter is the image's source URI. Rendered as <img>.

Figure: Leaf.Type<string>

A block image leaf. Renders as a <figure> element with an <img> in it. The parameter holds the image URI.

CaptionedFigure: Plot.Type<string>

A textblock plot representing a captioned image. The image URI is the plot's parameter, and the caption the content (inline). Assigned to the Content group by default. Renders as a <figure> element with <img> and <figcaption> elements as children.

ImageAlt: Mark.Type<string>

Image alt text. Renders as an alt attribute.

ImageSize: Mark.Type<number>

Image size, as a width in pixels. Renders as a width style.

Tables

Nodes and marks used to define tables. See wordgard/table for the supporting extensions that make table editing possible.

Cell: Plot.Tag<null>

Table cell plot. Allows inline content, and is in the TableCell group. Rendered as <td>

HeaderCell: Plot.Tag<null>

Table header cell. Like Cell, but rendered as <th>.

BlockCell: Plot.Tag<null>

Table cell with block content. Rendered as <td>. You can use either this one or Cell in your schema, not both.

BlockHeaderCell: Plot.Tag<null>

Table header cell with block content. Rendered as <th>.

TableRow: Plot.Tag<null>

Table row. Allows nodes with the TableCell group as content. Rendered as <tr>.

Table: Plot.Tag<null>

A table. Contains TableRows, is part of the Content group. Rendered as <table><tbody>.

ColSpan: Mark.Type<number>

Table cell column span. Rendered with the colspan attribute. Must hold a positive integer. Is assumed to be 1 when missing.

RowSpan: Mark.Type<number>

Table cell row span. Rendered with the rowspan attribute. Is assumed to be 1 when missing.


wordgard/schema

This module provides editing support extensions and convenient bundle functions for most of the schema elements defined in wordgard/types.

Block Structure

Extension bundle functions and individual extensions related to block-level document structure.

paragraph(): GardState.Extension

The basic paragraph schema element, with a menu button and key binding to switch to it.

paragraph.keyBinding: KeyBinding

Binds Ctrl-Shift-0 to switching the selected textblocks to regular paragraphs.

paragraph.button: Menu.Button

Button in the textblockStyle menu that makes the selected textblocks paragraphs.

heading(): GardState.Extension

Support for heading blocks. Includes the schema element, some key bindings, menu buttons, and an input rule to switch to this block type.

heading.keyBindings: KeyBinding[]

Binds Ctrl-Shift-1 through Ctrl-Shift-6 to make the selected textblocks headings of the given level.

heading.button1: Menu.Button

Button for the textblock style menu that switches to a level 1 heading.

heading.button2: Menu.Button

Menu button that switches to a level 2 heading.

heading.button3: Menu.Button

Menu button that switches to a level 3 heading.

heading.createOnHash: InputRule

Input rule that will switch to a heading when one to six hash characters, followed by a space, are typed at the start of a textblock, using the number of hash characters to determine the heading level.

codeBlock(): GardState.Extension

Extensions to add support for code blocks. Includes the schema element, a key binding, a menu button, and an input rule.

codeBlock.keyBinding: KeyBinding

Binds Ctrl-Shift-\ to switch the selected textblocks to a code block.

codeBlock.button: Menu.Button

Button for the textblock style menu that switches to a code block.

codeBlock.createOnBackticks: InputRule

Input rule that switches the current textblock to a code block when you type three backticks at its start.

alignment(): GardState.Extension

Extensions that add support for text alignment—the Alignment mark, a set of key bindings, and a menu button.

alignment.keyBindings: KeyBinding[]

Bind Mod-Shift-l to left-align, Mod-Shift-r to right-align, and Mod-Shift-e to center.

alignment.buttonStart: Menu.Button

A button that sets text alignments to start.

alignment.buttonEnd: Menu.Button

A button that sets text alignment to end.

alignment.buttonCenter: Menu.Button

Menu button that sets text alignment to center.

alignment.button: Menu.Submenu

A button that pops up a submenu for alignment choice. Includes alignment.buttonStart, alignment.buttonEnd, and alignment.buttonCenter as default content, so if you include this you don't have to explicitly include those.

direction(): GardState.Extension

Add support for selecting a text direction per textblock. Includes the Direction mark, the extension to make the editor interpret that, and a menu button.

direction.textblockDir: GardState.Extension

This extension makes the editor state understand the effect the Direction mark has on text direction, so that cursor motion and such behaves correctly in blocks that have it.

direction.buttonLTR: Menu.Button

Menu button to choose a left-to-right text direction.

direction.buttonRTL: Menu.Button

Button to choose a right-to-left text direction.

direction.buttonAuto: Menu.Button

Button that enables automatic text direction, where the content of the textblock determines direction.

direction.button: Menu.Submenu

Menu button for choosing text direction. Includes buttonLTR, buttonRTL, and buttonAuto buttonAuto as default content.

blockquote(): GardState.Extension

Support for blockquotes. Adds the schema element, a menu button, an input rule, and a default style.

blockquote.button: Menu.Button

Menu button that toggles a blockquote wrapper.

blockquote.createOnGT: InputRule

Input rule that wraps the current block in a blockquote when you type a greater-than sign followed by a space at its start.

blockquote.theme: GardState.Extension

Simple style that shows a border next to blockquotes to make them easy to see.

horizontalRule(): GardState.Extension

Support extension for horizontal rules. Provides the schema element and an input rule.

horizontalRule.createOnDashes: InputRule

Input rule that will create a horizontal rule if you type three dashes into an empty textblock.

bulletList(config: {
  blockItems?: boolean

By defaults, list items contain block content. Set this to false to allow only inline content.

} = {}): GardState.Extension[]

Enable support for bullet lists. Includes the schema elements, the menu button, and the input rule.

bulletList.createOnDash: InputRule

This rule wraps the current textblock in a bullet list when the user starts it by typing an optional space, a dash, and then another space.

bulletList.toggleButton: Menu.Button

A menu button that toggles bullet list wrapping for the selection.

orderedList(config: {
  blockItems?: boolean
} = {}): GardState.Extension[]

Returns extensions that enable ordered list support, including the schema elements, menu button, and input rule.

orderedList.createOnNumber: InputRule

An input rule that, when the user starts a textblock with an optional space, a number, a period, and a space, wraps the block in an ordered list.

orderedList.toggleButton: Menu.Button

A menu button that toggles an ordered list wrapper for the selected textblocks.

blockDoc(): GardState.Extension

Schema element that provides the outer document plot type for a document with block content.

inlineDoc(): GardState.Extension

Outer document schema element for a document that contains only inline content.

Inline Structure

Extensions related to inline content.

lineBreak(): GardState.Extension

Returns an extension that enables line breaks.

strong(): GardState.Extension

Extension that enables strong emphasis support. Includes the schema element, key binding, and menu button.

strong.keyBinding: KeyBinding

Binds Mod-b to toggle strong emphasis.

strong.button: Menu.Button

A menu button that toggles strong emphasis.

emphasis(): GardState.Extension

Returns extensions for the emphasis mark—the schema element, a key binding, and a menu button.

emphasis.keyBinding: KeyBinding

Binds Mod-i to toggle emphasis.

emphasis.button: Menu.Button

Menu button that toggles emphasis.

code(): GardState.Extension

Returns extensions that enable the code font mark. Includes the schema element, a key binding, and a menu button.

code.keyBinding: KeyBinding

Binds Mod-` to toggle the code font mark.

code.button: Menu.Button

Menu button for toggling code font.

underline(): GardState.Extension

Returns an extension bundle for the underline mark, including the schema element, a key bindding, and a menu button.

underline.keyBinding: KeyBinding

Binds Mod-u to toggle the underline mark.

underline.button: Menu.Button

Menu button for toggling underline.

strikethrough(): GardState.Extension

Extensions to support a strikethrough mark—the schema element, a key binding, and a menu button.

strikethrough.keyBinding: KeyBinding

Binds Mod-/ to toggle strikethrough.

strikethrough.button: Menu.Button

Menu button that toggles the strikethrough mark.

superscript(): GardState.Extension

Add support for a superscript mark. Includes the schema element, a key binding, and a menu button.

superscript.keyBinding: KeyBinding

Binds Mod-. to toggle superscript.

superscript.button: Menu.Button

Menu button for toggling superscript.

subscript(): GardState.Extension

Support for a subscript mark. Includes the schema element, a key binding, and a menu button.

subscript.keyBinding: KeyBinding

Binds Mod-, to toggle the subscript mark.

subscript.button: Menu.Button

Menu button for toggling subscript.

color(): GardState.Extension

Adds support for a text color mark. Includes the schema element, the menu button, and the color picker styles.

color.button: Menu.Submenu

A button that shows a color picker to change the text color.

backgroundColor(): GardState.Extension

Adds support for a background color mark. Includes the schema element, the menu button, and the color picker styles.

backgroundColor.button: Menu.Submenu

A button that expands a submenu with a color picker that can be used to change the text background color.

class ColorPicker {

User interface component that shows a grid of colors and allows the user to pick one, for use in a menu.

  dom: HTMLElement
  static create(
    wg: Wordgard,
    finish: (color: string) => void
  ): ColorPicker

Construct the color picker. finish will be called when a color is selected.

}
type ColorPicker.Option = {

The type used for color options.

  name: PhraseSet.Ref

Reference to a phrase giving the name of the color.

  detail?: PhraseSet.Ref

An optional second phrase to show after the name.

  value: string

The color as a CSS color string, or the empty string to indicate that this option clears the color.

}
ColorPicker.width: GardState.Facet<number, number>

Facet used to configure the width (in color widgets) of the color picker. Defaults to 10.

ColorPicker.options: GardState.Facet<
  readonly ColorPicker.Option[],
  readonly ColorPicker.Option[]
>

Facet used to configure color picker options. The default is a collection of 80 colors, starting with an empty one for clearing color.

ColorPicker.theme: GardState.Extension

Base theme for the color picker.

link(): GardState.Extension

Extensions for a link mark—the schema element, a key binding, a menu button, the link tooltip, and the paste-link handler.

link.keyBinding: KeyBinding

Binds Mod-k to toggle the link mark.

link.button: Menu.Button

Menu button that will remove the link mark from the selection if present, or prompt for a target and make the current selection a link.

link.tooltip: GardState.Extension

Extension that displays a tooltip with the link target below the cursor when that is in a link.

link.pasteOver: GardState.Extension

Registers a paste handler that, when a URI is pasted over a selection, will add a link to the selection instead of replacing it with the pasted text.

Images

Support for images and a dialog to create or modify them.

image(): GardState.Extension

Returns extensions that add support for inline images. That includes the schema element, the alt text mark, the menu button, a key binding, and a drop handler for image files.

image.keyBinding: KeyBinding

Binds Ctrl-Alt-i to open the image insert/update dialog.

image.button: Menu.Button

A menu button that opens a dialog to insert an image or figure, or to adjust the currently selected image or figure.

image.dropHandler: GardState.Extension

A custom drop handler that checks whether an image file is being dropped. When an uploader has been defined, it will feed the file to that, and insert an image with the resulting URI when it finishes.

image.insert: Command

The command that opens the image dialog.

image.uploader: GardState.Facet<(
  file: File,
  wg: Wordgard,
  progress: (percent: number) => void
) => Promise<string>>

A facet that you can use to register a handler for image uploads. Your function will be passed a file object, an editor instance, and a function that it can call to indicate progress, and should return a promise that resolves to a URI for the uploaded image.

figure(conf: {
  captioned?: boolean

When enabled, also support captioned figures.

} = {}): GardState.Extension

Support for block figures. Adds the same support extensions as image, but includes the Figure schema element instead.

imageResizing(): GardState.Extension

Add resizing functionality for images and figures. Includes the ImageSize mark, the drag handle, and key bindings.

imageResizing.resizeCommand: (
  by: number,
  relative: boolean = false
) => Command

Returns a command that resizes the currently selected image or figure. When relative is fale, by indicates a pixel amount, which can be positive or negative. When it is true, by is a scaling factor, which should be between 0 and 1 to shrink the image, and greater than1 to grow it.

imageResizing.keyBindings: KeyBinding[]

Binds Ctrl-Alt-l (Ctrl-Cmd-l on Mac) to grow an image by 10%, and Ctrl-Alt-k (Ctrl-Cmd-k on Mac) to shrink it by 10%.

imageResizing.dragHandle: GardState.Extension

Extension that displays a drag handle when the user hovers over an image or figure, which can be dragged to adjust the element's width.

Bundles

Helper functions that bundle the extensions provided by this module into groups, so that they can easily be included in a configuration.

basicMarks(): GardState.Extension

Enable the strong, emphasis, and link marks.

inlineMarks(): GardState.Extension
basicSchema(): GardState.Extension

Add the elements of a simple rich text schema: blockDoc, basicMarks, paragraph, heading, and lineBreak.

inlineSchema(): GardState.Extension

Add the extensions for a simple schema where the document is a single textblock: inlineDoc, basicMarks, image, and lineBreak.

fullSchema(): GardState.Extension

Returns an extension with all the schema elements included in this module, in a block document. Note that new elements may appear in this schema as new versions of the library add new features.


wordgard/table

Table editing support. Defines a custom cell selection type, a number of commands, and some support extensions to make table editing work.

Support

Miscellaneous extensions needed for working with tables.

tables(config: tables.Spec = {}): GardState.Extension

Enable table support with the given configuration. The returned extension will include the necessary schema elements, the cell selection, correction, paste and drop handlers, and menu items.

type tables.Spec = {

Configuration options for the tables function.

  headerCells?: boolean

Controls whether headers cells are enabled. Defaults to true.

  cellSpanning?: boolean

By default, cells can be merged, making them span more than one row or column. Set this to false to disable that.

  cellContent?: "inline" | "block"

Determines whether cells contain inline content or block content. Defaults to "inline".

}
tables.correction: Correction

Correct tables where the cells do not form a proper rectangle.

tables.pasteHandler: GardState.Extension

A paste handler that makes pasting into tables fill the entire cell selection or, if table cell content is being pasted into a cell, to expand the pasted region over the shape covered by the pasted content.

tables.dropHandler: GardState.Extension

A drop handler that overrides drops that move table cells.

tableMenu(): GardState.Extension

Returns the full table menu. This consists of two buttons that look the same but behave differently. When the selection is not in a table, tableMenu.createTable is shown, which provides an interface for creating a new table. Otherwise, tableMenu.modifyTable is visible, which contains menu items for manipulating the current table.

tableMenu.createTable: Menu.Submenu

A menu button that expands into a dimension picker when activated, which inserts a table with the given dimensions when confirmed.

tableMenu.modifyTable: Menu.Submenu

A submenu with for table manipulation items.

tableMenu.toggleHeader: Menu.Button

A button that toggles the selected cells between header and normal cells.

tableMenu.addRowAbove: Menu.Button

Adds a row above the selected cell(s).

tableMenu.addRowBelow: Menu.Button

Adds a row below the selected cell(s).

tableMenu.deleteRow: Menu.Button

Deletes the row(s) that hold the selection.

tableMenu.addColumnBefore: Menu.Button

Button to insert a column before the selection.

tableMenu.addColumnAfter: Menu.Button

Add a column after the selection.

tableMenu.deleteColumn: Menu.Button

Delete the selected column(s).

tableMenu.mergeCells: Menu.Button

When multiple cells are selected, merge them into a single cell.

tableMenu.splitCell: Menu.Button

When a merged cell is selected, split it into its smallest elements again.

Cell Selection

Custom handling of cross-cell selections makes it possible to select rows, columns, or rectangles in a table.

class CellSelection extends GardSelection {

A cell selection is a custom selection type that, instead of spanning the range between two document positions, spans the rectangle between two cells in the same table. It comes with extensions that make sure it is drawn in a recognizeable way, automatically created when a selection crosses cell boundaries, and adjusted appropriately in response to keyboard and mouse actions.

  anchorCell: number

The position directly in front of the cell that acts as this selection's anchor.

  headCell: number

The position directly in front of the cell acting as the selection head.

  moveHead(
    doc: Plot.Doc,
    dir: "forward" | "backward" | "up" | "down"
  ): CellSelection | null

Move the head cell in the given direction, returning a new cell selection if the selection can be extended that way.

  static between(
    doc: Plot.Doc,
    anchor: number,
    head: number
  ): CellSelection | null

Create a table selection between the given points. They should point before and after the first and last cell of the selection (anchor may be before or after head, as long as the lower sits before a cell and the higher after a cell). Returns null if the given range does not wrap a range of cells in a single table.

  static normalize(
    sel: GardSelection,
    doc: Plot.Doc
  ): GardSelection | null

Given a selection, this returns null if that selection is valid (is a cell selection, or a selection that starts and ends outside of tables, or in the same table cell). Otherwise, it will expand a selection within a single table to a cell selection, or expand a selection that crosses table boundaries to cover the entire table(s).

  static extension: GardState.Extension

This object can be used as an extension to enable cell selection.

}

Commands

Table editing commands.

addColumn: Command.Pure<"before" | "after">

Add a column at the given side of the selection. The selection may either be a cell selection or another type of selection inside a table cell.

addRow: Command.Pure<"before" | "after">

Add a row next to the selected cells, on the side indicated.

deleteColumn: Command.Pure

Delete the selected columns. Will act on a cell selection or another type of selection inside a cell.

deleteRow: Command.Pure

Delete the selected row(s).

toggleHeaderCell: Command.Pure
mergeCells: Command.Pure

If the current selection is a multi-cell cell selection, merge the cells into a single cell by giving it a RowSpan and/or ColSpan. The content from the cells is all put into the resulting merged cell.

splitCell: Command.Pure

When the selection is in or on a merged cell, split it again. Any content is left in the first split-off cell.


wordgard/collab

An implementation of basic collaborative editing. See the example for more information.

collab(config: collab.Config = {}): GardState.Extension

Create an instance of the collaborative editing plugin.

type collab.Config = {

Options to collab.

  startVersion?: number

The starting document version. Defaults to 0.

  clientID?: string

This client's identifying ID. Will be a randomly generated string if not provided.

  corrections?: readonly Correction[]

A set of corrections to apply to transformed changes. Can be used to enforce document shapes even in merged changes. Requires the exact same set, in the same order, to be used on the server.

  sharedEffects?: (tr: Transaction) =>
    readonly Transaction.Effect<any>[]

It is possible to share information other than document changes through this extension. If you provide this option, your function will be called on each transaction, and the effects it returns will be sent to the server, much like changes are. Such effects are automatically remapped when conflicting remote changes come in.

}
interface collab.Update {

An update is a set of changes and effects.

  version: number

The document version that this update starts from.

  clientID: string

The ID of the client who created this update.

  changes: ChangeSet

The changes made by this update.

  effects?: readonly Transaction.Effect<unknown>[]

The effects in this update. There'll only ever be effects here when you configure your collab extension with a sharedEffects option.

}
collab.receive(
  state: GardState,
  updates: readonly collab.Update[]
): Transaction

Create a transaction that represents a set of new updates received from the authority. Applying this transaction moves the state forward to, for remote changes, integrate them into our local state, and for our own changes, drop them from the set of unconfirmed local changes.

collab.sendableUpdate(state: GardState): collab.Update | null

If there are unconfirmed local changes that need to be sent to the server,return them as an Update object.

collab.hasUnsentUpdate(state: GardState): boolean

Returns true if there are unconfirmed changes for this editor.

collab.getSyncedVersion(state: GardState): number

Get the version up to which the collab plugin has synced with the central authority.

collab.getClientID(state: GardState): string

Get this editor's collaborative editing client ID.

collab.transformUpdate(
  update: collab.Update,
  over: readonly {
    doc: Plot.Doc,
    changes: ChangeSet,
    clientID: string
  }[],
  corrections?: readonly Correction[]
): collab.Update | null

Transform an update that arrives on the server with an outdated start version. Being able to do this requires tracking a document history server-side. It is not necessary to do this, but it helps a lot with “starvation” problems, where slower clients or clients with a high ping can, in a busy document, keep losing the race to submit their changes to other, faster clients.


wordgard/phrases

This module defines a simple abstraction for managing translatable text, as well as instances of that holding the text used by the other modules of this package.

class PhraseSet<Tags extends string> {

A phrase set defines a number of text phrases to display in the user interface, and makes translation of those phrases possible. It associates each phrase with a tag. The type parameter to this class is the set of tag names it defines.

  phrases: any
  get<Tag extends Tags>(
    state: GardState,
    tag: Tag,
    ...insert: any[]
  ): string

Look up a translation for phrase with the given tag.

If additional arguments are passed, they will be inserted in place of markers like $1 (for the first value) and $2, etc. A single $ is equivalent to $1, and $$ will produce a literal dollar sign.

  ref<Tag extends Tags>(tag: Tag): PhraseSet.Ref

Create a reference to a phrase. Returns a function that can be called with an editor state to get the phrase text.

  translate(phrases: any): GardState.Extension

Create a translation of this set. Adding the resulting extension to an editor configuration will cause access to the phrases in the set to return the translated text.

  translatePartial(phrases: any): GardState.Extension

Create a partial translation of this set. The only difference with translate is that this method won't cause a type error when you omit some tags.

  static define<
    Tags extends string
  >(phrases: {[tag in Tags]: string}): PhraseSet<Tags>

Define a new phrase set. Takes an object as argument that defines the default (usually English) text for all the tags in the set.

  static didChange(a: GardState, b: GardState): boolean

Check whether the phrase set configuration changed between the two given states. Can be useful to check whether some part of the interface needs to be redrawn.

}
type PhraseSet.Ref = (
  state: GardState,
  ...insert: any[]
) => string

A reference to a specific phrase. Call it with a state and, optionally, the inserted values you'd pass to PhraseSet.get to get a phrase.

type PhraseSet.Tag<
  Set extends PhraseSet<any>
> = Set extends PhraseSet<infer T> ? T : never

Get the set of tags defined by a given phrase set, as a type.

phrases: PhraseSet<
  "dialog_close" | "overflow_more" | "block_style" |
    "toggle_strong" | "toggle_em" | "toggle_code" | "toggle_underline" |
    "toggle_strikethrough" | "toggle_super" | "toggle_sub" | "link_target" |
    "create_link" | "text_color" | "background_color" | "undo" | "redo" |
    "paragraph" | "code_block" | "heading_1" | "heading_2" | "heading_3" |
    "toggle_bullet_list" | "toggle_ordered_list" | "toggle_quote" | "alignment" |
    "align_start" | "align_end" | "align_center" | "text_dir" |
    "text_dir_ltr" | "text_dir_rtl" | "text_dir_auto"
>

The phrase set for the core package and basic menu items.

imagePhrases: PhraseSet<
  "figure" | "auto" | "width" | "cancel" | "insert" | "inline" |
    "insert_image" | "update_image" | "update" | "figure_center" |
    "figure_end" | "captioned" | "image_style" | "uploading" |
    "upload_failed" | "upload_image" | "image_source" | "alt_text" |
    "describe_image"
>

Phrases used by the image dialog.

colorNames: PhraseSet<
  "dark" | "light" | "none" | "black" | "white" | "grey" |
    "red_berry" | "red" | "orange" | "yellow" | "green" | "cyan" |
    "cornflower" | "blue" | "purple" | "magenta" | "darker" | "darkest" |
    "lighter" | "lightest"
>

Phrases (mostly color names) used by the color picker.

tablePhrases: PhraseSet<
  "dimensions_title" | "dimensions_live" | "insert_table" |
    "modify_table" | "toggle_header" | "add_row_above" | "add_row_below" |
    "delete_row" | "add_col_before" | "add_col_after" | "delete_col" |
    "merge_cells" | "split_cell"
>

Phrases used by wordgard/table.