You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 07eaec2
Browse filesBrowse the repository at this point in the historyBrowse files
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} -->
_Requested by **Steedos** · [project
thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbK4VDoUnQep5cHGSLk5j5J5)_Fixes#29075
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:手机宽度(375/390
px)下,侧边抽屉里点任一入口后抽屉会自动关上;顶栏的按钮(侧栏开关、应用图标、搜索、收件箱、头像等)和它们打开的菜单行在 768 px
以下都放大到 44 px 触控尺寸。为放得下,640 px 以下应用内顶栏不再画产品图标和 Studio
按钮,两者仍在应用切换菜单里(「Back to home」和 Studio 一行),抽屉的「All apps」也能回首页。
- **为什么改**:Mobile 2.0 用 WebView 承载
Console(#29040),手机用户只有响应式布局。实测:点抽屉入口后新页面被仍然打开的抽屉盖住,要再点一次;顶栏控件只有 28–34
px,菜单行 32/38 px,低于 44 px。
- **风险与回滚**:低。只改 `@object-ui/app-shell` 的 7 个布局文件的 `max-md:` / `max-sm:`
类名,加一个按路由关闭手机抽屉的 effect;768 px 以上外观与行为不变(1280 px 实测尺寸与 main 相同)。回滚即
revert 本 PR。
- **先看**:① `ui/packages/app-shell/src/layout/UnifiedSidebar.tsx` 新增的
`routeKey` effect;② `ui/packages/app-shell/src/layout/AppHeader.tsx`
产品图标与 Studio 按钮的 `max-sm:hidden`;③
`ui/packages/app-shell/src/layout/AppSwitcher.tsx` 的触发器与菜单行。
- **两分钟验证**:`pnpm dev:crm`,浏览器 375 px 打开 Accounts:顶栏是 ☰ · 应用图标 · 标题 · 搜索
· 铃铛 · 头像;点 ☰ 再点 Contacts,抽屉自动关上,直接看到联系人列表。
- **需要你做的**:无,席位落地。
## What changes
The Navigation flow of the Console at phone width, audited at 375×812
and 390×844 on `examples/app-crm` and `examples/app-showcase` (card
#29075; parent audit #29040).
- **The phone drawer closes on navigation** (`UnifiedSidebar.tsx`).
Below 768 px the sidebar is a drawer over the page. A tapped entry
opened its page under a drawer that stayed open, so every navigation
cost a second tap. The sidebar now closes the drawer when the route
(pathname and search) changes. It is keyed on the route, not on one
row's handler, so navigation, favorite and recent rows and any other
control that changes the route all close it. Wider windows are
unchanged: the effect acts only while the sidebar is in its mobile form.
- **44 px controls below `md`.** `max-md:size-11` on the sidebar trigger
(class passed by `AppHeader`), the product mark, search, the Studio
buttons, the AI button and the account avatar (`AppHeader.tsx`), the
inbox bell (`InboxPopover.tsx`, the trigger's class only), the theme and
language buttons (`ModeToggle.tsx`, `LocaleSwitcher.tsx`) and the page
header's Refresh (`PageHeader.tsx`). The app pill gets `max-md:min-h-11
max-md:min-w-11` (`AppSwitcher.tsx`).
- **44 px menu rows below `md`.** The app switcher, account, theme and
language menus carry `max-md:[&_[role=menuitem]]:min-h-11` on their
content, so every row they hold, including rows drawn from metadata
(hidden apps in the account menu), is 44 px tall. The dropdown primitive
(`ui/dropdown-menu.tsx`) is not edited.
- **Room in a 375 px bar.** Seven 44 px controls left about 30 px for
the page title. Below 640 px an app's bar therefore draws neither the
product mark (and its `/` rule) nor the Studio button. Both stay one tap
away: the app switcher's menu has "Back to home" and the Studio row (the
bar hands it the same target), and the drawer's "All apps" goes home.
Home and the other platform surfaces keep the product mark, at 44 px;
the empty trigger slot there is not drawn below 640 px. Where a host
hides the switcher for a single app, Studio is not offered on a phone
bar; Studio is a desktop authoring surface.
- No prop, export, i18n key or metadata changes.
## Run record
Production Console build served at `/_console` by `objectstack dev
--fresh` for "before" (main 6b3ba82); the Vite Console on the branch
source against the same backends for "after". Chromium with `isMobile`
and touch, DPR 2. `touch-44` lists every visible interactive element
under 44 px on either side in the named scope. The keyboard check
focuses the search field and shrinks the viewport by 40 % (Android
`adjustResize`); iOS `visualViewport` is not simulated.
| Check | Page | crm 375 | crm 390 | showcase 375 | showcase 390 | Note
|
|---|---|---|---|---|---|---|
| touch-44 | Home (/home) | FAIL | FAIL | FAIL | FAIL | app bar fixed;
page body: separate card |
| no-horizontal-scroll | app landing | pass | pass | pass | pass | |
| touch-44 | app landing, app bar | FAIL → pass | FAIL → pass | FAIL →
pass | FAIL → pass | |
| touch-44 | app switcher menu | FAIL → pass | FAIL → pass | FAIL → pass
| FAIL → pass | |
| overlay-fits | app switcher menu | pass | pass | pass | pass | |
| touch-44 | drawer | pass | pass | pass | pass | |
| overlay-fits | drawer | pass | pass | pass | pass | |
| toggle | drawer nav group | pass | pass | pass | pass | |
| no-horizontal-scroll | drawer scrolled | pass | pass | pass | pass | |
| back-closes-drawer-stays-on-page | Back with drawer open | FAIL | FAIL
| FAIL | FAIL | card filed |
| drawer-closes-and-navigates | drawer entry tap | FAIL → pass | FAIL →
pass | FAIL → pass | FAIL → pass | |
| back-returns | Back after a drawer navigation | pass | pass | pass |
pass | |
| overlay-fits | search palette | pass | pass | pass | pass | |
| touch-44 | search palette | FAIL | FAIL | FAIL | FAIL | Close 16 px:
dialog.tsx, seat 4 |
| focused-field-visible | search, 40 % keyboard | pass | pass | pass |
pass | |
| touch-44 | account menu | FAIL | FAIL | FAIL | FAIL | left: two
notification switches, Notifications card |
| no-horizontal-scroll | object list | pass | pass | pass | pass | |
| touch-44 | object list | FAIL | FAIL | FAIL | FAIL | left: list
toolbar, Lists card |
| no-horizontal-scroll | record page | pass | pass | pass | pass | |
Other checks of the card:
- **No horizontal page scroll:** pass in every state measured (home,
landing, switcher open, drawer open, group toggled, drawer scrolled,
after navigation, search, keyboard, account menu, list, record), both
examples, both widths.
- **Dialogs and drawers fit the viewport:** pass (drawer 288 px wide,
switcher, palette and account menu inside the viewport).
- **Virtual keyboard:** pass (search field 69 to 127 px from the top
with the keyboard up).
- **Long lists at CPU 4×** (production build; click to the first painted
frame after the drawer mounts): drawer open on app-crm 107 to 253 ms, on
app-showcase (46 entries) 294 to 576 ms; nav-group toggle 173 to 252 ms;
20 frames of drawer scroll 316 to 524 ms. Drawer open on the larger app
is over a 200 ms interaction budget and is not changed here: #29141.
- **Back:** Back after a drawer navigation returns to the previous page
(pass). Back with the drawer open leaves the page instead of closing the
drawer: #29131. On the record page the phone ‹ is a link, so Back after
‹ reopens the record; routed to the Record flow with seat 4.
Screenshots before and after each fix are in the project folder
`issue-29075/` (`before/`, `after/`), with the run record
`run-record-before.md` and the script `audit.cjs`.
## Tests
- New `UnifiedSidebar.phoneDrawerCloses-29075.test.tsx` (4 tests, real
`SidebarProvider` and `@object-ui/layout`): a tapped drawer entry closes
the drawer; a navigation from outside the drawer closes it too; the same
route leaves it open; CONTROL, a wide window's sidebar is unchanged by a
route change.
- New `AppHeader.phoneTargets-29075.test.tsx` (8 tests, harness of
`AppHeader.sidebarTrigger-12429.test.tsx`): the 44 px classes on the
bar's controls in an app and on Home; the product mark, its rule and the
Studio buttons hidden below `sm` inside an app only; CONTROL, the
trigger, search and avatar are never hidden; the menus' row class and
the controls drawn by `AppSwitcher`, `ModeToggle`, `LocaleSwitcher`,
`InboxPopover` and `PageHeader`, read from their source.
- Ablation (`scripts/ablation-replace.mjs`, restored to HEAD
byte-identical by blob hash): the drawer close made a no-op (`if
(isMobile && Date.now() < 0) setOpenMobile(false);`) reds 2 of 4 in the
drawer test, the two navigation cases.
Run from `ui/` under `scripts/pm/os-verify-lock.sh`, head 0564756:
- `pnpm exec vitest run --maxWorkers=1` on the two new files: exit 0, 2
files, 12 tests.
- `pnpm exec vitest run --maxWorkers=2` on the 79 existing
`layout/__tests__` files for `AppHeader`, `AppSwitcher`,
`UnifiedSidebar`, `PageHeader`, `ConsolePageHeader`, `InboxPopover` and
`LocaleSwitcher` (including `AppHeader.homeMark-12605`, which pins the
product mark's classes): exit 0, 79 files, 512 tests (run at 7a92e5d;
0564756 changes only the two new test files).
- `pnpm exec eslint` on the ten changed files: no errors; the warnings
are the files' existing `any` and hook warnings, none on a changed line.
## Acceptance notes
- Claim 6571165367, amendments 6571821121 and 6572041449 (the file
surface: the seven layout files, two tests, the root changeset).
- Not fixed here: Back with the drawer open, #29131; drawer open over
budget at CPU 4×, #29141; the record page ‹ and Next and the palette
Close (`dialog.tsx`), seat 4's Record and Overlay cards; the list
toolbar, #29074; the notification switches, #29076; the home page body's
small links, a card the seat is filing.
- `LocalizedSidebarTrigger.tsx` was in the amended surface and needed no
edit: `AppHeader` passes the class.
- The derived gate `check-sdui-manifest` is green: no component config
changed.
## Verification
- `node scripts/pm/dispatch-gates.mjs --ran`: 34 derived, 34 run, 0
NOT-MEASURED, 0 UNRUN; every recorded exit is 0. The 30 gate commands
and `pnpm --dir ui exec vitest run scripts/` (169 files passed, 2
skipped) ran at 7a92e5d; 0564756 touches only the two new test files.
- `node scripts/check-changeset-no-major.mjs --base origin/main`, `node
scripts/check-adr-0087-registration.mjs --base origin/main`, `node
scripts/check-closing-keyword-parity.mjs` and `node
scripts/check-partof-closing-keyword.mjs`, with this body as the event:
exit 0 each.
- `node scripts/check-sdui-manifest.mjs`: exit 0.
- `pnpm exec turbo run type-check --filter=@object-ui/app-shell
--concurrency=2` at 0564756: exit 0, 65 of 65 tasks.
- Ablation re-run at 0564756: 2 of 4 red, restored byte-identical to
HEAD (blob acf4039).
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(not touched)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes. (CI runs the repo lint)
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (not touched)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QsR2jRQob8nVK5owwvNtfr)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
fix(app-shell): on a phone the navigation drawer closes when an entry opens its page, and the top bar's controls and menus are 44 px touch targets
6
+
7
+
Clause-②: no
8
+
Metadata-protocol: none
9
+
10
+
The Console at phone width (375 and 390 px), as a WebView host shows it:
11
+
12
+
-**The drawer closes on navigation.** Below 768 px the sidebar is a drawer over the page. Tapping one of its entries used to open the page under a drawer that stayed open, so every navigation cost a second tap. A route change now closes the drawer, whichever entry (navigation, favorite, recent) or other control changed the route. Wider windows are unchanged.
13
+
-**44 px controls below `md`.** The top bar's sidebar trigger, product mark, app pill, search, Studio button, inbox bell, AI button and account avatar measured 28 to 34 px; they are 44 px below 768 px. So are the theme and language buttons in the account menu and the page header's Refresh button. The rows of the app switcher, account, theme and language menus are 44 px tall there too (they were 32 and 38 px).
14
+
-**A roomier phone bar.** Below 640 px an app's bar no longer draws the product mark (and its `/` rule) or the Studio button, which made room for the 44 px controls and the page title. Both stay one tap away: the app switcher's menu has "Back to home" and the Studio row, and the drawer's "All apps" goes home. Home and the other platform surfaces keep the product mark.
0 commit comments