Skip to content

refactor(app-shell): move ObjectView's page header and record overlay into sibling sub-components (slice 10) - #26434

Merged
objectstack-fleet[bot] merged 23 commits into
mainfrom
claude/project-thread-x004t9
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 23 commits into
mainfrom
claude/project-thread-x004t9

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steve · project thread

Part of #25992 (slice 10 of the move-only split; slices 1 to 9 landed as #26152, #26233, #26310, #26336, #26353, #26374, #26396 and #26412, and the card stays open for slice 11).

Clause-②: no
Metadata-protocol: none

What moved

Base 4ef75053 (slice 9 merged, latest main merged in; main has not touched ObjectView.tsx since). ui/packages/app-shell/src/views/ObjectView.tsx is 2374 lines there. Two blocks of ObjectViewInner's JSX return move verbatim into sub-components in two new sibling modules. In each, every value the block read is passed as a prop, typed from its source (no any), the props keep the names the block used, no identifier is renamed, no context is added, and the rendered DOM is the same.

Q-header: the page header → objectViewPageHeader.tsx

Lines 1987–2100 (the hidden sm:block wrapper and its PageHeader) move into ObjectViewPageHeader, mounted in their place with the props objectDef, objectPluralLabel, objectDesc, createButton, importShown, setShowImport, importDisabled, t, listActions, isEnvironmentList, headerToolbarActions, environmentEntitlements, toolbarBar, listFavorite, activeViewHasPreference, handleResetView and foldedHeaderActions. That covers the plural title (objectui#11696), the managed-by badge (objectui#12194, #12606), the createElement icon, the create button, Import, the schema-driven toolbar or EnvironmentListToolbar, the overflow menu with favorites and "Reset view" (objectui#12427, #12418), and the folded face (objectui#12705). The "1. Header with breadcrumb + description" comment above it stays in place.

Q-overlay: the record detail overlay → objectViewRecordOverlay.tsx

Lines 2347–2368 (the NavigationOverlay drawn for every mode but split, with its RecordDetailView render function) move into ObjectViewRecordOverlay, with the props navOverlay, handleDrawerClose, objectLabel, objectDef, handleExpandDrawer, t, dataSource, objects and onEdit. The navOverlay.mode !== 'split' && guard around it stays in the component.

What stays in the return, and why

  • The import wizard. plugin-form/src/affordanceGrantMap-12082.test.tsx finds the ImportWizard mounts by walking JSX element names per file, and requires each mount to sit in a file the listImport site names (CONSOLE_LIST = ObjectView.tsx, which also reads the listImport row). A mount in a new file would fail that pin. Re-pointing it would change the site's readBy list rather than a path, so the block stays.
  • The split face of the overlay. It wraps the list itself (mainContent holds the PluginObjectView), so moving it would mean passing that subtree in as a prop, which changes lines.
  • The phone FAB, ManageViewsDialog, the managed-users invite line, the list area, MetadataPanel, ViewConfigPanel and CreateViewDialog stay. They are short, or they are the views' own config surface, which slice 11's handlers feed.

Imports

Each import moves with its only readers. createElement, Button, the four DropdownMenu* parts, Upload, MoreHorizontal, RotateCcw, SchemaRenderer, FavoriteMenuItem, getIcon, PageHeader, ManagedByBadge and EnvironmentListToolbar go to the header module. NavigationOverlay and RecordDetailView are still drawn by the split face, so the overlay module imports them too. Nothing new is exported from ObjectView.tsx, and the package entry is untouched. ObjectView.tsx goes from 2374 to 2263 lines.

Move-only proof: the multiset of non-blank lines of the base ObjectView.tsx is contained in new ObjectView.tsx plus the two modules. The only base lines missing are four import lines that lost a moved name: the react, lucide-react, ../layout/SidebarItemMenu.js and @object-ui/react imports. Each is replaced by its trimmed form. Removed outright are the Button and DropdownMenu* entries of the @object-ui/components list and the getIcon, PageHeader, ManagedByBadge and EnvironmentListToolbar imports, whose lines reappear verbatim in the header module. The added lines are the trimmed imports and the two new imports, the module headers and imports, the component signatures and prop types, return ( / );, and in ObjectView.tsx the two mounts with their props and one-line comments.

Test edits

None. No source reader of ObjectView.tsx names a moved line. The readers that mention PageHeader, NavigationOverlay, RecordDetailView or EnvironmentListToolbar read other files, mock the module, or assert an absence (navRunAction.privateConventionRetired: no runAction literal, which holds in every file). The affordanceGrantMap-12082 mount walk sees no host-gated component mounted in either new module.

Seam table rows this PR changes (measured on 4ef75053)

The full table (regions A to Q) is in #26336.

# Concern Lines on 4ef75053 Reads Read by Slice
Q-header PageHeader: title, badge, icon, create, Import, toolbar, overflow menu, folded face 1987–2100 A, C, D, E (setShowImport), G, H, P — 10, this PR
Q-overlay NavigationOverlay for every mode but split 2347–2368 A, C, L — 10, this PR
Q (rest) Providers, FAB, import wizard, tab row, dialogs, list area, split overlay, config panel 1968–2346 less Q-header everything — stays (see above)

Remaining slice (closes the card)

  • Slice 11 — B + I + O + G-columns: the view-config and view-tab CRUD handlers, renderListView and the column flags only it reads. The writeRefusalCensus-11583 rows, the relayRungCensus-7559 read, the check-i18n-call-site-keys.test.ts deleteViewConfirm row and the tree-view-config-readers-8253 CONSOLE_COMPOSITION read of the tree labelField rung are re-pointed (path and file-key changes only).

Verification

See the os-dev-report comment on the card for the full readings: app-shell type-check after rebuilding its dependency closure, every ObjectView.* test in views/ (header fold, favorites entry, list title plural, managed-by badge, setup list chrome, import and create predicates, record navigation and overlay tests among them), the AppContent* tests, every other test that reads ObjectView.tsx as source, eslint on the 3 touched files, the derived dispatch gates, and a Console build.

Acceptance notes

  • eslint: 0 errors, no new warning. The three files report 110 warnings, the same count as the base ObjectView.tsx alone; the two exhaustive-deps warnings on handleViewChange only moved lines.

Changeset: .changeset/25992-objectview-page-header-overlay-split.md (patch, @object-ui/app-shell).


🤖 Generated with Claude Code

https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz

claude added 22 commits October 26, 2026 14:40
…tViewOptions.ts (slice 2)

The timeline/calendar/gantt/gallery/kanban ViewOptions, storedLegacyOptions,
isSameOptionsValue and dispatchViewPatches move verbatim out of
views/ObjectView.tsx into views/objectViewOptions.ts. ObjectView.tsx imports
them back and re-exports every name it exported before.

Test edits forced by the move: the four source scans that read the
ObjectView face (calendarBinding-7029, galleryBinding-7547, ganttBinding-7070,
titleFieldConvergence) now read ObjectView.tsx plus objectViewOptions.ts, and
the writeRefusalCensus-11583 LEDGER row for dispatchViewPatches is keyed by
its new file.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…ome.tsx (slice 3)

ObjectNotFoundState, the saved-view tab strip's viewport check
(VIEW_TABS_MIN_WIDTH, viewTabsFitNow, useViewTabsFit) and
keepInlineEditModeForTheSession move verbatim out of views/ObjectView.tsx
into views/objectViewChrome.tsx. ObjectView.tsx imports them back and
re-exports ObjectNotFoundState, the one name it exported. No test edits.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…unt into a sibling hook

Slice 4 of the move-only ObjectView split. The realtime subscription,
the reconnection conflict resolution and the footer record-count fetch
move verbatim from ObjectViewInner into useObjectViewLiveData in
views/objectViewLiveData.ts. The component calls the hook at the same
position (hook order unchanged) and passes its inputs explicitly; the
recordCount state stays in the component. No test edits.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…igation into sibling hooks

Slices 5 and 6 of the move-only ObjectView split, paired in one PR.

- The identity-import gate, its password-policy state and data source,
  and the managed-environment Users marker move verbatim into
  useObjectViewIdentity in views/objectViewIdentity.tsx.
- The record navigation overlay (detail navigation config, drawer
  recordId URL state, row click, expand to page, list record links)
  moves verbatim into useObjectViewRecordNavigation in
  views/objectViewRecordNavigation.ts. The URL equality filters and the
  user-filter selections between its two blocks stay in the component.

Inputs are passed explicitly; the component destructures the returned
values under their original names. No test edits.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…m their sources

activeView, objectDef and objects were written as `any` in the new
hook's parameter type. They now name where the component gets each
value: the element of buildViewTabs' result, and
ConsoleObjectViewProps['objects'] and its element. No new
no-explicit-any warning; the moved bodies are unchanged.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…a sibling hook

Slice 7 of the move-only ObjectView split. The environment list's
entitlements, the list actions and their localized toolbar copies, and
the create and import buttons' toolbar-scope predicates move verbatim
into useObjectViewToolbarAffordances in
views/objectViewToolbarAffordances.ts. The resolveAffordance verdict
memo stays in the component (the affordance grant census reads it
there) and is passed in with the other inputs, typed from their
sources. No test edits.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…ate into sibling hooks

Slice 8 of the move-only ObjectView split.

- The header's list_toolbar actions (less the record-scoped ones the
  selection bar carries), the nav runAction deep link and the
  action:bar node move verbatim into useObjectViewHeaderToolbar in
  views/objectViewHeaderToolbar.ts.
- The list's uf_ URL state (seed, write-back, Reset view) moves into
  useObjectViewListUrlState, and the filter[...] equality filters with
  the restored user-filter selections into useObjectViewUrlFilters,
  both in views/objectViewListUrlState.ts.

Hook parameters are typed from their sources. One test edit, path
only: ObjectView.urlFilterSuffix-9196 reads objectViewListUrlState.ts,
where the scanned block now lives.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
…and header chrome into sibling hooks

Move-only split of ObjectViewInner: the view tab items, ViewTabBar and
its own-row face go to objectViewTabBar.tsx; the merged views and the
plugin ObjectViewSchema go to objectViewPluginSchema.ts; the create
button, folded header actions menu and refused-view face go to
objectViewHeaderChrome.tsx. Each hook is called where its block stood.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
… into sibling sub-components

Move-only split of ObjectViewInner's JSX: the PageHeader block goes to
ObjectViewPageHeader in objectViewPageHeader.tsx, and the non-split
NavigationOverlay with its RecordDetailView goes to
ObjectViewRecordOverlay in objectViewRecordOverlay.tsx. Every value is
passed in as a prop; the rendered DOM is unchanged.

Claude-Session: https://claude.ai/code/session_01QciHBvFJXnhZqXmdqpDtpz
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling labels Oct 27, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Test UI (2/16) failed on 472a4e66 in ui/scripts/__tests__/doc-version-claims.test.ts. The test reported "implausibly few node-version declarations … expected 1 to be greater than or equal to 15". That failure does not come from this PR:

I merged main (67461403, which carries #26432) into the branch, with no force-push. The new head is d5ff0d23. On that head the test passes locally, 29/29. The merge brings no change under ui/packages/app-shell/src/views/ or layout/. CI re-runs on the push.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · seat domain:ui#1 · 2026-10-27T04:23Z

Head d5ff0d23 (read from the PR object). It is 472a4e66 plus a merge of main 67461403, which carries #26432's fix for the doc-version-claims red that #26419 left on main. That red was inherited: Test UI 2/16 on 472a4e66. The slice content is unchanged. It is built on main 4ef75053, which is after slice 9 (#26412).

  • Shape. Draft on main, Part of #25992, slice 10 (Q). Two JSX blocks move verbatim into sub-components. Every value is a prop typed from its source, with no any, and the DOM is the same:
    • Q-header (the PageHeader block) → ObjectViewPageHeader in objectViewPageHeader.tsx.
    • Q-overlay (the non-split NavigationOverlay) → ObjectViewRecordOverlay in objectViewRecordOverlay.tsx. The split guard stays.
      ObjectView.tsx goes from 2374 to 2263 lines. No test edits. Root changeset. Clause-②: no, Metadata-protocol: none.
  • Carve-outs.
    • The import wizard stays in ObjectView.tsx. affordanceGrantMap-12082's mount walk needs every ImportWizard mount to sit in a file that the listImport row's readBy names, so moving it would change readBy, not just a path.
    • The split overlay also stays, because it wraps the list through mainContent.
  • Checks pinned to the head. All check runs completed: 0 failing, 0 pending. The seat's landing script checked this before posting.
  • Governed surface. check-governed-merges.mjs --pr 26434: not governed.
  • Evidence (dev).
    • The multiset proof holds; the only base lines missing are 4 trimmed imports.
    • Closure build and type-check exit 0.
    • 203 files / 3358 tests, including the PageHeader pins and approvalLock-25934. Console build under the lock.
    • eslint: 0 errors, and warnings equal to base. 27/27 gates.
  • Serial constraints. No other open PR touches ObjectView.tsx or its objectView* siblings. The ⛔ paths are untouched.

Landing: ready + auto-merge through the relay.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 04:24
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 04:24
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit a7f4b99 Oct 27, 2026
55 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-x004t9 branch October 27, 2026 04:53
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

Development

Successfully merging this pull request may close these issues.

2 participants