refactor: 移除前端 Mock API,新增共享组件,优化认证流程
- 删除 mock_api_client、mock_calendar_service、mock_history_service - 新增 fixed_length_code_input、link_button、message_composer 共享组件 - 优化登录/注册/密码重置页面使用新组件 - 简化 injection.dart 移除 mock 分支 - 更新 env.dart 配置(BACKEND_URL 替换 API_URL) - 后端 agentscope 工具和测试更新 - 重构 AGENTS.md 文档结构 - 新增 deploy/ 目录和 protocol 文档
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
# Home Composer Redesign Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** 重做 Home 输入组件,统一为单胶囊容器并稳定语音长按交互,消除布局漂移,同时保持 `+` 与发送等既有业务逻辑不变。
|
||||
|
||||
**Architecture:** 采用“单容器双模式”方案:`HomeScreen` 继续持有业务状态与动作,新增 `HomeComposer` 专注 UI 与手势分发;中间区域用受控状态在文本/按住说话/录音中/识别中之间切换,外层高度固定。录音提示与声波动画都内聚在主容器内部渲染,避免额外布局占位。
|
||||
|
||||
**Tech Stack:** Flutter, flutter_bloc, lucide_icons, design tokens (`AppColors`/`AppSpacing`/`AppRadius`), widget test
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 建立 HomeComposer 组件骨架与参数契约
|
||||
|
||||
**Files:**
|
||||
- Create: `apps/lib/features/home/ui/widgets/home_composer.dart`
|
||||
- Modify: `apps/lib/features/home/ui/screens/home_screen.dart`
|
||||
- Test: `apps/test/features/home/ui/widgets/home_composer_test.dart`
|
||||
|
||||
**Step 1: 写失败测试(渲染结构)**
|
||||
|
||||
```dart
|
||||
testWidgets('renders one unified rounded composer container', (tester) async {
|
||||
// pump HomeComposer with minimum required callbacks/states
|
||||
// expect: one root container, plus button, center content slot, right action slot
|
||||
});
|
||||
```
|
||||
|
||||
**Step 2: 运行测试确认失败**
|
||||
|
||||
Run: `flutter test test/features/home/ui/widgets/home_composer_test.dart --plain-name "renders one unified rounded composer container"`
|
||||
Expected: FAIL(`HomeComposer` 未定义或结构不匹配)
|
||||
|
||||
**Step 3: 写最小实现**
|
||||
|
||||
```dart
|
||||
class HomeComposer extends StatelessWidget {
|
||||
const HomeComposer({
|
||||
super.key,
|
||||
required this.isHoldToSpeakMode,
|
||||
required this.isRecording,
|
||||
required this.isTranscribing,
|
||||
required this.hasMessage,
|
||||
required this.isWaitingAgent,
|
||||
required this.onTapPlus,
|
||||
required this.onTapRightAction,
|
||||
required this.centerChild,
|
||||
});
|
||||
// unified capsule container with left/center/right slots
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4: 再跑测试确认通过**
|
||||
|
||||
Run: `flutter test test/features/home/ui/widgets/home_composer_test.dart --plain-name "renders one unified rounded composer container"`
|
||||
Expected: PASS
|
||||
|
||||
**Step 5: 小步提交(仅用户明确要求时)**
|
||||
|
||||
```bash
|
||||
git add apps/lib/features/home/ui/widgets/home_composer.dart apps/lib/features/home/ui/screens/home_screen.dart apps/test/features/home/ui/widgets/home_composer_test.dart
|
||||
git commit -m "refactor: extract unified home composer container"
|
||||
```
|
||||
|
||||
### Task 2: 完成右侧图标状态映射(activity/keyboard/send/stop)
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/features/home/ui/widgets/home_composer.dart`
|
||||
- Modify: `apps/lib/features/home/ui/screens/home_screen.dart`
|
||||
- Test: `apps/test/features/home/ui/widgets/home_composer_test.dart`
|
||||
|
||||
**Step 1: 写失败测试(图标状态机)**
|
||||
|
||||
```dart
|
||||
testWidgets('right action icon follows state priority', (tester) async {
|
||||
// waiting > hasMessage > holdToSpeakMode > textMode
|
||||
// expect LucideIcons.square/send/keyboard/activity respectively
|
||||
});
|
||||
```
|
||||
|
||||
**Step 2: 运行测试确认失败**
|
||||
|
||||
Run: `flutter test test/features/home/ui/widgets/home_composer_test.dart --plain-name "right action icon follows state priority"`
|
||||
Expected: FAIL(图标选择逻辑尚未完整实现)
|
||||
|
||||
**Step 3: 最小实现图标决策**
|
||||
|
||||
```dart
|
||||
IconData resolveRightIcon(...) {
|
||||
if (isWaitingAgent) return LucideIcons.square;
|
||||
if (hasMessage) return LucideIcons.send;
|
||||
return isHoldToSpeakMode ? LucideIcons.keyboard : LucideIcons.activity;
|
||||
}
|
||||
```
|
||||
|
||||
**Step 4: 再跑测试确认通过**
|
||||
|
||||
Run: `flutter test test/features/home/ui/widgets/home_composer_test.dart --plain-name "right action icon follows state priority"`
|
||||
Expected: PASS
|
||||
|
||||
**Step 5: 小步提交(仅用户明确要求时)**
|
||||
|
||||
```bash
|
||||
git add apps/lib/features/home/ui/widgets/home_composer.dart apps/lib/features/home/ui/screens/home_screen.dart apps/test/features/home/ui/widgets/home_composer_test.dart
|
||||
git commit -m "refactor: stabilize composer right action icon mapping"
|
||||
```
|
||||
|
||||
### Task 3: 实现中间区域双模式替换并固定高度
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/features/home/ui/widgets/home_composer.dart`
|
||||
- Modify: `apps/lib/features/home/ui/screens/home_screen.dart`
|
||||
- Test: `apps/test/features/home/ui/widgets/home_composer_test.dart`
|
||||
|
||||
**Step 1: 写失败测试(模式切换不改变高度)**
|
||||
|
||||
```dart
|
||||
testWidgets('composer height remains stable across mode switches', (tester) async {
|
||||
// measure size in text mode and hold-to-speak mode
|
||||
// expect equal heights
|
||||
});
|
||||
```
|
||||
|
||||
**Step 2: 运行测试确认失败**
|
||||
|
||||
Run: `flutter test test/features/home/ui/widgets/home_composer_test.dart --plain-name "composer height remains stable across mode switches"`
|
||||
Expected: FAIL(当前结构切换时高度波动)
|
||||
|
||||
**Step 3: 最小实现(AnimatedSwitcher + fixed constraints)**
|
||||
|
||||
```dart
|
||||
SizedBox(
|
||||
height: composerHeight,
|
||||
child: AnimatedSwitcher(
|
||||
duration: switchDuration,
|
||||
child: isHoldToSpeakMode ? holdToSpeakChild : textInputChild,
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
**Step 4: 再跑测试确认通过**
|
||||
|
||||
Run: `flutter test test/features/home/ui/widgets/home_composer_test.dart --plain-name "composer height remains stable across mode switches"`
|
||||
Expected: PASS
|
||||
|
||||
**Step 5: 小步提交(仅用户明确要求时)**
|
||||
|
||||
```bash
|
||||
git add apps/lib/features/home/ui/widgets/home_composer.dart apps/lib/features/home/ui/screens/home_screen.dart apps/test/features/home/ui/widgets/home_composer_test.dart
|
||||
git commit -m "refactor: keep composer layout stable during mode switch"
|
||||
```
|
||||
|
||||
### Task 4: 实现长按录音交互(开始/上滑取消/松开发送)
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/features/home/ui/screens/home_screen.dart`
|
||||
- Modify: `apps/lib/features/home/ui/widgets/home_composer.dart`
|
||||
- Test: `apps/test/features/home/ui/widgets/home_composer_test.dart`
|
||||
|
||||
**Step 1: 写失败测试(录音提示只在 recording)**
|
||||
|
||||
```dart
|
||||
testWidgets('recording hint appears only while recording', (tester) async {
|
||||
// idle hold-to-speak: no hint
|
||||
// recording: show "松开发送,上滑取消"
|
||||
});
|
||||
```
|
||||
|
||||
**Step 2: 运行测试确认失败**
|
||||
|
||||
Run: `flutter test test/features/home/ui/widgets/home_composer_test.dart --plain-name "recording hint appears only while recording"`
|
||||
Expected: FAIL
|
||||
|
||||
**Step 3: 最小实现录音流程映射**
|
||||
|
||||
```dart
|
||||
onLongPressStart => HapticFeedback.lightImpact() + onHoldStart();
|
||||
onLongPressMoveUpdate => if (dy < threshold) onHoldCancel();
|
||||
onLongPressEnd => onHoldEnd(autoSend: true);
|
||||
```
|
||||
|
||||
**Step 4: 再跑测试确认通过**
|
||||
|
||||
Run: `flutter test test/features/home/ui/widgets/home_composer_test.dart --plain-name "recording hint appears only while recording"`
|
||||
Expected: PASS
|
||||
|
||||
**Step 5: 小步提交(仅用户明确要求时)**
|
||||
|
||||
```bash
|
||||
git add apps/lib/features/home/ui/widgets/home_composer.dart apps/lib/features/home/ui/screens/home_screen.dart apps/test/features/home/ui/widgets/home_composer_test.dart
|
||||
git commit -m "feat: rework hold-to-speak interaction with stable recording state"
|
||||
```
|
||||
|
||||
### Task 5: 视觉重构为轻拟物胶囊(仅 tokens)
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/features/home/ui/widgets/home_composer.dart`
|
||||
- Modify: `apps/lib/core/theme/design_tokens.dart`(仅当现有 token 不足时新增)
|
||||
- Test: `apps/test/features/home/ui/widgets/home_composer_test.dart`
|
||||
|
||||
**Step 1: 写失败测试(主容器统一性)**
|
||||
|
||||
```dart
|
||||
testWidgets('plus, center and right action are inside same capsule', (tester) async {
|
||||
// find one capsule host and verify children are descendants
|
||||
});
|
||||
```
|
||||
|
||||
**Step 2: 运行测试确认失败**
|
||||
|
||||
Run: `flutter test test/features/home/ui/widgets/home_composer_test.dart --plain-name "plus, center and right action are inside same capsule"`
|
||||
Expected: FAIL
|
||||
|
||||
**Step 3: 最小视觉实现(不硬编码)**
|
||||
|
||||
```dart
|
||||
// use AppColors/AppSpacing/AppRadius and existing shadow tokens
|
||||
// no hardcoded color/spacing/radius/size
|
||||
```
|
||||
|
||||
**Step 4: 再跑测试确认通过**
|
||||
|
||||
Run: `flutter test test/features/home/ui/widgets/home_composer_test.dart --plain-name "plus, center and right action are inside same capsule"`
|
||||
Expected: PASS
|
||||
|
||||
**Step 5: 小步提交(仅用户明确要求时)**
|
||||
|
||||
```bash
|
||||
git add apps/lib/features/home/ui/widgets/home_composer.dart apps/lib/core/theme/design_tokens.dart apps/test/features/home/ui/widgets/home_composer_test.dart
|
||||
git commit -m "refactor: redesign home composer with neumorphic capsule style"
|
||||
```
|
||||
|
||||
### Task 6: 集成回归与文档同步
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/features/home/ui/screens/home_screen.dart`
|
||||
- Modify: `docs/runtime/runtime-route.md`(若交互说明有变化)
|
||||
|
||||
**Step 1: 运行目标测试文件**
|
||||
|
||||
Run: `flutter test test/features/home/ui/widgets/home_composer_test.dart`
|
||||
Expected: PASS
|
||||
|
||||
**Step 2: 运行 Home 相关回归测试(若新增)**
|
||||
|
||||
Run: `flutter test test/features/home`
|
||||
Expected: PASS(若目录存在)
|
||||
|
||||
**Step 3: 运行应用侧基础回归**
|
||||
|
||||
Run: `flutter test`
|
||||
Expected: PASS 或仅存在与本改动无关的已知失败
|
||||
|
||||
**Step 4: 记录验证结论**
|
||||
|
||||
```text
|
||||
- 输入组件统一容器:通过
|
||||
- 模式切换稳定:通过
|
||||
- 录音提示条件:通过
|
||||
- + 按钮/发送逻辑:通过
|
||||
```
|
||||
|
||||
**Step 5: 小步提交(仅用户明确要求时)**
|
||||
|
||||
```bash
|
||||
git add apps/lib/features/home/ui/screens/home_screen.dart apps/lib/features/home/ui/widgets/home_composer.dart apps/test/features/home/ui/widgets/home_composer_test.dart docs/runtime/runtime-route.md
|
||||
git commit -m "test: add regression coverage for home composer redesign"
|
||||
```
|
||||
|
||||
## 实施注意事项
|
||||
|
||||
- 保持 `HomeScreen` 作为业务状态单一来源,避免在 `HomeComposer` 内部复制业务状态。
|
||||
- 录音中 (`recording`) 禁止触发模式切换,防止并发手势引发错位。
|
||||
- 严格遵守 `apps/AGENTS.md`:不硬编码视觉值,必须使用 design tokens。
|
||||
- 用户反馈统一使用 `Toast.show(...)`,不得引入 `SnackBar`。
|
||||
Reference in New Issue
Block a user