Terminal UIs can be created using the membrane.lanterna
backend. For a full example, check out the example projects:
Compared to most of the other backends, there are a few major differences:
Many of the utilities found in membrane.ui
can be used as-is for building terminal UIs. For example: bounds
, origin
, vertical-layout
, on
, translate
, with-color
, and others all work as you would expect. However, some of the membrane.ui
primitives don't really make sense for terminals UIs. For example, label
, checkbox
, rectangle
, and some others will not work for terminal user interfaces because of the differences from most other backends. For primitives in membrane.ui
that don't make sense for terminal UIs, you can find counterparts in the membrane.lanterna
namespace.
Supporting user interfaces that simultaneously work in terminals and with other backends is a non-goal. While it might be tempting to try to make the exact same primitives work for these two different mediums, the differences would make the development experience unfun in the general case. However, the primitives in membrane.ui
and membrane.lanterna
are just regular clojure data structures. Building a reduced set of elements for a constrained use case that work in terminal and for desktop is possible, but is left as an exercise for the reader.
Repl driven development requires a config step since the default repl will consume System/in
and System/out
.
Below is the setup for cider/nrepl. If you need support for a different REPL, please file an issue or drop a request in #membrane on the clojurians slack.
To setup a repl driven workflow, start up the nrepl server in the terminal that will be displaying your UI. Probably something like:
clojure -Sdeps '{:deps {nrepl/nrepl {:mvn/version "0.8.3"} cider/cider-nrepl {:mvn/version "0.25.6"}}}' -M:dev:nrepl --middleware '["cider.nrepl/cider-middleware" "membrane.lanterna/preserve-system-io"]' --port 7888
This command will start an nrepl server on port 7888. The default nrepl middleware will replace System/in
and System/out
. Add the membrane.lanterna/preserve-system-io
to store System/in
and System/out
in the respective vars: membrane.lanterna/in
and membrane.lanterna/out
.
To connect to nrepl server in emacs, use M-x cider-connect
and pass localhost
as the host and 7888
for the port.
To run a UI that uses the correct I/O streams, pass the :in
and :out
as options like so:
(lanterna/run #'app-root
{:in membrane.lanterna/in
:out membrane.lanterna/out})
In some cases, you may want to restart the UI or display a different UI. You can stop a UI by passing a channel as the :close-ch
option that will stop rendering when it receives a value.
(require '[clojure.core.async :as async])
;; Rich comment block
(comment
;; start the ui
(do
(def close-ch (async/chan))
(lanterna/run #'app-root
{:in membrane.lanterna/in
:out membrane.lanterna/out
:close-ch close-ch}))
;; Some time later, stop the UI. You can call `lanterna/run` to start a new UI.
(async/close! close-ch)
,)
Can you improve this documentation? These fine people already did:
Adrian & Adrian SmithEdit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close