Current section
Files
Jump to
Current section
Files
src/examples/hello_world.gleam
//// This example shows how to print a hello world on a rainbow background
//// that is always centered (it waits for the `Resize` event and updates
//// the background accordingly).
import etch/command
import gleam/result
@target(javascript)
import etch/event.{type Event, type EventError}
@target(erlang)
import etch/event
import etch/stdout
import etch/style
import etch/terminal
@target(javascript)
import gleam/javascript/promise
@target(javascript)
import gleam/option.{type Option, None, Some}
@target(erlang)
import gleam/option.{None, Some}
import gleam/string
pub fn main() {
// First, we hide cursors
stdout.execute([command.HideCursor])
// We get windows size so we can center our rainbow background
let #(x, y) = terminal.window_size() |> result.unwrap(#(0, 0))
draw_centered_text(x, y)
// make sure to init event server before listening to events.
event.init_event_server()
loop()
}
@target(erlang)
fn loop() {
handle_events()
loop()
}
@target(javascript)
fn loop() {
use event <- promise.await(event.read())
handle_events(event)
loop()
}
@target(erlang)
fn handle_events() {
// We call `event.read()` to wait for available input.
// It blocks program execution until an event is received.
// This is exactly what we need, because the project has no logic
// running constantly in the background. We only need to update the screen
// when its size changes.
case event.read() {
// We only need to check for `Resize` event in this program.
Some(Ok(event.Resize(x, y))) -> {
draw_centered_text(x, y)
}
Some(_) -> Nil
None -> Nil
}
}
@target(javascript)
fn handle_events(event: Option(Result(Event, EventError))) {
// We call `event.read()` to wait for available input.
// It blocks program execution until an event is received.
// This is exactly what we need, because the project has no logic
// running constantly in the background. We only need to update the screen
// when its size changes.
case event {
// We only need to check for `Resize` event in this program.
Some(Ok(event.Resize(x, y))) -> {
draw_centered_text(x, y)
}
Some(_) -> Nil
None -> Nil
}
}
fn draw_centered_text(x: Int, y: Int) {
let s = " Hello from Etch! "
let len = string.length(s)
// `with`, `with_on` and `on` do not reset colors so make sure to reset it manually.
// using `with_on` if you want to set both foreground and background colors is slightly
// faster than using `with` and `on` separately.
let s =
style.with_on(s, style.Black, style.AnsiValue(40)) |> style.reset_color
// But here we don't reset color because calling `style.magenta()`
// at the end automatically resets the color.
let red = "█" |> string.repeat(len) |> style.with(style.Rgb(233, 51, 35))
let orange = "█" |> string.repeat(len) |> style.with(style.Rgb(241, 161, 57))
let yellow = "█" |> string.repeat(len) |> style.with(style.AnsiValue(226))
let light_blue =
"█"
|> string.repeat(len)
|> style.with(style.Rgb(74, 163, 228))
let blue = "█" |> string.repeat(len) |> style.blue()
// This `style.magenta()` resets color. If you don't know if the function resets color
// after applying it, read its documentation.
let purple = "█" |> string.repeat(len) |> style.magenta()
let x_offset = len / 2
let x = x / 2 - x_offset
// It should be clear from the names of the commands what it does
// but to make sure you get it, I'll explain you.
// First, we clear the terminal, then move to the first line (red)
// which is 3 lines above the center line, then we print it.
// repeat it for every line.
stdout.execute([
command.Clear(terminal.All),
command.MoveTo(x, y / 2 - 3),
command.Print(red),
command.MoveTo(x, y / 2 - 2),
command.Print(orange),
command.MoveTo(x, y / 2 - 1),
command.Print(yellow),
command.MoveTo(x, y / 2),
command.Print(s),
command.MoveTo(x, y / 2 + 1),
command.Print(light_blue),
command.MoveTo(x, y / 2 + 2),
command.Print(blue),
command.MoveTo(x, y / 2 + 3),
command.Print(purple),
])
}