1068 lines
45 KiB
Markdown
1068 lines
45 KiB
Markdown
# Tergent Android — UI 规范文档 v1.0
|
||
|
||
> 版本: 1.0 | 状态: 草稿 | 作者: 茂之核
|
||
> 基于: TERGENT-DESIGN.md + 界面布局参考 (tergent-ui-screens.html)
|
||
> 目标: Pixso 高保真设计输入 + 开发实现手册
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [设计语言](#1-设计语言)
|
||
2. [页面流程](#2-页面流程)
|
||
3. [9 个页面详细设计](#3-9-个页面详细设计)
|
||
4. [推荐第三方库](#4-推荐第三方库)
|
||
5. [改造优先级细化](#5-改造优先级细化)
|
||
6. [附录](#6-附录)
|
||
|
||
---
|
||
|
||
## 1. 设计语言
|
||
|
||
### 1.1 配色方案
|
||
|
||
Tergent 配色不与 Material Design 3 强绑定,采用更简洁、国内用户熟悉的风格。参考微信、飞书、即刻的用色逻辑,以中性蓝为主色,减少渐变和复杂阴影。
|
||
|
||
| Token | 色值 (Light) | 色值 (Dark) | 用途 |
|
||
|-------|-------------|-------------|------|
|
||
| `tergent_primary` | `#1C6BFF` | `#5B8CFF` | 主色:按钮、Tab 选中、链接、品牌强调 |
|
||
| `tergent_primary_dark` | `#1551CC` | `#3A6FE0` | 主色深色变体:按压态、标题强调 |
|
||
| `tergent_primary_bg` | `#EDF2FF` | `#1A2744` | 主色背景:选中态、轻高亮 |
|
||
| `tergent_accent` | `#06D6A0` | `#3AE0B5` | 辅助色:成功、在线、正面反馈 |
|
||
| `tergent_warning` | `#F59E0B` | `#FBBF24` | 警告色:连接不稳定、通知 |
|
||
| `tergent_danger` | `#EF4444` | `#F87171` | 危险色:错误、断开、删除 |
|
||
| `tergent_bg` | `#FFFFFF` | `#111827` | 页面背景 |
|
||
| `tergent_surface` | `#F8FAFC` | `#1F2937` | 卡片/列表背景 |
|
||
| `tergent_surface_raised` | `#FFFFFF` | `#374151` | 弹窗/Sheet 背景 |
|
||
| `tergent_border` | `#E5E7EB` | `#374151` | 分割线、边框 |
|
||
| `tergent_text_primary` | `#111827` | `#F9FAFB` | 主要文字 |
|
||
| `tergent_text_secondary` | `#6B7280` | `#9CA3AF` | 次要文字 |
|
||
| `tergent_text_tertiary` | `#9CA3AF` | `#6B7280` | 提示文字 |
|
||
| `tergent_nav_bar` | `#FFFFFF` | `#111827` | 底部导航栏背景 |
|
||
| `tergent_chat_bubble_self` | `#1C6BFF` | `#2563EB` | 自己发出的聊天气泡 |
|
||
| `tergent_chat_bubble_other` | `#F3F4F6` | `#2D3748` | 对方聊天气泡 |
|
||
| `tergent_success_green` | `#10B981` | `#34D399` | 在线指示、成功状态 |
|
||
| `tergent_input_bg` | `#F3F4F6` | `#1F2937` | 输入框背景 |
|
||
|
||
**设计原则:**
|
||
- 主色用纯蓝 `#1C6BFF`,避免紫/渐变(原 OpenClaw 风格),更接近国内主流 IM 应用
|
||
- 辅助色用绿松石 `#06D6A0` 做成功/在线态,清爽不刺眼
|
||
- 界面使用白色/浅灰背景,卡片使用圆角矩形,不依赖大块色彩区域
|
||
- 深色模式全程同步,取色逻辑为 Light 反转明度 + 降低饱和度
|
||
|
||
### 1.2 中文字体
|
||
|
||
| 层级 | 字体 | Fallback | 用途 |
|
||
|------|------|----------|------|
|
||
| 应用全局 | `"PingFang SC"` | `"Microsoft YaHei"`, `system-ui`, `sans-serif` | 所有 UI 文字 |
|
||
| 聊天内容 | `"PingFang SC"` | `"Microsoft YaHei"`, `-apple-system` | 消息正文 |
|
||
| 等宽文字 | `"JetBrains Mono"` | `"Cascadia Code"`, `monospace` | 日志、代码块、诊断信息 |
|
||
| 品牌字体 | `"PingFang SC"` | — | Logo、标题大字 |
|
||
|
||
**行高规范:**
|
||
- 正文 14px/16px: 1.5 倍行高
|
||
- 小字 11px/12px: 1.4 倍行高
|
||
- 标题 16px/18px: 1.3 倍行高
|
||
|
||
### 1.3 图标风格
|
||
|
||
- **风格**:线性描边图标,2px 描边,圆角端点 (round cap),填充色为当前文本色
|
||
- **来源**:`Remix Icon`(国内开源图标库,飞书也在用)+ 自定义品牌图标
|
||
- **Compose 兼容**:使用 `androidx.compose.material.icons.Icons.Default` 兜底,Remix 图标以 `ImageVector` 形式集成
|
||
- **尺寸规范**:
|
||
- 底部 Tab: 24×24dp
|
||
- 列表前导图标: 20×20dp
|
||
- 按钮图标: 18×18dp
|
||
- 空状态大图标: 48×48dp
|
||
- **颜色**:跟随 `tergent_text_secondary`,选中态变为主色 `tergent_primary`
|
||
|
||
### 1.4 间距 / 圆角 / 阴影
|
||
|
||
**间距体系(8 点网格):**
|
||
|
||
| Token | dp | 使用场景 |
|
||
|-------|----|---------|
|
||
| spacing_xs | 4 | 图标间距、小间隙 |
|
||
| spacing_sm | 8 | 列表项内边距 |
|
||
| spacing_md | 12 | 组件间间距 |
|
||
| spacing_lg | 16 | 卡片内边距、外边距 |
|
||
| spacing_xl | 24 | 段落间距、区块间距 |
|
||
| spacing_2xl | 32 | 大区块间距 |
|
||
|
||
**圆角体系:**
|
||
|
||
| Token | dp | 使用场景 |
|
||
|-------|----|---------|
|
||
| radius_sm | 4 | 标签、下拉项 |
|
||
| radius_md | 8 | 卡片、输入框、按钮 |
|
||
| radius_lg | 12 | 弹窗、聊天气泡、外露卡片 |
|
||
| radius_xl | 16 | 顶部/底部 Sheet |
|
||
| radius_full | 999 | 圆形按钮、头像、语音 Orb |
|
||
|
||
**阴影:**
|
||
|
||
| Token | 值 (Light) | 值 (Dark) | 使用场景 |
|
||
|-------|------------|-----------|----------|
|
||
| shadow_sm | `0 1px 2px rgba(0,0,0,0.06)` | `0 1px 2px rgba(0,0,0,0.4)` | 列表项、小卡片 |
|
||
| shadow_md | `0 4px 6px -1px rgba(0,0,0,0.1)` | `0 4px 6px rgba(0,0,0,0.5)` | 卡片、底部导航栏 |
|
||
| shadow_lg | `0 10px 15px -3px rgba(0,0,0,0.1)` | `0 10px 15px rgba(0,0,0,0.5)` | 弹窗、BottomSheet |
|
||
|
||
**Compose 实现参考:**
|
||
- 使用 `Modifier.shadow(elevation, shape, clip = true)` 实现 Android elevation shadow
|
||
- Light 模式下 elevation: sm=1dp, md=4dp, lg=8dp
|
||
- Dark 模式下不依赖系统 drop shadow,使用 `AmbientElevation` 配合 `surfaceTonalElevation`
|
||
|
||
---
|
||
|
||
## 2. 页面流程
|
||
|
||
### 2.1 用户全流程 (ASCII)
|
||
|
||
```
|
||
┌──────────────┐ 首次启动? ┌──────────────────┐
|
||
│ 启动 App │ ───────────────▶ │ Onboarding 引导 │
|
||
│ (Splash) │ │ (扫码/手动配对) │
|
||
└──────┬───────┘ └────────┬─────────┘
|
||
│ 已有配对 │ 配对完成
|
||
▼ ▼
|
||
┌───────────────────────────────────────────────────┐
|
||
│ 主界面 (PostOnboardingTabs) │
|
||
│ │
|
||
│ ┌────────┬────────┬────────┬────────┬────────┐ │
|
||
│ │ 连接 │ 聊天 │ 语音 │ 屏幕 │ 设置 │ │
|
||
│ │ Tab 0 │ Tab 1 │ Tab 2 │ Tab 3 │ Tab 4 │ │
|
||
│ └───┬────┴───┬────┴───┬────┴───┬────┴───┬────┘ │
|
||
│ │ │ │ │ │ │
|
||
│ ▼ ▼ ▼ ▼ ▼ │
|
||
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────────┐ │
|
||
│ │连接 │ │聊天 │ │语音 │ │屏幕 │ │设置页 │ │
|
||
│ │状态 │ │对话 │ │对讲 │ │WebView│ │(多子页) │ │
|
||
│ │&网关 │ │流 │ │ │ │canvas │ │ │ │
|
||
│ │管理 │ │ │ │ │ │ │ │ │ │
|
||
│ └──┬───┘ └──┬────┘ └──────┘ └───────┘ └─────┬────┘ │
|
||
│ │ │ │ │
|
||
│ │ │ ┌───────────────────┘ │
|
||
│ │ │ │ │
|
||
│ ▼ ▼ ▼ │
|
||
│ ┌──────────────┐ ┌──────────┐ ┌──────────────┐ │
|
||
│ │ 设备管理页 │ │ 通知页 │ │ 连接诊断 │ │
|
||
│ │ (子路由) │ │ (子路由) │ │ (子路由) │ │
|
||
│ └──────────────┘ └──────────┘ └──────────────┘ │
|
||
└───────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2.2 底部 Tab 默认排序
|
||
|
||
底部导航栏共 5 个 Tab,初期固定(不可编辑):
|
||
|
||
| 序号 | 名称 | 图标 (Remix) | 页面 | 优先级 |
|
||
|------|------|-------------|------|--------|
|
||
| 0 | 连接 | `ri-link` | ConnectScreen | P0 |
|
||
| 1 | 聊天 | `ri-chat-3-line` | ChatScreen | P0 |
|
||
| 2 | 语音 | `ri-mic-line` | VoiceScreen | P1 |
|
||
| 3 | 屏幕 | `ri-smartphone-line` | CanvasScreen | P1 |
|
||
| 4 | 设置 | `ri-settings-3-line` | SettingsScreen (多子页) | P0 |
|
||
|
||
**排序逻辑:**
|
||
1. 连接状态是首要信息 → 放最左边,一眼能看到连接状况
|
||
2. 聊天是核心使用场景 → 紧随其后,用户主要交互在此
|
||
3. 语音和屏幕是特色功能 → 中间位置,容易触及
|
||
4. 设置靠右提供访问入口 → 最右侧,符合 Android 操作习惯
|
||
|
||
### 2.3 页面之间的导航关系
|
||
|
||
| 从 → 到 | 触发方式 | 备注 |
|
||
|---------|---------|------|
|
||
| Splash → Onboarding | 首次启动 / 无配对记录 | 自动跳转 |
|
||
| Splash → 主界面 | 已有有效配对记录 | 跳过引导 |
|
||
| Onboarding → 主界面 | 配对成功 | 自动跳转 |
|
||
| 连接页 → 设备管理 | 点击"已配对网关"卡片 | Navigation push |
|
||
| 连接页 → 诊断页 | 点击"连接诊断"按钮 | Navigation push |
|
||
| 连接页 → 手动添加 | 点击"+ 手动添加网关" | Dialog / BottomSheet |
|
||
| 设置 → 通知页 | 点击"通知转发"行 | Navigation push |
|
||
| 设置 → 连接设置 | 点击"连接设置"行 | Navigation push |
|
||
| 聊天 → 设置 Tab | 点击导航栏第五个 Tab | Tab 切换 |
|
||
| 所有页面 → 其他 Tab | 点击对应 Tab | Tab 切换 |
|
||
|
||
---
|
||
|
||
## 3. 9 个页面详细设计
|
||
|
||
### 3.1 引导页 (Onboarding) — P0
|
||
|
||
#### 页面用途
|
||
首次安装后的配对引导流程。用户通过扫码(网关二维码)或手动输入方式连接到 Tergent 网关。
|
||
|
||
#### 用户场景
|
||
- 用户刚安装 Tergent,打开即看到此页
|
||
- 在电脑/服务器上启动 Tergent 网关后,用手机扫描配对二维码
|
||
- 扫码失败(光线差/损坏)时,可手动输入网关地址
|
||
|
||
#### 核心组件清单
|
||
|
||
```
|
||
┌──────────────────────────────┐
|
||
│ StatusBar │
|
||
│ [时间] [📶 🔋] │
|
||
├──────────────────────────────┤
|
||
│ │
|
||
│ ┌────────────────┐ │
|
||
│ │ Tergent │ │ ← Logo 60×60dp, 圆角16dp
|
||
│ │ 品牌图标 │ │ 渐变背景
|
||
│ └────────────────┘ │
|
||
│ │
|
||
│ Tergent │ ← 应用名 20sp, Bold
|
||
│ │
|
||
│ 连接你的设备,随时掌控 │ ← 副标题 13sp, tertiary
|
||
│ │
|
||
│ │
|
||
│ ┌──────────────────┐ │
|
||
│ │ │ │ ← QR 码区域 200×200dp
|
||
│ │ 「扫码框」 │ │ 虚线边框, 圆角8dp
|
||
│ │ │ │ 相机实时扫描
|
||
│ └──────────────────┘ │
|
||
│ │
|
||
│ 扫码配对 或 手动输入 │ ← 12sp, tertiary
|
||
│ │
|
||
│ ┌────────────────────┐ │
|
||
│ │ 开始连接 → │ │ ← 主色按钮, 圆角24dp
|
||
│ └────────────────────┘ │ padding 10×28dp
|
||
│ │
|
||
│ v0.1.0 alpha │ ← 11sp, tertiary
|
||
│ │
|
||
└──────────────────────────────┘
|
||
```
|
||
|
||
#### 组件明细
|
||
|
||
| 组件 | Compose 类型 | 规格 |
|
||
|------|-------------|------|
|
||
| Logo | `Image` (painterResource) | 60×60dp, 圆角 16dp, 品牌渐变 |
|
||
| 应用名 | `Text` | 20sp, FontWeight.Bold, `tergent_text_primary` |
|
||
| 副标题 | `Text` | 13sp, `tergent_text_secondary` |
|
||
| QR 扫描区 | `Box` + CameraX preview | 200×200dp, 虚线 BorderStroke, 圆角 8dp |
|
||
| 手动输入链接 | `TextButton` | "手动输入", 12sp, 主色/下划线 |
|
||
| 开始连接按钮 | `Button` | 主色填充, 圆角 24dp, 白色文字 14sp SemiBold |
|
||
| 版本号 | `Text` | 11sp, `tergent_text_tertiary` |
|
||
|
||
#### 交互说明
|
||
|
||
| 操作 | 行为 |
|
||
|------|------|
|
||
| 摄像头自动扫描 QR | 自动识别二维码中的 gateway connect info |
|
||
| 点击 QR 区域 | 手动触发一次扫码 |
|
||
| 点击"手动输入" | 弹出 `AlertDialog`:输入网关地址:端口 |
|
||
| 点击"开始连接" | 触发 `GatewaySession.connect()`, 按钮显示 Loading |
|
||
| 连接成功 | 自动跳转到主界面 |
|
||
| 连接失败 | Snackbar 提示具体错误原因 |
|
||
|
||
#### 需要 API 获取的数据
|
||
- 无需 API(配对过程本地完成,GatewaySession.connect() 处理认证)
|
||
|
||
#### 状态设计
|
||
|
||
| 状态 | UI 表现 |
|
||
|------|---------|
|
||
| 默认 | QR 扫描区虚线框 + 扫码提示文字 |
|
||
| 加载中 | 按钮变 `CircularProgressIndicator(4dp)` + "连接中…" |
|
||
| 错误 | Snackbar 显示具体错误原因 |
|
||
| 权限拒绝 | 回退为手动输入模式 |
|
||
| 相机不可用 | 自动降级为纯手动输入页面 |
|
||
|
||
---
|
||
|
||
### 3.2 连接页 (Connect) — P0
|
||
|
||
#### 页面用途
|
||
展示与 Tergent 网关的连接状态、已配对设备信息、网络概况。
|
||
|
||
#### 用户场景
|
||
- 日常检查:打开 App 首页即见连接状态
|
||
- 连接断开时:显示断开原因 + 自动重连计数
|
||
- 需要切换网关:点击"切换网关"更换目标
|
||
|
||
#### 核心组件清单
|
||
|
||
```
|
||
┌──────────────────────────────┐
|
||
│ StatusBar │
|
||
│ [时间] [📶 🔋] │
|
||
├──────────────────────────────┤
|
||
│ 连接 [⋮]│ ← TopAppBar, 右侧菜单
|
||
├──────────────────────────────┤
|
||
│ │
|
||
│ ┌── [●] 已连接 · hdtime ──┐│ ← 连接状态条
|
||
│ │ 网关: gateway.hdtime ││ 绿/黄/红 + 文字
|
||
│ └──────────────────────────┘│ 圆角6dp, 12sp
|
||
│ │
|
||
│ 已配对设备: 茂之钳 (main) │ ← 13sp, secondary
|
||
│ │
|
||
│ ┌──────────────────────────┐│
|
||
│ │ 连接信息 ││ ← 卡片, 圆角8dp
|
||
│ │ 网关地址 hdtime.space ││ border #E5E7EB
|
||
│ │ 端口 443 (WSS) ││ 标签12sp + 值12sp
|
||
│ │ 连接状态 ● 在线 ││
|
||
│ └──────────────────────────┘│
|
||
│ │
|
||
│ ┌──────────┐ ┌────────────┐│
|
||
│ │ 切换网关 │ │ 重新配对 ││ ← Outlined + Filled btn
|
||
│ └──────────┘ └────────────┘│
|
||
├──────────────────────────────┤
|
||
│ 🔗 💬 🎤 🖥 ⚙️ │
|
||
│ 连接 聊天 语音 屏幕 设置 │
|
||
└──────────────────────────────┘
|
||
```
|
||
|
||
#### 需要 API 获取的数据
|
||
|
||
| 数据 | 来源 |
|
||
|------|------|
|
||
| 连接状态 | `NodeRuntime.connectionState` (StateFlow) |
|
||
| 网关端点 | `NodeRuntime.gatewayEndpoint` |
|
||
| Ping 延迟 | `NodeRuntime.lastPingMs` |
|
||
| 已注册设备 | `NodeRuntime.registeredDevices` |
|
||
|
||
#### 状态设计
|
||
|
||
| 状态 | UI 表现 |
|
||
|------|---------|
|
||
| 在线 | 绿色 `#DCFCE7` + "已连接" |
|
||
| 连接中 | 黄色 `#FEF3C7` + "正在连接…" + 脉冲动画 |
|
||
| 重连中 | 黄色/灰色 + "第 N 次重连中…" |
|
||
| 断开 | 红色 `#FEE2E2` + "已断开" + "点击诊断" |
|
||
| 无网络 | 灰色 + "无网络连接" |
|
||
|
||
---
|
||
|
||
### 3.3 聊天页 (Chat) — P0
|
||
|
||
#### 页面用途
|
||
与 Tergent AI Agent 文字对话的核心界面。支持流式回复、多轮对话、Markdown、图片、会话管理。
|
||
|
||
#### 用户场景
|
||
- 日常问答:提问后获取流式回复
|
||
- 任务执行:发指令让 Agent 执行操作
|
||
- 历史回溯:翻阅历史对话
|
||
|
||
#### 核心组件清单
|
||
|
||
```
|
||
┌──────────────────────────────┐
|
||
│ StatusBar │
|
||
│ [时间] [📶 🔋] │
|
||
├──────────────────────────────┤
|
||
│ [●] 茂之钳 [⋮] [+]│ ← 头像+名称+菜单+新建
|
||
│ 在线 │
|
||
├──────────────────────────────┤
|
||
│ │
|
||
│ ┌────────────────────────┐ │
|
||
│ │ 你好!有什么需要帮忙的? │ │ ← 对方气泡 (左对齐)
|
||
│ └────────────────────────┘ │ 浅灰底, 圆角12dp
|
||
│ │
|
||
│ ┌────────────────┐ │
|
||
│ │ 帮我查一下天气 │ │ ← 自己气泡 (右对齐)
|
||
│ │ 14:30 │ │ 蓝色底, 圆角12dp
|
||
│ └────────────────┘ │
|
||
│ │
|
||
│ ┌──────────────┐ │
|
||
│ │ 正在输入... │ │ ← 打字指示器
|
||
│ └──────────────┘ │ animate 3 dots
|
||
│ │
|
||
│ ─── Tergent is working ─│ ← 状态提示
|
||
│ │
|
||
├──────────────────────────────┤
|
||
│ ┌────────────────────────┐ │
|
||
│ │ [输入消息...] [➤]│ │ ← 输入区, 圆角16dp
|
||
│ └────────────────────────┘ │
|
||
├──────────────────────────────┤
|
||
│ 🔗 💬 🎤 🖥 ⚙️ │
|
||
│ 连接 聊天 语音 屏幕 设置 │
|
||
└──────────────────────────────┘
|
||
```
|
||
|
||
#### 需要 API 获取的数据
|
||
|
||
| 数据 | 来源 | 用途 |
|
||
|------|------|------|
|
||
| 消息列表 | `ChatController.messages` (StateFlow) | LazyColumn 数据 |
|
||
| 会话列表 | `ChatController.sessions` | 切换 session |
|
||
| 连接状态 | `NodeRuntime.connectionState` | 输入框可用性 |
|
||
| 流式回复 | operatorSession chat.send 回调 | 实时更新气泡 |
|
||
| 历史消息 | 缓存 / 网关 pull | 分页加载 |
|
||
|
||
#### 状态设计
|
||
|
||
| 状态 | UI 表现 |
|
||
|------|---------|
|
||
| 空对话 | 居中占位: 48dp 图标 + "开始对话吧" |
|
||
| 输入中 | 输入框正常, 发送按钮高亮 |
|
||
| 发送中 | 气泡右下角 CircularProgressIndicator(4dp) |
|
||
| 发送失败 | 气泡右上角红色 ⚠️, 点击重发 |
|
||
| 流式加载 | typing indicator |
|
||
| 连接断开 | 输入框禁用 + 顶部 banner |
|
||
|
||
---
|
||
|
||
### 3.4 设置页 (Settings) — P0
|
||
|
||
#### 页面用途
|
||
应用全局设置入口。个人信息 + 功能开关 + 子页面路由。
|
||
|
||
#### 核心组件清单
|
||
|
||
```
|
||
┌──────────────────────────────┐
|
||
│ StatusBar │
|
||
│ [时间] [📶 🔋] │
|
||
├──────────────────────────────┤
|
||
│ 设置 │ ← TopAppBar
|
||
├──────────────────────────────┤
|
||
│ │
|
||
│ ┌─── 个人信息卡片 ────────┐ │
|
||
│ │ [●] Tergent Node │ │ ← 40dp 渐变头像
|
||
│ │ v0.1.0 │ │ 14sp + 11sp
|
||
│ └─────────────────────────┘ │
|
||
│ │
|
||
│ ┌── 开关行 ──────────────┐ │
|
||
│ │ 🔔 通知转发 [🔘] │ │
|
||
│ │ 📷 相机权限 [🔘] │ │
|
||
│ │ 📍 定位权限 [🔘] │ │
|
||
│ │ 🤖 语音唤醒 [🔘] │ │
|
||
│ │ 🌙 深色模式 [🔘] │ │
|
||
│ │ 📡 连接设置 › │ │
|
||
│ │ 📋 通知历史 › │ │
|
||
│ └────────────────────────┘ │
|
||
│ │
|
||
├──────────────────────────────┤
|
||
│ 🔗 💬 🎤 🖥 ⚙️ │
|
||
│ 连接 聊天 语音 屏幕 设置 │
|
||
└──────────────────────────────┘
|
||
```
|
||
|
||
#### 需要 API 获取的数据
|
||
|
||
| 数据 | 来源 |
|
||
|------|------|
|
||
| 持久化设置 | SecurePrefs / DataStore |
|
||
| 权限状态 | ContextCompat.checkSelfPermission() |
|
||
| 前台服务状态 | TergentForegroundService.isRunning |
|
||
| 连接统计 | GatewaySession.getStats() |
|
||
|
||
**子路由页面:** 连接设置(P0) / 通知历史(P2) / 关于(P2)
|
||
|
||
---
|
||
|
||
### 3.5 语音页 (Voice) — P1
|
||
|
||
#### 页面用途
|
||
与 Tergent 进行语音对话。支持点击录音 + TTS 语音回复。
|
||
|
||
#### 用户场景
|
||
- 开车/双手占用时语音提问
|
||
- 快速语音查询,无需打字
|
||
- 连续语音对话
|
||
|
||
#### 核心组件清单
|
||
|
||
```
|
||
┌──────────────────────────────┐
|
||
│ StatusBar │
|
||
│ [时间] [📶 🔋] │
|
||
├──────────────────────────────┤
|
||
│ 语音 │ ← TopAppBar
|
||
├──────────────────────────────┤
|
||
│ │
|
||
│ 点击按钮开始说话 │ ← 13sp, tertiary
|
||
│ │
|
||
│ ┌───────┐ │
|
||
│ │ ● │ │ ← 语音 Orb
|
||
│ │ Orb │ │ 72×72dp
|
||
│ └───────┘ │ 渐变 background
|
||
│ │
|
||
│ ▎▌█▌▎ ▎▌█▌▎ │ ← 声波可视化 (录制时)
|
||
│ │
|
||
│ "帮我查一下今天的日程" │ ← 识别文本 12sp italic
|
||
│ │
|
||
│ ┌────────────────────────┐ │
|
||
│ │ Tergent: 今天有2个会议 │ │ ← 回复气泡
|
||
│ └────────────────────────┘ │ 浅灰bg, 圆角8dp
|
||
│ │
|
||
│ 长按说话 / 点击开始 │ ← 11sp
|
||
│ │
|
||
├──────────────────────────────┤
|
||
│ 🔗 💬 🎤 🖥 ⚙️ │
|
||
│ 连接 聊天 语音 屏幕 设置 │
|
||
└──────────────────────────────┘
|
||
```
|
||
|
||
#### 需要 API 获取的数据
|
||
|
||
| 数据 | 来源 |
|
||
|------|------|
|
||
| 麦克风数据 | MicCaptureManager (AudioRecord PCM) |
|
||
| PTT 控制 | TalkModeManager (PttStart/Stop/Cancel) |
|
||
| TTS 播放 | TalkSpeakClient |
|
||
| 语音识别 | gateway transcription 回调 |
|
||
|
||
#### 状态设计
|
||
|
||
| 状态 | UI 表现 |
|
||
|------|---------|
|
||
| 空闲 | Orb 静态, "点击按钮开始说话" |
|
||
| 录音中 | Orb 呼吸动画 + 声波动画 + "正在听…" |
|
||
| 识别中 | Orb 微动 + "识别中…" |
|
||
| 识别完成 | 显示识别文本 |
|
||
| TTS 播放中 | Orb 微动 + 气泡逐步显示 |
|
||
| 连接断开 | Orb 灰色 + "需要网络连接" |
|
||
| 权限拒绝 | Orb 灰色 + "请允许麦克风权限" |
|
||
|
||
---
|
||
|
||
### 3.6 画布页 (Canvas) — P1
|
||
|
||
#### 页面用途
|
||
Agent 在 WebView 上展示交互式 HTML/CSS 内容。支持仪表盘、图形、表单等。
|
||
|
||
#### 用户场景
|
||
- Agent 展示实时数据仪表盘
|
||
- 交互式表单填写
|
||
- 富媒体内容呈现
|
||
|
||
#### 核心组件清单
|
||
|
||
```
|
||
┌──────────────────────────────┐
|
||
│ StatusBar │
|
||
│ [时间] [📶 🔋] │
|
||
├──────────────────────────────┤
|
||
│ 屏幕 [⊞][↻]│ ← TopAppBar
|
||
├──────────────────────────────┤
|
||
│ [📷截图] [🔍缩放] [↻刷新] │ ← 操作标签
|
||
│ │
|
||
│ ┌────────────────────────┐ │
|
||
│ │ │ │
|
||
│ │ WebView 内容区域 │ │ ← Agent 渲染的 HTML
|
||
│ │ │ │ 可交互点击/输入
|
||
│ │ │ │
|
||
│ └────────────────────────┘ │
|
||
│ │
|
||
├──────────────────────────────┤
|
||
│ 🔗 💬 🎤 🖥 ⚙️ │
|
||
│ 连接 聊天 语音 屏幕 设置 │
|
||
└──────────────────────────────┘
|
||
```
|
||
|
||
#### 需要 API 获取的数据
|
||
|
||
| 数据 | 来源 |
|
||
|------|------|
|
||
| 页面导航 | CanvasController.navigate() |
|
||
| JavaScript eval | CanvasController.eval() |
|
||
| 截图 | CanvasController.snapshotBase64() |
|
||
| A2UI 消息 | A2UIHandler.push() / pushJSONL() |
|
||
| URL 白名单 | CanvasActionTrust |
|
||
|
||
#### 状态设计
|
||
|
||
| 状态 | UI 表现 |
|
||
|------|---------|
|
||
| 空白 | 占位文字 "等待 Agent 展示内容" |
|
||
| 加载中 | 浅灰 bg + Center CircularProgressIndicator |
|
||
| 已加载 | 正常 WebView 内容 |
|
||
| A2UI 就绪 | 可接收 push/pushJSONL 实时更新 |
|
||
| 加载失败 | Error state: "内容加载失败" + 重试按钮 |
|
||
|
||
---
|
||
|
||
### 3.7 节点管理页 (Devices) — P1
|
||
|
||
#### 页面用途
|
||
已配对网关列表 + 发现的网关列表。管理多网关场景。
|
||
|
||
#### 用户场景
|
||
- 查看已配对的网关和在线状态
|
||
- 发现局域网内的其他 Tergent 网关
|
||
- 手动添加不在同一网段的网关
|
||
|
||
#### 核心组件清单
|
||
|
||
```
|
||
┌──────────────────────────────┐
|
||
│ StatusBar │
|
||
│ [时间] [📶 🔋] │
|
||
├──────────────────────────────┤
|
||
│ [←] 设备 │ ← TopAppBar 带返回
|
||
├──────────────────────────────┤
|
||
│ │
|
||
│ 已配对网关 │ ← Section header 12sp Bold
|
||
│ │
|
||
│ [●] hdtime.space │ ← 设备行
|
||
│ 茂之钳 · 在线 │ 绿/灰 dot + 名称 + 状态
|
||
│ ● 18789│ 右侧 ping/port
|
||
│ │
|
||
│ 发现的网关 │ ← Section header
|
||
│ │
|
||
│ [○] Local Gateway │ ← 未配对设备
|
||
│ 192.168.1.100:18789 │ 灰色 dot
|
||
│ │
|
||
│ + 手动添加网关 │ ← Dashed border button
|
||
│ │
|
||
├──────────────────────────────┤
|
||
│ 🔗 💬 🎤 🖥 ⚙️ │
|
||
│ 连接 聊天 语音 屏幕 设置 │
|
||
└──────────────────────────────┘
|
||
```
|
||
|
||
#### 需要 API 获取的数据
|
||
|
||
| 数据 | 来源 |
|
||
|------|------|
|
||
| 已配对设备 | NodeRuntime.registeredDevices |
|
||
| 发现结果 | GatewayDiscovery (mDNS + 广域网) |
|
||
| 设备状态 | NodeRuntime.nodeCapabilities |
|
||
|
||
#### 状态设计
|
||
|
||
| 状态 | UI 表现 |
|
||
|------|---------|
|
||
| 已配对且在线 | 绿色 dot + 设备名 + "在线" |
|
||
| 已配对但离线 | 灰色 dot + "离线" |
|
||
| 发现中 | 底部 shimmer 加载 |
|
||
| 未发现任何设备 | "未发现其他网关" 空状态 |
|
||
| 已配对列表为空 | "暂无已配对的网关" + 引导去 onboarding |
|
||
|
||
---
|
||
|
||
### 3.8 通知历史页 (Notifications) — P2
|
||
|
||
#### 页面用途
|
||
展示设备上被转发到网关的通知列表。用户可查看转发历史、开启/关闭转发。
|
||
|
||
#### 用户场景
|
||
- 查看手机上的通知历史
|
||
- 确认通知转发是否正常工作
|
||
- 关闭某个应用的通知转发
|
||
|
||
#### 核心组件清单
|
||
|
||
```
|
||
┌──────────────────────────────┐
|
||
│ StatusBar │
|
||
│ [时间] [📶 🔋] │
|
||
├──────────────────────────────┤
|
||
│ [←] 通知 │ ← TopAppBar 带返回
|
||
├──────────────────────────────┤
|
||
│ │
|
||
│ 通知转发 [🔘] │ ← 全局开关
|
||
│ │
|
||
│ ──── 微信 ──────────────── │
|
||
│ 收到了一条消息 │ ← 通知卡片
|
||
│ 刚刚 │ 12sp + 11sp time
|
||
│ │
|
||
│ ──── Slack ─────────────── │
|
||
│ #general: 新消息 │
|
||
│ 2分钟前 │
|
||
│ │
|
||
│ ──── 系统 ──────────────── │
|
||
│ 电池电量低 │
|
||
│ 10分钟前 │
|
||
│ │
|
||
└──────────────────────────────┘
|
||
```
|
||
|
||
#### 需要 API 获取的数据
|
||
|
||
| 数据 | 来源 |
|
||
|------|------|
|
||
| 通知列表 | DeviceNotificationListenerService 缓存 |
|
||
| 转发开关 | SecurePrefs |
|
||
| 应用列表 | NotificationListenerService.getActiveNotifications() |
|
||
|
||
#### 状态设计
|
||
|
||
| 状态 | UI 表现 |
|
||
|------|---------|
|
||
| 转发开启 + 有通知 | 正常列表展示 |
|
||
| 转发关闭 | 列表清空 + "通知转发已关闭" 提示 |
|
||
| 无通知历史 | 空状态 "暂无通知记录" |
|
||
| 通知服务未授权 | 引导跳转系统设置授权 |
|
||
|
||
---
|
||
|
||
### 3.9 连接诊断页 (Diagnostics) — P2
|
||
|
||
#### 页面用途
|
||
网络连接诊断工具。展示延迟、TLS、WebSocket 详细状态。
|
||
|
||
#### 用户场景
|
||
- 连接不稳定时排查问题
|
||
- 向运维提供诊断信息
|
||
|
||
#### 核心组件清单
|
||
|
||
```
|
||
┌──────────────────────────────┐
|
||
│ StatusBar │
|
||
│ [时间] [📶 🔋] │
|
||
├──────────────────────────────┤
|
||
│ [←] 诊断 │ ← TopAppBar 带返回
|
||
├──────────────────────────────┤
|
||
│ │
|
||
│ ┌── 连接不稳定 ──────────┐ │
|
||
│ │ [●] 延迟: 342ms │ │ ← 状态卡, 黄色警告
|
||
│ │ 重连次数: 3 │ │ dot 颜色随状态变
|
||
│ │ 信号质量: 82% │ │
|
||
│ └────────────────────────┘ │
|
||
│ │
|
||
│ [🔄 运行测试] │ ← 操作按钮
|
||
│ │
|
||
│ ┌──── ping gateway... ────┐ │
|
||
│ │ 64 bytes: 342ms │ │ ← 日志输出
|
||
│ │ TLS handshake... │ │ 等宽字体 10sp
|
||
│ │ OK (TOFU) │ │ 仿真终端样式
|
||
│ │ WebSocket upgrade... │ │
|
||
│ │ OK │
|
||
│ └────────────────────────┘ │
|
||
│ │
|
||
└──────────────────────────────┘
|
||
|
||
#### 需要 API 获取的数据
|
||
|
||
| 数据 | 来源 |
|
||
|------|------|
|
||
| Ping 延迟 | GatewaySession.ping() |
|
||
| TLS 状态 | GatewayTls 指纹校验结果 |
|
||
| WebSocket 状态 | GatewaySession 连接状态 |
|
||
| 重连次数 | GatewaySession.reconnectAttempts |
|
||
| 信号质量 | 综合评分 (ping+重连+rtt) |
|
||
|
||
#### 状态设计
|
||
|
||
| 状态 | UI 表现 |
|
||
|------|---------|
|
||
| 连接正常 | 绿色状态卡 + "连接正常" |
|
||
| 连接不稳定 | 黄色状态卡 + "连接不稳定" + 延迟/重连次数 |
|
||
| 连接断开 | 红色状态卡 + "已断开" |
|
||
| 测试运行中 | 日志区域动态追加输出行 + "正在测试…" |
|
||
| 测试完成 | 日志区域显示完整结果 |
|
||
|
||
---
|
||
|
||
## 4. 推荐第三方库
|
||
|
||
### 4.1 图片加载
|
||
|
||
| 库 | 推荐 | 理由 |
|
||
|----|------|------|
|
||
| **Coil** (v2.x) | ⭐ 首选 | Kotlin 原生 + Coroutines + Compose 集成 (`AsyncImage`)、包体小 (~1500 methods vs Glide ~3000) |
|
||
| Glide | 备选 | Compose 支持需额外 `landscapist-glide`,国内有更成熟的中文社区 |
|
||
|
||
**建议:** Coil。Tergent 不是高频图片应用,Coil 的轻量和 Kotlin 优雅更重要。
|
||
|
||
```kotlin
|
||
// Coil 用法
|
||
AsyncImage(
|
||
model = ImageRequest.Builder(LocalContext.current)
|
||
.data(url)
|
||
.crossfade(true)
|
||
.build(),
|
||
contentDescription = null,
|
||
modifier = Modifier.clip(CircleShape)
|
||
)
|
||
```
|
||
|
||
### 4.2 网络请求
|
||
|
||
| 库 | 推荐 | 理由 |
|
||
|----|------|------|
|
||
| **OkHttp** (v4.x) | ✅ 已有 | 项目已在用 |
|
||
| Retrofit | 可选,但非必需 | Tergent 不直接调用 REST API,通过 WebSocket + JSON RPC 通信 |
|
||
| **kotlinx.serialization** | ✅ 已有 | 已集成,用于 Gateway 协议序列化 |
|
||
|
||
### 4.3 状态管理
|
||
|
||
| 方案 | 推荐 | 理由 |
|
||
|------|------|------|
|
||
| **StateFlow + ViewModel** | ✅ 已有 | Jetpack 官方方案, 与 Compose `collectAsStateWithLifecycle` 结合 |
|
||
| **Compose Navigation** | ✅ 推荐 | 使用 `NavHost` + `NavController` 管理页面路由 |
|
||
| Hilt / Koin | P2 考虑 | 初期可以不引入,手动 DI 就够了 |
|
||
|
||
### 4.4 国内服务 SDK(可选)
|
||
|
||
| 功能 | 推荐 SDK | P | 理由 |
|
||
|------|---------|---|------|
|
||
| 推送通知 | 小米推送 / 华为推送 / 统一推送联盟 | P2 | 国内 Android 无 FCM,需厂商通道保活 |
|
||
| 扫码 | ZXing + CameraX | P0 | 二维码扫描,常见成熟方案 |
|
||
| 登录 | 微信登录 SDK / 飞书登录 SDK | P2 | 如果未来需要用户系统 |
|
||
| 地图 | 高德地图 SDK / 百度地图 SDK | P2 | 如果未来需要位置展示 |
|
||
| WebRTC | Google WebRTC | P2 | 如果未来需要 P2P 语音/视频 |
|
||
| Bug 上报 | Bugly (腾讯) / Sentry | P1 | 国内推荐 Bugly,免费且稳定 |
|
||
|
||
### 4.5 其他实用库
|
||
|
||
| 库 | 用途 | P |
|
||
|----|------|---|
|
||
| `androidx.compose.material3:material3` | Material3 组件 (核心依赖) | P0 |
|
||
| `androidx.navigation:navigation-compose` | Compose 导航 | P0 |
|
||
| `com.google.accompanist:accompanist-systemuicontroller` | 状态栏/导航栏沉浸式 | P0 |
|
||
| `io.coil-kt:coil-compose` | 图片加载 | P1 |
|
||
| `com.google.mlkit:barcode-scanning` | QR 扫码 (ML Kit) | P0 |
|
||
| `com.tencent.bugly:crashreport` | Bugly 崩溃上报 | P1 |
|
||
| `org.commonmark:commonmark` | Markdown 渲染 (已有) | P0 |
|
||
|
||
---
|
||
|
||
## 5. 改造优先级细化
|
||
|
||
### 5.1 优先级定义
|
||
|
||
| 级别 | 含义 | 交付标准 |
|
||
|------|------|---------|
|
||
| **P0** | 必须完成,否则不可用 | 完整的 UI + 功能 + 错误处理 |
|
||
| **P1** | 核心功能,应尽早交付 | 可用但不一定完整 |
|
||
| **P2** | 体验优化,锦上添花 | 可推迟到后续版本 |
|
||
|
||
### 5.2 页面工时估算
|
||
|
||
| 页面 | P | 预估工时 | 可复用 OpenClaw 组件 | 说明 |
|
||
|------|---|---------|---------------------|------|
|
||
| Onboarding | P0 | 2 天 | `GatewayDiscovery`, `GatewaySession.connect()` | 全新 UI,扫码逻辑可复用后端,UI 重写 |
|
||
| Connect | P0 | 2 天 | `NodeRuntime.connectionState`, `GatewayEndpoint` | 全新 UI,数据绑定改到新 VM |
|
||
| Chat | P0 | 3 天 | `ChatController`, `ChatModels`, `ChatMarkdown` | 核心消息 UI 重写,输入框/气泡/流式效果全部重做 |
|
||
| Settings | P0 | 2 天 | `SecurePrefs`, `PermissionRequester` | 列表式 UI,开关联动,各子页面路由 |
|
||
| Voice | P1 | 2 天 | `MicCaptureManager`, `TalkModeManager`, `TalkSpeakClient` | Orb + 声波动画,UI 非复杂,数据绑定为主 |
|
||
| Canvas | P1 | 1 天 | `CanvasController`, `A2UIHandler`, `CanvasActionTrust` | WebView 包装,UI 变动小,主要为品牌替换 |
|
||
| Devices | P1 | 1.5 天 | `GatewayDiscovery`, `ConnectionManager` | 列表 UI + 发现功能,可复用后端逻辑 |
|
||
| Notifications | P2 | 1 天 | `DeviceNotificationListenerService` | 简单列表 UI,数据来自 service 缓存 |
|
||
| Diagnostics | P2 | 1 天 | `GatewayTls`, `GatewaySession` | 调试工具,终端风格 log 输出 |
|
||
| **总计** | — | **15.5 天** | — | 不含第 0 阶段改名和架构重构 |
|
||
|
||
### 5.3 阶段工时分配
|
||
|
||
| 阶段 | 内容 | 工时 |
|
||
|------|------|------|
|
||
| 阶段 0: 基础设施改名 | 包名/品牌/服务类型/资源替换 | 1-2 天 |
|
||
| 阶段 1: 架构重构 | NodeRuntime 拆分、ViewModel 重写 | 3-5 天 |
|
||
| 阶段 2: UI 重制 (P0) | Onboarding + Connect + Chat + Settings | 9 天 |
|
||
| 阶段 3: 功能完善 (P1) | Voice + Canvas + Devices | 4.5 天 |
|
||
| 阶段 4: 打磨 (P2) | Notifications + Diagnostics + 动画/深色 | 3 天 |
|
||
| **Total** | — | **~20.5 天** |
|
||
|
||
### 5.4 可复用的 OpenClaw 组件清单
|
||
|
||
**✅ 直接可用(仅改包名):**
|
||
|
||
| 组件 | 文件 | 说明 |
|
||
|------|------|------|
|
||
| GatewaySession | `gateway/GatewaySession.kt` | WebSocket 会话,协议不变 |
|
||
| InvokeDispatcher | `node/InvokeDispatcher.kt` | 命令路由逻辑不变 |
|
||
| InvokeCommandRegistry | `node/InvokeCommandRegistry.kt` | 命令注册表不变 |
|
||
| 所有 Handler (×12) | `node/*Handler.kt` | 硬件/系统功能代码无品牌关联 |
|
||
| ChatController | `chat/ChatController.kt` | 聊天数据模型和控制逻辑不变 |
|
||
| CanvasController | `node/CanvasController.kt` | WebView 抽象无品牌关联 |
|
||
| A2UIHandler | `node/A2UIHandler.kt` | A2UI 协议处理不变 |
|
||
| CameraCaptureManager | `node/CameraCaptureManager.kt` | CameraX 纯硬件操作 |
|
||
| LocationCaptureManager | `node/LocationCaptureManager.kt` | 定位纯硬件操作 |
|
||
| MicCaptureManager | `voice/MicCaptureManager.kt` | 麦克风纯硬件操作 |
|
||
| SecurePrefs | `SecurePrefs.kt` | 加密存储抽象 |
|
||
| PermissionRequester | `PermissionRequester.kt` | 权限请求框架 |
|
||
| GatewayTls | `gateway/GatewayTls.kt` | TLS 指纹校验逻辑不变 |
|
||
| DeviceIdentityStore | `gateway/DeviceIdentityStore.kt` | 密钥管理不变 |
|
||
|
||
**🔄 需要修改后复用:**
|
||
|
||
| 组件 | 文件 | 修改内容 |
|
||
|------|------|---------|
|
||
| GatewayDiscovery | `gateway/GatewayDiscovery.kt` | 服务类型 `_openclaw-gw` → `_tergent-gw` |
|
||
| ConnectionManager | `node/ConnectionManager.kt` | UserAgent 字符串替换 |
|
||
| DeviceNotificationListenerService | `node/DeviceNotificationListenerService.kt` | Brand name 替换 |
|
||
| TalkSpeakClient | `voice/TalkSpeakClient.kt` | 改包名即可 |
|
||
|
||
**❌ 重写或删除:**
|
||
|
||
| 组件 | 处理 |
|
||
|------|------|
|
||
| `NodeRuntime.kt` | 拆分为 `TergentRuntime` + `SessionManager` + `RefreshCoordinator` + `VoiceCoordinator` |
|
||
| `MainViewModel.kt` | 重写为 `TergentViewModel` |
|
||
| `NodeApp.kt` | 重写为 `TergentApp` |
|
||
| `NodeForegroundService.kt` | 重写为 `TergentForegroundService` |
|
||
| 所有 `ui/*Screen.kt` | 全新 UI |
|
||
| `ui/design/*` | 全新主题系统 |
|
||
| `canvasScaffold.html` | 替换品牌标识 |
|
||
| `ShellScreen.kt` | 删除 |
|
||
| `DreamingSettingsScreen.kt` | 删除/推迟 |
|
||
| `TalkOrbOverlay.kt` | 删除 |
|
||
|
||
---
|
||
|
||
## 6. 附录
|
||
|
||
### 6.1 主题代码参考 (Compose)
|
||
|
||
```kotlin
|
||
// TergentTheme.kt
|
||
|
||
private val TergentLightColors = lightColorScheme(
|
||
primary = Color(0xFF1C6BFF),
|
||
onPrimary = Color.White,
|
||
primaryContainer = Color(0xFFEDF2FF),
|
||
onPrimaryContainer = Color(0xFF001B3D),
|
||
secondary = Color(0xFF06D6A0),
|
||
onSecondary = Color.White,
|
||
background = Color(0xFFFFFFFF),
|
||
onBackground = Color(0xFF111827),
|
||
surface = Color(0xFFF8FAFC),
|
||
onSurface = Color(0xFF111827),
|
||
surfaceVariant = Color(0xFFF3F4F6),
|
||
onSurfaceVariant = Color(0xFF6B7280),
|
||
outline = Color(0xFFE5E7EB),
|
||
error = Color(0xFFEF4444),
|
||
onError = Color.White,
|
||
)
|
||
|
||
private val TergentDarkColors = darkColorScheme(
|
||
primary = Color(0xFF5B8CFF),
|
||
onPrimary = Color(0xFF001B3D),
|
||
primaryContainer = Color(0xFF1A2744),
|
||
onPrimaryContainer = Color(0xFFD6E3FF),
|
||
secondary = Color(0xFF3AE0B5),
|
||
onSecondary = Color(0xFF003827),
|
||
background = Color(0xFF111827),
|
||
onBackground = Color(0xFFF9FAFB),
|
||
surface = Color(0xFF1F2937),
|
||
onSurface = Color(0xFFF9FAFB),
|
||
surfaceVariant = Color(0xFF2D3748),
|
||
onSurfaceVariant = Color(0xFF9CA3AF),
|
||
outline = Color(0xFF374151),
|
||
error = Color(0xFFF87171),
|
||
onError = Color(0xFF601410),
|
||
)
|
||
|
||
@Composable
|
||
fun TergentTheme(
|
||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||
content: @Composable () -> Unit
|
||
) {
|
||
val colorScheme = if (darkTheme) TergentDarkColors else TergentLightColors
|
||
|
||
MaterialTheme(
|
||
colorScheme = colorScheme,
|
||
typography = TergentTypography,
|
||
shapes = TergentShapes,
|
||
content = content
|
||
)
|
||
}
|
||
```
|
||
|
||
### 6.2 字体配置
|
||
|
||
```kotlin
|
||
// Typography
|
||
val TergentTypography = Typography(
|
||
displayLarge = TextStyle(
|
||
fontWeight = FontWeight.Bold,
|
||
fontSize = 28.sp,
|
||
lineHeight = 36.sp,
|
||
fontFamily = FontFamily(
|
||
Font(R.font.pingfang_sc),
|
||
Font(R.font.microsoft_yahei),
|
||
Font.SansSerif
|
||
)
|
||
),
|
||
titleLarge = TextStyle(
|
||
fontWeight = FontWeight.SemiBold,
|
||
fontSize = 20.sp,
|
||
lineHeight = 26.sp,
|
||
// same fontFamily
|
||
),
|
||
titleMedium = TextStyle(
|
||
fontWeight = FontWeight.SemiBold,
|
||
fontSize = 16.sp,
|
||
lineHeight = 22.sp,
|
||
),
|
||
bodyLarge = TextStyle(
|
||
fontWeight = FontWeight.Normal,
|
||
fontSize = 16.sp,
|
||
lineHeight = 24.sp,
|
||
),
|
||
bodyMedium = TextStyle(
|
||
fontWeight = FontWeight.Normal,
|
||
fontSize = 14.sp,
|
||
lineHeight = 21.sp,
|
||
),
|
||
bodySmall = TextStyle(
|
||
fontWeight = FontWeight.Normal,
|
||
fontSize = 12.sp,
|
||
lineHeight = 18.sp,
|
||
),
|
||
labelSmall = TextStyle(
|
||
fontWeight = FontWeight.Normal,
|
||
fontSize = 10.sp,
|
||
lineHeight = 14.sp,
|
||
)
|
||
)
|
||
```
|
||
|
||
### 6.3 圆角 Shapes
|
||
|
||
```kotlin
|
||
val TergentShapes = Shapes(
|
||
extraSmall = RoundedCornerShape(4.dp),
|
||
small = RoundedCornerShape(8.dp),
|
||
medium = RoundedCornerShape(12.dp),
|
||
large = RoundedCornerShape(16.dp),
|
||
extraLarge = RoundedCornerShape(24.dp),
|
||
)
|
||
```
|
||
|
||
### 6.4 颜色命名对照
|
||
|
||
| 设计 Token | Compose Color |
|
||
|-----------|--------------|
|
||
| `tergent_primary` | `MaterialTheme.colorScheme.primary` |
|
||
| `tergent_primary_bg` | `MaterialTheme.colorScheme.primaryContainer` |
|
||
| `tergent_accent` | `MaterialTheme.colorScheme.secondary` |
|
||
| `tergent_bg` | `MaterialTheme.colorScheme.background` |
|
||
| `tergent_surface` | `MaterialTheme.colorScheme.surface` |
|
||
| `tergent_border` | `MaterialTheme.colorScheme.outline` |
|
||
| `tergent_text_primary` | `MaterialTheme.colorScheme.onBackground` |
|
||
| `tergent_text_secondary` | `MaterialTheme.colorScheme.onSurfaceVariant` |
|
||
| `tergent_danger` | `MaterialTheme.colorScheme.error` |
|
||
|
||
### 6.5 文件结构建议 (ui/ 目录)
|
||
|
||
```
|
||
ui/
|
||
├── TergentTheme.kt # 主题: 颜色 + 字体 + Shapes
|
||
├── RootScreen.kt # 根路由 (Onboarding vs MainTabs)
|
||
├── OnboardingFlow.kt # 引导流程
|
||
├── MainTabs.kt # 底部 Tab 导航
|
||
├── screens/
|
||
│ ├── ConnectScreen.kt # 连接页
|
||
│ ├── ChatScreen.kt # 聊天页
|
||
│ ├── VoiceScreen.kt # 语音页
|
||
│ ├── CanvasScreen.kt # 画布页
|
||
│ ├── SettingsScreen.kt # 设置页 (主列表)
|
||
│ ├── DevicesScreen.kt # 节点管理
|
||
│ ├── NotificationsScreen.kt # 通知历史
|
||
│ └── DiagnosticsScreen.kt # 连接诊断
|
||
├── components/
|
||
│ ├── StatusBar.kt # 连接状态指示器
|
||
│ ├── ConnectionCard.kt # 连接信息卡片
|
||
│ ├── VoiceOrb.kt # 语音按钮 Orb
|
||
│ ├── VoiceWave.kt # 声波动画
|
||
│ ├── ChatBubble.kt # 聊天气泡
|
||
│ ├── ChatInput.kt # 输入框
|
||
│ ├── SettingsRow.kt # 设置行封装
|
||
│ └── EmptyState.kt # 空状态占位
|
||
├── navigation/
|
||
│ └── NavGraph.kt # NavHost 路由定义
|
||
└── viewmodels/
|
||
├── TergentViewModel.kt # 主 VM
|
||
├── ChatViewModel.kt # 聊天 VM
|
||
└── SettingsViewModel.kt # 设置 VM
|
||
```
|
||
|
||
---
|
||
|
||
*本文档由茂之核编写,茂之钳审阅。版本 v1.0,2026-05-23。* |