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
147
148
149
150
151
152
153
154
155
156
157
//! Pure row-builders for the coordinator session list.
//!
//! Why: the coordinator TUI renders one bullet for the controller plus one row
//! per managed session, and the *selected* session expands into a two-column
//! `[id] │ [summary]` line. Keeping the text/`Line` construction here — free of
//! any terminal or daemon dependency — lets the list content be unit-tested
//! exactly as the dashboard's `sidebar_items` are, satisfying the spec's
//! "pure/unit (no terminal, no network)" test tier (DOC-13 §9).
//! What: free functions that build ratatui [`Line`]s (and a couple of `String`
//! helpers the tests assert on) for the controller bullet, a non-selected
//! session row, and the selected two-column active row. No live data is wired
//! here — Child #1 feeds these placeholder rows; Child #2/#3 swap in the live
//! feed + real `last_summary`.
//! Test: `super::tests` covers `controller_bullet_row`, `session_short_id`, and
//! the two-column split via `active_row_two_col`.
use ;
/// The glyph drawn for the always-present controller bullet (row 0).
///
/// Why: the spec pins the controller as row 0 and always-active (`●`); a named
/// constant keeps the glyph in one place so the controller and an active
/// session render the same bullet.
/// What: the filled-circle bullet `●`.
/// Test: asserted in `controller_bullet_row` via the rendered span text.
pub const CONTROLLER_GLYPH: char = '●';
/// The glyph drawn for an idle / placeholder session row.
///
/// Why: non-active sessions use a hollow bullet (`○`) per the spec's bullet
/// legend (§3.2); Child #2 maps real `SessionStatus` to colored glyphs, but the
/// skeleton only needs the idle glyph for its placeholder rows.
/// What: the hollow-circle bullet `○`.
/// Test: asserted in `session_row_renders_bullet_and_prefix`.
pub const SESSION_GLYPH: char = '○';
/// The column separator drawn between a selected row's id and its summary.
///
/// Why: the spec's "active row" contract (§3.3) separates the short id from the
/// last-summarized message with a `│` glyph; centralising it keeps the split
/// rule and its unit test in agreement.
/// What: the box-drawing vertical bar `│`.
/// Test: asserted in `active_row_two_col_splits_on_bar`.
pub const COLUMN_SEPARATOR: char = '│';
/// Build the controller bullet line (always row 0 of the session list).
///
/// Why: the coordinator itself is represented by a pinned first row whose right
/// half is a synthetic status (`idle · N delegations · ready`), distinct from a
/// managed session. Rendering it through a dedicated builder keeps that contract
/// explicit and testable without a terminal.
/// What: returns a [`Line`] of `● controller <status>`; when `selected` the
/// whole line is bold/cyan to mirror the dashboard's selection highlight.
/// Test: `controller_bullet_row` asserts the glyph, label, and status text.
/// Build a non-selected managed-session row.
///
/// Why: rows beneath the controller show a hollow bullet, the session's short
/// id, its routing prefix, and a dimmed one-line status — the compact form for
/// every row that is not the active selection (§3.2).
/// What: returns a [`Line`] of `○ <short-id> <prefix> <status>` with the
/// status dimmed.
/// Test: `session_row_renders_bullet_and_prefix`.
/// Build a numbered, non-selected managed-session row: `N. ○ <id> <prefix> …`.
///
/// Why: STUI-1 (#1278) renders the list NUMBERED (1, 2, 3, …) so an operator can
/// refer to a session by its list position; the number is purely a display index
/// (1-based), distinct from the session id. Prefixing it here keeps the numbering
/// rule in the pure row layer where it is unit-testable, and keeps [`session_row`]
/// available for the un-numbered controller-adjacent rendering paths.
/// What: returns a [`Line`] of `<n>. ` (dimmed) followed by the standard
/// [`session_row`] spans (`○ <short-id> <prefix> <status>`). The number is
/// right-padded to two columns so single- and double-digit numbers align.
/// Test: `numbered_session_row_prefixes_number`.
/// Build the selected session's two-column active row: `[id] │ [summary]`.
///
/// Why: the headline feature of the coordinator list is that the *active* row
/// expands to show what the session is doing. The summary source (daemon
/// `last_summary`) lands in Child #3; Child #1 renders whatever placeholder
/// summary it is handed so the column contract (§3.3) exists and is testable.
/// What: returns a [`Line`] of `● <short-id> │ <summary>`, the whole row
/// bold/cyan to mark it as the selection; the `│` separator is the single
/// source of the column split.
/// Test: `active_row_two_col_splits_on_bar`.
/// Truncate a session id to its short, human-readable 8-char form.
///
/// Why: the list's left column shows the first 8 hex chars of the UUID, not the
/// full id (§3.3); a shared helper keeps the rule identical to the dashboard's
/// `short_session` so the two surfaces never diverge.
/// What: returns the first 8 characters of `id` (fewer if `id` is shorter).
/// Test: `session_short_id_truncates`.