Table of Contents

smudgy:core — Lines & buffer

Generated from smudgy v0.5.4-dev (smudgy-core.d.ts @ a1c9d1939ffb). Index: scriptref.

Read and edit the text on screen: the line a trigger is processing right now (line), recently printed lines (buffer), colors and styling, and alias capture control.

line

export const line: Line;

The line being processed right now. Meaningful while a line is being processed (its trigger and receive cascade and anything that runs before it is delivered): with no line in flight, reads come back empty and edits throw. Deferred code (an await continuation, a timer) acts on whichever line is in flight when it runs — during a burst, a later line (see Line).

buffer

export const buffer: Buffer;

This session's recent-lines buffer.

Line

export interface Line {
  insert(
    text: string | StyledText,
    begin: number,
    end?: number,
    options?: LineColorOptions | StyleBuilder,
  ): void;
  replaceAt(text: string | StyledText, begin: number, end: number): void;
  highlightAt(
    begin: number,
    end: number,
    options?: HighlightOptions | StyleBuilder | LinkTag,
  ): void;
  removeAt(begin: number, end: number): void;
  replace(oldStr: string, newStr: string | StyledText): boolean;
  highlight(str: string, options?: HighlightOptions | StyleBuilder | LinkTag): boolean;
  remove(str: string): boolean;
  gag(): void;
  redirect(pane: Pane | string): void;
  copy(pane: Pane | string): void;
  readonly text: string;
  readonly styles: StyleSpan[] | undefined;
  readonly number: number;
}

A line of output you can read and edit. Inside a trigger, line is the line being processed right now; buffer.line(n) reaches an already-printed line by number. The handle remembers which line it points at; methods never take a line number.

The line being processed accepts changes until its processing finishes and it is delivered. That window covers the whole cascade the line runs — its trigger and receive handlers, plus anything they set in motion that runs before delivery, such as handlers of events they emit. When no line is being processed there is no line to edit: any edit, gag(), redirect(), or copy() throws. One caution for deferred code — an async handler resuming after an await, a timer callback, anything that runs later: it acts on whichever line is being processed when it runs. Between lines that is the throw above; during a burst of output it can be a later line, edited silently. Capture what you need before deferring, and reach a specific line again through buffer.line(n). Reads stay safe anywhere (text is "" and styles an empty array outside the window). Already-printed lines reached through buffer.line(n) can be edited at any time.

The text-search methods (replace, highlight, remove) act on every occurrence of their target string; the *At forms take byte offsets (e.g. from styles).

Buffer

export interface Buffer {
  line(lineNumber: number): Line;
}

Already-printed lines, looked up by number (only roughly the most recent 1000 are reachable).

capture

export function capture(value: boolean): void;

From an alias handler: controls whether the command you typed (the one that matched) still goes to the MUD. By default an alias replaces your command: the typed line is captured, and the script sends something in its place. Call capture(false) to let the original line through. This is useful for scripts that watch what is typed but don't want to change it, or for aliases that only sometimes want to replace the command.

capture(true) forces a line to be captured, even if a previously or subsequently alias calls capture(false).

No effect in a trigger handler: incoming lines are always shown. Use line.gag() for similar behavior there.

style

export const style: StyleBuilder;

Builds StyledText for echo and the line-editing methods (see StyleBuilder).

export function link(command: string, options?: LinkOptions): LinkTag;
 
export function link(onClick: (click: LinkClick) => void, options?: LinkOptions): LinkTag;
 
export function link(action: null, options?: LinkOptions): LinkTag;

Makes text clickable. Pass a command, and clicking the text sends it exactly as if you typed it into the clicked window's session. Pass a function instead, and clicking runs it with the modifier keys that were held:

import { echo, link, send } from "smudgy:core";
 
echo`You see an exit ${link("north")`to the north`}.`;
echo`${link((click) => send(click.shift ? "open north" : "north"))`north`}`;

Links are underlined over a faint wash of the text's own color, so they read as links whatever the text's colors are. Style the text freely — the affordance keeps up:

import { echo, line, link, send, style } from "smudgy:core";
 
line.replace("north", link("north")`${style.cyan`north`}`);
line.replace("foo bar", link("https://www.google.com", {
  tooltip: async () => "hello",
})`foo bar`);
line.replace("status", link(null, { tooltip: "Nothing to do yet" })`status`);
line.replace("actions", link(null, {
  enabled: false,
  menu: [{ label: "Look", action: "look" }],
})`actions`); // right-click only
echo`${link("look", {
  title: "Actions",
  menu: [{ label: "Look", action: "look" }, "-", {
    label: "Wave",
    action: () => send("wave"),
  }],
})`room actions`}`;

A command link works forever, even on old lines. A function link lives with the script that made it: after a script reload the text remains but clicking it does nothing, and only the most recent function links are kept, so a very old one can expire early. Prefer command links for anything long-lived.

The tag also works directly as a highlight's options — line.highlight("goblin", link("kill goblin")) makes every match clickable in place, keeping the text's styling (see Line's highlight).

StyledText

export interface StyledText {
  readonly __smudgyStyled: true;
}

A piece of styled text, built with style or link. Accepted everywhere plain text is: echo (and a session's or pane's echo), and a line's insert, replaceAt, and replace. Fragments nest: interpolate one inside another and the inner text keeps its own styling, inheriting anything it didn't set from the fragment around it.

StyleBuilder

export interface StyleBuilder extends StyleTag {
  (options: LineColorOptions): StyleBuilder;
  fg(color: Color): StyleBuilder;
  bg(color: Color): StyleBuilder;
  readonly black: StyleBuilder;
  readonly red: StyleBuilder;
  readonly green: StyleBuilder;
  readonly yellow: StyleBuilder;
  readonly blue: StyleBuilder;
  readonly magenta: StyleBuilder;
  readonly cyan: StyleBuilder;
  readonly white: StyleBuilder;
  readonly default: StyleBuilder;
  readonly echo: StyleBuilder;
  readonly output: StyleBuilder;
  readonly warn: StyleBuilder;
  readonly bgBlack: StyleBuilder;
  readonly bgRed: StyleBuilder;
  readonly bgGreen: StyleBuilder;
  readonly bgYellow: StyleBuilder;
  readonly bgBlue: StyleBuilder;
  readonly bgMagenta: StyleBuilder;
  readonly bgCyan: StyleBuilder;
  readonly bgWhite: StyleBuilder;
  readonly bold: StyleBuilder;
  readonly faint: StyleBuilder;
  readonly italic: StyleBuilder;
  readonly underline: StyleBuilder;
  readonly doubleUnderline: StyleBuilder;
  readonly crossedOut: StyleBuilder;
  readonly reverse: StyleBuilder;
}

Builds styled text. Use it as a template tag, optionally picking colors and attributes first. Each step is itself a tag, so all of these work:

import { echo, style } from "smudgy:core";
 
echo`A ${style.red`red`} word and ${style.blue.bgYellow`a loud one`}.`;
echo`${style.bold.underline`emphasis`} without touching colors.`;
echo(style.fg({ r: 255, g: 128, b: 0 })`exact orange`);
echo(style({ fg: "cyan", bg: "black" })`both at once`);

Color names mean what they mean everywhere else (see Color): the ANSI names are the bright variant, the theme roles (default, echo, output, warn) follow the color scheme, and fg/bg accept any Color form, including { color, bold: false } for the dimmer shade. The attribute shorthands (bold, faint, italic, underline, doubleUnderline, crossedOut, reverse) each set one text attribute — bold is the font-weight attribute (attributes.bold), independent of the { color, bold } palette slot; blink and explicit false values stay in the attributes options form. Anything a fragment leaves unset behaves like plain text: the usual echo color when echoed, the surrounding style when spliced into a line.

A chain also works directly AS the options of a line write — line.highlight("goblin", style.red.bgWhite) — where it applies exactly what it set and leaves everything else untouched.

StyleTag

export interface StyleTag {
  (text: TemplateStringsArray, ...values: unknown[]): StyledText;
}

A template tag producing StyledText. Interpolated fragments keep their styling; any other value becomes plain text, exactly as it would in an ordinary template string.

LinkTag

export interface LinkTag extends StyleTag {
  readonly __smudgyLink: true;
}

The template tag link returns: a StyleTag whose fragments carry the link, also accepted directly as a highlight's options (where it covers the matched text with the link).

LinkClick

export interface LinkClick {
  shift: boolean;
  ctrl: boolean;
  alt: boolean;
}

Modifier keys held when a link was clicked.

LinkOptions

export interface LinkOptions {
  tooltip?: LinkTooltip;
  enabled?: boolean;
  menu?: readonly (LinkMenuItem | "-")[];
  title?: string;
}

LinkTooltip

export type LinkTooltip = string | (() => string | PromiseLike<string>);

Hover text for a link. A function is evaluated lazily on first hover and may return its text immediately or through a promise. Its result is cached.

LinkMenuItem

export interface LinkMenuItem {
  label: string;
  action: string | ((click: LinkClick) => void);
}

One action row in a link's right-click menu. A string sends a command; a function receives the same modifier snapshot as a primary callback.

Color

export type Color =
| string
| { r: number; g: number; b: number }
| {
    color: string;
    bold?: boolean;
    paletteBright?: boolean;
  };

A color accepted by the line-styling APIs. One of:

TextAttributes

export interface TextAttributes {
  bold: boolean;
  faint: boolean;
  italic: boolean;
  underline: "none" | "single" | "double";
  blink: "none" | "slow" | "fast";
  crossedOut: boolean;
  reverse: boolean;
}

The lossless non-color attributes carried by a terminal text run. Readback (StyleSpan) always carries every field; the write APIs accept any subset (Partial<TextAttributes>) and leave the attributes they don't mention alone.

StyleSpan

export interface StyleSpan {
  begin: number;
  end: number;
  fg: Color;
  bg: Color;
  attributes: TextAttributes;
  foregroundPaletteBright?: boolean;
}

One styled run read back from a line. begin/end are byte offsets into the line's text (not character counts; multi-byte characters span several bytes).

LineColorOptions

export interface LineColorOptions {
  fg?: Color;
  bg?: Color;
  attributes?: Partial<TextAttributes>;
  foregroundPaletteBright?: boolean;
}

Foreground, background, and/or text attributes for a line write. Set only what you mean to change: anything left unset is left alone — highlight keeps each span's existing value for that channel, and insert or a styled fragment inherits the surrounding (or delivery-default) style. attributes takes any subset of the seven attributes, per-field.

A StyleSpan is accepted directly, making readback lossless (its complete attributes object overwrites all seven).

HighlightOptions

export interface HighlightOptions extends LineColorOptions {
  link?: LinkTag | null;
}

What highlight/highlightAt accept: colors and attributes (unset ones left untouched, as everywhere), plus optionally the range's link coverage. link takes the tag link returns and covers each matched range with that link — replacing any links it overlaps, while a link reaching outside the range keeps its outside pieces — or null to strip links from the range. Left unset, existing links are untouched.


Script API reference · ← smudgy:core — Command input · smudgy:core — Shared state & events → · Scripting manual