• Stars
    star
    230
  • Rank 168,107 (Top 4 %)
  • Language
    OCaml
  • License
    MIT License
  • Created 5 months ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A fun little TUI framework for OCaml

Mint Tea

Mint Tea Logo

A fun, functional, and stateful way to build terminal apps in OCaml heavily inspired by BubbleTea. Mint Tea is built on Riot and uses The Elm Architecture.

Tutorial

Mint Tea is based on the functional paradigm of The Elm Architecture, which works great with OCaml. It's a delightful way to build applications.

This tutorial assumes you have a working knowledge of OCaml.

Getting Started

For this tutorial, we're making a shopping list.

We'll start by defining our dune-project file:

(lang dune 3.12)

And a dune file for our executable:

(executable
  (name shop)
  (libraries minttea))

Then we need to pin the minttea package to the github source:

$ opam install minttea

Opam will do some work installing minttea from the github source.

We can run dune build to validate the package has been installed correctly.

Great, now we can create a new shop.ml file and start by opening up Minttea:

open Minttea

Mint Tea programs are composed of a model that describes the application state, and three simple functions:

  • init, a function that returns the initial commands for the application to run
  • update a function that handles incoming events and updates the model accordingly
  • view, a function that renders the UI based on the data in the model

The Model

We'll start by creating a type for our model. This type can be anything you want, just remember that it must hold your entire application state.

type model = {
  (* the choices that will be used and whether they are selected or unselected *)
  choices : (string * [ `selected | `unselected ]) list;
  (* the current position of the cursor *)
  cursor : int;
}

Initialization

Next up, we'll create our initial_model function. If creating this initial state is too expensive, we could make it a function too, so we can call it when we need to start the application.

let initial_model =
  {
    cursor = 0;
    choices =
      [
        ("Buy empanadas 🥟", `unselected);
        ("Buy carrots 🥕", `unselected);
        ("Buy cupcakes 🧁", `unselected);
      ];
  }

Next we will define our init function. This function takes the initial state and returns a Mint Tea Command that kicks off the application. This can be going into fullscreen, setting up timers, or just nothing.

In this case we do nothing:

let init _model = Command.Noop

The Update Function

The interesting part of any TEA application is always how it updates the model based off incoming events. In Mint Tea things aren't any different. The update function gets called whenever "things happen" – this could be a key press, a timer going off, or even every rendering frame. There is even the possibility of using custom events.

let update event model =
  match event with
  (* if we press `q` or the escape key, we exit *)
  | Event.KeyDown (Key "q" | Escape) -> (model, Command.Quit)
  (* if we press up or `k`, we move up in the list *)
  | Event.KeyDown (Up | Key "k") ->
      let cursor =
        if model.cursor = 0 then List.length model.choices - 1
        else model.cursor - 1
      in
      ({ model with cursor }, Command.Noop)
  (* if we press down or `j`, we move down in the list *)
  | Event.KeyDown (Down | Key "j") ->
      let cursor =
        if model.cursor = List.length model.choices - 1 then 0
        else model.cursor + 1
      in
      ({ model with cursor }, Command.Noop)
  (* when we press enter or space we toggle the item in the list
     that the cursor points to *)
  | Event.KeyDown (Enter | Space) ->
      let toggle status =
        match status with `selected -> `unselected | `unselected -> `selected
      in
      let choices =
        List.mapi
          (fun idx (name, status) ->
            let status = if idx = model.cursor then toggle status else status in
            (name, status))
          model.choices
      in
      ({ model with choices }, Command.Noop)
  (* for all other events, we do nothing *)
  | _ -> (model, Command.Noop)

You may have noticed the special command Quit up there. This command tells Mint Tea that it's time for the application to shutdown.

The View Method

Finally, we need to render our TUI. For that we define a little view method that takes our model and creates a string. That string is our TUI!

Because the view describes the entire UI of your application, you don't have to worry about redrawing logic or things like that. Mint Tea takes care of it for you.

let view model =
  (* we create our options by mapping over them *)
  let options =
    model.choices
    |> List.mapi (fun idx (name, checked) ->
           let cursor = if model.cursor = idx then ">" else " " in
           let checked = if checked = `selected then "x" else " " in
           Format.sprintf "%s [%s] %s" cursor checked name)
    |> String.concat "\n"
  in
  (* and we send the UI for rendering! *)
  Format.sprintf
    {|
What should we buy at the market?

%s

Press q to quit.

  |} options

All Together Now

The last step is to simply run our program. We build our Mint Tea application by calling Minttea.app ~init ~update ~view () and we can start it by calling Minttea.start app ~initial_model

let app = Minttea.app ~init ~update ~view ()
let () = Minttea.start app ~initial_model

We can now run our application:

$ dune exec ./shop.exe

And we get our lovely little TUI app:

What's Next?

This tutorial covers the very basics of building an interactive terminal UI with Mint Tea, but in the real world you'll also need to perform I/O.

You can also check our other examples in GitHub to see more ways in which you can build your TUIs.

Libraries to use with Mint Tea

  • Leaves: Common Mint Tea components to get you started
  • Spices: style, format, and layout tools for terminal applications

More Repositories

1

tldr.jsx

📚 A Reactive web client for tldr-pages
JavaScript
1,547
star
2

caramel

🍬 a functional language for building type-safe, scalable, and maintainable applications
OCaml
1,025
star
3

reason-design-patterns

🗺 An unofficial collection of "design patterns" for ReScript, Reason, and OCaml
Reason
477
star
4

lam

🚀 a lightweight, universal actor-model vm for writing scalable and reliable applications that run natively and on WebAssembly
Rust
247
star
5

httpkit

⚡️ High-level, High-performance HTTP(S) Clients/Servers in Reason/OCaml
OCaml
202
star
6

riot

An actor-model multi-core scheduler for OCaml 5 🐫
OCaml
119
star
7

serde.ml

Serialization framework for OCaml
OCaml
109
star
8

ng2

📐 minimalistic modular angular.js app generator (outdated as of Dec 1st, 2013)
JavaScript
95
star
9

awesome-alt-langs

Just a list of Awesome Alt Langs to check out
78
star
10

reactor

🚀 Native Actors for Reason and OCaml
OCaml
70
star
11

cactus

🌵A composable static site generator
Reason
65
star
12

atacama

Modern, pure OCaml socket pool for Riot
OCaml
37
star
13

scarab

Benchmarking framework for OCaml
OCaml
24
star
14

trail

Minimal composable server framework for Riot
OCaml
24
star
15

blink

A pure OCaml HTTP client for Riot
OCaml
23
star
16

pachadb

an edge database
Rust
22
star
17

tty

A pure OCaml library for working with terminals
OCaml
22
star
18

hotstuff

🔥 Composable, incremental, turnkey document compiler
Rust
21
star
19

colors

A pure OCaml library for manipulating colors in different color spaces.
OCaml
20
star
20

watch

⌚ A portable Go alternative to GNU's watch – very useful for autorunning things!
Go
19
star
21

rules_reason

📐Reason/OCaml rules and tools for Bazel
Python
19
star
22

nomad

Pure OCaml HTTP 1.1/2 & WebSocket server for Riot
Elixir
19
star
23

mlx

OCaml
18
star
24

mesa

A modern, idiomatic web framework for Riot
OCaml
16
star
25

tldr.js

Old version now mirroring React-client, please go to
JavaScript
16
star
26

ocaml-grpc

A gRPC implementation written in pure OCaml/Reason
OCaml
16
star
27

dotfiles

💾 ~/.*
Python
14
star
28

q-lang

Rust
14
star
29

react-useMailbox

📫 A small React hook to turn your components into "Actors".
JavaScript
13
star
30

chatty

a TUI app for chatting on slack
OCaml
12
star
31

Bakery

CoffeeScript, Backbone, RequireJS, HeadJs and Jasmine. All together.
JavaScript
11
star
32

Expresso

CoffeeScript compiling for Pythonistas.
Python
10
star
33

config.ml

conditional compilation via attributes for OCaml
OCaml
10
star
34

castore

A portable pure OCaml CA Store
OCaml
10
star
35

loop

Unbounded loops with early breaks and continues for OCaml 5.
OCaml
9
star
36

hooke

Spring-based animation library for OCaml
OCaml
9
star
37

mixtape

🎧 🔄 Synchronised Playlist Playback for Spotify
JavaScript
8
star
38

telemetry

Lightweight event dispatching for OCaml.
OCaml
8
star
39

oak

🌳 A minimalistic Tree-like tool built in Reason Native
OCaml
8
star
40

twitchboard

📺 Real-time Stream Stats Tool for Twitch.tv
OCaml
7
star
41

idris-coda

📦 A collection of Idris packages
Idris
6
star
42

blast64

⚡ An apparently even faster base64 decoder for Chrome
HTML
6
star
43

fuzzql

⚙️ A GraphQL Fuzzy Testing Toolkit
OCaml
6
star
44

servus

A static-file server written with http/af + Reason Native
OCaml
6
star
45

woolly

a little mastodon client
TypeScript
6
star
46

asdf

🐛 Random code snippets
Idris
4
star
47

escheck

JavaScript
4
star
48

ng-board

a realtime dashboard
JavaScript
4
star
49

paper-eaters

📚
4
star
50

switchboard.rb

Ruby client for switchboard.spatch.co
Ruby
4
star
51

tap-idris

🍻 A simple TAP producer and consumer/reporter for Idris
Idris
4
star
52

reason-gc

🗑 A small exploration of the Reason/OCaml Garbage Collector
OCaml
4
star
53

wittgenstein

📚 A semantic, real-time, distributed, knowledge base.
Erlang
4
star
54

PhantomSDL

👾 A game engine I was building when I was 14
C
3
star
55

zazen

🙏 sit, breathe, code.
JavaScript
3
star
56

erlang-gui

An experiment in building high-performance, native graphical user interfaces in Erlang
Erlang
3
star
57

bazaar

find anything you need in ocaml
OCaml
3
star
58

jawa

OCaml
3
star
59

try

♻️ A portable Go utility to retry commands with backoff
Go
2
star
60

libra

⚖️ A Lisp Parser in Idris
Idris
2
star
61

anchorman

⚓👨 An Erlang library for broadcasting information
Erlang
2
star
62

unveil

🎬 The Reactive Javascript Presentation Library
JavaScript
2
star
63

play.app

an ember.js phonegap application
JavaScript
2
star
64

node-puntopagos

PuntoPagos Module for NodeJS
JavaScript
2
star
65

making-makefiles

A small set of koans to learn about Makefiles
Vim Script
2
star
66

rocket

following the Essentials of Compilation book in OCaml
OCaml
2
star
67

ws

🔄 An erlang WebSocket server
Erlang
2
star
68

pry

🔭 An Erlang application for observing supervision trees
Erlang
2
star
69

rx-history

History Observable for RxJS
JavaScript
2
star
70

dasBlog

For blogging wasn't German enough.
Python
2
star
71

rdn

Reason Data Notation
2
star
72

EXII-macOS

A poorly written User-land driver for the EXII USB device family
Swift
2
star
73

libc.ml

Raw bindings to platform APIs for OCaml
OCaml
2
star
74

ng-board-smoothie

smoothie graphs widget for ng-board
JavaScript
2
star
75

message.me

a sample web app built with ng2
JavaScript
1
star
76

go-go-gadget

go tests
Go
1
star
77

tweet-cli

tweet from your cli
JavaScript
1
star
78

projector

📽️ Stay on top of your Github Projects
JavaScript
1
star
79

retrie

A quick and dirty, likely broken trie
OCaml
1
star
80

reactor-web

OCaml
1
star
81

textmine.js

Text-mining for NodeJS
1
star
82

ngage-platformer

angularjs based platformer game
JavaScript
1
star
83

cerebro

🔭 A simple app to track down Mutant-grade Engineers on Github
JavaScript
1
star
84

piclevel-api

JavaScript
1
star
85

v-sexpr

An S-expression library for V
V
1
star
86

transporter.io-ng-board

ng-board transport for transporter.io
JavaScript
1
star
87

transporter.io

a transport system over websockets
JavaScript
1
star
88

PlayApp

the play app!
Objective-C
1
star
89

webtail

A simple file piping over http script using connect and spawn('tail')
JavaScript
1
star
90

grunt-knox

A set of Knox tasks for GruntJS
JavaScript
1
star
91

Toasty

A Toasted game framework for the Marmalade SDK
C++
1
star
92

anchorman.js

a reporter library for nodejs
JavaScript
1
star
93

bsb-load-test

Just a repo building a crapload of modules
OCaml
1
star
94

transporter.io-events

events transport for transporter.io
JavaScript
1
star
95

tribble

OCaml
1
star
96

lisping

some lisping!
Common Lisp
1
star
97

cleverboard

the first project using ng-board and transporter.io
1
star
98

setup.js

my setup scripts
JavaScript
1
star
99

message.me-api

The backend powering the app built with ng2
JavaScript
1
star
100

erl-port-test

Erlang
1
star