From b62e9697a48b603ac2ee635e6ccce80aac7fb538 Mon Sep 17 00:00:00 2001
From: QiuSW <105186638@qq.com>
Date: Tue, 4 Aug 2026 17:54:15 +0800
Subject: [PATCH] feat: add mobile interaction prototype
---
docs/README.md | 5 +-
docs/decisions.md | 11 +
docs/environment.md | 5 +
docs/implementation-plan.md | 47 +-
docs/prototype.md | 190 +++
docs/quality-gates.md | 13 +
prototype/README.md | 26 +
prototype/app.js | 733 ++++++++++
prototype/capture.mjs | 342 +++++
prototype/index.html | 58 +
prototype/screenshots/01-welcome.png | Bin 0 -> 55290 bytes
prototype/screenshots/02-question.png | Bin 0 -> 59376 bytes
prototype/screenshots/03-casting.png | Bin 0 -> 51061 bytes
prototype/screenshots/04-result-moving.png | Bin 0 -> 94007 bytes
prototype/screenshots/05-result-static.png | Bin 0 -> 75291 bytes
prototype/screenshots/06-ai-consent.png | Bin 0 -> 59884 bytes
prototype/screenshots/07-ai-explanation.png | Bin 0 -> 128735 bytes
prototype/screenshots/08-ai-error.png | Bin 0 -> 111856 bytes
prototype/styles.css | 1459 +++++++++++++++++++
19 files changed, 2875 insertions(+), 14 deletions(-)
create mode 100644 docs/prototype.md
create mode 100644 prototype/README.md
create mode 100644 prototype/app.js
create mode 100644 prototype/capture.mjs
create mode 100644 prototype/index.html
create mode 100644 prototype/screenshots/01-welcome.png
create mode 100644 prototype/screenshots/02-question.png
create mode 100644 prototype/screenshots/03-casting.png
create mode 100644 prototype/screenshots/04-result-moving.png
create mode 100644 prototype/screenshots/05-result-static.png
create mode 100644 prototype/screenshots/06-ai-consent.png
create mode 100644 prototype/screenshots/07-ai-explanation.png
create mode 100644 prototype/screenshots/08-ai-error.png
create mode 100644 prototype/styles.css
diff --git a/docs/README.md b/docs/README.md
index 479004c..2eb2569 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -2,7 +2,7 @@
> 文档状态:方案基线
> 最后核验:2026-08-04
-> 当前阶段:Android 工程尚未初始化,需求与工程约束已建立
+> 当前阶段:P-1 高保真交互原型已产出、待视觉确认;Android 工程尚未初始化
本目录是 Brainwave 的项目知识事实源。产品决策、领域算法、架构边界、验收标准和已知失败模式必须写入仓库;聊天记录、口头约定和临时提示不构成项目规范。
@@ -19,6 +19,7 @@ Brainwave 是一个以《易经》三枚铜币法为文化背景的 Android 个
| [产品规格](product-spec.md) | 判断功能范围和验收结果时 | 目标、非目标、需求编号、MVP 边界 |
| [领域规则](domain-rules.md) | 修改投币、六爻、卦象映射时 | 唯一允许的起卦算法与不变量 |
| [UX 与东方视觉](ux-design.md) | 修改页面、文案、动画和主题时 | 用户流程、文化表达、无障碍标准 |
+| [移动端交互原型](prototype.md) | 评审流程、视觉或开始 Compose 页面前 | 可点击原型、截图入口、确认清单和 Android 映射 |
| [系统架构](architecture.md) | 新增包、依赖、数据源或网络能力时 | 分层、依赖方向、运行时数据流 |
| [数据与内容](data-content.md) | 修改卦库、历史记录或内容来源时 | 数据契约、授权、隐私和迁移规则 |
| [AI 解释与安全](ai-safety.md) | 修改提示词、模型调用或解释结果时 | AI 调用门、输入输出契约和安全边界 |
@@ -31,7 +32,7 @@ Brainwave 是一个以《易经》三枚铜币法为文化背景的 Android 个
## 推荐阅读路径
- 实现起卦:本页 → [领域规则](domain-rules.md) → [系统架构](architecture.md) → [质量门禁](quality-gates.md)
-- 实现界面:本页 → [产品规格](product-spec.md) → [UX 与东方视觉](ux-design.md) → [系统架构](architecture.md)
+- 实现界面:本页 → [产品规格](product-spec.md) → [UX 与东方视觉](ux-design.md) → [移动端交互原型](prototype.md) → [系统架构](architecture.md)
- 接入 AI:本页 → [AI 解释与安全](ai-safety.md) → [数据与内容](data-content.md) → [质量门禁](quality-gates.md)
- 初始化/构建:本页 → [本地开发环境](environment.md) → [系统架构](architecture.md) → [实施计划](implementation-plan.md)
- 修复缺陷:本页 → [失败记忆](failure-memory.md) → 对应领域文档 → [质量门禁](quality-gates.md)
diff --git a/docs/decisions.md b/docs/decisions.md
index 93f50af..69bb8c1 100644
--- a/docs/decisions.md
+++ b/docs/decisions.md
@@ -90,6 +90,17 @@
- 原因:不同解释传统存在差异,产品不应把一种裁决算法伪装成唯一事实。
- 后果:AI 可以组织内容,但输入和界面保留全部动爻。
+## ADR-011:先确认可点击原型,再固化 Compose 页面
+
+- 状态:`Accepted`
+- 日期:2026-08-04
+- 关联:[移动端交互原型](prototype.md)、[实施计划 P-1](implementation-plan.md#2-p-1可点击原型与视觉确认)
+- 决定:Android 工程初始化前先产出可点击高保真原型和关键状态截图,由用户确认视觉与流程后再实现 Compose 页面。
+- 原因:手动录入、结果层级、AI 同意门和东方文化表达都具有较高体验返工成本,先在无构建成本的原型中验证更易调整。
+- 备选:Android 骨架初始化后直接实现 Compose;低保真线框图。
+- 后果:原型是体验契约与评审证据,但不是生产领域代码;P1 必须独立实现和穷举验证算法,P3 复用已确认的设计令牌与状态关系。
+- 复审条件:用户否定当前流程或目标平台发生变化。
+
## 未决问题
| ID | 问题 | 推荐默认 | 阻塞阶段 |
diff --git a/docs/environment.md b/docs/environment.md
index e45ae85..579418d 100644
--- a/docs/environment.md
+++ b/docs/environment.md
@@ -36,6 +36,8 @@
| `core.autocrlf` | `true` |
| `core.longpaths` | 未显式设置 |
| Python | `3.10.11`,`C:\Python310\python.exe` |
+| Node.js / npm | `22.22.1` / `11.12.1` |
+| Google Chrome | `150.0.7871.188`,`C:\Program Files\Google\Chrome\Application\chrome.exe` |
环境文件写入前,仓库没有 Gradle Wrapper、`build.gradle*`、`settings.gradle*` 或根 `AGENTS.md`。这些属于 Android 工程初始化阶段的交付物,不应被误认为已存在。
@@ -145,6 +147,7 @@ Android Studio 不是当前环境的可用前提。项目必须先支持 PowerSh
5. 仪器测试优先使用已连接的 API 36、arm64 真机;不假设 Emulator/AVD 存在。
6. 不自动安装系统级工具、SDK 平台或模拟器;确需新增环境能力时,先说明影响。
7. 发布时的 `targetSdk` 和商店要求是独立发布门禁,不能因为本机只有 Platform 34 就永久锁定;工具链升级后必须更新本文件和相关 ADR。
+8. P-1 原型截图使用现有 Node.js 与 Chrome DevTools 协议生成,不额外安装前端依赖;命令见[移动端交互原型](prototype.md#3-运行方式)。
## 10. 已知缺口
@@ -166,6 +169,8 @@ Android Studio 不是当前环境的可用前提。项目必须先支持 PowerSh
Get-CimInstance Win32_OperatingSystem
$PSVersionTable
git --version
+node --version
+npm --version
java -version
javac -version
Get-ChildItem $env:ANDROID_HOME\platforms -Directory
diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md
index 35f090c..603c85e 100644
--- a/docs/implementation-plan.md
+++ b/docs/implementation-plan.md
@@ -1,11 +1,13 @@
# 分阶段实施计划
-> 状态:未开始
-> 计划原则:先锁定确定性领域核心,再接内容和 UI,最后接网络 AI
+> 状态:P-1 高保真交互原型已完成,待用户视觉确认;P0 尚未开始
+> 计划原则:先用原型确认高返工成本体验,再锁定确定性领域核心,随后接内容和 UI,最后接网络 AI
## 1. 依赖图
```text
+P-1 可点击原型与视觉确认
+ ↓
P0 工程骨架
├── P1 起卦领域核心 ──→ P3 主流程 UI ──→ P4 本地完整 MVP
├── P2 内容数据管线 ──→ P3 主流程 UI
@@ -14,9 +16,30 @@ P0 工程骨架
P4 本地完整 MVP ──→ P5 AI 后端与解释
```
-P1 与 P2 可并行,但 P3 不能在领域与内容契约未稳定时复制临时算法或硬编码卦辞。
+P1 与 P2 可并行,但 P3 不能在领域与内容契约未稳定时复制原型算法或硬编码卦辞。P-1 的视觉确认不改变 P1/P2 的工程门禁。
-## 2. P0:仓库与 Android 骨架
+## 2. P-1:可点击原型与视觉确认
+
+目标:在 Android 工程初始化前确认核心流程、信息层级、手动录入方式、AI 同意门和东方文化视觉方向。
+
+任务:
+
+- 建立 360 × 792 基准视口的高保真 HTML 原型和设计令牌。
+- 覆盖欢迎、起念、六轮录入、结果、本地/AI 选择、同意、成功与失败状态。
+- 使用已知夹具核对本卦、之卦、动爻和无动爻布局。
+- 导出关键状态截图,并把评审入口与 Android 映射写入[原型说明](prototype.md)。
+- 收集用户对视觉、录入控件、结果层级与解释语气的明确反馈。
+
+退出条件:
+
+- 可点击主流程及关键异常状态可在 360 × 792 浏览器中复现。
+- `9,8,8,8,8,8` 显示复 24 → 坤 2、初爻动;全 7 显示乾 1 且无之卦。
+- 原型不发起外部请求,不伪装真实 AI,不把工作名当作正式决定。
+- 用户确认或提出一轮可执行的修改意见;确认前不把视觉固化为生产 Compose 页面。
+
+当前交付物已完成,视觉确认仍待用户评审。
+
+## 3. P0:仓库与 Android 骨架
目标:建立可构建、可测试、可导航的原生 Android 项目。
@@ -38,7 +61,7 @@ P1 与 P2 可并行,但 P3 不能在领域与内容契约未稳定时复制临
- 空应用可在模拟器启动,导航到占位欢迎页。
- 没有生产服务密钥或真实内容。
-## 3. P1:领域核心
+## 4. P1:领域核心
目标:在纯 Kotlin 中完成并证明三枚铜币算法。
@@ -58,7 +81,7 @@ P1 与 P2 可并行,但 P3 不能在领域与内容契约未稳定时复制临
- 测试无随机、无网络、无系统时间依赖。
- `CastEngine` API 经评审后冻结为 `coin-v1`。
-## 4. P2:内容数据管线
+## 5. P2:内容数据管线
目标:建立可追踪、可校验、可发布的本地内容包。
@@ -77,7 +100,7 @@ P1 与 P2 可并行,但 P3 不能在领域与内容契约未稳定时复制临
- 缺失、重复、非法顺序和错误映射测试均能失败。
- 内容负责人确认可再分发。
-## 5. P3:核心用户流程与东方设计系统
+## 6. P3:核心用户流程与东方设计系统
目标:完成离线起念、六次录入和结果阅读。
@@ -98,7 +121,7 @@ P1 与 P2 可并行,但 P3 不能在领域与内容契约未稳定时复制临
- AI/网络代码尚未存在也不影响流程。
- [UX 与东方视觉](ux-design.md)检查表通过。
-## 6. P4:本地解释与历史
+## 7. P4:本地解释与历史
目标:形成不依赖 AI 的完整 MVP。
@@ -117,7 +140,7 @@ P1 与 P2 可并行,但 P3 不能在领域与内容契约未稳定时复制临
- 删除行为与备份策略一致且经过验证。
- 此阶段已经是可发布的本地版候选。
-## 7. P5:AI 解读
+## 8. P5:AI 解读
目标:在不扩大起卦权限的前提下增加可控 AI 解释。
@@ -142,7 +165,7 @@ P1 与 P2 可并行,但 P3 不能在领域与内容契约未稳定时复制临
- 抓包显示只有用户明确动作触发请求,载荷与同意说明一致。
- 服务端故障不会改变或隐藏 `CastResult`。
-## 8. P6:质量、发布与运营
+## 9. P6:质量、发布与运营
贯穿所有阶段:
@@ -155,7 +178,7 @@ P1 与 P2 可并行,但 P3 不能在领域与内容契约未稳定时复制临
发布条件完全遵循[质量门禁](quality-gates.md#6-发布门禁)。
-## 9. 编码任务模板
+## 10. 编码任务模板
后续任务应使用以下结构,减少代理猜测:
@@ -185,7 +208,7 @@ P1 与 P2 可并行,但 P3 不能在领域与内容契约未稳定时复制临
任务应尽量小到单次变更可完整验证,不以“大致完成页面”作为验收描述。
-## 10. MVP 切分建议
+## 11. MVP 切分建议
最稳妥的发布顺序:
diff --git a/docs/prototype.md b/docs/prototype.md
new file mode 100644
index 0000000..6f3635c
--- /dev/null
+++ b/docs/prototype.md
@@ -0,0 +1,190 @@
+# 移动端交互原型
+
+> 状态:高保真候选 v0.1,待用户视觉与流程确认
+> 基准视口:360 × 792 CSS px(对应当前目标手机的紧凑竖屏体验)
+> 实现位置:[`prototype/`](../prototype/)
+
+## 1. 原型目的
+
+在初始化 Android 工程前,先用可点击原型确认最容易返工的产品决策:信息层级、六次录入方式、结果阅读顺序、AI 同意门和东方文化表达。原型确认后,P3 的 Compose 页面应复用这里的设计令牌和状态契约,而不是重新发明流程。
+
+本轮使用“一问”作为视觉工作名。它不构成正式命名决定;正式名称、应用图标和商店素材仍是 [TBD-001](decisions.md#未决问题)。
+
+## 2. 交付范围
+
+原型已经覆盖:
+
+- 欢迎与方法说明;
+- 开放式问题输入,以及明确的“不写具体内容”入口;
+- 三枚硬币逐枚录入、字/背计值、六轮 bottom-up 进度和撤销上一爻;
+- 本地确定性计算本卦、之卦和全部动爻;
+- 有动爻与无动爻两种结果布局;
+- 经典原文、本地白话和动爻的来源标签;
+- “解”的本地/AI 两种分支;
+- AI 发送范围说明、逐次同意、加载、成功、失败和本地降级;
+- 减少动态效果、键盘焦点、语义标签和最小触控目标。
+
+本轮不包含:
+
+- Android/Compose 生产代码;
+- 深色主题、平板和横屏定稿;
+- 历史记录与设置页面;
+- 真实 AI 请求、账户、服务端或持久化;
+- 完整 64 卦授权内容包;
+- 正式名称、图标、字体授权和商店视觉。
+
+## 3. 运行方式
+
+在仓库根目录运行:
+
+```powershell
+python -m http.server 4173 --bind 127.0.0.1
+```
+
+然后访问:
+
+```text
+http://127.0.0.1:4173/prototype/
+```
+
+原型无构建步骤、无第三方前端依赖,也不会发起外部网络请求。浏览器刷新会清空本次会话,这符合“评审原型”而非生产存储的定位。
+
+服务保持运行时,重新生成截图并执行浏览器门禁:
+
+```powershell
+node prototype\capture.mjs
+```
+
+脚本使用本机 Chrome/Edge 的 DevTools 协议,不安装 npm 依赖。可通过任务专用环境变量 `BRAINWAVE_PROTOTYPE_URL` 指向不同的本地服务地址。
+
+## 4. 评审入口
+
+正常入口可以完整点击。为稳定复现截图,还提供只用于评审的查询参数:
+
+| 状态 | URL | 评审重点 |
+|---|---|---|
+| 欢迎 | `?view=welcome` | 第一印象、文化气质、产品边界 |
+| 起念 | `?view=question` | 输入层级、隐私提示、跳过入口 |
+| 第三爻录入 | `?view=casting` | 手动录入、计值可见性、进度 |
+| 有动爻结果 | `?view=result` | 复 24 → 坤 2、初爻动、内容顺序 |
+| 无动爻结果 | `?view=static` | 乾 1、不出现之卦 |
+| AI 同意 | `?view=consent` | 发送范围与明确同意 |
+| AI 解读 | `?view=explanation` | 来源标签、非裁决语言、行动收束 |
+| 本地解读 | `?view=local` | 离线分支与来源区分 |
+| AI 失败 | `?view=error` | 结果保留、重试和本地降级 |
+
+这些参数只设置初始预览状态,不属于 Android 正式版路由。
+
+## 5. 交互与领域契约
+
+### 5.1 起卦
+
+- 用户必须亲自投币并逐枚录入;页面没有随机、摇一摇或 AI 代投入口。
+- 字记 2,背记 3;每轮三枚硬币全部有值后才能确认。
+- 六轮按初爻到上爻记录,界面以“从下往上”明确提示。
+- 6 为老阴、7 为少阳、8 为少阴、9 为老阳,6/9 标为动爻。
+- 原型包含完整 64 卦 King Wen 序号映射,用于验证交互结果;生产实现仍必须按[领域规则](domain-rules.md)在纯 Kotlin 中重建并穷举测试,不能复制前端代码当作权威领域实现。
+- 已确认的爻可逐步撤销;进入结果后本次结果不可被 AI 改写。
+
+### 5.2 结果阅读
+
+信息优先级固定为:
+
+1. 本卦、之卦和动爻;
+2. 用户本次所问;
+3. 经典原文;
+4. 本地白话;
+5. 动爻文本与提示;
+6. 用户主动点击“解”后的扩展解释。
+
+无动爻时只显示本卦和“卦象稳定”提示,不虚构之卦。多动爻时必须透明保留全部变化位置。
+
+### 5.3 AI 调用门
+
+原型中的 AI 加载是本地模拟,不会调用模型。正式流程必须维持以下顺序:
+
+```text
+本地结果已展示
+ → 用户点击「解」
+ → 用户选择 AI
+ → 页面说明确切发送字段
+ → 用户勾选并确认
+ → 才允许发起一次请求
+```
+
+失败状态不得隐藏或重算卦象,必须同时提供本地解释和重试入口。
+
+## 6. 视觉系统
+
+视觉遵循 [ADR-006](decisions.md#adr-006东方文化采用内容优先的纸墨朱砂设计):
+
+| 令牌 | 值 | 用途 |
+|---|---|---|
+| `paper` | `#F7F2E8` | 页面底色 |
+| `surface` | `#FFFDF7` | 阅读卡片与弹层 |
+| `ink` | `#1F1B16` | 标题、卦象和主要正文 |
+| `ink-soft` | `#655E55` | 次级正文 |
+| `cinnabar` | `#8C2F2B` | 主操作、动爻和关键提示 |
+| `ochre` | `#765D3E` | 辅助标签和次级动作 |
+| `outline` | `#B9AEA0` | 分隔和控件边界 |
+
+标题和文化文本优先使用系统可用的中文宋体/衬线字体,控件与说明使用中文黑体/无衬线字体。正式 Android 版本必须决定可再分发字体或采用平台字体回退,不能把本机字体直接打包。
+
+文化感来自排版、阅读节奏、卦象结构和克制的朱砂强调,不使用龙凤祥云、伪古印章、金色发光、正文毛笔字或抽卡式动效。
+
+## 7. 原型截图
+
+关键状态截图位于 [`prototype/screenshots/`](../prototype/screenshots/),命名按用户旅程排序:
+
+1. `01-welcome.png`
+2. `02-question.png`
+3. `03-casting.png`
+4. `04-result-moving.png`
+5. `05-result-static.png`
+6. `06-ai-consent.png`
+7. `07-ai-explanation.png`
+8. `08-ai-error.png`
+
+截图是 360 CSS px 宽度的评审证据,不是应用商店素材。
+
+## 8. 验收清单
+
+进入 Android P0/P3 前,需要用户明确反馈或确认:
+
+- [ ] “纸、墨、朱砂、留白”的整体气质是否合适;
+- [ ] 工作名只作占位,不把“一问”直接视为正式名称;
+- [ ] 三枚硬币用圆形切换控件录入是否容易理解;
+- [ ] 结果页先卦象、后原文/白话、再点击“解”的层级是否合适;
+- [ ] 本地解读与 AI 解读的选择、同意和来源标签是否足够清楚;
+- [ ] “可以试的一小步”的非预测、非裁决语气是否符合定位;
+- [ ] 是否需要在下一轮原型加入历史记录、深色主题或更大字号状态。
+
+未确认项应继续停留在原型层修改;不应提前固化为 Compose 生产页面。
+
+## 9. 向 Android 实现的映射
+
+| 原型概念 | Compose 目标 |
+|---|---|
+| CSS 颜色/间距/圆角令牌 | `MaterialTheme` 扩展与 design token |
+| `welcome/question/casting/result` | Navigation destination + 状态驱动页面 |
+| 三枚硬币控件 | 具备明确 semantics 的 48dp+ Compose 控件 |
+| 六爻图 | Canvas 绘制 + 完整读屏描述 |
+| 底部选择/同意层 | Material 3 modal bottom sheet/dialog |
+| 本地/AI/错误来源标签 | sealed UI state,不以颜色作为唯一提示 |
+| `prefers-reduced-motion` | 系统动画缩放/无障碍偏好适配 |
+
+Compose 实现必须从 `CastResult` 渲染,不能在 UI 层复制 King Wen 映射或根据文本猜测卦象。
+
+## 10. 本轮验证记录
+
+- `node --check prototype/app.js`:通过。
+- `node --check prototype/capture.mjs`:通过。
+- `node prototype/capture.mjs`:通过;八个状态完成 360px 截图与浏览器审计。
+- `git diff --check`:通过。
+- Chromium 360 × 792:欢迎、起念、录入、有/无动爻结果、AI 同意、成功与失败状态已渲染。
+- 已知夹具 `9,8,8,8,8,8`:显示复 24 → 坤 2,初爻动。
+- 静态夹具 `7,7,7,7,7,7`:显示乾 1,无动爻且无之卦。
+- 浏览器控制台错误:0;运行时异常:0;外部请求:0;模型调用:0。
+- 八个状态的未命名按钮、低于 48px 的按钮触控目标、过小复选框标签:均为 0。
+
+原型验证不能替代 P1 的 4,096 组合领域测试,也不能替代 Android 真机、TalkBack 与最大字体测试。
diff --git a/docs/quality-gates.md b/docs/quality-gates.md
index 44a72d0..0f54e75 100644
--- a/docs/quality-gates.md
+++ b/docs/quality-gates.md
@@ -41,6 +41,19 @@
## 3. 测试层次
+### 3.0 原型验证(P-1 阻塞视觉确认)
+
+- `node --check prototype/app.js` 无语法错误。
+- `node --check prototype/capture.mjs` 无语法错误。
+- `git diff --check` 无空白错误。
+- 本地服务运行时,`node prototype\capture.mjs` 退出码为 0。
+- Chromium 360 × 792 走通问题输入、六次录入、结果和解释选择。
+- `9,8,8,8,8,8` 显示复 24 → 坤 2、初爻动;全 7 显示乾 1、无动爻且无之卦。
+- 欢迎、起念、录入、有/无动爻结果、AI 同意、解释和失败状态均有可复现截图。
+- 浏览器控制台无应用错误;页面不发起外部请求;AI 状态明确标为本地模拟。
+
+原型通过只表示流程可供评审,不替代纯 Kotlin 领域测试、Compose UI 测试或真机无障碍检查。
+
### 3.1 纯 JVM 领域测试(最快,阻塞合并)
- 三枚铜币 8 种排列映射。
diff --git a/prototype/README.md b/prototype/README.md
new file mode 100644
index 0000000..7e857e4
--- /dev/null
+++ b/prototype/README.md
@@ -0,0 +1,26 @@
+# Brainwave 移动端原型
+
+这是一个无构建步骤、无外部依赖的高保真 HTML/CSS/JavaScript 原型。它用于在 Android 开发前确认流程与视觉,不是生产实现。
+
+从仓库根目录启动:
+
+```powershell
+python -m http.server 4173 --bind 127.0.0.1
+```
+
+打开 `http://127.0.0.1:4173/prototype/`。完整状态入口、领域边界、评审清单和 Android 映射见 [`docs/prototype.md`](../docs/prototype.md)。
+
+服务保持运行时,可用当前系统的 Chrome/Edge 重新生成八张截图并执行浏览器审计:
+
+```powershell
+node prototype\capture.mjs
+```
+
+脚本使用 Node 22 自带的 WebSocket 直接连接 Chrome DevTools,不安装 npm 包;它会核对两个已知卦象、AI 同意门、控制台/外部请求、按钮名称和 48px 最小触控目标。任一门禁失败时退出码为非零。
+
+约束:
+
+- 不发起外部请求,不调用 AI;
+- 不生成随机卦,用户逐枚录入硬币;
+- 查询参数只用于稳定预览和截图;
+- 工作名“一问”与当前文字内容均为评审候选,不代表正式命名或内容授权完成。
diff --git a/prototype/app.js b/prototype/app.js
new file mode 100644
index 0000000..c32813d
--- /dev/null
+++ b/prototype/app.js
@@ -0,0 +1,733 @@
+(() => {
+ "use strict";
+
+ const SAMPLE_QUESTION = "我是否要接受这次工作调整?怎样做能更稳妥?";
+ const SAMPLE_LINES = [9, 8, 8, 8, 8, 8];
+
+ const HEXAGRAM_NAMES = [
+ "", "乾", "坤", "屯", "蒙", "需", "讼", "师", "比", "小畜", "履", "泰", "否",
+ "同人", "大有", "谦", "豫", "随", "蛊", "临", "观", "噬嗑", "贲", "剥", "复",
+ "无妄", "大畜", "颐", "大过", "坎", "离", "咸", "恒", "遁", "大壮", "晋",
+ "明夷", "家人", "睽", "蹇", "解", "损", "益", "夬", "姤", "萃", "升", "困",
+ "井", "革", "鼎", "震", "艮", "渐", "归妹", "丰", "旅", "巽", "兑", "涣",
+ "节", "中孚", "小过", "既济", "未济"
+ ];
+
+ // 行为下卦、列为上卦;三位编码均按自下而上排列,1 为阳、0 为阴。
+ const KING_WEN = {
+ "111": { "111": 1, "100": 34, "010": 5, "001": 26, "000": 11, "011": 9, "101": 14, "110": 43 },
+ "100": { "111": 25, "100": 51, "010": 3, "001": 27, "000": 24, "011": 42, "101": 21, "110": 17 },
+ "010": { "111": 6, "100": 40, "010": 29, "001": 4, "000": 7, "011": 59, "101": 64, "110": 47 },
+ "001": { "111": 33, "100": 62, "010": 39, "001": 52, "000": 15, "011": 53, "101": 56, "110": 31 },
+ "000": { "111": 12, "100": 16, "010": 8, "001": 23, "000": 2, "011": 20, "101": 35, "110": 45 },
+ "011": { "111": 44, "100": 32, "010": 48, "001": 18, "000": 46, "011": 57, "101": 50, "110": 28 },
+ "101": { "111": 13, "100": 55, "010": 63, "001": 22, "000": 36, "011": 37, "101": 30, "110": 49 },
+ "110": { "111": 10, "100": 54, "010": 60, "001": 41, "000": 19, "011": 61, "101": 38, "110": 58 }
+ };
+
+ const icons = {
+ lock: '',
+ arrow: '',
+ chevron: '',
+ shield: ''
+ };
+
+ const screen = document.querySelector("#screen");
+ const modalRoot = document.querySelector("#modal-root");
+ const toastRegion = document.querySelector("#toast-region");
+ const backButton = document.querySelector("[data-action='back']");
+
+ const state = {
+ screen: "welcome",
+ question: "",
+ lines: [],
+ coins: [null, null, null],
+ modal: null,
+ consent: false,
+ explanationSource: "ai",
+ aiError: false
+ };
+
+ function escapeHtml(value) {
+ return String(value)
+ .replaceAll("&", "&")
+ .replaceAll("<", "<")
+ .replaceAll(">", ">")
+ .replaceAll('"', """)
+ .replaceAll("'", "'");
+ }
+
+ function lineIsYang(value) {
+ return value === 7 || value === 9;
+ }
+
+ function lineIsMoving(value) {
+ return value === 6 || value === 9;
+ }
+
+ function transformLines(lines) {
+ return lines.map((value) => {
+ if (value === 6) return 7;
+ if (value === 9) return 8;
+ return value;
+ });
+ }
+
+ function lookupHexagram(lines) {
+ const bits = lines.map((value) => lineIsYang(value) ? "1" : "0");
+ const lower = bits.slice(0, 3).join("");
+ const upper = bits.slice(3, 6).join("");
+ const number = KING_WEN[lower][upper];
+ return { number, name: HEXAGRAM_NAMES[number] };
+ }
+
+ function lineName(value, index) {
+ const position = ["初", "二", "三", "四", "五", "上"][index];
+ const polarity = lineIsYang(value) ? "九" : "六";
+ if (index === 0) return `${position}${polarity}`;
+ if (index === 5) return `${position}${polarity}`;
+ return `${polarity}${position}`;
+ }
+
+ function resultData() {
+ const sourceLines = state.lines.length === 6 ? state.lines : SAMPLE_LINES;
+ const changedLines = transformLines(sourceLines);
+ return {
+ sourceLines,
+ changedLines,
+ primary: lookupHexagram(sourceLines),
+ changed: lookupHexagram(changedLines),
+ movingIndexes: sourceLines
+ .map((value, index) => lineIsMoving(value) ? index : -1)
+ .filter((index) => index >= 0)
+ };
+ }
+
+ function stepper(current, total) {
+ const progress = Math.round((current / total) * 100);
+ return `
+
+
+
慢一点,看清当下
+
把一个犹豫,安静地放在这里。
+
用你手中的三枚硬币,依次记录六次结果。这里提供文化文本与反思线索,不替你预测,也不替你决定。
+
+
+ 观
+
+
+
+
+
+
+
+
工作名“一问”仅用于本轮视觉评审
+
+ `;
+ }
+
+ function questionView() {
+ return `
+
+ ${stepper(1, 2)}
+ 起念
+ 你想看清什么?
+ 写成一个开放的问题,比只问“会不会”更容易照见可行动的部分。
+
+
+
+
+ 只保存在本次原型会话中
+ ${state.question.length}/120
+
+
+
+ ${icons.lock}
+
起卦计算在本地完成。只有你主动选择 AI 解读并再次确认后,所需内容才会被发送。
+
+
+
+
+
+
+
+ `;
+ }
+
+ function lineStack(lines) {
+ return [5, 4, 3, 2, 1, 0].map((index) => {
+ const value = lines[index];
+ if (!value) {
+ return '
+ ${stepper(2, 2)}
+ ${escapeHtml(state.question)}
+
+
+
+
+
${state.lines.length} / 6
+
从下往上记录
+ ${state.lines.length ? '
' : ""}
+
+
${lineStack(state.lines)}
+
+
+
+
第 ${round} 爻
+
依照手中硬币,逐枚点选“字”或“背”
+
+
+
+ ${state.coins.map(coinButton).join("")}
+
+
+
+ ${sum ? `本次合计 ${sum} · ${currentType}` : "点按硬币可在“字 2”与“背 3”间切换"}
+
+
+
+
+
+
+ `;
+ }
+
+ function hexLine(value, index, showMoving) {
+ const kind = lineIsYang(value) ? "yang" : "yin";
+ const moving = showMoving && lineIsMoving(value) ? " hex-line--moving" : "";
+ return `
+
+
+ 起卦结果 · 本地计算
+ ${hasMoving ? `${data.movingIndexes.length} 个动爻` : "无动爻"}
+
+
+
+ ${hexagramFigure(data.sourceLines, data.primary, true)}
+ ${hasMoving ? `
之
${hexagramFigure(data.changedLines, data.changed, false)}` : ""}
+
+ ${hasMoving ? "" : '
卦象稳定 · 不显示之卦
'}
+
+
+
+
+
你所问
+ 本次记录
+
+
${escapeHtml(question)}
+
+
+ ${state.aiError ? errorCard() : ""}
+
+
+
+
经典原文
+ 经典文本
+
+
${copy.classic}
+
+
+
+
+ ${hasMoving ? `
+
+
动爻
变化处
+
+
${data.movingIndexes.map((index) => lineName(data.sourceLines[index], index)).join(" · ")}
+
${copy.moving}
+
${copy.movingPlain}
+
+
` : ""}
+
+
+
+
+
+
+
+ `;
+ }
+
+ function loadingView() {
+ return `
+
+
+
+
正在整理反思线索
+
结果已经生成。AI 只在此处协助组织文字,不参与起卦。
+
+ `;
+ }
+
+ function explanationView() {
+ const data = resultData();
+ const ai = state.explanationSource === "ai";
+ return `
+
+
+
+ 眼前更重要的,也许不是立刻选定一条路,而是辨认哪一步能让你重新获得稳定与判断力。
+
+
+ 可以先观察
+
+ - 你对“调整”的担心里,哪些是已经发生的事实,哪些是尚未验证的推测?
+ - 复卦的动处在最初一爻,适合把注意力放在能尽早修正的小偏差。
+ - 之卦为坤,提示行动时给现实条件、协作关系与节奏留出空间。
+
+
+
+
+ 问问自己
+
+ - 如果暂时不追求一次做对,我最想先确认的一个事实是什么?
+ - 哪一种选择更容易保留回转余地,而不是把自己锁死?
+ - 我可以向谁说明顾虑,并获得一条具体信息?
+
+
+
+
+
+
可以试的一小步
+
在今天结束前,写下“已知事实、尚待确认、我的底线”各一条,再约一次不超过 20 分钟的信息沟通。
+
+
+
+ 这是一种文化反思与文字整理,不是预测、诊断或替代你的判断。涉及医疗、法律、财务或人身安全时,请寻求相应专业支持。
+
+
+
+
+ `;
+ }
+
+ function interpretationModal() {
+ return `
+