Files
Jyotisha/docs/tasks/TASK-frontend-interaction-20260830.md
T
Jesse_Chen 8db71aaf81 docs: product-level README, AGENTS.md split into code/reading parts, add CLAUDE.md, move task briefs to docs/tasks
- README.md is now the product/repo front door (architecture, repo map,
  local dev, test tiers, delivery flow, doc map). Engine positioning,
  VedAstro/Codex setup and the oracle/benchmark command reference move
  verbatim to docs/engine/README.md, docs/engine/vedastro-gateway.md and
  docs/benchmark/README.md. Capability badges realigned with the registry
  (91/78/8/0); tests/test_readme_badges.py was red on staging.
- AGENTS.md: Part A (environment truth, delivery, worktrees, record
  placement, bug workflow, growth freeze, frontend red lines, privacy,
  pre-work check, test tiers) and Part B (reading-rigor constraints).
  GitHub issue-tracker/triage boilerplate removed: GitHub is a read-only
  mirror. All strings locked by tests/ are preserved.
- CLAUDE.md added: roles, three working modes, task-brief sections,
  acceptance criteria, session discipline; imports AGENTS.md.
- 50 tracked TASK-*/PROGRESS-* files and 3 never-committed briefs move to
  docs/tasks/ with an index; REPO_LAYOUT.md merged into README.

Docs-only change (no gated path touched).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0193vBv6w5MV2cifdTUu9H5P
2026-09-03 06:56:06 +00:00

13 KiB
Raw Blame History

任务书 · 客户端交互与 UI 收尾(2026-08-30

基线:origin/staging @ ea0fbd44

本轮四条任务全部来自一次对着代码实测的审计,每条都附了实测数据。先读完「硬红线」再动手。


硬红线

  1. 不得改变任何元素的视觉尺寸。 任务 1 是扩大热区,不是把按钮画大。改完截图对比,图标和留白必须和改前一模一样。
  2. 不得手写 useCallback / useMemo
  3. 不得修改既有测试断言 —— 除非该断言锁住的正是本轮要修的缺陷本身;那种情况下必须在断言上方写注释说明「原值是什么、为什么它是错的」,并在 PROGRESS 里单列。
  4. 推 staging 前必须 ./node_modules/.bin/tsc --noEmit 通过。不要用 npx tsc,本仓库环境下会装到空包 tsc@2.0.4
  5. 测试数不得低于基线,且 fail=0skipped=0(在有 Docker 的环境里跑)。本机没有 Docker 时会有 23 条数据库/部署类失败 + 10 条 skipped,那是既有环境缺口,不是你引入的 —— 但必须逐条比对失败清单,确认没有新增
  6. 浅色和深色两套都要验。 本仓库已有深色主题(prefers-color-scheme + data-theme),任何配色改动必须同时在两套下检查对比度。
  7. 不得改 .gitea/workflows/**。不得在有未提交改动的工作树上切分支。不得自行把 staging 提升到 main。

让步顺序:功能与测试不回归 > 可验证的修复 > 视觉一致 > 代码整洁。

开工前置

git fetch origin --prune
git worktree add -b codex/interaction-20260830 \
  ../.worktrees/interaction-20260830 origin/staging

基线必须是 origin/staging,不是任何本地 ref。读 pre_work_error_ledger.md,跑 scripts/pre_work_check.py,读 frontend/AGENTS.md(Next.js 版本与训练数据不同,写代码前先看 node_modules/next/dist/docs/)。改前先在 docs/BUG_HISTORY.md 检索同类记录。

下面所有行号只是线索,请按选择器定位origin/staging 上的行号可能有偏移。


任务 0(P0)· 深色下两处对比度不达标

事实

深色调色板的字色逐个对底色实测,两组低于 WCAG AA 4.5:1

组合 实测
--color-ink-tertiary #928e84 on --color-canvas-muted #30302d 4.05:1
--color-action #d4785a on --color-canvas-muted #30302d 4.18:1

frontend/tests/dark-theme-contract.test.ts 现有的对比度断言只验了「字色 vs --color-canvas,漏掉了卡片底 --color-canvas-muted,所以这两组没被拦住。代码块的语言标签(.markdown-code-language)正是 ink-tertiary 落在 .markdown-code 的暖卡片底上,是已在线上的实例。

做法

两个深色块(@media (prefers-color-scheme: dark) 内的 :root:not([data-theme="light"]),以及 :root[data-theme="dark"]各改一处,共两处:

--color-ink-tertiary: #928e84  →  #9c988e
--color-action:       #d4785a  →  #d78064

#d78064 与原值色相 15°、饱和度均不变,只提亮 2.5% 明度 —— 观感仍是同一个黏土橙,不要顺手换色相。

#d4785a 在仓库里共 15 处,四个根边界页(error.tsx / not-found.tsx / forbidden.tsx / global-error.tsx,它们各自内联了一份深色 token)和 DESIGN.md 的深色对照表里都有,必须一起换,否则边界页的行动色会和主站不一致:

grep -rn '#d4785a' frontend/src frontend/DESIGN.md

--color-focus 在深色块里跟随 --color-action,一并确认。

测试

dark-theme-contract.test.ts 里那条对比度断言从「1 种底色」扩成 8 种字色 × 4 种底色

  • 字色:color-inkcolor-ink-strongcolor-ink-secondarycolor-ink-tertiarycolor-actioncolor-dangercolor-successcolor-warning
  • 底色:color-canvascolor-canvas-softcolor-canvas-mutedcolor-sidebar-solid

全部 32 组必须 ≥ 4.5:1,失败信息里要打印出具体是哪一对、实际多少。不要放宽阈值来让测试通过。

验收

./node_modules/.bin/tsx --test tests/dark-theme-contract.test.ts
grep -rc '#d4785a' frontend/src frontend/DESIGN.md   # 期望全 0

反向验证:把 --color-ink-tertiary 改回 #928e84,测试必须变红。

建档

docs/BUG_HISTORY.md 新增一条(用户可见的可读性缺陷)。编号前先确认远端最大号,别抢号:

git fetch origin --prune && git show origin/staging:docs/BUG_HISTORY.md \
  | grep -o '^## BUG-[0-9]*' | sort -t- -k2 -n | tail -3

防复发条写清楚:新增字色/底色 token 时必须同时进这张 32 组矩阵,不得只验阅读面。


任务 1(P0)· 触达尺寸普遍低于文档承诺的 44px

事实

frontend/DESIGN.md:165 写着 "Touch targets are at least 44px"。实测违反:

选择器 实际 位置
.message-actions buttonglobals.css:1253 附近) 26×26pxpadding: 0 每条回答下方的赞/踩/复制/重新生成
.chart-nav-chip:732 32px 侧栏星盘 chip
.auth-links button:1657 32pxpadding: 0 登录页链接按钮
.select-item:2507 40px 模型选择器每一项
.birth-time-window-details .birth-time-skip-button:2450 40px
.report-center-section-heading button 40px

26px 那条优先级最高 —— 它是全产品点得最勤的控件,出现在每一条回答下方,且完全没有 padding 撑开热区。

做法

扩大热区,不改视觉尺寸。 用透明的伪元素把可点区域撑到 44px:

.message-actions button { position: relative; }
.message-actions button::after {
  position: absolute;
  content: "";
  inset: 50% 50% 50% 50%;
  translate: -50% -50%;
  width: 44px;
  height: 44px;
}

(上面是示意,具体写法自己定,但必须满足:视觉尺寸不变、热区 ≥44px、不影响相邻元素布局、不遮挡兄弟元素的点击。)

对 40px 那几个,直接把 min-height 提到 44px 更简单 —— 但前提是不撑破所在容器.select-item 在下拉里有 max-height 限制,改高之后一屏能显示的条目变少,需要目视确认下拉不会变得难用;如果变差,改用伪元素方案。

.chart-nav-chip 是 inline-flex 的胶囊,直接提高会改变视觉;用伪元素。

止损

  • 如果某一处用伪元素会造成热区互相重叠(相邻按钮间距小于 44px 时必然发生),不要硬撑到 44px:把该处的实际间距和重叠范围写进 BLOCKED.md,只做到不重叠的最大值,并说明还差多少。宁可 36px 不重叠,也不要 44px 互相吃点击。
  • .message-actions 一行有 4 个按钮,请先量一下它们的间距再决定。

验收

  • 改前改后各截一张图(浅色 + 深色),确认视觉零差异
  • 用浏览器 devtools 量出每个按钮的实际命中矩形,列表给出
  • 相邻按钮热区不得重叠 —— 给出实测间距
  • 新增合同测试:断言这些选择器要么 min-height ≥ 44px,要么带扩大热区的伪元素规则

建档

同任务 0,另建一条 BUG_HISTORY。防复发:新增可点元素必须满足 44px 命中区,视觉尺寸小于 44px 时用伪元素补。


任务 2P1)· 字数上限是静默的

事实

位置 上限 反馈
主对话输入框(page.tsx 传给 chat-composer.tsx 500
birth-time-guide-turn.tsx:34 500
birth-time-choice-question.tsx:113 240
姓名输入(page.tsx:909 80

只有 maxLength 属性。到顶之后继续打字,浏览器直接不接收,界面毫无反应 —— 这是最难自我诊断的一类问题,用户只会觉得"键盘坏了"。

做法

给这几处加接近上限时才出现的计数提示:

  • 平时不显示,剩余字数低于某个阈值(建议 50,短输入按比例调)才出现,避免常态噪声
  • --color-ink-tertiary;到达上限时才转 --color-danger
  • 计数区必须 aria-live="polite"aria-atomic="true",但只在阈值内挂载,不要让读屏用户每敲一个字都被播报
  • 输入框加 aria-describedby 指向计数区

主输入框的计数放哪需要判断:.composer-footer 已经存在,优先复用,不要新造一层容器把输入框顶高。

止损

如果加计数会让移动端输入区高度变化导致布局跳动,先只做主对话输入框,其余三处登记 BLOCKED.md。布局跳动比没有计数更糟。

验收

  • 四处(或已做的那几处)各录一段:从阈值外打到上限,说明何时出现、何时变红
  • 读屏验证:阈值外不播报,进入阈值后播报一次,不是每字一次
  • 移动端确认输入区高度不跳

任务 3(P1)· 三个输入框的发送键不一致,且无任何提示

事实

位置 发送方式
主对话输入框(page.tsx:3701 EnterShift+Enter 换行),已正确处理 isComposing
生时校正对话(rectification-agentic-chat.tsx:1233 Enter,已处理 isComposing
birth-time-guide-turn.tsx:39 ⌘/Ctrl+Enter

第三处不但与前两处相反,界面上没有一个字告诉用户。它有可见的「整理为经历草稿」按钮所以不会卡死,但用户在主对话养成的 Enter 习惯到这里只会换行。

做法

统一为 Enter 发送、Shift+Enter 换行,与前两处一致。改 birth-time-guide-turn.tsx

  • 必须同时加 !event.nativeEvent.isComposing 判断 —— 这是中文输入法的必要条件,前两处都有,第三处因为原本需要修饰键所以没加。漏了这条会导致中文用户选词时误发送,比现状严重得多。
  • 保留「整理为经历草稿」按钮不动。

那是一个 rows={3} 的多行框,改成 Enter 发送后换行只能靠 Shift+Enter。如果你判断这个框的使用场景更需要自由换行(用户在描述一段经历),可以反过来选择保留 ⌘+Enter 但补上可见提示(放在既有的 #birth-time-guide-hint 里)。两种都可接受,但必须二选一并在 PROGRESS 里写明理由 —— 不允许维持"既不一致又无提示"的现状。

验收

  • 中文输入法下逐字验证:输入拼音 → 按 Enter 选词 → 不得发送;选完词再按 Enter → 发送
  • 三个输入框的行为写成一张对照表放进 PROGRESS
  • 新增合同测试:三处的 onKeyDown 必须都带 isComposing 判断

不在本轮范围

  • 会话列表虚拟化 / page.tsx:1212visibleSessions 每次 render 重排:与已修好的消息列表同类,但会话数通常只有几十。没有渲染基准之前不得动手 —— 需要先按 frontend/scripts/home-streaming-render-benchmark.mts 的模式出一份会话列表基准,证明它是热点。红线也禁止手写 useMemo
  • 代码块语法高亮:产品已明确不做。
  • 会话搜索、编辑自己发出的消息:不在本轮。
  • 15 个无 CSS 规则的死类名:见 frontend/tests/class-name-definition-contract.test.tsknownUnstyled 白名单,都是有意的空修饰符,不要顺手清理。

收尾

  • PROGRESS 文件名写 PROGRESS-frontend-interaction-20260830.md不要写成 PROGRESS.md —— 根目录已有受版本控制的 progress.md,大小写不敏感文件系统上会互相覆盖。
  • 四条任务合成一次 staging 推送(staging push 触发全量构建+部署,没有路径过滤,不要分多次推)。
  • 推送后核对 https://staging.jyotisha.chat/api/health.deployment.gitCommit 等于新 SHA。流水线是 validatepublish → 才派发 deploy,全链路约 20 分钟,不要在十几分钟内就断定部署失败。
  • 不自行提升 main。

交付物清单

  1. 两个深色块各改两处色值,#d4785a 全仓 15 处清零
  2. dark-theme-contract.test.ts 扩成 32 组对比度断言 + 反向验证证据
  3. 触达尺寸改动,附改前改后截图(浅色+深色)证明视觉零差异
  4. 每个可点元素的实测命中矩形与相邻间距
  5. 字数计数提示,附阈值行为录制与读屏播报验证
  6. 发送键统一(或补提示)+ 中文输入法选词不误发的逐字验证
  7. 三个输入框行为对照表
  8. 新增合同测试(触达尺寸、isComposing
  9. docs/BUG_HISTORY.md 两条新条目(编号已对远端确认)
  10. BLOCKED.md:任何触发止损的项
  11. PROGRESS-frontend-interaction-20260830.md
  12. tsc --noEmit / eslint0 error/ tsx --test(失败清单与基线逐条比对)/ next build 四条命令的实际输出