hwylterm

Source   Edit  

Hwylterm

see also these utility modules:

moduledescription
hwylclimacro to generate CLI's styled by bbansi and with parsing parseopt3
choosersimple scrolling multi-item selecter
loggingwrapper for std/logging with styling

bbansi

BB-style markup for ANSI terminal colors and styles.

Basic Markup Syntax

Wrap text in [style]...[/style] tags:

[bold]hello[/bold]
[red]error[/red]
[bold red]warning[/]

  • [/style] — close a specific style
  • [/] — close the most recently opened style
  • [reset] — reset all active styles

Available Styles

Text styles

MarkupAbbreviationEffect
boldbbold
italiciitalic
underlineuunderline
faintdim/faint
blinkblinking
reversereverse video
strike~~strikethrough~~
concealhidden

Colors

Named xterm colors: black, red, green, yellow, blue, magenta, cyan, white, plus bright variants (brightred, brightblue, etc.).

Hex colors:

[#ff8800]orange text[/]

256-color palette:

[color(42)]text[/]

Background colors

Use the on keyword to set a background color:

[red on blue]text[/]
[on yellow]text[/]

Combined styles

Multiple styles can be combined in a single tag:

[bold red]error[/]
[bold italic underline]important[/]

Rendering to string

Parse markup into a BbString:

let s = bb("[bold]hello[/bold]")

Apply a style to a plain string:

let s = bb("hello", "bold red")

Render a BbString to an ANSI string using the global console:

echo $bb("[green]ok[/]")

Write to the global hwylConsole file (a wrapper around hwylConsole.file.write):

hecho bb("[bold]hello[/]"), " world"

Format strings with BB markup using bbfmt:

let name = "world"
hecho $bbfmt"[bold]hello {name}[/]"

Escaping

InputOutput
[[[
\\\

Use bbEscape to programmatically escape a string before embedding it in markup:

let userInput = bbEscape(untrustedText)
hecho $bb("[bold]" & userInput & "[/bold]")

Text Operations on BbString

All operations preserve styling:

# Truncate to N visible characters
let t = bb("[bold]hello world[/]").truncate(5)

# Word-wrap at N columns
let w = bb("[red]long text...[/]").wrapWords(40)

# Right-align / left-align (pad with spaces)
let r = bb("[blue]hi[/]").align(10)
let l = bb("[blue]hi[/]").alignLeft(10)

# Concatenate
let a = bb("[red]foo[/]") & bb("[blue]bar[/]")

# Side-by-side horizontal concatenation
let h = hconcat(bb("left\ncol"), bb("right\ncol"))

# Join a seq of BbStrings
let parts = @[bb("[red]a[/]"), bb("[green]b[/]")]
let joined = parts.join(bb(", "))

Environment / Color Mode

VariableEffect
NO_COLORDisables all color output
HWYLTERM_FORCE_COLORForces color on (even in pipes)
HWYLTERM_FORCE_MARKUPOutputs BB markup instead of ANSI codes

Compile-time flags (checked before environment variables):

FlagEffect
-d:bbansiOnForces color on
-d:bbansiOffForces color off
-d:bbansiNoColorDisables color (strips styles, keeps text)
-d:bbMarkupOutputs BB markup instead of ANSI codes

confirm

Prompt the user for a yes/no answer on stderr. Returns true for y/yes and false for n/no (case-insensitive). Reprompts on any other input.

import hwylterm/confirm

if confirm("Delete all files?"):
  echo "deleting..."
else:
  echo "aborted"

spin

Animated terminal spinner backed by a background thread. The simplest usage is the withSpinner template, which starts the spinner, runs a block, then stops it automatically.

import std/os
import hwylterm/spin

withSpinner("loading..."):
  sleep 2000

Use spinner.setText inside the block to update the message while running:

import std/os
import hwylterm/spin

withSpinner("step 1"):
  sleep 500
  spinner.setText("step 2")
  sleep 500
  spinner.setText("done")
  sleep 500

To pick a specific spinner style, use the with template:

import std/os
import hwylterm/spin

Moon.with("loading..."):
  sleep 2000

See spinners: SpinnerKind for all available styles.

progress

Progress bar iterator that wraps a sequence and renders a bar as items are consumed. It runs inside a withSpinner/useSpinner context so the bar animates smoothly.

import std/os
import hwylterm/progress

let items = (1..20).toSeq()
for item in progress(items):
  sleep 100

Segments

By default the bar renders only the bar itself. Pass segments to newProgress to add a fraction counter or percentage:

import std/os
import hwylterm/progress

var pb = newProgress(segments = @[Bar, Fraction, Percent])
for item in pb.progress((1..20).toSeq()):
  sleep 100

See progress: ProgressSegment for all options (Bar, Fraction, Percent).

With an existing spinner

If you already have a Spinny from the spin module, pass it to progress to share the spinner:

import std/os
import hwylterm/[spin, progress]

withSpinner("processing"):
  for item in progress(spinner, (1..20).toSeq()):
    sleep 100