boardx / boardx/boardx-dev-template
p7-F10 follow-up:语音转录落点忽略视口(平移后落屏幕外+零反馈),且现有 e2e 看不见
- Dominant language
- TypeScript
- Stars
- 1
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
拆自 #673(p7-F10 语音转录到白板)。**人类 2026-07-17 决定先合 #673**,本 issue 承接 review 提出的未修项,别让它们随合并消失。
## 🔴 落点硬编码,忽略视口 —— 平移过白板后转录文本落到屏幕外,且用户零反馈
`apps/web/components/board/board-canvas.tsx:1376-1377`(实测):
```ts
const x = 400;
const y = 300 + placeN.current++ * 40; // 多次转录避免完全重叠,仍聚在中央附近
```
`400/300` **只在默认视口**(`tx=0, ty=0, scale=1`)时才等于画布中央。而**同一个组件的第 587 行就有** `const [vp, setVp] = useState({ tx: 0, ty: 0, scale: 1 })` 和 `screenToBoardPoint` —— **本来就能正确算,只是没用**。
用户一旦平移过白板(**白板的常态**),这个板坐标就不再是可视区域,转录文本落到屏幕外。而 POST 返回 201,**不走任何错误分支** → 用户录完音"什么都没发生",零提示。直接破坏 F10 的 `user_visible_behavior`(UC 主流程 4 要求"画布中央附近")。
**修**:用 `vp` 反算当前可视区中心(`screenToBoardPoint` 容器中点)。
### ⚠️ 必须同时补断言,否则 verify 门控不住
现有 e2e **结构性看不见这个 bug**:`expectItemCount`/`canvasItems` 读的是**渲染层数据**,不是屏幕可见性;spec 里对位置的断言数 = **0**(实测)。所以 **5/5 绿证明不了 UC 主流程 4** —— 这正是 p14-F04(#483)那类"绿灯但功能不成立"的形状,本仓吃过亏。
**F10 翻 passing 前必须有一条「平移后录音仍落在可视区」的断言。**
## 🟡 `placeN` 共享计数器,步长语义打架
`placeN` 在 addNote/addText/addShape/embed 处都是 `* 250`(:1319/1340/1423/1467/1527),addVoiceText 用 `* 40`。双向后果:① 一次语音转录把共享计数器 +1 → 下一个便签额外下移 250px;② 用户先建 5 个便签再转录 → 落点 `y = 300 + 5*40 = 500`,注释声称的"仍聚在中央附近"不成立。**应使用独立计数器。**
## 🟡 `if (res.status !== 201) return;` 静默吞掉失败(:1381)
用户刚说的话直接丢弃、零提示。UC 主流程 7 明确要求"转录失败…显示错误或重试入口"。addText 有同样写法但丢的是常量 `DEFAULT_TEXT`(再点一次即可);**语音丢的是用户不可复现的口述内容,代价不对等**。PATCH 返回值同样未检查。
## 🟡 spec:96-110 把疑似不合规的行为固化成期望
UC 主流程 7 把"没有识别到语音"列为应显示错误/重试的场景,但实现(`if (!trimmed) return;`)和断言都是**静默回 idle 无提示**。请确认是否为有意的产品取舍,否则测试会锁死错误行为。
## 🟡 付费 STT 端点无限流(非 #673 引入,但暴露面变大)
`/api/ava/transcribe` 有鉴权收口 + 25MB/MIME 白名单,但**无限流**(全仓 grep `rateLimit|rate-limit` 在 `apps/web/lib`、`apps/web/app/api` 下**零命中** —— 无限流基建)。登录用户可无限刷 Whisper。#673 把它从 1 个入口(AVA)扩到 2 个(Board Header)。同类项见 #654。
## 🟢 证据披露(F10 翻 passing 时别漏)
无 `OPENAI_API_KEY` 时 route 返回 **stub 文案**(`api/ava/transcribe/route.ts:69-73`),spec 的 `expect(item.text.length).toBeGreaterThan(0)` 是被 stub 文本满足的。链路(录音→上传→回填→落库→选中)真实,但**不覆盖真实 Whisper 转写**。**落 evidence 时必须显式写明跑的是 stub 模式**,别让后来人误读"5/5 绿 = 真实 STT 已验证"。
## 注意:F10 尚未解锁
#673 **没有**改 `feature_list.json`,F10 仍是 `blocked`、`evidence: ""`(这是它做对的地方——把翻转留给 `pnpm harness verify` 门控)。所以 **#641 仍未关闭**,还需修完上述项 + 跑 verify 落 evidence。
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with apps/web/components/board/board-canvas.tsx around the viewport state, placement logic, and voice transcription handler, then inspect the F10 e2e spec around lines 96-110. Review apps/web/app/api/ava/transcribe/route.ts and run the relevant tests with the stub mode noted. Done means panned-board visibility, independent placement, failure or retry feedback, and the chosen empty-transcription behavior are covered before harness verification.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, frontend, security, testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100