Repository navigation
feat(plugin-detail)!: a tab row under the record header; an open panel face is the active tab - #28394
Conversation
…anel face is the active tab Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibzGCEznLfiQm2T5icF
…ace; changesets and docs Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibzGCEznLfiQm2T5icF
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibzGCEznLfiQm2T5icF
… drives The tier pins read the page's one tablist as its tab row; a new pin walks the row through the browser's history: one pushed entry, tab to tab replaces it, and 概览, Esc or Back return in one step. The retired full related face's header pin leaves with the face. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibzGCEznLfiQm2T5icF
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibzGCEznLfiQm2T5icF
|
domain:ui#2 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract-review-claim: |
|
domain:ui#2 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract reviewServed-tier: Inputs: card #27524 (record page v4; the seat's PR ③ claim 6540954535 for Targets 8 and 9 cut from ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
Verdict reasoning. The tab row is a pure width rule drawn by measurement with the active tab always on the row, the counts come from one deduplicated set, the enlarged face is the active tab under the untouched header with the same URL addresses and a history that returns in one step, the two retirements have no reader left and are declared with their markers and the right level, the row and face carry their roles and names in every pack, the pins hold both ways including on real browser history, and every latest check on this head is green or skipped. Implemented-by: VERDICT: PASS |
|
domain:ui#2 · os-tesla · session_01L9qibxgfM8p8t3fo6y9eFK 维护者速读
席位核对ACCEPT · head
|
domain:ui#2 · os-tesla · session_01L9qibzGCEznLfiQm2T5icF
Requested by Tesla · project thread
Part of #27524
Clause-②: yes (narrowing)
Metadata-protocol: none
Skills-docs: none — the narrowing retires a ui component export and an i18n key, not an author-writable metadata surface;
git grepfinds neitherArrangedRelatedTabnordetail.relatedEnlargedBackunderskills/,content/ordocs/.维护者速读
?related=对象.字段(动态是?related=timeline),「概览」或 Esc 返回。ArrangedRelatedTab导出和detail.relatedEnlargedBack一个键,只有控制台自己用过。回滚即 revert 本 PR。ui/packages/plugin-detail/src/RecordTabRow.tsx与synth/recordTabRow.ts(标签行和放几个的规则);RecordPageColumns.tsx(标签从哪来、当前标签面);app-shellRecordDetailView.tsx的openEnlargedRelated(标签间切换替换历史)。pnpm --dir ui exec vitest run packages/plugin-detail/src/synth/__tests__/recordTabRow-27524.test.ts packages/plugin-detail/src/__tests__/RecordTabRow-27524.test.tsx packages/plugin-detail/src/__tests__/RecordPageColumns.tabRow-27524.test.tsx packages/app-shell/src/views/RecordDetailView.recordTabRowUrl-27524.test.tsx,预期 4 个文件、26 个测试全绿。What changes
Record page v4, Target items 8 and 9, PR ③ of the four serial PRs for #27524. It is cut from
mainafter PR ② (#28116,3d2da4fd1a) and leavesmaincoherent on its own. The top bar (Target 7) is PR ④.8. The tab row (
RecordTabRow.tsx,synth/recordTabRow.ts,RecordPageColumns.tsx). A row at the bottom edge of the record header, on every face the record is drawn in: the page, the split pane, the overlay drawer, the maximized pane and a phone.record.related.arrangementorder (resolveRelatedArrangement, the order the right column draws). Each tab carries its count: 动态 the stream's size, a related tab its list's count.useRelatedCountsonce for every panel, hidden ones included, and hands the same counts to the right column (RelatedCardStacktakes a new optionalcountsprop and then asks for none itself). 动态's count is the stream's own, reported by therecord:activityblock throughRecordTimelinePanel's newcount/onCountprops in the peek face, and throughRecordTimelineCountContexton the 动态 tab while its chip is 全部.ResizeObserveron the row re-measures on every resize (window, sidebar, a pane dragged wider), against a hidden measuring copy of every tab and of the widest 「+N ▾」. The rule is the purelayoutRecordTabRow:tablistnamed 「记录页签」 and a single tab stop: ← → Home End move between the shown tabs, Enter or Space opens one.onEnlargeRelatedabsent), or where there is nothing but 「概览」.9. The enlarged face is the active tab (
RecordPageColumns.tsx, app-shellRecordDetailView.tsx).role="tabpanel". The ‹ back line and its ownh2are gone, because the tab names it. Esc (window capture, as in PR ②) and 「概览」 return to the page.?related=object.field,?related=timelinefor 动态. Leaving 「概览」 pushes one history entry. Moving from tab to tab now replaces that entry, keeping its state, so one Back, Esc or 「概览」 always returns to the page. A deep link to a tab opens it as the active tab, and 「概览」 then replaces the address, since there is nothing to pop.recordDrawerStack.tsx; the pane draws the same view).Retired.
ArrangedRelatedTab(plugin-detail entry): it laid the reader's arrangement over the 关联 tab of the tabbed tier that PR ② retired, and only the console's record view imported it. Its full-face chrome path inRelatedList(arrangedFull) and its scope branch in therecord:related-listrenderer go with it. A page still drawn as its strip is drawn as written.detail.relatedEnlargedBackleaves all ten packs.Arrives in ten packs:
detail.recordTabs,detail.recordTabOverview(概览 · Overview · 概要 · 개요 · Übersicht · Aperçu · Resumen · Visão geral · Обзор · نظرة عامة),detail.recordTabsMore(with each pack's plural forms) anddetail.recordTabsHidden.Declared.
Clause-②: yes (narrowing)(the claim said no; measured on the diff, the retired export and the retired key narrow two published surfaces). Two changesets:27524-record-page-tab-row.md(plugin-detail, app-shell):ArrangedRelatedTabundernot-required (runtime-export-retired …)with its FROM → TO line.27524-record-page-tab-row-i18n.md(i18n):TranslationKeysloses one key, undernot-required (runtime-interface-only …#TranslationKeys), as PR ① and ② did.Reads at open. A record open now counts every related panel, the hidden ones and those behind ⊟ off included (before, only the panels on screen), because their tabs show the counts. The requests are the same
RelatedCountStorecount requests, deduplicated with the panels' own. Nothing else a record open fetches changes. (Seat 2's #27627 / #28321 touches the data adapter; this PR does not.)File surface beyond the order.
RelatedList.tsx(the deadarrangedFullpath),renderers/record-related-list.tsx(the retired scope branch),index.tsx(the export),recordTimelineStream.tsandrenderers/record-activity.tsx(动态's count, for its tab),RelatedCardStack.tsxandRecordTimelinePanel.tsx(shared counts), the plugin-detail README andcontent/docs/ui/pages.mdx.navigation-overlay.tsx(#28294), the top bar (④) and #27545's split / overlay / maximize mechanics are untouched.Tests
New:
plugin-detail/src/synth/__tests__/recordTabRow-27524.test.ts(6): the layout rule at a wide, a medium and a narrow width; the active tab displacing the last tab, fitting beside them, or keeping its place; 「概览」 and the active tab at the narrowest; hidden panels forcing the dropdown and taking the trigger's width.plugin-detail/src/__tests__/RecordTabRow-27524.test.tsx(7), with given widths and a stubResizeObserver: the shown count follows the width and is recomputed on resize; the active tab is always on the row; the count chips; 「+N ▾」 lists the folded tabs with counts, then the hidden ones, and choosing one selects it; a hidden panel makes the dropdown on a wide row; clicking; the keyboard (one tab stop, ← → Home End).plugin-detail/src/__tests__/RecordPageColumns.tabRow-27524.test.tsx(10), the realRecordPageColumns: the tabs in arrangement order with hidden panels in the dropdown; counts for every panel from one request each (hidden ones included) and 动态's from the stream; selecting a tab and 「概览」; no row without a way to open; the stack tier; the enlarged face keeps the header, is atabpaneland draws no back line or title; the 动态 face; a body table's tab; Esc.app-shell/src/views/RecordDetailView.recordTabRowUrl-27524.test.tsx(3), a realRecordDetailViewon the browser's own history: a tab pushes one entry and tab to tab replaces it (three tabs visited, one entry), 「概览」 returns; Esc and the browser's Back return in one step at 720; a deep link opens its tab and 「概览」 replaces the address without growing history.Rewritten to the new anatomy:
RecordDetailView.recordPageTiers-26678(6): the page's only tablist is its tab row on every tier and embedded (overview, timeline, showcase_task.project, showcase_note.project), 「概览」 active;?related=timelinemakes 动态 the active tab.RelatedCardStack-27055: the enlarged face's back-button case reads 「概览」 and Esc; theArrangedRelatedTabcases leave with the component.RecordPageColumns.enlargedEscapeInPane-27524: Esc is pressed on the face, which no longer draws a back button to focus.RelatedPanel.header-27524: the case for the arranged full face (the retired tabbed tier's 关联 tab) leaves with that face.Ablations (each through
node scripts/ablation-replace.mjs, restore proven blob == HEAD)layoutRecordTabRow(chosen.add(active)):Tests 2 failed | 11 passed (13), the rule's and the drawn row's "active tab always shown" cases.Tests 1 failed | 9 passed (10), "each panel tab carries its count — the hidden ones counted too".openEnlargedRelatedpushing on every tab change (the replace branch removed):Tests 2 failed | 1 passed (3), the one-entry history case and the Esc / Back case.Acceptance notes
ResizeObserver; the rest in 「+N ▾」 with counts; the active tab always shown, in the last slot when it would fold; 「概览」 plus at least one more always; hidden panels at the dropdown's end.?related=object.field/?related=timeline; Esc or 「概览」 returns; not a modal, not a drill-down, no top-bar path, the header not compacted; in the pane it fills the pane body.examples/app-crm, not HotCRM, as in ① and ②. Ten shots*.pngin/mnt/project-files/ui-seat-2/r2026-10-31/out/27524-pr3-shots/(project files, not a repository branch), zh, through the console's dev server on this branch:page-1440andpage-1200: six tabs, no dropdown (概览 · 动态 · 活动列表 2 · 联系人列表 1 · 线索列表 1 · 商机列表 4).page-390: 概览 · 动态 · 活动列表 · 联系人列表, then 「+2」;page-390-more-open: the dropdown lists 线索列表 1 and 商机列表 4;page-390-folded-active: 商机列表 chosen from it takes the last slot (联系人列表 folds, still 「+2」) and its list draws below.page-1440-enlarged(联系人列表 active),page-1200-enlarged(商机列表 active, every column),page-1440-timeline(?related=timeline, the full stream with its chips).pane-720andpane-720-enlarged: the split pane beside the list (row 623 px wide), all six tabs; switching tabs replaced the URL, Esc returned to 「概览」 with the pane still open, and Back then closed the pane.a7825499aa(RecordPageV4Enlarged, RecordDrawerV4Enlarged, and the rows on RecordPageV4Panels / TwoCol / RecordDrawerV4Peek):RelatedList's own card header (title, count, 新建) and its 查看全部 foot, where the board draws a bare toolbar (filter, 全部 / 我的 / 逾期, 新建, 导出) over a table filling the rest of the height. That toolbar is not something the related list has today.page-1440-enlarged), 动态's tab has no count until the stream is drawn once: its count is the stream's own size, which only the stream reads.Known gaps
RelatedList's own card header (title, count, 新建) and its 查看全部 foot. The v4 board (RecordPageV4Enlarged / RecordDrawerV4Enlarged ata7825499aa) draws a bare toolbar there (filter, 全部 / 我的 / 逾期, 新建, 导出) over a table that fills the rest of the height. The related list has no such toolbar today; this PR does not add one.Verification
Tests, type-check and the derived gates on 572d6b1
Head
572d6b18fb, change set vs the merge basea878ff6c26: 36 paths, 1,816 (+1,478 / −338) changed lines.pnpm --dir ui exec vitest run packages/plugin-detail packages/i18nplus 226 test files acrossui/that name a module, test id or key this PR changes (RecordDetailView,RecordPageColumns,RelatedCardStack,ArrangedRelatedTab,relatedEnlargedBack,data-related-enlarged,RECORD_RELATED_ENLARGE,record-tab; derived bygit grep) →Test Files 672 passed | 1 skipped (673),Tests 6395 passed | 31 skipped (6426), VERDICT command-exit 0 (on7a0555e2;572d6b18fbonly addsobjectNameto one pin's record context, and that file then ranTests 10 passed (10)). A first broad run found the sixrecordPageTiers-26678cases (they asserted no tablist) and the retired full-face case inRelatedPanel.header-27524, both rewritten above.type-check(tsc --noEmit+tsc -p tsconfig.test.json) of@object-ui/i18n,@object-ui/plugin-detailand@object-ui/app-shell, under the lock: each exit 0 on572d6b18fb. The first plugin-detail run was exit 2 on the new pin's missingobjectName(TS2741), fixed in572d6b18fb.ts/tsxfiles underui/: 0 errors.pnpm --dir ui run check:i18n-drift: exit 0.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack→ 66 commands, the same list onc1734bc8and on572d6b18fb. Each was run on572d6b18fbwith its exit code captured before any pipe (the three heavy ones under the lock); all 66 are:: exit 0,node scripts/check-sdui-manifest.mjsamong them.--ranreconcile:66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN.!), once on572d6b18fbunder the lock:pnpm --filter @objectstack/dogfood test→ VERDICT command-exit 0,Test Files 361 passed | 3 skipped (364),Tests 2917 passed | 28 skipped (2945).572d6b18fb: all 59 check-runs concluded success, or skipped by path filter or opt-in;Test UI(16/16),Type Check UI,Lint UI,Live E2E UIandBuild & E2E UIamong them.mainhas moved 8 commits past the merge base;git merge-tree --write-tree origin/main HEADis clean, and no merge was pushed.ui/suite (Test UI;app-shellalone is 1,379 test files), the other type-check lanes, the repo-wide scans.Generated by Claude Code