Hacker News new | past | comments | ask | show | jobs | submit login
Ink: React for interactive command-line apps (github.com/vadimdemedes)
129 points by ingve on July 23, 2017 | hide | past | favorite | 42 comments



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.


Think [react-blessed](https://github.com/Yomguithereal/react-blessed) comes close to what you're thinking of.


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.

edit: whoa https://github.com/Yomguithereal/react-blessed looks awesome for this


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.


This is what [n]curses does, since decades ago.


Well, my point is that everyone can do this, ncurses is no black magic :)


If anyone is interested in using actual React for the same purposes, check out react-blessed: https://github.com/Yomguithereal/react-blessed


Given it’s barely updated and only has official support for React 0.14... it’s not really something I’d recommend.

Shame too, it has the potential to be great.


It not being the latest version if React is hardly a major issue. You miss out on some of the latest optimizations, but the API hasn't changed.


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.


Speaks to longevity and maintenance however. It’s not like React 15 is exactly new in the space, or hard to update to.


What ever happened to terminal "apps" being streams of text? Is just the JS ecosystem that does this or are others seeing the same trend?


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's a React interface to blessed! https://github.com/Yomguithereal/react-blessed


Brick for Haskell is really nice.


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.


That's...never been a thing?


Would it be possible to write an editor in Ink?


Right now, you can't even know the height or width of the terminal. So, I think you would need to start there.

https://github.com/vadimdemedes/ink/issues/5


What terminal emulator is being used in that image?


Answered in the issue tracker: https://github.com/vadimdemedes/ink/issues/10


Looks like hyper.


Nice project but why using REACT for this as REACT is designed for dom manipulation


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:

https://github.com/chentsulin/awesome-react-renderer


> React itself is fundamentally a reconciliation engine.

Do you know of any good articles / books / videos that talk more about this idea?


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... .


Thank you!


This was the first overview I read, and it's pretty good (but incomplete):

https://github.com/reactjs/react-basic


Thanks, I'll check it out.


Thanks, React for PDF is new to me. I'll check out.


Not just the DOM.

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.


React is a paradigm.

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


https://github.com/vadimdemedes/ink/blob/master/package.json

It's not using React. "X for Y" is an analogy, as in "it's like React, but for CLI apps!"


> 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.


I'm guessing the end-goal of this is to be an ncurses replacement. Anything else wouldn't make sense... right?


And why would that make sense? Ncurses has been around a long time and is solid.


It’s hardly elegant to use or reason about though.

Solid and proven and robust, yes.

Different things however.


Doesn't seem to be using react. Just a similar API.




Consider applying for YC's W25 batch! Applications are open till Nov 12.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: