- 删除 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 文档
9.7 KiB
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: 写失败测试(渲染结构)
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: 写最小实现
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: 小步提交(仅用户明确要求时)
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: 写失败测试(图标状态机)
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: 最小实现图标决策
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: 小步提交(仅用户明确要求时)
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: 写失败测试(模式切换不改变高度)
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)
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: 小步提交(仅用户明确要求时)
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)
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: 最小实现录音流程映射
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: 小步提交(仅用户明确要求时)
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: 写失败测试(主容器统一性)
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: 最小视觉实现(不硬编码)
// 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: 小步提交(仅用户明确要求时)
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: 记录验证结论
- 输入组件统一容器:通过
- 模式切换稳定:通过
- 录音提示条件:通过
- + 按钮/发送逻辑:通过
Step 5: 小步提交(仅用户明确要求时)
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。