dev:scriptref:core:lines

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).

  • insert — Insert text at byte offset begin (replacing up to end if given), with optional colors (plain options or a style chain); whatever options leaves unset inherits the style at the insertion point. Styled text keeps its own colors and links; options then supplies the colors its unstyled parts get.
  • replaceAt — Replace the byte range [begin, end) with text. Styled text keeps its own colors and links; its unstyled parts blend into the surrounding style.
  • highlightAt — Restyle the byte range [begin, end): what options sets changes, and everything it leaves unset keeps what each span already had — so { fg: "red" } recolors without touching backgrounds or bold, and { attributes: { bold: true } } emboldens without touching colors. A style chain works directly as the options (line.highlightAt(b, e, style.red.bgWhite)), and so does a link tag, linkifying the range in place (see HighlightOptions).
  • removeAt — Remove the byte range [begin, end).
  • replace — Replace every occurrence of oldStr with newStr (plain or styled; the search side is always plain text). Returns true if any was found.
  • highlight — Restyle every occurrence of str (see Line.highlightAt: unset options are left untouched, and a style chain like style.red.bold or a link tag like link("kill goblin") works directly as the options). Returns true if any was found.
  • remove — Remove every occurrence of str. Returns true if any was found.
  • gag — Hide this line: it never reaches the screen. Current-line only (a no-op on a buffer line).
  • redirect — Take the current line out of the main view and deliver it to pane instead. Styling is kept and later edits still apply; if called repeatedly, the last call wins. Current-line only (a no-op on a buffer line). A Pane handle from another session throws.
  • copy — Deliver the current line to pane as well as the main view. Current-line only (a no-op on a buffer line).
  • text — The line's text ("" for a buffer line outside the recent-lines window).
  • styles — The line's style runs (undefined for a buffer line outside the window).
  • number — The line's number (the current line reports the number it is about to be assigned).

Buffer

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

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

  • line — A handle to the already-printed line lineNumber.

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.

  • __smudgyStyled — Marks a value as styled text. Fragments come from the style tag; this property just keeps other values from being mistaken for one.

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.

  • (call) — Colors and/or text attributes, in the same shape highlight takes. attributes may be any subset: it refines what the chain has set so far, and fields no step sets inherit at delivery like unset colors.
  • bold — Font-weight bold (attributes.bold) — not the palette slot.
  • underline — Single underline; doubleUnderline for the double form.

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).

  • __smudgyLink — Marks a tag as carrying a link; the counterpart of StyledText's brand.

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;
}
  • enabled — Whether a normal left click may activate the link. Defaults to true. A menu remains available from right-click when this is false. For a null-action menu, true also lets an ordinary left click open it.
  • menu — Right-click rows. Use "-" for a separator.
  • title — Optional plain-text heading shown above the menu rows.

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:

  • an ANSI color name ("black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", meaning the bright variant), or a theme role: "default", "echo", "output", "warn"
  • { r, g, b } with each component 0-255, for an exact color
  • { color, bold?, paletteBright? }: an ANSI color name or "default" plus its palette slot. Omitted bold means what the bare name means (the bright variant for an ANSI name, the normal slot for "default"); bold: false selects the normal, dimmer variant. paletteBright is normally only needed when re-emitting style readback.

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).

  • foregroundPaletteBright — Present when fg is the compatibility string "default" but its terminal palette slot is the bright default. Passing this span back to a line styling method preserves that raw palette bit.

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).

  • foregroundPaletteBright — Lossless raw palette bit for a read-back fg: "default" span.

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