Skip to content

fix(plugin-detail, app-shell, components): the record page's controls are 44 px touch targets at phone width, and Back after 「‹」 leaves the record - #29210

Merged
objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/project-thread-amh4li
Nov 2, 2026
Merged

objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/project-thread-amh4li

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #29071

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:手机宽度(375 / 390 px)下,记录页自己的控件都达到 44 px 触控尺寸:记录标签和「+N」菜单、相关列表卡片的头部控件和行、放大视图的工具栏与分页、时间线筛选与「写备注」、评论工具栏、「显示 N 个空字段」、内联保存栏、底部操作栏、返回链接「‹」、上一条 / 下一条、复制记录 ID、附件上传、声明式操作按钮及其参数对话框的取消 / 确认。内联编辑铅笔(详情区和要点条)在触屏上始终显示。返回链接「‹」改为替换当前历史记录,浏览器「后退」不会再打开刚离开的记录。
  • 为什么改:移动端 App 用 WebView 承载 Console,手机用户看到的就是 375 / 390 px 的记录页。实测这些控件只有 16–40 px,铅笔要悬停才显示(触屏没有悬停,等于没有内联编辑入口),点「‹」后再按后退又回到记录。
  • 风险与回滚:低。只改 className 字符串和一个 <Link replace>;sm 以上尺寸全部不变(e2e 用例在 1280 px 钉住 36 / 24 / 28 px 和悬停才显示的铅笔)。不加键、prop、导出或断点。回滚即回退本 PR。
  • 先看:ui/packages/app-shell/src/views/RecordDetailView.tsx(phoneBack 的 replace);ui/packages/components/src/custom/record-top-bar.tsx;ui/packages/plugin-detail/src/RelatedList.tsx;ui/e2e/record-flow-phone-targets.spec.ts。
  • 两分钟验证:cd ui && npx playwright test e2e/record-flow-phone-targets.spec.ts --project=chromium(5 个用例通过);pnpm --dir ui exec vitest run packages/app-shell/src/views/RecordDetailView.phoneFace-27058.test.tsx(含新的「后退不回到记录」用例)。
  • 需要你做的:无,席位落地。下方「Findings」列出不属于本卡的问题及其归属。

What changes

The mobile app hosts the Console in a WebView, so a phone user gets the record page at 375 or 390 px. Driven there on six records, the page did not scroll sideways, its dialogs and menus fit the screen and the keyboard left the focused field in view, but most of the page's own controls were smaller than a finger, the inline-edit pencil was invisible on a touch screen, and the back link left the record behind it in the history.

  • 44 px targets below sm, through the spellings the code already uses for its phone faces (h-11 sm:h-N, min-h-[44px] sm:min-h-0, min-w-11 sm:min-w-0):
    • plugin-detail: RecordTabRow (tabs and the +N trigger), RelatedList (card head, New / Add, rows and their name buttons, footer "View all", the enlarged view's filter, chips, New / Add / Export, sort and paging, Enlarge), RelatedCardMenu (⋯ and the reorder handle), RelatedListEmptyFold and EmptySectionsFold (the fold toggles and Expand), DetailSection (pencil, empty toggle, title row), HeaderHighlight (the strip's pencil), InlineEditSaveBar (Cancel / Save), RecordActivityTimeline ("Write a note…", the filter chips and select), RichTextCommentInput (the toolbar, which now wraps), RecordTimelinePanel (head), record-reference-rail (View All).
    • app-shell: RecordDetailView (the phone's 「‹」), DeclaredActionsBar (the declared decision buttons and the override menu trigger), ActionParamDialog (footer Cancel / Confirm), RecordAttachmentsPanel (Upload).
    • components: record-top-bar (↓ ↑ and the bar's other icon buttons; the bar grows from h-10 to its content, min-h-11, so a 44 px control does not spill past its border), containers.tsx (the phone header bar's action buttons and ⋯), RecordTitleChip (copy record ID).
  • The inline-edit pencil in the details body and in the header strip is drawn on a coarse pointer (pointer-coarse:opacity-100). With a mouse it still waits for hover or focus.
  • 「‹」 replaces the record's history entry (<Link to={to} replace>), so the browser's Back after it leaves for the entry below and never opens the record again. A page pushed from a parent record (a related row or a lookup) already went back with navigate(-1) and is unchanged.

No key, prop, export or breakpoint is added. useIsMobile / usePhoneViewport and the existing phone layouts are untouched.

Run record

Six records, each at 375×812 and 390×844 (Chromium, isMobile + hasTouch), on the real Console (Vite source console against examples/app-crm and the showcase backend, signed in as the seeded admin):

Record Page
CRM lead /apps/com.example.crm/crm_lead/record/AsRu1Po-1-Fob7D-
CRM account (151 contacts added) /apps/com.example.crm/crm_account/record/ZE2x6eRx2CNUIEf1
CRM opportunity /apps/com.example.crm/crm_opportunity/record/MY5BwNPGtULohScT
Showcase account /apps/showcase_app/showcase_account/record/2OeMEsWhxzpXuMYa
Showcase project (line-item Tasks tab) /apps/showcase_app/showcase_project/record/9aZqRnYBQ6EY225w
Approval request /apps/showcase_app/sys_approval_request/record/areq_214e57da-…

On each: load, every tab, the tab overflow menu, inline edit, the related New dialog, the related ⋯ menu, View all, Enlarge, a declared action's dialog, the action bar's ⋯ and the Edit action.

Check Before (base 0e43df23), 375 and 390 After (head), 375 and 390
A. No horizontal page scroll Pass on all 12. Only intentional inner scrollers: the project's Tasks line items (826 px in 275 / 290), the opportunity's product lines in Edit (548 in 341 / 356), the account's line items in New (1020 in 341 / 356). Pass on all 12, same inner scrollers.
B. 44 px targets Fail. Smaller-than-44 controls per page at 375 (same at 390): lead 38, account 72, opportunity 63, showcase account 68, project 43, approval 21. Mine: tabs 36 tall, +N 32×36; card ⋯ / New 24×24, reorder 20×24, card rows 18.8 tall; footer "View all" 20 tall; Enlarge 36; enlarged chips / Export 32; timeline chips 24, "Write a note…" 32; comment toolbar 28×28; "Show N empty fields" 28, Expand 16; Save / Cancel 36; action bar Edit 40, ⋯ 40×40; 「‹」 92×28 (seat), 69–153×28 measured; ↓ ↑ 28×28 (seat); copy ID 20×20; New 36; decision buttons 36 tall; param dialog Cancel / Confirm 40 tall; the pencil 20×20 at opacity 0. Pass for every control this card owns. 「‹」 69.5×44, ↓ ↑ 44×44 in a 45 px bar, decision buttons and param Cancel / Confirm 44, pencil 44×44 at opacity 1 on all 12. Left (lead 14, account 28, opportunity 24, showcase account 35, project 25, approval 8): each is in Findings below.
C. Overlays inside the viewport Pass. Dialogs fill [0,0,W,H]; the tab overflow, related ⋯ and action ⋯ menus sit inside; the declared action dialog [0,92,375,720]. Pass, 40 overlay readings, all inside.
D. Keyboard (viewport shrunk by 336 px, focused field) Pass. The focused field stays visible and uncovered; Save stays visible when there is one. Pass (18 readings: visible, uncovered; Save visible in the 10 with a save bar).
E. 4× CPU, production build, account with 151 contacts Title 1181 / 1035 ms, related cards 2733 / 2619 ms, scroll 61 / 60 fps with a 17 ms worst frame gap, worst interaction 200 / 176 ms, longest task 245 / 248 ms (one run). A/B on the same box, interleaved, main's bundle against head's (only this PR's 19 source files differ): worst interaction main 320 / 256 / 256 / 176 ms, head 248 / 184 / 200 / 208 ms; longest task main 218–280, head 242–311; 60 fps and a 17 ms gap on both. Same envelope: the change costs nothing measurable.

Back after 「‹」 (seat input): from the lead list at 375 and 390, tap a row, step ↓ once, tap 「‹」, press the browser's Back. Before (the seat's reading): Back opened the record again. After: Back lands on the list (/apps/com.example.crm/crm_lead), the record is not reopened; history length 2 → 3 (the row's push only; ↓ and 「‹」 each replace).

CPU throttling and the keyboard are measured here only, per the card. Screenshots of every step (before and after) are in the project folder, not on GitHub.

Findings (not fixed here)

Each is measured in the after run and left to the card named.

Tests

  • ui/e2e/record-flow-phone-targets.spec.ts (new, Chromium): reads each control's class string from its source file, compiles Tailwind, and lays out a transcription of the record page (top bar with 「‹」 and ↓ ↑, tabs and +N, a section field with its pencil, the strip pencil, a related card head, menu, handle and rows, the comment toolbar, the save bar, the action bar). At 375×812 and 390×844 with touch: every control is at least 44×44, ends inside the viewport and is drawn at opacity 1; the page is no wider than the viewport; the top bar is at least 45 px. The mobile dialog fills the viewport and its close is 44 px. At 1280 with a mouse: a tab is 36, the card ⋯ 24, Save 36, ↓ 28, the strip pencil 20, and both pencils are at opacity 0. 5 passed.
  • RecordDetailView.phoneFace-27058.test.tsx: a new case, "「‹」 does not leave the record behind it in the history" (home → list → record, tap 「‹」, Back lands on the list with no record, Back again reaches home).

Acceptance notes

  • From sm up every control keeps its size; a mouse still reveals the pencil on hover (pinned in the spec at 1280).
  • The approval panel is Console at phone width: Approvals flow audit and fixes at 375 and 390 px #29070's and is not touched. Shared chrome (AppShell, ConsoleLayout, UnifiedSidebar, app switcher, nav groups) is not touched; no shared-chrome cause was found in the record flow.
  • Files outside the claim's declared surface (app-shell views, plugin-detail): components/src/custom/record-top-bar.tsx, components/src/custom/RecordTitleChip.tsx, components/src/renderers/layout/containers.tsx. Each is the record header's own control (the top bar, the copy-ID chip, the phone header's action bar), changed in class strings only, so no exported type moves and Clause-② stays no.
  • None of the files the card excludes is touched (navigation-overlay.tsx, record-list-face.tsx, ObjectView.tsx, recordOpenPreference.ts — feat(ui)!: a record opened from a list opens in the drawer by default, and the split pane retires #29108 changed them on main and merged in cleanly — ui/packages/mobile/**, the console's main / App / AppContent.tsx, sheet.tsx, dialog.tsx).
  • Diff against main: 22 files, 404 insertions, 66 deletions.

Verification

Head a8949249 merges origin/main at 104cd1a5. #29055 landed as 6ada509d and was merged in before the strip pencil was changed. The suites and gates below ran at 67398c1f (main at 4550091a); the last merge (104cd1a5, including #29108's drawer default) touched none of this PR's files, and at a8949249 the record-page phone, top-bar, recordPagePush and strip unit files (7 files, 48 tests), the e2e spec (5), the live Back check, check-sdui-manifest and the five PR-body gates were re-run: all green.

Tests
  • pnpm --dir ui exec vitest run packages/plugin-detail/ in three chunks under the verify lock, --maxWorkers=2: 367 files passed, 1 skipped; 3227 tests passed, 18 skipped.
  • app-shell: the 271 test files that import RecordDetailView, DeclaredActionsBar, ActionParamDialog, RecordAttachmentsPanel, recordPagePush or the top bar, plus the 24 components test files that import record-top-bar, RecordTitleChip or renderers/layout: 295 files, 2552 tests passed.
  • npx playwright test e2e/record-flow-phone-targets.spec.ts --project=chromium: 5 passed.
  • Ablations (scripts/ablation-replace.mjs, each restored to blob == HEAD with an empty git diff HEAD): TAB_CLASS given max-h-9 → the spec fails "tab Overview is 36px tall"; <Link to={to} replace → <Link to={to} → the new unit case fails (Back returned /apps/demo/crm_lead/record/r-1).
Type-check and lint
Derived gates

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran: 34 derived families, 34 run, 0 NOT-MEASURED, every one exit 0. That includes check-sdui-manifest and its --self-test, pnpm --dir ui exec vitest run scripts/ (169 files passed, 2 skipped; 4900 tests passed), check:query-options-erasure and check:type-check-debt under the verify lock (0 raw tsc errors above the ledger). The PR-body gates (check-changeset-no-major, check-closing-keyword-parity, check-adr-0087-registration, check-empty-changeset) ran on this body through GITHUB_EVENT_PATH.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QsR2jTg5fAobG4y7DkFwUE

… 44 px at phone width

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jTg5fAobG4y7DkFwUE
… param dialog at 44 px

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jTg5fAobG4y7DkFwUE
…'s history entry; top-bar steppers and View all reach 44 px

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jTg5fAobG4y7DkFwUE
… the record top bar grows past its border at phone width

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jTg5fAobG4y7DkFwUE
…44 px target on a phone

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jTg5fAobG4y7DkFwUE
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

席位核对 · ACCEPT · 2026-11-02T09:51Z

Head-sha: a8949249c04cd99a0e2366c120146c318b7c8e17


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 2, 2026 09:53
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit eed73a3 Nov 2, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-amh4li branch November 2, 2026 10:49
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 tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Console at phone width: Record flow audit and fixes at 375 and 390 px

1 participant