Skip to content

feat(app-shell): an object list's title names the object ahead of the view switcher - #28438

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-xn9w1t
Oct 31, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-xn9w1t

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Fixes #27950

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:对象列表页标题从「我的线索 ▾ · 12」变为「线索 / 我的线索 ▾ · 12」,对象复数名在视图切换器前面,点击回到该对象的默认列表;手机顶栏的视图切换按钮同样读作「线索 / 我的线索」。
  • 为什么改:侧栏收起或从链接直接打开列表时,页面上没有文字说明这是哪个对象;按维护者裁决 B(页头对象名前置视图切换器,窄列表对象名优先保留)。
  • 风险与回滚:只动 Console 列表页标题这一处界面,不碰顶栏 AppHeader、记录页与任何元数据;回滚即还原本 PR。
  • 先看:ui/packages/app-shell/src/views/objectViewPageHeader.tsx(ObjectListTitle 与 title 的三种取值)、ui/packages/app-shell/src/views/objectViewViewCrud.tsx(手机按钮文字)、新测试 ObjectView.objectListTitle-27950.test.tsx。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/ObjectView.objectListTitle-27950.test.tsx,预期 9 个测试全过;或在 Console 打开 CRM 线索的任一视图,标题左侧可见「线索 /」。
  • 需要你做的:无,席位落地。

What changes

The maintainer's ruling B on #27950 (comment 6539906268), verbatim: 「#27950 裁 B:页头对象名前置视图切换器;窄列表下对象名优先保留、视图名截断或换行、计数可省」.

  • Page header (objectViewPageHeader.tsx): when the list has a view switcher, the title is 「OBJECT / VIEW ▾ · COUNT」. The object's plural label (the reader's language, through objectPluralLabel) comes first, as a link to /apps/APP/OBJECT. That bare address opens the reader's pinned view, or else the declared default. Off an app route it is plain text.
    • A view with no label is already called by the plural (objectui#12774). Its title stays the switcher alone, so the object is named once and never as 「Leads / Leads」.
    • With no view to switch, the title is the plural, as before.
  • Narrow rows (the split pane's narrowed list, Record drawer v4: a bounded peek over the list — md cap instead of the field-count bucket, no dim overlay, the list stays readable and clickable, ⤢ maximizes in place at the same URL; the full page is reached only from the row ↗ and the header ⋯ #27545): the object label is shrink-0 whitespace-nowrap. The view label is the switcher's own truncate span inside a min-w-0 row, so the view label is cut first and the object label never is. The count stays: it is a few characters, and the ruling makes dropping it optional.
  • Phone (objectViewViewCrud.tsx): the page header is not drawn below sm. The app bar's view switcher trigger now receives triggerLabel 「OBJECT / VIEW」 through the existing MobileViewSwitcherContext field. That span truncates its end, so the view label gives way before the object's. A label-less view keeps the plain label. AppHeader.tsx is not touched.
  • useObjectViewTitleSwitcher also returns the titleLabel it already computed, and ObjectView passes it and the object list href to the header.
  • Docs: content/docs/ui/views.mdx › Switching views. Changeset: .changeset/27950-object-list-title-names-object.md (@object-ui/app-shell patch).

Tests

New ui/packages/app-shell/src/views/ObjectView.objectListTitle-27950.test.tsx (9 cells, real ObjectView → plugin ObjectView → ListView chain, the ObjectView.titleViewSwitcher-27594 harness):

  • On the object's address and on a view's address, the heading reads Leads then the switcher. The label is an A with href="/apps/crm/crm_lead", and following it opens the default view.
  • With the sidebar expanded and collapsed (the real SidebarProvider; useSidebar().state is read back as expanded / collapsed), the object label is drawn exactly once.
  • A record open beside the list (split preference, ?recordId=) gives one object label and one switcher.
  • A label-less view: no crumb, and the switcher reads Notes.
  • The narrow-row classes decide the cut: the crumb is shrink-0 + whitespace-nowrap, and the view label is truncate.
  • The phone trigger reads Leads / My Leads.

The record route's crumb is not drawn by this page: RecordDetailView is a separate route chunk. The existing AppHeader.appBarLeftGroup-12461 cells ("an object's view and record routes draw no crumb either") pin the app bar, and they pass unchanged in the run below.

Ablations (scripts/ablation-replace.mjs, WRAP mode, under os-verify-lock, trap restore)

No dist leg: the suite imports ./ObjectView from source.

leg mutation result
A1 title back to the switcher alone Tests 7 failed / 2 passed (9) (the label-less and phone cells stay green)
A2 phone triggerLabel dropped Tests 1 failed / 8 passed (9)
A3 crumb class shrink-0 whitespace-nowrap → min-w-0 truncate Tests 1 failed / 8 passed (9)
A4 label-less guard removed Tests 1 failed / 8 passed (9)

Every leg: ok mutation landed: anchor 1 -> 0. Every restore: blob == HEAD (objectViewPageHeader.tsx 540eb8ba8885, objectViewViewCrud.tsx a05153fc75e3) and git diff HEAD empty. These ablations ran at the pre-merge head, where both files were byte-identical to the final head.

Acceptance notes

  • The card's original file surface (AppHeader.tsx) was replaced by the ruling. The diff lands in objectViewPageHeader.tsx, objectViewViewCrud.tsx, objectViewTitleSwitcher.tsx (one returned value) and ObjectView.tsx (two props), as the decision comment's 裁后执行 line named.
  • Seat 1's Header action shows it is already pressed: page:header and DeclaredActionsBar read action.pressed #28242 (header action pressed state) works on the header's actions cluster. This diff touches only the title prop of the same PageHeader call in objectViewPageHeader.tsx, a different region.
  • The phone bar's trigger is still capped at 180px by AppHeader. An object plural longer than that would itself be cut. No authored object in this tree comes close, and widening the cap is AppHeader's, outside this ruling.
  • No new i18n key: the separator is a glyph (aria-hidden), and the link's name is the object's own label.
  • Not taken: a browser reading at 1440 / 464 / 390 px. The test DOM computes no layout, so the narrow-row behaviour is pinned by the classes that decide it. The landing seat may take the reading.

Verification

All at head 750e100f (branch merged with origin/main 5da3a8ce, which carries #28394), except the ablations as noted.

Derived gates — 66 derived, 66 run, all exit 0

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 66 commands from 7 paths vs merge base 5da3a8ce3. Each ran with </dev/null, and each exit code was recorded before any pipe. This includes node scripts/check-sdui-manifest.mjs (exit 0) and pnpm --dir ui exec vitest run scripts/ (Test Files 169 passed | 2 skipped (171), Tests 4901 passed | 2 skipped (4903)). Reconciliation: dispatch-gates --ran: 66 derived, 66 run, 0 NOT-MEASURED, 0 UNRUN. Before the gates: env -u OS_SKIP_DTS pnpm turbo run build --filter=@objectstack/spec --filter='@objectstack/lint...' --filter='@objectstack/client-react...' (38/38).

Tests and type-check
  • 106 ui suites (every app-shell ObjectView.* and AppHeader.* suite, plus every test naming MobileViewSwitcher, the page header, the title switcher or the view CRUD hook) under os-verify-lock: Test Files 106 passed (106), Tests 818 passed (818), VERDICT command-exit 0.
  • pnpm --filter @object-ui/app-shell type-check (both tsconfig.json and tsconfig.test.json; the test config lists the new suite) after env -u OS_SKIP_DTS pnpm turbo run build --filter='@object-ui/app-shell^...' (64/64): VERDICT command-exit 0.
  • eslint --no-inline-config on the four changed app-shell files: 0 errors (34 pre-existing warnings, none on the new lines). pnpm check:new-line-citations (ui): 0 new citations.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#2 · os-tesla · session_01L9qibxgfM8p8t3fo6y9eFK

席位意见:同意。按裁决 B 落在列表页页头,顶栏 AppHeader 未动;窄行对象名不缩、视图名先截断;对象名链接回该对象默认列表。席位落地。

席位核对

ACCEPT · head 750e100ff0bb9cea7ef922e1c52403314b3844ed · 2026-10-31T16:23Z

  1. 形态: 7 files, +350/−15; Fixes #27950; 0 governed paths, 365 lines ≤ 3000 (check-governed-merges --pr 28438).
  2. 前提实测: ruling B 6539906268 (object plural ahead of the Console lists: the list-view switcher becomes a dropdown on the list title (search, pinned default, recent), the flat view-tab row retires, and a starred view appears in the sidebar's 收藏 group #27594 switcher; narrow rows keep the object label whole). Surface moved from AppHeader.tsx to the page header per the ruling, amendment 6545834547. Seat read the hand-written diff: ObjectListTitle (object label shrink-0 whitespace-nowrap, links to /apps/APP/OBJECT, plain text off an app route); title = plural alone with no switcher, switcher alone when the open view's label equals the plural (no 「Leads / Leads」); phone trigger 「OBJECT / VIEW」 through the existing MobileViewSwitcherContext triggerLabel.
  3. 门禁 check-runs: 59 on the head, 51 success + 8 skipped, 0 failing; mergeable_state clean. Dev: new test 9/9, four ablations red then restored, 66/66 derived gates.
  4. 契约复审: Clause-②: no, Metadata-protocol: none — none of the four contract faces is touched; no second review owed. Prose face checked sentence by sentence by the seat: content/docs/ui/views.mdx 〈Switching views〉 matches the diff (object plural ahead, link to the default list, unlabelled view reads alone, object label kept whole on short rows).
  5. 范围外去向: none. The ruling's "count may drop" is permissive; the PR keeps the count.
  6. 落地条款: seat runs pr_ready + auto-merge on the maintainer's go of 2026-10-31T14:22Z 「使用 pm-dispatch 技能审核和落地pr」 (the relay write ran seconds before this record was stored; same head).

Merged via the queue into main with commit 41df5eb Oct 31, 2026
60 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-xn9w1t branch October 31, 2026 16:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling

Projects

None yet

1 participant