Skip to content

fix(plugin-detail): reveal the inline-edit pencil on keyboard focus - #26325

Merged
objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-25883-pencil-focus-reveal
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 7 commits into
mainfrom
claude/issue-25883-pencil-focus-reveal

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #25883
Clause-②: no
Metadata-protocol: none

Item 1 of #25883: the record page's read-mode inline-edit pencil now shows on keyboard focus, not only on hover. Item 2 (remember the empty-field toggle) has an open storage-key choice and stays on the card; #25883 remains open.

What changed

  • ui/packages/plugin-detail/src/DetailSection.tsx, the details body: the pencil beside an editable field was opacity-0 group-hover:opacity-100. It is a real button, so a Tab stop landed on a control the user could not see, and the shared Button focus ring was drawn at opacity 0. It now also carries group-focus-within:opacity-100 focus-visible:opacity-100.
  • ui/packages/plugin-detail/src/HeaderHighlight.tsx, the highlights strip: the same pencil gets the same two utilities.
  • The spelling is the one RowActionMenu (plugin-grid) and data-table (components) already use for their hover-revealed row buttons. focus-visible reveals the pencil while it has keyboard focus. group-focus-within reveals it while anything in its own field has focus, for example a lookup's link in the value.
  • Unchanged: the hover reveal, the row's hover background and title hint, double-click to edit, the pencil's accessible name (detail.editInlineHint), and every locale pack.
  • scripts/sdui-manifest.sources/plugin-detail.json was re-recorded with node scripts/gen-sdui-manifest-node.mjs under the verify lock. The artefact is unchanged; only this package's record moved.
  • .changeset/25883-pencil-focus-reveal.md: @object-ui/plugin-detail patch.

Tests

New: ui/packages/plugin-detail/src/__tests__/inlineEditPencil.focusReveal-25883.test.tsx, 8 tests, covering both places.

jsdom has no layout and no CSS engine, so it cannot measure "visible on focus". The tests pin the contract a browser turns into visibility:

  • the pencil is an enabled, focusable button and takes focus;
  • it carries focus-visible:opacity-100 and group-focus-within:opacity-100 next to the unchanged opacity-0 group-hover:opacity-100;
  • the shared Button's focus-visible:ring-2 survives the className merge;
  • the nearest .group ancestor is the pencil's own field, so focus in one field reveals only that field's pencil.

Activation of the focused pencil, double-click on the value, and no pencil on computed or non-editable fields are asserted as behaviour. The pencil is found by its icon, not by its localized label.

  • Red first, at base 2409c9c8d7 with the fix absent: Tests 2 failed | 6 passed (8). Both failures are at the focus-visible:opacity-100 assertion, one for DetailSection and one for HeaderHighlight. Focusability, hover and double-click were already green there, which is the defect: a focusable but invisible control.
  • Green, at head 76d5a63f (the fix, plus origin/main 6633075 merged in), from cwd ui/ under scripts/pm/os-verify-lock.sh:
    • pnpm exec vitest run packages/plugin-detail --maxWorkers=2: Test Files 305 passed | 1 skipped (306), Tests 2833 passed | 18 skipped (2851). All 306 test files under plugin-detail ran, the new one included. The same run on the pre-merge head 55fe5e19 read the same counts.
    • pnpm --filter @object-ui/plugin-detail run type-check (both tsc --noEmit and tsc -p tsconfig.test.json, so the new test is compiled): exit 0.
    • node scripts/check-sdui-manifest.mjs: exit 0. The artefact is unchanged, and 27 package records are current.

Gates

Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at head 76d5a63f: 45 commands.

  • 44 run locally, each exit 0, including node scripts/check-sdui-manifest.mjs, pnpm check:nul-bytes, pnpm check:cross-package-test-inputs, pnpm check:test-source-alias, pnpm check:type-check-coverage and the changeset gates.
  • NOT-MEASURED: pnpm check:type-check-debt. Its re-measure waits on the shared verify lock for a closure build and then runs tsc over every package. It was still waiting on the lock at a 300 s cap on a contended box, so it is left to CI's Lint & Repo Gates.
  • --ran reconciliation: 45 derived famil(ies) accounted for — 44 run, 1 NOT-MEASURED, exit 0.
  • CI runs every other family (lint, the type-check lanes, Build and E2E UI). This PR cites CI rather than re-running them locally.

Acceptance notes

  • Copy button, same hover-only class, not changed here. In DetailSection.tsx the copy button drawn beside a read-only, copyable field has the same opacity-0 group-hover:opacity-100 and is also a Tab stop. The claim scopes this PR to the pencils, so it is reported to the seat rather than fixed here.
  • The pencil's tooltip text. The tooltip, which Radix opens on keyboard focus, reads detail.editInlineHint ("Double-click to edit"). A keyboard user activates the pencil with Enter or Space. Locale packs are out of this claim's scope; this is left as an open question to the seat.
  • Touch. The pencil is still not revealed on a coarse pointer. RowActionMenu adds pointer-coarse:opacity-100 for that case. The mobile read row's missing edit path is out of this claim's scope.
  • Landing order. On the plugin-detail line this lands after feat(plugin-detail): the record activity timeline folds consecutive updates by one actor into one row with a count #26055 and fix(plugin-detail): related list with actions: [] no longer draws the built-in New #26277. Neither touches DetailSection.tsx or HeaderHighlight.tsx.

Generated by Claude Code

Red first: the read-mode pencil in DetailSection and HeaderHighlight is
revealed on hover only, so a Tab stop lands on an invisible control.

Claude-Session: https://claude.ai/code/session_01SMps7wq7rBTk53e8KTSEup
Co-authored-by: Claude <noreply@anthropic.com>
The read-mode pencil in DetailSection and HeaderHighlight was
`opacity-0 group-hover:opacity-100`: a real button, so a Tab stop
landed on an invisible control. It now also shows on
`focus-visible` and `group-focus-within`, the spelling RowActionMenu
and data-table already use. Hover and double-click are unchanged.

Claude-Session: https://claude.ai/code/session_01SMps7wq7rBTk53e8KTSEup
Co-authored-by: Claude <noreply@anthropic.com>
`node scripts/gen-sdui-manifest-node.mjs` under the verify lock: the
artefact is unchanged and only plugin-detail's record moved;
`node scripts/check-sdui-manifest.mjs` exits 0.

Claude-Session: https://claude.ai/code/session_01SMps7wq7rBTk53e8KTSEup
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

ACCEPT · seat 2 · 2026-10-26T21:12Z · PR #26325 head 76d5a63f (#25883 item 1, Part of #25883)

check reading
scope 5 files, +225/−3: one class string on the pencil in DetailSection.tsx and in HeaderHighlight.tsx, a new pin file, sources/plugin-detail.json, a root .changeset/
behaviour the pencil gains group-focus-within:opacity-100 focus-visible:opacity-100 (the RowActionMenu / data-table idiom); hover, title, double-click, aria-label and locale packs unchanged
red first at base 2409c9c8d7: 2 of 8 red, exactly the focus-reveal assertions in both components
tests full plugin-detail suite 305 files / 2833 tests green at head; type-check exit 0; jsdom limits declared (classes, focusability, own .group pinned)
gates 45 derived, 44 run exit 0; check:type-check-debt NOT MEASURED locally (lock), CI reads it
governance NOT governed, 228 lines

Not armed yet: on the plugin-detail line this PR enqueues after #26055 (seat 2) and #26277 (seat 1, #26182) have MERGED, with a re-record of plugin-detail.json then. The copy button beside the pencil has the same hover-only reveal and is filed as #26329. The keyboard wording of the pencil's label (needs a locale key) and the touch reveal are noted on that card, not owed here.

…ncil-focus-reveal

# Conflicts:
#	scripts/sdui-manifest.sources/plugin-detail.json
… tree

The merge took main's scripts/sdui-manifest.sources/plugin-detail.json
(main moved it with the activity fold and the related-list actions fix);
`node scripts/gen-sdui-manifest-node.mjs` under the verify lock re-records
it over the merged plugin-detail sources. The artefact is unchanged and
only plugin-detail's record moved; `node scripts/check-sdui-manifest.mjs`
exits 0.

Claude-Session: https://claude.ai/code/session_01SMps7wq7rBTk53e8KTSEup
Co-authored-by: Claude <noreply@anthropic.com>
…ncil-focus-reveal

# Conflicts:
#	scripts/sdui-manifest.sources/plugin-detail.json
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读

改了什么:记录详情页读模式下,可编辑字段旁的铅笔(行内编辑)按钮现在在键盘聚焦时也会显示,不再只在鼠标悬停时出现;详情区和顶部高亮条两处一致。
为什么改:铅笔是真实按钮,Tab 会停在它上面,但它和焦点环都是透明的,键盘用户停在一个看不见的控件上(#25883 第 1 项)。
风险与回滚:只在两处按钮上加两个显示类(与表格行操作按钮同一写法);悬停、双击编辑、提示文字和无障碍名称都不变。回滚 = revert 本 PR。
先看:ui/packages/plugin-detail/src/DetailSection.tsx(铅笔按钮 className)→ ui/packages/plugin-detail/src/HeaderHighlight.tsx(同一处)→ inlineEditPencil.focusReveal-25883.test.tsx。
两分钟验证:在 ui/ 下运行 pnpm exec vitest run packages/plugin-detail/src/__tests__/inlineEditPencil.focusReveal-25883.test.tsx,预期 8 个测试全过;或在记录页按 Tab 走到可编辑字段,铅笔应可见。
需要你做的:无,席位落地。
席位意见:ACCEPT。行为与此前席位 ACCEPT 相同;此后两轮只合 main(含 #26367 删除按包记录),本 PR 现不含记录文件。#25883 第 2 项仍留在卡上(Part of)。

席位核对

seat 2 · 2026-10-27T03:18Z · head 0438bd13 (#25883 item 1)

item reading
form 4 files, +224/−2: one class string on the pencil in DetailSection.tsx and HeaderHighlight.tsx (group-focus-within:opacity-100 focus-visible:opacity-100), the 8-case pin, root .changeset/; no record file
premise measured earlier seat-2 ACCEPT stands; patch rounds merged main f02e6443 (#26055, #26277) and abb90460 (#26367) by merge commits; no code conflict
gates (check-runs on 0438bd13) 54 runs: 45 success, 9 skipped, 0 failed. Local at this head: plugin-detail type-check exit 0; plugin-detail suite 306 files / 2853 tests; check-sdui-manifest exit 0; 35 of 35 derived gates exit 0
accepted deviations none new
out of scope copy-button hover-only reveal → #26329 (same file; next after this) ; #25883 item 2 stays on the card
landing NOT governed (check-governed-merges --pr 26325: 226 lines, no hit path); git merge-tree against origin/main clean

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 03:19
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 03:19
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 40443739774 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

分类:failure —— 按下面的日志分诊。

失败的 job(日志抽取,best effort):

  • Test UI (11/16) — 失败步骤: Run the ui tests (shard 11/16)

    × pins every workflow-anchored claim to the version .github/workflows declares 13ms
    × reads every declaration the tree contains, so the anchor cannot shrink unnoticed 44ms
    FAIL   unit  scripts/__tests__/doc-version-claims.test.ts > doc version claims - the workflow-version assertion > pins every workflow-anchored claim to the version .github/workflows declares
      ↳ 失败原因: AssertionError: implausibly few node-version declarations were read from ../.github/workflows - the anchor is thinner than the claims it backs, or the parser stopped reading them: expected 1 to be gre
    FAIL   unit  scripts/__tests__/doc-version-claims.test.ts > doc version claims - the workflow-version assertion > reads every declaration the tree contains, so the anchor cannot shrink unnoticed
      ↳ 失败原因: AssertionError: control for the agreement above: no node-version line was found in .github/workflows at all, so two empty lists just agreed with each other: expected 1 to be greater than or equal to 1
    

↳ 失败原因 是判读的关键:超时(Test timed out in … / Hook timed out in …)多半是负载/时序,不是本 PR 的回归;
断言(AssertionError: …)才指向真实的行为改变。两者的 FAIL 行长得一模一样,只有这一行能区分。

⚠️ 断言这一侧有一类例外,判据是断言在测什么,不是它是不是 AssertionError。 断言的对象是产品行为(一个值、一个形状、一次拒收)⇒ 照上面读:真实的行为改变,去查,⛔ 不要重排掉;
断言的对象是这次实验自身的有效性前提(跑完的耗时、负载下的先后、任何只在时间预算内才成立的条件)⇒ 它跟超时是同一类,同样对负载敏感,重排一次是合法的判别手段。
识别是机械的:断言的消息或它比较的值本身点名了一段时长、一个时间戳、一个耗时计数。实测过的一对 —— AssertionError: SecurityPlugin.init() ran: expected false to be true 测的是产品行为(真回归);
AssertionError: this run took over a second, so second-precision stamps could have differed too: expected 1006 to be less than 1000 测的是实验前提:它守护的那条不变式当时是绿的,同一个 head 原样重排一次即成功。
穿着 AssertionError 外衣的时间测量,仍然是时间测量。(⛔ 这只改「怎么读一次红」,不改「哪些测试可以重排」——后者由别处管。)

跨 PR 相同签名(24h,按失败测试文件聚合):

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 11 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 看上面的「跨 PR 相同签名」;已有汇总 issue ⇒ flaky/环境问题实锤,去那张 issue 上谈,修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Merged via the queue into main with commit 411fc00 Oct 27, 2026
55 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-25883-pencil-focus-reveal branch October 27, 2026 03:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants