Unlucky Launcher v0.2.3
CI / runtime smoke (macos arm64, probe) (push) Skipped
Release guard / 发布标记语义守门(push tag → 稳定版) (push) Canceled after 0s
Release / 选槽位(默认两个 Windows) (push) Canceled after 0s
Release / build (${{ matrix.slot }}) (push) Canceled after 0s
Release / GitHub Release (push) Canceled after 0s
CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
CI / runtime smoke (ubuntu x64, xvfb) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好一个文件) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s

This commit is contained in:
AskaEth committed 2026-11-08 00:15:58 +00:00
commit d616d65ea1
1219 files changed
+237663

No files matched your search

+371
View File
@@ -0,0 +1,371 @@
//! 组件档位数据表(**下标必须等于 `Component as usize`**,由测试钉死)。
//!
//! 一行一个组件。改值就是在改全站观感——同步协作文档再改。
//!
//! **末尾 5 行(M69 G9)** 的来源是 `research/15` §18~§23(同一套 `material3-android:1.4.0` 源码):
//! Checkbox / Radio / Search / NavigationDrawer / Divider。其中 Checkbox 的 **2dp** 圆角走
//! [`ComponentRadius`](D32 第二层,不塞进 `ShapeScale`),Divider 的厚度是一像素线(G12)而不在这张表里。
//!
//! **高程/形状取值来源(A 级)**:`androidx.compose.material3.tokens` 的各组件 `<X>Tokens.kt`
//! (FilledCard/OutlinedCard/ElevatedCard/Menu/Dialog/Snackbar/AppBar/List)。已登记的两处偏离:
//! - `Tooltip`:`PlainTooltipTokens.kt` 只有 Corner 行、**没有 Elevation 行**,Table 的 Level2 是
//! B 级(沿用 M3 旧规范「浮层 3dp」的直觉),有官方值后以官方为准;
//! - `ListItem`:官方 `ListTokens` 用 `CornerNone`(直角行),本项目取 `Small(8)`——列表行在本
//! 启动器里是**卡片内的独立可点块**(不是贴边通栏行),圆角与卡片同族更协调;偏差 8dp 已登记。
use super::{Component, ComponentSpec};
use crate::elevation::Elevation;
use crate::motion::{
DurationToken, Easing, SPRING_EXPRESSIVE_SPATIAL_DEFAULT, SPRING_EXPRESSIVE_SPATIAL_FAST,
};
use crate::shape::{ComponentRadius, ShapeMorph, ShapeScale};
use crate::typography::TypeRole;
/// 一个组件的档位行(`const fn` 便捷构造,字段名在 [`ComponentSpec`] 里)。
const fn spec(
component: Component,
shape: ShapeScale,
elevation: Elevation,
text: TypeRole,
support: Option<TypeRole>,
motion: Easing,
duration: DurationToken,
morph: Option<ShapeMorph>,
) -> ComponentSpec {
ComponentSpec {
component,
shape,
micro_radius: None,
elevation,
text,
support,
motion,
duration,
morph,
}
}
/// 圆角走「微控件层」(D32)的档位行:`shape` 自动填占位 `None`,半径取白名单值。
///
/// 为什么单独一个构造器:`micro_radius` 与 `shape = None` 必须成对出现,用构造器保证配对,
/// 不让 25 行表里出现「填了微半径忘了把 shape 清掉」。
const fn spec_micro(
component: Component,
micro_radius: ComponentRadius,
elevation: Elevation,
text: TypeRole,
support: Option<TypeRole>,
motion: Easing,
duration: DurationToken,
morph: Option<ShapeMorph>,
) -> ComponentSpec {
ComponentSpec {
component,
shape: ShapeScale::None,
micro_radius: Some(micro_radius),
elevation,
text,
support,
motion,
duration,
morph,
}
}
/// 25 个组件的档位表。
pub(super) const SPECS: [ComponentSpec; 25] = [
spec(
Component::TopAppBar,
ShapeScale::None,
Elevation::Level0,
TypeRole::TitleLarge,
Some(TypeRole::BodySmall),
Easing::Standard,
DurationToken::Medium2,
None,
),
spec(
Component::NavigationItem,
ShapeScale::Full,
Elevation::Level0,
TypeRole::LabelLarge,
None,
Easing::Emphasized,
DurationToken::Medium2,
Some(ShapeMorph::new(
ShapeScale::Large,
ShapeScale::Full,
SPRING_EXPRESSIVE_SPATIAL_FAST,
)),
),
spec(
Component::PrimaryButton,
ShapeScale::Full,
Elevation::Level0,
TypeRole::LabelLarge,
None,
Easing::Emphasized,
DurationToken::Short4,
None,
),
spec(
Component::SecondaryButton,
ShapeScale::Full,
Elevation::Level0,
TypeRole::LabelLarge,
None,
Easing::Standard,
DurationToken::Short4,
None,
),
spec(
Component::TextButton,
ShapeScale::Full,
Elevation::Level0,
TypeRole::LabelLarge,
None,
Easing::Standard,
DurationToken::Short4,
None,
),
spec(
Component::IconButton,
ShapeScale::Full,
Elevation::Level0,
TypeRole::LabelMedium,
None,
Easing::Standard,
DurationToken::Short3,
Some(ShapeMorph::new(
ShapeScale::Medium,
ShapeScale::Full,
SPRING_EXPRESSIVE_SPATIAL_FAST,
)),
),
// FilledCardTokens.ContainerElevation = Level0(hover 才到 L1、dragged 到 L3)。
spec(
Component::OutlinedCard,
ShapeScale::Medium,
Elevation::Level0,
TypeRole::TitleMedium,
Some(TypeRole::BodySmall),
Easing::Standard,
DurationToken::Medium1,
None,
),
spec(
Component::FilledCard,
ShapeScale::Medium,
Elevation::Level0,
TypeRole::TitleMedium,
Some(TypeRole::BodySmall),
Easing::Standard,
DurationToken::Medium1,
Some(ShapeMorph::new(
ShapeScale::Medium,
ShapeScale::Large,
SPRING_EXPRESSIVE_SPATIAL_DEFAULT,
)),
),
spec(
Component::ListItem,
ShapeScale::Small,
Elevation::Level0,
TypeRole::BodyLarge,
Some(TypeRole::BodySmall),
Easing::Standard,
DurationToken::Short4,
None,
),
spec(
Component::TextField,
ShapeScale::ExtraSmall,
Elevation::Level0,
TypeRole::BodyLarge,
Some(TypeRole::BodySmall),
Easing::Standard,
DurationToken::Short4,
None,
),
spec(
Component::SegmentedButton,
ShapeScale::Full,
Elevation::Level0,
TypeRole::LabelLarge,
None,
Easing::Emphasized,
DurationToken::Medium2,
None,
),
spec(
Component::Chip,
ShapeScale::Small,
Elevation::Level0,
TypeRole::LabelMedium,
None,
Easing::Standard,
DurationToken::Short3,
Some(ShapeMorph::new(
ShapeScale::ExtraSmall,
ShapeScale::Small,
SPRING_EXPRESSIVE_SPATIAL_FAST,
)),
),
spec(
Component::Switch,
ShapeScale::Full,
Elevation::Level0,
TypeRole::BodyMedium,
Some(TypeRole::BodySmall),
Easing::Emphasized,
DurationToken::Medium2,
None,
),
spec(
Component::Slider,
ShapeScale::Full,
Elevation::Level0,
TypeRole::BodyMedium,
Some(TypeRole::BodySmall),
Easing::Standard,
DurationToken::Short2,
None,
),
spec(
Component::ProgressIndicator,
ShapeScale::Full,
Elevation::Level0,
TypeRole::LabelMedium,
None,
Easing::Linear,
DurationToken::Long2,
None,
),
// DialogTokens.ContainerElevation = Level3,CornerExtraLarge = 28。
spec(
Component::Dialog,
ShapeScale::ExtraLarge,
Elevation::Level3,
TypeRole::HeadlineSmall,
Some(TypeRole::BodyMedium),
Easing::EmphasizedDecelerate,
DurationToken::Long4,
Some(ShapeMorph::new(
ShapeScale::Large,
ShapeScale::ExtraLarge,
SPRING_EXPRESSIVE_SPATIAL_DEFAULT,
)),
),
// MenuTokens:CornerExtraSmall(4) + Level2。
spec(
Component::Menu,
ShapeScale::ExtraSmall,
Elevation::Level2,
TypeRole::BodyMedium,
None,
Easing::StandardDecelerate,
DurationToken::Short4,
None,
),
spec(
Component::Snackbar,
ShapeScale::ExtraSmall,
Elevation::Level3,
TypeRole::BodyMedium,
None,
Easing::EmphasizedDecelerate,
DurationToken::Medium2,
None,
),
spec(
Component::Tooltip,
ShapeScale::ExtraSmall,
Elevation::Level2,
TypeRole::BodySmall,
None,
Easing::StandardDecelerate,
DurationToken::Short2,
None,
),
spec(
Component::EmptyState,
ShapeScale::Large,
Elevation::Level0,
TypeRole::HeadlineSmall,
Some(TypeRole::BodySmall),
Easing::Standard,
DurationToken::Medium2,
None,
),
// ---- M69 G9:末尾追加的 5 个(`research/15` §18~§23;前 20 行次序不许动)----
// Checkbox:圆角 **2dp** 不在 `ShapeScale` 刻度里 → 走 `spec_micro`(D32 第二层)。
// 高程 L0:`CheckboxTokens` 没有 Elevation 行,容器是「描边/填充」两态而非浮层。
// 字级 `BodyLarge` 是**团队口径**:官方没有「框 + 标签」组合 token(§18.9),取列表行同一档。
// 动效取 M3 motion scheme 的 fast-effects(100ms standard)——[推导],非 token 原文。
spec_micro(
Component::Checkbox,
ComponentRadius::Micro,
Elevation::Level0,
TypeRole::BodyLarge,
None,
Easing::Standard,
DurationToken::Short2,
None,
),
// Radio:真圆(`RadioButtonTokens.IconSize = 20` + `CornerFull`),内点/环线宽见
// `ComponentMetrics::RADIO`。字级同 Checkbox(团队口径)。
spec(
Component::Radio,
ShapeScale::Full,
Elevation::Level0,
TypeRole::BodyLarge,
None,
Easing::Standard,
DurationToken::Short2,
None,
),
// Search bar:圆角 `CornerFull` / `SearchBarCornerRadius = 56/2 = 28`。
// **高程取 L0**——`SearchBarTokens.ContainerElevation = Level3` 与
// `SearchBarDefaults.TonalElevation/ShadowElevation = Level0` 冲突(§26.2 矛盾 ②),
// 判据按观感取「无可见阴影 + tone 分层」,这条已在 `design-令牌约定` 登记。
// 无 shape morph:bar ⇄ docked 展开时**半径不变**(28 → 28),只长面积(§20C)。
spec(
Component::Search,
ShapeScale::Full,
Elevation::Level0,
TypeRole::BodyLarge,
Some(TypeRole::BodySmall),
Easing::Emphasized,
DurationToken::Medium2,
None,
),
// Navigation drawer:官方 `ContainerShape = CornerLargeEnd`(只有靠内容那侧两角 16,
// 贴边侧直角)。`ShapeScale` 表达不了「部分角」,这里记内侧角的 `Large(16)`,
// 哪两个角由组件按侧位决定;写四角 16 = 变成浮动卡片(§21.2 判据)。
// 常驻形态 `Surface` + L0(模态才用 `SurfaceContainerLow` + scrim,本项目只在 <600 窗宽兜底)。
spec(
Component::NavigationDrawer,
ShapeScale::Large,
Elevation::Level0,
TypeRole::LabelLarge,
Some(TypeRole::TitleSmall),
Easing::Emphasized,
DurationToken::Medium2,
None,
),
// Divider:无高程、无圆角、**无文字**(`text` 是占位值,消费方不得据此排版);
// 厚度是一像素线 `LayoutMetrics::divider_thickness`,颜色 `OutlineVariant`
// (search view 头部分隔是 `Outline`,两个不同角色,§20.8)。
// **`motion` / `duration` 也是占位值**(`ComponentSpec.motion` 不是 `Option`):Divider 没有
// 官方状态过渡令牌,消费方**不得据此排动效**;下轮把该字段改成 `Option` 更彻底。
spec(
Component::Divider,
ShapeScale::None,
Elevation::Level0,
TypeRole::BodySmall,
None,
Easing::Standard,
DurationToken::Short2,
None,
),
];