Skip to content

feat(ui)!: a record opened from a list opens in the drawer by default, and the split pane retires - #29108

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

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #28915

Clause-②: yes (narrowing)
Metadata-protocol: none
Skills-docs: follow-up owed — skills/objectstack-ui/rules/record-page.md, skills/objectstack-ui/evals/record-page.json

维护者速读

  • 改了什么:从列表打开记录,默认改为右侧抽屉,列表保持全宽、所有列都在;分屏模式整体退役。Display 菜单「打开记录」只剩「抽屉」和「整页」两个选项。
  • 为什么改:维护者裁定「默认改抽屉,分屏彻底退役,只留抽屉和整页」。分屏把列表挤成三列或卡片,读起来比抽屉差。
  • 风险与回滚:读者此前存的 split 偏好会按默认(抽屉)读出,不做兼容读;@object-ui/components 移除了分屏相关的 7 个导出(BREAKING,changeset 写明迁移)。回滚即 revert 本 PR,无数据或元数据迁移。
  • 先看:ui/packages/components/src/custom/navigation-overlay.tsx(分屏外观与 Esc 处理的删除)、ui/packages/app-shell/src/views/ObjectView.tsx(去掉 drawer→split 的映射)、ui/packages/app-shell/src/utils/recordOpenPreference.ts(偏好只认 drawer / page)。
  • 两分钟验证:pnpm dev:crm 打开 Accounts 列表,点一行:记录在右侧抽屉打开,列表不变窄;打开右上 Display 菜单,「打开记录」只有 Drawer 和 Full page。
  • 需要你做的:无,席位落地。

What changes

A record opened from a list now opens in the non-modal drawer over the list by default, and the list keeps its full width and every view column. The split pane beside the list retires: a record opens only as the drawer or as the full page.

  • recordOpenPreference.ts: RecordOpenPreference is 'drawer' | 'page', default drawer. A stored split is an unknown value and reads as the default, with no compatibility read.
  • ObjectView.tsx: the drawer-to-split mapping is gone; the overlay receives the resolved mode as is. The record-count bar beside the pane (RecordPaneListCount) goes with it.
  • navigation-overlay.tsx: NavigationOverlayMode is page | drawer | new_window | none. Removed: the split face, RECORD_SPLIT_MIN_CONTENT_PX, the width probe, useSplitEscape, paneBeside and the narrowed list face. The list under the drawer renders full width inside the active-row provider and goes inert while the drawer is maximized. Kept as they were: the non-modal drawer, a row click swapping the record, the stepper, drag-to-resize, maximize in place, and the modal sheet for a drawer with no list.
  • record-list-face.tsx and custom/index.ts: the narrowed-face API leaves (RecordListFaceProvider, useRecordListFace, recordListFaceFor, RECORD_LIST_CARD_FACE_BELOW_PX, RECORD_LIST_NARROW_COLUMN_COUNT, RecordListFace); the active-row store stays.
  • ObjectGrid.tsx: no three-column or compact-card narrowing beside a pane; the grid draws every view column.
  • ListView.tsx: the record-count bar always prints its count; the list.recordOpenSplit default is gone.
  • ViewSettingsPopover.tsx: the Open records control has two segments, Drawer and Full page; selectPage is gone from the host prop.
  • i18n: list.recordOpenSplit leaves all ten packs.
  • Docs: content/docs/frontend/plugins/plugin-view.mdx (the record drawer section) and content/docs/ui/views.mdx (the Open records paragraph).
  • Changeset .changeset/28915-record-open-drawer-default.md: BREAKING, FROM-TO for each of the seven retired exports, ADR-0087 disposition not-required (runtime-export-retired ...).

Why

Maintainer ruling on #28915: 「默认应该是普通列表,右侧弹出。drawer」 and 「默认改抽屉,分屏彻底退役,只留抽屉和整页,立卡。」 This supersedes #27545's Target item 1 and the split half of its item 2.

Tests

The #27545 split tests are converted to the drawer, not deleted:

  • navigation-overlay-split-27545 becomes navigation-overlay-drawer-27545 (drawer over the list, list full width, ✕ closes, retired exports absent from the module).
  • ObjectView.recordPaneV4-27545 becomes ObjectView.recordDrawer-27545 (all five columns kept, stored split reads as drawer, list inert while maximized).
  • ListView.recordPaneCount-27545 becomes ListView.recordDrawerCount-27545; ObjectGrid.recordPaneFace-27545 becomes ObjectGrid.recordDrawerList-27545.
  • Updated to the drawer: navigation-overlay-field-escape-28156, navigation-overlay-close-i18n, chrome-i18n-no-provider-fallback, record-top-bar-27524, record-list-face-27545, RecordPageColumns.enlargedEscapeInPane-27524, recordOpenPreference.test.ts, ObjectView.recordOpenPreference-26677, ObjectView.recordOpenDisplay-26681, ObjectView.recordCountFooter-10636, objectListTitle-27950, titleViewSwitcher-27594, viewRefusal-12112, phoneRecordPage-27058, RecordDetailView.topBar-27524, ViewSettingsPopover.recordOpen-26681.

Ablation: decoding a stored split as page instead of the default turns recordOpenPreference.test.ts red (1 failed); restored, tree byte-identical to HEAD.

Acceptance notes

Screenshots (examples/app-crm, Accounts, sidebar open; at 1000 px the sidebar is pinned open with Keep sidebar open, since the console folds it to a rail below 1416 px):

before after
1440, record open split pane, list narrowed to 3 columns drawer over the full-width list
1000, record open drawer (split fell back for want of width) drawer over the full-width list
Display menu, Open records Split / Drawer / Full page Drawer / Full page

The 12 files are in the project files under issue-28915/shots/ (before- and after-, 1440- and 1000-, list, display-menu, record-open).

Premise reading, ui/packages/app-shell/src/layout/consoleSidebarRail.ts (read-only here). CONSOLE_SIDEBAR_RAIL_BELOW_WIDTH is 1416 px, derived only from split content: RECORD_SPLIT_MIN_CONTENT_WIDTH = 440 + 720 plus the 256 px expanded sidebar. With the split pane retired that derivation has no remaining reason. The fold itself still has one: #27593 quotes the maintainer 「窄屏幕左侧菜单自动折叠,点击弹出」 with an original target near 1100 px. So the threshold, not the fold, is left without a reason. Out-of-scope finding, carrier: seat.

Other out-of-scope findings (carrier: seat):

  • Stale split-pane comments outside the declared surface: record-top-bar.tsx:11, data-table.tsx:3735, plugin-detail RecordPageColumns.tsx:135/237, useRecordPageTier.ts:31, synth/recordPageColumns.ts:16/93/110, react useNavigationOverlay.ts:278, plugin-view ObjectView.tsx:590/1193, RecordDetailView.tsx:943/1003, objectViewOpenAsPage.tsx:15, appObjectScope.ts:45, ObjectGrid.tsx:3202.
  • The console.objectView.recordCount key family is now read by no source file, and the overlay no longer reads common.closePanel.
  • Skills follow-up: skills/objectstack-ui/rules/record-page.md (line 48) and skills/objectstack-ui/evals/record-page.json (line 24) still say split is the default; skills/** is outside this card.

Deviations: content/docs/ui/views.mdx (one paragraph) is outside the declared file surface, a bounded in-place fix of the same claim; the plugin-detail and RecordDetailView.topBar test files are touched because they drove the split mode. Four test files are renamed. The dogfood battery ran as four vitest --shard legs because one foreground call is capped at 600 s; the legs cover all 387 files.

Verification

Head 2255b429. Main was merged twice, at 00d0668d and at 5d3667e2, both clean; neither carried a parallel change to the touched files (ListView.tsx merged with #29044's reset-to-default change automatically).

Gates

node scripts/pm/dispatch-gates.mjs --commands: 67 derived, 67 run, all exit 0, reconciled with --ran (0 NOT-MEASURED). check:skill-examples first exited 3 (needs @objectstack/client-react built) and passed after the full build. check:type-check-debt ran under the verify lock. node scripts/check-sdui-manifest.mjs exit 0. Gates ran at 407e38cf, before the main merge; the diff they derive from is unchanged by it.

Type-check and tests
  • turbo run type-check for components, i18n, plugin-grid, plugin-list, plugin-detail, plugin-view, app-shell and console: 78 of 78 tasks, exit 0 (at 407e38cf).
  • ui vitest at 407e38cf, --maxWorkers=2, locked: plugin-list whole suite 179 files 1725 tests; plugin-grid ObjectGrid set plus components/detail/view set 207 files 1692 tests; app-shell ObjectView set 141 files 1314 tests. All green, 527 files.
  • At 4b3e19eb: the 54 test files the branch touches against the base, 377 tests, green.
  • Dogfood battery at 4b3e19eb, locked, four shards: 384 files passed, 3 skipped (387); 3166 tests passed, 28 skipped.
  • At 2255b429 (after the second merge): the touched tests plus every plugin-list test naming the Display menu or the record count, and main's new ListView.restoreViewDefaults-28948 and data-table row-action tests: 29 files, 228 tests, green; turbo run type-check plugin-list and components 51 of 51.
  • Full pnpm build: 117 of 117 tasks.
  • ui/ eslint (the Lint UI config) over the 41 touched ts/tsx files: 0 errors.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic.
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint passes.
  • 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_01QsR2jRrZ9h4h6RnBzGnhgi


Generated by Claude Code

… retires

The list keeps its full width under a non-modal drawer; the reader's
record-open preference is drawer or page, and a stored split reads as
the default.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsR2jRrZ9h4h6RnBzGnhgi
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 2, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

Contract review

Served-tier: TIER_DEFAULT
Head-sha: aaa5cdb7968e6dd0f65be625474d510cc9198bb0
Local-runs: none

Face: declaration. The PR body and the changeset declare Clause-②: yes (narrowing). No published schema and no governed path are touched: check-governed-merges --pr 29108 hit 0 of 48 paths, and nothing is under packages/spec/src/**. So the review owed is the lane seat's TIER_DEFAULT record.

① Derived judgments

  • Seven exports leave @object-ui/components: RECORD_SPLIT_MIN_CONTENT_PX, RecordListFaceProvider, useRecordListFace, recordListFaceFor, RECORD_LIST_CARD_FACE_BELOW_PX, RECORD_LIST_NARROW_COLUMN_COUNT and the type RecordListFace. Each is the split pane's width floor or its narrowed list face, and each loses its only reason with the split. The open-row store (ActiveListRecordProvider, useIsActiveListRecord, ActiveListRow, useScrollActiveListRecordIntoView, listRecordIdOf) stays exported, because the drawer still marks the open row. navigation-overlay-drawer-27545.test.tsx pins the absence of every retired name. Correct, and a narrowing.
  • NavigationOverlay's accepted modes narrow to page | drawer | new_window | none. A host that passed mode="split" passes mode="drawer" with the same mainContent. The changeset names that rewrite. The drawer over mainContent stays non-modal and undimmed, so the list behind it keeps its row swap and stepper. Correct.
  • RecordOpenPreference narrows to 'drawer' | 'page' in app-shell, and its decode accepts only those two. A stored split reads as unknown and falls to DEFAULT_RECORD_OPEN_PREFERENCE = 'drawer', with no compatibility read, as the brief ruled. The ablation that maps split to page went red. RecordOpenPreference is not exported from @object-ui/app-shell's entry, so this half is internal.
  • plugin-list's recordOpen host prop narrows: preference is 'drawer' | 'page' and selectPage is gone, since setPreference('page') does the same job. The changeset names that rewrite. After the merge of feat(plugin-list): a phone list offers Sort in the Display panel and a search that expands in place #29054 (f67bbeca), the Display menu keeps feat(plugin-list): a phone list offers Sort in the Display panel and a search that expands in place #29054's phone Sort block, and Open records offers Drawer and Full page.
  • list.recordOpenSplit leaves all ten i18n packs, and a test pins its absence in en and zh.
  • Read-only fence held: consoleSidebarRail.ts was not edited. Its 1416 px fold still derives from RECORD_SPLIT_MIN_CONTENT_WIDTH, a local constant of that file and not the retired export. The seat filed that as Console sidebar: the auto-fold below 1416 px is derived only from the retired split pane, so a 1280–1415 px window still folds the sidebar to the rail for a reason that no longer exists #29109.

② Semver level

The five changeset entries are patch with Metadata-protocol: none. AGENTS.md Post-Task Checklist step 3: a non-protocol change, widening or breaking, ships patch. The changeset is marked BREAKING, has a FROM-TO line for each retired export and a rewrite line for each narrowed prop, and carries the ADR-0087 not-required (runtime-export-retired …) disposition naming all seven operands. check-changeset-no-major and check-adr-0087-registration exit 0 on the sent body. The title carries !. Consistent.

③ Boundary flags

Implemented-by: claude/project-thread-o2b23t
Reviewed-by: session_01QsR2jR7ZFkpqmm4U8S1f9y

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#4 · zhuangjianguo · session_01QsR2jR7ZFkpqmm4U8S1f9y

席位核对 · ACCEPT · 2026-11-02T08:05Z

Head-sha: aaa5cdb7968e6dd0f65be625474d510cc9198bb0


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 2, 2026 08:06
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit ea150b7 Nov 2, 2026
60 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-o2b23t branch November 2, 2026 09:19
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 tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Record open: the drawer is the default and the split pane retires — the list keeps its full width, and a record opens only as the drawer or the page

1 participant