Repository navigation
feat(app-shell): one top bar above the record header on every face, the enlarged related list draws a toolbar, and Activity counts on a deep link - #28458
Conversation
The page, the split pane, the drawer and the maximized pane draw one 40 px bar of frame controls: the breadcrumb on the left (object › record, or the path one level down), and on the right the counter with ↓ ↑, the right column's switch, ⤢ / ⤡ and ✕, each only where it applies. The crumb row above the page header goes; a page the list row's ↗ opened steps that list's rows. The enlarged related list draws a toolbar over its table, and the Activity tab names its count on a link that opens another tab. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgVP3twvBvDTwoLPU6J
… and the Activity count Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgVP3twvBvDTwoLPU6J
… Activity count on a deep link Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgVP3twvBvDTwoLPU6J
…rough that list's rows A list whose rows open as pages now hands the rows it shows in the page's `from` state, as the row's ↗ already did in its way back to the pane. The page's top bar draws the counter and ↓ ↑ for it and steps with `replace`, keeping the same list; ⤡ stays the ↗ arrival's alone. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgVP3twvBvDTwoLPU6J
…new tests' casts without any Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgVP3twvBvDTwoLPU6J
… row ↗'s Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgVP3twvBvDTwoLPU6J
… the next commit) Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgVP3twvBvDTwoLPU6J
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L6bhgVP3twvBvDTwoLPU6J
…ge selects
fr, es and pt gain the `many` slot, and ar's `one` and `two` slots carry
`{{count}}` as en's do. The residue pin counts the enlarged toolbar's Add as
RelatedList's fourth `detail.add` site, and the FLS pin reads the top bar's
crumb as the second place the record's gated name appears.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L6bhgVP3twvBvDTwoLPU6J
|
domain:ui#5 · os-sales · session_01L6bhgUhKx1XHzkoikGjRB4 Contract reviewServed-tier: ① Derived judgmentsThe face is the ② Semver levelpatch for every package, as the changeset declares: additions only, every new prop optional; a caller of ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:ui#5 · os-sales · session_01L6bhgUhKx1XHzkoikGjRB4 ACCEPT · 2026-10-31T18:10Z 席位核对
Generated by Claude Code |
domain:ui#5 · os-sales · session_01L6bhgVP3twvBvDTwoLPU6J
Requested by Sales · project thread
Fixes #27524
Clause-②: yes (widening:
@object-ui/componentsgainsRecordTopBar,RecordTopBarButton,useRecordStepKeysand the typesRecordTopBarProps/RecordTopBarStepper;NavigationOverlaygains an optionaltogglesprop and itsstepperan optionalpositionandscope;@object-ui/i18ngains eightdetail.*keys in all ten locales; no entry export retires and no accept set moves. The claim's provisionalnois corrected here, measured on the diff.)Metadata-protocol: none
维护者速读
Fixes #27524。PanelToggles、PageHeaderTrailingContext都保留,宿主自己的普通覆盖层仍把 ⊟ 放在记录头末尾),所以是 widening。整页上的计数和 ↓ ↑ 只在从列表打开时出现(行的 ↗,或行本身就开整页的列表,比如 HotCRM 线索),用replace换页,不增历史;⤡ 只给 ↗ 打开的整页。回滚即 revert 本 PR。ui/packages/components/src/custom/record-top-bar.tsx(顶栏和 j / k 归属);ui/packages/app-shell/src/views/RecordDetailView.tsx的pageLeading/pageBar;recordDrawerStack.tsx的useRecordPageReturn和RecordPaneBarToggles;ui/packages/plugin-detail/src/RelatedList.tsx的enlargedFace。pnpm --dir ui exec vitest run packages/components/src/__tests__/record-top-bar-27524.test.tsx packages/app-shell/src/views/RecordDetailView.topBar-27524.test.tsx packages/plugin-detail/src/__tests__/RelatedList.enlargedToolbar-27524.test.tsx packages/plugin-detail/src/__tests__/RecordPageColumns.tabRow-27524.test.tsx packages/app-shell/src/views/objectViewRecordNavigation.listRows-27524.test.tsx,预期 5 个文件 34 个测试全绿。What changes
Record page v4, Target 7 and the ⊟ half of Target 4, plus the two ride-alongs ruled at ③'s preview gate. PR ④ of the four serial PRs for #27524, cut from
mainafter ③ (#28394,bf74607954).The top bar (Target 7)
RecordTopBar(@object-ui/components, newcustom/record-top-bar.tsx) is one 40 px row above the record header. The page, the split pane, the overlay drawer, the maximized pane and a phone all draw this one component.?from=origin, without the record the list had open) or else to the object's list.?from=trail draws ← and the path, and each ancestor links to its own page.RecordPaneBarContext.reportTitle.handleNavOverlayNavigate) now hands the shown rows in the page'sfromstate when the record is one of them, and the ↗ also hands them in its way back to the pane. ↓ ↑ andj/kreplace the page with the sibling's page, keep the list's state, and drop?related=. On the page the arrow keys still scroll.j/kuntil it closes (useRecordStepKeyskeeps an owner stack).⊟ in the top bar (Target 4)
The right-column switch (「右栏」, ⌘⇧,) leaves the header's action row.
RecordPaneBarTogglesinto the bar'sdata-record-pane-togglesslot).PanelToggles.InterfaceListPage,ObjectDataPage) has no bar slot. It keeps ⊟ at the end of the header's action row throughPageHeaderTrailingContext, so that export stays.Ride-along (a): the enlarged related list draws the board toolbar
When a related list is the active tab,
RecordPageColumnsprovidesRelatedListEnlargedContext.RelatedListthen draws:role="toolbar") over the table, in place of the card header and the card foot. The toolbar holds:Each chip is drawn only where the child object declares what it says:
owner_idcolumn. It filters with{ owner_id: '{current_user_id}' }.date/datetimefield declaresdueLike: truewith nosettledWhen. It filters with{ field: { $lt: '{today}' } }. AsettledWhenis CEL that no query can ask, so such a field gets no chip.A chip narrows the list's own read: it is merged beside the parent scope with
mergeFilterNodes.导出 downloads a CSV through
dataSource.exportDownload. It sends the same parent, chip and list filter, the filter text assearch, the shown columns and the order.Ride-along (b): 动态's count on a deep link
RecordTimelineCountProbe(inrecord-activity.tsx) reads the feed the page already holds and reports its size to the tab row. The feed is the discussion context, the field-change context, and the node's types andshowCompleted. The probe starts no read of its own. It is mounted only when the 动态 tab is not active and the stream is not drawn elsewhere: a deep link to another tab, or ⊟ off. Before ④, 动态 had no count there until the stream was drawn once (③'s noted gap).i18n
Eight keys arrive in all ten packs, with each pack's plural forms:
detail.recordOfScope,detail.relatedFilter,detail.relatedChips,detail.relatedChipAll,detail.relatedChipMine,detail.relatedChipOverdue,detail.relatedExportanddetail.relatedRows.File surface beyond the claim
app-shell:ObjectView.tsx,objectViewRecordNavigation.tsandobjectViewRecordOverlay.tsx: the view's label as the counter's scope, and the shown rows handed to every page the list opens.recordDrawerContext.ts: the pane bar context.urlParams.ts: one state-key constant.components:custom/record-top-bar.tsxand its line incustom/index.ts, in place ofrenderers/layout/containers.tsx. The bar is a frame component, not a page-layout slot, socontainers.tsxis untouched.panel-toggles.tsxis untouched too: ⊟ is stillPanelToggles, drawn in a new place.plugin-detail:RelatedList.tsx,relatedListCompact.ts,renderers/record-activity.tsxanduseDetailTranslation.ts, for the two ride-alongs.#27545's split / overlay / maximize mechanics are not changed.
NavigationOverlayonly draws its existing ⤢ / ⤡ / ✕ into the shared bar.Tests
New:
components/src/__tests__/record-top-bar-27524.test.tsx(6):j/kbut not the arrows, a pane over it takes the keys until it closes, and a pane with no set holds them without stepping the page;NavigationOverlaydrawing the shared bar.app-shell/src/views/RecordDetailView.topBar-27524.test.tsx(7), a realRecordDetailView:replace,kback, ArrowUp ignored, ⤡ back to?recordId=with the restore state;jand ↑ step, and the next page keeps the samefromstate;?from=trail with ← and the ancestors;app-shell/src/views/objectViewRecordNavigation.listRows-27524.test.tsx(3), the list's navigation hook:plugin-detail/src/__tests__/RelatedList.enlargedToolbar-27524.test.tsx(5):plugin-detail/src/__tests__/RecordPageColumns.tabRow-27524.test.tsx(+3):Pins updated to the new anatomy, not deleted:
RecordDetailView.panelToggles-27052(console: one workspace-preference module (record open mode, drawer width, timeline / related column toggles) read per user × object, with a shared panel-toggle toolbar on the record page — defaults derived from the object, never metadata #27052): ⊟ in the top bar, nothing in the header's trailing slot, no crumb row, the bar before the header.recordDrawerStack-26679andrecordDrawerStack.rootStepper-26679(console record drawer v2: the bar is ↑ ↓ · ⤢ · ✕ (no object name, no counter); a related row or a lookup opens its record in place with a trail, ← / Esc go back one level; ⤢ carries the trail into the page; on the record page a related row opens a drawer over the page #26679): the root crumb 「对象 › 记录名」 and the counter.RelatedCardStack-27055(console record page v3: frameless detail sections, one merged timeline (discussion + activity + field changes), compact related cards, header 1 primary + 2 secondary + ⋯ — the synthesized default page, drawn from the object's roles; the dead BuildPageOptions hints go #27055): the enlarged face's stub sees the enlarged context.RecordDetailView.pageHeaderTitleFls-10499: the record's gated name now appears twice, in the H1 and in the top bar's crumb. The pin checks that the crumb names the same gated title and never the denied email, and that the body still shows the name once.residue-namespaces-3546: RelatedList'sdetail.addsites number four with the toolbar's Add.RecordDetailView.recordPageTiers-26678,RelatedList.compactColumn-26678) needed no change and ran green in the broad suite below. No test file is named for fix(plugin-detail,app-shell,components): record page round 2 — related OFF draws no column or rail, one segmented panel control in the header row, three columns in a 1440 window with the sidebar #27473. The plugin-detail package and the record-view tests, where its pins would sit, ran in full in that suite.Eleven ablations (each through
node scripts/ablation-replace.mjs, restore proven blob == HEAD)RecordDetailView.topBar-27524, 4 failed.fromrows not read (readFromSiblings):RecordDetailView.topBar-27524, 1 failed | 6 passed.from:objectViewRecordNavigation.listRows-27524, 2 failed | 1 passed.record-top-bar-27524, 2 failed.RecordPageColumns.tabRow-27524, 2 failed.RelatedList.enlargedToolbar-27524, 4 failed | 1 passed.RelatedList.enlargedToolbar-27524, 2 failed | 3 passed.RecordPageColumns:RelatedCardStack-27055, 1 failed | 12 passed.Acceptance notes
Screenshots on HotCRM against the v4 boards at
a7825499aa, zh-CN. They are committed on this branch inb0a817c91dand removed again in the next commit, so they never reachmain. The folder's README says how they were made, andfacts.jsonrecords each shot's top bar as read from the DOM.HotCRM runs on this branch's own server. The published 18.2.0 server has no
POST /data/:object/:id/page, which the console's record page now reads, so it shows 「未找到记录」. Three of HotCRM's lead form views declare the formtypes that spec 18.3.0 retired; in the rig's clone only, they were set tosimple.01-lead-1440-from-list.png1 / 21, the tooltip 「全部线索 中第 1 条,共 21 条」, and ↓ ↑. There is no ⊟: the lead declares its own page layout, which has no right column.02-lead-1440-after-step-down.png2 / 21, Kai Watanabe, the page replaced.03-lead-1200-direct.png04-contact-split-720.png05-contact-maximized.png06-contact-split-task-tab.png07-contact-page-task-tab-deep-link.png,07b-contact-page-task-tab-mine.png2although its stream is not drawn.08-product-1440.png09-lead-phone-390.png,09b-contact-phone-390.png10-crm_account-split-pane-counter.png,11-crm_account-split-pane-after-step.png3 / 9, then4 / 9after ↓, with ⤢ and ✕.12-crm_account-page-from-arrow.png1 / 9, ↓ ↑, ⊟, ⤡ 「收为抽屉」.No shot has the crumb row (
crumbRow: falsein everyfacts.jsonentry). Every top bar measures 40 px.Not built here, reported:
ListViewreports its row sequence only for the paged, ungrouped grid (console record drawer v2: the bar is ↑ ↓ · ⤢ · ✕ (no object name, no counter); a related row or a lookup opens its record in place with a trail, ← / Esc go back one level; ⤢ carries the trail into the page; on the record page a related row opens a drawer over the page #26679,rowSequenceShown). On a grouped list or a kanban board the pane therefore has no sibling set and draws no counter at all; the HotCRM contact list, grouped by account, is one. Naming the group needsListView(plugin-list, outside this claim) to report grouped and lane rows with their group. That is a follow-up.crm_task.due_datedoes not declaredueLike, so HotCRM's 任务 tab shows 全部 / 我的 only. The chip appears where an object declares the deadline.search, while the table's own text filter is a client-side contains sweep over the fetched page. The CSV can differ where the server's search matches other fields.Human Minimum
Each item is one rule from
CONTRIBUTING.md › Human Minimum,
which links the AGENTS.md text that states it.
packages/specholds no runtime logic. (packages/specis not touched.)main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed. (Not touched.)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Verification
All local readings are at head
d94e010b0a, which includes the merge oforigin/main5517005d3a. Against that merge base the change is 37 paths, +2,125 / −303. The evidence folder is added and removed inside the range, so it is not among them.Tests, type-check, lint
pnpm exec vitest run --maxWorkers=3 packages/plugin-detail packages/components packages/i18nplus the 619 other test files underui/that name a module, test id or key this PR changes, derived bygit grep.6c535caa83:Test Files 4 failed | 1529 passed | 2 skipped (1535),Tests 10 failed | 17140 passed | 67 skipped (17217).all-locales-key-parityandplural-categories-11432: fr / es / pt lackeddetail.relatedRows_many, and ar's_one/_twolacked{{count}};residue-namespaces-3546: RelatedList'sdetail.addcount, now 4 with the toolbar's Add;RecordDetailView.pageHeaderTitleFls-10499: the record's name now also appears in the top bar's crumb. The pin now checks that the crumb names the same gated title and never the denied email.d94e010b0a, and the four files rerun green:Tests 107 passed (107).type-check(tsc --noEmit+tsc -p tsconfig.test.json) of@object-ui/app-shell,@object-ui/componentsand@object-ui/plugin-detailunder the lock, and@object-ui/i18natd94e010b0a: each exit 0.ts/tsxfiles underui/atd94e010b0a: 0 errors. The warnings left on added lines arereact-refresh/only-export-componentson the hook exports beside components, a pattern these files already follow.pnpm --dir ui run check:i18n-drift: exit 0.Derived gate families: 34 derived, 34 run, 0 NOT-MEASURED
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives the same 34 commands at857d25f771and atd94e010b0a. Each was run atd94e010b0awith stdin from/dev/null, and its exit code was captured before any pipe.An earlier pass at
857d25f771had one red:pnpm --dir ui exec vitest run scripts/, incheck-doc-snippet-types' root-bound case onplugin-grid/plugin-reportREADMEs that this branch does not touch. It came while aturbobuild was rewriting@objectstack/spec'sdist(the HotCRM rig), and it is green on every run since:169 passed | 2 skipped,4901 passed.--ranon this record: "34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN … a DERIVED zero — all 34 recorded an exit code and none of them is 3".Merge-tree and what was not run
mainhas moved 7 commits past the merge base. Two of them are feat(app-shell): an object list's title names the object ahead of the view switcher #28438, which touchesObjectView.tsx, and feat(core): an action's param dialog description, helpText and outcomeMessages fill {{ }} template holes, and still fill ${…} #28436.git merge-tree --write-tree HEAD origin/main(776c1d875b) is clean, and so is the open feat(spec,lint): a form's thank-you panel names the record's engine-generated fields, and the public submit answer has a declared shape #28440. No second merge was pushed.ui/suite, the other type-check lanes and the repo-wide scans.CI check-runs: not started at the time of writing.
Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01L6bhgVP3twvBvDTwoLPU6J