Yacui is a Magit inspired Console User Interface library. If you haven’t used Magit, the tl;dr is:
- Screen is split into two: the current “view” and available key bindings.
- You navigate the interface using a single (or combined) letters.
Magit interface allows for easy discoverability and low learning curve. Simple mnemonic controls let you build muscle memory and use it fast when you’ve learned it. We need more software like that.
“Screenshot” of an imagined cookbook app:
Cookbook application 1. Recipe for cookies 2. Recipe for cake *SELECTED* -------------------------------------- q Quit n Next N Add new recipe p Previous D Delete recipe Enter View recipe Status: New recipe added successfully!
I need this in my small internal project, but doing this lib with a nice API was my guilty pleasure. I’d love to have a full Jira interface in console (Majira?) but my attention span is too short to code it.
Name is bad, but most names are taken.
Install pipenv and then use it to download dependencies. TODO: Package as a library
$ sudo apt install pipenv
$ pipenv install
$ pipenv shell
# Run an example:
$ ./example.py
I include this as a subtree in my other projects until it matures.
App ----> Console \---> Display ---> Current View \--> Bindings \-> View history \-> Discovery
Ties everything together. Can hold additional “global” state required by the app, although most of state should be handled by views.
Reads keys, and uses Bindings to resolve an action. If nothing is happening it’s calling a tick on the current view once a second.
This maaaybe nicer using asyncio, but currently works just fine and is simple.
Manages ncurses; initializes/deinitializes screen. Splits screen in windows, offers a low-level input control.
Holds the current View instance. Manages backward/forward navigation between view instances.
Manages views lifecycle by calling their on_enter/on_leave/on_drop methods.
A View controls what is displayed. Can always redraw the view data on the screen in case of resize event or view navigation.
It also controls what actions can be executed and what other views can be visited.
Converts pressed buttons into executed actions. There’s only one instance of Bindings class.
Current bindings can be pushed/popped from the binding stack.
Renders the current bindings in a usable and automatic way while preserving their order.
View can have multiple instances displaying in the same way different data. If necessary can share state via class members or app object. Therefore View instances hold the main app state.
Display manages screens and doesn’t hold additional state information. Global state (db connections, etc.) can be stored in the App instance.
This renders bad on Github. Use org-mode/emacs or ignore this part.
So it can use subview as a modal, for example action requires selected item, so it opens a subview for selection, but instructs it first what is it looking for and sets callbacks. Debug window, opened with –debug; helps a bit with development. It’s easy when you don’t miss something obvious - like recreating windows.- Note taken on [2020-09-22 Tue 01:17]
Partially. Via getkey not really, via SIGWINCH mostly YES, but the number of cols is not refreshed everywhere.
Current half-delay approach will break when someone types M-o twice or ESC twice fast.
Either embed readline… or implement it. Can’t be too hard. Minimum keys support: movement: C-f, C-b, M-f, M-b, C-a, C-e, C-n, C-p, LEFT, RIGHT edit: M-BACKSPACE, M-d, C-k, BACKSPACE, typing in insert mode C-y (yank previous cut) Maybe: mark (C-Space)
console.textpad after rereading the docs can work without blocking so it might be ok. Although I’m unsure if it’s worth using. The keys would have to be wrapped anyway.
Implemented by passing an unhandled keypresses to the view and then in 6 lines of code - without full edit though.
Name view, and name keybindings, allow overriding them via config.
- Generate PAD
- PAD displayed on the screen.
- Mock console
- Test View navigation, droping, bindings push/pop
- Create mechanism for sending key presses and checking result, to have an api to test applications, not the lib itself.