WEBVTT 00:07.600 --> 00:00:09.120 Hi! I'm Erik Anderson, 00:00:09.120 --> 00:00:10.559 and I'll be talking about tui, 00:00:10.559 --> 00:00:11.840 a user interface framework 00:11.840 --> 00:00:15.040 that I've written in Emacs Lisp. 00:15.040 --> 00:00:16.196 First, I want to talk a bit about 00:00:16.196 --> 00:00:17.296 the problem space of 00:00:17.296 --> 00:00:18.728 user interface development, 00:00:18.728 --> 00:00:20.880 specifically, I want to quickly illustrate 00:00:20.880 --> 00:00:22.320 some of the complexities involved 00:00:22.320 --> 00:00:26.480 with UI implementation in Emacs. 00:26.480 --> 00:00:27.920 In Emacs, we have the ubiquitous 00:00:27.920 --> 00:00:29.920 buffer object type that forms the container 00:00:29.920 --> 00:00:31.920 for most content in Emacs. 00:00:31.920 --> 00:00:34.079 Most interfaces we interact with 00:00:34.079 --> 00:00:36.239 consist of character based content 00:00:36.239 --> 00:00:38.239 in a buffer that's presented 00:00:38.239 --> 00:00:40.079 in a window in frame. 00:40.079 --> 00:00:41.520 Although the underlying content 00:00:41.520 --> 00:00:44.320 may be textual, Emacs has capable APIs 00:00:44.320 --> 00:00:47.680 to present rich content. 00:47.680 --> 00:00:49.200 The pervasiveness of buffers 00:00:49.200 --> 00:00:50.879 affords us wonderful flexibility. 00:00:50.879 --> 00:00:52.559 This presentation, for instance, 00:00:52.559 --> 00:00:55.520 is running in an Emacs buffer. 00:55.520 --> 00:00:57.420 Using Emacs's built-in basic 00:00:57.420 --> 00:00:59.199 button library, we can insert 00:00:59.199 --> 00:01:00.884 an interactive button 00:01:00.884 --> 00:01:01.760 that shows a message 00:01:01.760 --> 00:01:06.080 in the minibuffer when clicked. 01:06.080 --> 00:01:09.200 What about UIs that express application state? 01:09.200 --> 00:01:11.439 Most applications don't have a static UI. 01:11.439 --> 00:01:13.280 As application state changes, 00:01:13.280 --> 00:01:14.320 the UI should change 00:01:14.320 --> 00:01:18.479 to display the desired content. 01:18.479 --> 00:01:20.320 One simplifying strategy is to simply 01:20.320 --> 00:01:23.600 re-render the entire UI upon any change. 01:23.600 --> 00:01:25.680 First erase the contents of the buffer, 01:25.680 --> 00:01:27.600 and then reinsert your UI again 00:01:27.600 --> 00:01:29.040 with desired changes, 00:01:29.040 --> 00:01:33.040 and restore things like point and region. 01:33.040 --> 00:01:34.560 Basic composition is possible 00:01:34.560 --> 00:01:35.759 with this approach. 00:01:35.759 --> 00:01:37.200 Simply insert the elements 00:01:37.200 --> 00:01:39.280 of the UI in sequence. 01:39.280 --> 00:01:40.640 Complex elements can be 00:01:40.640 --> 00:01:44.320 composed of multiple sub-elements. 01:44.320 --> 00:01:45.840 UIs can be made extensible, 00:01:45.840 --> 00:01:47.040 and expose this composition, 00:01:47.040 --> 00:01:49.360 for example, with insertion hooks 00:01:49.360 --> 00:01:52.320 like magit's status sections hook. 01:52.320 --> 00:01:54.159 This generally relies on elements 00:01:54.159 --> 00:01:56.640 being well-behaved inserting themselves, 00:01:56.640 --> 00:02:00.399 not affecting the rest of the buffer. 02:00.399 --> 00:02:02.960 If we find ourselves with complex UIs, 02:02.960 --> 00:02:04.640 large buffers, long lines, 00:02:04.640 --> 00:02:06.320 or poor rendering performance, 00:02:06.320 --> 00:02:09.039 we might consider partial UI updates 00:02:09.039 --> 00:02:11.360 rather than re-rendering completely. 02:11.360 --> 00:02:12.800 In that case, the complexity 00:02:12.800 --> 00:02:15.840 for maintaining the UI quickly increases. 00:02:15.840 --> 00:02:17.360 As accessible as buffers are, 00:02:17.360 --> 00:02:19.120 we don't have high level abstractions 00:02:19.120 --> 00:02:20.879 for managing portions of a UI 00:02:20.879 --> 00:02:22.160 rendered to a buffer. 00:02:22.160 --> 00:02:23.520 (It) is left up to the programmers 00:02:23.520 --> 00:02:25.440 to track and update UI state. 00:02:25.440 --> 00:02:26.540 This is generally done by 00:02:26.540 --> 00:02:28.959 one of two methods, reflection, 00:02:28.959 --> 00:02:30.800 searching for strings or text properties 00:02:30.800 --> 00:02:34.239 within the buffer, or tracking segments 00:02:34.239 --> 00:02:36.080 of a UI buffer manually 00:02:36.080 --> 00:02:38.959 using numeric offsets, or marker, 00:02:38.959 --> 00:02:45.280 or overlay objects. 02:45.280 --> 00:02:47.280 Here we have a basic timer component 02:47.280 --> 00:02:48.720 that shows elapsed time 00:02:48.720 --> 00:02:50.319 after it's inserted. 00:02:50.319 --> 00:02:52.160 It works, but has several problems. 00:02:52.160 --> 00:02:55.519 It doesn't restore the user's point or mark, 02:55.519 --> 00:02:59.120 so it snaps back after every render, 02:59.120 --> 00:03:01.040 after every update. 03:01.040 --> 00:03:03.360 It relies on singleton global state, 00:03:03.360 --> 00:03:05.124 so isn't designed to coexist 00:03:05.124 --> 00:03:12.000 with other instances of itself. 03:12.000 --> 00:03:13.200 It doesn't use a marker, 00:03:13.200 --> 00:03:14.640 so it's sensitive to content 00:03:14.640 --> 00:03:16.239 proceeding it, that's following it, 00:03:16.239 --> 00:03:23.519 changing in the buffer. 03:23.519 --> 00:03:25.120 The update logic doesn't even consider 03:25.120 --> 00:03:26.799 which buffer it's trying to update. 00:03:26.799 --> 00:03:27.840 If I switch buffers, 00:03:27.840 --> 00:03:29.360 it will insert into another buffer, 00:03:29.360 --> 00:03:31.519 or even the minibuffer. 03:31.519 --> 00:03:33.360 It can't remove itself, or re-render 00:03:33.360 --> 00:03:34.879 if it gets corrupted, as you see. 00:03:34.879 --> 00:03:35.920 All in all, it's not 00:03:35.920 --> 00:03:38.400 a readily composable component. 03:38.400 --> 00:03:39.519 Addressing these components 00:03:39.519 --> 00:03:41.920 within this logic further increases 00:03:41.920 --> 00:03:43.936 the implementation complexity 00:03:43.936 --> 00:03:45.680 of this component, 00:03:45.680 --> 00:03:46.640 and still this component 00:03:46.640 --> 00:03:47.280 would likely have 00:03:47.280 --> 00:03:49.120 various subtle differences 03:49.120 --> 00:03:52.480 with other components 00:03:52.480 --> 00:03:58.959 implemented by other authors. 03:58.959 --> 00:04:00.319 For those of you unfamiliar 00:04:00.319 --> 00:04:02.159 with this term Yak Shaving, 00:04:02.159 --> 00:04:04.080 that is a quite technical term 00:04:04.080 --> 00:04:07.599 for any seemingly pointless activity, 00:04:07.599 --> 00:04:09.680 which is actually necessary 00:04:09.680 --> 00:04:10.879 to solve a problem, 04:10.879 --> 00:04:11.920 which solves a problem, 00:04:11.920 --> 00:04:14.799 which, several levels of recursion later, 04:14.799 --> 00:04:18.239 solves the real problem you're working on. 04:18.239 --> 00:04:19.943 The itch that led to this project 00:04:19.943 --> 00:04:21.840 was the desire to display a dense summary 00:04:21.840 --> 00:04:24.400 of local Git repository statuses. 04:24.400 --> 00:04:26.560 Encountering various implementation 04:26.560 --> 00:04:30.080 complexity for building UI elements, 04:30.080 --> 00:04:31.680 it led to the yak shaving endeavor 00:04:31.680 --> 00:04:33.680 that produced tui. 04:33.680 --> 00:04:35.440 When I wrote the library, 04:35.440 --> 00:04:36.479 I had recently played with 00:04:36.479 --> 00:04:39.360 a popular UI framework called React, 00:04:39.360 --> 00:04:41.840 and had an interest in learning 00:04:41.840 --> 00:04:44.080 about the internal architecture of React. 00:04:44.080 --> 00:04:45.680 So, rather than implement 00:04:45.680 --> 00:04:46.960 a string caching layer 00:04:46.960 --> 00:04:49.280 on top of tabulated list mode, 00:04:49.280 --> 00:04:50.360 I was rather inclined 00:04:50.360 --> 00:04:52.400 to go down the path of implementing 00:04:52.400 --> 00:04:58.960 the React API for Emacs Lisp. 04:58.960 --> 00:05:00.896 I'll offer a brief view of 00:05:00.896 --> 00:05:05.120 the tui Emacs Lisp API. 05:05.120 --> 00:05:07.360 Inserting component content 00:05:07.360 --> 00:05:08.320 is pretty straightforward. 00:05:08.320 --> 00:05:10.160 You take a component tree 00:05:10.160 --> 00:05:16.240 and render it in an Emacs buffer. 05:16.240 --> 00:05:18.639 If any elements in that tree are updated, 05:18.639 --> 00:05:21.039 their respective content on the tree 00:05:21.039 --> 00:05:26.320 is updated automatically. 05:26.320 --> 00:05:27.919 Here's a basic re-implementation 00:05:27.919 --> 00:05:29.919 of the straw man timer from earlier, 00:05:29.919 --> 00:05:32.400 using a macro for syntactic trigger. 05:32.400 --> 00:05:34.404 You'll notice that 00:05:34.404 --> 00:05:36.164 the signature includes its own 00:05:36.164 --> 00:05:44.560 object reference, arguments, and state. 05:44.560 --> 00:05:46.400 Associating arguments in the state 05:46.400 --> 00:05:51.360 with a component instance out of the box, 05:51.360 --> 00:05:54.400 makes it easy to design reusable components, 00:05:54.400 --> 00:06:06.080 and forms the basis for partial UI updates. 06:06.080 --> 00:06:07.840 The component rendering anchors 00:06:07.840 --> 00:06:09.199 are durable, so content can be 00:06:09.199 --> 00:06:11.360 added and removed surrounding content, 00:06:11.360 --> 00:06:12.479 or even within the region 00:06:12.479 --> 00:06:13.280 of the component, 00:06:13.280 --> 00:06:16.319 and replaced when it re-renders. 06:16.319 --> 00:06:17.600 Components will also 00:06:17.600 --> 00:06:20.880 cleanly remove themselves from a buffer 00:06:20.880 --> 00:06:28.400 when instructed to. 06:28.400 --> 00:06:30.160 tui contains the core implementation 00:06:30.160 --> 00:06:33.440 of the React API, so components, 06:33.440 --> 00:06:35.840 their constituent props, state, 00:06:35.840 --> 00:06:38.000 and all of the lifecycle methods 00:06:38.000 --> 00:06:39.440 associated with them, 06:39.440 --> 00:06:41.120 as well as keys, refs, 00:06:41.120 --> 00:06:43.228 and the fundamental 00:06:43.228 --> 00:06:47.520 reconciliation algorithm of React. 06:47.520 --> 00:06:52.188 A variety of other React APIs 00:06:52.188 --> 00:06:58.080 that haven't been implemented yet. 06:58.080 --> 00:07:00.080 It contains some useful features so far, 07:00.080 --> 00:07:02.639 such as hot reloading, reflection, 00:07:02.639 --> 00:07:06.164 and various debugging tools, 00:07:06.164 --> 00:07:12.639 and some reconciliation logging. 07:12.639 --> 00:07:14.880 Lastly, I'd like to give you some quick 07:14.880 --> 00:07:19.039 visual taste of components built with tui. 07:19.039 --> 00:07:20.240 The grid view that motivated 00:07:20.240 --> 00:07:21.520 my development of this package 00:07:21.520 --> 00:07:23.039 is very similar to Magit's 00:07:23.039 --> 00:07:30.720 list repositories functionality. 07:30.720 --> 00:07:36.080 Essentially, tabulated list mode 07:36.080 --> 00:07:39.440 but portable and has a separated model 00:07:39.440 --> 00:07:49.360 and presentation layers. 07:49.360 --> 00:07:51.840 Here's a basic xkcd comic viewer 00:07:51.840 --> 00:08:07.360 showing a couple classics. 08:07.360 --> 00:08:10.080 A long-standing React tutorial is 08:10.080 --> 00:08:13.280 building a tic-tac-toe game 08:13.280 --> 00:08:14.879 as a bit of a gimmick, 08:14.879 --> 00:08:17.039 and I'm not quite satisfied with 00:08:17.039 --> 00:08:25.599 the buffering direction, 00:08:25.599 --> 00:08:27.120 but it got me thinking about 00:08:27.120 --> 00:08:28.639 layout engines with text, 00:08:28.639 --> 00:08:35.120 so it was interesting. 08:35.120 --> 00:08:38.560 And here's a small 00:08:38.560 --> 00:08:46.080 Unicode character viewer 08:46.080 --> 00:08:55.279 capable of showing a bunch of characters. 08:55.279 --> 00:08:57.279 If this piques your interest, 00:08:57.279 --> 00:08:59.200 I would encourage you to check it out. 08:59.200 --> 00:09:01.440 tui should be usable by anyone 00:09:01.440 --> 00:09:04.640 with some basic Elisp familiarity, 09:04.640 --> 00:09:09.680 no prior knowledge about JavaScript 00:09:09.680 --> 00:09:12.080 or React is necessary. 09:12.080 --> 00:09:13.732 I'd absolutely love to talk with people 00:09:13.732 --> 00:09:14.880 about the tui package, 00:09:14.880 --> 00:09:17.440 textual user interfaces in general, 00:09:17.440 --> 00:09:19.040 and really anything in Emacs. 00:09:19.040 --> 00:09:20.693 If you have any ideas, feedback, 00:09:20.693 --> 00:09:21.680 or want to contribute, 00:09:21.680 --> 00:09:23.360 please reach out. 09:23.360 --> 00:09:24.360 Thank you all for listening. 00:09:24.360 --> 00:09:27.000 [captions by bhavin192 (Bhavin Gandhi)]