bare-tui 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,80 @@
1
+ # filepicker
2
+
3
+ Browse a filesystem and pick a file. Directory reads happen through commands
4
+ (async), and the filesystem dependency is **injected** — so the framework core
5
+ never depends on `bare-fs`, and your file UIs are trivially testable.
6
+
7
+ [← all components](../README.md#components)
8
+
9
+ ## Usage
10
+
11
+ ```js
12
+ const { filepicker } = require('bare-tui')
13
+
14
+ class App {
15
+ constructor() {
16
+ this.fp = filepicker.create({ height: 14 }) // lazily requires bare-fs
17
+ }
18
+ init() {
19
+ return this.fp.init()
20
+ }
21
+ update(msg) {
22
+ if (msg.type === 'filepicker.select') {
23
+ /* msg.path chosen */ return [this, null]
24
+ }
25
+ const [fp, cmd] = this.fp.update(msg)
26
+ this.fp = fp
27
+ return [this, cmd]
28
+ }
29
+ view() {
30
+ return this.fp.view()
31
+ }
32
+ }
33
+ ```
34
+
35
+ ## Dependency injection
36
+
37
+ The component never `require`s `bare-fs`/`bare-path` at module load. You either
38
+ pass them in, or `create()` lazily requires them — so importing the framework
39
+ pulls in nothing, and only constructing a real filepicker loads the filesystem.
40
+ The only surface used is `fs.readdir(dir, { withFileTypes: true }, cb)` and
41
+ `path.join` / `path.dirname`.
42
+
43
+ ## Options
44
+
45
+ | Option | Default | Description |
46
+ | ------------ | --------------------------- | ------------------------- |
47
+ | `fs` | lazy `require('bare-fs')` | Filesystem implementation |
48
+ | `path` | lazy `require('bare-path')` | Path implementation |
49
+ | `cwd` | `path.resolve('.')` | Starting directory |
50
+ | `height` | `12` | Visible rows |
51
+ | `showHidden` | `false` | Show dotfiles |
52
+
53
+ ## API
54
+
55
+ - `init()` → a command that reads the starting directory.
56
+ - `selectedPath()` — the chosen file path (once selected).
57
+
58
+ ## Messages
59
+
60
+ - `{ type: 'filepicker.entries', dir, entries }` — a directory was read.
61
+ - `{ type: 'filepicker.error', dir, error }` — a read failed.
62
+ - `{ type: 'filepicker.select', path }` — a file was chosen.
63
+
64
+ ## Keys
65
+
66
+ `↑`/`↓` (`k`/`j`) move, `enter`/`→` (`l`) open a directory or pick a file,
67
+ `backspace`/`←` (`h`) go up.
68
+
69
+ ## Testing
70
+
71
+ `filepicker.mock(tree)` returns an in-memory `{ fs, path, root }` from a plain
72
+ object — keys are entry names, an object value is a directory, anything else a
73
+ file:
74
+
75
+ ```js
76
+ const m = filepicker.mock({ docs: { 'a.md': null }, 'readme.txt': null })
77
+ const fp = filepicker.create({ fs: m.fs, path: m.path, cwd: m.root })
78
+ ```
79
+
80
+ No real disk access — ideal for tests.
package/docs/help.md ADDED
@@ -0,0 +1,45 @@
1
+ # help
2
+
3
+ Renders keybinding hints from `key.binding({ keys, help })` objects. It's a
4
+ _view helper_, not a loop model: `view(keymap)` is called from your own `view`.
5
+ Bindings without a `help` entry are skipped, so internal keys stay hidden.
6
+
7
+ [← all components](../README.md#components)
8
+
9
+ ## Usage
10
+
11
+ ```js
12
+ const { help, list } = require('bare-tui')
13
+
14
+ const h = help.create()
15
+
16
+ // short (one line) — pass any component's exported keymap:
17
+ h.view(list.keys)
18
+
19
+ // full (aligned columns) — toggle with '?', say:
20
+ h.showAll = true
21
+ h.view([[keys.up, keys.down], [keys.quit]])
22
+ ```
23
+
24
+ A keymap can be:
25
+
26
+ - an **array** of bindings (short) or array-of-arrays (full columns),
27
+ - an **object** of bindings (its values are used), or
28
+ - an object exposing **`shortHelp()`** / **`fullHelp()`**.
29
+
30
+ ## Options
31
+
32
+ | Option | Default | Description |
33
+ | ----------- | ------- | -------------------------------------------------- |
34
+ | `showAll` | `false` | Full multi-column help vs the one-line short form |
35
+ | `width` | `0` | Truncate the short line to this width (`0` = none) |
36
+ | `separator` | `' • '` | Between items in short mode |
37
+ | `styles` | — | `{ key, desc, sep }` functions to restyle parts |
38
+
39
+ ## API
40
+
41
+ - `view(keymap)` → the rendered hint string.
42
+ - `setWidth(n)`.
43
+
44
+ Every component that owns keys exports a `keys` keymap (`list.keys`,
45
+ `table.keys`, `viewport.keys`, `paginator.keys`) ready to pass straight in.
package/docs/list.md ADDED
@@ -0,0 +1,47 @@
1
+ # list
2
+
3
+ A selectable, filterable list. Owns selection and a scroll window that follows
4
+ the cursor; filtering is delegated to an embedded [textinput](textinput.md).
5
+
6
+ [← all components](../README.md#components)
7
+
8
+ ## Usage
9
+
10
+ ```js
11
+ const { list } = require('bare-tui')
12
+
13
+ const l = list.create({
14
+ items: ['apple', 'banana', 'cherry'],
15
+ height: 8,
16
+ title: ' fruit'
17
+ })
18
+
19
+ // in update: const [m, cmd] = l.update(msg); this.list = m
20
+ // pick: l.selectedItem()
21
+ ```
22
+
23
+ Items can be strings or objects: `title` is shown, `filterValue` (falling back
24
+ to `title`) is matched.
25
+
26
+ ## Options
27
+
28
+ | Option | Default | Description |
29
+ | ------------ | ------- | ------------------------------------------- |
30
+ | `items` | `[]` | Strings or `{ title, filterValue }` objects |
31
+ | `height` | `10` | Visible rows |
32
+ | `width` | `0` | Pad rows to this width (`0` = ragged) |
33
+ | `title` | `''` | Optional heading line |
34
+ | `filterable` | `true` | Allow `/` filtering |
35
+
36
+ ## API
37
+
38
+ - `selectedItem()` — the underlying item under the cursor (or `null`).
39
+ - `setItems(items)`.
40
+ - `.filtering` — whether the filter input is active. `.visibleCount`,
41
+ `.selected`, `.filter`.
42
+
43
+ ## Keys
44
+
45
+ `↑`/`↓` (`k`/`j`) move, `pgup`/`pgdn` page, `/` enters filter mode (type to
46
+ narrow, `enter` keeps it, `esc` clears it). Bindings are exported as `list.keys`
47
+ for the [help](help.md) component.
@@ -0,0 +1,43 @@
1
+ # paginator
2
+
3
+ Page state plus an indicator, for paging through a long collection. Holds the
4
+ current page and page size, handles the paging keys, and offers `sliceBounds()`
5
+ so you can carve the visible page out of your items.
6
+
7
+ [← all components](../README.md#components)
8
+
9
+ ## Usage
10
+
11
+ ```js
12
+ const { paginator } = require('bare-tui')
13
+
14
+ const p = paginator.create({ perPage: 10, total: items.length, type: 'dots' })
15
+
16
+ // in update: const [m] = p.update(msg); this.pager = m
17
+ // in view:
18
+ const [start, end] = p.sliceBounds()
19
+ render(items.slice(start, end))
20
+ p.view() // "●○○○○" (dots) or "1/5" (arabic)
21
+ ```
22
+
23
+ ## Options
24
+
25
+ | Option | Default | Description |
26
+ | --------------------------- | ------------- | -------------------------------------- |
27
+ | `perPage` | `10` | Items per page |
28
+ | `total` | `0` | Total item count |
29
+ | `page` | `0` | Initial page (zero-indexed) |
30
+ | `type` | `'arabic'` | `'arabic'` (`1/5`) or `'dots'` (`●○○`) |
31
+ | `activeDot` / `inactiveDot` | `'●'` / `'○'` | Dot characters |
32
+
33
+ ## API
34
+
35
+ - `sliceBounds(length?)` → `[start, end)` for the current page.
36
+ - `itemsOnPage(length?)` — count on the current page.
37
+ - `nextPage()` / `prevPage()` / `setPage(n)` / `setTotal(n)`.
38
+ - `.page` / `.totalPages` / `onFirstPage()` / `onLastPage()`.
39
+
40
+ ## Keys
41
+
42
+ `←`/`→` (`h`/`l`, `pgup`/`pgdn`) change pages. Bindings are exported as
43
+ `paginator.keys` for the [help](help.md) component.
@@ -0,0 +1,36 @@
1
+ # progress
2
+
3
+ A percentage bar. Static like [help](help.md): it holds the look and
4
+ `view(percent)` renders a bar at the given fraction. Drive the percent from your
5
+ model (a tick, a download callback, the OTA updater).
6
+
7
+ [← all components](../README.md#components)
8
+
9
+ ## Usage
10
+
11
+ ```js
12
+ const { progress } = require('bare-tui')
13
+
14
+ const bar = progress.create({ width: 40, gradient: ['#5A56E0', '#EE6FF8'] })
15
+
16
+ // in view:
17
+ bar.view(0.42) // "████████████░░░░… 42%"
18
+ ```
19
+
20
+ ## Options
21
+
22
+ | Option | Default | Description |
23
+ | ---------------- | ------- | ----------------------------------------------- |
24
+ | `width` | `40` | Total width including the percentage label |
25
+ | `full` | `'█'` | Filled cell character |
26
+ | `empty` | `'░'` | Empty cell character |
27
+ | `showPercentage` | `true` | Append a ` NNN%` label (reserves 5 cells) |
28
+ | `color` | `null` | Solid fill color (name, 0–255, or `#hex`) |
29
+ | `gradient` | `null` | `[fromHex, toHex]` interpolated across the fill |
30
+
31
+ ## API
32
+
33
+ - `view(percent)` → bar string. `percent` is `0..1` and is clamped.
34
+ - `setWidth(n)` — e.g. on a resize message.
35
+
36
+ The label slot is a fixed width, so the bar doesn't jump as the number changes.
@@ -0,0 +1,54 @@
1
+ # spinner
2
+
3
+ An animated loading indicator. Command-driven: it animates by re-issuing a tick
4
+ command each frame.
5
+
6
+ [← all components](../README.md#components)
7
+
8
+ ## Usage
9
+
10
+ ```js
11
+ const { spinner } = require('bare-tui')
12
+
13
+ class App {
14
+ constructor() {
15
+ this.spinner = spinner.create({ fps: 12 })
16
+ }
17
+ init() {
18
+ return this.spinner.init() // start animating
19
+ }
20
+ update(msg) {
21
+ if (msg.type === 'spinner.tick') {
22
+ const [s, cmd] = this.spinner.update(msg)
23
+ this.spinner = s
24
+ return [this, cmd]
25
+ }
26
+ return [this, null]
27
+ }
28
+ view() {
29
+ return `${this.spinner.view()} loading…`
30
+ }
31
+ }
32
+ ```
33
+
34
+ ## Options
35
+
36
+ | Option | Default | Description |
37
+ | -------- | -------------- | ------------------------------- |
38
+ | `frames` | `spinner.dots` | Array of frame strings to cycle |
39
+ | `fps` | `10` | Frames per second |
40
+
41
+ Frame presets: `spinner.dots`, `spinner.line`, `spinner.points`.
42
+
43
+ ## API
44
+
45
+ - `init()` → a command that starts the animation. Call from your `init`.
46
+ - `update(msg)` → advances on its own `spinner.tick`, returns the next tick
47
+ command.
48
+ - `view()` → the current frame string.
49
+
50
+ ## Messages
51
+
52
+ Emits and consumes `{ type: 'spinner.tick', id, tag }`. The `id` + `tag` ensure a
53
+ second spinner's ticks (or a duplicate) can't double-drive this one — route every
54
+ `spinner.tick` to `update` and it sorts itself out.
@@ -0,0 +1,52 @@
1
+ # stopwatch
2
+
3
+ Counts elapsed time upward. Command-driven like the [spinner](spinner.md):
4
+ `start()` returns a tick command, and each accepted tick advances `elapsed` and
5
+ re-issues the next.
6
+
7
+ [← all components](../README.md#components)
8
+
9
+ ## Usage
10
+
11
+ ```js
12
+ const { stopwatch } = require('bare-tui')
13
+
14
+ class App {
15
+ constructor() {
16
+ this.sw = stopwatch.create()
17
+ }
18
+ init() {
19
+ return this.sw.start()
20
+ }
21
+ update(msg) {
22
+ if (msg.type === 'stopwatch.tick') {
23
+ const [sw, cmd] = this.sw.update(msg)
24
+ this.sw = sw
25
+ return [this, cmd]
26
+ }
27
+ return [this, null]
28
+ }
29
+ view() {
30
+ return this.sw.view() // "01:23"
31
+ }
32
+ }
33
+ ```
34
+
35
+ ## Options
36
+
37
+ | Option | Default | Description |
38
+ | ---------- | ------- | ---------------------------- |
39
+ | `interval` | `1000` | Milliseconds per tick |
40
+ | `elapsed` | `0` | Initial elapsed milliseconds |
41
+
42
+ ## API
43
+
44
+ - `start()` / `stop()` / `toggle()` — return a command (or `null`); thread it up.
45
+ - `reset()` — zero `elapsed`; a running stopwatch keeps ticking.
46
+ - `.elapsed` (ms) / `.running`.
47
+ - `view()` → `MM:SS` (or `H:MM:SS`).
48
+
49
+ ## Messages
50
+
51
+ Emits and consumes `{ type: 'stopwatch.tick', id, tag }`; id + tag guard against
52
+ strays, so pause/resume can't double-drive it.
package/docs/table.md ADDED
@@ -0,0 +1,52 @@
1
+ # table
2
+
3
+ Fixed-width columns with selectable, scrolling rows. Cells are truncated/padded
4
+ to their column width (ANSI-aware), the selection is a reverse-video bar, and the
5
+ body scrolls in a fixed-height window.
6
+
7
+ [← all components](../README.md#components)
8
+
9
+ ## Usage
10
+
11
+ ```js
12
+ const { table } = require('bare-tui')
13
+
14
+ const t = table.create({
15
+ columns: [
16
+ { title: 'Package', width: 18 },
17
+ { title: 'Lang', width: 5 }
18
+ ],
19
+ rows: [
20
+ ['corestore', 'js'],
21
+ ['hypercore', 'js']
22
+ ],
23
+ height: 8
24
+ })
25
+
26
+ // in update: const [m] = t.update(msg); this.table = m
27
+ // selected: t.selectedRow()
28
+ ```
29
+
30
+ ## Options
31
+
32
+ | Option | Default | Description |
33
+ | --------- | ------- | ------------------------------------- |
34
+ | `columns` | `[]` | `{ title, width }` per column |
35
+ | `rows` | `[]` | Arrays of cell values, one per column |
36
+ | `height` | `10` | Visible body rows |
37
+ | `rule` | `'─'` | Character for the header underline |
38
+
39
+ ## API
40
+
41
+ - `selectedRow()` — the row array under the cursor (or `null`).
42
+ - `setRows(rows)` / `setColumns(columns)`.
43
+ - `gotoTop()` / `gotoBottom()`.
44
+ - `.cursor` / `.offset` / `.totalWidth`.
45
+
46
+ Renders a stable `2 + height`-row block (header, rule, body), so it sits cleanly
47
+ inside a `style` box.
48
+
49
+ ## Keys
50
+
51
+ `↑`/`↓` (`k`/`j`) move, `pgup`/`pgdn` page, `home`/`end` jump. Bindings are
52
+ exported as `table.keys` for the [help](help.md) component.
@@ -0,0 +1,43 @@
1
+ # textarea
2
+
3
+ A multi-line text editor. Soft-wraps to its width, scrolls a fixed-height
4
+ window, and moves the cursor by _visual_ row (so up/down feel right inside
5
+ wrapped text). Input-driven and focus-gated, like [textinput](textinput.md).
6
+
7
+ [← all components](../README.md#components)
8
+
9
+ ## Usage
10
+
11
+ ```js
12
+ const { textarea } = require('bare-tui')
13
+
14
+ const ta = textarea.create({ width: 60, height: 10, placeholder: 'Type…' }).focus()
15
+
16
+ // in update: const [t, cmd] = ta.update(msg); this.ta = t
17
+ // in view: this.ta.view()
18
+ ```
19
+
20
+ ## Options
21
+
22
+ | Option | Default | Description |
23
+ | ------------- | ------- | ----------------------------------------------- |
24
+ | `value` | `''` | Initial contents |
25
+ | `width` | `40` | Wrap width / render width |
26
+ | `height` | `6` | Visible rows |
27
+ | `placeholder` | `''` | Dim text shown when empty |
28
+ | `charLimit` | `0` | Max characters incl. newlines (`0` = unlimited) |
29
+ | `focused` | `false` | Start focused |
30
+
31
+ ## API
32
+
33
+ - `focus()` / `blur()`.
34
+ - `setValue(v)` / `reset()`.
35
+ - `setSize(width, height)` — e.g. on a resize message.
36
+ - `.value` — the text (lines joined by `\n`). `.length` — character count.
37
+
38
+ ## Keys
39
+
40
+ `←`/`→` move by character (wrapping across lines), `↑`/`↓` by visual row,
41
+ `home`/`end` to the visual row's edges, `enter` splits the line, `backspace`/
42
+ `delete` edit and merge lines, printable characters insert. Renders a
43
+ rectangular `width × height` block, so it drops straight into a `style` box.
@@ -0,0 +1,51 @@
1
+ # textinput
2
+
3
+ A single-line editable text field. Input-driven: no commands, just state folded
4
+ from key messages. Only consumes keys while focused, so a parent can host several
5
+ and route to whichever has focus.
6
+
7
+ [← all components](../README.md#components)
8
+
9
+ ## Usage
10
+
11
+ ```js
12
+ const { textinput } = require('bare-tui')
13
+
14
+ class Form {
15
+ constructor() {
16
+ this.name = textinput.create({ placeholder: 'name', prompt: '> ' }).focus()
17
+ }
18
+ update(msg) {
19
+ const [f, cmd] = this.name.update(msg)
20
+ this.name = f
21
+ return [this, cmd]
22
+ }
23
+ view() {
24
+ return this.name.view()
25
+ }
26
+ }
27
+ ```
28
+
29
+ ## Options
30
+
31
+ | Option | Default | Description |
32
+ | ------------- | ---------- | ------------------------------------------- |
33
+ | `value` | `''` | Initial text |
34
+ | `placeholder` | `''` | Dim text shown when empty |
35
+ | `prompt` | `''` | Prefix drawn before the value (e.g. `'> '`) |
36
+ | `charLimit` | `0` | Max length (`0` = unlimited) |
37
+ | `echoMode` | `'normal'` | `'password'` masks the value |
38
+ | `maskChar` | `'•'` | Mask character in password mode |
39
+ | `focused` | `false` | Start focused |
40
+
41
+ ## API
42
+
43
+ - `focus()` / `blur()` — toggle whether keys are consumed.
44
+ - `setValue(v)` / `reset()` — set or clear the text.
45
+ - `.value` — the current string.
46
+
47
+ ## Keys
48
+
49
+ `←`/`→` move, `home`/`end` (and `ctrl+a`/`ctrl+e`) jump, `backspace`/`delete`
50
+ edit, printable characters insert at the cursor. The field draws its own
51
+ reverse-video cursor (the Program hides the real one).
package/docs/timer.md ADDED
@@ -0,0 +1,57 @@
1
+ # timer
2
+
3
+ Counts a duration down to zero. Command-driven like the
4
+ [stopwatch](stopwatch.md); when it reaches zero it stops and emits a one-shot
5
+ timeout message.
6
+
7
+ [← all components](../README.md#components)
8
+
9
+ ## Usage
10
+
11
+ ```js
12
+ const { timer } = require('bare-tui')
13
+
14
+ class App {
15
+ constructor() {
16
+ this.timer = timer.create({ timeout: 10000 }) // 10s
17
+ }
18
+ init() {
19
+ return this.timer.start()
20
+ }
21
+ update(msg) {
22
+ if (msg.type === 'timer.timeout') {
23
+ /* done */ return [this, null]
24
+ }
25
+ if (msg.type === 'timer.tick') {
26
+ const [t, cmd] = this.timer.update(msg)
27
+ this.timer = t
28
+ return [this, cmd]
29
+ }
30
+ return [this, null]
31
+ }
32
+ view() {
33
+ return this.timer.view() // "00:09"
34
+ }
35
+ }
36
+ ```
37
+
38
+ ## Options
39
+
40
+ | Option | Default | Description |
41
+ | ---------- | ------- | -------------------------------- |
42
+ | `timeout` | `0` | Initial duration in milliseconds |
43
+ | `interval` | `1000` | Milliseconds per tick |
44
+
45
+ ## API
46
+
47
+ - `start()` / `stop()` / `toggle()` — return a command (or `null`). Won't start
48
+ from zero.
49
+ - `reset()` — restore the initial duration.
50
+ - `.timeout` (ms remaining) / `.running` / `.timedOut`.
51
+ - `view()` → `MM:SS`.
52
+
53
+ ## Messages
54
+
55
+ Consumes `{ type: 'timer.tick', id, tag }`; on reaching zero its `update` returns
56
+ a command that emits `{ type: 'timer.timeout', id }` — handle that to react to
57
+ the timeout.
@@ -0,0 +1,39 @@
1
+ # viewport
2
+
3
+ A scrollable window over content taller than the available space. Renders a
4
+ fixed `height`-row window and always emits exactly that many lines (padding short
5
+ content), so surrounding layout stays stable.
6
+
7
+ [← all components](../README.md#components)
8
+
9
+ ## Usage
10
+
11
+ ```js
12
+ const { viewport } = require('bare-tui')
13
+
14
+ const vp = viewport.create({ width: 40, height: 10 })
15
+ vp.setContent(longText)
16
+
17
+ // in update: const [v] = vp.update(msg); this.vp = v
18
+ // in view: this.vp.view()
19
+ ```
20
+
21
+ ## Options
22
+
23
+ | Option | Default | Description |
24
+ | -------- | ------- | -------------------------------------------------- |
25
+ | `width` | `0` | Truncate lines to this width (`0` = no truncation) |
26
+ | `height` | `0` | Visible rows |
27
+
28
+ ## API
29
+
30
+ - `setContent(string)` — set the scrollable text.
31
+ - `scrollUp(n)` / `scrollDown(n)` / `gotoTop()` / `gotoBottom()` /
32
+ `setYOffset(n)`.
33
+ - `.atTop` / `.atBottom` / `.scrollPercent` / `.maxOffset` / `.yOffset`.
34
+
35
+ ## Keys
36
+
37
+ `↑`/`↓` (`k`/`j`) by line, `pgup`/`pgdn` (`b`/`f`) by page, `ctrl+u`/`ctrl+d`
38
+ half-page, `home`/`end` to the ends. Bindings are exported as `viewport.keys`
39
+ for the [help](help.md) component.
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ declare module '@holepunchto/bare-tui'
package/index.js ADDED
@@ -0,0 +1,63 @@
1
+ // tea — a tiny Elm Architecture runtime for Bare terminals.
2
+ //
3
+ // Built on Bare's native primitives (bare-tty for raw IO + window size,
4
+ // bare-ansi-escapes for key decoding and escape sequences), so it runs
5
+ // anywhere Bare runs and pulls in no Node-only dependencies. The API is shaped
6
+ // after Charm's Bubble Tea (Model/Cmd/Msg/Program) with the intent of growing
7
+ // into a drop-out framework.
8
+ //
9
+ // const { Program, quit } = require('./lib/tea')
10
+ //
11
+ // class App {
12
+ // init() { return null }
13
+ // update(msg) {
14
+ // if (msg.type === 'key' && String(msg) === 'q') return [this, quit]
15
+ // return [this, null]
16
+ // }
17
+ // view() { return 'press q to quit' }
18
+ // }
19
+ //
20
+ // new Program(new App()).run()
21
+ const Program = require('./program')
22
+ const commands = require('./commands')
23
+ const messages = require('./messages')
24
+ const key = require('./key')
25
+ const ansi = require('./ansi')
26
+ const { style } = require('./style')
27
+ const spinner = require('./components/spinner')
28
+ const textinput = require('./components/textinput')
29
+ const autocomplete = require('./components/autocomplete')
30
+ const textarea = require('./components/textarea')
31
+ const viewport = require('./components/viewport')
32
+ const list = require('./components/list')
33
+ const table = require('./components/table')
34
+ const help = require('./components/help')
35
+ const progress = require('./components/progress')
36
+ const paginator = require('./components/paginator')
37
+ const stopwatch = require('./components/stopwatch')
38
+ const timer = require('./components/timer')
39
+ const filepicker = require('./components/filepicker')
40
+
41
+ module.exports = {
42
+ Program,
43
+ ...commands, // quit, batch, sequence, tick, every
44
+ KeyMsg: messages.KeyMsg,
45
+ key, // key.matches(msg, ...chords | bindings), key.binding({ keys, help })
46
+ ansi,
47
+ style, // style().bold().border(style.borders.rounded).render(...) + style.joinHorizontal/Vertical
48
+
49
+ // Components — each a composable { init?, update, view } model.
50
+ spinner, // spinner.create({ frames, fps })
51
+ textinput, // textinput.create({ placeholder, prompt, charLimit, echoMode })
52
+ autocomplete, // autocomplete.create({ prompt, placeholder, suggestions, trigger }) — input + suggestion menu
53
+ textarea, // textarea.create({ width, height, placeholder, charLimit }) — multi-line
54
+ viewport, // viewport.create({ width, height }) — scrollable window
55
+ list, // list.create({ items, height, width, title }) — selectable + filterable
56
+ table, // table.create({ columns, rows, height }) — selectable scrolling rows
57
+ help, // help.create() — renders keybinding hints; view(keymap)
58
+ progress, // progress.create({ width, gradient }) — view(percent)
59
+ paginator, // paginator.create({ perPage, total, type }) — page state + indicator
60
+ stopwatch, // stopwatch.create({ interval }) — counts up; start/stop/toggle
61
+ timer, // timer.create({ timeout, interval }) — counts down; emits timer.timeout
62
+ filepicker // filepicker.create({ fs, path, cwd }) — browse + pick; filepicker.mock(tree)
63
+ }