Repository navigation
feat(plugin-detail,app-shell): the default record page is one page at two widths — resident columns on a wide page, the tabs in a drawer - #26758
Merged
objectstack-fleet[bot] merged 6 commits intoOct 27, 2026
Conversation
…n tiers by container width Claude-Session: https://claude.ai/code/session_cdb7a717-def7-4e71-9988-f089fbaa18ff Co-authored-by: Claude <noreply@anthropic.com>
…partition and the related rail Claude-Session: https://claude.ai/code/session_cdb7a717-def7-4e71-9988-f089fbaa18ff Co-authored-by: Claude <noreply@anthropic.com>
…ds and collapses folded lists Claude-Session: https://claude.ai/code/session_cdb7a717-def7-4e71-9988-f089fbaa18ff Co-authored-by: Claude <noreply@anthropic.com>
…d-pages guide; changeset; live specs pin the tabbed tier Claude-Session: https://claude.ai/code/session_cdb7a717-def7-4e71-9988-f089fbaa18ff Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_cdb7a717-def7-4e71-9988-f089fbaa18ff Co-authored-by: Claude <noreply@anthropic.com>
…les as a complete host component Claude-Session: https://claude.ai/code/session_cdb7a717-def7-4e71-9988-f089fbaa18ff Co-authored-by: Claude <noreply@anthropic.com>
Contributor
Author
席位核对 · PR #26758 ← #26678 · epic PM
|
objectstack-fleet
Bot
deleted the
claude/issue-26678-record-page-two-widths
branch
October 27, 2026 23:08
This was referenced Oct 27, 2026
Merged
This was referenced Oct 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #26678
Clause-②: no
Metadata-protocol: none
Part of the record-page-v2 epic #26676 (child 2 of 4). The drawer top bar and in-place navigation (#26679) and the open-mode metadata (#26677) are not addressed here.
What this does
The maintainer's ruling 1 on the epic, verbatim: 「抽屉和整页是同一张页在两个宽度。」 The synthesized default record page (no authored page, or a slotted page that keeps the default tab strip) is now laid out by the width of the view it occupies, never the viewport:
rightRailslot, Approvals, Attachments, the created / updated footer)One header (title, actions, highlights, stage path) serves every tier.
?tab=keeps working in the tabbed tier and is ignored by the columns. An authored record page (kind: 'full') and an authoredslots.tabsstrip are drawn as authored at every width.Design: the columns are derived, never authored
No metadata key, page template or node type is added, and
packages/specis untouched. The page description stays the tabbed onebuildDefaultPageSchemawrites, which is also what Studio's page-create seeds and what a slotted page fills. A columns node would have been a new type inside the spec's closedrecord:/page:namespaces: refused at Studio's save, and a type an author or an AI could write that the spec could not judge. So the column layout is read off the synthesized page by each tab's stablevalue(details→ details;activity,history→ timeline after the discussion footer; every related tab,approvals,attachments→ related; theasideregion folds into the related column, one rail, not two). A strip the partition cannot read (an unknown value, avisibleWhenitem) answersnulland keeps its tabs. A test enumerates every valuebuildDefaultTabsemits under all threerelatedLayouts, so a new synthesized tab without a column goes red there.showReferenceRail/hideReferenceRailareBuildPageOptions(TypeScript options of the synthesizer), not spec keys, and their meaning is unchanged: in the tabbed tier the rail is today'shidden xl:flexaside; in the column tiers the same aside content sits in the related column.Files
ui/packages/plugin-detail/src/synth/recordPageColumns.ts(new):recordPageTierFor,recordPageColumnOfTab,buildDefaultPageColumns, the 1280 / 1024 floors.ui/packages/plugin-detail/src/useRecordPageTier.ts(new): measures the container (layout effect plusResizeObserver), re-renders only on a tier flip, answersnulluntil the first reading so the host never mounts the tab tree only to replace it.ui/packages/plugin-detail/src/RecordPageColumns.tsx(new): the column tiers; frame mirrors the tabbed page frame so the header does not move between tiers; the two-column rail toggle (aria-expanded,aria-controls, Escape closes back to the toggle); folded empty related lists collapse out of the column's gap.ui/packages/plugin-detail/src/relatedListCompact.ts(new) +RelatedList.tsx: inside the related column a list keeps its identity column only and lets its header wrap. Without it the 20rem column cut each table off at its edge and truncated titles to one letter. Anywhere else a related list is unchanged.ui/packages/app-shell/src/views/RecordDetailView.tsx: measures its own root, rendersRecordPageColumnsin the column tiers (tabbed path untouched), moves the provenance footer into the columns there, and treats History and the feed as on screen at open in a column tier (the audit read no longer waits for a tab click that cannot happen).ui/packages/plugin-detail/src/index.tsx,useDetailTranslation.ts(detail.relateddefault, byte-identical to theenpack).ui/packages/plugin-detail/README.md(new section, reference-rail condition 3 note),ui/content/docs/guide/slotted-pages.md.ui/e2e/live/detail-related-list.spec.ts,ui/e2e/live/record-history-display.spec.ts: both click a tab on a full record page under Desktop Chrome's 1280 px viewport, which leaves the record view at the two-column floor once the console sidebar takes its share; each now pins a 1200 px viewport to stay in the tabbed tier they test..changeset/26678-record-page-two-widths.md(patch,Metadata-protocol: none).Screenshots
HotCRM (
1f3f8880, platform 18.2.0 backend) with this branch's console on a Vite dev server. Premise correction: a HotCRM lead renders its authoredlead_detailpage (kind: 'full',isDefault: true), which the ruling leaves untouched, so it cannot show the tiers. The shots use a contact and a task, objects with no authored record page. Container width = the record view's measured width; the console sidebar takes 256 px of the viewport.Evidence branch
claude/issue-26678-evidence-20261027at5c2f4343db70bd213abef0895804cb66f02df5d5(never merged).Reading worth a look: with the console sidebar open, a 1440 px screen gives the record view 1184 px, so it lands in the two-column tier. Three columns need about 1536 px of screen with the sidebar open, or a collapsed sidebar. The floors are the card's (1280 / 1024 of container).
Tests
Readings at
177cf60f6f(branch head after mergingorigin/main4373103165, an unrelated auth change); the README snippet fixecfa2c5574re-ranpnpm --dir ui run check:doc-snippets(803 of 803 blocks judged, 0 failed), the derived 33 families (all exit 0) andcheck:new-line-citations(0 new) atecfa2c5574— this line edited by the seat on the dev's report, the body is otherwise the dev's:pnpm --dir ui exec vitest run packages/plugin-detail/src/synth/__tests__/recordPageColumns.test.ts packages/plugin-detail/src/__tests__/RecordPageColumns.test.tsx packages/plugin-detail/src/__tests__/RelatedList.compactColumn-26678.test.tsx packages/app-shell/src/views/RecordDetailView.recordPageTiers-26678.test.tsx: 4 files, 25 tests passed.RecordDetailView.recordPageTiers-26678: 1440 px is three columns, no tablist, related lists in the related column, thesys_audit_logread at open; 1100 px is two columns, the toggle, lists mounted only on open; 900 px and embedded at 1440 px are the tab strip, with no audit read at 900;?tab=relatedselects the tab at 900 and is ignored at 1440.packages/plugin-detail/: 316 files passed, 1 skipped; 2917 tests passed, 18 skipped.packages/app-shell/src/views/RecordDetailView.*(72 files, 489 tests) plus the 121 other app-shell test files that import the record view,plugin-detailorDETAIL_DEFAULT_TRANSLATIONS(1312 tests): all passed. Declared narrowing: the remaining app-shell files are CI'sTest UI.pnpm --filter @object-ui/plugin-detail run type-check,pnpm --filter @object-ui/app-shell run type-check: exit 0 (on the pre-merge head; the merge touched onlyui/packages/auth).?tab=, related-tab order,hideRelatedTab,referenceRailRoute-9816) pass unchanged: jsdom measures 0 px, which is the tabbed tier.Ablations (one-shot, through
scripts/ablation-replace.mjs, restore proven by blob equal toHEADand emptygit diff HEAD; the tests resolve@object-ui/*to source throughui/vitest.config.mts, so nodistwas involved):recordPageTierForansweringtabsat 1280 px and up: 3 tests red (the tier boundary, the 1440 px mount, the?tab=case). Restored.Local gate subset (derived by
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackover this branch's 17 changed paths): all 33 node /pnpm check:*families exit 0.pnpm check:new-line-citations(ui): 0 new citations. ESLint on every changed ui file: 0 errors. The 34th family,pnpm --dir ui exec vitest run scripts/, exits 1 here for two host reasons, neither a reading of this change, so it is NOT MEASURED locally and CI's run is the reading:select-affected-tests-wiring(2 tests) runs abashscript usingmapfile, which macOS's stockbash3.2 lacks (exit 127);check-i18n-dead-keys(2 tests: "POSITIVE: every property-access-read key names LoadingScreen.tsx as a hit", "keeps every key it makes reachable OUT of both tiers") timed out at the 15 s budget scanning the real tree on this machine (16 s and 656 s). The other 176 files and 5062 tests pass.detail.related, the one key this change reads anew, was already read by the tab strip's label map.Verification ran in declared unlocked mode:
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable flock on this host, so the shared verify lock was NEVER taken and NOTHING was serialized.Acceptance notes
page:headeraction row: putting it there would need a host slot in@object-ui/components' header.detail.viewAlltext), not 「查看全部 N 条 →」; the count is in the card header.DetailTabs.tsx(named in the card) draws the legacyDetailViewstrip; the synthesized page's strip ispage:tabs(PageTabsRendererin@object-ui/components), so it is not part of this change.Generated by Claude Code