Some time ago I built a (terminal) text editor and one of the challenges is to implement the renderer which is normally done by messing with termios and changing some terminal parameters - as this is "device"-dependent there exist databases like terminfo and abstraction-layers like curses which give you an API similar to "Write this text at position x/y in green".
However curses and terminfo are big programs and most terminals they support are not in use anymore, so if one is OK with supporting just 99.9 (and not 99.999 like curses)% of modern terminals (say xterm, linux-console, st, urxvt, $otherterm), it is feasible to roll your own or use an alternative like e.g. termbox (https://github.com/nsf/termbox).
It provides a simple API like "write Unicode-Char to position x/y in specific $style" and some utility functions to setup the terminal (e.g. one has to consider how to react to window-size (== terminal rows/cols size) events). You can then write your app similar to a game-loop like the following:
while state['should_run']:
render(state)
update(state)
get_input(state)
I think such an API is pretty nice to work with - this is different to the React approach, but for writing terminal GUIs like text editors it worked pretty well for me.
Many (native) games use a similar approach when rendering menu's and stuff and although there are terrible ones most menus of games look pretty slick.
(just to emphasize, I think parent understands this)
The render function here can be stateful, React vdom-style: diff with previous output, efficiently update output based on the perf characteristics of a terminal. But it's an entirely separate domain, which is great for the developer.
Surprised to see that this doesn't use ncurses at all. I was really hoping to see something that brought the painlessness of using React to building an ncurses textual interface.
Seeing that it doesn't use ncurses, I'm suspicious of how it does re-draws. I'd imagine the naive thing of just redrawing every bit of text on the screen for every reactive event would work, but be pretty slow. Of course, if they're doing something smarter than this, I'd be interested to hear how it's done.
One optimization that's not hard to do for terminal output is a line cache: if a row is the same, don't render it. The rendering logic is probably already operating on lines, so this doesn't require the fancy diffing; but diffing a flat grid seems easier anyway vs the dom tree. For context, (I suspect parent already understands this) this is analogous to the vdom optimizations (not shouldComponentUpdate optimizations) possible with React. Except the algorithm is more straightforward: for each character on the grid, is the content the same since the previous render?
This library looks real neat, a declarative TUI with an api already familiar to many developers. I've thought this would be a neat thing to exist since I started using React after having written a similar declarative, redraw-only-as-necessary terminal rendering library for bpython (http://ballingt.com/bpython-curtsies/).
In it (http://curtsies.readthedocs.io/en/latest/) the only rendering optimization was a line cache. For the specific use case of an interactive interpreter this worked pretty well: once a line is changing, it's probably changing a lot. But you could go further using the output of diffing of arrays of terminal text.
If you keep an internal representation of the terminal (which you should totally do when building a terminal toolkit), you can just update whatever changed. It is not hard with cursor positioning sequences.
Which is actually a really good argument for avoiding this project - if it's so easy to stay compatible, then... stay compatible. Feels like a dead project if it's so far out of date.
vi's initial release date is from 1976. Maybe you could ask someone who used it back then whatever happened to terminal apps being streams of text? :)
This is an interesting idea; a React-like layer to something like ncurses could be super useful and encourage more command line apps. Doing this in JS seems like a waste though...
I'm hopeful that someone will add a terminal mode interface to VSCode. In theory, this would allow it to even run without Electron (using just NodeJS). The team was shrewd enough not to expose too many internals through the extension interface in the original design, so it's not intractable (at least not as high up on the scale of intractability as making non-breaking changes to Firefox).
Ok fair point :-). And ncurses has been around a few years too. I just object to things like test-runners (looking at you jest) being written like this.
There is a distinction to be made between terminal and command-line applications. You can have CLI without going through terminal layer, and you can have very GUI-esque stuff in your terminal.
Well, no it isn't. ReactDOM is designed for consuming the output of React and constructing a DOM-based UI. React itself is fundamentally a reconciliation engine. This is why we've seen React move to such places as React Native (React for native iOS and Android interfaces), React-UMG (React for constructing interfaces in Unreal Engine's Unreal Motion Graphics), React-PDF for generating PDFs, the list goes on. You can view more here:
Yeah - my React/Redux links list has a big section of articles that talk about React's implementation, including both articles that dig into the actual React source and implementation, and many "build-a-mini React" articles: https://github.com/markerikson/react-redux-links/blob/master... .
We use React at Netflix for building our TV app. We moved away from a WebKit stack a few years ago; we have our own retained-mode graphics engine optimized for TV devices so we created React-Gibbon. Besides the developer ergonomics stemming from React, we like the approach it affords us for reaching low-performing devices.
At Airbnb we built a renderer for Sketch that allows us to generate templates for designers with our real, cross-platform, production components (built with React Primitives) https://github.com/airbnb/react-sketchapp
> The key difference you have to remember is that the rendering result isn't a DOM, but a string, which Ink writes to the output.
Seems like a pretty easy switch.
The best part about React is being able to declare what your UI will look like instead of constructing it. It's a useful abstraction for any dynamic UI.
However curses and terminfo are big programs and most terminals they support are not in use anymore, so if one is OK with supporting just 99.9 (and not 99.999 like curses)% of modern terminals (say xterm, linux-console, st, urxvt, $otherterm), it is feasible to roll your own or use an alternative like e.g. termbox (https://github.com/nsf/termbox).
It provides a simple API like "write Unicode-Char to position x/y in specific $style" and some utility functions to setup the terminal (e.g. one has to consider how to react to window-size (== terminal rows/cols size) events). You can then write your app similar to a game-loop like the following:
I think such an API is pretty nice to work with - this is different to the React approach, but for writing terminal GUIs like text editors it worked pretty well for me.Many (native) games use a similar approach when rendering menu's and stuff and although there are terrible ones most menus of games look pretty slick.