fatty 0.99.0 → 0.99.3

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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.org +11 -29
  3. data/README.md +2207 -18
  4. data/README.org +2066 -104
  5. data/exe/fatty +236 -93
  6. data/help/help.md +233 -0
  7. data/lib/fatty/action.rb +3 -7
  8. data/lib/fatty/actionable.rb +2 -0
  9. data/lib/fatty/alert.rb +65 -39
  10. data/lib/fatty/ansi/renderer.rb +26 -6
  11. data/lib/fatty/ansi.rb +1 -0
  12. data/lib/fatty/api/alert.rb +15 -0
  13. data/lib/fatty/api/choose.rb +145 -0
  14. data/lib/fatty/api/environment.rb +12 -0
  15. data/lib/fatty/api/keytest.rb +11 -0
  16. data/lib/fatty/api/menu.rb +104 -0
  17. data/lib/fatty/api/output.rb +55 -0
  18. data/lib/fatty/api/progress.rb +24 -0
  19. data/lib/fatty/api/prompt.rb +77 -0
  20. data/lib/fatty/api/status.rb +55 -0
  21. data/lib/fatty/api.rb +38 -0
  22. data/lib/fatty/callback_environment.rb +74 -0
  23. data/lib/fatty/colors/color.rb +366 -232
  24. data/lib/fatty/colors/pairs.rb +2 -0
  25. data/lib/fatty/command.rb +78 -0
  26. data/lib/fatty/config.rb +227 -39
  27. data/lib/fatty/config_files/config.yml +4 -12
  28. data/lib/fatty/config_files/keydefs.yml +13 -23
  29. data/lib/fatty/config_files/themes/capuchin_monk.yml +140 -0
  30. data/lib/fatty/config_files/themes/catppuccin_latte.yml +140 -0
  31. data/lib/fatty/config_files/themes/catppuccin_mocha.yml +140 -0
  32. data/lib/fatty/config_files/themes/cyberpunk.yml +141 -0
  33. data/lib/fatty/config_files/themes/dracula.yml +140 -0
  34. data/lib/fatty/config_files/themes/everforest_dark.yml +140 -0
  35. data/lib/fatty/config_files/themes/gruvbox_dark.yml +140 -0
  36. data/lib/fatty/config_files/themes/gruvbox_light.yml +140 -0
  37. data/lib/fatty/config_files/themes/mono.yml +119 -47
  38. data/lib/fatty/config_files/themes/monokai.yml +140 -0
  39. data/lib/fatty/config_files/themes/nordic.yml +1 -0
  40. data/lib/fatty/config_files/themes/onedark.yml +140 -0
  41. data/lib/fatty/config_files/themes/solarized_dark.yml +1 -0
  42. data/lib/fatty/config_files/themes/solarized_light.yml +140 -0
  43. data/lib/fatty/config_files/themes/terminal.yml +67 -0
  44. data/lib/fatty/config_files/themes/tokyo_night.yml +140 -0
  45. data/lib/fatty/config_files/themes/wordperfect.yml +80 -9
  46. data/lib/fatty/core_ext/hash.rb +19 -0
  47. data/lib/fatty/core_ext/string.rb +19 -12
  48. data/lib/fatty/core_ext.rb +1 -0
  49. data/lib/fatty/curses/context.rb +86 -84
  50. data/lib/fatty/curses/event_source.rb +128 -40
  51. data/lib/fatty/curses/key_decoder.rb +77 -11
  52. data/lib/fatty/env.rb +21 -20
  53. data/lib/fatty/help.rb +1 -29
  54. data/lib/fatty/history/entry.rb +2 -0
  55. data/lib/fatty/history.rb +17 -14
  56. data/lib/fatty/input_buffer.rb +71 -47
  57. data/lib/fatty/input_field.rb +216 -149
  58. data/lib/fatty/key_event.rb +67 -66
  59. data/lib/fatty/key_map.rb +54 -16
  60. data/lib/fatty/keymaps/emacs.rb +40 -6
  61. data/lib/fatty/log_formats/json.rb +2 -0
  62. data/lib/fatty/log_formats/text.rb +2 -0
  63. data/lib/fatty/logger.rb +7 -2
  64. data/lib/fatty/markdown/ansi_renderer.rb +75 -6
  65. data/lib/fatty/markdown/render.rb +2 -3
  66. data/lib/fatty/mouse_event.rb +142 -2
  67. data/lib/fatty/output_buffer.rb +2 -0
  68. data/lib/fatty/pager.rb +158 -47
  69. data/lib/fatty/progress.rb +417 -0
  70. data/lib/fatty/renderer/curses.rb +340 -184
  71. data/lib/fatty/renderer/truecolor.rb +266 -163
  72. data/lib/fatty/renderer.rb +115 -97
  73. data/lib/fatty/screen.rb +2 -0
  74. data/lib/fatty/session/alert_session.rb +39 -21
  75. data/lib/fatty/session/isearch_session.rb +92 -38
  76. data/lib/fatty/session/keytest_session.rb +105 -85
  77. data/lib/fatty/session/modal_session.rb +38 -1
  78. data/lib/fatty/session/output_session.rb +472 -21
  79. data/lib/fatty/session/popup_session.rb +251 -134
  80. data/lib/fatty/session/prompt_session.rb +68 -26
  81. data/lib/fatty/session/search_session.rb +79 -31
  82. data/lib/fatty/session/shell_session.rb +383 -368
  83. data/lib/fatty/session/status_session.rb +167 -0
  84. data/lib/fatty/session.rb +95 -95
  85. data/lib/fatty/terminal/popup_owner.rb +14 -11
  86. data/lib/fatty/terminal.rb +515 -813
  87. data/lib/fatty/themes/loader.rb +4 -2
  88. data/lib/fatty/themes/manager.rb +4 -0
  89. data/lib/fatty/themes/registry.rb +2 -0
  90. data/lib/fatty/themes/resolver.rb +175 -162
  91. data/lib/fatty/version.rb +1 -1
  92. data/lib/fatty/viewport.rb +8 -1
  93. data/lib/fatty.rb +35 -7
  94. metadata +42 -40
  95. data/.envrc +0 -2
  96. data/.simplecov +0 -23
  97. data/.yardopts +0 -4
  98. data/CHANGELOG.md +0 -34
  99. data/Rakefile +0 -15
  100. data/TODO.org +0 -163
  101. data/examples/markdown/native-markdown.md +0 -370
  102. data/examples/markdown/ox-gfm-markdown.md +0 -373
  103. data/examples/markdown/ox-gfm-markdown.org +0 -376
  104. data/fatty.gemspec +0 -42
  105. data/lib/fatty/accept_env.rb +0 -32
  106. data/lib/fatty/config_files/help.md +0 -120
  107. data/lib/fatty/config_files/help.org +0 -124
  108. data/lib/fatty/config_files/themes/wordperfect_light.yml +0 -77
  109. data/lib/fatty/menu_env.rb +0 -22
  110. data/lib/fatty/session/input_session.rb +0 -99
  111. data/lib/fatty/sessions.rb +0 -14
  112. data/lib/fatty/terminal/progress.rb +0 -374
  113. data/lib/fatty/view/alert_view.rb +0 -14
  114. data/lib/fatty/view/cursor_view.rb +0 -18
  115. data/lib/fatty/view/input_view.rb +0 -9
  116. data/lib/fatty/view/output_view.rb +0 -9
  117. data/lib/fatty/view/status_view.rb +0 -14
  118. data/lib/fatty/view.rb +0 -33
  119. data/lib/fatty/views.rb +0 -9
  120. data/sig/fatty.rbs +0 -4
data/README.md CHANGED
@@ -1,31 +1,2220 @@
1
- - [Introduction](#orga462d10)
2
- - [Reading Keys](#org64156e4)
3
- - [Alerts](#org38ad970)
1
+ - [Introduction](#org92a02ab)
2
+ - [Quick Start](#orga9758e6)
3
+ - [Installing](#org5d48af2)
4
+ - [Trying it Out with the \`fatty\` Demo](#orga0811d0)
5
+ - [Builtin commands](#org10b740e)
6
+ - [Screenshots](#orga21015c)
7
+ - [Command line editing](#orge811242)
8
+ - [Searchable Output](#org86db3fd)
9
+ - [Paging Markdown](#org465e882)
10
+ - [Popup Selection](#orged601b1)
11
+ - [Usage](#org0fbc008)
12
+ - [Launching a Fatty Terminal](#orgdd7a899)
13
+ - [`on_accept`](#orge36ae8e)
14
+ - [Parameters to `on_accept`](#org74e77b2)
15
+ - [The `line` parameter to `on_accept`](#org21b8961)
16
+ - [The callback parameter to `on_accept`](#org3091db0)
17
+ - [Output ordering](#orgcd415d8)
18
+ - [Long-running callbacks and interruption](#org0d9a504)
19
+ - [ANSI colors in output](#org89ae995)
20
+ - [Other parameters to `Terminal.new`](#orgce83cdf)
21
+ - [The Callback API](#orgef3914d)
22
+ - [`append(text, follow: true)`](#org1f2fcbb)
23
+ - [`append_now(text, follow: true, mode: nil)`](#org3318ee2)
24
+ - [`markdown(text)`](#org3da5738)
25
+ - [`status(text, role: :info)`](#org5e0ab5d)
26
+ - [`good(text, replace: false, render: true)`](#orgd2a8b1b)
27
+ - [`info(text, replace: false, render: true)`](#org8ec7886)
28
+ - [`warn(text, replace: false, render: true)`](#org6bb2e02)
29
+ - [`error(text, replace: false, render: true)`](#org6040c43)
30
+ - [`oops(text, replace: false, render: true)`](#org3e60ade)
31
+ - [`alert(text, role: :info)`](#org5e74284)
32
+ - [`prompt(prompt, initial: "", cancel_value: nil, history_key: nil, save_history: true)`](#org71a56a2)
33
+ - [`check_interrupt!`](#orgd0da92b)
34
+ - [`add_progress(label:, total: nil, style: :percent, role: :info, width: 40)`](#orgfbc8aa5)
35
+ - [Initialization `add_progress(label:, style: :percent, total: nil, role: :info, width: 40)`](#orgd758f7b)
36
+ - [Update `update(current: nil, indicator: nil, render: false)`](#orgbe50f7a)
37
+ - [Finish](#org058be1b)
38
+ - [Clear](#org22e4b8d)
39
+ - [`choose(prompt, choices:, initial_choice_idx: 0, cancel_value: nil)`](#orgcfb56dd)
40
+ - [`choose_multi(prompt, choices:, cancel_value: nil)`](#org5b1d7d1)
41
+ - [`confirm(prompt, yes_label: "Yes", no_label: "No", cancel_value: false)`](#org9b37a8e)
42
+ - [`menu(prompt, choices:, initial_choice_idx: 0, cancel_value: nil)`](#org8d63dae)
43
+ - [`environment`](#org1750b9e)
44
+ - [Default Interaction](#org727ceaf)
45
+ - [Parts of the Screen](#orgd560bdc)
46
+ - [Input Field](#org6ae09c7)
47
+ - [Output Pane](#org73b9106)
48
+ - [Status Area](#orga48a015)
49
+ - [Alert Area](#org5098d11)
50
+ - [Command-line Editing](#org2926c89)
51
+ - [Input Context](#orgbcdaaad)
52
+ - [Cursor Motion](#org1af96be)
53
+ - [Clearing](#org32a2afa)
54
+ - [Undo and Redo](#org72cce5e)
55
+ - [Deletion and Killing](#org5ac8fea)
56
+ - [Yanking, Mark, and Region](#org948d8d5)
57
+ - [Count Prefix](#orgb998032)
58
+ - [History Navigation](#org30cf355)
59
+ - [Completion](#org0c995ec)
60
+ - [Accept or Cancel](#orgf4edfcc)
61
+ - [Paging Context](#org8d58d09)
62
+ - [Searching Output](#org89f3027)
63
+ - [String and Regex Search](#org77137d8)
64
+ - [Incremental Search](#orgd90c796)
65
+ - [Configuration](#orgc8458e1)
66
+ - [General Configuration `config.yml`](#org0add8c9)
67
+ - [`word_char_re`](#org1099eb8)
68
+ - [`esc_delay`](#orgce3e466)
69
+ - [`history`](#org95b5e48)
70
+ - [`file`](#orgdc30f58)
71
+ - [`max`](#org0941468)
72
+ - [`theme`](#orgce1b37d)
73
+ - [`truecolor`](#orgdd529bd)
74
+ - [`log`](#org2b58449)
75
+ - [`file`](#org0e3b938)
76
+ - [`level`](#org9c70445)
77
+ - [`tags`](#orge47f304)
78
+ - [Key code definitions `keydefs.yml`](#org220de42)
79
+ - [Key bindings `keybindings.yml`](#orgbd79429)
80
+ - [Key Names](#org698f2a9)
81
+ - [Printable Keys](#org58581e4)
82
+ - [Curses and Fatty Named Keys](#org4b0cf5b)
83
+ - [Possibly Invisible Keys](#orgeab9ebe)
84
+ - [Unnamed but Recognized Keys](#orgde02d1d)
85
+ - [Mouse Events](#orge487bc7)
86
+ - [Modifiers](#orgcf36a39)
87
+ - [Contexts](#orgc29aad9)
88
+ - [Actions](#orgdb5aa78)
89
+ - [Input buffer actions](#orgf51a408)
90
+ - [Input field actions](#org3f876b8)
91
+ - [Shell session actions](#orge0c4e6e)
92
+ - [Pager actions](#org05813c6)
93
+ - [Pager search actions](#orgea58518)
94
+ - [Popup actions](#org6ea9ecc)
95
+ - [Prompt popup actions](#org8f3d6dc)
96
+ - [Utility Actions](#org4a9e2d4)
97
+ - [Themes `themes/`](#org7e9ed87)
98
+ - [Distributed Themes](#orgf951ab7)
99
+ - [Custom Themes](#orgcf6fbb1)
100
+ - [Theme Name](#orgb8ead89)
101
+ - [Inheritance from Another Theme](#orgc49dfe9)
102
+ - [Inheritance within a Theme](#org371d3bb)
103
+ - [Theme Roles](#org83d0479)
104
+ - [Role Colors and Attributes and Frame Style](#orgabf70f2)
4
105
 
5
- #+PROPERTY: header-args:ruby :results value :colnames no :hlines yes :exports both :dir "./"
6
- #+PROPERTY: header-args:ruby+ :wrap example :session fatty_session :eval yes
7
- #+PROPERTY: header-args:ruby+ :prologue "$:.unshift('./lib') unless $:.first == './lib'; require 'fatty'"
8
- #+PROPERTY: header-args:ruby+ :ruby "bundle exec irb"
9
- #+PROPERTY: header-args:sh :exports code :eval no
10
- #+PROPERTY: header-args:bash :exports code :eval no
11
106
 
12
- [![CI](https://github.com/ddoherty03/fatty/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/ddoherty03/fatty/actions/workflows/main.yml)
13
107
 
108
+ <a id="org92a02ab"></a>
109
+
110
+ # Introduction
111
+
112
+ `Fatty` aims to provide a full-featured command-line environment that provides:
113
+
114
+ 1. an emacs-like editing command-line input, including undo/redo and kill/yank
115
+ 2. a history facility,
116
+ 3. command completion,
117
+ 4. completion of partially-typed file path names,
118
+ 5. output into a paging environment,
119
+ 6. searching within the paged output,
120
+ 7. issuing messages to a "status" area separate from the output pane,
121
+ 8. binding keys to pre-defined actions,
122
+ 9. defining unrecognized key-codes as named keys,
123
+ 10. color themes that can be selected in real time,
124
+ 11. a way to define new themes,
125
+ 12. processing completed command lines with a callback procedure of your choosing,
126
+ 13. several user-interface widgets such as selection popups, text prompts, menus, and progress indicators,
127
+ 14. a way to render markdown to the output pane,
128
+
129
+ In other words, fatty allows you to write a terminal-based REPL of your choosing but takes care of all the difficult parts.
130
+
131
+ `Fatty` is written entirely in Ruby and was born from my frustrations at the limitations of libraries like `readline` and `reline`. It uses `curses` and `truecolor` sequences for low-level rendering and is surprisingly snappy.
132
+
133
+ `Fatty` is *not* a terminal emulator but runs on top of one.
134
+
135
+
136
+ <a id="orga9758e6"></a>
137
+
138
+ # Quick Start
139
+
140
+
141
+ <a id="org5d48af2"></a>
142
+
143
+ ## Installing
144
+
145
+ `Fatty` is a ruby gem, so it can be installed with
146
+
147
+ ```sh
148
+ $ gem install fatty
149
+ ```
150
+
151
+
152
+ <a id="orga0811d0"></a>
153
+
154
+ ## Trying it Out with the \`fatty\` Demo
155
+
156
+ Once installed, you can try out `fatty` with the included program called `fatty`. It allows you to easily exercise all of the important features of the `fatty` library. At a normal shell prompt simply type `fatty`, and it will launch the `fatty` demo program.
157
+
158
+ Once inside `fatty` you will be prompted with a prompt that names your current directory. Type `help` to get a summary of the builtin commands available to you. If you type anything other than a builtin command, `fatty` attempts to run it as a shell command and displays the output.
159
+
160
+
161
+ <a id="org10b740e"></a>
162
+
163
+ ### Builtin commands
164
+
165
+ Here are the commands builtin to `fatty`
166
+
167
+ | Command | Description |
168
+ |--------------------------- |----------------------------------------------------------------------- |
169
+ | help | Display this file on the output pane |
170
+ | cd | Change the current directory used by the shell |
171
+ | choose | Present a series of choices in a popup window |
172
+ | choosevals | Also present choices in a popup window but return an associated value |
173
+ | choose\_multi | Present choices with a "checkbox" for selecting multiple values |
174
+ | choosevals\_multi | Also present a checkbox but return associated values |
175
+ | menu | Present a menu of labeled routines to run |
176
+ | info | Display an "info" message on the status line |
177
+ | good | Display a "good" message colored to indicate success |
178
+ | warn | Display a "warn" message colored to indicate caution |
179
+ | oops | Display an "oops" message colored to indicate failure |
180
+ | interrupt | Test fatty's interrupt handling |
181
+ | prompt | Popup a text box for entering a value in response to a prompt |
182
+ | progress count <N> | Display an animated progress indicator counting up to 40 or the given N |
183
+ | progress percent <N> | Same but also show the percent complete |
184
+ | progress count\_percent <N> | Same but show both count and the percent complete |
185
+ | progress trail | Show progress by displaying an "indicator" character for each step |
186
+ | progress bar | Show progress by a filling bar using ASCII characters |
187
+ | progress unicode\_bar | Same, but using unicode characters |
188
+ | progress braille\_bar | Same, but using braille characters |
189
+ | progress spinner | Animate a "spinner" showing a busy state |
190
+ | markdown <file.md> | Render the markdown file to the output pane; defaults to a demo file |
191
+ | keytest | Enter key diagnostic mode to report keycodes, key names, and bindings |
192
+ | colors | Display ANSI, 256-color, and X11 color diagnostics |
193
+
194
+
195
+ <a id="orga21015c"></a>
196
+
197
+ ### Screenshots
198
+
199
+
200
+ <a id="orge811242"></a>
201
+
202
+ #### Command line editing
203
+
204
+ Here is the command line, mid-edit showing a region selected and, in dim text to the right, a predictive completion based on history.
205
+
206
+ ![img](images/input_editing.png "Input editing showing region and predictive completion.")
207
+
208
+
209
+ <a id="org86db3fd"></a>
210
+
211
+ #### Searchable Output
212
+
213
+ While paging output, you can search for words, as here the user searches for the word `tty` in the output. The current match is highlighted in yellow with other matches highlighted in gray. The paging status line shows the search term and the direction of search. The user can navigate for other matches in the output using `n` and `N`.
214
+
215
+ ![img](images/search_output.png "Searching for instances of `tty` in the output.")
216
+
217
+
218
+ <a id="org465e882"></a>
219
+
220
+ #### Paging Markdown
221
+
222
+ The `fatty` demo running the `markdown` command and paging the output. It shows `fatty's` ability to render markdown using ANSI codes, including colorizing code blocks, as well as its paging interface.
223
+
224
+ ![img](images/page_markdown.png "Running the `fatty` demo `markdown` command.")
225
+
226
+
227
+ <a id="orged601b1"></a>
228
+
229
+ #### Popup Selection
230
+
231
+ One of the many "widgets" available through `fatty` is the ability to present the user with a set of choices to select from. After running the demo's `choose "AAA" "BBB" "CCC" "DDD" "EEE"`
232
+
233
+ ![img](images/choose_popup.png "Running the `fatty` demo `choose` command.")
234
+
235
+
236
+ <a id="org0fbc008"></a>
237
+
238
+ # Usage
239
+
240
+
241
+ <a id="orgdd7a899"></a>
242
+
243
+ ## Launching a Fatty Terminal
244
+
245
+
246
+ <a id="orge36ae8e"></a>
247
+
248
+ ### `on_accept`
249
+
250
+ You can launch a `Fatty` terminal session that does arbitrary processing of an edited command line and returns a String to be displayed on the output pane by providing an `on_accept` proc as a parameter as shown in this file that we'll call `loudrev`:
251
+
252
+ ```ruby
253
+ #! /usr/bin/env ruby
254
+ # -*- mode: ruby -*-
255
+
256
+ require 'fatty'
257
+
258
+ loud_reversal = lambda do |line|
259
+ line.upcase.reverse
260
+ end
261
+
262
+ Fatty::Terminal.new(
263
+ on_accept: loud_reversal,
264
+ ).go
265
+ ```
266
+
267
+ Now you have an interactive application that allows you to type text and see what it looks like when written backwards in uppercase letters. It's as easy as that!
268
+
269
+ When a `fatty` application runs, `fatty` installs a few files if they do not exist:
270
+
271
+ - a history file at `~/.fatty_history`
272
+ - a config directory at `~/.config/fatty` that contains
273
+ - `config.yml`, the main configuration file
274
+ - `keydefs.yml`, a file for associating keycodes emitted by the underlying terminal with names when curses does not do so automatically,
275
+ - `keybindings.yml`, a file for associating named keys with actions above and beyond the emacs bindings that are used by default, and
276
+ - `themes`, a directory of pre-defined theme definitions that you can choose from and add to by adding your own themes.
277
+
278
+
279
+ <a id="org74e77b2"></a>
280
+
281
+ #### Parameters to `on_accept`
282
+
283
+ The `on_accept` proc passed to an instance of `Fatty::Terminal` can take one or two parameters: (1) `line`, the edited line as it exists when the user types `RETURN` and (2) an optional callback parameter that you can use to access the facilities of `fatty`.
284
+
285
+
286
+ <a id="org21b8961"></a>
287
+
288
+ #### The `line` parameter to `on_accept`
289
+
290
+ `Fatty` does not parse command lines for your application. When the user accepts a `line`, `Fatty` passes the line to `on_accept` as text. It is up to your callback to decide whether to strip whitespace, split words, interpret quotes, parse options, recognize subcommands, or treat punctuation specially.
291
+
292
+ This keeps `Fatty` useful for many different kinds of REPLs. Some applications want shell-like parsing. Others want to preserve the user's input exactly.
293
+
294
+ For shell-like parsing, Ruby's standard `Shellwords` library is often useful:
295
+
296
+ ```ruby
297
+ #! /usr/bin/env ruby
298
+ # -*- mode: ruby -*-
299
+
300
+ require 'fatty'
301
+ require 'shellwords'
302
+
303
+ runner = lambda do |line, fatty|
304
+ words = Shellwords.split(line)
305
+
306
+ case words
307
+ when ["echo", *rest]
308
+ fatty.append("#{rest.join(" ")}\n")
309
+ when ["count", *rest]
310
+ fatty.append("#{rest.length}\n")
311
+ when []
312
+ fatty.status("Blank line", role: :info)
313
+ else
314
+ fatty.alert("Unknown command: #{words.first}", role: :warn)
315
+ end
316
+ rescue ArgumentError => e
317
+ fatty.alert("Could not parse line: #{e.message}", role: :error)
318
+ end
319
+
320
+ Fatty::Terminal.new(
321
+ prompt: "shellwords> ",
322
+ on_accept: runner,
323
+ ).go
324
+ ```
325
+
326
+
327
+ <a id="org3091db0"></a>
328
+
329
+ #### The callback parameter to `on_accept`
330
+
331
+ The `on_accept` proc can have a second parameter named whatever you like that serves as a callback into certain facilities provided by `fatty`. In this README we use the name `fatty` for the callback parameter since it provides access to the facilities provided by the `fatty` library. Here is a variation of the prior example that also echoes the original typed string into the so-called status area:
332
+
333
+ ```ruby
334
+ #! /usr/bin/env ruby
335
+ # -*- mode: ruby -*-
336
+
337
+ require 'fatty'
338
+
339
+ loud_reversal = lambda do |line, fatty|
340
+ fatty.good(line)
341
+ line.upcase.reverse
342
+ end
343
+
344
+ Fatty::Terminal.new(
345
+ on_accept: loud_reversal,
346
+ ).go
347
+ ```
348
+
349
+ The callback parameter responds to several methods that allow your application to interact with the user. They are documented below.
350
+
351
+
352
+ <a id="orgcd415d8"></a>
353
+
354
+ #### Output ordering
355
+
356
+ The `on_accept` callback can send text to the output pane in three ways:
357
+
358
+ 1. by returning a value from the callback;
359
+ 2. by calling `append` or `markdown` on the callback environment; or
360
+ 3. by calling `append_now` on the callback environment.
361
+
362
+ A returned `String` is the simplest output mechanism. It is displayed after the callback finishes:
363
+
364
+ ```ruby
365
+ Fatty::Terminal.new(
366
+ on_accept: ->(line) { line.upcase.reverse },
367
+ ).go
368
+ ```
369
+
370
+ Calls to `append` and `markdown` also queue output until the callback finishes. If a callback both calls `append` or `markdown` and returns a `String`, the queued output is displayed first and the returned string is displayed after it.
371
+
372
+ Calls to `append_now` are different. They append text and immediately render a frame, so the user can see output while the callback is still running. Use `append_now` for long-running callbacks that should stream progress to the output pane.
373
+
374
+ At the end of the callback, `Fatty` finishes the command and updates the pager state. Output produced with `append_now` may therefore appear during the callback, while output from `append`, `markdown`, and the callback's return value appears after the callback returns.
375
+
376
+
377
+ <a id="org0d9a504"></a>
378
+
379
+ #### Long-running callbacks and interruption
380
+
381
+ The `on_accept` callback runs synchronously. While it is running, control does not return to Fatty's normal event loop. A callback that performs lengthy work should therefore provide explicit safe points at which the user may interrupt it:
382
+
383
+ ```ruby
384
+ runner = lambda do |_line, fatty|
385
+ records.find_each do |record|
386
+ process(record)
387
+ fatty.check_interrupt!
388
+ end
389
+ fatty.good("Processing complete.")
390
+ end
391
+ ```
392
+
393
+ The `check_interrupt!` method performs a throttled, nonblocking input check. If the user has typed C-c or C-g, it raises `Fatty::Interrupt`. Fatty catches that exception at the callback boundary, finishes the current command cleanly, and returns to the normal input loop.
394
+
395
+ Calls to `check_interrupt!` should be placed only at points where it is safe to stop the application work. For example, they can go between records, batches, files, or other complete units of work. Fatty does not interrupt callbacks asynchronously and does not stop threads or raise exceptions at arbitrary instructions.
396
+
397
+ Fast callbacks do not need to call `check_interrupt!`. Long-running callbacks that already update a shared progress facility can often place the check there so that individual commands do not need separate interruption logic.
398
+
399
+ Code that needs to report interruption before allowing Fatty to perform its normal cleanup may rescue and re-raise the exception:
400
+
401
+ ```ruby
402
+ begin
403
+ items.each do |item|
404
+ process(item)
405
+ fatty.check_interrupt!
406
+ end
407
+ rescue Fatty::Interrupt
408
+ fatty.warn("Processing interrupted.", replace: true)
409
+ raise
410
+ else
411
+ fatty.good("Processing complete.", replace: true)
412
+ end
413
+ ```
414
+
415
+
416
+ <a id="org89ae995"></a>
417
+
418
+ #### ANSI colors in output
419
+
420
+ Text passed to `append`, `append_now`, `status`, and `alert` may contain ANSI SGR color/style sequences. Fatty interprets those sequences relative to the current theme role, so an ANSI reset returns to the active Fatty role rather than to the terminal's physical default colors. `fatty` includes the nice [`Rainbow` gem](https://github.com/ku1ik/rainbow) for colorizing text as a convenience.
421
+
422
+
423
+ <a id="orgce83cdf"></a>
424
+
425
+ ### Other parameters to `Terminal.new`
426
+
427
+ When building a REPL with `Terminal.new` you can also supply several other parameters:
428
+
429
+ - **`prompt:`:** a `String` or a zero-argument callable such as a `Proc` or `lambda` that returns the prompt used for the input line to be edited. Because it can be a callable, it can change after each run of `on_accept`; if not given, it defaults to '> ';
430
+ - **`app_name`:** the name for the "app" using the `fatty` library; this allows app-specific configuration under `~/.config/fatty/apps/<app_name>` or a different directory specified by `app_config_dir`;
431
+ - **`app_config_dir`:** an alternative directory for app-specific configuration instead of the default;
432
+ - **`completion_proc`:** a callable that takes the current text of the input field's buffer and returns an `Array` of possible completions at that point in the input; by default, there is no completion proc;
433
+ - **`history_path`:** a `String` for the file path where command history is loaded from and saved. The default, `:default`, uses `history.file` from Fatty's configuration, falling back to `~/.fatty_history` if no history file is configured. Passing `nil` or `false` disables persistent history and keeps history in memory only. Passing a string uses that path directly.
434
+ - **`history_ctx`:** optional history context used to prefer relevant history entries. It may be a hash or a callable returning a hash. The context is stored in the history file with each accepted line and is used by history navigation and history autosuggestions. Matching context entries are preferred, but history is not strictly partitioned; Fatty can still fall back to other entries. This is useful when the same application wants different history behavior in different directories, ledgers, projects, modes, or accounts.
435
+
436
+ For example, the `fatty` demo executable uses the current working directory as history context:
437
+
438
+ ```ruby
439
+ Fatty::Terminal.new(
440
+ history_ctx: -> { { pwd: Dir.pwd } },
441
+ on_accept: ->(line, env) {
442
+ # ...
443
+ },
444
+ ).go
445
+ ```
446
+
447
+ With that setup, commands entered in `~/src/byr` are favored when the terminal is again in `~/src/byr`, while commands entered elsewhere remain available as fallback history.
448
+
449
+
450
+ <a id="orgef3914d"></a>
451
+
452
+ ## The Callback API
453
+
454
+ Here are the details on the messages that you can send to the callback parameter to the `on_accept` proc.
455
+
456
+
457
+ <a id="org1f2fcbb"></a>
458
+
459
+ ### `append(text, follow: true)`
460
+
461
+ Add the given text to the output pane. After a full page of output is produced, enter paging mode so the user can view the output at leisure and search the output. If `follow` is true, keep the output pane's viewport covering the last lines of output.
462
+
463
+
464
+ <a id="org3318ee2"></a>
465
+
466
+ ### `append_now(text, follow: true, mode: nil)`
467
+
468
+ Like `append`, but display output as it is produced rather than wait for a full page to be produced.
469
+
470
+ Setting the mode to `:scrolling` or `:paging` determines whether the output is scrolled continuously or paused on each page of output.
471
+
472
+
473
+ <a id="org3da5738"></a>
474
+
475
+ ### `markdown(text)`
476
+
477
+ Render the given text as markdown source according to the current theme then `append` the result to the output pane.
478
+
479
+ We will not rehearse here how to compose markdown, but one thing that is a bit special with `fatty` is forcing a line break: you should use `<br>` to force a line break in the middle of a line.
480
+
481
+ ```
482
+ This line has a forced line break <br> and this should appear on the next line.
483
+ ```
484
+
485
+ will render as
486
+
487
+ ```
488
+ This line has a forced line break
489
+ and this should appear on the next line.
490
+ ```
491
+
492
+
493
+ <a id="org5e0ab5d"></a>
494
+
495
+ ### `status(text, role: :info)`
496
+
497
+ Display the text in the "status" area, the lines immediately above the input field, using one of the following "roles":
498
+
499
+ - **:good:** colored to indicate something affirming according to the theme, usually something greenish;
500
+ - **:info:** colored something neutral simply for informational purposes;
501
+ - **:warn:** colored something to suggest caution, usually something in the orange to yellow range;
502
+ - **:error:** colored something to suggest danger, usually some tone of red.
503
+
504
+
505
+ <a id="orgd2a8b1b"></a>
506
+
507
+ ### `good(text, replace: false, render: true)`
508
+
509
+ Display the text in the status area with the role :good.
510
+
511
+
512
+ <a id="org8ec7886"></a>
513
+
514
+ ### `info(text, replace: false, render: true)`
515
+
516
+ Display the text in the status area with the role :info.
517
+
518
+
519
+ <a id="org6bb2e02"></a>
520
+
521
+ ### `warn(text, replace: false, render: true)`
522
+
523
+ Display the text in the status area with the role :warn.
524
+
525
+
526
+ <a id="org6040c43"></a>
527
+
528
+ ### `error(text, replace: false, render: true)`
529
+
530
+ Display the text in the status area with the role :error.
531
+
532
+
533
+ <a id="org3e60ade"></a>
534
+
535
+ ### `oops(text, replace: false, render: true)`
536
+
537
+ An alias for `error(text)`
538
+
539
+
540
+ <a id="org5e74284"></a>
541
+
542
+ ### `alert(text, role: :info)`
543
+
544
+ Display the text in the one-line alert panel just below the input field. In the alert panel, the role only controls the foreground color, not the background.
545
+
546
+
547
+ <a id="org71a56a2"></a>
548
+
549
+ ### `prompt(prompt, initial: "", cancel_value: nil, history_key: nil, save_history: true)`
550
+
551
+ Display a popup dialog box that allows the user to type in any string and return it. Combine the input line with a prompted-for string and shuffle their letters together:
552
+
553
+ ```ruby
554
+ #! /usr/bin/env -S ruby -Ilib
555
+ # -*- mode: ruby -*-
556
+
557
+ require 'fatty'
558
+
559
+ weaver = lambda do |line, fatty|
560
+ str = fatty.prompt("Secondary string to weave in with '#{line}'")
561
+ (line + str).downcase.gsub(/[^a-z]/, '').split('').shuffle.join
562
+ end
563
+
564
+ Fatty::Terminal.new(
565
+ on_accept: weaver,
566
+ prompt: "Primary String> "
567
+ ).go
568
+ ```
569
+
570
+ You can pre-fill the prompt's input line by passing in an `initial:` parameter.
571
+
572
+ The `prompt` input has its own history facility separate from the history in the main input command line. You can turn it off by setting `save_history` to `false`.
573
+
574
+ `prompt` returns whatever the user typed and returns `nil` if the user cancels with C-c or C-g unless you specify an alternative `cancel_value`.
575
+
576
+
577
+ <a id="orgd0da92b"></a>
578
+
579
+ ### `check_interrupt!`
580
+
581
+ Provide a cooperative interruption point during a long-running `on_accept` callback. Fatty periodically performs a nonblocking input check and raises `Fatty::Interrupt` if the user has typed C-c or C-g.
582
+
583
+ The polling is throttled, so `check_interrupt!` may be called frequently. It should nevertheless be called only at points where the callback can safely stop its work.
584
+
585
+ Fatty catches `Fatty::Interrupt` at the callback boundary, finishes the current command, restores the terminal state, and resumes the normal input loop.
586
+
587
+
588
+ <a id="orgfbc8aa5"></a>
589
+
590
+ ### `add_progress(label:, total: nil, style: :percent, role: :info, width: 40)`
591
+
592
+ Display a progress widget in the status area to show the user that the system is working, not frozen. The `add_progress` method returns a `Progress` object on which the `#update` method can be called to cause it to animate one step. To the left of the widget the `label:` is displayed, followed by the widget. For processes where the total size is known in advance, the `total:` parameter indicates that size. The `role:` parameter controls the styling of the widget. The `width:` parameter places a limit on the size of the widget. `Fatty` supports several styles of progress widgets:
593
+
594
+ - **:spinner:** a simple busy-wait indicator that just animates on every call;
595
+ - **:count:** count up to `total` on each call of `update(current: <count>)`
596
+ - **:percent:** count up to percent of `total` on each call of `update(current: <count>)`
597
+ - **:count\_percent:** count up and show percent of `total` on each call of `update(current: <count>)`
598
+ - **:bar:** display a filling ASCII progress bar with percent and count
599
+ - **:unicode\_bar:** display a filling Unicode progress bar with percent and count
600
+ - **:braille\_bar:** display a filling progress bar with percent and count using braille characters
601
+ - **:trail:** display an "indicator" on each call of `update(indicator: <string>)`
602
+
603
+
604
+ <a id="orgd758f7b"></a>
605
+
606
+ #### Initialization `add_progress(label:, style: :percent, total: nil, role: :info, width: 40)`
607
+
608
+ - **`label:`:** Text displayed before the progress widget, "Progress" by default.
609
+ - **`style:`:** The style of the progress widget from one of those named above.
610
+ - **`total:`:** For all styles except :trail and :spinner, the number that represents the size of the task. As the `#update` calls increase the value of `current` towards `total`, the widget indicates increasing completion.
611
+ - **`role:`:** One of :good, :info, :warn, or :error to color the widget according to the theme's idea of these roles. By default, :info.
612
+ - **`width`:** The number of characters for the full widget display: it is only relevant for the bar styles and the trail style; otherwise it is ignored.
613
+
614
+
615
+ <a id="orgbe50f7a"></a>
616
+
617
+ #### Update `update(current: nil, indicator: nil, render: false)`
618
+
619
+ You update the progress widget by calling `#update` on the Progress object, passing as the `current:` parameter a number that indicates progress so far.
620
+
621
+ ```ruby
622
+ #! /usr/bin/env -S ruby -Ilib
623
+ # -*- mode: ruby -*-
624
+
625
+ require 'fatty'
626
+ require 'prime'
627
+
628
+ primer = lambda do |input, fatty|
629
+ if input.match?(/\A[1-9]\d*\z/)
630
+ k = input.to_i
631
+ prog = fatty.add_progress(label: "Thinking...", total: k, style: :bar)
632
+ out_line = +""
633
+ num_primes = 0
634
+ Prime.each do |p|
635
+ num_primes += 1
636
+ out = "#{p} "
637
+
638
+ if out_line.length + out.length > 100
639
+ fatty.append_now("#{out_line}\n", mode: :scrolling)
640
+ out_line.clear
641
+ end
642
+
643
+ out_line << out
644
+ prog.update(current: num_primes)
645
+ break if num_primes >= k
646
+ end
647
+ fatty.append_now("#{out_line}\n", mode: :scrolling) unless out_line.empty?
648
+ prog.finish("Done")
649
+ else
650
+ fatty.alert "Give me a positive integer", role: :error
651
+ end
652
+ end
653
+
654
+ Fatty::Terminal.new(
655
+ on_accept: primer,
656
+ prompt: 'How many primes? ',
657
+ ).go
658
+ ```
659
+
660
+ For the trail-style progress, you can use an arbitrary string to indicate progress by adding an `indicator:` parameter to the `#update` call.
661
+
662
+ Suppose, for example, you wanted to print a trail in which the last digit of the prime is displayed to indicate progress and colored so it stands out:
663
+
664
+ ```ruby
665
+ #! /usr/bin/env -S ruby -Ilib
666
+ # -*- mode: ruby -*-
667
+
668
+ require 'fatty'
669
+ require 'prime'
670
+ require "rainbow/refinement"
671
+
672
+ using Rainbow
673
+
674
+ primer = lambda do |input, fatty|
675
+ if input.match?(/\A[1-9]\d*\z/)
676
+ k = input.to_i
677
+ prog = fatty.add_progress(label: "Thinking...", total: k, style: :trail, width: 100)
678
+ out_line = +""
679
+ num_primes = 0
680
+ Prime.each do |p|
681
+ num_primes += 1
682
+ out = "#{p} "
683
+
684
+ if out_line.length + out.length > 100
685
+ fatty.append_now("#{out_line}\n", mode: :scrolling)
686
+ out_line.clear
687
+ end
688
+
689
+ out_line << out
690
+ last_dig = p.to_s.split('').last
691
+ sig =
692
+ case last_dig
693
+ when '1'
694
+ last_dig.red
695
+ when '3'
696
+ last_dig.blue
697
+ when '7'
698
+ last_dig.green
699
+ when '9'
700
+ last_dig.yellow
701
+ else
702
+ '!'
703
+ end
704
+ prog.update(current: num_primes, indicator: sig)
705
+ break if num_primes >= k
706
+ end
707
+ fatty.append_now("#{out_line}\n", mode: :scrolling) unless out_line.empty?
708
+ prog.finish("Done")
709
+ else
710
+ fatty.alert "Give me a positive integer", role: :error
711
+ end
712
+ end
713
+
714
+ Fatty::Terminal.new(
715
+ on_accept: primer,
716
+ prompt: 'How many primes? ',
717
+ ).go
718
+ ```
719
+
720
+
721
+ <a id="org058be1b"></a>
722
+
723
+ #### Finish
724
+
725
+ As the prior examples illustrate, you can issue an ending message at the end of the process by calling `#finish` on the Progress object.
726
+
727
+
728
+ <a id="org22e4b8d"></a>
729
+
730
+ #### Clear
731
+
732
+ And, if you have occasion, you can clear the Progress by calling `#clear` on the Progress object.
733
+
734
+
735
+ <a id="orgcfb56dd"></a>
736
+
737
+ ### `choose(prompt, choices:, initial_choice_idx: 0, cancel_value: nil)`
738
+
739
+ Present a set of `choices:`, which can be either:
740
+
741
+ - an `Array` of `String` choices, where the selected string is returned; or
742
+ - a `Hash` whose keys are converted to `String` labels and whose values are returned.
743
+
744
+ `choose` returns:
745
+
746
+ - the selected `String` when `choices:` is an `Array`;
747
+ - the associated value when `choices:` is a `Hash`; or
748
+ - `cancel_value` when the user cancels with C-c or C-g.
749
+
750
+ The `prompt` String (by default "Choose") can guide the user about the purpose of the choices.
751
+
752
+ If you want to set one of the choices as the initial choice, set `initial_choice_idx:` to the Integer index of one of the choices.
753
+
754
+ If you want a value associated with the user's cancellation of the chooser with C-c or C-g, set `cancel_value:` to that value.
755
+
756
+ With an Array of Strings:
757
+
758
+ ```ruby
759
+ #! /usr/bin/env -S ruby -Ilib
760
+ # -*- mode: ruby -*-
761
+
762
+ require 'fatty'
763
+
764
+ rgb = lambda do |_line, fatty|
765
+ color = fatty.choose(
766
+ "Choose a color",
767
+ choices: ["red", "green", "blue"],
768
+ )
769
+ fatty.status("If you say so: #{Rainbow(color).send(color.to_sym)} it is!\n")
770
+ end
771
+
772
+ Fatty::Terminal.new(
773
+ prompt: "Hit RETURN to choose a color> ",
774
+ on_accept: rgb,
775
+ ).go
776
+ ```
777
+
778
+ Or with a Hash:
779
+
780
+ ```ruby
781
+ #! /usr/bin/env -S ruby -Ilib
782
+ # -*- mode: ruby -*-
783
+
784
+ require 'fatty'
785
+
786
+ nums = lambda do |_line, fatty|
787
+ const = fatty.choose(
788
+ "Choose a constant",
789
+ choices: { "Pi" => 3.14159, "Euler" => 2.718281828, "Golden Ratio" => 1.61803398875 }
790
+ )
791
+ fatty.status("Somewhere around #{const}\n")
792
+ end
793
+
794
+ Fatty::Terminal.new(
795
+ prompt: "Hit RETURN to choose a constant> ",
796
+ on_accept: nums,
797
+ ).go
798
+ ```
799
+
800
+
801
+ <a id="org5b1d7d1"></a>
802
+
803
+ ### `choose_multi(prompt, choices:, cancel_value: nil)`
804
+
805
+ Present a set of `choices:`, which can either be
806
+
807
+ - an Array whose items are converted to `Strings` and presented as choices
808
+ - a Hash whose keys converted to `Strings` and presented as choices
809
+
810
+ `choose_multi` returns
811
+
812
+ - a `Hash` whose keys and values are the user's selections if `choices:` was an `Array` of `Strings`,
813
+ - a `Hash` whose keys and values are those selected from the `choices:` `Hash` if `choices:` was an `Hash`,
814
+ - or the `cancel_value` if the user canceled the selection with C-c or C-g.
815
+
816
+ The `prompt` String (by default "Choose Many") can guide the user about the purpose of the choices.
817
+
818
+
819
+ <a id="org9b37a8e"></a>
820
+
821
+ ### `confirm(prompt, yes_label: "Yes", no_label: "No", cancel_value: false)`
822
+
823
+ Present the user with a simple Yes/No choice using your choice of ways to express "Yes" or "No" with `yes_label:` and `no_label:`.
824
+
825
+ `confirm` returns `true` for "Yes" and `false` for "No."
826
+
827
+ `confirm` will return `false` on cancellation with C-c or C-g unless you provide an alternative `cancel_value:`, in which case it returns that.
828
+
829
+
830
+ <a id="org8d63dae"></a>
831
+
832
+ ### `menu(prompt, choices:, initial_choice_idx: 0, cancel_value: nil)`
833
+
834
+ Present the user with a set of `choices:` representing actions to execute.
835
+
836
+ `choices:` is a `Hash` whose keys are labels presented to the user and whose values are procs, lambdas, or other objects that respond to `#call`.
837
+
838
+ ```ruby
839
+ #! /usr/bin/env -S ruby -Ilib
840
+ # -*- mode: ruby -*-
841
+
842
+ require 'fatty'
843
+
844
+ asker = lambda do |_line, fatty|
845
+ result = fatty.menu(
846
+ "Pick an action",
847
+ choices: {
848
+ "Self Echo" => ->(fatty, label) {
849
+ fatty.append("You chose #{label}\n")
850
+ :echoed
851
+ },
852
+ "Say Hello" => ->(fatty, _label) {
853
+ name = fatty.prompt("Name?")
854
+ fatty.append("Hello, #{name}\n")
855
+ :greeted
856
+ },
857
+ },
858
+ )
859
+ fatty.good(result)
860
+ end
861
+
862
+ Fatty::Terminal.new(
863
+ on_accept: asker,
864
+ prompt: "Type RETURN for some action> ",
865
+ ).go
866
+ ```
867
+
868
+ Each callable has access to the `CallbackEnvironment`, called `fatty` here and the `String` label that the user selected.
869
+
870
+ Each callable can return a value that the `@on_accept` proc can process as it pleases. In the example, they are printed to the status area.
871
+
872
+ If you want to set one of the choices as the initial choice, set `initial_choice_idx:` to the Integer index of one of the choices.
873
+
874
+ If you want a value associated with the user's cancellation of the chooser with C-c or C-g, set `cancel_value:` to that value.
875
+
876
+
877
+ <a id="org1750b9e"></a>
878
+
879
+ ### `environment`
880
+
881
+ This returns a `Hash` of runtime conditions detected by `Fatty`:
882
+
883
+ | Key | Description |
884
+ |--------------------- |---------------------------------------------------- |
885
+ | `:arch` | System CPU architecture, e.g., `x86_64` |
886
+ | `:os` | Operating system detected |
887
+ | `:ruby_platform` | Ruby platform, e.g., `x86_64-linux` |
888
+ | `:screen` | Whether the terminal is running under `screen` |
889
+ | `:ssh` | Whether the terminal is running under SSH |
890
+ | `:tmux` | Whether the terminal is running under `tmux` |
891
+ | `:terminal` | Detected underlying terminal, e.g., `kitty` |
892
+ | `:terminal_version` | Version of the terminal, if known |
893
+ | `:term` | The `TERM` terminal type |
894
+ | `:truecolor_detected` | Whether truecolor capability appears to be available |
895
+ | `:truecolor_enabled` | Whether Fatty is actually using truecolor rendering |
896
+ | `:curses` | Runtime curses capabilities and parameters |
897
+
898
+ The environment report also includes a nested `:curses` hash:
899
+
900
+ | Key | Description |
901
+ |------------------- |---------------------------------------------------- |
902
+ | `:started` | Whether curses has been initialized |
903
+ | `:truecolor` | Whether the active curses context is using truecolor |
904
+ | `:key_min` | Lowest keycode with a curses name |
905
+ | `:key_max` | Highest keycode with a curses name |
906
+ | `:lines` | Number of terminal rows available |
907
+ | `:cols` | Number of terminal columns available |
908
+ | `:has_colors` | Whether curses reports color support |
909
+ | `:colors` | Number of colors available to curses |
910
+ | `:color_pairs` | Number of color pairs available to curses |
911
+ | `:can_change_color` | Whether curses can redefine color values |
912
+
913
+
914
+ <a id="org727ceaf"></a>
915
+
916
+ # Default Interaction
917
+
918
+
919
+ <a id="orgd560bdc"></a>
920
+
921
+ ## Parts of the Screen
922
+
923
+
924
+ <a id="org6ae09c7"></a>
925
+
926
+ ### Input Field
927
+
928
+ Just above the bottom of the screen where all the action takes place: it is a line for editing the input. It displays a prompt followed by an area in which you build the command line using `fatty's` editing facilities.
929
+
930
+
931
+ <a id="org73b9106"></a>
932
+
933
+ ### Output Pane
934
+
935
+ Most of the top part of the screen is reserved for displaying whatever output is sent to it with the `on_accept` callback to the `Terminal`. It can render colored ANSI-encoded strings and will page long output so you can view it a page at a time and even search the output.
936
+
937
+
938
+ <a id="orga48a015"></a>
939
+
940
+ ### Status Area
941
+
942
+ The one to four lines just above the Input Field that displays output to the user that is out of band for the Output Pane. Brief messages of confirmation, warning, or error can be displayed there so as to get the user's immediate attention. Progress bars also render there where their visibility is made prominent.
943
+
944
+
945
+ <a id="org5098d11"></a>
946
+
947
+ ### Alert Area
948
+
949
+ Alerts are short-lived, non-scrolling messages shown below the input field. They are intended for user-visible conditions that require attention. `Fatty` uses this area to warn the user of unrecognized key codes and of unbound key presses.
950
+
951
+
952
+ <a id="org2926c89"></a>
953
+
954
+ ## Command-line Editing
955
+
956
+ `fatty` aims to have a full-featured command-line editor for editing a single line of text that is sent to the `on_accept` proc for the `Terminal`. The default keybindings are based on Emacs keybindings, but they are fully customizable. Implementing `vim`-like keybindings is not currently possible because `fatty` has no notion of editing "modes" as would be required for any kind of a modal editor&#x2014;at least not yet. Maybe some day.
957
+
958
+
959
+ <a id="orgbcdaaad"></a>
960
+
961
+ ### Input Context
962
+
963
+ When editing the input line or text input for widgets like the \`prompt\`, \`fatty\` provides emacs-like editing keybindings by default. Many of these commands can take a count prefix argument to repeat the command count times. For example, \`M-8 M-0 #\` will insert 80 '#' characters at the cursor.
964
+
965
+
966
+ <a id="org1af96be"></a>
967
+
968
+ #### Cursor Motion
969
+
970
+ `Fatty` implements all the expected actions for moving the cursor around the input buffer. You can move by characters or words or to the beginning or end of the buffer.
971
+
972
+ | Key | Description |
973
+ |-------- |--------------------------------- |
974
+ | C-a | move to the beginning of the line |
975
+ | :home | move to the beginning of the line |
976
+ | C-e | move to the end of the line |
977
+ | :end | move to the end of the line |
978
+ | C-f | move cursor right one character |
979
+ | :right | move cursor right one character |
980
+ | C-b | move cursor left one character |
981
+ | :left | move cursor left one character |
982
+ | M-f | move cursor right one word |
983
+ | M-:right | move cursor right one word |
984
+ | C-:right | move cursor right one word |
985
+ | M-b | move cursor left one word |
986
+ | M-:left | move cursor left one word |
987
+ | C-:left | move cursor left one word |
988
+
989
+
990
+ <a id="org32a2afa"></a>
991
+
992
+ #### Clearing
993
+
994
+ This is one action that does not affect the input buffer but the output pane. It clears it.
995
+
996
+ | Key | Description |
997
+ |--- |--------------------- |
998
+ | C-l | clear the output pane |
999
+
1000
+
1001
+ <a id="org72cce5e"></a>
1002
+
1003
+ #### Undo and Redo
1004
+
1005
+ `Fatty` keeps a stack of buffer-modifying actions, recording the buffer contents before any change is made. The undo-redo facility allows you to traverse that stack, `undo` travels backward to an earlier buffer state, and, once at an earlier state, `redo` travels forward to a more recent buffer state.
1006
+
1007
+ | Key | Description |
1008
+ |----- |----------- |
1009
+ | C-/ | undo |
1010
+ | C-\_ | undo |
1011
+ | C-M-/ | redo |
1012
+ | M-/ | redo |
1013
+
1014
+
1015
+ <a id="org5ac8fea"></a>
1016
+
1017
+ #### Deletion and Killing
1018
+
1019
+ `Fatty` implements the emacs-esque notion of "killing" text by deleting it but also adding it to the "kill ring", a circular structure of all prior kills. It is circular in that successive "yanks", or pastes, traverse the kill ring and return to the beginning after reaching the end. By contrast a "deletion" merely removes text from the buffer without adding it to the kill ring.
1020
+
1021
+ When the region is active, destructive editing commands kill the region rather than deleting characters or words.
1022
+
1023
+ | Key | Description |
1024
+ |---------- |------------------------------ |
1025
+ | C-d | delete character at cursor |
1026
+ | :delete | delete character at cursor |
1027
+ | :backspace | delete character before cursor |
1028
+ | M-d | kill word at cursor |
1029
+ | C-w | kill word before cursor |
1030
+ | C-k | kill to end of line |
1031
+ | C-t | transpose characters |
1032
+ | M-t | transpose words |
1033
+
1034
+
1035
+ <a id="org948d8d5"></a>
1036
+
1037
+ #### Yanking, Mark, and Region
1038
+
1039
+ Text that has been killed can be "yanked" into the buffer at the cursor position with the "yank" action. That text can be replaced by successive entries in the kill ring with the yank\_pop action.
1040
+
1041
+ In addition, `fatty` allows you to define a "region" by setting a mark and moving the cursor. The text between the mark and the cursor is the region. The region can be killed or copied, that is deleted and added to the kill ring or just added to the kill ring.
1042
+
1043
+ Notice that commands that normally delete characters or words, kill the region if the region is active.
1044
+
1045
+ | Key | Description |
1046
+ |---------- |------------------------------------------------- |
1047
+ | C-y | yank last kill at the cursor |
1048
+ | M-y | replace last yank with next item in the kill ring |
1049
+ | | |
1050
+ | C-d | when region active, kill the region |
1051
+ | :delete | when region active, kill the region |
1052
+ | :backspace | when region active, kill the region |
1053
+ | M-d | when region active, kill the region |
1054
+ | C-w | when region active, kill the region |
1055
+ | | |
1056
+ | C-:space | set the mark at the current cursor position |
1057
+ | C-@ | set the mark at the current cursor position |
1058
+ | C-g | clear the mark |
1059
+ | C-w | kill the region |
1060
+ | M-w | copy the region |
1061
+
1062
+
1063
+ <a id="orgb998032"></a>
1064
+
1065
+ #### Count Prefix
1066
+
1067
+ By default each action acts on one item, so the default count is 1. But for certain commands that operate on characters or words, you can modify the count by typing `M-<digit>` keys before the command. For example, if you want to insert exactly 80 '#' characters into the buffer, `M-8 M-0 #` will do the trick.
1068
+
1069
+ There is one command, however, for which the count is not used as a repeat count: for `page_top` (bound in paging to `g`) the count is used as a line number to navigate to.
1070
+
1071
+ Fatty follows the emacs convention of making `C-u` multiply the count prefix by 4 with each press. So you can insert 64 '#' characters with `C-u C-u C-u #`.
1072
+
1073
+ | Key | Description |
1074
+ |--------- |-------------------------------------------------- |
1075
+ | C-u | universal count argument (times 4 with each press) |
1076
+ | M-<digit> | accumulate a count argument |
1077
+
1078
+
1079
+ <a id="org30cf355"></a>
1080
+
1081
+ #### History Navigation
1082
+
1083
+ `Fatty` sports a rather sophisticated history system. It not only records each completed item of input but can also be made to note the kind of items and the "context" in which it was entered.
1084
+
1085
+ History is essentially partitioned into separate "kinds" of history so that the command line can occupy one part of history and search strings a different part of history. That way, the history is made relevant to the current input buffer that you are working on.
1086
+
1087
+ In addition, each history item can note its "context". This is less strict than "kinds" of history but allows the history mechanism to favor history items from the same "context" as the current context. For example, in the `fatty` demo, any line that is not a builtin command is treated as a shell
1088
+
1089
+ if you traverse history items, you will be shown command executed from the directory you are in before other history items. It still will give you items from other contexts, it just pushes items from the current context to the front of the list.
1090
+
1091
+ You can navigate history items with the `up` and `down` keys as you would expect. But you can also have the history items presented in a popup that shows all history items then allows you to narrow the items displayed by typing text into its narrowing input field. Bonus: the narrowing input field itself has a history that you can access with `M-p` and `M-n`. History on history.
1092
+
1093
+ | Key | Description |
1094
+ |----- |-------------------------------------------- |
1095
+ | C-p | replace the line with the prior history item |
1096
+ | :up | replace the line with the prior history item |
1097
+ | C-n | replace the line with the next history item |
1098
+ | :down | replace the line with the next history item |
1099
+ | C-r | search the history in a popup |
1100
+
1101
+
1102
+ <a id="org0c995ec"></a>
1103
+
1104
+ #### Completion
1105
+
1106
+ `Fatty` also provides a completion facility that makes entering input easier.
1107
+
1108
+ 1. Input Completion
1109
+
1110
+ As you type a command-line, `fatty` will auto-suggest ways to complete the line with dimmed text to the right of the cursor:
1111
+
1112
+ ![img](images/auto_suggest.png)
1113
+
1114
+ After typing 'ec' `fatty` suggests a command-line pulled from history using the dimmed text to the right of the cursor. The suggestion can be accepted by moving the cursor into the suggestion, and any text then to the left of the cursor becomes part of the input buffer.
1115
+
1116
+ `fatty` may have several completions suggested by the text-so-far and you can cycle through them by hitting the `tab` key. And if you want, you can see all the suggestions in a popup with `M-tab`. That allows narrowing by the text of the completions and the narrowing filter has its own history.
1117
+
1118
+ ![img](images/completion_popup.png)
1119
+
1120
+ 2. Sources for Input Completion
1121
+
1122
+ For input completion, `fatty` uses two sources:
1123
+
1124
+ 1. The input history.
1125
+ 2. The candidates returned by the `Terminal#completion_proc`.
1126
+
1127
+ History completion is always available when the terminal has history enabled. It lets the user complete from lines they have previously accepted. This is especially useful for command shells, REPL-style interfaces, and applications where users often repeat or slightly modify earlier input.
1128
+
1129
+ Application completion is supplied by the optional `completion_proc` passed to `Fatty::Terminal.new`. The proc is called with the input buffer's text-so-far and returns candidate strings. This is where an application can offer completions for commands, file names, account names, option names, object identifiers, or anything else meaningful to the application.
1130
+
1131
+ The two sources are combined before they are presented to the user. Fatty deduplicates candidates and then offers the result through the normal completion interface. `TAB` cycles through completions inline. `M-TAB` opens a completion popup, where the user can narrow the candidate list by typing in the `Complete:` field.
1132
+
1133
+ Application candidates are offered first. History candidates fill in behind them. This gives the host application priority for current, domain-specific completions while still letting users recover earlier accepted input.
1134
+
1135
+ The completion popup is still a popup filter. Its narrowing text has its own history, separate from command input history and from other popup filters. Use `M-p` and `M-n` inside the completion popup to recall previous completion filters.
1136
+
1137
+ 3. File Name Completion
1138
+
1139
+ Apart from command-line completion, `fatty` offers completion of file names if the cursor is at the end of a string that looks like a file name: that is, it starts with './', '../', '~/' or '/'.
1140
+
1141
+ 1. If the string-so-far matches only one directory or file name, it inserts that into the input buffer and continues completion from there.
1142
+ 2. If the string-so-far matches multiple files or directories it auto-suggests the first and `TAB` cycles through each of the candidates which you can accept by moving the cursor past the suggestion. Likewise `S-TAB` cycles backwards through the list of candidates.
1143
+ 3. If the string-so-far matches no files, completion does nothing.
1144
+
1145
+ Just as with command line completion, you can invoke a popup of completions with `M-TAB` and a popup of possible file names will appear within which you may filter the candidates down by narrowing. Unlike other forms of completion, the file name completion popup allows you to fill out the path by navigating directories within the popup by hitting `TAB` or `right` to descend into a sub-directory or `S-TAB` or `left` to ascend to the parent directory. The popup title shows the path so far.
1146
+
1147
+
1148
+ <a id="orgf4edfcc"></a>
1149
+
1150
+ #### Accept or Cancel
1151
+
1152
+ Finally, when the command-line is to your satisfaction, you can submit the line to the `on_accept` proc which determines how your app responds to it.
1153
+
1154
+ You can end a `fatty` session with `C-c` at the command line, or (only if the buffer is empty) with `C-d`.
1155
+
1156
+ | Key | Description |
1157
+ |-------- |------------------------------------------------------ |
1158
+ | :enter | feed the line to the on\_accept proc and page output |
1159
+ | M-:enter | feed the line to the on\_accept proc and scroll output |
1160
+ | C-c | quit \`fatty\` |
1161
+ | C-d | quit \`fatty\` only if the input line is empty |
1162
+
1163
+
1164
+ <a id="org8d58d09"></a>
1165
+
1166
+ ### Paging Context
1167
+
1168
+ Once the command-line is accepted, \`fatty\` sends output to the large output pane, and if the output is more than one screen long presents a paging environment for viewing and searching the output. From there, `fatty` offers the expected navigation aids for examining the output:
1169
+
1170
+ | Key | Count? | Description |
1171
+ |----------- |------ |--------------------------------------------------- |
1172
+ | `up` | Yes | move output one line up |
1173
+ | `k` | Yes | move output one line up |
1174
+ | `down` | Yes | move output one line down |
1175
+ | `j` | Yes | move output one line down |
1176
+ | `page_up` | Yes | move back one page |
1177
+ | `b` | Yes | move back one page |
1178
+ | `u` | Yes | move back one page |
1179
+ | `M-v` | Yes | move back one page |
1180
+ | `backspace` | Yes | move back one page |
1181
+ | `page_down` | Yes | move forward one page |
1182
+ | `f` | Yes | move forward one page |
1183
+ | `d` | Yes | move forward one page |
1184
+ | `space` | Yes | move forward one page |
1185
+ | `C-v` | Yes | move forward one page |
1186
+ | `home` | No | move to the top of output |
1187
+ | `M-<` | No | move to the top of output |
1188
+ | `g` | Yes | move to the top, or to the counted original line\* |
1189
+ | `end` | No | move to the end of output |
1190
+ | `M->` | No | move to the end of output |
1191
+ | `G` | No | move to the end of output |
1192
+ | `=` | No | toggle the display of line numbers |
1193
+ | `M-s` | No | toggle between paging and scrolling |
1194
+ | `/` | No | initiate a fixed-string search forward |
1195
+ | `?` | No | initiate a fixed-string search backward |
1196
+ | `C-s` | No | initiate an Isearch session forward |
1197
+ | `C-r` | No | initiate an Isearch session backward |
1198
+ | `C-M-s` | No | initiate an Regexp session forward |
1199
+ | `C-M-r` | No | initiate an Regexp session backward |
1200
+ | `C-/` | No | initiate an Regexp session forward |
1201
+ | `<` | No | narrow output to lines containing all entered terms |
1202
+ | `>` | No | widen output by clearing the narrowing |
1203
+ | `C-c` | No | quit paging |
1204
+ | `q` | No | quit paging |
1205
+ | `C-g` | No | quit paging |
1206
+ | `escape` | No | quit paging |
1207
+ | `n` | Yes | move to the next instance of the current search |
1208
+ | `N` | Yes | move to the prior instance of the current search |
1209
+
1210
+ `*` Without a count, `g` moves to the beginning of output. With a numeric prefix, it moves to that original output line; for example, `M-1 M-0 M-0 M-0 g` moves to line 1000. It uses the original line numbers even when output is narrowed.
1211
+
1212
+ As you can see, `fatty` tries to accommodate a number of finger habits besides `emacs`, including `vim` and `Emacs Info`, by offering several alternative ways to invoke navigation actions.
1213
+
1214
+ One unusual action is invoked by `M-s` which toggles the output between scrolling mode without pause and paging mode. It can be invoked while doing either to enter the other mode.
1215
+
1216
+ You can add line numbers to the display while paging by using the `=` key.
1217
+
1218
+ You can also "narrow" the output with `<`, which will prompt you for one or more terms to match. The output will then show only lines that match all the terms typed. To return to the full output, "widen" with `>`.
1219
+
1220
+
1221
+ <a id="org89f3027"></a>
1222
+
1223
+ ### Searching Output
1224
+
1225
+ While paging, you can initiate a search of the output as indicated in the above key bindings for paging. Once a search session is initiated, `fatty` presents an input line for entering search terms.
1226
+
1227
+ After a search session has ended, `fatty` returns to paging but remembers the last search term and allows navigation among them with `n` and `N`: `n` searches in the direction of the last search and `N` searches in the opposite direction. Notice that the current search term and direction are added to the paging bar after the information on your location within the output.
14
1228
 
15
- <a id="orga462d10"></a>
16
1229
 
17
- # Introduction
1230
+ <a id="org77137d8"></a>
1231
+
1232
+ #### String and Regex Search
1233
+
1234
+ `/` and `?` prompt for a fixed string and search forward or backward. `C-M-s` (or `C-/`) and `C-M-r` (or `C-?`) perform the corresponding regular-expression searches. Accepting the search leaves the selected match active for later navigation with `n` and `N`.
1235
+
1236
+
1237
+ <a id="orgd90c796"></a>
1238
+
1239
+ #### Incremental Search
1240
+
1241
+ `C-s` and `C-r` begin forward or backward incremental search. The output moves to matching text as the search term is edited. Accepting commits the match; canceling restores the output position from before the search began.
1242
+
1243
+
1244
+ <a id="orgc8458e1"></a>
1245
+
1246
+ # Configuration
1247
+
1248
+ When a Fatty application starts, Fatty installs default configuration files under `~/.config/fatty` if they do not already exist. Fatty does not overwrite files that are already there, so you can edit these files directly and keep them under version control.
1249
+
1250
+ The config files are:
1251
+
1252
+ - **`config.yml`:** general configuration;
1253
+ - **`keydefs.yml`:** terminal-specific keycode naming;
1254
+ - **`keybindings.yml`:** user-defined keybindings;
1255
+ - **`themes/`:** YAML theme files.
1256
+
1257
+ The default user-specific configuration directory follows `XDG_CONFIG_HOME` if it is set. Otherwise it is `~/.config/fatty`.
1258
+
1259
+ In addition to the user-specific configuration, fatty will read app-specific configuration files from `~/.config/fatty/apps/<app_name>` if `Terminal` was given an `app_name` parameter. It will use a different directory of your choosing if `Terminal` was given an `app_config_dir` directory name.
1260
+
1261
+
1262
+ <a id="org0add8c9"></a>
1263
+
1264
+ ## General Configuration `config.yml`
1265
+
1266
+ `config.yml` controls general behavior such as word movement, ESC handling, logging, history, the initial theme, and truecolor rendering.
1267
+
1268
+ A small configuration might look like this:
1269
+
1270
+ ```yaml
1271
+ word_char_re: "[[:alnum:]_.:-]"
1272
+
1273
+ esc_delay: 0
1274
+
1275
+ history:
1276
+ file: "~/.fatty_history"
1277
+ max: 10000
1278
+
1279
+ theme: wordperfect
1280
+
1281
+ truecolor: auto
1282
+ ```
1283
+
1284
+
1285
+ <a id="org1099eb8"></a>
1286
+
1287
+ ### `word_char_re`
1288
+
1289
+ This setting is a Ruby regular expression fragment used to decide what counts as a word in the input buffer. It must be a `String` representing a `Regexp` that matches a single character that is meant to be a part of a "word" for purposes of cursor movement. The default setting treats letters, digits, and underscore as word characters:
1290
+
1291
+ ```yaml
1292
+ word_char_re: "[[:alnum:]_]"
1293
+ ```
1294
+
1295
+ For command-line applications, you may want dashes to be part of a word:
1296
+
1297
+ ```yaml
1298
+ word_char_re: "[[:alnum:]_-]"
1299
+ ```
1300
+
1301
+ For Ruby constants, hostnames, or command-like tokens, you may want a broader definition:
1302
+
1303
+ ```yaml
1304
+ word_char_re: "[[:alnum:]_.:-]"
1305
+ ```
1306
+
1307
+
1308
+ <a id="orgce3e466"></a>
1309
+
1310
+ ### `esc_delay`
1311
+
1312
+ `esc_delay` controls how long curses waits after receiving `ESC` before deciding whether another key is part of the same Meta-key sequence. The value is in milliseconds.
1313
+
1314
+ ```yaml
1315
+ esc_delay: 0
1316
+ ```
1317
+
1318
+
1319
+ <a id="org95b5e48"></a>
1320
+
1321
+ ### `history`
1322
+
1323
+
1324
+ <a id="orgdc30f58"></a>
1325
+
1326
+ #### `file`
1327
+
1328
+ The path to the history file to use, by default `~/.fatty_history`
1329
+
1330
+
1331
+ <a id="org0941468"></a>
1332
+
1333
+ #### `max`
1334
+
1335
+ The maximum number of lines of history retained, by default 10,000.
1336
+
1337
+
1338
+ <a id="orgce1b37d"></a>
1339
+
1340
+ ### `theme`
1341
+
1342
+ The `theme` setting names the theme Fatty applies at startup:
1343
+
1344
+ ```yaml
1345
+ theme: nordic
1346
+ ```
1347
+
1348
+
1349
+ <a id="orgdd529bd"></a>
1350
+
1351
+ ### `truecolor`
1352
+
1353
+ The `truecolor` setting controls whether Fatty uses truecolor escape sequences for rendering colors. The usual setting is `auto`, meaning that `truecolor` is enabled if available. It can be set to `true`, `false`, `on`, `off` or `auto`.
1354
+
1355
+ ```yaml
1356
+ truecolor: auto
1357
+ ```
1358
+
1359
+
1360
+ <a id="org2b58449"></a>
1361
+
1362
+ ### `log`
1363
+
1364
+ Logging is configured under `log`:
1365
+
1366
+ ```yaml
1367
+ log:
1368
+ file: "~/log/fatty.log"
1369
+ level: debug
1370
+ tags:
1371
+ - keycode
1372
+ - keyevent
1373
+ - keybinding
1374
+ - action
1375
+ - command
1376
+ - session
1377
+ - render
1378
+ - perf
1379
+ ```
1380
+
1381
+
1382
+ <a id="org0e3b938"></a>
1383
+
1384
+ #### `file`
1385
+
1386
+ The path to the log file to use.
1387
+
1388
+
1389
+ <a id="org9c70445"></a>
1390
+
1391
+ #### `level`
1392
+
1393
+ Can be one of the following
1394
+
1395
+ - **`info`:** only informational messages
1396
+ - **`warn`:** only non-fatal warning messages and `info` messages
1397
+ - **`error`:** only fatal error messages and `warn` and `info` messages
1398
+ - **`debug`:** detailed messages to assist debugging `fatty` plus all of the above.
1399
+
1400
+
1401
+ <a id="orge47f304"></a>
1402
+
1403
+ #### `tags`
1404
+
1405
+ `log.tags` controls which tagged log messages are written. Tags are ordinary symbolic labels used by Fatty's logging calls; they are not checked against a fixed registry. A tag is useful if Fatty logs messages with that tag.
1406
+
1407
+ The special tag `all` enables all tagged log messages except `perf`. Performance logging is intentionally noisy and must be enabled explicitly with `perf`.
1408
+
1409
+ ```yaml
1410
+ log:
1411
+ tags:
1412
+ - keycode
1413
+ - keybinding
1414
+ - render
1415
+ - perf
1416
+ ```
1417
+
1418
+
1419
+ <a id="org220de42"></a>
1420
+
1421
+ ## Key code definitions `keydefs.yml`
1422
+
1423
+ Most keys are named correctly by curses. Some terminal emulators, multiplexers, or keyboard modes emit numeric keycodes that curses does not name usefully. The `keydefs.yml` file lets you assign names and modifiers to those numeric codes.
1424
+
1425
+ When Fatty runs inside `tmux`, key definitions are looked up under `tmux`, not under the outer terminal emulator. `tmux` translates the outer terminal's input before Fatty sees it, so keycodes observed inside `tmux` may differ from keycodes observed directly under GNOME Terminal, Kitty, Alacritty, or another terminal. Use `keytest` in the same environment where the application will normally run. The same comment applies to `screen`.
1426
+
1427
+ A key definition is terminal-specific. For example:
1428
+
1429
+ ```yaml
1430
+ tmux:
1431
+ 555:
1432
+ key: left
1433
+ meta: true
1434
+ 570:
1435
+ key: right
1436
+ meta: true
1437
+ 557:
1438
+ key: left
1439
+ ctrl: true
1440
+ 572:
1441
+ key: right
1442
+ ctrl: true
1443
+ ```
1444
+
1445
+ This says that, when Fatty detects that the terminal is `tmux`, curses keycode `555` should be treated as `M-left`, keycode `570` as `M-right`, and so on.
1446
+
1447
+ The demo executable has a `keytest` command that is useful when building this file. Run `fatty`, use the `keytest` command, press the key you want to identify, and add an entry for the numeric code if Fatty does not already know what to call it.
1448
+
1449
+ Here is what I found running `keytest` on a variety of popular terminals to determine how each reported `PageDown`, `C-PageDown`, `M-PageDown`, and `C-M-PageDown`:
1450
+
1451
+ | Terminal | Key Pressed | Ctrl? | Meta? | Code | Status |
1452
+ |-------------- |----------- |----- |----- |------ |----------------- |
1453
+ | gnome-terminal | PageDown | no | no | 338 | Defined and bound |
1454
+ | gnome-terminal | PageDown | yes | no | 559 | Undefined |
1455
+ | gnome-terminal | PageDown | no | yes | 557 | Undefined |
1456
+ | gnome-terminal | PageDown | yes | yes | 561 | Undefined |
1457
+ | alacritty | PageDown | no | no | 338 | Defined and bound |
1458
+ | alacritty | PageDown | yes | no | 559 | Undefined |
1459
+ | alacritty | PageDown | no | yes | 557 | Undefined |
1460
+ | alacritty | PageDown | yes | yes | 561 | Undefined |
1461
+ | kitty | PageDown | no | no | 338 | Defined and bound |
1462
+ | kitty | PageDown | yes | no | 565 | Undefined |
1463
+ | kitty | PageDown | no | yes | 563 | Undefined |
1464
+ | kitty | PageDown | yes | yes | 567 | Undefined |
1465
+ | terminator | PageDown | no | no | 338 | Defined and bound |
1466
+ | terminator | PageDown | yes | no | <none> | Undefinable |
1467
+ | terminator | PageDown | no | yes | 557 | Undefined |
1468
+ | terminator | PageDown | yes | yes | 561 | Undefined |
1469
+ | ghostty | PageDown | no | no | 338 | Defined and bound |
1470
+ | ghostty | PageDown | yes | no | 564 | Undefined |
1471
+ | ghostty | PageDown | no | yes | 562 | Undefined |
1472
+ | ghostty | PageDown | yes | yes | 566 | Undefined |
1473
+ | konsole | PageDown | no | no | 338 | Defined and bound |
1474
+ | konsole | PageDown | yes | no | 559 | Undefined |
1475
+ | konsole | PageDown | no | yes | 557 | Undefined |
1476
+ | konsole | PageDown | yes | yes | 561 | Undefined |
1477
+
1478
+ I could turn this into a `keydefs.yml` file that would allow me to define all those keys:
1479
+
1480
+ ```yaml
1481
+ gnome-terminal:
1482
+ 559:
1483
+ key: page_down
1484
+ ctrl: true
1485
+ 557:
1486
+ key: page_down
1487
+ meta: true
1488
+ 561:
1489
+ key: page_down
1490
+ ctrl: true
1491
+ meta: true
1492
+
1493
+ alacritty:
1494
+ 559:
1495
+ key: page_down
1496
+ ctrl: true
1497
+ 557:
1498
+ key: page_down
1499
+ meta: true
1500
+ 561:
1501
+ key: page_down
1502
+ ctrl: true
1503
+ meta: true
1504
+
1505
+ kitty:
1506
+ 565:
1507
+ key: page_down
1508
+ ctrl: true
1509
+ 563:
1510
+ key: page_down
1511
+ meta: true
1512
+ 567:
1513
+ key: page_down
1514
+ ctrl: true
1515
+ meta: true
1516
+
1517
+ terminator:
1518
+ # 559:
1519
+ # key: page_down
1520
+ # ctrl: true
1521
+ 557:
1522
+ key: page_down
1523
+ meta: true
1524
+ 561:
1525
+ key: page_down
1526
+ ctrl: true
1527
+ meta: true
1528
+
1529
+ ghostty:
1530
+ 564:
1531
+ key: page_down
1532
+ ctrl: true
1533
+ 562:
1534
+ key: page_down
1535
+ meta: true
1536
+ 566:
1537
+ key: page_down
1538
+ ctrl: true
1539
+ meta: true
1540
+
1541
+ konsole:
1542
+ 559:
1543
+ key: page_down
1544
+ ctrl: true
1545
+ 557:
1546
+ key: page_down
1547
+ meta: true
1548
+ 561:
1549
+ key: page_down
1550
+ ctrl: true
1551
+ meta: true
1552
+ ```
1553
+
1554
+ After installing that keydefs, I find that the PageDown variants are all recognized by name and modifiers (except for the strange failure of `terminator` to recognize `C-PageDown`!).
1555
+
1556
+
1557
+ <a id="orgbd79429"></a>
1558
+
1559
+ ## Key bindings `keybindings.yml`
1560
+
1561
+ Fatty has built-in Emacs-style bindings for ordinary input editing, history, completion, popup navigation, and paging. The `keybindings.yml` file lets you add or override bindings without changing Ruby code.
1562
+
1563
+ A keybinding names a key chord, the context in which it applies, and the Fatty action to run:
1564
+
1565
+ ```yaml
1566
+ - key: left
1567
+ meta: true
1568
+ action: move_word_left
1569
+
1570
+ - key: right
1571
+ meta: true
1572
+ action: move_word_right
1573
+
1574
+ - key: space
1575
+ context: paging
1576
+ action: page_down
1577
+
1578
+ - key: backspace
1579
+ context: paging
1580
+ action: page_up
1581
+ ```
1582
+
1583
+
1584
+ <a id="org698f2a9"></a>
1585
+
1586
+ ### Key Names
1587
+
1588
+ A keybinding uses the unmodified key name under the `key` field:
1589
+
1590
+ ```yaml
1591
+ - key: f
1592
+ meta: true
1593
+ context: text
1594
+ action: move_word_right
1595
+ ```
1596
+
1597
+
1598
+ <a id="org58581e4"></a>
1599
+
1600
+ #### Printable Keys
1601
+
1602
+ Most printable keys can be written as the character itself. Quote keys that YAML might otherwise treat specially or make hard to read, such as `":"`, `"#"`, `"["`, `"]"`, `"{"`, `"}"`, `"\""`, and `"\\"`.
1603
+
1604
+ ```yaml
1605
+ - key: a
1606
+ - key: /
1607
+ - key: .
1608
+ - key: =
1609
+ - key: "\\"
1610
+ - key: "\""
1611
+ ```
1612
+
1613
+
1614
+ <a id="org4b0cf5b"></a>
1615
+
1616
+ #### Curses and Fatty Named Keys
1617
+
1618
+ `Fatty` translates codes returned by curses for special keys into names:
1619
+
1620
+ | | | | | |
1621
+ |-------------- |-------------- |--------------- |----------------- |-------------- |
1622
+ | `tab` | `insert` | `delete` | `home` | `end` |
1623
+ | `left` | `right` | `page_up` | `page_down` | `up` |
1624
+ | `down` | `f1` | `f2` | `f3` | `f4` |
1625
+ | `f8` | `f9` | `f10` | `f11` | `f12` |
1626
+ | `keypad_0` | `keypad_1` | `keypad_2` | `keypad_3` | `keypad_4` |
1627
+ | `keypad_8` | `keypad_9` | `keypad_divide` | `keypad_multiply` | `keypad_minus` |
1628
+ | `keypad_enter` | `keypad_comma` | | | |
1629
+
1630
+ Some terminals can report application-keypad sequences such as `keypad_divide`, `keypad_multiply`, or `keypad_enter`, but numeric keypad digits are often not distinguishable from ordinary digits when NumLock is on, and are often reported as navigation keys such as `end`, `down`, `page_down`, or `left` when NumLock is off. Use `keytest` to see what your terminal actually sends.
1631
+
1632
+ `Fatty` also provides convenient names to other common keys:
1633
+
1634
+ - `enter`,
1635
+ - `escape`, and
1636
+ - `space`
1637
+
1638
+
1639
+ <a id="orgeab9ebe"></a>
1640
+
1641
+ #### Possibly Invisible Keys
1642
+
1643
+ Some physical keys may never reach Fatty at all. They may be handled by the keyboard firmware, the kernel, the desktop environment, the window manager, or the terminal emulator before curses can report them. Common examples include `Print Screen`, `Scroll Lock`, `Pause/Break`, `Caps Lock`, `Num Lock`, `Fn`, `Menu/Application`, `Super/Windows`, power/sleep/wake keys, brightness keys, volume/media keys, keyboard-backlight keys, airplane-mode keys, and touchpad toggle keys.
1644
+
1645
+ Use `keytest` to check. If Fatty sees a key as a raw numeric code, you can name it in `keydefs.yml` and bind it. If `keytest` shows nothing, the key is being intercepted before Fatty receives it.
1646
+
1647
+
1648
+ <a id="orgde02d1d"></a>
1649
+
1650
+ #### Unnamed but Recognized Keys
1651
+
1652
+ Some terminals report modified special keys as distinct numeric keycodes that `fatty` and `curses` do not name. Use `keytest` to discover those codes, then assign names to them in `keydefs.yml`. The name can be one of Fatty's usual names, or a new name of your own.
1653
+
1654
+ For example, if a keyboard has an extra thumb key and `keytest` reports it as an uncoded keycode `601`, you could name it:
1655
+
1656
+ ```yaml
1657
+ gnome-terminal:
1658
+ 601:
1659
+ key: thumb_left
1660
+ ```
1661
+
1662
+ Then bind it in `keybindings.yml`:
1663
+
1664
+ ```yaml
1665
+ - key: thumb_left
1666
+ context: text
1667
+ action: move_word_left
1668
+ ```
1669
+
1670
+ Fatty does not require custom key names to come from a fixed registry. A custom name only needs to be used consistently between `keydefs.yml` and `keybindings.yml`.
1671
+
1672
+
1673
+ <a id="orge487bc7"></a>
1674
+
1675
+ ### Mouse Events
1676
+
1677
+ Mouse events can also be bound:
1678
+
1679
+ ```yaml
1680
+ - button: left_clicked
1681
+ context: popup
1682
+ action: choose
1683
+ ```
1684
+
1685
+ Recognized mouse button names include:
1686
+
1687
+ ```org
1688
+ left_pressed
1689
+ left_released
1690
+ left_clicked
1691
+ left_double_clicked
1692
+ left_triple_clicked
1693
+ middle_pressed
1694
+ middle_released
1695
+ middle_clicked
1696
+ middle_double_clicked
1697
+ middle_triple_clicked
1698
+ right_pressed
1699
+ right_released
1700
+ right_clicked
1701
+ right_double_clicked
1702
+ right_triple_clicked
1703
+ scroll_up
1704
+ scroll_down
1705
+ ```
1706
+
1707
+
1708
+ <a id="orgcf36a39"></a>
1709
+
1710
+ ### Modifiers
1711
+
1712
+ A binding may include these modifiers:
1713
+
1714
+ ```yaml
1715
+ ctrl: true
1716
+ meta: true
1717
+ shift: true
1718
+ ```
1719
+
1720
+
1721
+ <a id="orgc29aad9"></a>
1722
+
1723
+ ### Contexts
1724
+
1725
+ In `Fatty`, the key map is partitioned by "contexts" and in different environments the contexts are tried in order from highest priority to lowest and the first binding found wins. The following table lists the contexts associated with each interaction environment:
1726
+
1727
+ | Environment | Contexts High to Low |
1728
+ |------------------ |--------------------------------------- |
1729
+ | Input Field | :input, :text, :terminal |
1730
+ | Output Paging | :paging, :terminal |
1731
+ | Output Scrolling | :terminal |
1732
+ | String Search | :search, :text, :terminal |
1733
+ | Incremental Search | :isearch, :text, :terminal |
1734
+ | Prompt Popup | :prompt, :text, :terminal |
1735
+ | Popup Choose | :popup, :text, :terminal |
1736
+ | Popup Multi-Choose | :popup\_multi, :popup, :text, :terminal |
1737
+
1738
+ The available contexts are:
1739
+
1740
+ | Context | Meaning |
1741
+ |-------------- |----------------------------------------------------- |
1742
+ | `:terminal` | global terminal/session commands |
1743
+ | `:text` | ordinary text-editing commands shared by input fields |
1744
+ | `:input` | the main command input field |
1745
+ | `:paging` | output pager navigation |
1746
+ | `:search` | non-incremental search field |
1747
+ | `:isearch` | incremental search field |
1748
+ | `:prompt` | prompt popup input |
1749
+ | `:popup` | popup selection/navigation |
1750
+ | `:popup_multi` | multi-select popup behavior |
1751
+
1752
+ The \`:terminal\` context is the lowest-priority fallback context for terminal-wide commands. It is where Fatty puts bindings that are not specific to text editing, paging, search, or popup selection, such as theme selection, key testing, and quitting. Because \`:terminal\` is searched last, any more specific context can override a terminal-wide binding.
1753
+
1754
+
1755
+ <a id="orgdb5aa78"></a>
1756
+
1757
+ ### Actions
1758
+
1759
+ A keybinding connects a key event in a context to an "action." An action is a named Fatty command such as \`move\_left\`, \`delete\_backward\`, \`accept\_line\`, \`page\_down\`, \`choose\_theme\`, or \`quit\`.
1760
+
1761
+ A binding entry names the action with the \`action\` field:
1762
+
1763
+ ```yaml
1764
+ - key: left
1765
+ context: text
1766
+ action: move_left
1767
+
1768
+ - key: f
1769
+ meta: true
1770
+ context: text
1771
+ action: move_word_right
1772
+
1773
+ - key: page_down
1774
+ context: paging
1775
+ action: page_down
1776
+ ```
1777
+
1778
+ Most movement, deletion, history, completion, paging, and popup-navigation actions honor Fatty's numeric count argument. The count is entered before the command, Emacs-style. For example, `M-5 C-f` moves forward five characters if `C-f` is bound to move\_right, and `M-3 C-n` moves down three items if `C-n` is bound to the current environment's `next-item` action.
1779
+
1780
+ Use \`keytest\` to check what action is currently bound to a key. If a key is bound, \`keytest\` reports the matching context, action name, arguments, and the object that handles the action.
1781
+
1782
+
1783
+ <a id="orgf51a408"></a>
1784
+
1785
+ #### Input buffer actions
1786
+
1787
+ Input buffer actions edit the contents of a text buffer. These are the ordinary line-editing operations shared by the main input field, search fields, prompt popups, and other text-like interactions. Many of these actions honor the numeric prefix count.
1788
+
1789
+ Terminology notes:
1790
+
1791
+ - **kill ring:** like emacs, `fatty` keeps a "clipboard" of items with the newest item in the current position and older items "before" it but in a "ring" that return to the beginning;
1792
+ - **yank:** retrieve the most recent item from the kill ring
1793
+ - **yank pop:** retrieve successively older items from the kill ring
1794
+ - **delete:** removes text without copying it to the kill ring;
1795
+ - **kill:** removes text and copies it to the kill ring for later yank;
1796
+ - **word:** a sequence of contiguous characters that are each matched by the `word_char_re` from the config file or "[[:alnum]\_]" by default;
1797
+
1798
+ | Action Name | Count? | Description |
1799
+ |---------------------- |------ |------------------------------------------ |
1800
+ | bol | No | Move cursor to beginning of buffer |
1801
+ | clear | No | Clear the buffer |
1802
+ | clear\_mark | No | Clear the region mark |
1803
+ | copy\_region | No | Copy the text in the region to kill ring |
1804
+ | delete\_char\_backward | Yes | Delete char to the left of cursor |
1805
+ | delete\_char\_forward | Yes | Delete char to the right of cursor |
1806
+ | delete\_region | No | Delete the text in the region |
1807
+ | eol | No | Move cursor to end of buffer |
1808
+ | kill\_region | No | Kill the text in the region |
1809
+ | kill\_to\_bol | No | Kill the text from cursor to beginning |
1810
+ | kill\_to\_eol | No | Kill the text from cursor to end |
1811
+ | kill\_word\_backward | Yes | Kill the word to the left of cursor |
1812
+ | kill\_word\_forward | Yes | Kill the word to the right of cursor |
1813
+ | move\_left | Yes | Move cursor one char left |
1814
+ | move\_right | Yes | Move cursor one char right |
1815
+ | move\_word\_left | Yes | Move cursor one word left |
1816
+ | move\_word\_right | Yes | Move cursor one word right |
1817
+ | redo | No | Redo an undone action |
1818
+ | set\_mark | No | Set the region mark at cursor |
1819
+ | transpose\_chars | Yes | Switch position of char with prior char |
1820
+ | transpose\_words | Yes | Switch position of word with prior char |
1821
+ | undo | No | Revert last edit action |
1822
+ | yank | No | Copy kill ring to buffer at cursor |
1823
+ | yank\_pop | Yes | Copy next kill ring item and replace prior |
1824
+
1825
+
1826
+ <a id="org3f876b8"></a>
1827
+
1828
+ #### Input field actions
1829
+
1830
+ Input field actions operate on an editable input field. An input field occurs at the main input prompt, but also in things like search-narrowing input, incremental-search, a prompt for text, etc. It is lower level than the shell session input described in the next section. In particular, it can have its own history attached to it depending on its purpose. They handle accepting the current line, moving through history, and receiving pasted text.
1831
+
1832
+ | Action Name | Count? | Description |
1833
+ |------------- |------ |--------------------------------------------------------- |
1834
+ | accept\_line | No | Accept the current text as the input |
1835
+ | history\_prev | No | Insert the prior relevant history item in the input field |
1836
+ | history\_next | No | Insert the next relevant history item in the input field |
1837
+
1838
+
1839
+ <a id="orge0c4e6e"></a>
1840
+
1841
+ #### Shell session actions
1842
+
1843
+ Shell session actions operate at the session level around the main command input and output area. They submit input, interrupt running work, clear output, start completion, open completion/history popups, and manage numeric prefix entry.
1844
+
1845
+ | Action Name | Count? | Description |
1846
+ |-------------------- |------ |---------------------------------------------------------------- |
1847
+ | submit\_line | No | Pass the current text to the on\_accept proc and page output |
1848
+ | submit\_and\_scroll | No | Pass the current text to the on\_accept proc and scroll output |
1849
+ | interrupt | No | End the current session |
1850
+ | interrupt\_if\_empty | No | End the current session only if the current text is empty |
1851
+ | clear\_output | No | Clear the output pane |
1852
+ | complete | No | Suggest the next possible completion as an autosuggestion |
1853
+ | complete\_previous | No | Suggest the prior possible completion as an autosuggestion |
1854
+ | completion\_popup | No | Display all possible completions in a popup for selection |
1855
+ | history\_search | No | Display history item starting with the text-so-far for selection |
1856
+ | count\_digit | No | Add a digit to the numeric prefix |
1857
+ | universal\_argument | No | Set the numeric argument to 4 or multiply the current one by 4 |
1858
+
1859
+
1860
+ <a id="org05813c6"></a>
1861
+
1862
+ #### Pager actions
1863
+
1864
+ Pager actions navigate output when the output area is in paging mode. They move by line, page, top, bottom, or end of output, handle mouse-wheel scrolling, and switch between paging and scrolling behavior. Most movement actions honor the numeric prefix count.
1865
+
1866
+ | Action Name | Count? | Description |
1867
+ |--------------------- |------ |------------------------------------------------ |
1868
+ | page\_up | Yes | Move the output view one page up |
1869
+ | page\_down | Yes | Move the output view one page down |
1870
+ | end\_of\_output | No | Move the output to the end of output and scroll |
1871
+ | line\_up | Yes | Move the output view one line up |
1872
+ | line\_down | Yes | Move the output view one line down |
1873
+ | scroll\_up | Yes | Move the output view six lines up |
1874
+ | scroll\_down | Yes | Move the output view six lines down |
1875
+ | page\_top | Yes | Move to the beginning, or to the counted line |
1876
+ | page\_bottom | No | Move the output to the end of output |
1877
+ | paging\_to\_scrolling | No | Quit paging and scroll output as it is produced |
1878
+ | toggle\_paging | No | Toggle output mode between paging and scrolling |
1879
+ | quit\_paging | No | Quit paging and return control to the input line |
1880
+
1881
+
1882
+ <a id="orgea58518"></a>
1883
+
1884
+ #### Pager search actions
1885
+
1886
+ Pager search actions can *initiate* a search session within the pager: a string search, a regular expression search, or an incremental-search. Once a search session is entered, other actions navigate matches in the output. There are also actions to end a search session.
1887
+
1888
+ While the search session is active, accepting commits the current match and canceling restores the pager to the position it had when the search began. After the search is accepted, `n` and `N` repeat the committed search in the pager; at that point the search session is no longer active.
1889
+
1890
+ | Action Name | Count? | Description |
1891
+ |------------------------------ |------ |------------------------------------------------------------------- |
1892
+ | pager\_search\_forward | No | Initiate a string search forward from current output point |
1893
+ | pager\_search\_backward | No | Initiate a string search backward from current output point |
1894
+ | pager\_regex\_search\_forward | No | Initiate a regex search forward from current output point |
1895
+ | pager\_regex\_search\_backward | No | Initiate a regex search backward from current output point |
1896
+ | pager\_isearch\_forward | No | Initiate an incremental search forward from current output point |
1897
+ | pager\_isearch\_backward | No | Initiate an incremental search backward from current output point |
1898
+ | pager\_search\_next | No | Move output to the next search match |
1899
+ | pager\_search\_prev | No | Move output to the prior search match |
1900
+ | search\_toggle\_regex | No | Toggle between regex and string search |
1901
+ | search\_step\_forward | No | Preview the next match using the current search text |
1902
+ | search\_step\_backward | No | Preview the prior match using the current search text |
1903
+ | isearch\_next | No | Move output to the next isearch match |
1904
+ | isearch\_prev | No | Move output to the prior isearch match |
1905
+ | search\_accept | No | Commit the current search and close the search session |
1906
+ | search\_cancel | No | Cancel the active search session and restore its starting point |
1907
+ | isearch\_accept | No | Commit the current incremental search and close the search session |
1908
+ | isearch\_cancel | No | Cancel the active incremental search and restore its starting point |
1909
+
1910
+
1911
+ <a id="org6ea9ecc"></a>
1912
+
1913
+ #### Popup actions
1914
+
1915
+ Popup actions control selection and narrowing popups such as choose, menu, completion, and history popups. You can narrow the selections presented by typing in the input field presented. You can move through the list, accept or cancel the selection, page through choices, jump to the top or bottom, recenter the selection, and toggle selection in multi-select popups.
1916
+
1917
+ | Action Name | Count? | Description |
1918
+ |----------------------- |------ |--------------------------------------------------------- |
1919
+ | popup\_cancel | No | Close the popup and return its cancel\_value |
1920
+ | popup\_accept | No | Close the popup and return the selection |
1921
+ | popup\_next | No | Move the selection cursor to the next item |
1922
+ | popup\_prev | No | Move the selection cursor to the prior item |
1923
+ | popup\_page\_down | No | Move the selection list down one page |
1924
+ | popup\_page\_up | No | Move the selection list up one page |
1925
+ | popup\_top | No | Move to the top of the selection list |
1926
+ | popup\_bottom | No | Move to the bottom of the selection list |
1927
+ | popup\_recenter | No | Recenter the selection list on the current item |
1928
+ | popup\_toggle\_selected | No | In multi-select, toggle the selection of the current item |
1929
+
1930
+
1931
+ <a id="org8f3d6dc"></a>
1932
+
1933
+ #### Prompt popup actions
1934
+
1935
+ Prompt popup actions control prompt dialogs that collect a string from the user. They accept the prompt, cancel it, or cancel only when the prompt is empty.
1936
+
1937
+ | Action Name | Count? | Description |
1938
+ |------------------------- |------ |---------------------------------------------------- |
1939
+ | prompt\_accept | No | Close the prompt popup and return the current string |
1940
+ | prompt\_cancel | No | Close the prompt popup and return its cancel\_value |
1941
+ | prompt\_cancel\_if\_empty | No | Cancel only if the input buffer is empty |
1942
+
1943
+
1944
+ <a id="org4a9e2d4"></a>
1945
+
1946
+ #### Utility Actions
1947
+
1948
+ Some actions exist to support `Fatty`'s own input machinery and are not normally useful as ordinary user bindings. For example, `insert`, `replace`, and `paste` need text supplied by code: `paste` is used for bracketed paste events from the terminal and normalizes pasted text for a one-line input field; `self_insert` is selected automatically for printable characters. They are listed here for completeness.
1949
+
1950
+ | Action Name | Args | Count? | Description |
1951
+ |------------ |---- |------ |---------------------------------- |
1952
+ | insert | str | Yes | Insert the string at point |
1953
+ | self\_insert | | Yes | Insert the bound key at point |
1954
+ | replace | str | No | Replace the buffer with the string |
1955
+ | set | str | No | Alias for `replace` |
1956
+ | paste | str | No | Insert pasted text at point |
1957
+
1958
+
1959
+ <a id="org7e9ed87"></a>
1960
+
1961
+ ## Themes `themes/`
1962
+
1963
+
1964
+ <a id="orgf951ab7"></a>
1965
+
1966
+ ### Distributed Themes
1967
+
1968
+ As distributed, `fatty` comes with the following themes, most of which are based on popular themes used in many text editors and IDEs.
1969
+
1970
+ Within a `fatty` terminal, themes can be changed interactively. The default Emacs-style bindings include:
1971
+
1972
+ | Key | Action |
1973
+ |------- |--------------------------------------- |
1974
+ | `C-M-t` | cycle to the next loaded theme |
1975
+ | `M-=` | preview and choose a theme from a popup |
1976
+
1977
+ Here are the builtin themes:
1978
+
1979
+ | Name | Description |
1980
+ |----------------- |-------------------------------------------------- |
1981
+ | capuchin\_monk | A tribute to St. Francis of Assisi |
1982
+ | catppuccin\_latte | Pastel theme of the latte flavor |
1983
+ | catppuccin\_mocha | Pastel theme of the mocha flavor |
1984
+ | cyberpunk | Brash colorful theme |
1985
+ | dracula | Dark theme inspired by the Count |
1986
+ | everforest\_dark | Dark green-esque theme |
1987
+ | gruvbox\_dark | Engineered for focus, built for developers |
1988
+ | gruvbox\_light | Engineered for focus, built for developers |
1989
+ | mono | Minimal black-and-white theme |
1990
+ | monokai | Based on <https://monokai.pro/history> |
1991
+ | nordic | Shiver me timbers, it's northern |
1992
+ | onedark | Shameless imitation of Atom editor theme |
1993
+ | solarized\_dark | Ethan Schoonover's famous terminal theme |
1994
+ | solarized\_light | And its light variant |
1995
+ | terminal | Use the terminal's color scheme |
1996
+ | tokyo\_night | Celebrates the neon lights and dark vibes of Tokyo |
1997
+ | wordperfect | Nostalgic nod to the good old word processor |
1998
+
1999
+
2000
+ <a id="orgcf6fbb1"></a>
2001
+
2002
+ ### Custom Themes
2003
+
2004
+
2005
+ <a id="orgb8ead89"></a>
2006
+
2007
+ #### Theme Name
2008
+
2009
+ A Fatty theme is a YAML file in the user-specific config directory (normally `~/.config/fatty/themes`) or the app-specific config directory (by default `~/.config/fatty/apps/<app_name>/themes`). The filename does not determine the theme name: the theme file must have a `name` field to set the name:
2010
+
2011
+ ```yaml
2012
+ name: nordic
2013
+ ```
2014
+
2015
+
2016
+ <a id="orgc49dfe9"></a>
2017
+
2018
+ #### Inheritance from Another Theme
2019
+
2020
+ Themes can inherit from other themes with the `inherit:` tag:
2021
+
2022
+ ```yaml
2023
+ name: my_nordic
2024
+ inherit: nordic
2025
+
2026
+ output:
2027
+ fg: "#eeeeee"
2028
+ ```
2029
+
2030
+ This means that all the theme setting from the `nordic` theme apply except to the extent changed by the rest of the `my_nordic` theme file.
2031
+
2032
+
2033
+ <a id="org371d3bb"></a>
2034
+
2035
+ #### Inheritance within a Theme
2036
+
2037
+ Not only may one theme inherit from another theme, some roles within a theme inherit from other roles. For example, the `input` role inherits from the `output` role.
2038
+
2039
+ Thus, a minimal theme can define only `output` and inherit everything else through Fatty's role defaults:
2040
+
2041
+ ```yaml
2042
+ name: simple_dark
2043
+ inherit: null
2044
+
2045
+ output:
2046
+ fg: white
2047
+ bg: black
2048
+ ```
2049
+
2050
+ This means that the input field will have the same coloring and attributes as the output pane.
2051
+
2052
+
2053
+ <a id="org83d0479"></a>
2054
+
2055
+ #### Theme Roles
2056
+
2057
+ Here is a listing of what roles act as the parent for each of the roles, their purpose, and the role, if any, from which they inherit.
2058
+
2059
+ | Role name | Used for | Parent Role |
2060
+ |----------------------- |----------------------------------------- |--------------------- |
2061
+ | `output` | Main output area text. | <none> |
2062
+ | `input` | Main command/input field. | `output` |
2063
+ | `input_suggestion` | Autosuggestion or completion suffix text. | `input` |
2064
+ | `cursor` | Cursor cell styling where applicable. | `input` |
2065
+ | `region` | Selected text / active region. | `output` |
2066
+ | `status` | Status area base role. | `output` |
2067
+ | `info` | Informational messages. | `output` |
2068
+ | `good` | Success messages. | `info` |
2069
+ | `warn` | Warning messages. | `info` |
2070
+ | `error` | Error messages. | `warn` |
2071
+ | `alert` | Alert panel base role. | `output` |
2072
+ | `pager_status` | Pager status / prompt row. | `status` |
2073
+ | `line_number` | Line numbers when toggled on | `output` |
2074
+ | `search_input` | Search input field. | `popup` |
2075
+ | `match_current` | Current search match. | `region` |
2076
+ | `match_other` | Other search matches. | `region` |
2077
+ | `popup` | Popup body. | `output` |
2078
+ | `popup_frame` | Popup border/frame. | `popup` |
2079
+ | `popup_input` | Popup filter/input field. | `input` |
2080
+ | `popup_selection` | Selected popup item row. | `region` |
2081
+ | `popup_counts` | Popup counts/status row. | `popup` |
2082
+ | `markdown_h1` | Markdown level-1 headings. | `output` |
2083
+ | `markdown_h2` | Markdown level-2 headings. | `markdown_h1` |
2084
+ | `markdown_h3` | Markdown level-3 headings. | `markdown_h2` |
2085
+ | `markdown_code` | Inline markdown code. | `output` |
2086
+ | `markdown_code_block` | Markdown fenced-code block base role. | `markdown_code` |
2087
+ | `markdown_code_gutter` | Code block gutter/prefix. | `markdown_code_block` |
2088
+ | `markdown_strong` | Markdown strong/bold text. | `output` |
2089
+ | `markdown_emphasis` | Markdown emphasized text. | `output` |
2090
+ | `markdown_link` | Markdown link text. | `output` |
2091
+ | `markdown_url` | Markdown displayed URL text. | `markdown_link` |
2092
+ | `markdown_quote_gutter` | Markdown block quote gutter. | `output` |
2093
+ | `markdown_highlight` | Markdown highlight / marked text. | `output` |
2094
+ | `markdown_table_header` | Markdown table header cells. | `markdown_strong` |
2095
+ | `markdown_table_cell` | Markdown table body cells. | `output` |
2096
+ | `markdown_underline` | Markdown underline text. | `output` |
2097
+ | `markdown_hrule` | Markdown horizontal rules. | `output` |
2098
+
2099
+
2100
+ <a id="orgabf70f2"></a>
2101
+
2102
+ #### Role Colors and Attributes and Frame Style
2103
+
2104
+ Each role can have assigned to it a foreground color (`fg:`), a background color (`bg:`), and one or more "attributes" (`attrs:`).
18
2105
 
2106
+ 1. Colors
19
2107
 
20
- <a id="org64156e4"></a>
2108
+ The colors can be a hex string of the form "#RRGGBB", that is a mixture of red, green, and blue at level given by three valid hexadecimal numbers RR, GG, and BB. For example "#FF0000" is pure red, "#330044" is a middling mixture of red and blue, a shade of purple. In addition, `fatty` recognizes color names defined as 16 ANSI colors, 256 xterm colors, and X11 colors, all of which you can see by invoking `fatty`'s CLI command `colors`.
2109
+
2110
+ The special color `default` means to inherit colors from the underlying terminal program.
21
2111
 
22
- # Reading Keys
2112
+ 2. Attributes
23
2113
 
24
- Shift/Ctrl/Meta F-keys are normalized when ncurses provides distinct constants.
2114
+ Each role can also have non-color attributes applied by specifying an `attrs` list. Supported attributes are `bold`, `dim`, `italic`, `underline`, and `reverse`. Italic is emitted by the truecolor renderer and by markdown ANSI output, but curses terminals may display it inconsistently or not at all.
25
2115
 
2116
+ 3. Frame Style
26
2117
 
27
- <a id="org38ad970"></a>
2118
+ In addition, one role, `popup_frame:`, in addition to these have two attributes for styling the "frame" drawn around popup:
2119
+
2120
+ | `border:` | single, double, or ascii |
2121
+ | `corners:` | rounded or square |
28
2122
 
29
- # Alerts
2123
+ 4. An Example Theme
30
2124
 
31
- Alerts are short-lived, non-scrolling messages shown below the input line. They are intended for user-visible conditions that require attention.
2125
+ ```yaml
2126
+ name: example
2127
+ inherit: null
2128
+
2129
+ output:
2130
+ fg: "#d8dee9"
2131
+ bg: "#2e3440"
2132
+
2133
+ input:
2134
+ fg: "#eceff4"
2135
+ bg: "#3b4252"
2136
+ attrs: [bold]
2137
+
2138
+ input_suggestion:
2139
+ fg: "#81a1c1"
2140
+ bg: "#3b4252"
2141
+
2142
+ cursor:
2143
+ fg: "#2e3440"
2144
+ bg: "#88c0d0"
2145
+
2146
+ region:
2147
+ fg: "#2e3440"
2148
+ bg: "#88c0d0"
2149
+
2150
+ info:
2151
+ fg: "#d8dee9"
2152
+ bg: "#2e3440"
2153
+
2154
+ good:
2155
+ fg: green
2156
+ bg: "#2e3440"
2157
+
2158
+ warn:
2159
+ fg: "#2e3440"
2160
+ bg: "#ebcb8b"
2161
+
2162
+ error:
2163
+ fg: "#eceff4"
2164
+ bg: "#bf616a"
2165
+
2166
+ pager_status:
2167
+ fg: black
2168
+ bg: lightgreen
2169
+
2170
+ search_input:
2171
+ fg: black
2172
+ bg: cyan
2173
+
2174
+ match_current:
2175
+ fg: black
2176
+ bg: yellow
2177
+
2178
+ match_other:
2179
+ fg: black
2180
+ bg: lightgray
2181
+
2182
+ popup:
2183
+ fg: "#d8dee9"
2184
+ bg: "#3b4252"
2185
+
2186
+ popup_input:
2187
+ fg: "#eceff4"
2188
+ bg: "#434c5e"
2189
+
2190
+ popup_selection:
2191
+ fg: "#2e3440"
2192
+ bg: "#88c0d0"
2193
+
2194
+ popup_counts:
2195
+ fg: "#2e3440"
2196
+ bg: white
2197
+
2198
+ popup_frame:
2199
+ fg: "#81a1c1"
2200
+ bg: "#3b4252"
2201
+ border: single
2202
+ corners: rounded
2203
+ ```
2204
+
2205
+ Or you can group them under `markdown` without the `markdown_` prefix:
2206
+
2207
+ ```yaml
2208
+ markdown:
2209
+ h1:
2210
+ fg: yellow
2211
+ bg: navy
2212
+ attrs: [bold]
2213
+ code:
2214
+ fg: lightgreen
2215
+ bg: black
2216
+ link:
2217
+ fg: cyan
2218
+ bg: navy
2219
+ attrs: [underline]
2220
+ ```