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
//! `<table>` column-width synchronization pass.
//!
//! ## Why this exists
//!
//! The C.8a UA rules give `<td>` / `<th>` a default `width: Auto`,
//! which makes each cell size to its own content. Inside a single
//! `<tr>` (our flex-direction:Row container) cells sit next to
//! each other at content-driven widths, but DIFFERENT rows would
//! land on different widths — so columns don't line up.
//!
//! This pass walks every `<table>` in the DOM and resolves each
//! column's *used* width — **the column's author width if one is
//! specified, else its content width** — recording it on each cell's
//! [`TuiExt::table_used_width`](crate::TuiExt::table_used_width), a
//! **layout output** field flex reads as the cell's main size. Every
//! cell in column N gets the same width, so columns line up.
//!
//! `TABLE-COLSYNC-1`: the used width is *never* written back to
//! `inline_style` — author width (input) and computed width (output)
//! stay separate. That's why an explicit width survives a re-size
//! (`Column.width` works), and why no `data-rdom-colsync` re-cascade
//! hack is needed: the value is read by full layout each frame, not by
//! the incremental cascade. (Full CSS table layout — `display:table`,
//! the auto min/max algorithm, spanning, CSS-rule widths — is the
//! `TABLE-TFC-1` roadmap item; this is the bounded, web-faithful fix.)
//!
//! ## Content measurement
//!
//! v1 measures display width via `UnicodeWidthStr` on the
//! concatenated text descendants. Nested element widths are NOT
//! accounted for — a cell containing `<b>bold</b>` reports the
//! bold text's width; a cell with a `<progress>` bar reports 0
//! (no text descendants). Authors who need richer measurement
//! override with explicit `Fixed` widths via author CSS.
//!
//! Padding budget: the UA `<td>` / `<th>` rule uses `padding:
//! 0 1 0 1` (2 horizontal cells). The pre-pass adds that 2 to the
//! content width. Authors who override padding have to override
//! the width too.
//!
//! ## When it runs
//!
//! [`size_all_tables`] is called once from `App::build` after the
//! other builtin installs. Apps that mutate table content at
//! runtime can call it themselves to re-sync.
//!
//! ## Deferred to the full table model (`TABLE-TFC-1`)
//!
//! - **Explicit width source.** Author widths are read from
//! `inline_style.width` (set directly / via `set_width` / a
//! `Column` width), NOT from a CSS rule (`td { width }`) — this pass
//! runs before cascade. The TFC computes widths in-layout, post-cascade.
//! - `colspan` / `rowspan` spanning-cell width distribution.
//! - `<col>` / `<colgroup>` width hints.
//! - Percentage column widths + the auto min/max-content redistribution
//! when an explicit table width conflicts with content.
//! - Content measurement still eyeballs text via `UnicodeWidthStr` on
//! text descendants (nested-element / replaced-content widths ignored).
use ;
use UnicodeWidthStr;
use crateTuiDom;
use crateSize;
use crateValue;
/// Horizontal padding implied by the UA `<td>` / `<th>` rule
/// (`padding: 0 1 0 1`). The pre-pass adds this to measured
/// content widths.
const CELL_H_PADDING: u16 = 2;
/// Walk the whole DOM; size columns on every `<table>` found.
/// Resolve each column's *used* width for a single `<table>` and record it on
/// every cell's [`TuiExt::table_used_width`](crate::TuiExt::table_used_width)
/// (a **layout output** field). Flex reads it as the cell's main size, so all
/// cells in a column line up. No-op when the table has no rows.
///
/// Per column the used width is **the column's author width if one is
/// specified, else its content width** (`TABLE-COLSYNC-1`):
/// - **Author width (input):** a cell's `inline_style.width: Fixed(n)`
/// (set directly, or via `set_width` / a `Column` width). Respected and
/// never overwritten — the max specified across the column wins. *(Divergence:
/// a width from a CSS **rule** — `td { width }` — is not honored here; this
/// pass runs before cascade and reads only inline/author widths. Full CSS
/// table layout is `TABLE-TFC-1`.)*
/// - **Content (fallback):** the widest cell's text width + the UA cell
/// padding ([`CELL_H_PADDING`]).
///
/// Crucially this **does not touch `inline_style`** (so author intent and the
/// computed result never conflate, the dead-`Column.width` / `::after`-clip
/// bugs go away) and needs **no cascade dirty signal** — the value is read by
/// full layout each frame, not by the incremental cascade.
/// The cell's *author-specified* fixed width (`inline_style.width: Fixed(n)`),
/// or `None`. The column-sizing input — read, never written.
// ── Tree traversal helpers ─────────────────────────────────────────
/// Collect every `<tr>` under a `<table>`, descending through the
/// optional `<thead>` / `<tbody>` / `<tfoot>` row groups.
/// Display width of the cell's concatenated text descendants —
/// walks text nodes recursively. Wide CJK glyphs contribute 2
/// cells each via `UnicodeWidthStr`. Empty cells measure 0.