1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! ebman — k9s-style TUI for AWS Elastic Beanstalk.
//!
//! The crate is split lib + bin: this library holds the testable logic
//! (all the modules below), and `src/main.rs` is a thin binary entry
//! point that wires argv parsing, logging, the TUI lifecycle, and
//! dispatch into the lib. See `CLAUDE.md` for working rules and
//! `BACKLOG.md` for the milestone plan.
//!
//! # What is public, and why so little
//!
//! Only what `src/main.rs` needs. Concretely: `App` and its seven
//! methods (`new`, `new_demo`, `run`, `persist_state`, `set_read_only`,
//! `set_log_reload`, `reload_requested`), the ten `cli::*::run` entry
//! points, `config::load` with `Config`'s two icon accessors, one
//! function each from `audit` / `project` / `freeze` / `control` /
//! `splash` / `util`, `resolve_icons_setting`, and the `Tui` /
//! `LogReloadHandle` aliases. Everything else is `pub(crate)`.
//!
//! **Read the module list below as modules, not as surface.** An
//! earlier version of this note said "only the nine modules `main.rs`
//! needs are public" and left the impression that the API was about
//! nine things. It was 4565 items. `pub mod app` alone carried most of
//! them, because a public module re-exports everything `pub` inside it
//! — `App`'s 91 public fields, the mode types, `ViewState`, the lot.
//! Narrowing the *modules* had barely moved the number; narrowing the
//! items took it to 212. (Both figures are `cargo public-api` with no
//! flags; omitting auto-derived and blanket impls the surface is 67.)
//!
//! The cost of leaving it wide was not theoretical. Adding a field to
//! an internal struct was a semver event twice in two releases —
//! `Form.banner` in 0.31.0 and `WorkerQueues.dlq_origin` in 0.32.0 —
//! which made `cargo-semver-checks` a tax on ordinary refactoring
//! rather than a safety net on the API anyone actually uses. And 0.33.0
//! shipped a Breaking section enumerating **38** items that inherited a
//! type-identity change from a ratatui bump, none of which any consumer
//! could have wanted. After this narrowing that set is **two**: the
//! `Tui` alias and `ControlOp::Key(KeyEvent)`, both irreducible because
//! `main.rs` genuinely owns the alt-screen lifecycle and the control
//! channel.
//!
//! Keeping it narrow is enforced, not remembered: `unreachable_pub`
//! (below) catches a `pub` item inside a `pub(crate)` module, which is
//! the leak that made `App::theme`'s ratatui `Color` fields publicly
//! readable while `Theme` itself stayed unnameable.
// `unwrap_used` / `expect_used` are denied crate-wide (see
// Cargo.toml). Test code is exempt: a panic in a test IS the failure
// report, and `#[allow]` on every assertion would be noise. This does
// not weaken production checking: `cargo clippy --all-targets`
// compiles the lib WITHOUT cfg(test) as well, and that build still
// denies. Verified by planting an `unwrap` in production code and
// confirming clippy still errors — by the CI clippy job, not by a
// test, since a test cannot observe what clippy did.
// Catches the leak this narrowing was cleaning up: a `pub` item inside a
// `pub(crate)` module, which is invisible in the API listing but still
// widens it through any public signature that names it. `App::theme:
// Arc<Theme>` was exactly that — `Theme` is `pub` in a `pub(crate)`
// module, so its ratatui `Color` fields were publicly readable while the
// type stayed unnameable. Grep does not find that class; the compiler does.
pub
pub
pub
pub
pub
pub
pub
pub
pub
// `font_probe` and `overlay` live in the shared `tui-common` crate so
// the sibling pgman repo can depend on the same code. Re-exported here
// so existing `crate::font_probe::*` / `crate::overlay::*` paths (and
// the `ebman::*` paths from the bin) keep working unchanged.
// Only the one function `main` calls, not the whole module. Re-exporting
// `font_probe` wholesale put `AutoResolved` and the two `detect_*` probes
// in ebman's public API to serve a single call site — and they are
// tb-tui-common's types, so a major bump there broke ebman's API for no
// consumer. Nothing inside this crate uses the module at all; the comment
// above claiming `crate::font_probe::*` paths depend on it was stale.
pub use resolve_icons_setting;
pub use overlay;
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub
use Stdout;
use ;
use ;
/// Concrete `ratatui` terminal we drive through the alt-screen. Lives
/// in the lib so `app::App` can hold a mutable reference to it through
/// long-running operations (embedded shell, `$EDITOR` hand-off).
pub type Tui = ;
/// Best-effort terminal restore: every step attempted regardless of
/// whether the previous one failed.
///
/// Shared because it was written twice with a `?` between the steps —
/// once in `main`'s `leave_tui`, once in the `$EDITOR` hand-off — and in
/// both a failure in `disable_raw_mode` meant the alternate screen was
/// never left. The operator is then looking at a dead screen with mouse
/// capture on, typing `reset` blind. Which step fails matters less than
/// the fact that a `?` between them stops the rest from running, and
/// there is no useful second move here: if the terminal will not
/// restore, trying the remaining steps anyway is strictly better than
/// stopping.
/// Handle for live-reloading the log filter from the running app.
/// Constructed by `main::init_logging` and threaded onto `App` so
/// `:loglevel` can mutate the active subscriber at runtime.
pub type LogReloadHandle = Handle;