{"_id":"@acransac/terminal","_rev":"2-e938b2483bd4e5e10922b352062f79f2","name":"@acransac/terminal","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@acransac/terminal","version":"1.0.0","description":"A framework to create a UI in a terminal","main":"src/index.js","scripts":{"test":"(cd test && node test_terminal.js)"},"author":{"name":"Adrien Cransac"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/acransac/terminal.git"},"bugs":{"url":"https://github.com/acransac/terminal/issues"},"homepage":"https://github.com/acransac/terminal","dependencies":{"neo-blessed":"^0.2.0","@acransac/streamer":"^1.0.0","@acransac/tester":"^1.0.0"},"gitHead":"58ba3ed06e464b3d52ca3a570be2275b736621d2","_id":"@acransac/terminal@1.0.0","_nodeVersion":"12.12.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-C0WVX0FSHzWEtTbTv5rTynu+FGwIMPCm5LSJm+Nof2srRQ+RnU+s2xCdQpBNiXk/uORwLD/2AYJFhaSAXgpRSw==","shasum":"4ad4d7f2848c997c5fae416c21e29bf28d906bb5","tarball":"https://registry.npmjs.org/@acransac/terminal/-/terminal-1.0.0.tgz","fileCount":35,"unpackedSize":737274,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfJvamCRA9TVsSAnZWagAAzvgP/iOsfYtqqInPWM/4Uj32\nYmq1gCN5Jun8SVgec+KfOU7/tAeP+ki/YPJXv1Iy78EURYYTN4F9z2qfPIBB\nuF9HwgKLMweUiaBDy4werZbQ62bqhrkDUal5ek+Duo3tcEUZuEZmzmpQLONZ\nJ5vMqYgPIL97OldPmyiMftbr+m/WDq25hXMSdeTtpaqjzWoIDCBvWxce1YtJ\nR3wB3e6lAkJBBqo2SB1P2lRlc6UUA0gmOmOK/Tacq1Q27UlNdKkjpjA9YHwp\nBLEQYAJQYhUYe9/1zgmDI4agvM2WGuqkOZRHunsUcwFo9JCf5tPGQbmskKK1\nrc68cBy3+2HDsVQmKA/wX3OHR7PLtsYz+9TkVCDz9yiICdfHrl+sAJZDvpUx\nZngxQxFwONIqcQPxKriw+vilhJblyTnd/B10ULAG1p2ily3GH1sBA1NsXuNw\nzsiALWvid0fuxSj/BthR6+Dc3/cgO2t0TOGn1C/fQbRU9tEXFL3zCz+6s5j2\nmjnq+pV6ep20bGSkoUgNe/tRTPI+4yHTQkUUKGeuKgqsAza14C7gjwVqVD3+\n34oqlu5wBj7EsifWDZd5hYG1Jy+GMWj6bXV/XaLLGY0r6FliGGd3KEVfeni4\nwSsPpk+BpAs+h+LV3vL27cOJgEWjUqnYvi7YAqJvac/QCg0ihwqK60IlxBab\nwSPD\r\n=br+r\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCU284zcyUC6VPSXIBlo9Fh3qIXvn8UvqIakzZRh7svMAIhAPTPLPc/U9pWu6fdA5qn8q6VLjr+Sef30MQkaP783Csu"}]},"maintainers":[{"name":"acransac","email":"adrien.cransac@gmail.com"}],"_npmUser":{"name":"acransac","email":"adrien.cransac@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/terminal_1.0.0_1596389030061_0.3539110302046078"},"_hasShrinkwrap":false},"1.0.1":{"name":"@acransac/terminal","version":"1.0.1","description":"A framework to create a UI in a terminal","main":"src/index.js","scripts":{"test":"(cd test && node test_terminal.js)"},"author":{"name":"Adrien Cransac"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/acransac/terminal.git"},"bugs":{"url":"https://github.com/acransac/terminal/issues"},"homepage":"https://github.com/acransac/terminal","dependencies":{"neo-blessed":"^0.2.0","@acransac/streamer":"^1.0.0","@acransac/tester":"^1.0.0"},"gitHead":"038873bcd9ceac1ffbb6a2ce8de2f79183c853ad","_id":"@acransac/terminal@1.0.1","_nodeVersion":"12.12.0","_npmVersion":"6.11.3","dist":{"integrity":"sha512-nPqpSPeUTM0ZVlHsXEZV85Y6DGVefw+lVRvlACDDKjTm3iSpiPcnybDFcXFJ5sPlgMAPC5ZaM8tkNhE0qxuxWQ==","shasum":"8cb7884b15cea0141847bc29cf9dcb3b4255e683","tarball":"https://registry.npmjs.org/@acransac/terminal/-/terminal-1.0.1.tgz","fileCount":35,"unpackedSize":737159,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfKarpCRA9TVsSAnZWagAAInIQAJtSVQSh+lFs9EYw4lpX\nqhgXaEPp8Aic/GEZEXIPWCQVP2PWPDkVlXM18W/8NS0gjP416TNeyB+bjutc\nCkBW9GW5pmG5Tf22BveuCSS6gNoTbiP+RR29qurqN5vJpaigKAumnHV8QJlb\njSfYqOUXltuBXtv/w00J9ADahPZyPS2kxhDZ+CdevJSfPbLfFqsP0qlENhhz\nyUzOBJOiLFBct2i0nO3C3aZ6s5s0hsB8iZSXgU5USINyVvUMBrbGp0HUAMax\nzC4yElfM/i3lfbHUcuIAro/NjRZEfP1ekJ+ySKNQo5kwtL5cncdBlOrPeibA\n01eZxkVfPPgVtTYgy7+p5f3PQ6B5VmQC3JaooHi+Nl24ZQWkUolqndQQAnzX\nbrt0zTytwR3x/4Nobz/X/gVITwYzAO8Rbe6h/SqXLbFQszg8WDRM7goGMYmk\nU5Bj8YkJD2r7gj4wEI7z155CZCLr/fYo36J91PtZarYcRz6S1s65ZaVzicWe\nHQkkLfwboVdEr0g18lpA5l4clqxMXMYR0HfkbZdfx+o5SSLmwhhHqA+ILYlT\nKZRUYPspoSaxtJefzA0thGMwLz9ZfaTAh4QcjRo8FWog5N+Qsh64qcIVPv1W\nzRmOg2Da9uWMt27z+hL8aS17ly/P/MQkmRf1JWCzLAAka1hf8LKZnThMaykx\nvrZw\r\n=CEnF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFVtsLiZyXa0fJs2MAYU3YslI8dIumRIyJgd+X+6LLlXAiEA4dxgSQ2Kya9CXXPEG36c5xed3Flic+OgWt4WxG75Hh4="}]},"maintainers":[{"name":"acransac","email":"adrien.cransac@gmail.com"}],"_npmUser":{"name":"acransac","email":"adrien.cransac@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/terminal_1.0.1_1596566249384_0.4110389297466981"},"_hasShrinkwrap":false}},"time":{"created":"2020-08-02T17:23:50.031Z","1.0.0":"2020-08-02T17:23:50.391Z","modified":"2022-04-04T11:16:08.526Z","1.0.1":"2020-08-04T18:37:29.495Z"},"maintainers":[{"name":"acransac","email":"adrien.cransac@gmail.com"}],"description":"A framework to create a UI in a terminal","homepage":"https://github.com/acransac/terminal","repository":{"type":"git","url":"git+https://github.com/acransac/terminal.git"},"author":{"name":"Adrien Cransac"},"bugs":{"url":"https://github.com/acransac/terminal/issues"},"license":"MIT","readme":"# Introduction\n**terminal** provides a model to make terminal user interfaces. The user sees a _display_ which is either an _atom_ or a _list_ of displays. An atom is a possibly labelled bordered box with text. Using [streamer](https://github.com/acransac/streamer), a display can react to events generated by the user or the environment.\n\n**terminal** benefits from the high-level modelling of terminal graphics as a tree introduced by [blessed](https://github.com/chjj/blessed). It also borrows its tools and terminology from the Lisp family of programming languages.\n\n# How To Use Terminal\nAdd **terminal** to a project with:\n\n```shell\n    $ npm install @acransac/terminal\n```\n\nand import the needed functionalities:\n\n```javascript\n    const { atom, column, compose, cons, emptyList, indent, inline, label, renderer, row, show, sizeHeight, sizeWidth, TerminalTest, vindent } = require('@acransac/terminal');\n```\n\n## Make An Inert Atomic Display\nThe rendering engine is initialized with `renderer`. The latter returns functions to render a display and terminate the engine. An atomic display is created with `atom` and passed to the render function. Then, the engine is terminated. An atom's box can be labelled with `label`:\n* `renderer:: Maybe<Stream.Writable> -> (Display -> (), () -> ())`\n  | Parameter / Returned | Type                      | Description |\n  |----------------------|---------------------------|-------------|\n  | output               | Maybe\\<Stream.Writable>   | A writable Node.js stream to which the displayed characters and escape sequences are written. Default: `process.stdout` |\n  | _returned_           | (Display -> (), () -> ()) | An array of two functions. The first renders on the output the display passed as argument. The second tears down the rendering system |\n\n* `atom:: String -> Display`\n  | Parameter | Type   | Description                           |\n  |-----------|--------|---------------------------------------|\n  | content   | String | The text to print within the atom box |\n\n* `label:: (Atom<Display>, String) -> Atom<Display>`\n  | Parameter | Type           | Description       |\n  |-----------|----------------|-------------------|\n  | atom      | Atom\\<Display> | The atom to label |\n  | title     | String         | The label's text  |\n\nExamples:\n\n1.\n\n```javascript\n    const { atom, renderer } = require('@acransac/terminal');\n\n    const [render, terminate] = renderer();\n\n    render(atom(\"abc\"));\n\n    setTimeout(terminate, 2000);\n```\n\n```shell\n    $ node example.js\n```\n![Alt text](doc/screen_capture_1.png?raw=true)\n\n2.\n\n```javascript\n    const { atom, label, renderer } = require('@acransac/terminal');\n\n    const [render, terminate] = renderer();\n\n    render(label(atom(\"abc\"), \"example\"));\n\n    setTimeout(terminate, 2000);\n```\n\n```shell\n    $ node example.js\n```\n![Alt text](doc/screen_capture_2.png?raw=true)\n\n## Make An Inert List Display\nAs mentioned before, **terminal**'s tooling inherits the fundamental notions of the Lisp dialects. A display is a recursive structure expressed as nested lists of displays. It is built by `cons`'ing displays onto lists, ending such ramified chains with the `emptyList`. The latter can be dimensioned in height or in width with `row` or `column`:\n* `cons:: (Display, List<Display>) -> List<Display>`\n  | Parameter | Type           | Description                           |\n  |-----------|----------------|---------------------------------------|\n  | display   | Display        | The display, atom or list, to prepend |\n  | list      | List\\<Display> | The list of displays to prepend to    |\n\n* `emptyList:: () -> List<Display>`\n  | Returned  | Type           | Description                                                                                |\n  |-----------|----------------|--------------------------------------------------------------------------------------------|\n  | _returned_| List\\<Display> | A list display printing nothing. The starting point to which useful displays are prepended |\n\n* `row:: Number -> List<Display>`\n  | Parameter | Type   | Description |\n  |-----------|--------|-------------|\n  | height    | Number | The empty list's height proportionally to the underlying list's height if any. Otherwise, it is proportional to the screen's height. Expressed in percentage |\n\n* `column:: Number -> List<Display>`\n  | Parameter | Type   | Description |\n  |-----------|--------|-------------|\n  | width     | Number | The empty list's width proportionally to the underlying list's width if any. Otherwise, it is proportional to the screen's width. Expressed in percentage |\n\nExamples:\n\n1.\n\n```javascript\n    const { atom, cons, emptyList, renderer } = require('@acransac/terminal');\n\n    const [render, terminate] = renderer();\n\n    render(cons(atom(\"abc\"), emptyList()));\n\n    setTimeout(terminate, 2000);\n```\n\n```shell\n    $ node example.js\n```\n![Alt text](doc/screen_capture_1.png?raw=true)\n\nNote: The last example display looks the same as the first one, except that it is a list and not an atom.\n\n2.\n\n```javascript\n    const { atom, cons, renderer, row } = require('@acransac/terminal');\n\n    const [render, terminate] = renderer();\n\n    render(cons(atom(\"abc\"), row(50)));\n\n    setTimeout(terminate, 2000);\n```\n\n```shell\n    $ node example.js\n```\n![Alt text](doc/screen_capture_3.png?raw=true)\n\n3.\n\n```javascript\n    const { atom, column, cons, renderer } = require('@acransac/terminal');\n\n    const [render, terminate] = renderer();\n\n    render(cons(atom(\"abc\"), column(50)));\n\n    setTimeout(terminate, 2000);\n```\n\n```shell\n    $ node example.js\n```\n![Alt text](doc/screen_capture_4.png?raw=true)\n\n## Size And Position Displays\n`sizeWidth` and `sizeHeight` are used to size atoms. `indent` and `vindent` position displays. They allow to make list displays with several atoms or lists visible. Also, `inline` places a list of atoms in a row:\n* `sizeWidth:: (Number, Atom<Display>) -> Atom<Display>`\n  | Parameter | Type           | Description        |\n  |-----------|----------------|--------------------|\n  | size      | Number         | The atom's width proportionally to the underlying list's width if any. Otherwise, it is proportional to the screen's width. Expressed in percentage |\n  | atom      | Atom\\<Display> | The atom to resize |\n\n* `sizeHeight:: (Number, Atom<Display>) -> Atom<Display>`\n  | Parameter | Type           | Description        |\n  |-----------|----------------|--------------------|\n  | size      | Number         | The atom's height proportionally to the underlying list's height if any. Otherwise, it is proportional to the screen's height. Expressed in percentage |\n  | atom      | Atom\\<Display> | The atom to resize |\n\n* `indent:: (Number, Display) -> Display`\n  | Parameter | Type    | Description         |\n  |-----------|---------|---------------------|\n  | offset    | Number  | The display's offset from the underlying list's left edge if any. Otherwise, it is from the screen's left edge. Expressed in percentage of the underlier's width |\n  | display   | Display | The display to move |\n\n* `vindent:: (Number, Display) -> Display`\n  | Parameter | Type    | Description         |\n  |-----------|---------|---------------------|\n  | offset    | Number  | The display's offset from the underlying list's top edge if any. Otherwise, it is from the screen's top edge. Expressed in percentage of the underlier's height |\n  | display   | Display | The display to move |\n\n* `inline:: List<Atom<Display>> -> Display`\n  | Parameter | Type                  | Description |\n  |-----------|-----------------------|-------------|\n  | lat       | List\\<Atom\\<Display>> | A display that is a list of atoms with specified widths that are to be indented so that they show in a row |\n\nExamples:\n\n1.\n\n```javascript\n    const { atom, indent, renderer, sizeHeight, sizeWidth, vindent } = require('@acransac/terminal');\n\n    const [render, terminate] = renderer();\n\n    render(vindent(25, indent(25, sizeHeight(50, sizeWidth(50, atom(\"abc\"))))));\n\n    setTimeout(terminate, 2000);\n```\n\n```shell\n    $ node example.js\n```\n![Alt text](doc/screen_capture_5.png?raw=true)\n\n2.\n\n```javascript\n    const { atom, cons, emptyList, indent, renderer, vindent } = require('@acransac/terminal');\n\n    const [render, terminate] = renderer();\n\n    render(vindent(10, indent(10, cons(atom(\"abc\"), emptyList()))));\n\n    setTimeout(terminate, 2000);\n```\n\n```shell\n    $ node example.js\n```\n![Alt text](doc/screen_capture_6.png?raw=true)\n\n3.\n\n```javascript\n    const { atom, cons, emptyList, indent, renderer, sizeWidth } = require('@acransac/terminal');\n\n    const [render, terminate] = renderer();\n\n    render(cons(sizeWidth(50, atom(\"abc\")),\n                cons(indent(50, sizeWidth(50, atom(\"def\"))),\n                     emptyList())));\n\n    setTimeout(terminate, 2000);\n```\n\n```shell\n    $ node example.js\n```\n![Alt text](doc/screen_capture_7.png?raw=true)\n\n4.\n\n```javascript\n    const { atom, cons, emptyList, inline, renderer, sizeWidth } = require('@acransac/terminal');\n\n    const [render, terminate] = renderer();\n\n    render(inline(cons(sizeWidth(50, atom(\"abc\")),\n                       cons(sizeWidth(50, atom(\"def\")),\n                            emptyList()))));\n\n    setTimeout(terminate, 2000);\n```\n\n```shell\n    $ node example.js\n```\n![Alt text](doc/screen_capture_7.png?raw=true)\n\n## Make A Reactive Display\nA reactive display changes as the user interacts with the application or network messages are received, for example. **terminal** uses the [streamer](https://github.com/acransac/streamer) framework to listen to events and a reactive display is integrated into the process attached to the source. This integration is borne by `compose` which distributes the stream to a series of _components_ and injects their output into a _template_. `compose` is immediately chained with the function generated by passing the render function to `show` and that executes the rendering of the injected template:\n* `Component:: Any... -> Any -> Stream -> ComposerHandle`\n  | Parameter / Returned | Type                     | Description                                             |\n  |----------------------|--------------------------|---------------------------------------------------------|\n  | _parameters_         | Any...                   | The state of the component                              |\n  | _predecessor_        | Any                      | The output of the component on the last event processed |\n  | _returned_           | Stream -> ComposerHandle | The logic of the component which processes the stream and returns to `compose` a handle to the parameters and output of the form `f => f(updatedParameters)(output)`. Using this handle, `compose` provides the parameters and predecessor to the component on the next event, and injects the ouput into the template |\n\n* `Template:: Component... -> Display`\n  | Parameter   | Type         | Description                                                                        |\n  |-------------|--------------|------------------------------------------------------------------------------------|\n  | _components_| Component... | A series of components whose output values are passed as arguments to the template |\n\n* `compose:: (Template, Component...) -> Composer`\n  | Parameter          | Type         | Description                                          |\n  |--------------------|--------------|------------------------------------------------------|\n  | template           | Template     | The template organizing the display                  |\n  | reactiveComponents | Component... | A sequence of components parameterizing the template |\n\n* `show:: Display -> () -> Composer -> Process`\n  | Parameter | Type          | Description                                |\n  |-----------|---------------|--------------------------------------------|\n  | render    | Display -> () | The render function returned by `renderer` |\n\nExample:\n\n```javascript\n    const { continuation, forget, later, now, Source, StreamerTest, value } = require('@acransac/streamer');\n    const { atom, compose, renderer, show } = require('@acransac/terminal');\n\n    const [render, terminate] = renderer();\n\n    const loop = async (stream) => {\n      if (value(now(stream)) === \"end\") {\n        return terminate();\n      }\n      else {\n        return loop(await continuation(now(stream))(forget(await later(stream))));\n      }\n    };\n\n    const template = component => atom(component);\n\n    const component = noParameters => predecessor => stream => {\n      const processed = predecessor ? predecessor : \"\";\n\n      if (value(now(stream)) === \"end\") {\n        return f => f(noParameters)(processed);\n      }\n      else {\n        return f => f(noParameters)(`${processed}${value(now(stream))}`);\n      }\n    };\n\n    Source.from(StreamerTest.emitSequence([\"a\", \"b\", \"c\", \"end\"], 1000), \"onevent\")\n          .withDownstream(async (stream) => loop(await show(render)(compose(template, component))(stream)));\n```\n\n```shell\n    $ node example.js\n```\n![Alt text](doc/screen_capture_8.gif?raw=true)\n\n## Test The Display\n`TerminalTest.reviewDisplays` introduces testing a sequence of displays. A display test is created with `makeTestableInertDisplay` or `makeTestableReactiveDisplay`. When run, a script executing this review accepts one command line option: `look` shows the displays in turn so that they can be visually checked. `save` writes the characters and escape sequences of the displays to files so that they can be programmatically verified. `control` runs the actual test, comparing the generated displays to the control files and logging the successes and failures. It is the default option:\n* `TerminalTest.reviewDisplays:: ([TestableDisplay], Maybe<String>) -> ()`\n  | Parameter        | Type              | Description                                                          |\n  |------------------|-------------------|----------------------------------------------------------------------|\n  | testableDisplays | [TestableDisplay] | An array of testable displays, whether inert or reactive             |\n  | testSuiteName    | Maybe\\<String>    | The name of the test suite that appears in logs. Default: Test Suite |\n\n* `TerminalTest.makeTestableInertDisplay:: (() -> Display, String) -> TestableDisplay`\n  | Parameter | Type          | Description                               |\n  |-----------|---------------|-------------------------------------------|\n  | display   | () -> Display | A deferred inert display                  |\n  | testName  | String        | The name of the test that appears in logs |\n\n* `TerminalTest.makeTestableReactiveDisplay:: (ReactiveDisplayTest, String, Maybe<TestInitializer>) -> TestableDisplay`\n  | Parameter      | Type                    | Description                                                         |\n  |----------------|-------------------------|---------------------------------------------------------------------|\n  | produceDisplay | ReactiveDisplayTest     | The test function displaying what is to be checked. Its signature depends on the setup done by the initializer. By default, it should provide a placeholder for the render function as first argument, and then a placeholder for the continuation of the test suite. The test runner provides the actual functions when executing |\n  | testName       | String                  | The name of the test that appears in logs                           |\n  | init           | Maybe\\<TestInitializer> | Logic to execute before running the test. Default: calls `renderer` |\n\n* `TestInitializer:: (Stream.Writable, ReactiveDisplayTest, () -> ()) -> ()`\n  | Parameter     | Type                | Description                                                               |\n  |---------------|---------------------|---------------------------------------------------------------------------|\n  | displayTarget | Stream.Writable     | A reference to the target for writing the characters and escape sequences of the display. It could be a file or the standard output depending on the mode of the test runner |\n  | test          | ReactiveDisplayTest | A reference to the test function to call once the initialization is done  |\n  | finish        | () -> ()            | A reference to the continuation of the test to call once the test is done |\n\nExamples:\n\n1.\n\n```javascript\n    const { continuation, forget, later, now, Source, StreamerTest, value } = require('@acransac/streamer');\n    const { atom, compose, show, TerminalTest } = require('@acransac/terminal');\n\n    function reactiveDisplayTest(render, finish) {\n      const loop = async (stream) => {\n        if (value(now(stream)) === \"end\") {\n          return finish();\n        }\n        else {\n          return loop(await continuation(now(stream))(forget(await later(stream))));\n        }\n      };\n\n      const template = component => atom(component);\n\n      const component = noParameters => predecessor => stream => {\n        const processed = predecessor ? predecessor : \"\";\n\n        if (value(now(stream)) === \"end\") {\n          return f => f(noParameters)(processed);\n        }\n        else {\n          return f => f(noParameters)(`${processed}${value(now(stream))}`);\n        }\n      };\n\n      Source.from(StreamerTest.emitSequence([\"a\", \"b\", \"c\", \"end\"], 1000), \"onevent\")\n            .withDownstream(async (stream) => loop(await show(render)(compose(template, component))(stream)));\n    }\n\n    TerminalTest.reviewDisplays([\n      TerminalTest.makeTestableInertDisplay(() => atom(\"abc\"), \"Inert Display\"),\n      TerminalTest.makeTestableReactiveDisplay(reactiveDisplayTest, \"Reactive Display\")\n    ], \"Example Tests\");\n```\n\n```shell\n    $ node example.js look\n```\n![Alt text](doc/screen_capture_9.gif?raw=true)\n\n```shell\n    $ node example.js save\n    $ node example.js control\n    --------------------\n    Example Tests:\n\n        2 / 2 test(s) passed\n    --------------------\n```\n\n2.\n\n```javascript\n    const { continuation, forget, later, now, Source, StreamerTest, value } = require('@acransac/streamer');\n    const { atom, compose, renderer, show, TerminalTest } = require('@acransac/terminal');\n\n    function reactiveDisplayTest(render, finish) {\n      const loop = async (stream) => {\n        if (value(now(stream)) === \"end\") {\n          return finish();\n        }\n        else {\n          return loop(await continuation(now(stream))(forget(await later(stream))));\n        }\n      };\n\n      const template = component => atom(component);\n\n      const component = noParameters => predecessor => stream => {\n        const processed = predecessor ? predecessor : \"\";\n\n        if (value(now(stream)) === \"end\") {\n          return f => f(noParameters)(processed);\n        }\n        else {\n          return f => f(noParameters)(`${processed}${value(now(stream))}`);\n        }\n      };\n\n      return async (stream) => loop(await show(render)(compose(template, component))(stream));\n    }\n\n    function testInitializer(displayTarget, test, finish) {\n      const [render, terminate] = renderer(displayTarget);\n\n      const conclude = () => {\n        terminate();\n\n        return finish();\n      }\n\n      Source.from(StreamerTest.emitSequence([\"a\", \"b\", \"c\", \"end\"], 1000), \"onevent\")\n                              .withDownstream(async (stream) => test(render, conclude)(stream));\n    }\n\n    TerminalTest.reviewDisplays([\n      TerminalTest.makeTestableReactiveDisplay(reactiveDisplayTest,\n                                               \"Reactive Display With Initializer\",\n                                               testInitializer)\n    ], \"Example Tests\");\n```\n\n```shell\n    $ node example.js look\n```\n![Alt text](doc/screen_capture_10.gif?raw=true)\n\n```shell\n    $ node example.js save\n    $ node example.js control\n    --------------------\n    Example Tests:\n\n        1 / 1 test(s) passed\n    --------------------\n```\n","readmeFilename":"README.md"}