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
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
use crateIntent;
/// Which way the light falls across a two-tone edge.
///
/// The whole content of a bevel, once colour and thickness are deferred. The
/// light is always assumed to come from the top left: every consumer measured
/// agreed on that and none of them ever varied it, so it is an invariant here
/// rather than a parameter.
///
/// # The two corners that belong to both edges
///
/// Top-right and bottom-left are where the lit run meets the shaded one, and
/// the description's claim is that they belong to *both*. How a renderer says
/// that is its own business, because the answer is bounded by resolution and
/// not by taste:
///
/// - A terminal cell is roughly 8x17 device pixels, so giving the whole corner
/// to one tone thickens that edge by a cell and reads as one run overrunning
/// the other. A half-cell glyph divides the cell already, so `makeover-tui`
/// splits it and recovers real information. Its box-drawing fallback cannot:
/// a single stroke has no half to give, so there both corners go to dark.
/// - A pixel bevel is a one-point stroke by default, which makes the corner a
/// one-point square. There is nothing to divide — a diagonal seam across one
/// point is sub-pixel, and antialiasing renders it as the blend a mitred join
/// already produces. So `makeover-immediate` mitres and is *not* diverging;
/// it is the same rule at a resolution where the split degenerates.
///
/// Stated here so the difference reads as a decision rather than as drift. A
/// renderer with room to divide the corner should; one without should mitre or
/// pick the shaded tone, and neither is a bug.
/// One side of a bevel, named by the intent it takes.
/// A surface intent a region is filled with.
///
/// `#[non_exhaustive]`, so a renderer must carry a wildcard arm and a new
/// member is additive rather than breaking. The vocabulary exists to grow and
/// the renderers exist to disagree about how much of it they answer, so growth
/// must not be a lockstep event. The renderer's wildcard is not a hole:
/// [`Fill`] is resolved through a fallible lookup, and a missing intent is
/// answered with structure rather than with a substituted colour.
///
/// [`Sunken`]: Fill::Sunken
// No `fallback` here, deliberately. An earlier cut had `Fill::Well` fall back
// to `Fill::Page` so a consumer on makeover 2.2.0, which has no `surface-well`,
// had something to paint. makeover-tui found that wrong within a day: page is
// the surface a well is usually cut into, so on a terminal that substitution
// produces exactly the invisibility it was meant to prevent, and the right
// answer there is a drawn edge rather than a different colour.
//
// Substituting one intent for another is renderer policy. The description says
// what the region is and stops.
/// How a region sits relative to the surface behind it.
///
/// Fill and bevel are named together because naming them apart is what let
/// them disagree. Every consumer measured had at least one region carrying a
/// raised bevel over a recessed fill: audiofiles fixed it in `raised_frame`
/// and recorded the bug in its doc comment, and Balanced Breakfast still had
/// twelve of them a year later. A single name for the pair makes that
/// unrepresentable.
/// `#[non_exhaustive]` for the same reason as [`Fill`], and in the same
/// release: a depth this renderer has no drawing for should cost it a
/// wildcard arm, not a compile error and a wait on someone else's publish.
/// An interaction state a region can be in, beside whatever [`Depth`] it is.
///
/// Orthogonal to depth on purpose. A disabled button is still [`Depth::Raised`]
/// and a disabled field is still a [`Depth::Well`], so folding either member
/// into `Depth` would make [`Depth::bevel`] and [`Depth::fill`] answer for
/// something that is not a depth, and would leave disabled-button and
/// disabled-field sharing one variant that cannot tell them apart.
///
/// # Why hover and pressed are not members
///
/// The line is whether every renderer has the state to express, not whether CSS
/// does. Hover is renderer policy and `makeover-webview` says so in its own
/// header: a terminal and an immediate-mode painter have no pointer hovering
/// over anything, and pressed already arrives through [`Bevel::pressed`] and
/// [`Depth::pressed`], where it belongs, because pressing is a depth inversion
/// rather than a separate condition.
///
/// Focus and disabled are different in kind. A TUI has a focused widget and a
/// greyed-out one; so does egui. Both were unsayable here, so all three webview
/// consumers supplied them from outside the primitive by out-specifying rules
/// they did not own: goingson alone carries 19 of them, and the MNW server
/// another 21. That is the divergence this crate exists to end, arriving one
/// layer down.
///
/// # The principle this encodes
///
/// A primitive owns every state it implies. A renderer that emits a hover rule
/// for a thing owes disabled and the capability answer for that same thing,
/// because anything less exports the completion work to N consumers who will
/// each do it differently.
///
/// Focus is not on that list and is not on this axis. It is the renderer's,
/// decided after the description; see the crate header, "Reach,
/// focus and the focus ring", for the three terms and who owns each.
///
/// `#[non_exhaustive]` for the reason [`Fill`] and [`Depth`] carry it: growth
/// must not be a lockstep event across the three renderers.