std/term

Colors and styles

print(paint("error:", "bold red"), "file not found")
print(paint("note", "italic #ff8800 on_#202020"))
print(success("Saved"), warning("Low disk space"))
Function Result
paint(text, style) text in a style: words separated by spaces
bold, dim, italic, underline (text) shortcuts for one style
red, green, yellow, blue, magenta, cyan, gray (text) shortcuts for one color
rgb(r, g, b) a color word such as #ff8800, for paint
link(text, url) a clickable link in terminals that support them
success, failure, warning, info (text) text after a colored ✔, ✖, ⚠, or ℹ
strip_styles(text) text without any terminal codes
set_color(on) turns colors on or off whatever the output is
color_enabled() whether colors are in use

Style words are bold, dim, italic, underline, blink, inverse, hidden, and strike; the colors black, red, green, yellow, blue, magenta, cyan, white, gray, and bright_ forms such as bright_red; hex colors such as #ff8800; and any color with on_ for the background. An unknown word stops the program with a message, so a typo is found at once. Styles nest: a painted piece inside painted text keeps its own style, and the outer style returns after it.

Colors are used when standard output is a terminal, unless the NO_COLOR variable is set or TERM is dumb; FORCE_COLOR turns them on. set_color overrides all of that. Without colors every function returns plain text, so output sent to a file or another program stays clean.

The cursor and the screen

Each of these returns the code to write, or "" when output is not a terminal:

Function Moves or clears
cursor_up, cursor_down, cursor_left, cursor_right (n) the cursor by n
cursor_to(row, column) the cursor to a place, counted from 1
cursor_column(column) the cursor to a column of its line
hide_cursor(), show_cursor() the cursor's visibility
clear_line() the current line, moving to its start
clear_below() from the cursor to the end of the screen
clear_screen() everything, moving to the top

controls_enabled() says whether these codes are in use: on a terminal they are, even when NO_COLOR turns colors off.

enter_screen() switches to the terminal's second screen, as editors and games do, and hides the cursor; leave_screen() brings back the normal screen as it was. If the program ends first, even by a runtime error, the normal screen comes back anyway.

Measuring and laying out text

print(box(["Cuarzo 1.0", "ready"], "welcome"))
print(table(["name", "stars"], [["cuarzo", "★★★"], ["seed", "★"]]))
print(progress_bar(0.42, 20))

std/term passes on std/text, so text_width, pad_right, truncate, wrap, and the rest of its measuring and laying out come with it.

Function Result
columns(items, width) items in columns, as ls lists files
rule(width) a horizontal line
box(lines, title) lines in a box with rounded corners
table(header, rows) rows in aligned columns under a bold header
progress_bar(fraction, width) a bar filled from 0 to 1, with the percent
spinner_frame(frame) one frame of a spinner, for frames 0, 1, 2, ...

Events, screens, and frames

Full-screen programs read every event of the terminal, draw on a Screen of cells that writes only what changed, and run at a steady number of frames a second:

import "std/term"

enter_screen()
Screen s = screen(terminal_width(), terminal_height())
f64 x = 10.0
run_frames(30, func(TermEvent e) bool do
    if e.kind == "resize" then
        screen_resize(s, e.width, e.height)
    end
    return not (e.kind == "key" and e.key == "q")
end, func(f64 seconds) bool do
    if key_down("right") then
        x += 20.0 * seconds
    end
    screen_clear(s)
    screen_text(s, int(x), 2, "▶", "bold green")
    screen_flush(s)
    return true
end)
stop_events()
leave_screen()
Function Result
read_event(wait_ms) the next TermEvent, waiting up to wait_ms milliseconds, or for ever below 0; kind "none" when none came
key_down(name) whether a key, named as events name keys, is held now
key_events_supported() whether the terminal reports releases: always on Windows, and elsewhere where it has the kitty keyboard protocol
mouse_mode(on) turns the reporting of clicks, drags, motion, and the wheel on or off
stop_events() leaves the mode read_event puts the terminal in; the end of the program leaves it too
screen(width, height), screen_resize(s, width, height) a Screen of cells, drawn from row top and column left, both 1 unless changed
screen_put(s, x, y, glyph, style), screen_text(s, x, y, text, style) a character, or text a character a cell, at column x and row y from 0; a wide character takes two cells
screen_fill(s, x, y, width, height, glyph, style), screen_clear(s) fills a rectangle, or empties every cell
screen_pixel(s, x, y, color) colors a pixel of a grid twice as tall as the screen, two square pixels a cell, drawn with half blocks
screen_cell(s, x, y), screen_style(s, x, y) what a cell holds
screen_flush(s), screen_redraw(s) writes the cells that changed since the last flush and gives how many; screen_redraw makes the next one write them all
run_frames(fps, on_event, on_frame) calls on_frame fps times a second with the seconds since the last frame and gives on_event each event between; either returning false ends it; frames missed while the program was busy are skipped
style_code(style) the terminal code that starts a style, as paint uses it
bell() rings the terminal's bell

A TermEvent has a kind: "key", "mouse", "resize", "focus", "paste", "none", or "end". A key event has an action, "press", "repeat", or "release"; a key, the name of the key without shift, as "a", "1", "space", "enter", "up", "f5", or "shift"; modifiers, as "ctrl+shift"; and text, what it types, as "A" with shift. name() gives the name read_key would, as "A" or "ctrl+c", and has("ctrl") says whether a modifier was held. A mouse event has an action, "press", "release", "move", or "wheel"; the button in key, "left", "middle", "right", or "none", or the wheel's way; and x and y from 1. A resize has the new width and height, a focus event focused, and a paste its text.

On Windows the console's input records give presses, repeats, releases, the mouse, and resizes; pasted text comes as keys. Elsewhere the kitty keyboard protocol gives releases where the terminal has it, found by asking it; SGR mouse reporting gives the mouse, SIGWINCH resizes, and bracketed paste and focus reporting the rest. Where releases are not reported, key_down counts a key as held for a moment after each press and while it repeats.