Skip to content

feat(app-shell): a phone record page draws one bar, its own top bar - #28883

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

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-aydadq

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #28483

Clause-②: yes (widening)
Metadata-protocol: none

维护者速读

  • 改了什么:手机(宽度小于 768 px)上打开一条记录,页面顶部只剩记录自己的那条栏(「‹ 对象复数」和 n / N ↓ ↑),不再叠一条应用栏,记录名只出现一次。应用菜单、搜索、通知、账号这四扇门留在列表、首页等其它页面,记录页按一次「‹」就回到有门的页面。记录打不开时(加载中、没找到、无权访问、读取失败等),手机上同样画这条「‹」栏,深链进来也总有路出去。
  • 为什么改:按裁决 6556587051(字母 A)执行。之前手机记录页顶部叠两条栏,上面那条又把记录名重复了一遍,与看板不符。
  • 风险与回滚:768 px 及以上完全不变。@object-ui/layout 的 AppShell 新增一个可选属性 showHeader(默认 true),不传时渲染与以前逐字节相同,所以是加宽(Clause-② yes (widening)),changeset 为 patch。回滚就是还原本 PR。
  • 先看:ui/packages/app-shell/src/layout/ConsoleLayout.tsx(按路由 + 视口决定不画应用栏的那一处)、ui/packages/app-shell/src/views/RecordDetailView.tsx(返回链接的常量上移到各个提前返回之前,withPhoneBar 包住每个提前返回的状态)、ui/packages/layout/src/AppShell.tsx(showHeader)。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/layout/__tests__/ConsoleLayout.phoneRecordOneBar-28483.test.tsx packages/app-shell/src/views/RecordDetailView.phoneGuardBar-28483.test.tsx packages/layout/src/__tests__/app-shell-show-header-28483.test.tsx,预期全部通过:390 px 记录路由零个应用栏、一条栏、记录名一次;768 px 与 1440 px 应用栏照旧。
  • 需要你做的:无,席位落地。

What changes

Below 768 px a console record page now has one bar at its top, its own top bar, and the record's name shows once, as the header title under it. This is the director's ruling 6556587051, letter A.

  • ConsoleLayout (@object-ui/app-shell) decides the app bar by route and viewport. On a phone (usePhoneViewport, 768 px, the breakpoint the record page draws its phone face at, read synchronously so the two never disagree for a frame) and on a record route, it tells the shell to draw no header. A record route is /apps/APP/OBJECT/record/ID or the same record on a named record page (.../page/PAGE), the two routes RecordDetailView draws (isRecordPagePath). The record's edit form (.../edit) is not one and keeps the app bar.
  • The four doors (app menu, search, notifications, account) stay in the app bar on every other route: the object's list, home, dashboards, the edit form. A phone record page reaches them through its 「‹」 link. No menu is added to the record top bar and there is no bottom navigation bar.
  • AppShell gains showHeader (@object-ui/layout, optional, default true). showHeader={false} draws no header element and does not mount navbar. Left out, the shell renders exactly as before, and a shell with no navbar still draws its empty bar, which an external host may rely on. Documented in the layout README's AppShellProps table and in the Header Bar section of content/docs/frontend/app-shell.mdx.
  • RecordDetailView's early states keep the 「‹」 bar on a phone. The page returns early while loading, for an unknown object, a named page that is not one of the object's record pages, a missing record, ended approval access, a refused read and a failed read. None of those drew the top bar, and with no app bar a phone deep link to such a record would have had no way off the page. The back-link values (pageTrail, objectListPath, originIsObjectList, phoneBack and the helpers they read) move above the guards unchanged. They are plain consts, so hook order does not change. On a phone withPhoneBar draws the phone RecordTopBar (the 「‹」 only) above each early state. From 768 px up those states render as before.
  • 768 px and up is unchanged. The existing AppHeader tests and RecordDetailView.phoneFace-27058 are untouched.

Tests

  • ConsoleLayout.phoneRecordOneBar-28483 (real ConsoleLayout + real AppShell): at 390 px a record route has zero header elements and one bar, the record's own, and the record name once; the named-record-page route the same; the root routes (app home, object list, a view, a dashboard) and the edit form keep the app bar at 390 px; control at 768 px and 1440 px: the record route keeps the app bar; 767 px is already the phone face; isRecordPagePath names the two record routes and nothing else.
  • RecordDetailView.phoneGuardBar-28483 (the real ObjectStackAdapter over a stubbed transport, recordReadFailure-11902's harness): at 390 px a 403, a 404 and a 500 each draw their state under one bar whose 「‹ Invoices」 links to the object's list; control at 1440 px: the refused state draws no bar.
  • app-shell-show-header-28483 (@object-ui/layout): showHeader={false} draws no header and does not mount navbar; controls: left out, true, and no navbar at all each draw the bar as before.
Runs (head 2e4d74e, after merging main)
  • The three new files: 3 files, 15 tests passed.
  • Ablations, each a one-line mutation through scripts/ablation-replace.mjs on the committed fix, restored to HEAD (blob equal, git diff HEAD empty), all red as expected:
    • ConsoleLayout: showHeader={!recordPageOwnsTopBar} to showHeader={true}: ConsoleLayout.phoneRecordOneBar-28483 3 failed / 4 passed (390 px record route, named page, 767 px).
    • RecordDetailView: withPhoneBar's phonePage ? to false ?: RecordDetailView.phoneGuardBar-28483 3 failed / 1 passed (the 403, 404 and 500 legs; the 1440 px control stays green).
    • AppShell: {showHeader && ( to {true && (: 4 failed / 7 passed across app-shell-show-header-28483 and ConsoleLayout.phoneRecordOneBar-28483.
  • Neighbours, narrowed (declared): every app-shell test that names ConsoleLayout, AppHeader or consoleTopBar, every RecordDetailView test that sets a viewport width, the console mobile (≤ 640 px): list as card rows with view chips and a FAB, the record page with a compact header, sticky tabs and a bottom action bar, related records pushed as full pages with a path row and back — the record is always a page on a phone #27058 phone files, the read-failure files, and the four RecordDetailView tests main changed: 145 files, 1039 tests passed, at --maxWorkers=2 in four batches. CI's Test UI runs the rest.
  • packages/layout/ whole: 47 files, 387 tests passed. Its two guide pins first went red on the new prop (the guide's AppShellProps table and a header-class read that matched a JSDoc mention); the table in content/docs/frontend/layout.mdx now names showHeader, and the JSDoc no longer spells the element.
  • turbo run type-check --filter=@object-ui/app-shell --filter=@object-ui/layout: 66 tasks successful.
  • pnpm --dir ui exec vitest run scripts/: 169 files passed, 2 skipped.

Acceptance notes

Verification

  • Derived gates: node scripts/pm/dispatch-gates.mjs --commands at head 2e4d74e (after merging main) names 66 families; all 66 ran locally with exit 0, and --ran reconciles 66 derived, 66 run, 0 NOT-MEASURED. check:skill-examples first exited 3 (its @objectstack/client-react prerequisite was not built); built, it exits 0. node scripts/check-sdui-manifest.mjs: exit 0 (no component config changed).
  • PR-body gates on this exact body: check-changeset-no-major --base origin/main, check-adr-0087-registration --base origin/main, check-closing-keyword-parity and check-partof-closing-keyword, all exit 0.
  • Not run locally, left to CI: pnpm lint, the full Test UI shards, dogfood (no ! in the title and no (narrowing) arm).

🤖 Generated with Claude Code

https://claude.ai/code/session_01QsR2jUBErc6JfKjjeojWHS


Generated by Claude Code

Below 768 px the console mounts no app bar on a record route; the record
page's back bar is the only bar. AppShell gains an additive showHeader
prop so the shell can draw no header element. The record page's early
states (loading, not found, refused, missing, access ended, forbidden,
failed) draw the same back bar on a phone, so a deep link always has a
way off the page.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jUBErc6JfKjjeojWHS
…ader; docs and changeset

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

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

席位核对 · ACCEPT · 2026-11-02T03:09Z

Head-sha: 2e4d74ec4bd3b14b22bee571708fc6f4336d3a66

  • Shape: draft → main. The first content line is Fixes #28483, and no other card sits beside a closing keyword. 10 files, +537 / −81, no governed path (check-governed-merges --pr 28883: NOT governed). The declaration lines are Clause-②: yes (widening) and Metadata-protocol: none. They are copied from amendment 6566728227 and start their lines in the body and the changeset. The widening is one optional prop, AppShellProps.showHeader (default true), on published @object-ui/layout. Both packages ship at patch, per AGENTS.md's level rule.
  • Read in the diff against ruling A (6556587051):
    • Below 768 px, ConsoleLayout sets showHeader={false} only on isRecordPagePath, which matches /apps/APP/OBJECT/record/ID and the same path with /page/PAGE. The record's edit form keeps the app bar.
    • The phone test is usePhoneViewport, the breakpoint the record page draws its phone face at.
    • Omitting showHeader renders exactly as before, including a shell with no navbar.
    • No menu is added to the record bar, and there is no bottom bar.
    • On a phone, every early state of RecordDetailView draws the ‹ bar: loading, not found, refused, missing, access ended, forbidden and failed. The back-link values moved above the guards as plain consts.
    • 768 px and up is unchanged.
  • Gates (check-runs on this head): 51 success, 8 skipped, 0 failed or pending. mergeable_state is clean. The head carries main after feat(rest,console): a record open is one request; the page payload carries the record's streams and the related rows' names #28682 (Record page open: the discussion, the activity feed, the attachment count and the related rows' reference names arrive in the record's page payload, so one open is one request plus the shell's own reads #28470), which edits RecordDetailView.tsx, so the serial note is met.
  • Tests (dev): 3 new files with 15 tests: the one-bar pin at 390 px, the guard-state bar, and showHeader with an omitted-prop control. Three ablations went red and were restored. 145 neighbouring app-shell files (1039 tests) and packages/layout (387 tests) pass. The type-check passes 66 of 66. Derived gates: 66 of 66 exit 0.
  • Accepted deviation: content/docs/frontend/layout.mdx gains the showHeader row. Its pin, guide-layout-app-shell-doc, reads AppShellProps from source and went red without it. This is the same docs item the amendment names.
  • Out of scope / NOT MEASURED: an AppSchemaRenderer with no navbar still draws an empty bar. That is unchanged by design, and the seat did not file it. The seat did no live phone run. Skills-docs: is not owed.
  • Landing: ordinary queue landing, pr_ready then automerge_enable. Fixes #28483 closes the card on merge.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 2, 2026 03:11
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 2, 2026 03:11
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit b15b634 Nov 2, 2026
60 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-aydadq branch November 2, 2026 03:32
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/l tooling

Projects

None yet

1 participant