ratatui-style 0.2.0

A CSS cascade engine for ratatui — selectors, specificity, inheritance, pseudo-states, and data-driven styling.
Documentation
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
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
# ratatui-style

[![crates.io](https://img.shields.io/crates/v/ratatui-style.svg)](https://crates.io/crates/ratatui-style)
[![docs.rs](https://docs.rs/ratatui-style/badge.svg)](https://docs.rs/ratatui-style)
[![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

**[English](README.en.md)**

一个面向 [ratatui](https://ratatui.rs) 的 CSS 级联引擎 —— 选择器、优先级、继承、伪状态、数据驱动样式。
输出原生 ratatui `Style` / `Block` / `Constraint`,不做并行渲染。

使用标准 CSS 属性名(`color`、`background-color`、`font-weight`、`border`、`padding`、`margin`、`text-align`、`width` …),
支持 `serde`(服务端驱动的 UI 可通过 JSON 传输样式),实现级联规则:
**来源层 × 优先级 × 继承 × 伪状态**。

## 截图展示

<p align="center">
  <a href="examples/00_hello_world.rs"><b>00_hello_world</b> · 最小首渲染</a><br>
  <img src="screenshot/hello-world.png" alt="hello world">
</p>

<p align="center">
  <a href="examples/06_tailwind.rs"><b>06_tailwind</b> · 工具类设计系统</a><br>
  <img src="screenshot/tailwind-style.png" alt="tailwind 风格">
</p>

<p align="center">
  <a href="examples/07_scifi_hud.rs"><b>07_scifi_hud</b> · 赛博朋克 HUD</a><br>
  <img src="screenshot/sci-fi-hud.png" alt="sci-fi HUD">
</p>

更多示例见下文 [示例](#示例) 一节。

## 快速开始

```rust
use ratatui_style::{CssStyle, Origin, OwnedNode, Stylesheet};

let mut sheet = Stylesheet::new();
sheet.add(
    "Button.primary",
    CssStyle::new().color("#fff").background("blue").bold(),
    Origin::User,
)?;

let node = OwnedNode::new("Button").with_classes(["primary"]);
let computed = sheet.compute(&node, None);

// 投影到原生 ratatui 类型:
let _style   = computed.to_style();         // → ratatui::style::Style
let _block   = computed.to_block();         // → ratatui::widgets::Block
let _area    = computed.apply_margin(area); // 缩小 Rect
let _layout  = computed.constraints();      // → (Constraint, Constraint)
let _align   = computed.alignment();        // → Alignment
```

### 盒模型 builder 的类型化输入

`.padding()` / `.margin()` / `.border()` 既能接受类型化值(零 panic),也能保留 CSS 简写字符串的便利:

```rust
use ratatui_style::{BorderStyle, CssStyle};

// 类型化 —— 不会 panic
CssStyle::new().padding(1u16);                       // 四边各 1
CssStyle::new().padding((0u16, 2u16));               // 上下 0、左右 2
CssStyle::new().padding((1u16, 2u16, 3u16, 4u16));   // 上右下左
CssStyle::new().border(BorderStyle::Rounded);
CssStyle::new().border((BorderStyle::Rounded, "#00d4ff"));

// 字符串简写仍可用(适合编译期已知的字面量,坏值会 panic)
CssStyle::new().padding("0 2").border("rounded #00d4ff");
```

## CSS 文本样式表

```rust
use ratatui_style::Stylesheet;

let sheet = Stylesheet::parse(r#"
    :root { --accent: #00d4ff; }

    Button.primary {
        color: var(--accent);
        background: blue;
        font-weight: bold;
        border: rounded;
        padding: 0 2;
    }
    Button:focus { background: green; }
    #save:disabled { color: gray; }
"#)?;
```

## 级联模型

级联按五个步骤为每个元素解析样式:

1. **收集** 所有选择器匹配该节点的规则。
2. **排序**`(来源层, 优先级, 源码顺序)` 升序。
3. **叠加** 声明 —— 后出现的规则逐字段覆盖前面的。
4. **继承** —— 可继承属性(`color``font-weight``font-style``text-decoration``underline-color``text-align`)从父元素的计算样式流入子元素的 `None` 字段。
5. **解析** `var()` 引用,对照令牌表替换为具体值。

### 来源层

规则按来源分层;同等优先级下,高来源层覆盖低来源层:

| 来源 | 优先级 | 用途 |
|---|---|---|
| `UserAgent` | 最低 | 内置默认值 |
| `Theme` | | 应用级主题 |
| `User` | | 用户配置 / CSS 文本 |
| `Inline` | 最高 | 行内样式 |

### 优先级(Specificity)

`(id数, class数+伪类数, 类型数)` —— 标准 CSS 优先级,以元组形式比较。
`*`(通用选择器)为 `(0, 0, 0)`。

## 支持的 CSS 属性

| 属性 | 值类型 | 映射到 |
|---|---|---|
| `color` | [颜色]#颜色语法 | `Style::fg` |
| `background` / `background-color` | 颜色 | `Style::bg` / `Block::style` |
| `font-weight` | `bold` / `normal` / `100``900` | `Modifier::BOLD`(见下方说明) |
| `font-style` | `italic` / `normal` | `Modifier::ITALIC` |
| `text-decoration` | `underline` / `line-through` / 两者组合 | `Modifier::UNDERLINED` / `CROSSED_OUT` |
| `underline-color` | 颜色 | `Style::underline_color` |
| `border` | `none` / `single` / `rounded` / `double` / `thick` [颜色] | `Block::borders` + `border_type` |
| `border-top` / `border-right` / `border-bottom` / `border-left` / `border-x` / `border-y` |`border` 简写(`<样式> [颜色]`),只作用于指定边 | `Block::borders`(按边组合) |
| `padding` | `1` / `1 2` / `1 2 3` / `1 2 3 4` | `Block::padding` |
| `margin` | 同 padding 简写 | `Rect` 缩小 |
| `text-align` | `left` / `center` / `right` | `Alignment` |
| `width` / `height` | `auto` / `10` / `50%` / `min(3)` / `max(5)` | `Constraint` |

### 边框:单边与组合

`border` 简写画出全部四条边。要只画某一条(或几条)边,用单边声明:

```css
.card { border-bottom: rounded #00d4ff; }   /* 只画底边 */
.tabs { border-bottom: single; }
```

支持的属性:

| 属性 | 作用的边 |
|---|---|
| `border-top` ||
| `border-right` ||
| `border-bottom` ||
| `border-left` ||
| `border-x` | 左 + 右 |
| `border-y` | 顶 + 底 |

值格式与 `border` 一致:`<样式> [颜色]`,如 `border-bottom: rounded red`。

**组合语义**:单边声明在级联中以「按位 OR」累加,而非互相覆盖。所以两个原子类可以组合出顶 + 底:

```css
.bt { border-top: single; }
.bb { border-bottom: single; }
/* <div class="bt bb"> → 顶 + 底两条边 */
```

这与 Tailwind 式的 `.rounded`(设样式)+ `.border-slate-700`(设颜色)组合在同一元素上拼成一条完整边框是同一套机制。`border` 简写(全四边)的优先级更高:它声明的是「全部边」,与单边声明组合后仍是全部边(不会收窄)。

### `font-weight` 的限制

ratatui(以及终端本身)只支持单一的加粗修饰位(`Modifier::BOLD`),没有真正的字重(100–900)细分。因此:

- `font-weight: bold` / `bolder` / `700``900` → 加粗。
- `font-weight: normal` / `lighter` / `100``500` → 不加粗。
- `500``normal` 等价,`600``900` 等价。

这是终端字体能力的固有限制,不是解析器缺陷。

## 颜色语法

所有颜色属性支持:

| 语法 | 示例 |
|---|---|
| 十六进制 3/4/6/8 位 | `#fff` `#fff0` `#ff8800` `#ff8800ff` |
| `rgb()` / `rgba()` | `rgb(255, 128, 0)` `rgba(0,0,0,0.5)` |
| CSS 命名颜色 | `red` `blue` `cyan` `orange` `gold`|
| `transparent` / `none` / `reset` | 重置为终端默认 |
| `inherit` | 强制从父元素继承 |
| `var(--name)` | CSS 自定义属性,可带回退值:`var(--accent, #fff)` |

## 选择器与伪类

复合选择器格式 `Type.class#id:pseudo…`,支持逗号列表和 `*` 通配:

```
Button                /* 类型 */
.primary              /* 类 */
#save                 /* id */
Button.primary:focus  /* 复合 */
Text, .muted, #title  /* 逗号列表 */
*                     /* 通配 */
```

伪类:`:focus` `:hover` `:disabled` `:checked` `:active`

## 继承与 `var()`

`color`、`font-weight`、`font-style`、`text-decoration`、`underline-color`、`text-align`
可从父元素的计算样式继承。`var(--name)` 从 `:root` 令牌表解析
(也可通过编程方式构建 `ThemeTokens`,或从 themekit 桥接)。

`var()` 当前支持**颜色**与**长度**(`width` / `height`)两类自定义属性:

```css
:root {
    --accent: #00d4ff;   /* 颜色 */
    --sidebar-w: 22;     /* 长度:等价于 22 个 cell */
    --half: 50%;         /* 长度:百分比 */
}
.side { width: var(--sidebar-w); }
.col  { width: var(--half); }
```

颜色与长度的字面量语法互不重叠(`#fff`/`rgb()`/命名色 vs `10`/`50%`/`auto`/`min(n)`),
因此 `:root` 会自动判定每个 `--name` 的类型。`var()` 引用可链式(`--w: var(--w2)`),
类型由链的终点决定;未定义或类型不符的 `var()` 在宽松解析时降级(颜色 → `Reset`,长度 → `Auto`),
在严格模式([`Stylesheet::parse_strict`])下报 `UndefinedVariable`。

> **暂不支持**`padding` / `margin` / `border``var()` —— 这些属性的 `BoxEdges` / `BorderSpec`
> 表示尚无 `Var` 变体,改动较大,留待后续。
>
> **暂不支持**`Block` 标题样式映射(`title-color` / `title-align`)。ratatui 的
> `Block::title_style` / `title_alignment` 已就绪,但本 crate 目前无法设置标题文本本身,
> 仅映射标题样式意义有限,留待后续与标题内容一起支持。

```rust
use ratatui_style::{CssStyle, Origin, OwnedNode, Stylesheet};

let mut sheet = Stylesheet::new();
sheet.tokens_mut().insert("accent", "#00d4ff");

sheet.add("Panel", CssStyle::new().color("#cdd6f4").italic(), Origin::Theme)?;
sheet.add("Button", CssStyle::new().background("var(--accent)").bold(), Origin::User)?;
sheet.add("Button:disabled", CssStyle::new().color("gray"), Origin::User)?;

// Panel 解析自身样式
let panel = sheet.compute(&OwnedNode::new("Panel"), None);

// Text 从 Panel 继承 color + italic
let text = sheet.compute(&OwnedNode::new("Text"), Some(&panel));

// 禁用按钮::disabled 规则生效,color=gray
let btn = sheet.compute(
    &OwnedNode::new("Button").with_state(ratatui_style::State::disabled()),
    Some(&panel),
);
```

### 遍历组件树:`CascadeContext`

真实组件树里要给每个子节点手动传 `Some(&parent)` 既啰嗦又易错。`CascadeContext`
是一个级联遍历器:它持有一个 `Stylesheet` 引用 + 可复用 scratch + 一个 parent
计算样式栈。`enter(node)` 自动用栈顶(若有)作为 parent 计算节点样式、压栈并返回
owned 副本;`leave()` 弹栈。这样遍历组件树时完全无需手写 parent 穿线。

```rust
use ratatui_style::{CascadeContext, OwnedNode, Stylesheet};

let sheet: Stylesheet = /* … */;
let mut ctx = CascadeContext::new(&sheet);

// Root
let root = ctx.enter(&OwnedNode::new("Root"));
// …渲染 root…

// Panel(Root 的子节点)
let panel = ctx.enter(&OwnedNode::new("Panel"));
// …渲染 panel…

// Text(Panel 的子节点)—— 自动继承 Panel 的 color
let text = ctx.enter(&OwnedNode::new("Text"));
// …渲染 text…
ctx.leave(); // 回到 Panel 上下文

ctx.leave(); // 回到 Root 上下文
ctx.leave(); // 完成
```

> `enter` 返回 owned 副本,调用者无需操心借用顺序(避免返回 `&self` 借用
> 导致无法嵌套 `enter` 的问题)。压栈时的一次 `clone` 也极廉价:解析后的
> `ComputedStyle` 只含 `Literal`/`Reset` 字段(`var()` 已解析),无 `String`/
> `Box`/`Vec` 堆字段,是纯栈上 memcpy。

## 严格模式与带位置的错误

默认的 [`Stylesheet::parse`] 是**宽松**的:未知属性被静默忽略(向前兼容),
未定义的 `var()` 在级联时降级为 `Reset`。这对生产渲染很稳健,但对"手写 CSS"
的诊断体验不佳——拼写错误悄悄消失。

为此提供两个改进:

### 1. 解析错误带行:列

所有由解析产生的 [`CssError`] 现在携带一个 1-based 的 [`Loc { line, column }`]。
注释剥离被改为**位置保持**(把注释字符替换为空格、但保留其中的 `\n`),所以
清洗后的文本与原输入等长,字节偏移可直接映射回原文行:列。

```rust
use ratatui_style::Stylesheet;

let css = "Button {\n    color: red;\n    background: #zzz;\n}\n";
let err = Stylesheet::parse(css).unwrap_err();
let loc = err.loc.unwrap();
assert_eq!(loc.line, 3); // 指向写错的 #zzz 那一行
```

### 2. `parse_strict` 严格解析

[`Stylesheet::parse_strict`] 在 [`parse`] 的基础上把两种情况升级为硬错误:

- **未知属性**:不在已知属性集里、且不是 `--` 前缀自定义属性的声明。错误类型为
  `CssErrorKind::UnknownProperty`,loc 精确指向该属性名。
- **未定义变量**:没有 fallback 的 `var(--name)`,且 `name` 不在本样式表的令牌表
  中。错误类型为 `CssErrorKind::UndefinedVariable`(loc 目前为 `None`,见下文注记)。
  带 fallback 的 `var(--nope, #fff)` 不会报错。

```rust
use ratatui_style::{Stylesheet, CssErrorKind};

// 拼写错误:colr → UnknownProperty,loc 指向第 1 行
let err = Stylesheet::parse_strict("Foo { colr: red; }").unwrap_err();
assert!(matches!(err.kind, CssErrorKind::UnknownProperty(ref p) if p == "colr"));

// 未定义变量 → UndefinedVariable
let err = Stylesheet::parse_strict("Foo { color: var(--nope); }").unwrap_err();
assert!(matches!(err.kind, CssErrorKind::UndefinedVariable(_)));

// 先声明再引用 → 正常
Stylesheet::parse_strict(":root{--x:red;}\nFoo{color:var(--x);}").unwrap();
// 带 fallback → 正常
Stylesheet::parse_strict("Foo { color: var(--nope, #fff); }").unwrap();
```

> **注记**:未定义变量错误目前不带精确 `loc`(为 `None`)。属性错误的 loc 是精确的。
> 这是有意取舍:属性名在解析阶段即可定位,而 `var()` 出现的位置需要额外的解析期
> 记录才能回溯,成本较高,故先以不带 loc 的形式报出 kind。

[`Stylesheet::parse`]: https://docs.rs/ratatui-style/latest/ratatui_style/stylesheet/struct.Stylesheet.html#method.parse
[`Stylesheet::parse_strict`]: https://docs.rs/ratatui-style/latest/ratatui_style/stylesheet/struct.Stylesheet.html#method.parse_strict
[`parse`]: https://docs.rs/ratatui-style/latest/ratatui_style/stylesheet/struct.Stylesheet.html#method.parse
[`CssError`]: https://docs.rs/ratatui-style/latest/ratatui_style/struct.CssError.html
[`Loc { line, column }`]: https://docs.rs/ratatui-style/latest/ratatui_style/struct.Loc.html

## 框架集成

在你的节点类型上实现 `StyledNode` —— 引擎不依赖任何特定框架:

```rust
use ratatui_style::{Classes, StyledNode, State, Position};

impl StyledNode for MyNode {
    fn type_name(&self) -> &str { &self.kind }
    fn id(&self) -> Option<&str> { self.id.as_deref() }
    // classes() 现在返回零分配视图 Classes<'_>,而非 Vec<&str>。
    fn classes(&self) -> Classes<'_> {
        Classes::from_vec(self.classes.iter().map(String::as_str).collect())
    }
    fn state(&self) -> State { self.state }
    fn position(&self) -> Position { self.position.clone() }
}
```

### 每帧零分配的热路径

draw 循环里反复调用 `compute` 会成为分配热点。用借用的 [`NodeRef`](构造零分配)
+ 复用的 [`ComputeScratch`](匹配缓冲跨帧复用)来消除每帧分配:

```rust
use ratatui_style::{NodeRef, ComputeScratch};

// 在 draw 之外(或 main 中)持有一个 scratch,跨帧复用容量:
let mut scratch = ComputeScratch::new();

// draw 循环内:NodeRef 全是 &'static str 借用,零 String/Vec 分配。
let node = NodeRef::new("Button").classes(&["primary"]).state(State::focus());
let computed = sheet.compute_with(&node, None, &mut scratch);
```

`OwnedNode` 仍保留作为便利的拥有型节点(测试、一次性查询);它的 `classes()` 仍会
产生一次 `Vec` 分配,但热点路径已迁到 `NodeRef`。

### 运行时主题与文件热重载

主题不一定来自编译期 `css!` 宏。`RuntimeStyle` 支持两种构造基表(base stylesheet):

- **静态基表**`RuntimeStyle::new(&'static Stylesheet)`):`css!` 宏工作流,零成本。
- **拥有型基表**`RuntimeStyle::from_owned(Arc<Stylesheet>)`):从磁盘/配置/网络在运行时
  解析的主题,无需 `Box::leak`
```rust
use std::sync::Arc;
use ratatui_style::{RuntimeStyle, Stylesheet};

let css = std::fs::read_to_string("theme.css")?;
let style = RuntimeStyle::from_owned(Arc::new(Stylesheet::parse(&css)?));
```

加载后可用 `load_override(&path)` 一次性叠加用户层 CSS(`Origin::User` 覆盖 `Origin::Theme`)。
更实用的是基于 mtime 的轻量热重载 —— 在 app 的 tick 里调用,文件没改就不重解析:

```rust
// 在事件循环的 tick / poll 里:
if style.reload_if_changed(path.as_ref())? {
    // 主题文件变了 —— 已重新解析合并,下一次 compute() 即生效
}
```

`reload_if_changed` 只在文件 mtime 变化时返回 `true`;文件被删除视为"移除 override"
(与 `load_override` 的 `NotFound` 语义一致)。文件系统无法提供 mtime 时降级为"每次都重载",
确保不会静默丢失更新。

## Feature 标志

| Feature | 默认 | 说明 |
|---|---|---|
| `serde` || 为所有值类型提供 `Serialize`/`Deserialize` —— JSON 属性映射、配置文件、传输格式 |
| `themekit` || `ThemeTokens::from_themekit` —— 将 `ratatui-themekit` 语义颜色槽桥接为 CSS `var()` 令牌 |

禁用默认 feature 可获得零依赖的纯样式引擎:

```toml
[dependencies]
ratatui-style = { version = "0.1", default-features = false }
```

## 示例

```sh
# Hello, World! —— 最小示例:一条 CSS 规则 → 渲染 "Hello, World!"
cargo run --example 00_hello_world

# 交互式仪表盘 —— 纯 CSS 驱动,单一样式表
cargo run --example 05_dashboard

# 级联演示 —— 继承、var()、优先级、伪状态
cargo run --example 03_cascade

# CSS 文本样式表解析
cargo run --example 02_stylesheet

# 颜色与值解析
cargo run --example 01_values

# css! 宏:编译期嵌入 + 运行时覆盖
cargo run --example 09_runtime_override

# scss! 宏:编译期嵌入 SCSS(需要 scss feature)
cargo run --example 10_scss_embed --features scss

# themekit 桥接(需要 themekit feature)
cargo run --example 11_themekit_bridge --features themekit

# CSS 驱动布局 —— width/height 声明 → ratatui Constraint
cargo run --example 13_sizing

# 服务端驱动 UI —— 通过 serde 加载 JSON 样式
cargo run --example 14_data_driven

# 严格模式 —— 捕获 CSS 属性名拼写错误和未定义变量
cargo run --example 15_strict
```

## 预设库:`ratatui-style-presets`

不想从零写 CSS?配套 crate [`ratatui-style-presets`](crates/presets) 提供开箱即用的主题与样式,按 feature flag 按需启用:

| 预设 | 说明 |
|---|---|
| `default`(始终可用) | neutral 默认主题 + 基础组件类(`Button`/`Panel`/`Text`/`List`/`Badge` …) |
| `tailwind` | Tailwind 式原子工具类(`.bg-*`/`.text-*`/`.p-*`/`.rounded` …) |
| `widgets` | ratatui widget 类型默认样式(`Table`/`List`/`Tabs`/`Gauge`/`Scrollbar` …) |
| `catppuccin` / `nord` / `dracula` | 官方调色板,填同一套语义 token |

所有主题填**同一套规范语义 token**(`--bg`/`--text`/`--accent`/`--success`/…),换基表即换肤:

```rust
use ratatui_style_presets::{merge, Preset};

// 默认主题 + widget 默认样式 + Catppuccin 调色板,叠成一张表。
let sheet = merge(&[Preset::Default, Preset::Widgets, Preset::Catppuccin]);
let computed = sheet.compute(&ratatui_style::NodeRef::new("Button").classes(&["primary"]), None);
let _block = computed.to_block();
```

```sh
# 预设画廊:侧栏浏览全部预设,`c` 切换「整框随主题重渲染」
cargo run -p ratatui-style-presets --example 02_gallery --all-features
```

详见 [presets/README.md](crates/presets/README.md)。

## 实际案例

### [disk-cleaner]https://github.com/Liangdi/disk-cleaner

跨平台的终端磁盘占用分析与清理工具(WinDirStat 风格目录树 + Kondo 风格构建产物清理)。
其科幻 HUD 风格 TUI —— 目录树、详情面板、进度条、直方图 —— 的样式来自
`src/tui/theme.css`,在编译期通过 `css!` 宏加载;改 CSS 变量与选择器即可换肤,
无需改动 Rust 代码。

<p align="center">
  <a href="https://github.com/Liangdi/disk-cleaner">
    <img src="https://raw.githubusercontent.com/Liangdi/disk-cleaner/master/screenshot/tui.png" alt="disk-cleaner TUI" width="580">
  </a>
</p>

### [ra-killer]https://github.com/Liangdi/ra-killer

自动监控并杀死高内存占用 `rust-analyzer` 进程的 TUI 工具。其交互式终端界面
(`--tui` 模式)—— 实时内存进度条、进程列表(PID / CPU / 内存)、阈值告警配色 ——
全部由 `ratatui-style` 的 CSS 样式表驱动。

<p align="center">
  <a href="https://github.com/Liangdi/ra-killer">
    <img src="https://raw.githubusercontent.com/Liangdi/ra-killer/master/screenshot/tui.png" alt="ra-killer TUI" width="580">
  </a>
</p>

> 两者均采用链接时优化等尺寸优化构建(disk-cleaner 用 `lto = "fat"`,ra-killer 用
> `opt-level = "z"` + `lto` + `strip` + `panic = "abort"`),`ratatui-style` 在这些
> 精简 release 二进制中运行良好。

## 生态定位

| Crate | 定位 | `ratatui-style` |
|---|---|---|
| `ratatui-themekit` | 15 个语义颜色槽 + 调色板 | **组合使用** —— `ThemeTokens::from_themekit` 填充 CSS 变量 |
| `tui-theme-builder` | 编译期 `Style`| `ratatui-style` 覆盖 **运行时/配置驱动** 场景 |
| `lipgloss` | "终端 CSS"(自有渲染栈) | 同类 DX,基于 ratatui 的 buffer 模型 |

## 当前状态

已实现:CSS 文本解析器、复合选择器、优先级、级联层(`UserAgent` < `Theme` < `User` < `Inline`)、
伪状态、`var()`(含回退值)、继承、盒模型(`padding` / `margin` / `border`)、
尺寸(`width` / `height` → `Constraint`)、`serde` 集成、`themekit` 桥接。

计划中:后代/子代组合器(`A B`、`A > B`)、`:nth-child`、`@media`、`ComputedStyle` 缓存。

## 许可证

MIT