okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
f7fcae1
main
Design and interaction
For a concrete build, use the compact picker. For a bug fix, use the focus and modal walkthrough.
These are design recommendations synthesized from the studied applications, not a mandatory Charm visual style. For observations and exact evidence, see the case studies.
Design the smallest complete loop
Start with the user outcome, not the component catalogue. For a server picker the loop might be: identify candidates, narrow the set, inspect health, connect. Every persistent screen element should support one of those steps or explain the current state.
An inline prompt should leave the surrounding shell comprehensible. A full-screen browser can spend more space on context and inspection because the user intends to stay. Do not use a full-screen dashboard for a yes/no question or hide a complex monitoring workflow inside a one-line prompt.
Gum's filter supports an inline/fixed-height presentation and a full-screen branch. Its command wrapper also separates interactive rendering from result production. The transferable idea is a scoped interaction that remains useful in a larger shell workflow. S06 S07
A useful design note before implementation is:
Task: choose a server and connect. Primary flow: type to filter -> move cursor -> inspect health -> accept. Cancel: exit without emitting a server ID. Output: one stable server ID, not the displayed label. Preserve: existing search semantics, indicators, counters, animation, and keys. Small layout: hide secondary columns; retain identity, health, and accept/cancel.
This is an example of the note's content, not a requirement to add a new file to every repository.
Give the screen a reading order
A practical hierarchy is context first, work second, feedback third, controls last. Title and controls should be discoverable but not compete with the data.
An illustrative picker, not a prescribed theme:
Connect 2 of 18
Filter: prod_
> prod-api-01 ready 23 ms
prod-api-02 connecting --
enter connect esc cancel ? help
The second number describes matching or total candidates only if that relationship is clear. Missing latency is not 0 ms. A highlighted row is not automatically an accepted selection. While probing, allow the user to move or cancel; show pending work without pretending it has completed.
At a smaller width, simplify structure rather than scale all labels into fragments:
Connect 2/18 > prod-api-01 ready · 23 ms enter connect · esc cancel
Do not let an automatic switch discard the query, move the cursor to a different ID, or clear multi-selection.
Make state readable without a legend
| State | Useful presentation | Common failure |
|---|---|---|
| Initial load | What is loading and how to cancel | A spinner with no object or action |
| Refresh | Existing data plus refreshing/stale indicator | Blanking useful content every refresh |
| Truly empty dataset | What is absent and how to create/import/retry | “No results” with no context |
| No filter matches | Query context and a clear-filter action | An empty list that still accepts Enter |
| Recoverable failure | Operation, concise reason, retry/back | Full-screen fatal error for a transient issue |
| Destructive action | Exact target and consequence | A generic “Are you sure?” |
| Completed action | Stable confirmation or returned value | Disappearing feedback before it can be read |
Avoid boolean combinations that can accidentally render “loading,” “failed,” and “success” together. Model lifecycle states clearly, while retaining separate flags only for genuinely independent concerns.
Progress bars need a meaningful denominator. Otherwise use an activity indicator and useful counts or elapsed time. An animation should stop when its operation stops. Do not animate the entire screen to prove that it is alive.
Focus is a spatial and behavioral promise
The focused control should be visually recognizable. The same visual emphasis should not mean focus in one place and danger in another. Multi-selection needs a separate mark from the cursor; keep marker widths stable so text does not jump between rows.
Define navigation per mode. An example policy is:
Normal: arrows or j/k move; / enters search; Enter accepts current item.
Search: printable text edits; arrows may navigate results if documented.
Escape leaves search; a subsequent Escape cancels the picker.
Modal: only modal controls react; closing restores previous focus.
This is not a universal keymap. A type-to-filter picker may start in search and never need a separate normal mode. Preserve an existing convention rather than force a Vim-like mode onto it.
Glow explicitly protects an active filter from its ordinary q quit action. Gum disables normal-mode navigation bindings while search is focused. Both are evidence that keyboard shortcuts must respect editing context. S05 S06
Do not consume the same key both globally and locally. Enter that dismisses an error should not also open the first item. Escape that closes a dialog should not also quit the application. If Ctrl+C first cancels work instead of quitting, make that policy observable and test repeated presses.
Teach controls at the point of use
Show the few likely actions near the active task; put the complete reference behind a help action. Derive both from actual enabled bindings where feasible. Hide or explicitly disable controls that cannot work in the current state.
Soft Serve's help depends on the active page and whether filtering is occurring; it also measures its rendered footer for layout. Help is an active part of the interaction and geometry, not a fixed decorative string. S10
Use action verbs: “open,” “connect,” “retry,” “clear filter,” “cancel.” “Submit” may be appropriate for a form, but it often says less than the actual operation. Show the target or scope for a destructive action. Reserve a persistent status area when frequent updates would otherwise make content move.
Develop a theme from semantic roles
A small theme may define foreground, muted foreground, accent, border, focused row, selected mark, success, warning, and error. Derive component styles from these roles. Let a product's existing identity guide the actual colors.
Prefer the terminal's own background unless full-surface coloring serves a clear purpose. Check muted text on both light and dark backgrounds; “muted” must still be legible. Test a monochrome rendering: cursor marks, labels, and layout should preserve meaning after color disappears.
Borders consume cells and add noise. Use one around a coherent interaction, or a separator between independent regions, before boxing every field. Prefer padding inside a frame to margins that accidentally push content past it. Use bold or contrast for a few hierarchy levels, not every heading, badge, and value.
A mascot or accent animation can make a tool memorable. Keep it small, optional when appropriate, and separate from the user's input cursor. Do not remove an existing distinctive feature under the guise of code simplification; reduce implementation duplication rather than behavior.
Accessibility and terminal-native operation
Keep the whole workflow usable with a keyboard and without special fonts. Use text alternatives for icons, color, and changing animation. Avoid stealing conventional copy/paste combinations without a strong reason. Mouse support should add convenience, not become the only way to reach an action.
A no-color TUI can still be difficult for assistive software because it repeatedly redraws and repositions text. Huh provides WithAccessible(true), which changes interaction into ordinary prompts; that is a distinct capability from changing a palette. Offer a comparable plain or noninteractive path where the application needs one. S11
Treat confirmation, cancellation, and recovery as part of polish. A pretty interface that cannot be safely cancelled, piped, or read at a small size is not finished.
1# Design and interaction 2 3For a concrete build, use [the compact picker](../examples/WORKED-EXAMPLES.md#1-build-a-compact-picker). For a bug fix, use [the focus and modal walkthrough](../examples/WORKED-EXAMPLES.md#2-fix-search-keys-and-modal-fall-through). 4 5These are design recommendations synthesized from the studied applications, not a mandatory Charm visual style. For observations and exact evidence, see the [case studies](case-studies.md). 6 7## Design the smallest complete loop 8 9Start with the user outcome, not the component catalogue. For a server picker the loop might be: identify candidates, narrow the set, inspect health, connect. Every persistent screen element should support one of those steps or explain the current state. 10 11An inline prompt should leave the surrounding shell comprehensible. A full-screen browser can spend more space on context and inspection because the user intends to stay. Do not use a full-screen dashboard for a yes/no question or hide a complex monitoring workflow inside a one-line prompt. 12 13Gum's filter supports an inline/fixed-height presentation and a full-screen branch. Its command wrapper also separates interactive rendering from result production. The transferable idea is a scoped interaction that remains useful in a larger shell workflow. [S06](sources.md#s06) [S07](sources.md#s07) 14 15A useful design note before implementation is: 16 17``` text 18Task: choose a server and connect. 19Primary flow: type to filter -> move cursor -> inspect health -> accept. 20Cancel: exit without emitting a server ID. 21Output: one stable server ID, not the displayed label. 22Preserve: existing search semantics, indicators, counters, animation, and keys. 23Small layout: hide secondary columns; retain identity, health, and accept/cancel. 24``` 25 26This is an example of the note's content, not a requirement to add a new file to every repository. 27 28## Give the screen a reading order 29 30A practical hierarchy is context first, work second, feedback third, controls last. Title and controls should be discoverable but not compete with the data. 31 32An illustrative picker, not a prescribed theme: 33 34``` text 35Connect 2 of 18 36Filter: prod_ 37 38> prod-api-01 ready 23 ms 39prod-api-02 connecting -- 40 41enter connect esc cancel ? help 42``` 43 44The second number describes matching or total candidates only if that relationship is clear. Missing latency is not `0 ms`. A highlighted row is not automatically an accepted selection. While probing, allow the user to move or cancel; show pending work without pretending it has completed. 45 46At a smaller width, simplify structure rather than scale all labels into fragments: 47 48``` text 49Connect 2/18 50> prod-api-01 51ready · 23 ms 52 53enter connect · esc cancel 54``` 55 56Do not let an automatic switch discard the query, move the cursor to a different ID, or clear multi-selection. 57 58## Make state readable without a legend 59 60| State | Useful presentation | Common failure | 61| --- | --- | --- | 62| Initial load | What is loading and how to cancel | A spinner with no object or action | 63| Refresh | Existing data plus refreshing/stale indicator | Blanking useful content every refresh | 64| Truly empty dataset | What is absent and how to create/import/retry | “No results” with no context | 65| No filter matches | Query context and a clear-filter action | An empty list that still accepts Enter | 66| Recoverable failure | Operation, concise reason, retry/back | Full-screen fatal error for a transient issue | 67| Destructive action | Exact target and consequence | A generic “Are you sure?” | 68| Completed action | Stable confirmation or returned value | Disappearing feedback before it can be read | 69 70Avoid boolean combinations that can accidentally render “loading,” “failed,” and “success” together. Model lifecycle states clearly, while retaining separate flags only for genuinely independent concerns. 71 72Progress bars need a meaningful denominator. Otherwise use an activity indicator and useful counts or elapsed time. An animation should stop when its operation stops. Do not animate the entire screen to prove that it is alive. 73 74## Focus is a spatial and behavioral promise 75 76The focused control should be visually recognizable. The same visual emphasis should not mean focus in one place and danger in another. Multi-selection needs a separate mark from the cursor; keep marker widths stable so text does not jump between rows. 77 78Define navigation per mode. An example policy is: 79 80``` text 81Normal: arrows or j/k move; / enters search; Enter accepts current item. 82Search: printable text edits; arrows may navigate results if documented. 83Escape leaves search; a subsequent Escape cancels the picker. 84Modal: only modal controls react; closing restores previous focus. 85``` 86 87This is not a universal keymap. A type-to-filter picker may start in search and never need a separate normal mode. Preserve an existing convention rather than force a Vim-like mode onto it. 88 89Glow explicitly protects an active filter from its ordinary `q` quit action. Gum disables normal-mode navigation bindings while search is focused. Both are evidence that keyboard shortcuts must respect editing context. [S05](sources.md#s05) [S06](sources.md#s06) 90 91Do not consume the same key both globally and locally. Enter that dismisses an error should not also open the first item. Escape that closes a dialog should not also quit the application. If Ctrl+C first cancels work instead of quitting, make that policy observable and test repeated presses. 92 93## Teach controls at the point of use 94 95Show the few likely actions near the active task; put the complete reference behind a help action. Derive both from actual enabled bindings where feasible. Hide or explicitly disable controls that cannot work in the current state. 96 97Soft Serve's help depends on the active page and whether filtering is occurring; it also measures its rendered footer for layout. Help is an active part of the interaction and geometry, not a fixed decorative string. [S10](sources.md#s10) 98 99Use action verbs: “open,” “connect,” “retry,” “clear filter,” “cancel.” “Submit” may be appropriate for a form, but it often says less than the actual operation. Show the target or scope for a destructive action. Reserve a persistent status area when frequent updates would otherwise make content move. 100 101## Develop a theme from semantic roles 102 103A small theme may define foreground, muted foreground, accent, border, focused row, selected mark, success, warning, and error. Derive component styles from these roles. Let a product's existing identity guide the actual colors. 104 105Prefer the terminal's own background unless full-surface coloring serves a clear purpose. Check muted text on both light and dark backgrounds; “muted” must still be legible. Test a monochrome rendering: cursor marks, labels, and layout should preserve meaning after color disappears. 106 107Borders consume cells and add noise. Use one around a coherent interaction, or a separator between independent regions, before boxing every field. Prefer padding inside a frame to margins that accidentally push content past it. Use bold or contrast for a few hierarchy levels, not every heading, badge, and value. 108 109A mascot or accent animation can make a tool memorable. Keep it small, optional when appropriate, and separate from the user's input cursor. Do not remove an existing distinctive feature under the guise of code simplification; reduce implementation duplication rather than behavior. 110 111## Accessibility and terminal-native operation 112 113Keep the whole workflow usable with a keyboard and without special fonts. Use text alternatives for icons, color, and changing animation. Avoid stealing conventional copy/paste combinations without a strong reason. Mouse support should add convenience, not become the only way to reach an action. 114 115A no-color TUI can still be difficult for assistive software because it repeatedly redraws and repositions text. Huh provides `WithAccessible(true)`, which changes interaction into ordinary prompts; that is a distinct capability from changing a palette. Offer a comparable plain or noninteractive path where the application needs one. [S11](sources.md#s11) 116 117Treat confirmation, cancellation, and recovery as part of polish. A pretty interface that cannot be safely cancelled, piped, or read at a small size is not finished.