prd-to-flutter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +149 -0
  2. package/bin/p2f.mjs +18 -0
  3. package/dist/cli.js +203 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/clean.js +35 -0
  6. package/dist/commands/clean.js.map +1 -0
  7. package/dist/commands/doctor.js +148 -0
  8. package/dist/commands/doctor.js.map +1 -0
  9. package/dist/commands/generate.js +120 -0
  10. package/dist/commands/generate.js.map +1 -0
  11. package/dist/commands/init.js +46 -0
  12. package/dist/commands/init.js.map +1 -0
  13. package/dist/commands/paths.js +58 -0
  14. package/dist/commands/paths.js.map +1 -0
  15. package/dist/commands/remove.js +23 -0
  16. package/dist/commands/remove.js.map +1 -0
  17. package/dist/commands/screenshots-audit.js +39 -0
  18. package/dist/commands/screenshots-audit.js.map +1 -0
  19. package/dist/commands/skills-install.js +21 -0
  20. package/dist/commands/skills-install.js.map +1 -0
  21. package/dist/commands/sync.js +84 -0
  22. package/dist/commands/sync.js.map +1 -0
  23. package/dist/commands/update.js +93 -0
  24. package/dist/commands/update.js.map +1 -0
  25. package/dist/core/existing-page-detector.js +463 -0
  26. package/dist/core/existing-page-detector.js.map +1 -0
  27. package/dist/core/fail-fast.js +50 -0
  28. package/dist/core/fail-fast.js.map +1 -0
  29. package/dist/core/feature-coverage-builder.js +667 -0
  30. package/dist/core/feature-coverage-builder.js.map +1 -0
  31. package/dist/core/flutter-project-scanner.js +393 -0
  32. package/dist/core/flutter-project-scanner.js.map +1 -0
  33. package/dist/core/implementation-plan-writer.js +190 -0
  34. package/dist/core/implementation-plan-writer.js.map +1 -0
  35. package/dist/core/logger.js +39 -0
  36. package/dist/core/logger.js.map +1 -0
  37. package/dist/core/package-info.js +33 -0
  38. package/dist/core/package-info.js.map +1 -0
  39. package/dist/core/paths.js +37 -0
  40. package/dist/core/paths.js.map +1 -0
  41. package/dist/core/playwright-capture.js +539 -0
  42. package/dist/core/playwright-capture.js.map +1 -0
  43. package/dist/core/playwright-cli.js +26 -0
  44. package/dist/core/playwright-cli.js.map +1 -0
  45. package/dist/core/playwright-install.js +40 -0
  46. package/dist/core/playwright-install.js.map +1 -0
  47. package/dist/core/prd-clone.js +131 -0
  48. package/dist/core/prd-clone.js.map +1 -0
  49. package/dist/core/prd-install.js +108 -0
  50. package/dist/core/prd-install.js.map +1 -0
  51. package/dist/core/profile.js +149 -0
  52. package/dist/core/profile.js.map +1 -0
  53. package/dist/core/project-doc-reader.js +252 -0
  54. package/dist/core/project-doc-reader.js.map +1 -0
  55. package/dist/core/report-writer.js +90 -0
  56. package/dist/core/report-writer.js.map +1 -0
  57. package/dist/core/route-name.js +60 -0
  58. package/dist/core/route-name.js.map +1 -0
  59. package/dist/core/run-context.js +160 -0
  60. package/dist/core/run-context.js.map +1 -0
  61. package/dist/core/screenshot-auditor.js +405 -0
  62. package/dist/core/screenshot-auditor.js.map +1 -0
  63. package/dist/core/screenshot-exploration-plan-writer.js +200 -0
  64. package/dist/core/screenshot-exploration-plan-writer.js.map +1 -0
  65. package/dist/core/semantic-model-builder.js +922 -0
  66. package/dist/core/semantic-model-builder.js.map +1 -0
  67. package/dist/core/skill-install.js +78 -0
  68. package/dist/core/skill-install.js.map +1 -0
  69. package/dist/core/stage-stub.js +24 -0
  70. package/dist/core/stage-stub.js.map +1 -0
  71. package/dist/core/task-index-writer.js +149 -0
  72. package/dist/core/task-index-writer.js.map +1 -0
  73. package/dist/core/update-checker.js +155 -0
  74. package/dist/core/update-checker.js.map +1 -0
  75. package/dist/core/vue-page-locator.js +748 -0
  76. package/dist/core/vue-page-locator.js.map +1 -0
  77. package/dist/core/vue-project-reader.js +116 -0
  78. package/dist/core/vue-project-reader.js.map +1 -0
  79. package/docs/artifacts-and-agent.md +203 -0
  80. package/docs/development.md +118 -0
  81. package/docs/usage.md +246 -0
  82. package/package.json +50 -0
  83. package/skills/p2f/SKILL.md +303 -0
  84. package/skills/p2f/references/page-layout-patterns.md +120 -0
  85. package/skills/p2f/references/youfi-flutter-guidelines.md +71 -0
@@ -0,0 +1,120 @@
1
+ # YouFi 页面布局模式
2
+
3
+ 根据 p2f 产物实现普通 Flutter 页面前,特别是设置页、表单页、详情页、带底部操作区的页面,必须阅读并遵守本文。
4
+
5
+ ## 标准 AppBar
6
+
7
+ - 普通二级页面必须在 `Scaffold.appBar` 使用 `CommonAppBar`,不要手写 `AppBar`。
8
+ - 当设计需要顶部背景连续时,`CommonAppBar.backgroundColor` 应与 `Scaffold.backgroundColor` 一致。
9
+ - `CommonAppBar` 已统一标题样式、返回/关闭行为、图标颜色和状态栏主题。
10
+ - 只有已有特殊模块模式、沉浸式页面、明确自定义导航行为的页面,才使用自定义 app bar。
11
+
12
+ ## 安全区域
13
+
14
+ - 页面 `body` 必须考虑安全区域。
15
+ - 使用普通 `CommonAppBar` 时,优先使用 `SafeArea(top: false, child: ...)`,因为 app bar 已经占用了顶部,避免额外顶部空白。
16
+ - 无 app bar 页面、沉浸式页面、自定义导航页面,按已有模块模式选择安全区边缘。
17
+ - 底部固定操作区应位于底部安全区内,或位于包含底部 inset 的 `body SafeArea` 内。
18
+
19
+ ## 滚动和撑满高度
20
+
21
+ 普通纵向页面优先使用:
22
+
23
+ ```dart
24
+ SafeArea(
25
+ top: false,
26
+ child: LayoutBuilder(
27
+ builder: (context, constraints) {
28
+ return SingleChildScrollView(
29
+ child: ConstrainedBox(
30
+ constraints: BoxConstraints(minHeight: constraints.maxHeight),
31
+ child: ...,
32
+ ),
33
+ );
34
+ },
35
+ ),
36
+ )
37
+ ```
38
+
39
+ 这样短内容可以撑满可用高度,长内容可以滚动,也能避免脆弱的固定高度。
40
+
41
+ 当页面同时有主体内容和底部操作区时,把两者都放在受约束的 body 内:
42
+
43
+ ```dart
44
+ Column(
45
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
46
+ children: [
47
+ // main content
48
+ // bottom action
49
+ ],
50
+ )
51
+ ```
52
+
53
+ 除非现有页面架构要求,否则不要为了固定底部按钮而使用绝对定位。
54
+
55
+ ## 设置/表单页面骨架
56
+
57
+ 以 `lib/app/modules/profile/views/settings_view.dart` 作为参考模式:
58
+
59
+ ```dart
60
+ return Scaffold(
61
+ backgroundColor: themeService.colors.colorBgSurface2,
62
+ appBar: CommonAppBar(
63
+ title: 'settings_title'.tr,
64
+ backgroundColor: themeService.colors.colorBgSurface2,
65
+ ),
66
+ body: SafeArea(
67
+ top: false,
68
+ child: LayoutBuilder(
69
+ builder: (context, constraints) {
70
+ return SingleChildScrollView(
71
+ child: ConstrainedBox(
72
+ constraints: BoxConstraints(minHeight: constraints.maxHeight),
73
+ child: Padding(
74
+ padding: EdgeInsets.fromLTRB(16.w, 16.w, 16.w, 32.w),
75
+ child: Column(
76
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
77
+ children: [
78
+ // page content
79
+ // bottom action
80
+ ],
81
+ ),
82
+ ),
83
+ ),
84
+ );
85
+ },
86
+ ),
87
+ ),
88
+ );
89
+ ```
90
+
91
+ ## 滚动物理
92
+
93
+ `YouFiScrollBehavior` 已全局处理常见滚动物理效果。只有当前页面或现有模块模式需要时,才显式添加 `BouncingScrollPhysics`。
94
+
95
+ ## 防止 OVERFLOWED
96
+
97
+ 实现页面时必须把截图当作一个状态样本,而不是固定尺寸模板。凡是文案、数字、价格、按钮、标签、列表项可能随语言、数据或设备宽度变化的地方,都要主动处理约束。
98
+
99
+ - `Row` 中放文本、价格、名称、按钮组时,文本侧优先包 `Expanded` 或 `Flexible`,并设置合适的 `maxLines`、`overflow: TextOverflow.ellipsis` 或允许换行。
100
+ - `Column` 内如果页面在 `SingleChildScrollView` 中,不要随意使用 `Expanded` / `Flexible`,因为滚动方向上常是无界约束;需要撑满高度时用本文的 `LayoutBuilder + ConstrainedBox(minHeight: constraints.maxHeight)` 模式。
101
+ - 标签、筛选项、按钮组、横向状态项如果可能超出一行,优先使用 `Wrap`、横向 `SingleChildScrollView`,或按现有模块模式折行/滚动;不要用固定宽 Row 硬塞。
102
+ - 固定宽度只能用于图标、头像、开关、短数字列等稳定元素。对名称、描述、国际化文案、金额、百分比、证券代码等动态内容,必须给弹性空间和溢出策略。
103
+ - 不要用截图里的精确像素高度强行限制包含文本的容器。含文本的卡片、列表项、表单项优先使用 `minHeight`、`padding` 和内容自然高度。
104
+ - 多语言场景下英文通常更长,中文/繁中/英文三套 `.tr` 文案都要考虑。按钮文字、标题、副标题、空状态文案尤其容易溢出。
105
+ - 金融数字、金额、百分比、期权合约名等要按最大合理长度设计,不要只按截图样本宽度设计。
106
+ - 表格/左右两列行情数据要用明确的列宽策略:稳定列可固定,文本列弹性,窄屏下按已有模块模式横向滚动或省略。
107
+ - `Stack` 中不要让绝对定位文本依赖单一屏幕尺寸;能用普通布局表达时不要用 `Positioned` 贴位置。
108
+ - 如果必须裁剪,用 `ClipRect`、`ClipRRect` 等明确表达裁剪意图;不要让 RenderFlex 自然 overflow。
109
+
110
+ ## OVERFLOWED 检测
111
+
112
+ `flutter analyze` 通常检测不到运行时布局 overflow。实现后必须用运行时或测试手段检查。
113
+
114
+ - 首选在目标页面 debug 运行,查看控制台是否出现 `A RenderFlex overflowed by ... pixels`、`Bottom overflowed by ... pixels` 等日志。
115
+ - 对照 p2f 的 `375x812` 基准截图检查,同时至少检查一个更窄/更矮的视口或设备尺寸,尤其是有键盘、底部按钮、长列表、长标题的页面。
116
+ - 手工截图或自动截图时,必须检查是否出现黄色/黑色 overflow 条纹、红色 `OVERFLOWED` 标记、文字截断异常、按钮文字挤压、底部按钮被安全区遮挡。
117
+ - 对支持多语言的页面,至少用最长文案语言检查关键标题、按钮、菜单项、空状态和弹窗;新增翻译后尤其要检查。
118
+ - 对动态数据页面,使用长名称、长金额、长百分比、空数据、极端涨跌值等样本检查,而不是只用截图中的短 mock 数据。
119
+ - 如果项目已有 widget test 或页面 smoke test,可以在测试里捕获 `FlutterError.onError`,把包含 `overflowed by`、`RenderFlex overflowed` 的错误作为失败条件。
120
+ - 修复 overflow 后再次截图确认,不要只看代码推断。
@@ -0,0 +1,71 @@
1
+ # YouFi Flutter 代码规范
2
+
3
+ 根据 p2f 产物实现或修改 YouFi 页面 Dart 代码前,必须阅读并遵守本文。
4
+
5
+ ## 需要检查的本地文件
6
+
7
+ - `README.md`:开发规范、UI 规范、网络请求规范、字体规范。
8
+ - `CLAUDE.md`:架构、模块结构、页面基类、关键规则。
9
+ - `analysis_options.yaml`:Dart lint 规则。
10
+ - `lib/app/services/theme_service.dart`:主题色、文字样式、主题切换。
11
+ - `lib/app/common/theme/app_colors_extension.dart`:语义色 token。
12
+ - `lib/app/common/theme/text_style_extension.dart`:文字样式 token。
13
+ - `lib/app/common/theme/app_values.dart`:尺寸、间距、圆角常量。
14
+ - `lib/app/common/base/base_get_view.dart`:页面基类和生命周期。
15
+ - `lib/app/common/widget/common_app_bar.dart`:统一导航栏。
16
+
17
+ ## 主题、文字、尺寸
18
+
19
+ - 颜色必须优先使用 `themeService.colors.*`。常用 token 包括:`colorBgBase`、`colorBgSurface1`、`colorBgSurface2`、`colorTextNormal`、`colorTextDescription`、`colorLightLine`、`colorButtonBrand`、`colorButtonDisabled`、`colorTrendRed1`、`colorTrendGreen1`、`colorTrendFlat`。
20
+ - 只有为了匹配已有历史代码时才使用 `AppColors.*`。新增业务 UI 不要新增裸 `Color(0x...)`。
21
+ - 行情上涨、下跌、平盘颜色必须优先使用已有 helper,例如 `PriceColorHelper`;不要自己手写红绿逻辑。
22
+ - 文本样式必须优先使用 `themeService.textStyles.*`,再用 `.copyWith(...)` 做局部颜色、字重调整。不要散落原始 `TextStyle(fontSize: ...)`,除非当前模块已有同类写法或没有合适 token。
23
+ - p2f 页面实现必须先读取 `cli/analysis/text-style-map.md`,按其中 CSS 字号/字重/行高到 YouFi token 的映射选择 `themeService.textStyles.*`;不要凭视觉猜 `bodyM/bodyR`。
24
+ - 文字 token 包括:`titleB`、`titleM`、`headlineB`、`headlineM`、`bodyM`、`bodyR`、`small1R`、`small1M`、`small2R`、`small3R`、`small4R`、`small4M`、`number1B`、`number2B`、`number3B`、`tabB`、`tabM`。
25
+ - 尺寸、间距、圆角、图标大小必须使用 ScreenUtil `.w`、`.h`、`.sp`、`.r` 或已有 `AppValues.*`;不要写未适配的设计稿像素。
26
+ - App 入口已经禁用系统文字缩放,不要额外做局部 `textScaler` 处理。
27
+ - 图片优先使用 `CommonImage.asset`,以便深色模式自动把 `assets/images/` 映射到 `assets/dark_images/`。SVG 优先使用 `CommonSvg.asset`。
28
+ - 判断当前真实浅色/深色状态时使用 `themeService.isRealLightTheme`。
29
+ - 除非用户明确要求,不要修改全局主题、状态栏/导航栏行为、`ThemeService.lightColors/darkColors` 或 `AppTheme`。
30
+
31
+ ## 页面、状态、模块
32
+
33
+ - 页面优先使用 `BaseGetView<T>`,实现 `createController()` 和 `buildView()`。除非匹配已有模块模式,不要新增裸 `GetView<T>` 或 `StatefulWidget`。
34
+ - Controller 继承 `GetxController`;响应式状态使用 `.obs`;UI 更新使用 `Obx()`。
35
+ - `Obx` 只能包裹直接消费 Rx 的最小 UI 片段,禁止把整页、整块静态布局或无关子树套进一个大 `Obx`。
36
+ - 不要用 `controller.foo.value;` 这种空读来“骗过” GetX;Rx 值必须直接参与当前 `Obx` builder 返回的 widget 渲染。
37
+ - 如果子组件依赖 Rx,优先在子组件内部用局部 `Obx`,或在父级 `Obx` 中读取 `.value` 后把普通值传给子组件。
38
+ - `BaseGetView` 已负责页面控制器生命周期;不要为了页面有状态就在 `buildView()` 外层包整页 `Obx`。`dart analyze` 通过不代表 GetX 运行时响应式范围正确。
39
+ - 页面生命周期使用 `onInit()`、`onVisible()`、`onInvisible()`、`onDispose()`。请求取消、WebSocket 取消订阅等资源清理放在合适生命周期里。
40
+ - 模块目录遵循 `controllers/`、`views/`、`models/`、`net/`、`widgets/`、`utils/`、`bindings/`。页面私有组件放在模块内部;除非有明确跨模块复用,不要提升到 `lib/app/common/`。
41
+ - 页面要按功能需要处理 Loading、Empty、Content、Error/Retry 状态,不要只实现静态截图态。
42
+
43
+ ## Tab、分段控件和指标切换
44
+
45
+ - 实现 Tab、segmented control、筛选 chip、指标切换等高频点击控件时,应尽量保留原型的轻量过渡感。
46
+ - 对胶囊型选中态,优先用一个单独的选中 pill / indicator 背景层在选项之间移动,例如 `Stack` + `AnimatedPositioned` / `AnimatedAlign`。不要让每个 Tab item 各自用 `AnimatedContainer` 绘制选中背景,否则切换时容易出现旧背景退场和新背景入场叠加的残影/闪烁。
47
+ - 文本颜色过渡使用 `AnimatedDefaultTextStyle`、`DefaultTextStyle` 配合父级动画,或项目已有组件。容器背景移动和文字颜色变化应使用接近的 `duration` / `curve`,避免一个瞬切、一个动画。
48
+ - Tab 组的 `Obx` 范围要收敛到实际消费 active state 的最小组件。不要把整块包含大量静态内容的区域包进同一个 `Obx`;如果 Tab item 是独立私有组件,应在 item 内部消费 active state,或由父级读取后传入普通 `isActive`。
49
+
50
+ ## 路由、国际化、资源
51
+
52
+ - 新增或修改路由必须使用当前仓库实际存在的路由文件。检查 `lib/app/routes/app_routes.dart` / `app_pages.dart` 和 `lib/app/routes/utils/app_routes.dart` / `app_pages.dart`,使用当前项目启用的一组。
53
+ - 所有用户可见文本必须使用 `.tr`。新增 key 必须写入 `lib/app/translations/zh_CN.dart`、`zh_HK.dart`、`en_US.dart`。
54
+ - 带参数文案使用 `.trParams({'key': value})`;翻译模板使用 `@key`。
55
+ - 文件名使用 `snake_case.dart`;类名 `PascalCase`;变量/方法 `camelCase`;常量 `UPPER_SNAKE_CASE`;翻译 key `snake_case`。
56
+ - 引用资源前先检查 `pubspec.yaml` 和现有 assets。不要引入未声明的资源路径。
57
+
58
+ ## 网络、存储、实时数据
59
+
60
+ - 网络请求必须使用项目 HTTP 客户端,例如 `Http.instance.get/post/put/delete` 或目标模块已有 domain client。
61
+ - API 路径必须使用 `lib/app/common/constants/apis.dart` 中的 `Apis.*` 常量。
62
+ - `ErrorInterceptor` 统一处理错误展示;除非现有模块有明确特殊逻辑,否则不要在 `onError` 里重复 Toast。
63
+ - 使用 `CancelToken`,并在 `onClose()` 或相关清理生命周期中取消。
64
+ - WebSocket 订阅和取消订阅必须使用同一个函数引用。
65
+ - Hive model 使用项目注解,并通过 `HiveManager.instance.box<T>(ModelClass)` 访问。修改 Hive model 后运行 `dart run build_runner build`。
66
+
67
+ ## Lint 和验证
68
+
69
+ - 遵守 `analysis_options.yaml`:禁止 `print`,优先 `const`,优先 `final`,公开 API 显式返回类型,添加 `@override`,Widget 构造函数传 key,处理 `use_build_context_synchronously`,关闭 subscriptions/sinks。
70
+ - 项目不强制 80 字符行宽。优先保证可读性,格式交给 `dart format`。
71
+ - 生成或修改代码后优先运行 `./scripts/check_code.sh lib`。该脚本会执行 `dart analyze --fatal-warnings --fatal-infos`,warnings 和 infos 都必须修。