首次提交:脑晴测 H5 项目
包含前端源码、预构建产物(dist/)、零依赖 Node.js 服务器(server.mjs)、 Cloudflare Worker 版本(worker/)、部署文档(DEPLOY.md)和交接文档。 技术总监可直接 clone 后运行 node server.mjs 启动,无需安装 React 或 build。 DEEPSEEK_API_KEY 通过环境变量注入,未纳入仓库。
2
brain-h5/.env.example
Normal file
@@ -0,0 +1,2 @@
|
||||
# Server-side only. Never rename this to VITE_DEEPSEEK_API_KEY.
|
||||
DEEPSEEK_API_KEY=replace_with_your_rotated_key
|
||||
6
brain-h5/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
node_modules/
|
||||
dist/
|
||||
test-results/
|
||||
playwright-report/
|
||||
.env
|
||||
.env.local
|
||||
2
brain-h5/.npmrc
Normal file
@@ -0,0 +1,2 @@
|
||||
fund=false
|
||||
audit=false
|
||||
4
brain-h5/.openai/hosting.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"d1": null,
|
||||
"r2": null
|
||||
}
|
||||
91
brain-h5/AGENTS.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# Mobile Prototype Agent Guide
|
||||
|
||||
## Prototype Instructions
|
||||
|
||||
### Durable product decisions
|
||||
|
||||
- Each assessment owns a distinct visual theme: ivory/cobalt for symbol matching, mint for trail making, sky blue for attention, and peach/orange for shopping memory. Do not apply one global green theme to all games.
|
||||
- On narrow screens, every active game must keep its primary prompt and controls inside one `100dvh` interaction surface without long page scrolling.
|
||||
- Assessment screens use an unframed, low-nesting composition. Generated raster assets must be post-processed to real transparency before use.
|
||||
- Every task starts with a hands-on practice interaction. Timed tasks stop at zero; completion-based tasks stop immediately when their required objective is reached.
|
||||
- The home screen is a compact app entry, not a marketing landing page. It includes age, gender, and education selection directly and starts the first task without a separate profile screen.
|
||||
- Do not use brain imagery in the brand or home experience. Use a calm abstract product mark.
|
||||
- Active game headers contain only a back control and the timer. Do not show task numbers, percentages, or progress bars.
|
||||
- Correct and incorrect feedback is icon-only: a green check or red cross.
|
||||
- Tutorial overlays must point toward the interaction target and ask the user to perform the action, instead of only describing the rules.
|
||||
- Exact-change practice teaches the controls with an easy example. Formal testing uses three progressively harder, separately timed rounds, does not reveal the target change amount, includes half-unit values, and allows at most two submissions per round.
|
||||
- Product name is temporarily “脑晴测”. It is a free, no-login Chinese H5 cognitive check that must avoid diagnosis or disease claims.
|
||||
- The default product is a responsive full-browser H5 page. Desktop must fill the browser viewport and must not show the black phone/device preview frame, status bar, camera cutout, or device picker. Narrow screens retain a portrait-friendly responsive layout without simulated device chrome.
|
||||
- The selected visual direction combines tactile 2.5D objects resting on a pearl-white surface with spectral cyan/violet/coral gradient indicators.
|
||||
- The core is four working assessments (symbol matching, alternating trail making, attention/direction, shopping working memory) followed by an AI-generated, tiered report.
|
||||
- Reference recordings and screenshots define task logic only; all UI copy, assets, motion, composition, and visual identity must be original.
|
||||
- DeepSeek credentials remain server-side only. Never expose the API key through a `VITE_` environment variable or client bundle.
|
||||
|
||||
In ChatGPT Work Mode, run `sites-preview start "$PWD"`, open `http://terminal.local:4173/` in the cloud browser, and verify the rendered app and its primary interactions. Keep that preview open and tell the user to inspect it in the cloud browser; do not present the local URL as a user-facing chat link. In Codex Desktop, run the local server yourself, open the preview in the in-app browser, and provide the clickable local URL. Do not deploy to Sites unless the user explicitly asks to share, publish, or deploy. Do not give the user server-start instructions when you can run it.
|
||||
|
||||
Before planning or implementing any mobile-app change, read this `AGENTS.md` in full. It is the source of truth for the template's runtime and component guidance.
|
||||
|
||||
Before making substantial visual changes, use the Product Design plugin's `get-context` skill when the visual source is unclear or no longer matches the current goal. When the user gives durable prototype-specific design feedback, preferences, or decisions, record them in `AGENTS.md`.
|
||||
|
||||
When implementing from a selected generated mock, treat that image as the source of truth for layout, component anatomy, density, spacing, color, typography, visible content, and hierarchy.
|
||||
|
||||
## Editing Boundary
|
||||
|
||||
- Build app-specific UI in `src/Prototype.tsx` and `src/prototype.css`.
|
||||
- Treat `src/App.tsx`, `src/main.tsx`, `src/styles.css`, `src/mobile/`, `public/assets/iphone/`, `public/assets/android/`, `public/assets/status/`, `vite.config.ts`, `worker/index.js`, and `scripts/prepare-sites-build.mjs` as protected runtime files. Do not edit, replace, remove, or recreate them unless the user explicitly asks to change the mobile runtime itself. For an explicit runtime change, update the affected lock hashes only after verifying the new runtime behavior.
|
||||
- Run `npm run check:runtime` before preview or handoff. If it fails, restore the protected runtime instead of weakening or bypassing the check.
|
||||
- `npm run build` preserves the mobile runtime and prepares the static Cloudflare Worker output required by Sites. Before a Sites handoff, confirm `dist/client/index.html`, `dist/server/index.js`, `dist/.openai/hosting.json`, and source `.openai/hosting.json` exist, then run `npm run test:sites`. Do not replace this project with a Vinext starter.
|
||||
|
||||
## Runtime Contract
|
||||
|
||||
- Project-specific override (confirmed by the user on 2026-07-23): the production/default route must render the app directly as a full-browser responsive web experience. The older `PhoneFrame` preservation rules below describe the original development template only and do not apply to the default product route. A device preview may exist only as an explicitly separate development tool and must never wrap the user-facing H5.
|
||||
- Preserve the mobile device runtime unless the user's task explicitly asks otherwise. Do not replace it with a standalone page. Visual fidelity applies to app-owned content inside the device screen, not to template-owned device chrome.
|
||||
- Keep `App` composed around `PhoneFrame` -> `KeyboardProvider`, with `StatusBar`, app content, `HomeIndicator`, and `KeyboardDock` mounted inside the phone frame. `StatusBar` and the iOS home indicator are overlaid device chrome. When the Android keyboard is closed, the app viewport reserves the protected navigation-bar region instead of painting behind it. When the Android keyboard is open, preserve the current full-screen keyboard layout: its asset includes the IME navigation strip and the separate black navigation bar is hidden. iOS screens continue to paint behind the home-indicator area and own their safe-area content padding.
|
||||
- Preserve the `iPhone` / `Pixel 10` device picker and both calibrated device presets. The Pixel screen is `427 x 952`; its `32 x 32` camera circle and `public/assets/android/navigation-bar.svg` bottom navigation bar are protected device chrome, not app content.
|
||||
- Preserve the device picker's intentionally lightweight Codex styling in the top-right corner: its trigger wrapper is borderless and transparent, its trigger sizes to content, and its right-aligned menu uses the compact 3px inset plus the specified hairline and elevation shadow layers. Keep the prototype root and default app screen white.
|
||||
- Preserve `StatusBar` as live device chrome, including its platform-specific typography, source status-icon assets, and spacing. Pixel 10 uses Roboto, Android indicators, and 32px top, left, and right padding. iPhone uses its iOS indicators, system typography, and calibrated spacing. Do not hardcode screenshot times like `9:41` into the status bar, replace its real-time clock, or move status bar content into app markup unless the user explicitly asks for a fixed/mock device time.
|
||||
- `PhoneFrame` owns the calibrated device frame, screen portal, device picker, camera cutout, and custom cursor. Keep device assets in `public/assets/iphone/` and `public/assets/android/`; if an asset fails to load, repair the asset path or restore the asset instead of removing the frame, keyboard, or image render.
|
||||
- Use `MobileScroll` directly for simple single-screen prototypes. Use `FlowStack` for conventional multi-screen flows whose routes can own their fixed header and footer; when using it, define each route as a `FlowScreen`: `{ id, header?, headerHeight?, footer?, footerHeight?, render }`, and use `flow.push(screen)`, `flow.pop()`, and `flow.replace(screen)` from `FlowStack` render callbacks or `useFlow()` instead of introducing another router.
|
||||
- Use `Carousel` for a carousel, horizontal rail, swipeable cards, image or media strip, horizontally scrollable cards, chip rail, or other horizontal collection.
|
||||
- For a layered app shell—such as a persistent composer, independently presented sheet, pushed/peek sidebar, or app-wide transition—compose directly in `Prototype.tsx` rather than forcing it through `FlowStack`. Keep app-owned fixed chrome as sibling layers outside `MobileScroll`.
|
||||
- When using `FlowScreen`, put route-owned fixed headers or footers in `FlowScreen.header` or `FlowScreen.footer`. Set `headerHeight` to the visible app-toolbar height; `FlowStack` adds the device's top safe-area/status-bar inset automatically. Do not include `StatusBar` or its height in the header. Set `footerHeight` to the full app-footer height. `FlowScreen.footer` is an overlay, not reserved layout space; screens using it must add their own bottom content padding such as `padding-bottom: calc(var(--flow-footer-height) + var(--mobile-safe-area-height) + 24px)` so final content can scroll above the footer while still painting behind it.
|
||||
- Render only scrollable content inside `MobileScroll`; it is for content that should move with scroll and rubber-band overscroll. Keep app-owned headers, nav bars, tabs, composers, and overlays outside it. This keeps scroll physics, safe areas, keyboard insets, scrollbars, and drag click suppression active without letting content paint under fixed chrome.
|
||||
- Buttons, links, cards, and images inside `MobileScroll` should still allow drag scrolling when the pointer moves beyond tap slop. Use `data-scroll-drag="ignore"` only for rare controls that must own the drag gesture themselves.
|
||||
- Do not add `var(--keyboard-height)` to ordinary screen/content padding inside `MobileScroll`; the scroll viewport already shrinks above the simulated keyboard. For custom fixed composers, search bars, or toast chrome, use `useKeyboardInsets().bottomInset`. It is relative to the app viewport: Android returns `0` while the closed-keyboard viewport already reserves navigation, then returns the keyboard height while open; iOS continues to clear the home indicator while closed and ride directly above the keyboard while open. Do not pin custom bottom chrome to `bottom: 0` or only `keyboardHeight`.
|
||||
- Use `KeyboardInput`, `KeyboardTextarea`, or `MobileTextField` for every text-entry control. A raw `input` or `textarea` disconnects focus, keyboard animation, safe-area insets, and attached surfaces.
|
||||
- Use `BottomSheet` for phone-scoped sheets. Its props are `open`, `onOpenChange`, `title`, optional `description`, optional `snap`, and `children`; it renders through the phone screen portal and dismisses the keyboard before opening.
|
||||
|
||||
## Horizontal Carousels
|
||||
|
||||
- Use `Carousel` for horizontally draggable cards, images, media, chips, or other horizontal collections. Do not recreate these with `overflow-x`, custom pointer handlers, or a generic div.
|
||||
- `Carousel` can be nested directly inside `MobileScroll`. It owns horizontal gestures and automatically yields vertical gestures to the parent.
|
||||
- Never put `data-scroll-drag="ignore"` on or around a `Carousel`; doing so prevents vertical parent scrolling when a gesture begins inside it.
|
||||
- Do not add CSS scroll snapping to `Carousel`; its runtime owns momentum and release motion.
|
||||
- Use `data-scroll-drag="ignore"` only when a control must prevent parent scrolling in every drag direction.
|
||||
|
||||
See `src/mobile/COMPONENTS.md` for the full component and gesture contract.
|
||||
|
||||
## Keyboard Rule
|
||||
|
||||
The simulated keyboard is a separate top-layer component. Before presenting anything that behaves like iOS navigation or modal UI, dismiss it first.
|
||||
|
||||
Call `keyboard.hide()` before:
|
||||
|
||||
- pushing, popping, or replacing FlowStack routes
|
||||
- opening bottom sheets, action sheets, dialogs, menus, or navigation sheets
|
||||
- starting transitions where the destination should not inherit text-input focus
|
||||
|
||||
`FlowStack` already hides the keyboard for `push`, `pop`, and `replace`. `BottomSheet` already hides it before opening. If you add new modal/sheet/navigation primitives, follow the same rule.
|
||||
|
||||
When a composer, search surface, or other keyboard-attached component closes, call `keyboard.hide()` in the same event before changing that component's open state. Position attached surfaces from `useKeyboardInsets()` rather than a separate timer or visibility flag so both dismiss together.
|
||||
|
||||
When any text-entry control loses focus, dismiss the simulated keyboard. If the control is custom or does not use the runtime's keyboard-aware fields, handle its blur event and call `keyboard.hide()` explicitly. Keep the keyboard open only when focus is moving directly to another text-entry control that should share the same keyboard session.
|
||||
|
||||
## Interaction Rules
|
||||
|
||||
- Do not trigger buttons or inputs after a pointer has become a drag. Preserve the drag suppression behavior in `MobileScroll`.
|
||||
- Do not allow native browser image/file dragging inside the phone frame. Preserve the phone-level `dragstart` suppression and non-draggable image styles so scroll drags that begin on images still scroll the prototype.
|
||||
- Use `KeyboardInput`, `KeyboardTextarea`, or `MobileTextField` for text entry so the simulated keyboard and safe-area insets stay connected.
|
||||
- Fixed phone chrome should not animate with pushed screens. Screen content can animate; the status bar, camera cutout, and preview chrome should stay put.
|
||||
- Keep the keyboard below the home indicator/safe area layer in z-index, and above ordinary app UI while visible.
|
||||
- Keep the home indicator as the topmost safe-area layer in the z-index above everything else in the prototype.
|
||||
42
brain-h5/GAME-AUDIT.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# 脑晴测游戏完整性复审
|
||||
|
||||
复审日期:2026-07-23
|
||||
复审范围:无登录入口、资料页、四项任务、AI 报告、手机竖屏交互。
|
||||
|
||||
## 复审结论
|
||||
|
||||
初版已具备完整闭环,但存在“能玩、尚未完全产品化”的问题。本轮已完成关键补强,当前版本适合作为免费 H5 内测版;评分仍属于产品分档,不属于医学常模。
|
||||
|
||||
## 已发现并修复
|
||||
|
||||
1. 教学只有文字:四项任务现均包含必须完成的互动练习,错误时给出纠错提示,练习不计分。
|
||||
2. 连线任务辨识度不足:改为绿色扇区面积排序,目标位置每次随机且避免重叠,并绘制真实连接轨迹。
|
||||
3. 注意力场景存在位置规律:青色目标位置和所有干扰方向改为逐题随机。
|
||||
4. 任务未完成可能出现虚高分:评分加入任务完成度 20% 权重,报告展示完成比例。
|
||||
5. 购物记忆缺少辅助行为记录:每轮允许查看一次清单并记录辅助使用。
|
||||
6. 找零一次错误即结束:每轮增加一次纠错机会,第二次后才推进。
|
||||
7. 切到后台继续计时:改为自动暂停,返回后由用户主动恢复。
|
||||
8. 报告只能查看处理速度:四个认知维度均可点击展开解释、完成度和建议。
|
||||
9. 基础资料文案容易让人误认为已有常模:明确说明当前尚未建立医学常模。
|
||||
10. 可访问性不足:增加中文方向标签、计时器语义、键盘焦点样式和减少动画适配。
|
||||
|
||||
## 当前仍需后续数据工作的部分
|
||||
|
||||
- 分数阈值需要真实用户样本校准。
|
||||
- 年龄、教育程度和设备差异目前只用于 AI 解释,不用于医学常模换算。
|
||||
- 上线前应完成至少 30–50 名目标用户的可用性测试,再调整题量、限时和字体尺寸。
|
||||
- 若未来用于科研或医疗辅助,需要伦理、数据合规和专业量表效度验证。
|
||||
|
||||
## 审核证据
|
||||
|
||||
- `audit-2026-07-23/01-start.png`
|
||||
- `audit-2026-07-23/04-symbol.png`
|
||||
- `audit-2026-07-23/05-trail.png`
|
||||
- `audit-2026-07-23/06-attention.png`
|
||||
- `audit-2026-07-23/07-memory.png`
|
||||
- `audit-2026-07-23/08-report.png`
|
||||
- `audit-2026-07-23/09-practice.png`
|
||||
- `audit-2026-07-23/10-trail-improved.png`
|
||||
- `audit-2026-07-23/11-report-improved.png`
|
||||
|
||||
final result: passed for H5 internal testing
|
||||
104
brain-h5/GAME-DESIGN.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# 脑晴测 H5 游戏设计规范(增强版 v1.1)
|
||||
|
||||
## 通用测试约束
|
||||
|
||||
- 竖屏触控优先;正式测试时不允许横屏布局改变刺激相对关系。
|
||||
- 每项任务必须先完成一道互动练习,教程和正式测试严格分离,练习行为不进入正式分数。
|
||||
- 每个有效操作记录:游戏、题号、期望答案、用户答案、正确性、反应时间和时间戳。
|
||||
- 切换后台时自动暂停计时并要求用户主动恢复;页面刷新、主动退出视为未完整完成。
|
||||
- 报告只解释本次任务表现,不输出疾病或诊断结论。
|
||||
|
||||
## 1. 符号速配(处理速度)
|
||||
|
||||
### 规则
|
||||
|
||||
- 10 个视觉符号与数字 0–9 建立随机一一映射。
|
||||
- 同一次正式测试中映射固定,重新评估时重新随机。
|
||||
- 屏幕中央每题显示一个目标符号,用户点击其映射数字。
|
||||
- 连续目标不得与上一题相同。
|
||||
- 30 题或 60 秒先到即结束。
|
||||
|
||||
### 数据
|
||||
|
||||
- 正确数、错误数、正确率。
|
||||
- 每题反应时间、平均反应时间。
|
||||
- 连续错误、超时、完成题数。
|
||||
|
||||
### 评分重点
|
||||
|
||||
- 正确率 55%,速度 20%,任务完成度 20%,基础分 5%,错误额外扣分。
|
||||
- 未完成 30 题时按完成比例扣分,避免“只做少量题但正确率高”获得虚高结果。
|
||||
|
||||
## 2. 交替连线(执行功能)
|
||||
|
||||
### 规则
|
||||
|
||||
- 画布中包含数字 1–10 和 10 个绿色面积依次增大的扇区圆。
|
||||
- 每次评估重新随机 20 个目标的位置,并使用预设安全点位避免重叠。
|
||||
- 正确顺序:1 → 最小绿色扇区 → 2 → 第二小绿色扇区,直到 10 → 最大绿色扇区。
|
||||
- 每次正确选择后绘制连接轨迹,帮助用户确认路径与当前进度。
|
||||
- 点错不推进当前步骤,并记录错误目标和错误类型。
|
||||
- 20 个目标或 180 秒先到即结束。
|
||||
|
||||
### 数据
|
||||
|
||||
- 已完成步骤、总用时、单步停顿时间。
|
||||
- 数字顺序错误、色块大小错误、规则切换错误。
|
||||
- 错误发生在任务前段还是后段。
|
||||
|
||||
## 3. 方向追踪(注意力)
|
||||
|
||||
### 规则
|
||||
|
||||
- 每题展示 7 架飞机,只有 1 架保持青色,其余为高区分度干扰颜色。
|
||||
- 飞机使用唯一长机头、钝尾、后方短翼和尾翼,避免方向歧义。
|
||||
- 方向仅使用上、右、下、左四个标准 90° 旋转。
|
||||
- 用户找到青色目标后点击机头方向。
|
||||
- 每题重新随机青色目标位置和干扰飞机方向,避免用户通过固定位置规律作答。
|
||||
- 30 题或 60 秒先到即结束;3 次错误后提前结束。
|
||||
|
||||
### 数据
|
||||
|
||||
- 正确率、平均反应时间、剩余容错次数。
|
||||
- 上下混淆、左右混淆和连续失误。
|
||||
- 前后半程反应速度变化。
|
||||
|
||||
## 4. 购物记忆(工作记忆)
|
||||
|
||||
### 第 1–4 轮:清单回忆
|
||||
|
||||
- 清单长度依次为 2、3、4、5 件。
|
||||
- 每轮从 8 件商品中随机抽取,用户确认记住后隐藏清单。
|
||||
- 用户从完整货架中多选刚才出现的商品。
|
||||
- 每轮允许查看一次原清单;查看行为会被记录为辅助使用并进入错误/辅助指标。
|
||||
- 记录命中、遗漏、误选、辅助查看和回忆时间。
|
||||
|
||||
### 独立任务:精确找零
|
||||
|
||||
- 正式测试不直接显示应找金额,用户需根据价格与付款金额自行计算。
|
||||
- 第 1 轮:价格 ¥13,付款 ¥20,目标找零 ¥7,限时 40 秒。
|
||||
- 第 2 轮:价格 ¥26.5,付款 ¥40,目标找零 ¥13.5,限时 45 秒。
|
||||
- 第 3 轮:价格 ¥41.5,付款 ¥60,目标找零 ¥18.5,限时 50 秒。
|
||||
- 可使用 ¥0.5、¥1、¥2、¥5、¥10、¥20 面额组合。
|
||||
- 每轮最多尝试 2 次;第一次错误后清空金额并提供一次重试机会。
|
||||
- 记录每次金额、金额误差、尝试次数和操作时间。
|
||||
|
||||
### 结束条件
|
||||
|
||||
- 购物记忆完成 3 轮后结束该任务;精确找零完成 3 轮后结束该任务。每轮以各自倒计时为准。
|
||||
|
||||
## 分数与报告
|
||||
|
||||
- 每个维度输出 20–98 分,避免原型阶段制造虚假的绝对满分精度。
|
||||
- 分数同时考虑正确率、反应速度、任务完成度和错误惩罚。
|
||||
- 80–98:状态不错;60–79:状态平稳;20–59:可以提升。
|
||||
- DeepSeek 根据结构化维度数据生成总结、优势、提升空间和三条建议。
|
||||
- DeepSeek 超时、配置缺失或返回格式错误时,自动使用本地规则报告。
|
||||
- 所有措辞限定为本次表现,不使用医疗诊断词汇。
|
||||
- 报告中的四个维度均可点击展开,并显示对应解释、完成度和建议。
|
||||
|
||||
## 后续校准
|
||||
|
||||
- 当前分值属于产品 MVP 分档,不是临床常模。
|
||||
- 后续应按年龄、教育程度、设备类型和真实样本分布建立常模。
|
||||
- 在常模完成前,报告必须保留“仅供自我了解与能力参考”的说明。
|
||||
31
brain-h5/README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# 脑晴测 H5
|
||||
|
||||
一个无需登录的中文数字认知评估 H5,包含处理速度、执行功能、注意力和工作记忆四项任务,以及 DeepSeek 辅助生成的非医疗化表现报告。
|
||||
|
||||
## 技术栈
|
||||
|
||||
- React 19 + Vite
|
||||
- TypeScript/JSX(业务代码不依赖复杂类型系统,后续可平移至 Taro React)
|
||||
- Phosphor Icons
|
||||
- Cloudflare Worker / Vite 本地中间件代理 DeepSeek
|
||||
|
||||
## 本地运行
|
||||
|
||||
1. 复制 `.env.example` 为 `.env`。
|
||||
2. 在 `.env` 中设置服务端 `DEEPSEEK_API_KEY`,不要添加 `VITE_` 前缀。
|
||||
3. 安装依赖并运行 `pnpm dev`。
|
||||
|
||||
## 验证命令
|
||||
|
||||
- `pnpm run check:runtime`
|
||||
- `pnpm run build`
|
||||
- `pnpm run test:sites`
|
||||
|
||||
## 隐私与安全
|
||||
|
||||
- API Key 不进入浏览器构建产物。
|
||||
- 测试结果默认仅保存在当前浏览器 `localStorage`,用于显示最近一次报告。
|
||||
- 调用 AI 时仅发送基础范围、四维指标和事件数量,不发送姓名、手机号等身份信息。
|
||||
- 上线部署时应将 `DEEPSEEK_API_KEY` 配置为服务端 Secret。
|
||||
|
||||
详细任务规则见 [GAME-DESIGN.md](./GAME-DESIGN.md)。
|
||||
58
brain-h5/design-qa.md
Normal file
@@ -0,0 +1,58 @@
|
||||
**Design QA**
|
||||
|
||||
- Source visual truth:
|
||||
- `C:\Users\明年\AppData\Local\Temp\codex-clipboard-b2896c59-bbb1-4cbb-9a79-a7e0830e0d46.png`
|
||||
- `C:\Users\明年\AppData\Local\Temp\codex-clipboard-84af1b55-83a2-49e7-b04f-1e84ea95049f.png`
|
||||
- `C:\Users\明年\AppData\Local\Temp\codex-clipboard-b0b0bd77-9865-4dc2-a5a5-d72b44f3dfba.png`
|
||||
- `C:\Users\明年\AppData\Local\Temp\codex-clipboard-14be5be9-42f8-4947-81e3-695cca505d36.png`
|
||||
- `C:\Users\明年\AppData\Local\Temp\codex-clipboard-19cddb0d-1628-4d21-a97d-6695a376ac74.png`
|
||||
- `C:\Users\明年\AppData\Local\Temp\codex-clipboard-0f791933-9ea7-4cb1-8fa1-b8b12745ee6f.png`
|
||||
- `C:\Users\明年\AppData\Local\Temp\codex-clipboard-5b000418-c746-465d-9d1-1cd4cdafaa70.png`
|
||||
- Implementation screenshot: `D:\GMS project\brain-h5\qa-home-mobile.png`
|
||||
- Viewport: 393 x 852 CSS pixels, device scale factor 1.
|
||||
- State: mobile home screen; assessment screens could not be captured.
|
||||
- Source density: mixed supplied PNG dimensions.
|
||||
- Implementation density: 393 x 852 pixels.
|
||||
|
||||
**Comparison History**
|
||||
|
||||
- Initial implementation used one green-led palette, vertically stacked desktop panels on mobile, opaque checkerboard assets, a 5-column number pad, linear direction controls, duplicate target-color planes, manual memory confirmation, and timers that did not stop the task.
|
||||
- Fixes applied:
|
||||
- Added distinct ivory/cobalt, mint, sky-blue, and peach/orange game themes.
|
||||
- Constrained active mobile game surfaces to `100dvh`.
|
||||
- Removed large nested game containers on the mobile breakpoint.
|
||||
- Converted supplied images to real transparent PNG assets.
|
||||
- Added 3 x 4 numeric keypad layout and floating target motion.
|
||||
- Made attention target color unique and direction controls spatial.
|
||||
- Made conveyor items move in one horizontal row and select immediately.
|
||||
- Added image-based expandable shopping list.
|
||||
- Replaced pre-game rule cards with in-context guided demos on the real game surfaces.
|
||||
- Added four-piece click-blocking shade, a live target cutout, and Chinese coach bubbles.
|
||||
- Required real clicks on the symbol keypad, trail nodes, direction key, shopping list, and conveyor item before formal timing starts.
|
||||
- Added objective and timer completion states.
|
||||
- Compacted mobile home, profile, and 2 x 2 overview screens.
|
||||
- Restored plane entry from all four screen edges while preventing delayed planes from appearing in the middle.
|
||||
- Added animated symbol relocation, 3.5-second conveyor spacing, larger shopping-list imagery, and ringed result icons.
|
||||
|
||||
**Fidelity Surfaces**
|
||||
|
||||
- Typography: Chinese copy and hierarchy compile correctly; game-screen visual comparison remains unavailable.
|
||||
- Spacing and layout: code constrains active games to one mobile viewport; browser-rendered confirmation remains unavailable.
|
||||
- Colors: four independent themes are implemented and no longer share one global green theme.
|
||||
- Image quality: all 20 supplied assets now contain real alpha; checkerboard pixels are not rendered as content.
|
||||
- Copy: Chinese in-context coaching, timeout messaging, and shopping-list labels are present.
|
||||
|
||||
**Verification**
|
||||
|
||||
- `npm run check:runtime`: passed.
|
||||
- `npm run build`: passed.
|
||||
- Local HTTP health: 200.
|
||||
- Server binding: `0.0.0.0:4173`.
|
||||
- Primary interactions tested in browser: home navigation only; the latest guided-demo states could not be captured.
|
||||
- Browser console: no application error was observed before local-address interaction was blocked.
|
||||
|
||||
**Blocker**
|
||||
|
||||
- The in-app browser security policy stopped further interaction with the local address after the home screen. Therefore matching assessment-screen screenshots and full interaction testing could not be completed in this run.
|
||||
|
||||
final result: blocked
|
||||
4
brain-h5/dist/.openai/hosting.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"d1": null,
|
||||
"r2": null
|
||||
}
|
||||
BIN
brain-h5/dist/client/assets/android/Keyboard.png
vendored
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
brain-h5/dist/client/assets/android/Pixel10.png
vendored
Normal file
|
After Width: | Height: | Size: 209 KiB |
6
brain-h5/dist/client/assets/android/navigation-bar.svg
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="428" height="48" viewBox="0 0 428 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="427" height="48" transform="translate(1)" fill="#0F0F0F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M119.492 16.0134C120.159 15.6203 121 16.1009 121 16.8748V31.1254C121 31.8993 120.159 32.3799 119.492 31.9867L107.41 24.8615C106.753 24.4746 106.753 23.5256 107.41 23.1387L119.492 16.0134Z" fill="white"/>
|
||||
<circle cx="223" cy="24" r="8" fill="white"/>
|
||||
<rect x="325" y="17" width="14" height="14" rx="2" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 542 B |
BIN
brain-h5/dist/client/assets/brain/airplane-source.png
vendored
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
brain-h5/dist/client/assets/brain/airplane.png
vendored
Normal file
|
After Width: | Height: | Size: 456 KiB |
BIN
brain-h5/dist/client/assets/brain/design-reference.png
vendored
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
brain-h5/dist/client/assets/brain/direction-object.png
vendored
Normal file
|
After Width: | Height: | Size: 712 KiB |
BIN
brain-h5/dist/client/assets/brain/direction-source.png
vendored
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
brain-h5/dist/client/assets/brain/memory/bread.png
vendored
Normal file
|
After Width: | Height: | Size: 967 KiB |
BIN
brain-h5/dist/client/assets/brain/memory/carrot.png
vendored
Normal file
|
After Width: | Height: | Size: 510 KiB |
BIN
brain-h5/dist/client/assets/brain/memory/cheese.png
vendored
Normal file
|
After Width: | Height: | Size: 481 KiB |
BIN
brain-h5/dist/client/assets/brain/memory/coffee.png
vendored
Normal file
|
After Width: | Height: | Size: 525 KiB |
BIN
brain-h5/dist/client/assets/brain/memory/cookie.png
vendored
Normal file
|
After Width: | Height: | Size: 771 KiB |
BIN
brain-h5/dist/client/assets/brain/memory/egg.png
vendored
Normal file
|
After Width: | Height: | Size: 526 KiB |
BIN
brain-h5/dist/client/assets/brain/memory/fish.png
vendored
Normal file
|
After Width: | Height: | Size: 589 KiB |
BIN
brain-h5/dist/client/assets/brain/memory/orange.png
vendored
Normal file
|
After Width: | Height: | Size: 564 KiB |
BIN
brain-h5/dist/client/assets/brain/planes/coral.png
vendored
Normal file
|
After Width: | Height: | Size: 527 KiB |
BIN
brain-h5/dist/client/assets/brain/planes/teal.png
vendored
Normal file
|
After Width: | Height: | Size: 510 KiB |
BIN
brain-h5/dist/client/assets/brain/symbols/camera.png
vendored
Normal file
|
After Width: | Height: | Size: 697 KiB |
BIN
brain-h5/dist/client/assets/brain/symbols/coffee.png
vendored
Normal file
|
After Width: | Height: | Size: 541 KiB |
BIN
brain-h5/dist/client/assets/brain/symbols/gear.png
vendored
Normal file
|
After Width: | Height: | Size: 611 KiB |
BIN
brain-h5/dist/client/assets/brain/symbols/lightning.png
vendored
Normal file
|
After Width: | Height: | Size: 541 KiB |
BIN
brain-h5/dist/client/assets/brain/symbols/lock.png
vendored
Normal file
|
After Width: | Height: | Size: 622 KiB |
BIN
brain-h5/dist/client/assets/brain/symbols/moon.png
vendored
Normal file
|
After Width: | Height: | Size: 570 KiB |
BIN
brain-h5/dist/client/assets/brain/symbols/puzzle.png
vendored
Normal file
|
After Width: | Height: | Size: 684 KiB |
BIN
brain-h5/dist/client/assets/brain/symbols/squares.png
vendored
Normal file
|
After Width: | Height: | Size: 683 KiB |
BIN
brain-h5/dist/client/assets/brain/symbols/star.png
vendored
Normal file
|
After Width: | Height: | Size: 583 KiB |
BIN
brain-h5/dist/client/assets/brain/symbols/sun.png
vendored
Normal file
|
After Width: | Height: | Size: 584 KiB |
9
brain-h5/dist/client/assets/index-8xOYLbW9.js
vendored
Normal file
1
brain-h5/dist/client/assets/index-ouTen0sP.css
vendored
Normal file
BIN
brain-h5/dist/client/assets/iphone/Bezel.png
vendored
Normal file
|
After Width: | Height: | Size: 346 KiB |
BIN
brain-h5/dist/client/assets/iphone/Keyboard.png
vendored
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
brain-h5/dist/client/assets/money/money-05.png
vendored
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
brain-h5/dist/client/assets/money/money-1.png
vendored
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
brain-h5/dist/client/assets/money/money-10.png
vendored
Normal file
|
After Width: | Height: | Size: 216 KiB |
BIN
brain-h5/dist/client/assets/money/money-2.png
vendored
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
brain-h5/dist/client/assets/money/money-20.png
vendored
Normal file
|
After Width: | Height: | Size: 222 KiB |
BIN
brain-h5/dist/client/assets/money/money-5.png
vendored
Normal file
|
After Width: | Height: | Size: 215 KiB |
BIN
brain-h5/dist/client/assets/roboto-latin-500-normal-7RbcRiD8.woff2
vendored
Normal file
BIN
brain-h5/dist/client/assets/roboto-latin-500-normal-DQZyH_nt.woff
vendored
Normal file
7
brain-h5/dist/client/assets/status/ios-status-icons.svg
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg width="79" height="13" viewBox="0 0 79 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect opacity="0.35" x="51.4333" y="0.5" width="24" height="12" rx="3" stroke="black"/>
|
||||
<path opacity="0.4" d="M76.9333 4.66667V8.66667C77.7381 8.32789 78.2614 7.5398 78.2614 6.66667C78.2614 5.79353 77.7381 5.00544 76.9333 4.66667Z" fill="black"/>
|
||||
<rect x="52.9333" y="2" width="21" height="9" rx="1.33333" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M35.1006 3.06735C37.5671 3.06745 39.9394 3.96498 41.727 5.57443C41.8616 5.69869 42.0767 5.69712 42.2093 5.57092L43.4961 4.3411C43.5632 4.27709 43.6006 4.19038 43.6001 4.10017C43.5995 4.00996 43.561 3.92367 43.4931 3.86041C38.8012 -0.397806 31.3992 -0.397806 26.7073 3.86041C26.6393 3.92363 26.6007 4.00988 26.6001 4.1001C26.5995 4.19031 26.6368 4.27704 26.7039 4.3411L27.9911 5.57092C28.1236 5.69731 28.3389 5.69888 28.4734 5.57443C30.2613 3.96488 32.6338 3.06735 35.1006 3.06735ZM35.0988 7.17524C36.4449 7.17516 37.743 7.67327 38.7408 8.57279C38.8757 8.70045 39.0883 8.69768 39.2199 8.56655L40.4965 7.28238C40.5637 7.21502 40.601 7.12364 40.6001 7.02869C40.5991 6.93373 40.5599 6.84313 40.4914 6.77715C37.4528 3.96328 32.7473 3.96328 29.7088 6.77715C29.6402 6.84313 29.601 6.93378 29.6001 7.02876C29.5992 7.12375 29.6366 7.21512 29.704 7.28238L30.9803 8.56655C31.1119 8.69768 31.3244 8.70045 31.4594 8.57279C32.4565 7.67386 33.7536 7.1758 35.0988 7.17524ZM37.6 9.8944C37.6019 9.99694 37.5652 10.0958 37.4984 10.1676L35.3398 12.557C35.2765 12.6272 35.1902 12.6667 35.1002 12.6667C35.0102 12.6667 34.9239 12.6272 34.8606 12.557L32.7016 10.1676C32.6349 10.0958 32.5982 9.99686 32.6002 9.89431C32.6022 9.79177 32.6426 9.69466 32.712 9.62592C34.0906 8.34702 36.1097 8.34702 37.4884 9.62592C37.5577 9.69472 37.5981 9.79185 37.6 9.8944Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.2 1.79175C19.2 1.17043 18.7224 0.666748 18.1333 0.666748H17.0667C16.4776 0.666748 16 1.17043 16 1.79175V11.5417C16 12.1631 16.4776 12.6667 17.0667 12.6667H18.1333C18.7224 12.6667 19.2 12.1631 19.2 11.5417V1.79175ZM11.7667 3.06675H12.8333C13.4224 3.06675 13.9 3.58252 13.9 4.21875V11.5147C13.9 12.151 13.4224 12.6667 12.8333 12.6667H11.7667C11.1776 12.6667 10.7 12.151 10.7 11.5147V4.21875C10.7 3.58252 11.1776 3.06675 11.7667 3.06675ZM7.43333 5.66675H6.36667C5.77756 5.66675 5.3 6.18908 5.3 6.83341V11.5001C5.3 12.1444 5.77756 12.6667 6.36667 12.6667H7.43333C8.02244 12.6667 8.5 12.1444 8.5 11.5001V6.83341C8.5 6.18908 8.02244 5.66675 7.43333 5.66675ZM2.13333 8.06675H1.06667C0.477563 8.06675 0 8.58162 0 9.21675V11.5167C0 12.1519 0.477563 12.6667 1.06667 12.6667H2.13333C2.72244 12.6667 3.2 12.1519 3.2 11.5167V9.21675C3.2 8.58162 2.72244 8.06675 2.13333 8.06675Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
5
brain-h5/dist/client/assets/status/status-icons.svg
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="50" height="14" viewBox="0 0 50 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M36.3722 5.48652C36.7266 5.10328 36.7295 4.50844 36.3477 4.15255C35.3799 3.25059 34.284 2.51985 33.1002 1.98957C31.6417 1.33625 30.0786 1 28.5 1C26.9214 1 25.3583 1.33626 23.8998 1.98957C22.716 2.51985 21.6201 3.25059 20.6523 4.15255C20.2705 4.50844 20.2734 5.10329 20.6278 5.48653L28.5 14L36.3722 5.48652Z" fill="#1C1B14"/>
|
||||
<path d="M14 13V2.41421C14 1.52331 12.9229 1.07714 12.2929 1.7071L1.70711 12.2929C1.07714 12.9229 1.52331 14 2.41421 14H13C13.5523 14 14 13.5523 14 13Z" fill="#1C1B14"/>
|
||||
<path d="M49 2C49.5523 2 50 2.44772 50 3V13C50 13.5523 49.5523 14 49 14H43C42.4477 14 42 13.5523 42 13V3C42 2.44772 42.4477 2 43 2H44C44 1.44772 44.4477 1 45 1H47C47.5523 1 48 1.44772 48 2H49Z" fill="#1C1B14"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 824 B |
BIN
brain-h5/dist/client/assets/ui/finish-all.png
vendored
Normal file
|
After Width: | Height: | Size: 390 KiB |
BIN
brain-h5/dist/client/assets/ui/finish-attention.png
vendored
Normal file
|
After Width: | Height: | Size: 250 KiB |
BIN
brain-h5/dist/client/assets/ui/finish-memory.png
vendored
Normal file
|
After Width: | Height: | Size: 299 KiB |
BIN
brain-h5/dist/client/assets/ui/finish-symbol.png
vendored
Normal file
|
After Width: | Height: | Size: 246 KiB |
BIN
brain-h5/dist/client/assets/ui/finish-trail.png
vendored
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
brain-h5/dist/client/assets/ui/home-compass.png
vendored
Normal file
|
After Width: | Height: | Size: 202 KiB |
BIN
brain-h5/dist/client/assets/ui/report-insight.png
vendored
Normal file
|
After Width: | Height: | Size: 159 KiB |
14
brain-h5/dist/client/index.html
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<title>脑晴测</title>
|
||||
<script type="module" crossorigin src="/assets/index-8xOYLbW9.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-ouTen0sP.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
52
brain-h5/dist/server/index.js
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
const SYSTEM_PROMPT = `你是一名数字脑健康表现报告助手。根据用户完成游戏时产生的结构化数据,生成通俗、克制、非医疗化的中文报告。不得诊断疾病,不得推荐药物或医疗处置,不得使用“异常、受损、提示存在问题或挑战”等暗示医学结论的说法。只描述本次表现,优先使用“反应相对偏慢、注意表现有波动、容易遗漏、仍有提升空间”。只输出合法 JSON:{"summary":"80字内总结","strength":"60字内相对优势","opportunity":"60字内提升空间","recommendations":["建议1","建议2","建议3"]}`;
|
||||
|
||||
async function analyze(request, env) {
|
||||
if (!env.DEEPSEEK_API_KEY) return Response.json({ error: "AI service is not configured" }, { status: 503 });
|
||||
try {
|
||||
const assessment = await request.json();
|
||||
const upstream = await fetch("https://api.deepseek.com/chat/completions", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json", authorization: `Bearer ${env.DEEPSEEK_API_KEY}` },
|
||||
body: JSON.stringify({
|
||||
model: "deepseek-chat",
|
||||
temperature: 0.35,
|
||||
response_format: { type: "json_object" },
|
||||
messages: [
|
||||
{ role: "system", content: SYSTEM_PROMPT },
|
||||
{ role: "user", content: JSON.stringify({ profile: assessment.profile, metrics: assessment.metrics, eventCount: assessment.events?.length ?? 0 }) },
|
||||
],
|
||||
}),
|
||||
signal: AbortSignal.timeout(15000),
|
||||
});
|
||||
if (!upstream.ok) return Response.json({ error: "AI service request failed" }, { status: 502 });
|
||||
const payload = await upstream.json();
|
||||
const content = payload?.choices?.[0]?.message?.content;
|
||||
const rawReport = JSON.parse(String(content || "{}").replace(/^```json\s*|\s*```$/g, ""));
|
||||
const parsed = {
|
||||
summary: String(rawReport.summary || "本次四项任务已经完成。"),
|
||||
strength: String(rawReport.strength || "部分任务表现相对稳定。"),
|
||||
opportunity: String(rawReport.opportunity || "仍有可以通过规律练习提升的空间。"),
|
||||
recommendations: Array.isArray(rawReport.recommendations) ? rawReport.recommendations.slice(0, 3).map(String) : [],
|
||||
};
|
||||
return Response.json(parsed, { headers: { "cache-control": "no-store" } });
|
||||
} catch {
|
||||
return Response.json({ error: "AI report could not be generated" }, { status: 502 });
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request, env) {
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname === "/api/analyze") {
|
||||
if (request.method !== "POST") return new Response("Method not allowed", { status: 405 });
|
||||
return analyze(request, env);
|
||||
}
|
||||
const response = await env.ASSETS.fetch(request);
|
||||
const acceptsHtml = request.headers.get("accept")?.includes("text/html");
|
||||
if (response.status !== 404 || !acceptsHtml || !["GET", "HEAD"].includes(request.method)) return response;
|
||||
const indexUrl = new URL(request.url);
|
||||
indexUrl.pathname = "/index.html";
|
||||
indexUrl.search = "";
|
||||
return env.ASSETS.fetch(new Request(indexUrl, request));
|
||||
},
|
||||
};
|
||||
13
brain-h5/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||||
<title>脑晴测</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
30
brain-h5/mobile-runtime.lock.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"scripts/check-mobile-runtime.mjs": "ee85738cc2ef889c0b457d8ccac61e017b8c5d16f393e766ec9fb7d11c9aff7f",
|
||||
"scripts/prepare-sites-build.mjs": "b6a6adaa4fab3234676116dd1c9cb6611275ab9d92dd26f5bf402393e3744bf6",
|
||||
"scripts/update-mobile-runtime-lock.mjs": "b4e1aa41133f19ba8bb32685b3ec05798b1ceabaf72c328419d545aa60fe1f0b",
|
||||
"vite.config.ts": "0e87e403d04a2345ce91b27c0694060bf7c362505bfdf4cae3ffac2a9fc2113b",
|
||||
"src/App.tsx": "d24c44012ce15f46a8e821b0876a35b9198301e9fd229adabff9673ca0e0c884",
|
||||
"src/main.tsx": "01b7049715148ab645637905ccbbc91fc3f87659198a309541c2d1f03bb537d0",
|
||||
"src/styles.css": "a75c99f677a94a5f6f28f4240bdca42de8e323ae6d61920d7d4979d906a0ad74",
|
||||
"src/mobile/BottomSheet.tsx": "f9c376dc78b8b5feb1c8752aa23414993ab66013d11e81b2ebb8342c570032ac",
|
||||
"src/mobile/Carousel.tsx": "242f4a7488e18c35e9b43651c9d5877288655a6f9f1734e03e56108f17526bf7",
|
||||
"src/mobile/Device.tsx": "867de9c65220f2ac7202f895d2b10a64c747f211a4b22cd8f81a1a1d66e52fc2",
|
||||
"src/mobile/FlowStack.tsx": "1e85744c5f1c1138dd5e8b7b27fbb6c05139d5aac93d94c45084a018eddd4ebc",
|
||||
"src/mobile/Keyboard.tsx": "862587264375499846385ce817ea1b813db8f118140cdb6da163f989a6946cac",
|
||||
"src/mobile/MobileCursor.tsx": "be2353205e05d823bc23ecae88179d712781ee9ede8fa317057c194c7c23ee7d",
|
||||
"src/mobile/MobileRuntime.tsx": "1ffe80a9582460005d66f10fc73deaff08b81431785a506887cc0d8320be747f",
|
||||
"src/mobile/MobileScroll.tsx": "ea3e3691f587ea13753162b9e64c1981263e5b9beddebea21a6a1551dce76430",
|
||||
"src/mobile/PhoneFrame.tsx": "73015590b468acbb4b58e3f6fe5eef99c5f68f67722caffd8f00d271ba00a91d",
|
||||
"src/mobile/assets.ts": "d40cb8e5390fd2b54618266e7694e40349006dfe480f65d60559316ca8c9c234",
|
||||
"src/mobile/components.tsx": "20b02f6c6de4cfd08aa6a49bf9bc261c0ea000864b37e1001afdcd892afe4bc8",
|
||||
"src/mobile/geometry.ts": "f1382e5d6cc9adfd7ac142304f2f9a5a2330db1d74b3f0848adbaf311676bf16",
|
||||
"src/mobile/index.ts": "ee69d9341571db5dee84dd2d7452a62eef09085a84a85f198a3edc48047e0412",
|
||||
"public/assets/iphone/Bezel.png": "ee2514d74d2f75f3541405fa2f20811f159879a66586778cfd8c9c84c44959df",
|
||||
"public/assets/iphone/Keyboard.png": "292e2d83d69adaeb663d6a840ce8db8d0a5257710b472c20713e03f56b08ea88",
|
||||
"public/assets/android/Pixel10.png": "a87c1250ba20756f5d020b5351a7ae71d663dcfc64aca3a1cd56452e8a05233e",
|
||||
"public/assets/android/Keyboard.png": "6085e50be17ca2705b4351283187be2c5b01b58262c70b90d74b3376efb4adfe",
|
||||
"public/assets/android/navigation-bar.svg": "077a782a3b6c2915fc518dfb4bba825c8c5861b2572f9522a7024902b9cb2850",
|
||||
"public/assets/status/status-icons.svg": "1a8bd4207cf34532ae514bdcdca693acc9a2b8ebc9d3182dc163c3e9ddcc4823",
|
||||
"public/assets/status/ios-status-icons.svg": "40e218b274d0973d519b50f42884e6e05ef0f2b6a0e46e102fda54218784466d",
|
||||
"worker/index.js": "60a1b9c1f779ba779e1fb7a617c97a3457b843d3c6b7d20906360e8524bf622f"
|
||||
}
|
||||
35
brain-h5/package.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "brain-h5",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"check:runtime": "node scripts/check-mobile-runtime.mjs",
|
||||
"update:runtime-lock": "node scripts/update-mobile-runtime-lock.mjs",
|
||||
"test:runtime": "playwright test",
|
||||
"test:sites": "node --test tests/sites-worker.test.mjs",
|
||||
"predev": "node scripts/check-mobile-runtime.mjs",
|
||||
"dev": "vite",
|
||||
"prebuild": "node scripts/check-mobile-runtime.mjs",
|
||||
"build": "tsc && vite build && node scripts/prepare-sites-build.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/roboto": "5.2.10",
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"@radix-ui/react-dialog": "1.1.19",
|
||||
"@radix-ui/react-dropdown-menu": "2.1.20",
|
||||
"@radix-ui/react-icons": "1.3.2",
|
||||
"@use-gesture/react": "10.3.1",
|
||||
"motion": "12.42.2",
|
||||
"react": "19.2.7",
|
||||
"react-dom": "19.2.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.61.1",
|
||||
"@types/react": "19.2.17",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@vitejs/plugin-react": "6.0.3",
|
||||
"typescript": "7.0.2",
|
||||
"vite": "8.1.3"
|
||||
}
|
||||
}
|
||||
18
brain-h5/playwright.config.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { defineConfig } from "@playwright/test";
|
||||
|
||||
const testPort = Number(process.env.MOBILE_RUNTIME_TEST_PORT ?? 4174);
|
||||
|
||||
export default defineConfig({
|
||||
testDir: "./tests",
|
||||
testMatch: "**/*.spec.ts",
|
||||
timeout: 20_000,
|
||||
use: {
|
||||
baseURL: `http://127.0.0.1:${testPort}`,
|
||||
viewport: { width: 1100, height: 1100 },
|
||||
},
|
||||
webServer: {
|
||||
command: `npm run dev -- --port ${testPort}`,
|
||||
url: `http://127.0.0.1:${testPort}/tests/runtime-fixture.html`,
|
||||
reuseExistingServer: process.env.MOBILE_RUNTIME_TEST_PORT == null,
|
||||
},
|
||||
});
|
||||
1622
brain-h5/pnpm-lock.yaml
generated
Normal file
BIN
brain-h5/public/assets/android/Keyboard.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
brain-h5/public/assets/android/Pixel10.png
Normal file
|
After Width: | Height: | Size: 209 KiB |
6
brain-h5/public/assets/android/navigation-bar.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="428" height="48" viewBox="0 0 428 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="427" height="48" transform="translate(1)" fill="#0F0F0F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M119.492 16.0134C120.159 15.6203 121 16.1009 121 16.8748V31.1254C121 31.8993 120.159 32.3799 119.492 31.9867L107.41 24.8615C106.753 24.4746 106.753 23.5256 107.41 23.1387L119.492 16.0134Z" fill="white"/>
|
||||
<circle cx="223" cy="24" r="8" fill="white"/>
|
||||
<rect x="325" y="17" width="14" height="14" rx="2" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 542 B |
BIN
brain-h5/public/assets/brain/airplane-source.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
brain-h5/public/assets/brain/airplane.png
Normal file
|
After Width: | Height: | Size: 456 KiB |
BIN
brain-h5/public/assets/brain/design-reference.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
brain-h5/public/assets/brain/direction-object.png
Normal file
|
After Width: | Height: | Size: 712 KiB |
BIN
brain-h5/public/assets/brain/direction-source.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
brain-h5/public/assets/brain/memory/bread.png
Normal file
|
After Width: | Height: | Size: 967 KiB |
BIN
brain-h5/public/assets/brain/memory/carrot.png
Normal file
|
After Width: | Height: | Size: 510 KiB |
BIN
brain-h5/public/assets/brain/memory/cheese.png
Normal file
|
After Width: | Height: | Size: 481 KiB |
BIN
brain-h5/public/assets/brain/memory/coffee.png
Normal file
|
After Width: | Height: | Size: 525 KiB |
BIN
brain-h5/public/assets/brain/memory/cookie.png
Normal file
|
After Width: | Height: | Size: 771 KiB |
BIN
brain-h5/public/assets/brain/memory/egg.png
Normal file
|
After Width: | Height: | Size: 526 KiB |
BIN
brain-h5/public/assets/brain/memory/fish.png
Normal file
|
After Width: | Height: | Size: 589 KiB |
BIN
brain-h5/public/assets/brain/memory/orange.png
Normal file
|
After Width: | Height: | Size: 564 KiB |
BIN
brain-h5/public/assets/brain/planes/coral.png
Normal file
|
After Width: | Height: | Size: 527 KiB |
BIN
brain-h5/public/assets/brain/planes/teal.png
Normal file
|
After Width: | Height: | Size: 510 KiB |
BIN
brain-h5/public/assets/brain/symbols/camera.png
Normal file
|
After Width: | Height: | Size: 697 KiB |
BIN
brain-h5/public/assets/brain/symbols/coffee.png
Normal file
|
After Width: | Height: | Size: 541 KiB |
BIN
brain-h5/public/assets/brain/symbols/gear.png
Normal file
|
After Width: | Height: | Size: 611 KiB |
BIN
brain-h5/public/assets/brain/symbols/lightning.png
Normal file
|
After Width: | Height: | Size: 541 KiB |
BIN
brain-h5/public/assets/brain/symbols/lock.png
Normal file
|
After Width: | Height: | Size: 622 KiB |
BIN
brain-h5/public/assets/brain/symbols/moon.png
Normal file
|
After Width: | Height: | Size: 570 KiB |
BIN
brain-h5/public/assets/brain/symbols/puzzle.png
Normal file
|
After Width: | Height: | Size: 684 KiB |
BIN
brain-h5/public/assets/brain/symbols/squares.png
Normal file
|
After Width: | Height: | Size: 683 KiB |
BIN
brain-h5/public/assets/brain/symbols/star.png
Normal file
|
After Width: | Height: | Size: 583 KiB |
BIN
brain-h5/public/assets/brain/symbols/sun.png
Normal file
|
After Width: | Height: | Size: 584 KiB |
BIN
brain-h5/public/assets/iphone/Bezel.png
Normal file
|
After Width: | Height: | Size: 346 KiB |
BIN
brain-h5/public/assets/iphone/Keyboard.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
brain-h5/public/assets/money/money-05.png
Normal file
|
After Width: | Height: | Size: 135 KiB |
BIN
brain-h5/public/assets/money/money-1.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
brain-h5/public/assets/money/money-10.png
Normal file
|
After Width: | Height: | Size: 216 KiB |
BIN
brain-h5/public/assets/money/money-2.png
Normal file
|
After Width: | Height: | Size: 128 KiB |