Colour for a piece drawn in one ink: one colour for every character, or several as a gradient across the picture, and colours for its parts.
Most pieces are text in one ink, the page's or the terminal's own. Given
an ink (Ascii.new(piece, ink: ...)), their frames come with colours,
so every renderer draws them as it draws a coloured piece: a canvas in the
browser, 24-bit or xterm-256 escapes in a terminal, and plain text where
colour cannot be shown (mode: :mono). The picture itself is unchanged,
character for character: the ink is laid over it, never drawn by the piece.
A coloured piece keeps its own palette; drawn in one ink (color: false)
it takes the ink like any other.
{:ok, anim} = Ascii.new("big-text", ink: ["#ff5f6d", "#ffc371"])
{:ok, anim} = Ascii.new("donut", ink: "#7fdbca")
{:ok, anim} = Ascii.new("wave-text", ink: "#36d1dc,#5b86e5", gradient: :vertical)Pieces that know their parts (a file tree's folders, a boot log's
statuses) take colours for those too, with :parts, over the ink or
without one: see Ascii.Parts.
Colours
#rrggbb or #rgb, the # optional; several as a list or as one string,
comma-separated (as from a form or a command line), up to 16. One colour
is every character's. Several run evenly from the first to the last across
the whole picture, in a straight line between each and the next, as CSS's
linear-gradient does, so a piece whose text moves (a marquee, a wave)
moves through them.
CSS variables
One ink, or a part's colour, can be a CSS custom property, so a piece on
a page takes the page's own colours, and follows them into dark mode:
"var(--color-success)", or with a colour for where there is no CSS,
"var(--color-success, #3fb950)". In HTML (Ascii.Render.HTML, the
Phoenix players, Ascii.Render.SVG) the variable is the colour; a
terminal draws the fallback, or its own ink without one (resolve/1).
Several inks run as a gradient, worked out here, so they must be colours.
Gradients
:horizontal(the default) - left to right.:vertical- top to bottom.:diagonal- top left to bottom right, at 45° on screen, cells being as tall as the piece'scellsays.
A gradient is 65 steps, whatever the picture's size, so its palette is the same for every frame (a player sends it once) and a wide picture costs few escapes in a terminal and few spans in HTML.
Summary
Types
Which way several colours run.
An ink: its colours, as #rrggbb or a CSS variable (nil for the
renderer's own), which way they run, and colours for parts (see Ascii.Parts). Made by
cast/2 and put_parts/2; the rest is its layout for one size of frame,
filled in as it is needed.
Functions
An ink from outside (code, LiveView params, a command line): its colours
and, for several, which way they run. No colours at all (nil, "" or
[]) is no ink. Never creates an atom.
A colour from outside, as this library keeps it: #rrggbb (from
#rrggbb or #rgb, the # optional), or a CSS variable as
var(--name) or var(--name, #rrggbb); nil if it is neither.
The ink with colours for parts (as Ascii.Parts.cast/2 makes them),
laid over its own. Without an ink, the parts take theirs over the
renderer's own ink; without parts, the ink is as it was.
The #rrggbb to draw a colour in where CSS cannot be read (a terminal, a
framebuffer): the colour itself, a CSS variable's fallback, or nil for
the renderer's own ink.
The frame in this ink: its text as it is, each cell given a colour, and no
ground behind it. With parts, the frame's runs of parts (see
Ascii.Parts), a part with a colour of its own takes it. A frame with
colours of its own, or no ink (nil), is left as it is.
Types
@type gradient() :: :horizontal | :vertical | :diagonal
Which way several colours run.
@type t() :: %Ascii.Ink{ cells: binary() | nil, colors: [String.t(), ...] | nil, gradient: gradient(), index: %{optional(String.t()) => non_neg_integer() | :base} | nil, palette: [String.t() | nil] | nil, parts: Ascii.Parts.colors(), size: {pos_integer(), pos_integer(), 1 | 2} | nil }
An ink: its colours, as #rrggbb or a CSS variable (nil for the
renderer's own), which way they run, and colours for parts (see Ascii.Parts). Made by
cast/2 and put_parts/2; the rest is its layout for one size of frame,
filled in as it is needed.
Functions
@spec cast(colors :: term(), gradient :: term()) :: {:ok, t() | nil} | Ascii.Options.error()
An ink from outside (code, LiveView params, a command line): its colours
and, for several, which way they run. No colours at all (nil, "" or
[]) is no ink. Never creates an atom.
iex> Ascii.Ink.cast("#F80")
{:ok, %Ascii.Ink{colors: ["#ff8800"], gradient: :horizontal}}
iex> Ascii.Ink.cast("#ff5f6d,#ffc371", "vertical")
{:ok, %Ascii.Ink{colors: ["#ff5f6d", "#ffc371"], gradient: :vertical}}
iex> Ascii.Ink.cast("red")
{:error, {:invalid_option, :ink}}
A colour from outside, as this library keeps it: #rrggbb (from
#rrggbb or #rgb, the # optional), or a CSS variable as
var(--name) or var(--name, #rrggbb); nil if it is neither.
iex> Ascii.Ink.color("F80")
"#ff8800"
iex> Ascii.Ink.color("var( --accent ,#FFF)")
"var(--accent, #ffffff)"
iex> Ascii.Ink.color("var(--x);color:red")
nil
@spec put_parts(ink :: t() | nil, parts :: Ascii.Parts.colors()) :: t() | nil
The ink with colours for parts (as Ascii.Parts.cast/2 makes them),
laid over its own. Without an ink, the parts take theirs over the
renderer's own ink; without parts, the ink is as it was.
The #rrggbb to draw a colour in where CSS cannot be read (a terminal, a
framebuffer): the colour itself, a CSS variable's fallback, or nil for
the renderer's own ink.
iex> Ascii.Ink.resolve("var(--ok, #3fb950)")
"#3fb950"
iex> Ascii.Ink.resolve("var(--ok)")
nil
@spec tint(frame :: Ascii.Frame.t(), ink :: t() | nil, parts :: Ascii.Parts.t() | nil) :: Ascii.Frame.t()
The frame in this ink: its text as it is, each cell given a colour, and no
ground behind it. With parts, the frame's runs of parts (see
Ascii.Parts), a part with a colour of its own takes it. A frame with
colours of its own, or no ink (nil), is left as it is.