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
//! [`Style`] — non-colour visual tokens (spacing, radius, border width,
//! focus-ring weight, …), the geometry half of theming that
//! [`crate::Theme`] deliberately does not cover.
//!
//! # Why this is a separate struct, not new `Theme` fields
//!
//! `Theme`'s own module doc already states the rule: adding a `pub`
//! field to `Theme` is a breaking change, because `coord-tui` builds
//! three of its four palettes with exhaustive struct literals and no
//! `..Default::default()` spread. A flat colour struct and a flat
//! geometry struct would share that exact liability, for no benefit —
//! colour and geometry are independent axes an app may want to override
//! separately (a high-contrast *colour* theme with the *same* spacing,
//! or a compact-density *geometry* preset layered under three different
//! colour themes). Keeping them as two independently defaulted types
//! means a consumer who only ever wants to touch colours — today, both
//! of them — never has to look at this file at all, and a later field
//! goes through the ordinary additive path this file sets up from its
//! first commit.
//!
//! # Where each token maps on TUI
//!
//! D-014 in `docs/decisions/DECISIONS.md` requires every new capability
//! to declare its TUI story. Every [`Style`] token's is the same shape,
//! stated once here rather than per-field: TUI paints a whole-cell
//! grid, so a sub-cell geometry token has nothing to refine — see
//! [`crate::paint_surface`]'s module doc, "Why TUI stays out", for the
//! identical reasoning applied to the drawing-verb trait this struct's
//! tokens are consumed through. Concretely:
//!
//! - **No-op** — the token has no effect because the TUI rasteriser
//! for that primitive doesn't read [`Style`] at all. [`Self::focus_ring_width`]
//! is this: [`crate::tui::draw_focus_ring`] always paints a 1-cell
//! box-drawing border regardless of what a GUI backend's style carries
//! (its own doc already says so — this struct doesn't change that).
//! - **Cell-quantised** — a future spacing/padding token would round to
//! whole cells rather than being dropped outright, the same way
//! `LayoutMetrics::cell_quantum` already quantises hit-test geometry
//! (`docs/decisions/DECISIONS.md` D-016's "Paint/click drift" note).
//! No token is shaped that way yet — see *Scope* below.
//!
//! No [`Style`] token is physically absent on TUI the way a tray icon
//! or dock badge is (D-014's **N/A** tier) — every one of them is a
//! pixel-only *refinement* with a well-defined "coarsest cell" fallback,
//! so **no-op**/**cell-quantised** cover the whole token set by
//! construction, not by accident.
//!
//! # Scope — first token only
//!
//! This is deliberately a **one-token start**, not the full tokens
//! `CLAUDE.md` eventually wants (padding, corner radius, border width,
//! per-role font size, …). [`Self::focus_ring_width`] is first because
//! it is the one geometry value all three pixel backends agree on
//! byte-for-byte, and it is painted through the shared
//! [`crate::paint_surface::PaintSurface`] trait, so a single call
//! site reaches `GtkBackend`, `MacBackend` and `WinBackend` at once.
//! Each further token — padding, corner radius, border width on the
//! primitives that still hardcode them — is its own follow-up PR, one
//! token/primitive pair at a time, the same discipline
//! `docs/decisions/DECISIONS.md` already applies to the `*_layout`
//! coordinate-frame conversions (D-005) and the LOCAL→ABSOLUTE
//! migration (D-016's point 4): batching every hardcoded literal in the
//! codebase into one PR is what `PRIMITIVE_RULES.md` rule 4 forbids, at
//! a much larger scale.
//!
//! See `docs/decisions/DECISIONS.md` D-017 for the full design — token
//! catalogue, why a separate struct, and the TUI-mapping rule — this
//! module implements only the first slice of it.
use ;
/// Non-colour visual tokens shared by every pixel backend. See the
/// module doc for why this is a separate type from [`crate::Theme`],
/// which rasterisers that want styling read alongside it the same way
/// [`crate::Backend::style`] pairs with [`crate::Backend::theme`].
///
/// `#[non_exhaustive]`, from this struct's first commit: every future
/// token (padding, corner radius, border width, …) lands as a purely
/// additive field, with no retrofit later needed — the same
/// `Default`/`with_*` shape `TextInput`, `Toolbar` and `Editor` carry
/// (see [`Style::default`] and
/// [`Style::with_focus_ring_width`]). Unlike a plain struct,
/// `#[non_exhaustive]` blocks *any* struct-literal construction from
/// outside this crate — including `Style { focus_ring_width: 4.0,
/// ..Default::default() }` — so a downstream consumer always goes
/// through `Default::default()` plus a `with_*` builder, which is the
/// construction shape that keeps compiling as fields are added. Compare
/// `Theme`'s own doc, which explains why it *can't* adopt this attribute
/// while `coord-tui` builds it with exhaustive literals — `Style`
/// avoids that trap only because it is new.