Hi! I'm Erik Anderson,
and I'll be talking about tui,
a user interface framework
that I've written in Emacs Lisp.
First, I want to talk a bit about
the problem space of
user interface development,
specifically, I want to quickly illustrate
some of the complexities involved
with UI implementation in Emacs.
In Emacs, we have the ubiquitous
buffer object type that forms the container
for most content in Emacs.
Most interfaces we interact with
consist of character based content
in a buffer that's presented
in a window in frame.
Although the underlying content
may be textual, Emacs has capable APIs
to present rich content.
The pervasiveness of buffers
affords us wonderful flexibility.
This presentation, for instance,
is running in an Emacs buffer.
Using Emacs's built-in basic
button library, we can insert
an interactive button
that shows a message
in the minibuffer when clicked.
What about UIs that express application state?
Most applications don't have a static UI.
As application state changes,
the UI should change
to display the desired content.
One simplifying strategy is to simply
re-render the entire UI upon any change.
First erase the contents of the buffer,
and then reinsert your UI again
with desired changes,
and restore things like point and region.
Basic composition is possible
with this approach.
Simply insert the elements
of the UI in sequence.
Complex elements can be
composed of multiple sub-elements.
UIs can be made extensible,
and expose this composition,
for example, with insertion hooks
like magit's status sections hook.
This generally relies on elements
being well-behaved inserting themselves,
not affecting the rest of the buffer.
If we find ourselves with complex UIs,
large buffers, long lines,
or poor rendering performance,
we might consider partial UI updates
rather than re-rendering completely.
In that case, the complexity
for maintaining the UI quickly increases.
As accessible as buffers are,
we don't have high level abstractions
for managing portions of a UI
rendered to a buffer.
(It) is left up to the programmers
to track and update UI state.
This is generally done by
one of two methods, reflection,
searching for strings or text properties
within the buffer, or tracking segments
of a UI buffer manually
using numeric offsets, or marker,
or overlay objects.
Here we have a basic timer component
that shows elapsed time
after it's inserted.
It works, but has several problems.
It doesn't restore the user's point or mark,
so it snaps back after every render,
after every update.
It relies on singleton global state,
so isn't designed to coexist
with other instances of itself.
It doesn't use a marker,
so it's sensitive to content
proceeding it, that's following it,
changing in the buffer.
The update logic doesn't even consider
which buffer it's trying to update.
If I switch buffers,
it will insert into another buffer,
or even the minibuffer.
It can't remove itself, or re-render
if it gets corrupted, as you see.
All in all, it's not
a readily composable component.
Addressing these components
within this logic further increases
the implementation complexity
of this component,
and still this component
would likely have
various subtle differences
with other components
implemented by other authors.
For those of you unfamiliar
with this term Yak Shaving,
that is a quite technical term
for any seemingly pointless activity,
which is actually necessary
to solve a problem,
which solves a problem,
which, several levels of recursion later,
solves the real problem you're working on.
The itch that led to this project
was the desire to display a dense summary
of local Git repository statuses.
Encountering various implementation
complexity for building UI elements,
it led to the yak shaving endeavor
that produced tui.
When I wrote the library,
I had recently played with
a popular UI framework called React,
and had an interest in learning
about the internal architecture of React.
So, rather than implement
a string caching layer
on top of tabulated list mode,
I was rather inclined
to go down the path of implementing
the React API for Emacs Lisp.
I'll offer a brief view of
the tui Emacs Lisp API.
Inserting component content
is pretty straightforward.
You take a component tree
and render it in an Emacs buffer.
If any elements in that tree are updated,
their respective content on the tree
is updated automatically.
Here's a basic re-implementation
of the straw man timer from earlier,
using a macro for syntactic trigger.
You'll notice that
the signature includes its own
object reference, arguments, and state.
Associating arguments in the state
with a component instance out of the box,
makes it easy to design reusable components,
and forms the basis for partial UI updates.
The component rendering anchors
are durable, so content can be
added and removed surrounding content,
or even within the region
of the component,
and replaced when it re-renders.
Components will also
cleanly remove themselves from a buffer
when instructed to.
tui contains the core implementation
of the React API, so components,
their constituent props, state,
and all of the lifecycle methods
associated with them,
as well as keys, refs,
and the fundamental
reconciliation algorithm of React.
A variety of other React APIs
that haven't been implemented yet.
It contains some useful features so far,
such as hot reloading, reflection,
and various debugging tools,
and some reconciliation logging.
Lastly, I'd like to give you some quick
visual taste of components built with tui.
The grid view that motivated
my development of this package
is very similar to Magit's
list repositories functionality.
Essentially, tabulated list mode
but portable and has a separated model
and presentation layers.
Here's a basic xkcd comic viewer
showing a couple classics.
A long-standing React tutorial is
building a tic-tac-toe game
as a bit of a gimmick,
and I'm not quite satisfied with
the buffering direction,
but it got me thinking about
layout engines with text,
so it was interesting.
And here's a small
Unicode character viewer
capable of showing a bunch of characters.
If this piques your interest,
I would encourage you to check it out.
tui should be usable by anyone
with some basic Elisp familiarity,
no prior knowledge about JavaScript
or React is necessary.
I'd absolutely love to talk with people
about the tui package,
textual user interfaces in general,
and really anything in Emacs.
If you have any ideas, feedback,
or want to contribute,
please reach out.
Thank you all for listening.
[captions by bhavin192 (Bhavin Gandhi)]