Repository navigation
Commit ee29ac3
feat(app-shell,components): on a phone the record is always a page, with a compact header, a bottom action bar and related records pushed as pages (#28477)
domain:ui#5 · os-sales · session_01L6bhgT93xhnhe2yKWX7YPc
Fixes #27058
Clause-②: yes (widening) — `@object-ui/components` gains
`PageHeaderPhoneContext` (+ type `PageHeaderPhoneFace`) and an optional
`PageHeaderActionsProps.face`; `MobileViewSwitcherContext` is not on the
`@object-ui/app-shell` entry, so its retirement narrows nothing.
Metadata-protocol: none
## 维护者速读
- **改了什么**:手机上(窗口宽度小于 768)记录永远是整页:列表点一行直接进记录页,不再弹全屏抽屉或分屏;列表的视图切换器「全部线索 ▾
· 5」挪到列表工具栏首位,顶部应用栏不再画第二个视图下拉;记录页顶栏只剩一个返回和计数 ↓ ↑,顶层页写「‹
对象列表」,推入的下一层写父记录名(「‹ Acme Corp」);头部用对象声明的前两个文本高亮做副标题,后面最多 4
个高亮;业务操作放进页面底部的操作栏(主操作、一个次操作、⋯);关联行和查找值整页推入,顶栏下有路径行「线索 Marie Curie ›
客户」,‹ 返回上一层并回到原来的滚动位置。
- **为什么改**:#27058(画布 60 的 RecordMobile 板):手机是 v4 记录页压到 390
px,再加三件只有手机才有的事:记录永远是整页、业务操作在底栏、关联记录整页推入带路径行。
- **风险与回滚**:只动 ui 包(`app-shell`、`components`),不碰 spec、不碰服务端;768 px
及以上一切照旧(每个新测试都带桌面对照)。`components` 新增一个宿主插槽和一个可选 prop,是 widening;退役的
`MobileViewSwitcherContext` 从未在包入口导出。回滚即 revert。
- **先看**:`ui/packages/components/src/renderers/layout/containers.tsx` 的
`barQueue`(底栏按钮怎么排、编辑何时进
⋯);`ui/packages/app-shell/src/views/RecordDetailView.tsx` 的 `phoneBack`
/ `phonePathRow` 和 `data-record-action-bar`
容器;`ui/packages/app-shell/src/views/objectViewRecordNavigation.ts` 的
`phone` 分支(`?recordId=` 换成整页)。
- **两分钟验证**:`pnpm --dir ui exec vitest run
packages/components/src/__tests__/page-header-phone-bar-27058.test.tsx
packages/app-shell/src/views/ObjectView.phoneRecordPage-27058.test.tsx
packages/app-shell/src/views/RecordDetailView.phoneFace-27058.test.tsx
packages/app-shell/src/views/__tests__/recordPagePush-27058.test.tsx
packages/app-shell/src/layout/__tests__/AppHeader.phoneNoViewDropdown-27058.test.tsx`,预期
5 个文件 31 个测试全绿。
- **需要你做的**:无,席位落地。
## What changes
Below 768 px (the `useIsMobile` breakpoint, read synchronously by the
new `hooks/usePhoneViewport.ts`, because what it decides is mounted, not
styled) the console is on a phone. What follows is the v4 record page
(#27524) in a container under 760 px plus the three things only a phone
adds. No metadata key, no reader preference, no `packages/spec` file.
### Target 1: the record is always a page
- `objectViewRecordNavigation.ts` answers the spec's own rule on a
phone: `deriveRecordSurface(objectDef, { viewport: 'mobile' })` →
`page`. A row tap navigates to the record route with the list as its
`from` (and the shown rows as its ↓ ↑ set, the #27524 hand-off).
- `ObjectView.tsx` mounts no `ObjectViewRecordOverlay` on a phone (no
split pane, no drawer, no maximize), and hands the list no `recordOpen`,
so the Display menu has no Open records section and a stored split /
drawer preference draws nothing.
- A `?recordId=` URL (a shared pane link) is replaced, not pushed, by
the record page.
- `console/AppContent.tsx`: a record created from the list opens with
the same surface rule (`viewport: 'mobile'` on a phone).
### Target 2: one view switcher
- Below `sm` (the header's own breakpoint, `useViewTabsFit`) the page
header is not drawn, so the #27594 title switcher is handed to the list
as `phoneTitle`: it leads the list's toolbar row (`ListView`'s existing
`toolbarLeading` slot) beside the filter / sort / display icons, or
heads a chart face. The header then gets no switcher, so there is
exactly one.
- The list's own view-type switcher (`showViewSwitcher`, the Display
menu's Layout row) is off there.
- `AppHeader` draws the page's name and no view dropdown;
`MobileViewSwitcherContext.tsx` is deleted, with its provider in
`ConsoleLayout.tsx` and its registration in `objectViewViewCrud.tsx`.
### Target 3: the top bar
`RecordDetailView`'s page bar on a phone is `RecordTopBar` with one way
back on the left and the counter + ↓ ↑ on the right when the page knows
its rows. No ⊟ (the stack has no right column) and no ⤡ (no drawer to
collapse to). On a top-level page the way back reads 「‹ OBJECT_PLURAL」
and returns to the list the page came from, else the object's list. On a
page one level down it is named by where it returns (Target 8, see
Acceptance notes): the parent record's title from the `?from=` trail (「‹
Acme Corp」), or its short id when the trail carries none; it links to
the trail's parent, or goes back in the history on a page pushed in this
session.
### Target 4: the header
For a default (synthesized) page, `views/recordPhoneHeader.ts` takes the
subtitle from the first two declared `highlightFields` whose type is
`text`, joined by 「 · 」 from the values the reader can see, and keeps at
most four highlights after them (`PHONE_HIGHLIGHTS_MAX`). The subtitle
reaches `page:header` through the host slot below and is drawn only when
the header declares no `subtitle` of its own. An object that declares no
highlights gets the derived strip and no subtitle; an authored page
draws its own header.
### Target 7: the bottom action bar
- `@object-ui/components` gains `PageHeaderPhoneContext`
(`renderers/layout/page-header-phone-context.ts`, beside
`PageHeaderTrailingContext`): a host slot, never metadata. The record
page provides it on a phone with its bar element and the subtitle.
- Under it `PageHeaderRenderer` draws no action row: the same
`PageHeaderActions` resolver renders in its new `face: 'bar'` and is
portaled (`createPortal`) into the host's bar, so the actions keep the
record's contexts. Nothing is drawn until the bar element exists.
- The bar face holds the primary action, one more and the `⋯`
(`PHONE_ACTION_BAR_BUTTONS = 2`; a page's declared `mobileMaxVisible`
still wins). Each button shares the bar's width; the host's Delete stays
in the `⋯`'s destructive band.
- The bar is the page column's last flex row (`border-t`,
`pb-[max(env(safe-area-inset-bottom),0.75rem)]`), under the scroller, so
the body ends where the bar starts. There is no FAB on a record page
(the FAB is the list's).
### Target 8: one level down
- `RelatedRecordActionsBridge.openRecord` on a phone navigates to the
record route with the `?from=` trail extended (as before) and the
history state `recordPagePushState` builds (`views/recordPagePush.ts`):
the parent page as `from`, the related list's rows as `from.siblings`,
and a `recordPagePushed` marker. No drawer stack is mounted on a phone
(`RecordDetailView` returns its page tree without
`RecordPageDrawerHost`).
- A path row under the top bar draws the trail (「线索 Marie Curie › 客户
Acme Corp › 商机」), each ancestor a link; on a pushed page the parent
entry and 「‹ Acme Corp」 go back in the history (`navigate(-1)`).
- `useRecordPageScrollMemory` remembers the page scroller's position per
history entry and restores it when the reader comes back by Back /
Forward, retrying while the panels load, for up to 3 s, until the reader
touches the scroller.
### Unchanged
768 px and up; the pane's split / overlay / maximize mechanics (#27545);
`navigation-overlay.tsx` and `recordDrawerStack.tsx` (not mounted on a
phone, not edited); the record page's layout rule (the stack below 760
px, the tab row's own width count, the enlarged tab's card rows: all
already read the page's own width and needed no change); `plugin-list`
(its `toolbarLeading` and `recordOpen` props already existed).
### File surface beyond the claim amendment 6548379901
- The phone context lives at
`ui/packages/components/src/renderers/layout/page-header-phone-context.ts`,
beside `page-header-trailing-context.ts`, not under `custom/` as the
amendment spelled it.
- `ui/packages/app-shell/src/views/viewTabLabel.ts` and one comment in
`ObjectView.tsx`: comment-only lines that named the retired mobile
switcher.
- Tests in `ui/packages/plugin-detail` and `ui/packages/plugin-grid`
(pins only, see Tests).
- Not needed and not touched: `objectViewRecordLinks.ts`,
`objectViewPageHeader.tsx`, `RecordPageColumns.tsx`,
`synth/recordPageColumns.ts`, `RecordTabRow.tsx`, `RelatedList.tsx`,
`record-top-bar.tsx`, the i18n packs (no key added; the bar reuses
`detail.goBack`, `common.breadcrumb` and the header's own keys).
## Tests
New (31):
- `components/src/__tests__/page-header-phone-bar-27058.test.tsx` (6):
one declared action → [Convert Lead][Edit][⋯] in the host's bar and no
row in the header; a declared second action takes slot 2 and Edit folds
into the `⋯` after the page's folded action, before Delete; a declared
`mobileMaxVisible: 1` sets the budget; nothing is drawn before the bar
element exists; the host subtitle stands in for an undeclared one and an
authored one wins; control: without the context the row face, Edit
inline.
- `app-shell/src/views/ObjectView.phoneRecordPage-27058.test.tsx` (5):
at 390 a row tap lands on the record route with `from.pathname` the list
and `from.siblings` its rows; a `?recordId=` URL is REPLACED by the
record route; a stored `split` / `drawer` preference draws nothing and
the Display menu has no Open records; control at 1440: the same
`?recordId=` opens the split pane.
- `app-shell/src/views/RecordDetailView.phoneFace-27058.test.tsx` (10),
a real `RecordDetailView` at 390: 「‹ Leads」 to the list, `1 / 3`, no
Panels group, no ⤡, no crumb; opened directly: 「‹ Leads」 and no counter;
subtitle 「Atlas Construction · Director of Sales」 and four highlights;
the bottom bar [Convert Lead][Assign][⋯] with Edit in the `⋯`, below the
body, the header with no toolbar; one declared action → [Convert
Lead][Edit]…; a pushed page: path row 「Lead Mira CostaTask」, 「‹ Mira
Costa」 a button, `1 / 3`, back to the parent, whose own way back reads
「‹ Leads」 again (both texts pinned in one case); the path row's parent
goes back too; a `?from=` page from a link: 「‹ Mira Costa」 and the path
row both link to the parent; a trail entry with no title: 「‹ #L-1」;
control at 1440: ⊟, the header's own row, no bar.
- `app-shell/src/views/__tests__/recordPagePush-27058.test.tsx` (9): the
bridge at 390 pushes a related row with the extended trail and `{ from:
{ pathname, label, siblings }, recordPagePushed: true }`, a lookup
without siblings; control at 1440: no state; the state helpers; Back
restores 286 and a fresh visit starts at 0; `phoneRecordHeader`'s rule
(text only, two at most, trimmed, masked values skipped, array-form
fields, no declared highlights → no subtitle).
-
`app-shell/src/layout/__tests__/AppHeader.phoneNoViewDropdown-27058.test.tsx`
(1): at 390 on a two-view list, no 「Switch view」 trigger, the page's
name only.
Pins updated to the phone face, not deleted:
- #27524: `RecordDetailView.recordPageTiers-26678` (the phone case now
sets a 390 px window as well as a 390 px root, and asserts the bottom
bar after the stack with no header toolbar; the 720 split-pane case
asserts the header's row and no bar);
`plugin-detail/src/__tests__/RecordTabRow-27524` (+1: the 366 px row of
a 390 px page shows Overview, Activity and two related tabs, then 「+2」).
- #27594: `ObjectView.titleViewSwitcher-27594` (+1: at 390 the one
switcher leads the toolbar row with its count, opens the same dropdown,
and switches the view).
- #26896: `plugin-grid/src/__tests__/groupedMobileCards-26896` (+1: a
tapped card hands its row to the host's `onRowClick`, the door the
phone's page navigation takes).
- The retired channel's tests: `ObjectView.objectListTitle-27950` lost
its `MobileViewSwitcherContext` probe; its phone case now pins the one
switcher in the toolbar row and none in the hidden header.
`ObjectView.chartViewTabs-13114`'s phone case pins the one switcher
heading the chart.
<details><summary>Ablations (each through scripts/ablation-replace.mjs,
restore proven: blob == HEAD and git diff HEAD empty)</summary>
At head `1511473e38`:
- `PHONE_BELOW_WIDTH = 768` → `0` (the phone signal off): 15 failed | 22
passed over the four phone files + the tiers pin + the 27594 pin. Every
phone case red; every control and every unit case green. The 27594 pin
stays green there by design: the toolbar title follows the header
breakpoint, ablated next.
- Four held together, one run: the bar queue with the host's buttons
before the page's own (`containers.tsx`), the subtitle reading no field
(`recordPhoneHeader.ts`), `phoneTitle` never set (`ObjectView.tsx`), the
scroll restore never armed (`recordPagePush.ts`): 10 failed | 35 passed.
Red: the Edit-folds case (components) and the bottom-bar case (record
page); the three subtitle unit cases and the header case; the three
phone switcher pins (27594, 27950, 13114); the Back-restores case.
At head `f33dd4f88c`:
- The way back one level down named by the object plural again
(`RecordDetailView.tsx` `phoneBack`: `? parent.t ||
shortRecordId(parent.i)` → `? plural`): 3 failed | 7 passed in
`RecordDetailView.phoneFace-27058`. Red: the pushed page (「‹ Mira
Costa」), the `?from=` link page, the untitled trail entry (「‹ #L-1」);
the two top-level 「‹ Leads」 cases stay green.
</details>
## Acceptance notes
**Screenshots** at 390 × 844 (Chromium mobile emulation, zh-CN) on
`examples/app-crm`, run from this branch's own source and server. They
are committed in `08421f1c82` and removed in the next commit, so they
never reach `main` (an earlier set, `74dd2d29a6`, predates the way-back
label change and is superseded). The folder's
[README](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/README.md)
says how they were made;
[`run.log`](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/run.log)
records each shot's route, the bar's box, the top bar's way back as
drawn and the scroll readings;
[`evidence.cjs`](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/evidence.cjs)
is the script.
| Shot | What it shows |
|:--|:--|
|
[01-lead-list](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/01-lead-list.png?raw=true)
| The lead list: 「全部线索 ▾ · 5」 leads the toolbar row with the filter and
display icons; card rows; the app bar draws no view dropdown. |
|
[02-lead-record](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/02-lead-record.png?raw=true)
| A row tapped, a top-level page: 「‹ 线索列表」, `5 / 5`, ↓ ↑; name and
object chip; four highlights in two columns; the stage path; the tab
row; the details; the bar [Convert Lead][编辑][⋯]. |
|
[03-lead-record-mid-scroll](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/03-lead-record-mid-scroll.png?raw=true)
| Scrolled to the middle of the body (scrollTop 106 of 212): the bar
stays at the bottom edge (`y 779 + h 65 = 844`). |
|
[04-lead-record-end](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/04-lead-record-end.png?raw=true)
| The end of the stack: the details, then the 动态 panel; the bar under
them. |
|
[05-lead-to-account-pushed](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/05-lead-to-account-pushed.png?raw=true)
| The lead's Account lookup tapped: Acme Corp pushed as a page, one
level down: 「‹ Marie Curie」 and the path row 「线索 Marie Curie › 客户」. |
|
[06-account-related-panels](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/06-account-related-panels.png?raw=true)
| That page scrolled to its related panels, in their order, with the bar
under them. |
|
[07-account-to-opportunity-pushed](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/07-account-to-opportunity-pushed.png?raw=true)
| A related opportunity row tapped: one more level, 「‹ Acme Corp」, 「线索
Marie Curie › 客户 Acme Corp › 商机」, `1 / 3` over the account's shown
opportunity rows; the tab row 「概览 · 动态 0 · 活动列表 0 · 线索列表 0 · +1 ▾」. |
|
[08-back-to-account](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/08-back-to-account.png?raw=true)
| 「‹ Acme Corp」: the account where it was left, scrollTop 315 → 315, its
own 「‹ Marie Curie」. |
|
[09-back-to-lead](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/09-back-to-lead.png?raw=true)
| 「‹ Marie Curie」: the lead where it was left, 212 → 212, the top-level
「‹ 线索列表」 again. |
|
[10-account-record](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/10-account-record.png?raw=true)
| An account from its list: 「‹ 客户列表」 `2 / 3`; the tab row 「概览 · 动态 0 ·
活动列表 2 · 联系人列表 1 · +2 ▾」 at 390 px; the bar [编辑][⋯] (the account
declares no action). |
|
[11-account-related-tab-active](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/11-account-related-tab-active.png?raw=true)
| 活动列表 active (`?related=crm_activity.account`): the child object's card
rows fill the body under the enlarged toolbar. |
|
[12-lead-bar-more](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/12-lead-bar-more.png?raw=true)
| The bar's ⋯: 刷新, 添加到收藏, 分享, then 删除 in its own band. |
|
[13-lead-header-declared-highlights](https://github.com/objectstack-ai/objectstack/blob/08421f1c820f2582c1e6c1524d23dfa557df177e/evidence/27058-phone-record-page/13-lead-header-declared-highlights.png?raw=true)
| **Rig only.** app-crm declares no `highlightFields`, so this shot
patches the served lead metadata in the browser (`page.route`; nothing
in the diff) with `company, title, email, phone, source, lead_score`:
the subtitle 「Radium Labs · Director of Research」, then four highlights.
|
**The two points the seat asked to see stated:**
- **The app bar (`AppHeader`) is kept on the phone record page.** The
board's frame starts at the record's top bar, but on a phone `AppHeader`
is the only door to the app menu (the sidebar trigger), search,
notifications and the account menu, on every route, the record page
included. The card names no other place for them, so retiring the bar on
record routes would leave a reader on a record page with no way to
another app or to search. What this PR removes from the bar is only the
second view dropdown (Target 2). If the preview gate wants the record
page without it, that is a separate decision about where those four
doors go on a phone.
- **Edit and the bar's second slot.** In the bar face the host's Edit is
one of the record's actions: it queues after the page's own actions
(`barQueue`: the primary, then the authored actions in their order, then
the host's non-destructive buttons). So a page that declares ONE action
shows [that action][编辑][⋯] (shot 02, Convert Lead); a page that declares
a second action shows [primary][second][⋯] and 编辑 is the first host
entry in the `⋯`, after the page's own folded actions (pinned in
`page-header-phone-bar-27058` and `RecordDetailView.phoneFace-27058`); a
page that declares none shows [编辑][⋯] (shot 10). On the desktop row
nothing changes: the host's buttons stay inline beside the authored
ones.
**Readings and deviations** (the first three agreed with the seat before
the build):
- **No avatar initials** in the phone header (hotcrm#1199, ruling A). An
object with a declared picture field still shows its picture through the
header's existing chip.
- **The subtitle** comes only from declared text `highlightFields` on a
default page; no declared list means no subtitle (app-crm declares none:
shots 02 and 13).
- **The bar is a flex row** at the foot of the page column, not
`position: fixed` with a `scroll-pb`. It stays visible while the body
scrolls (shot 03, bar box in `run.log` for every record shot), and the
body ends where the bar starts, which is what the `scroll-pb` was for.
- **The ‹ label: Target 3 governs the top level, Target 8 names the next
level** (the seat's reading). Target 3's 「‹ OBJECT_PLURAL」 is the way
back of a top-level page, which returns to the object's list. Target 8
says a page one level down returns to its parent record, so its 「‹」 is
named by that return target: the parent's title from the `?from=` trail
(「‹ Acme Corp」, as the board's pushed frame reads), else its short id. A
pushed page and a `?from=` page opened from a link read the same; back
on the parent, its own 「‹」 reads the object plural again. Both texts are
pinned in one case of `RecordDetailView.phoneFace-27058`.
- **The card's lead → task shots.** app-crm has no task object, so the
push is shown as lead → account (a lookup) → opportunity (a related
row), and the 任务 tab as the account's 活动列表 tab.
- **640–767 px** (a large phone in landscape, a small tablet): the
record is a page (below 768), and the page header with its title
switcher is drawn (from `sm`, 640), as at that width before.
- **Not built here:** the counter's group scope, as in #27524 ④
(`ListView` reports rows only for the ungrouped grid).
## Human Minimum
Each item is one rule from
[CONTRIBUTING.md › Human
Minimum](https://github.com/objectstack-ai/objectstack/blob/main/CONTRIBUTING.md#human-minimum),
which links the AGENTS.md text that states it.
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(`packages/spec` is not touched.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; eslint on the changed files: 0 errors.
- [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 (the 维护者速读 block is the
project's Chinese summary).
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
## Verification
All local readings are at head `15455616ba` unless named. Its tree is
identical to `f33dd4f88c` (`git diff f33dd4f 1545561` is empty:
the two commits after it add and remove only `evidence/`), so a reading
taken at `f33dd4f88c` is a reading of this tree. Against the merge base
`8e2d56dcce` the change is 31 paths, +2,042 / −348; the evidence folders
are added and removed inside the range, so they are not among them. `git
merge-tree` against `origin/main` (11 commits ahead of the base) is
clean.
<details><summary>Tests: 643 files, 4624 tests green over every touched
surface; tsc 0; eslint 0 errors</summary>
- The touched surfaces, one run under the verify lock: `pnpm --dir ui
exec vitest run --maxWorkers=3
packages/components/src/__tests__/page-header
packages/components/src/__tests__/snapshot-critical.test.tsx
packages/app-shell/src/views/ObjectView.
packages/app-shell/src/views/RecordDetailView.
packages/app-shell/src/views/__tests__ packages/app-shell/src/layout
packages/app-shell/src/console
packages/plugin-detail/src/__tests__/RecordTabRow
packages/plugin-grid/src/__tests__/groupedMobileCards`
- `Test Files 643 passed (643)` · `Tests 4624 passed (4624)` · VERDICT
command-exit 0 (at `1511473e38` the same set read 643 / 4623; the +1 is
the untitled-trail case).
- The five phone files plus the tiers pin, at `f33dd4f88c`: `Test Files
5 passed (5)` · `Tests 36 passed (36)`.
- `npx tsc --noEmit -p tsconfig.json` in `ui/packages/app-shell` at
`f33dd4f88c`: exit 0 (`ui/packages/components`, unchanged since
`1511473e38`: exit 0 there).
- eslint on every changed source and test file: 0 errors (warnings are
the files' existing `no-explicit-any`).
- The 34 derived gates include `pnpm --dir ui exec vitest run scripts/`:
`Test Files 169 passed | 2 skipped (171)` · `Tests 4901 passed | 2
skipped (4903)`.
</details>
<details><summary>Derived gate families: 34 derived, 34 run, every exit
0, 0 NOT-MEASURED (at f33dd4f, this tree)</summary>
- `node scripts/check-adr-0087-registration.mjs --base origin/main`:
exit 0
- `node scripts/check-adr-0087-registration.mjs --self-test`: exit 0
- `node scripts/check-changeset-no-major.mjs --base origin/main`: exit 0
- `node scripts/check-closing-keyword-parity.mjs`: exit 0
- `node scripts/check-closing-keyword-parity.mjs --self-test`: exit 0
- `node scripts/check-comment-mask-corpus.mjs`: exit 0
- `node scripts/check-empty-changeset.mjs --base origin/main`: exit 0
- `node scripts/check-empty-changeset.mjs --self-test`: exit 0
- `node scripts/check-sdui-manifest.mjs`: exit 0
- `node scripts/check-sdui-manifest.mjs --self-test`: exit 0
- `node scripts/pm/release-rehearsal-clone.mjs --self-test`: exit 0
- `node scripts/release-pending-publish.mjs --self-test`: exit 0
- `pnpm --dir ui exec vitest run scripts/`: exit 0
- `pnpm --filter @objectstack/spec run check:duration-unit-keys`: exit 0
- `pnpm check:changeset-gate-self-tests`: exit 0
- `pnpm check:closed-ceremony-text`: exit 0
- `pnpm check:cross-package-test-inputs`: exit 0
- `pnpm check:dispatcher-error-vocabulary`: exit 0
- `pnpm check:driver-memory-census`: exit 0
- `pnpm check:engine-double-contract`: exit 0
- `pnpm check:gitlink-declared`: exit 0
- `pnpm check:nul-bytes`: exit 0
- `pnpm check:pm-changeset-deadline-census`: exit 0
- `pnpm check:published-files`: exit 0
- `pnpm check:query-options-erasure`: exit 0
- `pnpm check:refd-timer-probe`: exit 0
- `pnpm check:skill-identifier-liveness`: exit 0
- `pnpm check:test-source-alias`: exit 0
- `pnpm check:tier-file-adoption`: exit 0
- `pnpm check:type-check-coverage`: exit 0
- `pnpm check:type-check-debt`: exit 0
- `pnpm check:undeclared-dep-imports`: exit 0
- `pnpm check:watch-hint-literal`: exit 0
- `pnpm check:where-matcher`: exit 0
`node scripts/pm/dispatch-gates.mjs --ran` over that record: 34 derived
famil(ies) accounted for, 34 run, 0 NOT-MEASURED, 0 UNRUN.
</details>
CI check-runs: not started at the time of writing.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01L6bhgT93xhnhe2yKWX7YPc)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01L6bhgT93xhnhe2yKWX7YPc
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 82b29fe commit ee29ac3
32 files changed
Lines changed: 2069 additions & 353 deletions
File tree
- .changeset
- ui/packages
- app-shell/src
- console
- hooks
- layout
- __tests__
- views
- __tests__
- components/src
- __tests__
- renderers/layout
- plugin-detail/src/__tests__
- plugin-grid/src/__tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
| 40 | + | |
40 | 41 | | |
41 | 42 | | |
42 | 43 | | |
| |||
962 | 963 | | |
963 | 964 | | |
964 | 965 | | |
965 | | - | |
| 966 | + | |
| 967 | + | |
| 968 | + | |
966 | 969 | | |
967 | 970 | | |
968 | 971 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
55 | 55 | | |
56 | 56 | | |
57 | 57 | | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | 58 | | |
62 | 59 | | |
63 | 60 | | |
| |||
93 | 90 | | |
94 | 91 | | |
95 | 92 | | |
96 | | - | |
97 | 93 | | |
98 | 94 | | |
99 | 95 | | |
| |||
483 | 479 | | |
484 | 480 | | |
485 | 481 | | |
486 | | - | |
487 | 482 | | |
488 | 483 | | |
489 | 484 | | |
| |||
942 | 937 | | |
943 | 938 | | |
944 | 939 | | |
945 | | - | |
946 | | - | |
947 | | - | |
948 | | - | |
949 | | - | |
950 | | - | |
951 | | - | |
952 | | - | |
953 | | - | |
954 | | - | |
955 | | - | |
956 | | - | |
957 | | - | |
958 | | - | |
959 | | - | |
960 | | - | |
961 | | - | |
962 | | - | |
963 | | - | |
964 | | - | |
965 | | - | |
966 | | - | |
967 | | - | |
968 | | - | |
969 | | - | |
970 | | - | |
971 | | - | |
972 | | - | |
973 | | - | |
974 | | - | |
975 | | - | |
976 | | - | |
977 | | - | |
978 | | - | |
979 | | - | |
980 | | - | |
981 | | - | |
982 | | - | |
983 | | - | |
984 | | - | |
985 | | - | |
986 | | - | |
987 | | - | |
988 | | - | |
989 | | - | |
990 | | - | |
991 | | - | |
992 | | - | |
993 | | - | |
994 | | - | |
995 | | - | |
996 | | - | |
997 | | - | |
998 | | - | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
999 | 946 | | |
1000 | 947 | | |
1001 | 948 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
32 | | - | |
33 | 32 | | |
34 | 33 | | |
35 | 34 | | |
| |||
212 | 211 | | |
213 | 212 | | |
214 | 213 | | |
215 | | - | |
216 | 214 | | |
217 | 215 | | |
218 | 216 | | |
| |||
339 | 337 | | |
340 | 338 | | |
341 | 339 | | |
342 | | - | |
343 | 340 | | |
344 | 341 | | |
345 | 342 | | |
This file was deleted.
0 commit comments