Skip to content

feat(ui): one Personalized marker beside the list title holds the one reset to the view - #29266

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/project-thread-o05ndx
Nov 2, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/project-thread-o05ndx

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #28950

Clause-②: yes (widening)
Metadata-protocol: none

维护者速读

  • 改了什么:列表与视图不一致时,标题旁出现一个「已个性化」标记;点开列出哪里不同(筛选、搜索、排序、分组、列布局、隐藏字段、行高、布局、行颜色、行号、行内编辑),并给出唯一的「恢复为视图设置」。页头 ⋯ 里的「重置视图」和 Display 面板的「恢复视图默认设置」去掉;排序弹层的重置和筛选面板的「全部清除」保留。
  • 为什么改:原来有两个重置入口,各管一部分,而且刷新或打开链接后 uf_* 参数会把旧列表带回来,搜索词也会从缓存回填。读者看不出列表被改过,也找不到一个能彻底恢复的地方。
  • 风险与回滚:只加不删:ListView 多一个可选宿主属性 onPersonalizationChange,i18n 多一组键,没有元数据键或选项。未传该属性的宿主(InterfaceListPage、ObjectDataPage、嵌入列表)保留原来的 Display 重置。回滚即 revert 本 PR,无数据迁移。
  • 先看:ui/packages/app-shell/src/views/objectViewListUrlState.ts(handleResetView 与 withoutListStateParams:清存储、清搜索缓存、剥离整组 uf_*)、ui/packages/plugin-list/src/ListView.tsx(各部分与视图的比对)、ui/packages/app-shell/src/views/ListPersonalizedMarker.tsx。
  • 两分钟验证:pnpm dev:crm 打开 Accounts 列表,按名称倒序并搜索 a:标题旁出现「已个性化」;点开看到「搜索」「排序」,点「恢复为视图设置」:列表回到视图排序、搜索清空、地址栏不再有 uf_*,刷新后仍然干净,标记消失。
  • 需要你做的:无,席位落地。

What changes

While a reader's list differs from its view, the Console draws one Personalized marker beside the list title. Opening it names what differs and holds the one Reset to view, with a sentence saying what it covers and what it leaves alone. The marker is not configurable: no metadata key and no option reach it.

  • ListView (@object-ui/plugin-list): one new optional host prop, onPersonalizationChange(parts). It reports, on change, which parts of the list on screen differ from the view: sort, filters (panel conditions, user-filter chips, the queue toggle), search, grouping, hiddenFields, density, layout, rowColor, rowNumbers, inlineEdit. The callback is read through a ref (AGENTS.md chore: version packages #10), and the report is keyed on the joined part list, so it fires only when the set changes. With the prop wired, the Display popover draws no reset of its own (onReset is left undefined; ViewSettingsPopover.tsx is untouched). viewDefaults also accepts grouping, the view's own grouping under one the host laid over it; without that key, the grouping the list opened with counts as the view's.
  • ListPersonalizedMarker.tsx (new, app-shell): an outline pill with a popover. Below sm it is the icon alone, named by its aria-label, so it never pushes the view name out. Each part label is a literal i18n key.
  • objectViewListUrlState.ts: the reset now drops the view's stored entry, clears the cached search term, and strips the whole uf_* family from the address (uf__filter, uf__search, uf__sort, uf__group and every uf_<field> chip) through the new withoutListStateParams. A pending-reset flag makes the next seed read storage, not the URL, so a reload or an opened link cannot bring the old list back. The host adds columns to the parts when the stored entry carries a column state.
  • ObjectView.tsx, objectViewPageHeader.tsx: the marker sits in the existing PageHeader badge slot (both header faces) and beside the phone title. PageHeader.tsx is not edited.
  • objectViewPageHeader.tsx, objectViewHeaderChrome.tsx: the ⋯ entry "Reset view" and the folded bar's object_view_reset_view action are gone; ⋯ keeps favorites and is drawn only when favorites are.
  • i18n, all ten packs: new console.objectView.personalized, personalizedHeading and personalizedParts.*; resetView and resetViewTitle are reworded to label the marker's reset and say everything it covers. No key is removed.
  • Docs: content/docs/ui/views.mdx, the list personalization paragraph.
  • Changeset .changeset/28950-list-personalized-marker.md, patch on plugin-list, app-shell and i18n.

Why

Measured on main a4015ab before the change (code reading):

  • The header ⋯ "Reset view" was offered only while a stored entry existed. It dropped the entry and remounted the list, but it left the search term (re-seeded from the search cache as uf__search) and the uf_<field> chips. After a reload or an opened link it left everything the URL carried, because the remount re-seeded sort, filters, search and grouping from the same uf_* params. A reader who changed only search, grouping, chips or a Display setting was never offered it.
  • The Display popover's "Reset to view default" restored the Display settings only.

So a reader had two partial resets and no sign the list was theirs. The card asks for one marker and one whole reset.

Tests

  • ui/packages/plugin-list/src/__tests__/ListView.personalization-28950.test.tsx (new, 12): nothing reported on the view's own list; sort compared against viewDefaults; search and filters; an incomplete filter row is not a filter; grouping against viewDefaults plus its control; hidden fields and density; a row-numbers toggle reported and then reverted; typed search; no Display reset when the callback is wired, kept without it (control); the sort popover's own reset stays.
  • ui/packages/app-shell/src/views/ObjectView.listPersonalized-28950.test.tsx (new, 5): withoutListStateParams strips the family and keeps filter[owner]; the marker shows only while parts are reported and ⋯ has no reset; after a reload the reset leaves no uf_* param, keeps filter[owner], and the list opens with no search, filters or chips and the view's sort; in session the reset clears the cached search; a stored column state alone draws the marker and the reset clears it (control).
  • ui/packages/app-shell/src/views/ObjectView.userListState-12418.test.tsx: its ⋯ "Reset view" pins move to the marker.

Ablations (each restored, tree byte-identical to HEAD):

  • Removing && !reset from the seed condition turns the reload test red (1 of 5 failed).
  • Making the Display onReset unconditional turns the "no Display reset" test red (1 of 12 failed).

Acceptance notes

  • Entries removed: the header ⋯ "Reset view" (both header faces). The Display popover's whole-Display reset is hidden on the Console list, where the marker carries the reset; hosts that do not wire onPersonalizationChange keep it.
  • Per-control resets kept: the sort popover's reset and the Filter panel's "Clear all".
  • Not touched by the reset, by design: the record-open preference, pinned views and their order, favorites, a "View all" link's filter[...] scope, and the gallery card size (an object-wide setting, not this view's state, so it is not a marker part either).

Screenshots (examples/app-crm, Accounts, ?uf__sort=[name desc]&uf__search=a opened as a link, then the reset, then a reload). The 16 files are in the project files under issue-28950/shots/ (before- and after-, 1440- and 390-):

before (main dedc350) after (this branch)
1440, personalized link no sign the list differs; ⋯ holds only "Add to favorites" (no stored entry, so "Reset view" was never offered) Personalized pill beside the view name; popover names Search and Sort
1440, Display popover "Reset to view default" footer no whole-Display reset; the Open records, Group, Density, Row numbers and Edit inline controls stay
390, personalized link nothing icon-only marker beside the phone title, same popover
after Reset to view n/a list back to the view's sort, search empty, address bar has no query; after a reload still none, marker gone (1440 and 390)

Verification

Readings at b2d090d (merge of main dedc350, which carries #29047 and #29108 as ea150b7):

  • pnpm exec turbo run type-check for plugin-list, app-shell and i18n with dependencies: 67/67.
  • pnpm build (whole workspace): 117/117.
  • vitest from ui/: plugin-list 183 files, 1759 passed; app-shell ObjectView* / objectView* set 88 files, 735 passed (351 + 384); i18n 95 files, 1443 passed, 13 skipped; the two new files plus 12418, 37 passed.
  • node scripts/pm/dispatch-gates.mjs --ran: 66 derived families, all run, all exit 0 (includes check-sdui-manifest and vitest run scripts/).
  • PR-body gates with this body: check-changeset-no-major, check-adr-0087-registration, check-closing-keyword-parity, check-partof-closing-keyword all exit 0.
  • ui eslint on the touched files: 0 errors.
  • Live run (app-crm + console dev server): the reset leaves location.search empty and a reload keeps it empty at 1440 and 390 px.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (not touched)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes. (ui eslint on the touched files: 0 errors)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: its generated artifacts are regenerated and committed. (not touched)
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QsR2jQki8itS3sC2eZCj7J

…st title holds the one reset to the view

ListView reports which parts of the list on screen differ from its view
through a new optional host prop, onPersonalizationChange; with it the
Display control draws no reset of its own. The Console draws the marker in
the page header's badge slot and beside the phone title, and its reset
clears the reader's stored entry and cached search, strips the whole uf_*
family from the URL and remounts the list. The header overflow entry
"Reset view" goes.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jQki8itS3sC2eZCj7J
…ker and its one reset

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jQki8itS3sC2eZCj7J
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

席位核对 · ACCEPT · 2026-11-02T10:59Z

Head-sha: b2d090d81918aef5b24f8fe2a6e2e0b9a0e9b7e2

  • Shape:
    • The PR is a draft against main. The first card line is Fixes #28950, under claim 6572102963.
    • 22 files, +1141 / −128, inside the claimed surface: ListView.tsx, the six named app-shell view files plus the new ListPersonalizedMarker.tsx, the ten locale packs, their tests, one content/docs page and a root changeset. ViewSettingsPopover.tsx and PageHeader.tsx are untouched, as the claim requires. check-governed-merges --pr 29266: not governed, 1,269 changed lines, under the human-merge threshold.
    • Clause-②: yes (widening) and Metadata-protocol: none start their lines in the body and the changeset, as claimed. The changeset is patch.
  • Contract review (seat self-review, per the triage ruling of 10:43Z): widening only.
    • ListViewProps is a published export of @object-ui/plugin-list. It gains the optional onPersonalizationChange, and viewDefaults widens from Pick<…, 'hiddenFields' | 'rowHeight' | 'sort'> to include grouping. Every existing caller still type-checks. A host that does not pass the prop keeps the Display reset (onReset is undefined only when it is wired).
    • The ten locale packs add console.objectView.personalized, personalizedHeading and the eleven personalizedParts.* keys. resetView and resetViewTitle keep their keys with new copy in every pack. No key is removed.
    • ListPersonalizationPart is exported from ListView.tsx but not from the package entry. There is no metadata key or option.
  • Read in the diff:
    • The reset drops the view's stored entry, clears the cached search and strips every uf_* parameter (withoutListStateParams). A pending-reset flag makes the next seed read storage, not the URL, so a reload or an opened link can't bring the old list back.
    • ListView reports the differing parts through a ref, keyed on the joined part list, so it fires only when the set changes.
    • The marker sits in the existing PageHeader badge slot and beside the phone title, and shows the icon alone below sm. The header's ⋯ "Reset view" is gone, while the Sort reset and the Filter "Clear all" stay.
  • Pins changed: ObjectView.userListState-12418 moves the reset from the ⋯ menu item to the marker's popover, the change this card asks for. The reset's effect (the view's own sort, density and hidden fields come back) is still pinned.
  • Gates (check-runs on this head): 51 success, 8 skipped, 0 failed or pending. mergeable_state is clean.
  • Tests (dev):
    • On this head: type-check 67 of 67, build 117 of 117, plugin-list 1,759, the app-shell ObjectView set 735, and i18n 1,443. The new files hold 12 and 5 tests, 12418 holds 32, and 2 ablations went red and were restored.
    • Live at 1440 and 390 px: the address bar is clean after reset and reload. Derived gates 66 of 66. Screenshots: /mnt/project-files/issue-28950/shots/.
  • NOT MEASURED: the seat ran no live session.
  • Findings: not filed. Lists outside ObjectView (InterfaceListPage, ObjectDataPage, embeds) don't wire onPersonalizationChange, so they show no marker and keep the Display-only reset.
  • Serial: ListView.tsx is also in PR fix(ui): the list flow at phone width: 44 px targets, inset panels, a card pager, the active sort on the bar, and Back that pops cleanly #29273 (Console at phone width: Lists flow audit and fixes at 375 and 390 px #29074). Whichever lands second merges main.
  • Landing: ordinary queue landing, pr_ready then automerge_enable. Fixes #28950 closes the card on merge.

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/xl tooling

Projects

None yet

1 participant