gleam-community / ansi

ANSI colours, formatting, and control codes
Apache License 2.0
24 stars 0 forks source link

Support for Advanced Styling in the Terminal #10

Open erikareads opened 9 months ago

erikareads commented 9 months ago

I need a styling library for teashop, ansi supports many of the operations that I want to support, but not quite in the right way.

This issue is an open question of whether it makes sense to adapt/enhance ansi to support my styling needs.

For performance reasons, I would like to apply as many styles at once as possible, to avoid unnecessary string concatenations. Especially for Erlang. For teashop I might need to render multiple styles at 60 FPS.

For this, I would like to do something like a builder API:

pub fn bold(style, bool) { Style(..style, bold: bool) }

I would also like to support operations that don't feel like they fit into ansi, such as text alignment, padding, and borders.

Here is an incomplete list of all the things I want to be able to support:

pub type Style {
  Style(
    background: Option(Color),
    blink: Bool,
    bold: Bool,
    faint: Bool,
    foreground: Option(Color),
    height: Option(Int),
    italic: Bool,
    margin_bottom: Int,
    margin_left: Int,
    margin_right: Int,
    margin_top: Int,
    max_height: Option(Int),
    max_width: Option(Int),
    padding_bottom: Int,
    padding_left: Int,
    padding_right: Int,
    padding_top: Int,
    reverse: Bool,
    strikethrough: Bool,
    underline: Bool,
    width: Option(Int),
    horizontal_alignment: Option(Alignment),
    vertical_alignment: Option(Alignment),
    underline_spaces: Bool,
    strikethrough_spaces: Bool,
    transform: Option(fn(String) -> String),
  )
}

I will be using ANSI escape codes to achieve these colors and styles. As much as possible, I would like to present a unified interface for building a style.

In Go, the styles are built up first, then concatenated in one operation:

https://github.com/muesli/termenv/blob/9850e567a4bfc1af41a28a48fbb1f6fde4e7f20a/style.go#L43-L57

Does it make sense to add such functionality to ansi?

Regardless of decision, I think it's important to note that ansi works right now with teashop, since teashop doesn't care where the ANSI strings come from.

erikareads commented 9 months ago

My end goal is to support a similar breadth of functionality as https://github.com/charmbracelet/lipgloss for teashop.

erikareads commented 9 months ago

Another consideration is the simple exposure of the escape codes, perhaps in a separate module: ansi/escape_codes.

This is what minttea did in OCaml: https://github.com/leostera/tty/blob/main/tty/escape_seq.ml https://github.com/leostera/tty/blob/main/tty/color.ml

Just having the ability to get the escape codes for styles and colors from ansi would avoid me having to re-implement that code in my styling package.

brettkolodny commented 9 months ago

I think to start we'll just expose the codes in an ansi/codes module like you suggested. I'm open to including some more utilities around using them in the future but I'd rather start here and see what people use them for/the broader needs of the community around them before building anything out.

TanklesXL commented 2 months ago

I am also looking for some way to do custom styling, at the very least a way to use arbitrary ANSI escape codes without having to re-implement a lot of what this library does under the hood.

The library currently contains the run and code functions, it would make sense to leverage those to provide some kind of function like the following:

pub fn colour_code(text: String, colour: Int) -> String {
  run(text, code([colour], 39)
}
brettkolodny commented 2 months ago

@erikareads @TanklesXL Thank's for following up on this issue!

I've started some work adding this functionality to the library, at the moment what I'm looking at is adding a few more modules:

I need to see the level of terminal support for all of these codes though. It also brings up the question if the currently featured functionality should be moved to a module like ansi.text