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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
//! **Composer focus discipline** (DESIGN §11): the one mechanism deciding when
//! the keyboard lands in the message box, so the operator can just type.
//!
//! Not to be confused with [`crate::app::focus`], which owns the *selection* —
//! which conversation is picked. This module owns the **keyboard**,
//! and it owns it through exactly one piece of state: the deferred request bit
//! [`ShellState::focus_composer`]. Nothing else in `shell/*` may call
//! `request_focus` on a composer; a gesture states its intent by setting the
//! bit, and whichever composer paints next frame — the bottom one or the
//! empty-world bootstrap — consumes it in [`take`]. egui focus is per-frame and
//! a gesture is handled before any widget exists, so "next frame" is not a
//! delay bolted on: it is the only frame that has a box to hand the focus to.
//!
//! [`take`] also schedules a **one-frame repeat** of the bit it just spent
//! (`refocus_composer`, bl-58e4). That is not a second request path — no gesture
//! reaches it and nothing else reads it — but the bit's own delivery, because a
//! request that rode an arrow key does not actually land on the first frame.
//! The reason is ruled at [`take`].
//!
//! It also carries the §11 **list gestures** that move the selection since the
//! unfold (bl-fa82) — the ↑/↓ walk over the *visible* rows, the ←/→ that fold
//! one, and the field's click — because each of them ends by handing the
//! keyboard over under the rules below, and each is a thin call onto
//! `nav::convs::expand`'s tested derivations rather than a decision made here.
//!
//! Two rules decide who sets it, and there is no third:
//!
//! 1. **A pointer gesture hands the keyboard back.** Opening a conversation,
//! switching workspace, launching, sending, and dismissing a modal all end
//! with the cursor in the box. The mouse said *where*; the keyboard's only
//! remaining job is *what to say*.
//! 2. **A selection lands the composer no matter the plane it rode.** Opening
//! the app focuses the chat prompt, and so does selecting an agent, which
//! supersedes the older rule that a keyboard gesture left the keyboard plane alone. A
//! bare ↓ therefore surrenders the very plane it was pressed on, and that
//! cost is accepted: the walk's continuation is spelled on the combo plane
//! (Ctrl+↑/↓), which survives text focus. `i` / Ctrl+I stays the explicit
//! request from anywhere and Escape the release — and Escape with nothing
//! pending must still not re-grab, since with every selection landing here it
//! is the one door back to the bare plane.
use crateAppModel;
use crateCenterTab;
use ;
use ShellState;
/// Ask that the composer take the keyboard on the next frame it is painted
/// (§11) — and seat the center on the tab that *has* one.
///
/// Still one request bit. The seat beside it is not a second mechanism but the
/// bit's precondition: since bl-1ca2 the center is a strip of tab focuses and
/// only the Conversation tab carries a composer, so a request made while
/// Config is up would wait for a box that never paints. Asking for the
/// keyboard **is** asking for the surface that takes it, which is why this is
/// one call and not two at every site.
pub
/// Focus a §11 center tab — the left-panel entries, the strip itself, the
/// keyboard's Command+Shift+digit, and Escape's way back.
///
/// Landing on the conversation is a selection like any other and hands the
/// keyboard back (rule 1); the other tabs hold no composer, so they take the
/// seat and leave the keyboard where it is — which is what keeps Escape the
/// door back to the bare plane rather than a re-grab.
pub
/// Honour a pending request on the composer just painted, consuming it — the
/// **only** `request_focus` on a composer in the tree. Called unconditionally
/// by every composer; the bit decides.
///
/// **A request that rode an arrow key is asked again next frame** (bl-58e4).
/// egui walks the *focus floor* on a bare arrow (its own cardinal navigation),
/// and a widget that GAINS focus during a frame has not yet installed the event
/// filter that would claim the arrow for itself — so the very ↑/↓ that made the
/// selection is also read as "step the floor one control on", and the keyboard
/// lands on whatever sits under the box rather than in it. Rule 2 was therefore
/// only ever nearly true: `wants_keyboard_input` said yes while the cursor was
/// on the Send button. Re-asking on the next frame — which no longer carries
/// the key — makes it true outright, and a second request on a box that already
/// has the keyboard is a no-op.
///
/// The band reorder is what made "nearly" not good enough: the
/// control under the box is now the settings band, whose height settles a frame
/// late, so the control the floor stepped onto could vanish mid-settle and take
/// the keyboard down with it.
///
/// **Escape outranks a carried request**, exactly as rule 2 says it outranks a
/// standing one — it is the one door back to the bare plane — so a request still
/// in flight from the frame before must not re-grab on the frame that puts the
/// keyboard down. An Escape frame that *also* asks is a different thing and is
/// honoured: that is a dismissed modal handing the keyboard back (rule 1).
pub
/// Select a conversation and hand the keyboard over (rules 1 and 2) — a list
/// row, a descent-tree member, a followed card. The selection is the §6
/// acknowledgement gesture ([`AppModel::focus_agent`]) and it re-targets the
/// composer, so the box the operator lands in is already aimed at what they
/// just picked.
///
/// Revealing what was selected is **not** here — [`reveal_selection`] is the
/// one home of that, read by the list itself, so a selection made anywhere
/// (including outside this module) lands on a row the operator can see.
pub
/// **§11's visible-selection invariant** (bl-fa82): the selected agent's row is
/// painted. Landing the operator on a row they cannot see is the *why am I
/// here* §6 already forbids of a jump's landing, and the gestures that can do
/// it are many — a §8.5 address, a start's adoption, the attention jump, a
/// pointer on any row — so the invariant is kept once, by the list,
/// rather than by a clause at each of their sites.
///
/// Opening the agent's ancestor chain is exactly enough: a visible row's parent
/// is visible by construction. A depth-0 root's chain is empty, so the ordinary
/// case adds nothing. It cannot fight a fold, either, because both collapsing
/// gestures ([`collapse_row`], [`toggle_row`]) carry the selection up to the row
/// they shut — so by the time this runs the selection is never under it.
pub
/// The descent-id chain above `agent` in `ws`, outermost first — read off the
/// same [`nav::convs`](crate::nav::convs) derivation the list itself renders,
/// never re-derived here. Empty for a root and for an id this snapshot has not
/// got.
/// The list as the frame paints it — the one derivation every gesture below
/// reads, so the walk, the fold and the paint can never disagree about which
/// rows exist (§11, `nav::convs::expand`).
/// The focused agent, owned — the id every unfold gesture acts on.
/// The focused workspace, owned — every selection below is made inside it,
/// since the walk stopped crossing walls with bl-fa82.
/// ↑/↓ — step the selection ±`delta` through the **visible** list rows in paint
/// order and hand the keyboard over (rule 2). A collapsed subtree contributes
/// one row to that list, so the step skips it whole without the walk knowing
/// anything about folding — the ruling's *"don't automatically expand just from
/// going down"* with no branch implementing it. The request is unconditional:
/// with nothing to select, the composer is where the keyboard belongs anyway.
pub
/// → — unfold the selected row. Fires no verb and takes no keyboard: it
/// repaints a viewport (§11 rule 3), so the plane it was pressed on is still
/// live under the operator's hand.
pub
/// ← — fold the selected row shut, or, with nothing to fold, page the selection
/// up to its parent row (§11). The two are one gesture and this is the whole of
/// it: a row that *was* open closes, and one that was not walks out a level, so
/// `←` held down leaves a descent the way `↑` walks up a list. Paging is a
/// selection, so it lands the composer like every other; closing a fold is not,
/// and does not.
pub
/// The subagent field's click: flip one id in the expanded set through
/// [`toggle_path`](crate::jsonview::toggle_path) — the crate's one disclosure
/// toggle, shared with the transcript and queue folds — then keep §11's
/// visible-selection invariant by carrying a selection the fold just hid up to
/// the row that hid it. Reads the ancestor chain, so a selection three
/// generations down is caught by the same one check.
pub
/// Select a workspace and hand the keyboard over — the tab bar, the overflow
/// menu, and `new conversation`, which is the same move with the agent
/// selection cleared (the keyboard's `n` rides here, rule 2).
pub